Compare commits

..

No commits in common. "2a4e2724a35f322af3ae26c637c74161c4884542" and "92852d823508ac38d0e346b14cf01a69456f1cdb" have entirely different histories.

14 changed files with 60 additions and 131 deletions

View File

@ -203,7 +203,7 @@ var testCasesNixos = []sealTestCase{
Link(fst.Tmp+"/etc/zshenv", "/etc/zshenv"). Link(fst.Tmp+"/etc/zshenv", "/etc/zshenv").
Link(fst.Tmp+"/etc/zshrc", "/etc/zshrc"). Link(fst.Tmp+"/etc/zshrc", "/etc/zshrc").
Tmpfs("/run/user", 4096, 0755). Tmpfs("/run/user", 4096, 0755).
Tmpfs("/run/user/1971", 8388608, 0700). Tmpfs("/run/user/1971", 8388608, 0755).
Bind("/tmp/fortify.1971/tmpdir/1", "/tmp", sandbox.BindWritable). Bind("/tmp/fortify.1971/tmpdir/1", "/tmp", sandbox.BindWritable).
Bind("/var/lib/persist/module/fortify/0/1", "/var/lib/persist/module/fortify/0/1", sandbox.BindWritable). Bind("/var/lib/persist/module/fortify/0/1", "/var/lib/persist/module/fortify/0/1", sandbox.BindWritable).
Place("/etc/passwd", []byte("u0_a1:x:1971:100:Fortify:/var/lib/persist/module/fortify/0/1:/run/current-system/sw/bin/zsh\n")). Place("/etc/passwd", []byte("u0_a1:x:1971:100:Fortify:/var/lib/persist/module/fortify/0/1:/run/current-system/sw/bin/zsh\n")).

View File

@ -146,7 +146,7 @@ var testCasesPd = []sealTestCase{
Link(fst.Tmp+"/etc/zshenv", "/etc/zshenv"). Link(fst.Tmp+"/etc/zshenv", "/etc/zshenv").
Link(fst.Tmp+"/etc/zshrc", "/etc/zshrc"). Link(fst.Tmp+"/etc/zshrc", "/etc/zshrc").
Tmpfs("/run/user", 4096, 0755). Tmpfs("/run/user", 4096, 0755).
Tmpfs("/run/user/65534", 8388608, 0700). Tmpfs("/run/user/65534", 8388608, 0755).
Bind("/tmp/fortify.1971/tmpdir/0", "/tmp", sandbox.BindWritable). Bind("/tmp/fortify.1971/tmpdir/0", "/tmp", sandbox.BindWritable).
Bind("/home/chronos", "/home/chronos", sandbox.BindWritable). Bind("/home/chronos", "/home/chronos", sandbox.BindWritable).
Place("/etc/passwd", []byte("chronos:x:65534:65534:Fortify:/home/chronos:/run/current-system/sw/bin/zsh\n")). Place("/etc/passwd", []byte("chronos:x:65534:65534:Fortify:/home/chronos:/run/current-system/sw/bin/zsh\n")).
@ -366,7 +366,7 @@ var testCasesPd = []sealTestCase{
Link(fst.Tmp+"/etc/zshenv", "/etc/zshenv"). Link(fst.Tmp+"/etc/zshenv", "/etc/zshenv").
Link(fst.Tmp+"/etc/zshrc", "/etc/zshrc"). Link(fst.Tmp+"/etc/zshrc", "/etc/zshrc").
Tmpfs("/run/user", 4096, 0755). Tmpfs("/run/user", 4096, 0755).
Tmpfs("/run/user/65534", 8388608, 0700). Tmpfs("/run/user/65534", 8388608, 0755).
Bind("/tmp/fortify.1971/tmpdir/9", "/tmp", sandbox.BindWritable). Bind("/tmp/fortify.1971/tmpdir/9", "/tmp", sandbox.BindWritable).
Bind("/home/chronos", "/home/chronos", sandbox.BindWritable). Bind("/home/chronos", "/home/chronos", sandbox.BindWritable).
Place("/etc/passwd", []byte("chronos:x:65534:65534:Fortify:/home/chronos:/run/current-system/sw/bin/zsh\n")). Place("/etc/passwd", []byte("chronos:x:65534:65534:Fortify:/home/chronos:/run/current-system/sw/bin/zsh\n")).

View File

@ -291,7 +291,7 @@ func (seal *outcome) finalise(ctx context.Context, sys sys.State, config *fst.Co
// inner XDG_RUNTIME_DIR default formatting of `/run/user/%d` as post-fsu user // inner XDG_RUNTIME_DIR default formatting of `/run/user/%d` as post-fsu user
innerRuntimeDir := path.Join("/run/user", mapuid.String()) innerRuntimeDir := path.Join("/run/user", mapuid.String())
seal.container.Tmpfs("/run/user", 1<<12, 0755) seal.container.Tmpfs("/run/user", 1<<12, 0755)
seal.container.Tmpfs(innerRuntimeDir, 1<<23, 0700) seal.container.Tmpfs(innerRuntimeDir, 1<<23, 0755)
seal.env[xdgRuntimeDir] = innerRuntimeDir seal.env[xdgRuntimeDir] = innerRuntimeDir
seal.env[xdgSessionClass] = "user" seal.env[xdgSessionClass] = "user"
seal.env[xdgSessionType] = "tty" seal.env[xdgSessionType] = "tty"

View File

@ -35,7 +35,7 @@ package
*Default:* *Default:*
` <derivation fortify-static-x86_64-unknown-linux-musl-0.3.1> ` ` <derivation fortify-static-x86_64-unknown-linux-musl-0.3.0> `
@ -606,7 +606,7 @@ package
*Default:* *Default:*
` <derivation fortify-fsu-0.3.1> ` ` <derivation fortify-fsu-0.3.0> `

View File

@ -31,7 +31,7 @@
buildGoModule rec { buildGoModule rec {
pname = "fortify"; pname = "fortify";
version = "0.3.1"; version = "0.3.0";
src = builtins.path { src = builtins.path {
name = "${pname}-src"; name = "${pname}-src";

View File

@ -165,7 +165,7 @@ func (p *Container) Start() error {
syscall.CLONE_NEWNS, syscall.CLONE_NEWNS,
// remain privileged for setup // remain privileged for setup
AmbientCaps: []uintptr{CAP_SYS_ADMIN, CAP_SETPCAP}, AmbientCaps: []uintptr{CAP_SYS_ADMIN},
UseCgroupFD: p.Cgroup != nil, UseCgroupFD: p.Cgroup != nil,
} }

View File

@ -108,9 +108,6 @@ func Init(prepare func(prefix string), setVerbose func(verbose bool)) {
} }
} }
// cache sysctl before pivot_root
LastCap()
/* /*
set up mount points from intermediate root set up mount points from intermediate root
*/ */
@ -217,31 +214,18 @@ func Init(prepare func(prefix string), setVerbose func(verbose bool)) {
} }
/* /*
caps/securebits and seccomp filter load seccomp filter
*/ */
if _, _, errno := syscall.Syscall(PR_SET_NO_NEW_PRIVS, 1, 0, 0); errno != 0 { if _, _, err := syscall.Syscall(PR_SET_NO_NEW_PRIVS, 1, 0, 0); err != 0 {
log.Fatalf("prctl(PR_SET_NO_NEW_PRIVS): %v", errno) log.Fatalf("prctl(PR_SET_NO_NEW_PRIVS): %v", err)
} }
if _, _, errno := syscall.Syscall(syscall.SYS_PRCTL, PR_CAP_AMBIENT, PR_CAP_AMBIENT_CLEAR_ALL, 0); errno != 0 {
log.Fatalf("cannot clear the ambient capability set: %v", errno)
}
for i := uintptr(0); i <= LastCap(); i++ {
if _, _, errno := syscall.Syscall(syscall.SYS_PRCTL, syscall.PR_CAPBSET_DROP, i, 0); errno != 0 {
log.Fatalf("cannot drop capability from bonding set: %v", errno)
}
}
if err := capset(
&capHeader{_LINUX_CAPABILITY_VERSION_3, 0},
&[2]capData{{0, 0, 0}, {0, 0, 0}},
); err != nil {
log.Fatalf("cannot capset: %v", err)
}
if err := seccomp.Load(params.Flags.seccomp(params.Seccomp)); err != nil { if err := seccomp.Load(params.Flags.seccomp(params.Seccomp)); err != nil {
log.Fatalf("cannot load syscall filter: %v", err) log.Fatalf("cannot load syscall filter: %v", err)
} }
/* at this point CAP_SYS_ADMIN can be dropped, however it is kept for now as it does not increase attack surface */
/* /*
pass through extra files pass through extra files
*/ */

37
sandbox/overflow.go Normal file
View File

@ -0,0 +1,37 @@
package sandbox
import (
"bytes"
"log"
"os"
"strconv"
"sync"
)
var (
ofUid int
ofGid int
ofOnce sync.Once
)
const (
ofUidPath = "/proc/sys/kernel/overflowuid"
ofGidPath = "/proc/sys/kernel/overflowgid"
)
func mustReadOverflow() {
if v, err := os.ReadFile(ofUidPath); err != nil {
log.Fatalf("cannot read %q: %v", ofUidPath, err)
} else if ofUid, err = strconv.Atoi(string(bytes.TrimSpace(v))); err != nil {
log.Fatalf("cannot interpret %q: %v", ofUidPath, err)
}
if v, err := os.ReadFile(ofGidPath); err != nil {
log.Fatalf("cannot read %q: %v", ofGidPath, err)
} else if ofGid, err = strconv.Atoi(string(bytes.TrimSpace(v))); err != nil {
log.Fatalf("cannot interpret %q: %v", ofGidPath, err)
}
}
func OverflowUid() int { ofOnce.Do(mustReadOverflow); return ofUid }
func OverflowGid() int { ofOnce.Do(mustReadOverflow); return ofGid }

View File

@ -1,16 +1,11 @@
package sandbox package sandbox
import ( import "syscall"
"syscall"
"unsafe"
)
const ( const (
O_PATH = 0x200000 O_PATH = 0x200000
PR_SET_NO_NEW_PRIVS = 0x26 PR_SET_NO_NEW_PRIVS = 0x26
CAP_SYS_ADMIN = 0x15
CAP_SYS_ADMIN = 0x15
) )
const ( const (
@ -20,44 +15,13 @@ const (
func SetDumpable(dumpable uintptr) error { func SetDumpable(dumpable uintptr) error {
// linux/sched/coredump.h // linux/sched/coredump.h
if _, _, errno := syscall.Syscall(syscall.SYS_PRCTL, syscall.PR_SET_DUMPABLE, dumpable, 0); errno != 0 { if _, _, errno := syscall.RawSyscall(syscall.SYS_PRCTL, syscall.PR_SET_DUMPABLE, dumpable, 0); errno != 0 {
return errno return errno
} }
return nil return nil
} }
const (
_LINUX_CAPABILITY_VERSION_3 = 0x20080522
PR_CAP_AMBIENT = 47
PR_CAP_AMBIENT_CLEAR_ALL = 4
CAP_SETPCAP = 8
)
type (
capHeader struct {
version uint32
pid int32
}
capData struct {
effective uint32
permitted uint32
inheritable uint32
}
)
func capset(hdrp *capHeader, datap *[2]capData) error {
if _, _, errno := syscall.Syscall(syscall.SYS_CAPSET,
uintptr(unsafe.Pointer(hdrp)),
uintptr(unsafe.Pointer(&datap[0])), 0); errno != 0 {
return errno
}
return nil
}
// IgnoringEINTR makes a function call and repeats it if it returns an // IgnoringEINTR makes a function call and repeats it if it returns an
// EINTR error. This appears to be required even though we install all // EINTR error. This appears to be required even though we install all
// signal handlers with SA_RESTART: see #22838, #38033, #38836, #40846. // signal handlers with SA_RESTART: see #22838, #38033, #38836, #40846.

View File

@ -1,47 +0,0 @@
package sandbox
import (
"bytes"
"log"
"os"
"strconv"
"sync"
)
var (
kernelOverflowuid int
kernelOverflowgid int
kernelCapLastCap int
sysctlOnce sync.Once
)
const (
kernelOverflowuidPath = "/proc/sys/kernel/overflowuid"
kernelOverflowgidPath = "/proc/sys/kernel/overflowgid"
kernelCapLastCapPath = "/proc/sys/kernel/cap_last_cap"
)
func mustReadSysctl() {
if v, err := os.ReadFile(kernelOverflowuidPath); err != nil {
log.Fatalf("cannot read %q: %v", kernelOverflowuidPath, err)
} else if kernelOverflowuid, err = strconv.Atoi(string(bytes.TrimSpace(v))); err != nil {
log.Fatalf("cannot interpret %q: %v", kernelOverflowuidPath, err)
}
if v, err := os.ReadFile(kernelOverflowgidPath); err != nil {
log.Fatalf("cannot read %q: %v", kernelOverflowgidPath, err)
} else if kernelOverflowgid, err = strconv.Atoi(string(bytes.TrimSpace(v))); err != nil {
log.Fatalf("cannot interpret %q: %v", kernelOverflowgidPath, err)
}
if v, err := os.ReadFile(kernelCapLastCapPath); err != nil {
log.Fatalf("cannot read %q: %v", kernelCapLastCapPath, err)
} else if kernelCapLastCap, err = strconv.Atoi(string(bytes.TrimSpace(v))); err != nil {
log.Fatalf("cannot interpret %q: %v", kernelCapLastCapPath, err)
}
}
func OverflowUid() int { sysctlOnce.Do(mustReadSysctl); return kernelOverflowuid }
func OverflowGid() int { sysctlOnce.Do(mustReadSysctl); return kernelOverflowgid }
func LastCap() uintptr { sysctlOnce.Do(mustReadSysctl); return uintptr(kernelCapLastCap) }

View File

@ -115,7 +115,7 @@
current-system = fs "80001ff" null null; current-system = fs "80001ff" null null;
opengl-driver = fs "80001ff" null null; opengl-driver = fs "80001ff" null null;
user = fs "800001ed" { user = fs "800001ed" {
"1000" = fs "800001c0" { "1000" = fs "800001ed" {
bus = fs "10001fd" null null; bus = fs "10001fd" null null;
pulse = fs "800001c0" { native = fs "10001b6" null null; } null; pulse = fs "800001c0" { native = fs "10001b6" null null; } null;
wayland-0 = fs "1000038" null null; wayland-0 = fs "1000038" null null;
@ -203,7 +203,7 @@
(ent "/dri" "/dev/dri" "rw,nosuid" "devtmpfs" "devtmpfs" ignore) (ent "/dri" "/dev/dri" "rw,nosuid" "devtmpfs" "devtmpfs" ignore)
(ent "/etc" "/.fortify/etc" "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw") (ent "/etc" "/.fortify/etc" "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
(ent "/" "/run/user" "rw,nosuid,nodev,relatime" "tmpfs" "tmpfs" "rw,size=4k,mode=755,uid=1000003,gid=1000003") (ent "/" "/run/user" "rw,nosuid,nodev,relatime" "tmpfs" "tmpfs" "rw,size=4k,mode=755,uid=1000003,gid=1000003")
(ent "/" "/run/user/1000" "rw,nosuid,nodev,relatime" "tmpfs" "tmpfs" "rw,size=8192k,mode=700,uid=1000003,gid=1000003") (ent "/" "/run/user/1000" "rw,nosuid,nodev,relatime" "tmpfs" "tmpfs" "rw,size=8192k,mode=755,uid=1000003,gid=1000003")
(ent "/tmp/fortify.1000/tmpdir/3" "/tmp" "rw,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw") (ent "/tmp/fortify.1000/tmpdir/3" "/tmp" "rw,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
(ent "/var/lib/fortify/u0/a3" "/var/lib/fortify/u0/a3" "rw,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw") (ent "/var/lib/fortify/u0/a3" "/var/lib/fortify/u0/a3" "rw,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
(ent ignore "/etc/passwd" "ro,nosuid,nodev,relatime" "tmpfs" "rootfs" "rw,uid=1000003,gid=1000003") (ent ignore "/etc/passwd" "ro,nosuid,nodev,relatime" "tmpfs" "rootfs" "rw,uid=1000003,gid=1000003")

View File

@ -115,7 +115,7 @@
current-system = fs "80001ff" null null; current-system = fs "80001ff" null null;
opengl-driver = fs "80001ff" null null; opengl-driver = fs "80001ff" null null;
user = fs "800001ed" { user = fs "800001ed" {
"65534" = fs "800001c0" { "65534" = fs "800001ed" {
bus = fs "10001fd" null null; bus = fs "10001fd" null null;
pulse = fs "800001c0" { native = fs "10001b6" null null; } null; pulse = fs "800001c0" { native = fs "10001b6" null null; } null;
wayland-0 = fs "1000038" null null; wayland-0 = fs "1000038" null null;
@ -203,7 +203,7 @@
(ent "/dri" "/dev/dri" "rw,nosuid" "devtmpfs" "devtmpfs" ignore) (ent "/dri" "/dev/dri" "rw,nosuid" "devtmpfs" "devtmpfs" ignore)
(ent "/etc" "/.fortify/etc" "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw") (ent "/etc" "/.fortify/etc" "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
(ent "/" "/run/user" "rw,nosuid,nodev,relatime" "tmpfs" "tmpfs" "rw,size=4k,mode=755,uid=1000001,gid=1000001") (ent "/" "/run/user" "rw,nosuid,nodev,relatime" "tmpfs" "tmpfs" "rw,size=4k,mode=755,uid=1000001,gid=1000001")
(ent "/" "/run/user/65534" "rw,nosuid,nodev,relatime" "tmpfs" "tmpfs" "rw,size=8192k,mode=700,uid=1000001,gid=1000001") (ent "/" "/run/user/65534" "rw,nosuid,nodev,relatime" "tmpfs" "tmpfs" "rw,size=8192k,mode=755,uid=1000001,gid=1000001")
(ent "/tmp/fortify.1000/tmpdir/1" "/tmp" "rw,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw") (ent "/tmp/fortify.1000/tmpdir/1" "/tmp" "rw,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
(ent "/var/lib/fortify/u0/a1" "/var/lib/fortify/u0/a1" "rw,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw") (ent "/var/lib/fortify/u0/a1" "/var/lib/fortify/u0/a1" "rw,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
(ent ignore "/etc/passwd" "ro,nosuid,nodev,relatime" "tmpfs" "rootfs" "rw,uid=1000001,gid=1000001") (ent ignore "/etc/passwd" "ro,nosuid,nodev,relatime" "tmpfs" "rootfs" "rw,uid=1000001,gid=1000001")

View File

@ -116,7 +116,7 @@
current-system = fs "80001ff" null null; current-system = fs "80001ff" null null;
opengl-driver = fs "80001ff" null null; opengl-driver = fs "80001ff" null null;
user = fs "800001ed" { user = fs "800001ed" {
"65534" = fs "800001c0" { "65534" = fs "800001ed" {
bus = fs "10001fd" null null; bus = fs "10001fd" null null;
pulse = fs "800001c0" { native = fs "10001b6" null null; } null; pulse = fs "800001c0" { native = fs "10001b6" null null; } null;
wayland-0 = fs "1000038" null null; wayland-0 = fs "1000038" null null;
@ -205,7 +205,7 @@
(ent "/dri" "/dev/dri" "rw,nosuid" "devtmpfs" "devtmpfs" ignore) (ent "/dri" "/dev/dri" "rw,nosuid" "devtmpfs" "devtmpfs" ignore)
(ent "/etc" "/.fortify/etc" "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw") (ent "/etc" "/.fortify/etc" "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
(ent "/" "/run/user" "rw,nosuid,nodev,relatime" "tmpfs" "tmpfs" "rw,size=4k,mode=755,uid=1000002,gid=1000002") (ent "/" "/run/user" "rw,nosuid,nodev,relatime" "tmpfs" "tmpfs" "rw,size=4k,mode=755,uid=1000002,gid=1000002")
(ent "/" "/run/user/65534" "rw,nosuid,nodev,relatime" "tmpfs" "tmpfs" "rw,size=8192k,mode=700,uid=1000002,gid=1000002") (ent "/" "/run/user/65534" "rw,nosuid,nodev,relatime" "tmpfs" "tmpfs" "rw,size=8192k,mode=755,uid=1000002,gid=1000002")
(ent "/tmp/fortify.1000/tmpdir/2" "/tmp" "rw,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw") (ent "/tmp/fortify.1000/tmpdir/2" "/tmp" "rw,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
(ent "/var/lib/fortify/u0/a2" "/var/lib/fortify/u0/a2" "rw,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw") (ent "/var/lib/fortify/u0/a2" "/var/lib/fortify/u0/a2" "rw,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
(ent ignore "/etc/passwd" "ro,nosuid,nodev,relatime" "tmpfs" "rootfs" "rw,uid=1000002,gid=1000002") (ent ignore "/etc/passwd" "ro,nosuid,nodev,relatime" "tmpfs" "rootfs" "rw,uid=1000002,gid=1000002")

View File

@ -99,15 +99,6 @@ print(denyOutputVerbose)
# Fail direct fsu call: # Fail direct fsu call:
print(machine.fail("sudo -u alice -i fsu")) print(machine.fail("sudo -u alice -i fsu"))
# Verify capabilities/securebits in user namespace:
print(machine.succeed("sudo -u alice -i fortify run capsh --print"))
print(machine.succeed("sudo -u alice -i fortify run capsh --has-no-new-privs"))
print(machine.fail("sudo -u alice -i fortify run capsh --has-a=CAP_SYS_ADMIN"))
print(machine.fail("sudo -u alice -i fortify run capsh --has-b=CAP_SYS_ADMIN"))
print(machine.fail("sudo -u alice -i fortify run capsh --has-i=CAP_SYS_ADMIN"))
print(machine.fail("sudo -u alice -i fortify run capsh --has-p=CAP_SYS_ADMIN"))
print(machine.fail("sudo -u alice -i fortify run umount -R /dev"))
# Verify PrintBaseError behaviour: # Verify PrintBaseError behaviour:
if denyOutput != "fsu: uid 1001 is not in the fsurc file\n": if denyOutput != "fsu: uid 1001 is not in the fsurc file\n":
raise Exception(f"unexpected deny output:\n{denyOutput}") raise Exception(f"unexpected deny output:\n{denyOutput}")