Compare commits

..

1 Commits

Author SHA1 Message Date
40028f3c03
container: optionally isolate host abstract UNIX domain sockets via landlock
All checks were successful
Test / Create distribution (push) Successful in 37s
Test / Create distribution (pull_request) Successful in 33s
Test / Sandbox (push) Successful in 2m13s
Test / Sandbox (pull_request) Successful in 2m10s
Test / Hpkg (pull_request) Successful in 4m2s
Test / Hpkg (push) Successful in 4m11s
Test / Sandbox (race detector) (pull_request) Successful in 4m15s
Test / Sandbox (race detector) (push) Successful in 4m22s
Test / Hakurei (pull_request) Successful in 4m39s
Test / Hakurei (race detector) (push) Successful in 5m3s
Test / Hakurei (race detector) (pull_request) Successful in 4m57s
Test / Flake checks (pull_request) Successful in 1m25s
Test / Hakurei (push) Successful in 38s
Test / Flake checks (push) Successful in 1m23s
2025-08-18 14:35:49 +09:00
3 changed files with 73 additions and 38 deletions

View File

@ -13,9 +13,7 @@ import (
"strconv" "strconv"
. "syscall" . "syscall"
"time" "time"
"unsafe"
"hakurei.app/container/landlock"
"hakurei.app/container/seccomp" "hakurei.app/container/seccomp"
) )
@ -94,7 +92,7 @@ type (
RetainSession bool RetainSession bool
// Do not [syscall.CLONE_NEWNET]. // Do not [syscall.CLONE_NEWNET].
HostNet bool HostNet bool
// Do not [landlock.LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET]. // Do not [LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET].
HostAbstract bool HostAbstract bool
// Retain CAP_SYS_ADMIN. // Retain CAP_SYS_ADMIN.
Privileged bool Privileged bool
@ -191,19 +189,19 @@ func (p *Container) Start() error {
// landlock: depends on per-thread state but acts on a process group // landlock: depends on per-thread state but acts on a process group
{ {
scoped := landlock.LANDLOCK_SCOPE_SIGNAL scoped := LANDLOCK_SCOPE_SIGNAL
if !p.HostAbstract { if !p.HostAbstract {
scoped |= landlock.LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET scoped |= LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET
} }
rulesetAttr := landlock.NewRulesetAttr(scoped) rulesetAttr := NewRulesetAttr(scoped)
if abi, _, errno := Syscall(seccomp.SYS_LANDLOCK_CREATE_RULESET, 0, 0, landlock.LANDLOCK_CREATE_RULESET_VERSION); abi < 0 { if abi, err := LandlockGetABI(); err != nil {
if p.HostAbstract { if p.HostAbstract {
// landlock can be skipped here as it restricts access to resources // landlock can be skipped here as it restricts access to resources
// already covered by namespaces (pid) // already covered by namespaces (pid)
goto landlockOut goto landlockOut
} }
return wrapErrSuffix(errno, return wrapErrSuffix(err,
"landlock does not appear to be enabled:") "landlock does not appear to be enabled:")
} else if abi < 6 { } else if abi < 6 {
if p.HostAbstract { if p.HostAbstract {
@ -216,23 +214,18 @@ func (p *Container) Start() error {
msg.Verbosef("landlock abi version %d", abi) msg.Verbosef("landlock abi version %d", abi)
} }
if rulesetFd, _, errno := Syscall(seccomp.SYS_LANDLOCK_CREATE_RULESET, msg.Verbosef("creating landlock ruleset scoped %#x", int(scoped))
uintptr(unsafe.Pointer(&rulesetAttr)), if rulesetFd, err := LandlockCreateRuleset(&rulesetAttr, 0); err != nil {
unsafe.Sizeof(rulesetAttr), 0, return wrapErrSuffix(err,
); rulesetFd < 0 {
return wrapErrSuffix(errno,
"cannot create landlock ruleset:") "cannot create landlock ruleset:")
} else { } else {
CloseOnExec(int(rulesetFd)) msg.Verbose("enforcing landlock ruleset")
if err = LandlockRestrictSelf(rulesetFd, 0); err != nil {
var r uintptr _ = Close(rulesetFd)
if r, _, errno = Syscall(seccomp.SYS_LANDLOCK_RESTRICT_SELF, rulesetFd, 0, 0); r != 0 { return wrapErrSuffix(err,
_ = Close(int(rulesetFd))
return wrapErrSuffix(errno,
"cannot enforce landlock ruleset:") "cannot enforce landlock ruleset:")
} }
msg.Verbosef("enforced landlock ruleset scoped %#x", int(scoped)) if err = Close(rulesetFd); err != nil {
if err := Close(int(rulesetFd)); err != nil {
msg.Verbosef("cannot close landlock ruleset: %v", err) msg.Verbosef("cannot close landlock ruleset: %v", err)
// not fatal // not fatal
} }

59
container/landlock.go Normal file
View File

@ -0,0 +1,59 @@
package container
/*
#include <linux/landlock.h>
#include <sys/syscall.h>
*/
import "C"
import (
"syscall"
"unsafe"
"hakurei.app/container/seccomp"
)
const (
LANDLOCK_CREATE_RULESET_VERSION = C.LANDLOCK_CREATE_RULESET_VERSION
LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET = C.LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET
LANDLOCK_SCOPE_SIGNAL = C.LANDLOCK_SCOPE_SIGNAL
)
type RulesetAttr = C.struct_landlock_ruleset_attr
func NewRulesetAttr(scoped int) RulesetAttr { return RulesetAttr{scoped: C.__u64(scoped)} }
/* TODO: remove everything above this */
func LandlockCreateRuleset(rulesetAttr *RulesetAttr, flags uintptr) (fd int, err error) {
var pointer, size uintptr
// NULL needed for abi version
if rulesetAttr != nil {
pointer = uintptr(unsafe.Pointer(rulesetAttr))
size = unsafe.Sizeof(*rulesetAttr)
}
rulesetFd, _, errno := syscall.Syscall(seccomp.SYS_LANDLOCK_CREATE_RULESET, pointer, size, flags)
fd = int(rulesetFd)
err = errno
if fd < 0 {
return
}
if rulesetAttr != nil { // not a fd otherwise
syscall.CloseOnExec(fd)
}
return fd, nil
}
func LandlockGetABI() (int, error) {
return LandlockCreateRuleset(nil, LANDLOCK_CREATE_RULESET_VERSION)
}
func LandlockRestrictSelf(rulesetFd int, flags uintptr) error {
r, _, errno := syscall.Syscall(seccomp.SYS_LANDLOCK_RESTRICT_SELF, uintptr(rulesetFd), flags, 0)
if r != 0 {
return errno
}
return nil
}

View File

@ -1,17 +0,0 @@
package landlock
/*
#include <linux/landlock.h>
#include <sys/syscall.h>
*/
import "C"
const (
LANDLOCK_CREATE_RULESET_VERSION = C.LANDLOCK_CREATE_RULESET_VERSION
LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET = C.LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET
LANDLOCK_SCOPE_SIGNAL = C.LANDLOCK_SCOPE_SIGNAL
)
type RulesetAttr = C.struct_landlock_ruleset_attr
func NewRulesetAttr(scoped int) RulesetAttr { return RulesetAttr{scoped: C.__u64(scoped)} }