container: optionally isolate host abstract UNIX domain sockets via landlock
All checks were successful
Test / Create distribution (push) Successful in 38s
Test / Create distribution (pull_request) Successful in 33s
Test / Sandbox (push) Successful in 2m19s
Test / Sandbox (pull_request) Successful in 2m16s
Test / Hakurei (push) Successful in 3m8s
Test / Hakurei (pull_request) Successful in 3m4s
Test / Hpkg (push) Successful in 4m17s
Test / Hpkg (pull_request) Successful in 4m15s
Test / Sandbox (race detector) (push) Successful in 4m27s
Test / Sandbox (race detector) (pull_request) Successful in 4m24s
Test / Hakurei (race detector) (push) Successful in 4m59s
Test / Hakurei (race detector) (pull_request) Successful in 4m55s
Test / Flake checks (push) Successful in 1m23s
Test / Flake checks (pull_request) Successful in 1m23s
All checks were successful
Test / Create distribution (push) Successful in 38s
Test / Create distribution (pull_request) Successful in 33s
Test / Sandbox (push) Successful in 2m19s
Test / Sandbox (pull_request) Successful in 2m16s
Test / Hakurei (push) Successful in 3m8s
Test / Hakurei (pull_request) Successful in 3m4s
Test / Hpkg (push) Successful in 4m17s
Test / Hpkg (pull_request) Successful in 4m15s
Test / Sandbox (race detector) (push) Successful in 4m27s
Test / Sandbox (race detector) (pull_request) Successful in 4m24s
Test / Hakurei (race detector) (push) Successful in 4m59s
Test / Hakurei (race detector) (pull_request) Successful in 4m55s
Test / Flake checks (push) Successful in 1m23s
Test / Flake checks (pull_request) Successful in 1m23s
This commit is contained in:
parent
69a4ab8105
commit
1fa1ea5cbb
@ -28,6 +28,8 @@ type appInfo struct {
|
||||
// passed through to [hst.Config]
|
||||
Net bool `json:"net,omitempty"`
|
||||
// passed through to [hst.Config]
|
||||
ScopeAbstract bool `json:"scope_abstract,omitempty"`
|
||||
// passed through to [hst.Config]
|
||||
Device bool `json:"dev,omitempty"`
|
||||
// passed through to [hst.Config]
|
||||
Tty bool `json:"tty,omitempty"`
|
||||
|
@ -13,7 +13,9 @@ import (
|
||||
"strconv"
|
||||
. "syscall"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"hakurei.app/container/landlock"
|
||||
"hakurei.app/container/seccomp"
|
||||
)
|
||||
|
||||
@ -92,6 +94,8 @@ type (
|
||||
RetainSession bool
|
||||
// Do not [syscall.CLONE_NEWNET].
|
||||
HostNet bool
|
||||
// Do not [landlock.LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET].
|
||||
HostAbstract bool
|
||||
// Retain CAP_SYS_ADMIN.
|
||||
Privileged bool
|
||||
}
|
||||
@ -185,6 +189,58 @@ func (p *Container) Start() error {
|
||||
"prctl(PR_SET_NO_NEW_PRIVS):")
|
||||
}
|
||||
|
||||
// landlock: depends on per-thread state but acts on a process group
|
||||
{
|
||||
scoped := landlock.LANDLOCK_SCOPE_SIGNAL
|
||||
if !p.HostAbstract {
|
||||
scoped |= landlock.LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET
|
||||
}
|
||||
rulesetAttr := landlock.NewRulesetAttr(scoped)
|
||||
|
||||
if abi, _, errno := Syscall(seccomp.SYS_LANDLOCK_CREATE_RULESET, 0, 0, landlock.LANDLOCK_CREATE_RULESET_VERSION); abi < 0 {
|
||||
if p.HostAbstract {
|
||||
// landlock can be skipped here as it restricts access to resources
|
||||
// already covered by namespaces (pid)
|
||||
goto landlockOut
|
||||
}
|
||||
return wrapErrSuffix(errno,
|
||||
"landlock does not appear to be enabled:")
|
||||
} else if abi < 6 {
|
||||
if p.HostAbstract {
|
||||
// see above comment
|
||||
goto landlockOut
|
||||
}
|
||||
return msg.WrapErr(ENOSYS,
|
||||
"kernel version too old for LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET")
|
||||
} else {
|
||||
msg.Verbosef("landlock abi version %d", abi)
|
||||
}
|
||||
|
||||
if rulesetFd, _, errno := Syscall(seccomp.SYS_LANDLOCK_CREATE_RULESET,
|
||||
uintptr(unsafe.Pointer(&rulesetAttr)),
|
||||
unsafe.Sizeof(rulesetAttr), 0,
|
||||
); rulesetFd < 0 {
|
||||
return wrapErrSuffix(errno,
|
||||
"cannot create landlock ruleset:")
|
||||
} else {
|
||||
CloseOnExec(int(rulesetFd))
|
||||
|
||||
var r uintptr
|
||||
if r, _, errno = Syscall(seccomp.SYS_LANDLOCK_RESTRICT_SELF, rulesetFd, 0, 0); r != 0 {
|
||||
_ = Close(int(rulesetFd))
|
||||
return wrapErrSuffix(errno,
|
||||
"cannot enforce landlock ruleset:")
|
||||
}
|
||||
msg.Verbosef("enforced landlock ruleset scoped %#x", int(scoped))
|
||||
if err := Close(int(rulesetFd)); err != nil {
|
||||
msg.Verbosef("cannot close landlock ruleset: %v", err)
|
||||
// not fatal
|
||||
}
|
||||
}
|
||||
|
||||
landlockOut:
|
||||
}
|
||||
|
||||
msg.Verbose("starting container init")
|
||||
if err := p.cmd.Start(); err != nil {
|
||||
return msg.WrapErr(err, err.Error())
|
||||
|
17
container/landlock/landlock.go
Normal file
17
container/landlock/landlock.go
Normal file
@ -0,0 +1,17 @@
|
||||
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)} }
|
@ -79,6 +79,8 @@ type (
|
||||
Userns bool `json:"userns,omitempty"`
|
||||
// share host net namespace
|
||||
Net bool `json:"net,omitempty"`
|
||||
// share abstract unix socket scope
|
||||
Abstract bool `json:"abstract,omitempty"`
|
||||
// allow dangerous terminal I/O
|
||||
Tty bool `json:"tty,omitempty"`
|
||||
// allow multiarch
|
||||
|
@ -33,6 +33,7 @@ func newContainer(s *hst.ContainerConfig, os sys.State, prefix string, uid, gid
|
||||
SeccompPresets: s.SeccompPresets,
|
||||
RetainSession: s.Tty,
|
||||
HostNet: s.Net,
|
||||
HostAbstract: s.Abstract,
|
||||
|
||||
// the container is canceled when shim is requested to exit or receives an interrupt or termination signal;
|
||||
// this behaviour is implemented in the shim
|
||||
|
@ -137,6 +137,7 @@ in
|
||||
multiarch
|
||||
env
|
||||
;
|
||||
scope_abstract = app.scopeAbstract;
|
||||
map_real_uid = app.mapRealUid;
|
||||
|
||||
filesystem =
|
||||
|
22
options.md
22
options.md
@ -572,6 +572,28 @@ boolean
|
||||
|
||||
|
||||
|
||||
*Example:*
|
||||
` true `
|
||||
|
||||
|
||||
## environment\.hakurei\.apps\.\<name>\.scopeAbstract
|
||||
|
||||
|
||||
|
||||
Whether to restrict abstract UNIX domain socket access\.
|
||||
|
||||
|
||||
|
||||
*Type:*
|
||||
boolean
|
||||
|
||||
|
||||
|
||||
*Default:*
|
||||
` true `
|
||||
|
||||
|
||||
|
||||
*Example:*
|
||||
` true `
|
||||
|
||||
|
@ -182,6 +182,9 @@ in
|
||||
net = mkEnableOption "network access" // {
|
||||
default = true;
|
||||
};
|
||||
scopeAbstract = mkEnableOption "abstract unix domain socket access" // {
|
||||
default = true;
|
||||
};
|
||||
|
||||
nix = mkEnableOption "nix daemon access";
|
||||
mapRealUid = mkEnableOption "mapping to priv-user uid";
|
||||
|
@ -64,6 +64,10 @@ func (p *Proxy) Start() error {
|
||||
argF, func(z *container.Container) {
|
||||
z.SeccompFlags |= seccomp.AllowMultiarch
|
||||
z.SeccompPresets |= seccomp.PresetStrict
|
||||
|
||||
// xdg-dbus-proxy fails with scoped abstract unix sockets despite pathname socket being available
|
||||
z.HostAbstract = true
|
||||
|
||||
z.Hostname = "hakurei-dbus"
|
||||
if p.output != nil {
|
||||
z.Stdout, z.Stderr = p.output, p.output
|
||||
|
@ -243,7 +243,7 @@ in
|
||||
seccomp = true;
|
||||
|
||||
try_socket = "/tmp/.X11-unix/X0";
|
||||
socket_abstract = true;
|
||||
socket_abstract = false;
|
||||
socket_pathname = true;
|
||||
};
|
||||
}
|
||||
|
@ -269,7 +269,7 @@ in
|
||||
seccomp = true;
|
||||
|
||||
try_socket = "/tmp/.X11-unix/X0";
|
||||
socket_abstract = true;
|
||||
socket_abstract = false;
|
||||
socket_pathname = false;
|
||||
};
|
||||
}
|
||||
|
@ -264,7 +264,7 @@ in
|
||||
seccomp = true;
|
||||
|
||||
try_socket = "/tmp/.X11-unix/X0";
|
||||
socket_abstract = true;
|
||||
socket_abstract = false;
|
||||
socket_pathname = false;
|
||||
};
|
||||
}
|
||||
|
@ -262,7 +262,7 @@ in
|
||||
seccomp = true;
|
||||
|
||||
try_socket = "/tmp/.X11-unix/X0";
|
||||
socket_abstract = true;
|
||||
socket_abstract = false;
|
||||
socket_pathname = false;
|
||||
};
|
||||
}
|
||||
|
@ -275,7 +275,7 @@ in
|
||||
seccomp = true;
|
||||
|
||||
try_socket = "/tmp/.X11-unix/X0";
|
||||
socket_abstract = true;
|
||||
socket_abstract = false;
|
||||
socket_pathname = true;
|
||||
};
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user