diff --git a/cmd/planterette/app.go b/cmd/planterette/app.go index 082bc28..6fb4119 100644 --- a/cmd/planterette/app.go +++ b/cmd/planterette/app.go @@ -29,6 +29,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"` diff --git a/container/container.go b/container/container.go index 8001673..48b0bdb 100644 --- a/container/container.go +++ b/container/container.go @@ -83,6 +83,8 @@ type ( RetainSession bool // Do not [syscall.CLONE_NEWNET]. HostNet bool + // Scope abstract UNIX domain sockets using LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET. + ScopeAbstract bool // Retain CAP_SYS_ADMIN. Privileged bool } diff --git a/container/init.go b/container/init.go index 04867ba..3894861 100644 --- a/container/init.go +++ b/container/init.go @@ -13,6 +13,7 @@ import ( . "syscall" "time" + "hakurei.app/container/landlock" "hakurei.app/container/seccomp" ) @@ -256,6 +257,12 @@ func Init(prepare func(prefix string), setVerbose func(verbose bool)) { msg.Verbose("syscall filter not configured") } + if params.ScopeAbstract { + if err := landlock.ScopeAbstract(); err != nil { + log.Fatalf("could not scope abstract unix sockets: %v", err) + } + } + extraFiles := make([]*os.File, params.Count) for i := range extraFiles { // setup fd is placed before all extra files diff --git a/container/landlock/landlock-helper.c b/container/landlock/landlock-helper.c new file mode 100644 index 0000000..c7564dd --- /dev/null +++ b/container/landlock/landlock-helper.c @@ -0,0 +1,14 @@ +#include +#include +#include +#include + +#include "landlock-helper.h" + +int hakurei_scope_abstract_unix_sockets(int* p_errno, int fd) { + int res = psx_syscall3(SYS_landlock_restrict_self, fd, 0, 0); + + *p_errno = errno; + + return res; +} diff --git a/container/landlock/landlock-helper.h b/container/landlock/landlock-helper.h new file mode 100644 index 0000000..b34e638 --- /dev/null +++ b/container/landlock/landlock-helper.h @@ -0,0 +1,3 @@ +#pragma once + +int hakurei_scope_abstract_unix_sockets(int* p_errno, int fd); diff --git a/container/landlock/landlock.go b/container/landlock/landlock.go new file mode 100644 index 0000000..014a97a --- /dev/null +++ b/container/landlock/landlock.go @@ -0,0 +1,57 @@ +package landlock + +/* +#cgo linux pkg-config: --static libpsx + +#include +#include + +#include "landlock-helper.h" +*/ +import "C" + +import ( + "fmt" + "syscall" + "unsafe" +) + +const ( + LANDLOCK_CREATE_RULESET_VERSION = C.LANDLOCK_CREATE_RULESET_VERSION + LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET = C.LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET + + SYS_LANDLOCK_CREATE_RULESET = C.SYS_landlock_create_ruleset +) + +type LandlockRulesetAttr = C.struct_landlock_ruleset_attr + +func ScopeAbstract() error { + abi, _, err := syscall.Syscall(SYS_LANDLOCK_CREATE_RULESET, 0, 0, LANDLOCK_CREATE_RULESET_VERSION) + + if err != 0 { + return fmt.Errorf("could not fetch landlock ABI: errno %v", err) + } + + if abi < 6 { + return fmt.Errorf("landlock ABI must be >= 6, got %d", abi) + } + + attrs := LandlockRulesetAttr{ + scoped: LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET, + } + + fd, _, err := syscall.Syscall(SYS_LANDLOCK_CREATE_RULESET, uintptr(unsafe.Pointer(&attrs)), unsafe.Sizeof(attrs), 0) + + if err != 0 { + return fmt.Errorf("could not create landlock ruleset: errno %v", err) + } + + defer syscall.Close(int(fd)) + + var errno C.int + if rv := C.hakurei_scope_abstract_unix_sockets(&errno, C.int(fd)); rv != 0 { + return fmt.Errorf("could not restrict self via landlock: errno %v", errno) + } + + return nil +} diff --git a/hst/container.go b/hst/container.go index 00f7479..47416b1 100644 --- a/hst/container.go +++ b/hst/container.go @@ -22,6 +22,8 @@ type ( Userns bool `json:"userns,omitempty"` // share host net namespace Net bool `json:"net,omitempty"` + // disallow accessing abstract UNIX domain sockets created outside the container + ScopeAbstract bool `json:"scope_abstract,omitempty"` // allow dangerous terminal I/O Tty bool `json:"tty,omitempty"` // allow multiarch diff --git a/internal/app/container_linux.go b/internal/app/container_linux.go index 98cddc7..2ebb32f 100644 --- a/internal/app/container_linux.go +++ b/internal/app/container_linux.go @@ -32,6 +32,7 @@ func newContainer(s *hst.ContainerConfig, os sys.State, uid, gid *int) (*contain SeccompPresets: s.SeccompPresets, RetainSession: s.Tty, HostNet: s.Net, + ScopeAbstract: s.ScopeAbstract, } { diff --git a/nixos.nix b/nixos.nix index bae7d72..f4fee53 100644 --- a/nixos.nix +++ b/nixos.nix @@ -135,6 +135,7 @@ in multiarch env ; + scope_abstract = app.scopeAbstract; map_real_uid = app.mapRealUid; filesystem = diff --git a/options.md b/options.md index 7d6fc26..bdac871 100644 --- a/options.md +++ b/options.md @@ -572,6 +572,28 @@ boolean +*Example:* +` true ` + + +## environment\.hakurei\.apps\.\\.scopeAbstract + + + +Whether to restrict abstract UNIX domain socket access\. + + + +*Type:* +boolean + + + +*Default:* +` true ` + + + *Example:* ` true ` diff --git a/options.nix b/options.nix index 684485e..061c1dd 100644 --- a/options.nix +++ b/options.nix @@ -203,6 +203,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"; diff --git a/package.nix b/package.nix index bf35e06..2d2ee9c 100644 --- a/package.nix +++ b/package.nix @@ -5,6 +5,7 @@ makeBinaryWrapper, xdg-dbus-proxy, pkg-config, + libcap, libffi, libseccomp, acl, @@ -80,11 +81,17 @@ buildGoModule rec { hsu = "/run/wrappers/bin/hsu"; }; - # nix build environment does not allow acls - env.GO_TEST_SKIP_ACL = 1; + env = { + # required by libpsx + CGO_LDFLAGS_ALLOW = "-Wl,(--no-whole-archive|--whole-archive)"; + + # nix build environment does not allow acls + GO_TEST_SKIP_ACL = 1; + }; buildInputs = [ + libcap libffi libseccomp acl diff --git a/system/dbus/proc.go b/system/dbus/proc.go index 879bc77..f4abf68 100644 --- a/system/dbus/proc.go +++ b/system/dbus/proc.go @@ -68,6 +68,10 @@ func (p *Proxy) Start() error { argF, func(z *container.Container) { z.SeccompFlags |= seccomp.AllowMultiarch z.SeccompPresets |= seccomp.PresetStrict + + // xdg-dbus-proxy requires host abstract UNIX domain socket access + z.ScopeAbstract = false + z.Hostname = "hakurei-dbus" z.CommandContext = p.CommandContext if p.output != nil {