Compare commits
6 Commits
92852d8235
...
2a4e2724a3
Author | SHA1 | Date | |
---|---|---|---|
2a4e2724a3 | |||
d613257841 | |||
18644d90be | |||
52fcc48ac1 | |||
8b69bcd215 | |||
2dd49c437c |
@ -203,7 +203,7 @@ var testCasesNixos = []sealTestCase{
|
||||
Link(fst.Tmp+"/etc/zshenv", "/etc/zshenv").
|
||||
Link(fst.Tmp+"/etc/zshrc", "/etc/zshrc").
|
||||
Tmpfs("/run/user", 4096, 0755).
|
||||
Tmpfs("/run/user/1971", 8388608, 0755).
|
||||
Tmpfs("/run/user/1971", 8388608, 0700).
|
||||
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).
|
||||
Place("/etc/passwd", []byte("u0_a1:x:1971:100:Fortify:/var/lib/persist/module/fortify/0/1:/run/current-system/sw/bin/zsh\n")).
|
||||
|
@ -146,7 +146,7 @@ var testCasesPd = []sealTestCase{
|
||||
Link(fst.Tmp+"/etc/zshenv", "/etc/zshenv").
|
||||
Link(fst.Tmp+"/etc/zshrc", "/etc/zshrc").
|
||||
Tmpfs("/run/user", 4096, 0755).
|
||||
Tmpfs("/run/user/65534", 8388608, 0755).
|
||||
Tmpfs("/run/user/65534", 8388608, 0700).
|
||||
Bind("/tmp/fortify.1971/tmpdir/0", "/tmp", 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")).
|
||||
@ -366,7 +366,7 @@ var testCasesPd = []sealTestCase{
|
||||
Link(fst.Tmp+"/etc/zshenv", "/etc/zshenv").
|
||||
Link(fst.Tmp+"/etc/zshrc", "/etc/zshrc").
|
||||
Tmpfs("/run/user", 4096, 0755).
|
||||
Tmpfs("/run/user/65534", 8388608, 0755).
|
||||
Tmpfs("/run/user/65534", 8388608, 0700).
|
||||
Bind("/tmp/fortify.1971/tmpdir/9", "/tmp", 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")).
|
||||
|
@ -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
|
||||
innerRuntimeDir := path.Join("/run/user", mapuid.String())
|
||||
seal.container.Tmpfs("/run/user", 1<<12, 0755)
|
||||
seal.container.Tmpfs(innerRuntimeDir, 1<<23, 0755)
|
||||
seal.container.Tmpfs(innerRuntimeDir, 1<<23, 0700)
|
||||
seal.env[xdgRuntimeDir] = innerRuntimeDir
|
||||
seal.env[xdgSessionClass] = "user"
|
||||
seal.env[xdgSessionType] = "tty"
|
||||
|
@ -35,7 +35,7 @@ package
|
||||
|
||||
|
||||
*Default:*
|
||||
` <derivation fortify-static-x86_64-unknown-linux-musl-0.3.0> `
|
||||
` <derivation fortify-static-x86_64-unknown-linux-musl-0.3.1> `
|
||||
|
||||
|
||||
|
||||
@ -606,7 +606,7 @@ package
|
||||
|
||||
|
||||
*Default:*
|
||||
` <derivation fortify-fsu-0.3.0> `
|
||||
` <derivation fortify-fsu-0.3.1> `
|
||||
|
||||
|
||||
|
||||
|
@ -31,7 +31,7 @@
|
||||
|
||||
buildGoModule rec {
|
||||
pname = "fortify";
|
||||
version = "0.3.0";
|
||||
version = "0.3.1";
|
||||
|
||||
src = builtins.path {
|
||||
name = "${pname}-src";
|
||||
|
@ -165,7 +165,7 @@ func (p *Container) Start() error {
|
||||
syscall.CLONE_NEWNS,
|
||||
|
||||
// remain privileged for setup
|
||||
AmbientCaps: []uintptr{CAP_SYS_ADMIN},
|
||||
AmbientCaps: []uintptr{CAP_SYS_ADMIN, CAP_SETPCAP},
|
||||
|
||||
UseCgroupFD: p.Cgroup != nil,
|
||||
}
|
||||
|
@ -108,6 +108,9 @@ func Init(prepare func(prefix string), setVerbose func(verbose bool)) {
|
||||
}
|
||||
}
|
||||
|
||||
// cache sysctl before pivot_root
|
||||
LastCap()
|
||||
|
||||
/*
|
||||
set up mount points from intermediate root
|
||||
*/
|
||||
@ -214,18 +217,31 @@ func Init(prepare func(prefix string), setVerbose func(verbose bool)) {
|
||||
}
|
||||
|
||||
/*
|
||||
load seccomp filter
|
||||
caps/securebits and seccomp filter
|
||||
*/
|
||||
|
||||
if _, _, err := syscall.Syscall(PR_SET_NO_NEW_PRIVS, 1, 0, 0); err != 0 {
|
||||
log.Fatalf("prctl(PR_SET_NO_NEW_PRIVS): %v", err)
|
||||
if _, _, errno := syscall.Syscall(PR_SET_NO_NEW_PRIVS, 1, 0, 0); errno != 0 {
|
||||
log.Fatalf("prctl(PR_SET_NO_NEW_PRIVS): %v", errno)
|
||||
}
|
||||
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 {
|
||||
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
|
||||
*/
|
||||
|
@ -1,37 +0,0 @@
|
||||
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 }
|
@ -1,11 +1,16 @@
|
||||
package sandbox
|
||||
|
||||
import "syscall"
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
O_PATH = 0x200000
|
||||
O_PATH = 0x200000
|
||||
|
||||
PR_SET_NO_NEW_PRIVS = 0x26
|
||||
CAP_SYS_ADMIN = 0x15
|
||||
|
||||
CAP_SYS_ADMIN = 0x15
|
||||
)
|
||||
|
||||
const (
|
||||
@ -15,13 +20,44 @@ const (
|
||||
|
||||
func SetDumpable(dumpable uintptr) error {
|
||||
// linux/sched/coredump.h
|
||||
if _, _, errno := syscall.RawSyscall(syscall.SYS_PRCTL, syscall.PR_SET_DUMPABLE, dumpable, 0); errno != 0 {
|
||||
if _, _, errno := syscall.Syscall(syscall.SYS_PRCTL, syscall.PR_SET_DUMPABLE, dumpable, 0); errno != 0 {
|
||||
return errno
|
||||
}
|
||||
|
||||
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
|
||||
// EINTR error. This appears to be required even though we install all
|
||||
// signal handlers with SA_RESTART: see #22838, #38033, #38836, #40846.
|
||||
|
47
sandbox/sysctl.go
Normal file
47
sandbox/sysctl.go
Normal file
@ -0,0 +1,47 @@
|
||||
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) }
|
@ -115,7 +115,7 @@
|
||||
current-system = fs "80001ff" null null;
|
||||
opengl-driver = fs "80001ff" null null;
|
||||
user = fs "800001ed" {
|
||||
"1000" = fs "800001ed" {
|
||||
"1000" = fs "800001c0" {
|
||||
bus = fs "10001fd" null null;
|
||||
pulse = fs "800001c0" { native = fs "10001b6" null null; } null;
|
||||
wayland-0 = fs "1000038" null null;
|
||||
@ -203,7 +203,7 @@
|
||||
(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 "/" "/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=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 "/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 ignore "/etc/passwd" "ro,nosuid,nodev,relatime" "tmpfs" "rootfs" "rw,uid=1000003,gid=1000003")
|
||||
|
@ -115,7 +115,7 @@
|
||||
current-system = fs "80001ff" null null;
|
||||
opengl-driver = fs "80001ff" null null;
|
||||
user = fs "800001ed" {
|
||||
"65534" = fs "800001ed" {
|
||||
"65534" = fs "800001c0" {
|
||||
bus = fs "10001fd" null null;
|
||||
pulse = fs "800001c0" { native = fs "10001b6" null null; } null;
|
||||
wayland-0 = fs "1000038" null null;
|
||||
@ -203,7 +203,7 @@
|
||||
(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 "/" "/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=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 "/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 ignore "/etc/passwd" "ro,nosuid,nodev,relatime" "tmpfs" "rootfs" "rw,uid=1000001,gid=1000001")
|
||||
|
@ -116,7 +116,7 @@
|
||||
current-system = fs "80001ff" null null;
|
||||
opengl-driver = fs "80001ff" null null;
|
||||
user = fs "800001ed" {
|
||||
"65534" = fs "800001ed" {
|
||||
"65534" = fs "800001c0" {
|
||||
bus = fs "10001fd" null null;
|
||||
pulse = fs "800001c0" { native = fs "10001b6" null null; } null;
|
||||
wayland-0 = fs "1000038" null null;
|
||||
@ -205,7 +205,7 @@
|
||||
(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 "/" "/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=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 "/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 ignore "/etc/passwd" "ro,nosuid,nodev,relatime" "tmpfs" "rootfs" "rw,uid=1000002,gid=1000002")
|
||||
|
@ -99,6 +99,15 @@ print(denyOutputVerbose)
|
||||
# Fail direct fsu call:
|
||||
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:
|
||||
if denyOutput != "fsu: uid 1001 is not in the fsurc file\n":
|
||||
raise Exception(f"unexpected deny output:\n{denyOutput}")
|
||||
|
Loading…
Reference in New Issue
Block a user