Compare commits
1 Commits
ff58de323a
...
40028f3c03
Author | SHA1 | Date | |
---|---|---|---|
40028f3c03 |
@ -189,10 +189,11 @@ func (p *Container) Start() error {
|
||||
|
||||
// landlock: depends on per-thread state but acts on a process group
|
||||
{
|
||||
rulesetAttr := &RulesetAttr{Scoped: LANDLOCK_SCOPE_SIGNAL}
|
||||
scoped := LANDLOCK_SCOPE_SIGNAL
|
||||
if !p.HostAbstract {
|
||||
rulesetAttr.Scoped |= LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET
|
||||
scoped |= LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET
|
||||
}
|
||||
rulesetAttr := NewRulesetAttr(scoped)
|
||||
|
||||
if abi, err := LandlockGetABI(); err != nil {
|
||||
if p.HostAbstract {
|
||||
@ -213,11 +214,12 @@ func (p *Container) Start() error {
|
||||
msg.Verbosef("landlock abi version %d", abi)
|
||||
}
|
||||
|
||||
if rulesetFd, err := rulesetAttr.Create(0); err != nil {
|
||||
msg.Verbosef("creating landlock ruleset scoped %#x", int(scoped))
|
||||
if rulesetFd, err := LandlockCreateRuleset(&rulesetAttr, 0); err != nil {
|
||||
return wrapErrSuffix(err,
|
||||
"cannot create landlock ruleset:")
|
||||
} else {
|
||||
msg.Verbosef("enforcing landlock ruleset %s", rulesetAttr)
|
||||
msg.Verbose("enforcing landlock ruleset")
|
||||
if err = LandlockRestrictSelf(rulesetFd, 0); err != nil {
|
||||
_ = Close(rulesetFd)
|
||||
return wrapErrSuffix(err,
|
||||
|
@ -1,210 +1,30 @@
|
||||
package container
|
||||
|
||||
/*
|
||||
#include <linux/landlock.h>
|
||||
#include <sys/syscall.h>
|
||||
*/
|
||||
import "C"
|
||||
import (
|
||||
"strings"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"hakurei.app/container/seccomp"
|
||||
)
|
||||
|
||||
// include/uapi/linux/landlock.h
|
||||
|
||||
const (
|
||||
LANDLOCK_CREATE_RULESET_VERSION = 1 << iota
|
||||
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 LandlockAccessFS uintptr
|
||||
type RulesetAttr = C.struct_landlock_ruleset_attr
|
||||
|
||||
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
|
||||
func NewRulesetAttr(scoped int) RulesetAttr { return RulesetAttr{scoped: C.__u64(scoped)} }
|
||||
|
||||
_LANDLOCK_ACCESS_FS_DELIM
|
||||
)
|
||||
/* TODO: remove everything above this */
|
||||
|
||||
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, " ")
|
||||
}
|
||||
}
|
||||
|
||||
type LandlockAccessNet uintptr
|
||||
|
||||
const (
|
||||
LANDLOCK_ACCESS_NET_BIND_TCP LandlockAccessNet = 1 << iota
|
||||
LANDLOCK_ACCESS_NET_CONNECT_TCP
|
||||
|
||||
_LANDLOCK_ACCESS_NET_DELIM
|
||||
)
|
||||
|
||||
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, " ")
|
||||
}
|
||||
}
|
||||
|
||||
type LandlockScope uintptr
|
||||
|
||||
const (
|
||||
LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET LandlockScope = 1 << iota
|
||||
LANDLOCK_SCOPE_SIGNAL
|
||||
|
||||
_LANDLOCK_SCOPE_DELIM
|
||||
)
|
||||
|
||||
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, " ")
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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, ", ")
|
||||
}
|
||||
|
||||
func (rulesetAttr *RulesetAttr) Create(flags uintptr) (fd int, err error) {
|
||||
func LandlockCreateRuleset(rulesetAttr *RulesetAttr, flags uintptr) (fd int, err error) {
|
||||
var pointer, size uintptr
|
||||
// NULL needed for abi version
|
||||
if rulesetAttr != nil {
|
||||
@ -227,7 +47,7 @@ func (rulesetAttr *RulesetAttr) Create(flags uintptr) (fd int, err error) {
|
||||
}
|
||||
|
||||
func LandlockGetABI() (int, error) {
|
||||
return (*RulesetAttr)(nil).Create(LANDLOCK_CREATE_RULESET_VERSION)
|
||||
return LandlockCreateRuleset(nil, LANDLOCK_CREATE_RULESET_VERSION)
|
||||
}
|
||||
|
||||
func LandlockRestrictSelf(rulesetFd int, flags uintptr) error {
|
||||
|
@ -1,61 +0,0 @@
|
||||
package container_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"unsafe"
|
||||
|
||||
"hakurei.app/container"
|
||||
)
|
||||
|
||||
func TestLandlockString(t *testing.T) {
|
||||
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) {
|
||||
if got := tc.rulesetAttr.String(); got != tc.want {
|
||||
t.Errorf("String: %s, want %s", got, tc.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestLandlockAttrSize(t *testing.T) {
|
||||
want := 24
|
||||
if got := unsafe.Sizeof(container.RulesetAttr{}); got != uintptr(want) {
|
||||
t.Errorf("Sizeof: %d, want %d", got, want)
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user