aboutsummaryrefslogtreecommitdiffhomepage
path: root/container
diff options
context:
space:
mode:
authorOphestra <cat@gensokyo.uk>2026-04-10 23:56:45 +0900
committerOphestra <cat@gensokyo.uk>2026-04-10 23:56:45 +0900
commitb39064037625d1757a65913086ec3a247ad5bb29 (patch)
tree7fd59329e779e195aaadab8846996844e5f45974 /container
parentad2c9f36cddfd8adcd06fbe9238d176a8eb89614 (diff)
internal/landlock: relocate from package container
This is not possible to use directly, so remove it from the public API. Signed-off-by: Ophestra <cat@gensokyo.uk>
Diffstat (limited to 'container')
-rw-r--r--container/container.go11
-rw-r--r--container/container_test.go3
-rw-r--r--container/landlock.go259
-rw-r--r--container/landlock_test.go65
4 files changed, 9 insertions, 329 deletions
diff --git a/container/container.go b/container/container.go
index aeea3dcc..a798084a 100644
--- a/container/container.go
+++ b/container/container.go
@@ -21,6 +21,7 @@ import (
"hakurei.app/container/std"
"hakurei.app/ext"
"hakurei.app/fhs"
+ "hakurei.app/internal/landlock"
"hakurei.app/message"
)
@@ -317,12 +318,14 @@ func (p *Container) Start() error {
// landlock: depends on per-thread state but acts on a process group
{
- rulesetAttr := &RulesetAttr{Scoped: LANDLOCK_SCOPE_SIGNAL}
+ rulesetAttr := &landlock.RulesetAttr{
+ Scoped: landlock.LANDLOCK_SCOPE_SIGNAL,
+ }
if !p.HostAbstract {
- rulesetAttr.Scoped |= LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET
+ rulesetAttr.Scoped |= landlock.LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET
}
- if abi, err := LandlockGetABI(); err != nil {
+ if abi, err := landlock.GetABI(); err != nil {
if p.HostAbstract || !p.HostNet {
// landlock can be skipped here as it restricts access
// to resources already covered by namespaces (pid, net)
@@ -351,7 +354,7 @@ func (p *Container) Start() error {
}
} else {
p.msg.Verbosef("enforcing landlock ruleset %s", rulesetAttr)
- if err = LandlockRestrictSelf(rulesetFd, 0); err != nil {
+ if err = landlock.RestrictSelf(rulesetFd, 0); err != nil {
_ = Close(rulesetFd)
return &StartError{
Fatal: true,
diff --git a/container/container_test.go b/container/container_test.go
index 092a7a37..41d39f68 100644
--- a/container/container_test.go
+++ b/container/container_test.go
@@ -26,6 +26,7 @@ import (
"hakurei.app/fhs"
"hakurei.app/hst"
"hakurei.app/internal/info"
+ "hakurei.app/internal/landlock"
"hakurei.app/internal/params"
"hakurei.app/ldd"
"hakurei.app/message"
@@ -456,7 +457,7 @@ func TestContainer(t *testing.T) {
c.RetainSession = tc.session
c.HostNet = tc.net
if info.CanDegrade {
- if _, err := container.LandlockGetABI(); err != nil {
+ if _, err := landlock.GetABI(); err != nil {
if !errors.Is(err, syscall.ENOSYS) {
t.Fatalf("LandlockGetABI: error = %v", err)
}
diff --git a/container/landlock.go b/container/landlock.go
deleted file mode 100644
index a8007692..00000000
--- a/container/landlock.go
+++ /dev/null
@@ -1,259 +0,0 @@
-package container
-
-import (
- "strings"
- "syscall"
- "unsafe"
-
- "hakurei.app/ext"
-)
-
-// include/uapi/linux/landlock.h
-
-const (
- LANDLOCK_CREATE_RULESET_VERSION = 1 << iota
-)
-
-// LandlockAccessFS is bitmask of handled filesystem actions.
-type LandlockAccessFS uint64
-
-const (
- LANDLOCK_ACCESS_FS_EXECUTE LandlockAccessFS = 1 << iota
- LANDLOCK_ACCESS_FS_WRITE_FILE
- LANDLOCK_ACCESS_FS_READ_FILE
- LANDLOCK_ACCESS_FS_READ_DIR
- LANDLOCK_ACCESS_FS_REMOVE_DIR
- LANDLOCK_ACCESS_FS_REMOVE_FILE
- LANDLOCK_ACCESS_FS_MAKE_CHAR
- LANDLOCK_ACCESS_FS_MAKE_DIR
- LANDLOCK_ACCESS_FS_MAKE_REG
- LANDLOCK_ACCESS_FS_MAKE_SOCK
- LANDLOCK_ACCESS_FS_MAKE_FIFO
- LANDLOCK_ACCESS_FS_MAKE_BLOCK
- LANDLOCK_ACCESS_FS_MAKE_SYM
- LANDLOCK_ACCESS_FS_REFER
- LANDLOCK_ACCESS_FS_TRUNCATE
- LANDLOCK_ACCESS_FS_IOCTL_DEV
-
- _LANDLOCK_ACCESS_FS_DELIM
-)
-
-// String returns a space-separated string of [LandlockAccessFS] flags.
-func (f LandlockAccessFS) String() string {
- switch f {
- case LANDLOCK_ACCESS_FS_EXECUTE:
- return "execute"
-
- case LANDLOCK_ACCESS_FS_WRITE_FILE:
- return "write_file"
-
- case LANDLOCK_ACCESS_FS_READ_FILE:
- return "read_file"
-
- case LANDLOCK_ACCESS_FS_READ_DIR:
- return "read_dir"
-
- case LANDLOCK_ACCESS_FS_REMOVE_DIR:
- return "remove_dir"
-
- case LANDLOCK_ACCESS_FS_REMOVE_FILE:
- return "remove_file"
-
- case LANDLOCK_ACCESS_FS_MAKE_CHAR:
- return "make_char"
-
- case LANDLOCK_ACCESS_FS_MAKE_DIR:
- return "make_dir"
-
- case LANDLOCK_ACCESS_FS_MAKE_REG:
- return "make_reg"
-
- case LANDLOCK_ACCESS_FS_MAKE_SOCK:
- return "make_sock"
-
- case LANDLOCK_ACCESS_FS_MAKE_FIFO:
- return "make_fifo"
-
- case LANDLOCK_ACCESS_FS_MAKE_BLOCK:
- return "make_block"
-
- case LANDLOCK_ACCESS_FS_MAKE_SYM:
- return "make_sym"
-
- case LANDLOCK_ACCESS_FS_REFER:
- return "fs_refer"
-
- case LANDLOCK_ACCESS_FS_TRUNCATE:
- return "fs_truncate"
-
- case LANDLOCK_ACCESS_FS_IOCTL_DEV:
- return "fs_ioctl_dev"
-
- default:
- var c []LandlockAccessFS
- for i := LandlockAccessFS(1); i < _LANDLOCK_ACCESS_FS_DELIM; i <<= 1 {
- if f&i != 0 {
- c = append(c, i)
- }
- }
- if len(c) == 0 {
- return "NULL"
- }
- s := make([]string, len(c))
- for i, v := range c {
- s[i] = v.String()
- }
- return strings.Join(s, " ")
- }
-}
-
-// LandlockAccessNet is bitmask of handled network actions.
-type LandlockAccessNet uint64
-
-const (
- LANDLOCK_ACCESS_NET_BIND_TCP LandlockAccessNet = 1 << iota
- LANDLOCK_ACCESS_NET_CONNECT_TCP
-
- _LANDLOCK_ACCESS_NET_DELIM
-)
-
-// String returns a space-separated string of [LandlockAccessNet] flags.
-func (f LandlockAccessNet) String() string {
- switch f {
- case LANDLOCK_ACCESS_NET_BIND_TCP:
- return "bind_tcp"
-
- case LANDLOCK_ACCESS_NET_CONNECT_TCP:
- return "connect_tcp"
-
- default:
- var c []LandlockAccessNet
- for i := LandlockAccessNet(1); i < _LANDLOCK_ACCESS_NET_DELIM; i <<= 1 {
- if f&i != 0 {
- c = append(c, i)
- }
- }
- if len(c) == 0 {
- return "NULL"
- }
- s := make([]string, len(c))
- for i, v := range c {
- s[i] = v.String()
- }
- return strings.Join(s, " ")
- }
-}
-
-// LandlockScope is bitmask of scopes restricting a Landlock domain from accessing outside resources.
-type LandlockScope uint64
-
-const (
- LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET LandlockScope = 1 << iota
- LANDLOCK_SCOPE_SIGNAL
-
- _LANDLOCK_SCOPE_DELIM
-)
-
-// String returns a space-separated string of [LandlockScope] flags.
-func (f LandlockScope) String() string {
- switch f {
- case LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET:
- return "abstract_unix_socket"
-
- case LANDLOCK_SCOPE_SIGNAL:
- return "signal"
-
- default:
- var c []LandlockScope
- for i := LandlockScope(1); i < _LANDLOCK_SCOPE_DELIM; i <<= 1 {
- if f&i != 0 {
- c = append(c, i)
- }
- }
- if len(c) == 0 {
- return "NULL"
- }
- s := make([]string, len(c))
- for i, v := range c {
- s[i] = v.String()
- }
- return strings.Join(s, " ")
- }
-}
-
-// RulesetAttr is equivalent to struct landlock_ruleset_attr.
-type RulesetAttr struct {
- // Bitmask of handled filesystem actions.
- HandledAccessFS LandlockAccessFS
- // Bitmask of handled network actions.
- HandledAccessNet LandlockAccessNet
- // Bitmask of scopes restricting a Landlock domain from accessing outside
- // resources (e.g. IPCs).
- Scoped LandlockScope
-}
-
-// String returns a user-facing description of [RulesetAttr].
-func (rulesetAttr *RulesetAttr) String() string {
- if rulesetAttr == nil {
- return "NULL"
- }
- elems := make([]string, 0, 3)
- if rulesetAttr.HandledAccessFS > 0 {
- elems = append(elems, "fs: "+rulesetAttr.HandledAccessFS.String())
- }
- if rulesetAttr.HandledAccessNet > 0 {
- elems = append(elems, "net: "+rulesetAttr.HandledAccessNet.String())
- }
- if rulesetAttr.Scoped > 0 {
- elems = append(elems, "scoped: "+rulesetAttr.Scoped.String())
- }
- if len(elems) == 0 {
- return "0"
- }
- return strings.Join(elems, ", ")
-}
-
-// Create loads the ruleset into the kernel.
-func (rulesetAttr *RulesetAttr) Create(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(
- ext.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
-}
-
-// LandlockGetABI returns the ABI version supported by the kernel.
-func LandlockGetABI() (int, error) {
- return (*RulesetAttr)(nil).Create(LANDLOCK_CREATE_RULESET_VERSION)
-}
-
-// LandlockRestrictSelf applies a loaded ruleset to the calling thread.
-func LandlockRestrictSelf(rulesetFd int, flags uintptr) error {
- r, _, errno := syscall.Syscall(
- ext.SYS_LANDLOCK_RESTRICT_SELF,
- uintptr(rulesetFd),
- flags,
- 0,
- )
- if r != 0 {
- return errno
- }
- return nil
-}
diff --git a/container/landlock_test.go b/container/landlock_test.go
deleted file mode 100644
index 87dc2496..00000000
--- a/container/landlock_test.go
+++ /dev/null
@@ -1,65 +0,0 @@
-package container_test
-
-import (
- "testing"
- "unsafe"
-
- "hakurei.app/container"
-)
-
-func TestLandlockString(t *testing.T) {
- t.Parallel()
-
- testCases := []struct {
- name string
- rulesetAttr *container.RulesetAttr
- want string
- }{
- {"nil", nil, "NULL"},
- {"zero", new(container.RulesetAttr), "0"},
- {"some", &container.RulesetAttr{Scoped: container.LANDLOCK_SCOPE_SIGNAL}, "scoped: signal"},
- {"set", &container.RulesetAttr{
- HandledAccessFS: container.LANDLOCK_ACCESS_FS_MAKE_SYM | container.LANDLOCK_ACCESS_FS_IOCTL_DEV | container.LANDLOCK_ACCESS_FS_WRITE_FILE,
- HandledAccessNet: container.LANDLOCK_ACCESS_NET_BIND_TCP,
- Scoped: container.LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET | container.LANDLOCK_SCOPE_SIGNAL,
- }, "fs: write_file make_sym fs_ioctl_dev, net: bind_tcp, scoped: abstract_unix_socket signal"},
- {"all", &container.RulesetAttr{
- HandledAccessFS: container.LANDLOCK_ACCESS_FS_EXECUTE |
- container.LANDLOCK_ACCESS_FS_WRITE_FILE |
- container.LANDLOCK_ACCESS_FS_READ_FILE |
- container.LANDLOCK_ACCESS_FS_READ_DIR |
- container.LANDLOCK_ACCESS_FS_REMOVE_DIR |
- container.LANDLOCK_ACCESS_FS_REMOVE_FILE |
- container.LANDLOCK_ACCESS_FS_MAKE_CHAR |
- container.LANDLOCK_ACCESS_FS_MAKE_DIR |
- container.LANDLOCK_ACCESS_FS_MAKE_REG |
- container.LANDLOCK_ACCESS_FS_MAKE_SOCK |
- container.LANDLOCK_ACCESS_FS_MAKE_FIFO |
- container.LANDLOCK_ACCESS_FS_MAKE_BLOCK |
- container.LANDLOCK_ACCESS_FS_MAKE_SYM |
- container.LANDLOCK_ACCESS_FS_REFER |
- container.LANDLOCK_ACCESS_FS_TRUNCATE |
- container.LANDLOCK_ACCESS_FS_IOCTL_DEV,
- HandledAccessNet: container.LANDLOCK_ACCESS_NET_BIND_TCP |
- container.LANDLOCK_ACCESS_NET_CONNECT_TCP,
- Scoped: container.LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET |
- container.LANDLOCK_SCOPE_SIGNAL,
- }, "fs: execute write_file read_file read_dir remove_dir remove_file make_char make_dir make_reg make_sock make_fifo make_block make_sym fs_refer fs_truncate fs_ioctl_dev, net: bind_tcp connect_tcp, scoped: abstract_unix_socket signal"},
- }
- for _, tc := range testCases {
- t.Run(tc.name, func(t *testing.T) {
- t.Parallel()
- if got := tc.rulesetAttr.String(); got != tc.want {
- t.Errorf("String: %s, want %s", got, tc.want)
- }
- })
- }
-}
-
-func TestLandlockAttrSize(t *testing.T) {
- t.Parallel()
- want := 24
- if got := unsafe.Sizeof(container.RulesetAttr{}); got != uintptr(want) {
- t.Errorf("Sizeof: %d, want %d", got, want)
- }
-}