Compare commits
No commits in common. "d2f9a9b83b1c43304081493eb2305309be1dd504" and "0e957cc9c1578ec5c2dd56b664c6731e9d032921" have entirely different histories.
d2f9a9b83b
...
0e957cc9c1
2
.gitignore
vendored
2
.gitignore
vendored
@ -26,7 +26,7 @@ go.work.sum
|
|||||||
.vscode
|
.vscode
|
||||||
|
|
||||||
# go generate
|
# go generate
|
||||||
/cmd/hakurei/LICENSE
|
security-context-v1-protocol.*
|
||||||
|
|
||||||
# release
|
# release
|
||||||
/dist/hakurei-*
|
/dist/hakurei-*
|
2
LICENSE
2
LICENSE
@ -1,4 +1,4 @@
|
|||||||
Copyright (c) 2024-2025 Ophestra
|
Copyright (c) 2024 Ophestra Umiker
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
@ -7,8 +7,8 @@
|
|||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<a href="https://pkg.go.dev/hakurei.app"><img src="https://pkg.go.dev/badge/hakurei.app.svg" alt="Go Reference" /></a>
|
<a href="https://pkg.go.dev/git.gensokyo.uk/security/hakurei"><img src="https://pkg.go.dev/badge/git.gensokyo.uk/security/hakurei.svg" alt="Go Reference" /></a>
|
||||||
<a href="https://goreportcard.com/report/hakurei.app"><img src="https://goreportcard.com/badge/hakurei.app" alt="Go Report Card" /></a>
|
<a href="https://goreportcard.com/report/git.gensokyo.uk/security/hakurei"><img src="https://goreportcard.com/badge/git.gensokyo.uk/security/hakurei" alt="Go Report Card" /></a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
Hakurei is a tool for running sandboxed graphical applications as dedicated subordinate users on the Linux kernel.
|
Hakurei is a tool for running sandboxed graphical applications as dedicated subordinate users on the Linux kernel.
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
#include "libacl-helper.h"
|
#include "acl-update.h"
|
||||||
#include <acl/libacl.h>
|
#include <acl/libacl.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <sys/acl.h>
|
#include <sys/acl.h>
|
||||||
|
|
||||||
int hakurei_acl_update_file_by_uid(const char *path_p, uid_t uid,
|
int hakurei_acl_update_file_by_uid(const char *path_p, uid_t uid, acl_perm_t *perms,
|
||||||
acl_perm_t *perms, size_t plen) {
|
size_t plen) {
|
||||||
int ret = -1;
|
int ret = -1;
|
||||||
bool v;
|
bool v;
|
||||||
int i;
|
int i;
|
@ -1,4 +1,4 @@
|
|||||||
#include <sys/acl.h>
|
#include <sys/acl.h>
|
||||||
|
|
||||||
int hakurei_acl_update_file_by_uid(const char *path_p, uid_t uid,
|
int hakurei_acl_update_file_by_uid(const char *path_p, uid_t uid, acl_perm_t *perms,
|
||||||
acl_perm_t *perms, size_t plen);
|
size_t plen);
|
@ -4,7 +4,7 @@ package acl
|
|||||||
/*
|
/*
|
||||||
#cgo linux pkg-config: --static libacl
|
#cgo linux pkg-config: --static libacl
|
||||||
|
|
||||||
#include "libacl-helper.h"
|
#include "acl-update.h"
|
||||||
*/
|
*/
|
||||||
import "C"
|
import "C"
|
||||||
|
|
@ -7,7 +7,7 @@ import (
|
|||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"hakurei.app/system/acl"
|
"git.gensokyo.uk/security/hakurei/acl"
|
||||||
)
|
)
|
||||||
|
|
||||||
const testFileName = "acl.test"
|
const testFileName = "acl.test"
|
@ -1,9 +0,0 @@
|
|||||||
package state_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"hakurei.app/cmd/hakurei/internal/state"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestMulti(t *testing.T) { testStore(t, state.NewMulti(t.TempDir())) }
|
|
@ -1,51 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
// this works around go:embed '..' limitation
|
|
||||||
//go:generate cp ../../LICENSE .
|
|
||||||
|
|
||||||
import (
|
|
||||||
_ "embed"
|
|
||||||
"errors"
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"hakurei.app/container"
|
|
||||||
"hakurei.app/internal"
|
|
||||||
"hakurei.app/internal/hlog"
|
|
||||||
"hakurei.app/internal/sys"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
errSuccess = errors.New("success")
|
|
||||||
|
|
||||||
//go:embed LICENSE
|
|
||||||
license string
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() { hlog.Prepare("hakurei") }
|
|
||||||
|
|
||||||
var std sys.State = new(sys.Std)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
// early init path, skips root check and duplicate PR_SET_DUMPABLE
|
|
||||||
container.TryArgv0(hlog.Output{}, hlog.Prepare, internal.InstallOutput)
|
|
||||||
|
|
||||||
if err := container.SetDumpable(container.SUID_DUMP_DISABLE); err != nil {
|
|
||||||
log.Printf("cannot set SUID_DUMP_DISABLE: %s", err)
|
|
||||||
// not fatal: this program runs as the privileged user
|
|
||||||
}
|
|
||||||
|
|
||||||
if os.Geteuid() == 0 {
|
|
||||||
log.Fatal("this program must not run as root")
|
|
||||||
}
|
|
||||||
|
|
||||||
buildCommand(os.Stderr).MustParse(os.Args[1:], func(err error) {
|
|
||||||
hlog.Verbosef("command returned %v", err)
|
|
||||||
if errors.Is(err, errSuccess) {
|
|
||||||
hlog.BeforeExit()
|
|
||||||
os.Exit(0)
|
|
||||||
}
|
|
||||||
// this catches faulty command handlers that fail to return before this point
|
|
||||||
})
|
|
||||||
log.Fatal("unreachable")
|
|
||||||
}
|
|
@ -6,10 +6,10 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
|
|
||||||
"hakurei.app/container/seccomp"
|
"git.gensokyo.uk/security/hakurei/dbus"
|
||||||
"hakurei.app/hst"
|
"git.gensokyo.uk/security/hakurei/hst"
|
||||||
"hakurei.app/system"
|
"git.gensokyo.uk/security/hakurei/sandbox/seccomp"
|
||||||
"hakurei.app/system/dbus"
|
"git.gensokyo.uk/security/hakurei/system"
|
||||||
)
|
)
|
||||||
|
|
||||||
type appInfo struct {
|
type appInfo struct {
|
||||||
@ -115,10 +115,10 @@ func (app *appInfo) toFst(pathSet *appPathSet, argv []string, flagDropShell bool
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
if app.Multiarch {
|
if app.Multiarch {
|
||||||
config.Container.SeccompFlags |= seccomp.AllowMultiarch
|
config.Container.Seccomp |= seccomp.FilterMultiarch
|
||||||
}
|
}
|
||||||
if app.Bluetooth {
|
if app.Bluetooth {
|
||||||
config.Container.SeccompFlags |= seccomp.AllowBluetooth
|
config.Container.Seccomp |= seccomp.FilterBluetooth
|
||||||
}
|
}
|
||||||
return config
|
return config
|
||||||
}
|
}
|
||||||
|
@ -10,10 +10,10 @@ import (
|
|||||||
"path"
|
"path"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"hakurei.app/command"
|
"git.gensokyo.uk/security/hakurei/command"
|
||||||
"hakurei.app/hst"
|
"git.gensokyo.uk/security/hakurei/hst"
|
||||||
"hakurei.app/internal"
|
"git.gensokyo.uk/security/hakurei/internal"
|
||||||
"hakurei.app/internal/hlog"
|
"git.gensokyo.uk/security/hakurei/internal/hlog"
|
||||||
)
|
)
|
||||||
|
|
||||||
const shellPath = "/run/current-system/sw/bin/bash"
|
const shellPath = "/run/current-system/sw/bin/bash"
|
||||||
@ -42,7 +42,7 @@ func main() {
|
|||||||
flagVerbose bool
|
flagVerbose bool
|
||||||
flagDropShell bool
|
flagDropShell bool
|
||||||
)
|
)
|
||||||
c := command.New(os.Stderr, log.Printf, "planterette", func([]string) error { internal.InstallOutput(flagVerbose); return nil }).
|
c := command.New(os.Stderr, log.Printf, "planterette", func([]string) error { internal.InstallFmsg(flagVerbose); return nil }).
|
||||||
Flag(&flagVerbose, "v", command.BoolFlag(false), "Print debug messages to the console").
|
Flag(&flagVerbose, "v", command.BoolFlag(false), "Print debug messages to the console").
|
||||||
Flag(&flagDropShell, "s", command.BoolFlag(false), "Drop to a shell in place of next hakurei action")
|
Flag(&flagDropShell, "s", command.BoolFlag(false), "Drop to a shell in place of next hakurei action")
|
||||||
|
|
||||||
|
@ -8,8 +8,8 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
|
||||||
"hakurei.app/hst"
|
"git.gensokyo.uk/security/hakurei/hst"
|
||||||
"hakurei.app/internal/hlog"
|
"git.gensokyo.uk/security/hakurei/internal/hlog"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -9,9 +9,9 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
|
||||||
"hakurei.app/hst"
|
"git.gensokyo.uk/security/hakurei/hst"
|
||||||
"hakurei.app/internal"
|
"git.gensokyo.uk/security/hakurei/internal"
|
||||||
"hakurei.app/internal/hlog"
|
"git.gensokyo.uk/security/hakurei/internal/hlog"
|
||||||
)
|
)
|
||||||
|
|
||||||
var hakureiPath = internal.MustHakureiPath()
|
var hakureiPath = internal.MustHakureiPath()
|
||||||
|
@ -5,9 +5,9 @@ import (
|
|||||||
"path"
|
"path"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"hakurei.app/container/seccomp"
|
"git.gensokyo.uk/security/hakurei/hst"
|
||||||
"hakurei.app/hst"
|
"git.gensokyo.uk/security/hakurei/internal"
|
||||||
"hakurei.app/internal"
|
"git.gensokyo.uk/security/hakurei/sandbox/seccomp"
|
||||||
)
|
)
|
||||||
|
|
||||||
func withNixDaemon(
|
func withNixDaemon(
|
||||||
@ -46,7 +46,7 @@ func withNixDaemon(
|
|||||||
Hostname: formatHostname(app.Name) + "-" + action,
|
Hostname: formatHostname(app.Name) + "-" + action,
|
||||||
Userns: true, // nix sandbox requires userns
|
Userns: true, // nix sandbox requires userns
|
||||||
Net: net,
|
Net: net,
|
||||||
SeccompFlags: seccomp.AllowMultiarch,
|
Seccomp: seccomp.FilterMultiarch,
|
||||||
Tty: dropShell,
|
Tty: dropShell,
|
||||||
Filesystem: []*hst.FilesystemConfig{
|
Filesystem: []*hst.FilesystemConfig{
|
||||||
{Src: pathSet.nixPath, Dst: "/nix", Write: true, Must: true},
|
{Src: pathSet.nixPath, Dst: "/nix", Write: true, Must: true},
|
||||||
@ -86,7 +86,7 @@ func withCacheDir(
|
|||||||
|
|
||||||
Container: &hst.ContainerConfig{
|
Container: &hst.ContainerConfig{
|
||||||
Hostname: formatHostname(app.Name) + "-" + action,
|
Hostname: formatHostname(app.Name) + "-" + action,
|
||||||
SeccompFlags: seccomp.AllowMultiarch,
|
Seccomp: seccomp.FilterMultiarch,
|
||||||
Tty: dropShell,
|
Tty: dropShell,
|
||||||
Filesystem: []*hst.FilesystemConfig{
|
Filesystem: []*hst.FilesystemConfig{
|
||||||
{Src: path.Join(workDir, "nix"), Dst: "/nix", Must: true},
|
{Src: path.Join(workDir, "nix"), Dst: "/nix", Must: true},
|
||||||
|
@ -3,7 +3,7 @@ package command_test
|
|||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"hakurei.app/command"
|
"git.gensokyo.uk/security/hakurei/command"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestBuild(t *testing.T) {
|
func TestBuild(t *testing.T) {
|
||||||
|
@ -10,7 +10,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"hakurei.app/command"
|
"git.gensokyo.uk/security/hakurei/command"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestParse(t *testing.T) {
|
func TestParse(t *testing.T) {
|
||||||
|
@ -1,130 +0,0 @@
|
|||||||
#ifndef _GNU_SOURCE
|
|
||||||
#define _GNU_SOURCE /* CLONE_NEWUSER */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "libseccomp-helper.h"
|
|
||||||
#include <assert.h>
|
|
||||||
#include <errno.h>
|
|
||||||
#include <sys/socket.h>
|
|
||||||
|
|
||||||
#define LEN(arr) (sizeof(arr) / sizeof((arr)[0]))
|
|
||||||
|
|
||||||
int32_t hakurei_export_filter(int *ret_p, int fd, uint32_t arch,
|
|
||||||
uint32_t multiarch,
|
|
||||||
struct hakurei_syscall_rule *rules,
|
|
||||||
size_t rules_sz, hakurei_export_flag flags) {
|
|
||||||
int i;
|
|
||||||
int last_allowed_family;
|
|
||||||
int disallowed;
|
|
||||||
struct hakurei_syscall_rule *rule;
|
|
||||||
|
|
||||||
int32_t res = 0; /* refer to resPrefix for message */
|
|
||||||
|
|
||||||
/* Blocklist all but unix, inet, inet6 and netlink */
|
|
||||||
struct {
|
|
||||||
int family;
|
|
||||||
hakurei_export_flag flags_mask;
|
|
||||||
} socket_family_allowlist[] = {
|
|
||||||
/* NOTE: Keep in numerical order */
|
|
||||||
{AF_UNSPEC, 0},
|
|
||||||
{AF_LOCAL, 0},
|
|
||||||
{AF_INET, 0},
|
|
||||||
{AF_INET6, 0},
|
|
||||||
{AF_NETLINK, 0},
|
|
||||||
{AF_CAN, HAKUREI_EXPORT_CAN},
|
|
||||||
{AF_BLUETOOTH, HAKUREI_EXPORT_BLUETOOTH},
|
|
||||||
};
|
|
||||||
|
|
||||||
scmp_filter_ctx ctx = seccomp_init(SCMP_ACT_ALLOW);
|
|
||||||
if (ctx == NULL) {
|
|
||||||
res = 1;
|
|
||||||
goto out;
|
|
||||||
} else
|
|
||||||
errno = 0;
|
|
||||||
|
|
||||||
/* We only really need to handle arches on multiarch systems.
|
|
||||||
* If only one arch is supported the default is fine */
|
|
||||||
if (arch != 0) {
|
|
||||||
/* This *adds* the target arch, instead of replacing the
|
|
||||||
* native one. This is not ideal, because we'd like to only
|
|
||||||
* allow the target arch, but we can't really disallow the
|
|
||||||
* native arch at this point, because then bubblewrap
|
|
||||||
* couldn't continue running. */
|
|
||||||
*ret_p = seccomp_arch_add(ctx, arch);
|
|
||||||
if (*ret_p < 0 && *ret_p != -EEXIST) {
|
|
||||||
res = 2;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (flags & HAKUREI_EXPORT_MULTIARCH && multiarch != 0) {
|
|
||||||
*ret_p = seccomp_arch_add(ctx, multiarch);
|
|
||||||
if (*ret_p < 0 && *ret_p != -EEXIST) {
|
|
||||||
res = 3;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < rules_sz; i++) {
|
|
||||||
rule = &rules[i];
|
|
||||||
assert(rule->m_errno == EPERM || rule->m_errno == ENOSYS);
|
|
||||||
|
|
||||||
if (rule->arg)
|
|
||||||
*ret_p = seccomp_rule_add(ctx, SCMP_ACT_ERRNO(rule->m_errno),
|
|
||||||
rule->syscall, 1, *rule->arg);
|
|
||||||
else
|
|
||||||
*ret_p = seccomp_rule_add(ctx, SCMP_ACT_ERRNO(rule->m_errno),
|
|
||||||
rule->syscall, 0);
|
|
||||||
|
|
||||||
if (*ret_p == -EFAULT) {
|
|
||||||
res = 4;
|
|
||||||
goto out;
|
|
||||||
} else if (*ret_p < 0) {
|
|
||||||
res = 5;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Socket filtering doesn't work on e.g. i386, so ignore failures here
|
|
||||||
* However, we need to user seccomp_rule_add_exact to avoid libseccomp doing
|
|
||||||
* something else: https://github.com/seccomp/libseccomp/issues/8 */
|
|
||||||
last_allowed_family = -1;
|
|
||||||
for (i = 0; i < LEN(socket_family_allowlist); i++) {
|
|
||||||
if (socket_family_allowlist[i].flags_mask != 0 &&
|
|
||||||
(socket_family_allowlist[i].flags_mask & flags) !=
|
|
||||||
socket_family_allowlist[i].flags_mask)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
for (disallowed = last_allowed_family + 1;
|
|
||||||
disallowed < socket_family_allowlist[i].family; disallowed++) {
|
|
||||||
/* Blocklist the in-between valid families */
|
|
||||||
seccomp_rule_add_exact(ctx, SCMP_ACT_ERRNO(EAFNOSUPPORT),
|
|
||||||
SCMP_SYS(socket), 1,
|
|
||||||
SCMP_A0(SCMP_CMP_EQ, disallowed));
|
|
||||||
}
|
|
||||||
last_allowed_family = socket_family_allowlist[i].family;
|
|
||||||
}
|
|
||||||
/* Blocklist the rest */
|
|
||||||
seccomp_rule_add_exact(ctx, SCMP_ACT_ERRNO(EAFNOSUPPORT), SCMP_SYS(socket), 1,
|
|
||||||
SCMP_A0(SCMP_CMP_GE, last_allowed_family + 1));
|
|
||||||
|
|
||||||
if (fd < 0) {
|
|
||||||
*ret_p = seccomp_load(ctx);
|
|
||||||
if (*ret_p != 0) {
|
|
||||||
res = 7;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
*ret_p = seccomp_export_bpf(ctx, fd);
|
|
||||||
if (*ret_p != 0) {
|
|
||||||
res = 6;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
out:
|
|
||||||
if (ctx)
|
|
||||||
seccomp_release(ctx);
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
@ -1,24 +0,0 @@
|
|||||||
#include <seccomp.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
#if (SCMP_VER_MAJOR < 2) || (SCMP_VER_MAJOR == 2 && SCMP_VER_MINOR < 5) || \
|
|
||||||
(SCMP_VER_MAJOR == 2 && SCMP_VER_MINOR == 5 && SCMP_VER_MICRO < 1)
|
|
||||||
#error This package requires libseccomp >= v2.5.1
|
|
||||||
#endif
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
HAKUREI_EXPORT_MULTIARCH = 1 << 0,
|
|
||||||
HAKUREI_EXPORT_CAN = 1 << 1,
|
|
||||||
HAKUREI_EXPORT_BLUETOOTH = 1 << 2,
|
|
||||||
} hakurei_export_flag;
|
|
||||||
|
|
||||||
struct hakurei_syscall_rule {
|
|
||||||
int syscall;
|
|
||||||
int m_errno;
|
|
||||||
struct scmp_arg_cmp *arg;
|
|
||||||
};
|
|
||||||
|
|
||||||
int32_t hakurei_export_filter(int *ret_p, int fd, uint32_t arch,
|
|
||||||
uint32_t multiarch,
|
|
||||||
struct hakurei_syscall_rule *rules,
|
|
||||||
size_t rules_sz, hakurei_export_flag flags);
|
|
@ -1,188 +0,0 @@
|
|||||||
package seccomp
|
|
||||||
|
|
||||||
/*
|
|
||||||
#cgo linux pkg-config: --static libseccomp
|
|
||||||
|
|
||||||
#include <libseccomp-helper.h>
|
|
||||||
*/
|
|
||||||
import "C"
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"runtime"
|
|
||||||
"syscall"
|
|
||||||
"unsafe"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
ErrInvalidRules = errors.New("invalid native rules slice")
|
|
||||||
)
|
|
||||||
|
|
||||||
// LibraryError represents a libseccomp error.
|
|
||||||
type LibraryError struct {
|
|
||||||
Prefix string
|
|
||||||
Seccomp syscall.Errno
|
|
||||||
Errno error
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *LibraryError) Error() string {
|
|
||||||
if e.Seccomp == 0 {
|
|
||||||
if e.Errno == nil {
|
|
||||||
panic("invalid libseccomp error")
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("%s: %s", e.Prefix, e.Errno)
|
|
||||||
}
|
|
||||||
if e.Errno == nil {
|
|
||||||
return fmt.Sprintf("%s: %s", e.Prefix, e.Seccomp)
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("%s: %s (%s)", e.Prefix, e.Seccomp, e.Errno)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *LibraryError) Is(err error) bool {
|
|
||||||
if e == nil {
|
|
||||||
return err == nil
|
|
||||||
}
|
|
||||||
if ef, ok := err.(*LibraryError); ok {
|
|
||||||
return *e == *ef
|
|
||||||
}
|
|
||||||
return (e.Seccomp != 0 && errors.Is(err, e.Seccomp)) ||
|
|
||||||
(e.Errno != nil && errors.Is(err, e.Errno))
|
|
||||||
}
|
|
||||||
|
|
||||||
type (
|
|
||||||
ScmpSyscall = C.int
|
|
||||||
ScmpErrno = C.int
|
|
||||||
)
|
|
||||||
|
|
||||||
// A NativeRule specifies an arch-specific action taken by seccomp under certain conditions.
|
|
||||||
type NativeRule struct {
|
|
||||||
// Syscall is the arch-dependent syscall number to act against.
|
|
||||||
Syscall ScmpSyscall
|
|
||||||
// Errno is the errno value to return when the condition is satisfied.
|
|
||||||
Errno ScmpErrno
|
|
||||||
// Arg is the optional struct scmp_arg_cmp passed to libseccomp.
|
|
||||||
Arg *ScmpArgCmp
|
|
||||||
}
|
|
||||||
|
|
||||||
type ExportFlag = C.hakurei_export_flag
|
|
||||||
|
|
||||||
const (
|
|
||||||
// AllowMultiarch allows multiarch/emulation.
|
|
||||||
AllowMultiarch ExportFlag = C.HAKUREI_EXPORT_MULTIARCH
|
|
||||||
// AllowCAN allows AF_CAN.
|
|
||||||
AllowCAN ExportFlag = C.HAKUREI_EXPORT_CAN
|
|
||||||
// AllowBluetooth allows AF_BLUETOOTH.
|
|
||||||
AllowBluetooth ExportFlag = C.HAKUREI_EXPORT_BLUETOOTH
|
|
||||||
)
|
|
||||||
|
|
||||||
var resPrefix = [...]string{
|
|
||||||
0: "",
|
|
||||||
1: "seccomp_init failed",
|
|
||||||
2: "seccomp_arch_add failed",
|
|
||||||
3: "seccomp_arch_add failed (multiarch)",
|
|
||||||
4: "internal libseccomp failure",
|
|
||||||
5: "seccomp_rule_add failed",
|
|
||||||
6: "seccomp_export_bpf failed",
|
|
||||||
7: "seccomp_load failed",
|
|
||||||
}
|
|
||||||
|
|
||||||
// Export streams filter contents to fd, or installs it to the current process if fd < 0.
|
|
||||||
func Export(fd int, rules []NativeRule, flags ExportFlag) error {
|
|
||||||
if len(rules) == 0 {
|
|
||||||
return ErrInvalidRules
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
arch C.uint32_t = 0
|
|
||||||
multiarch C.uint32_t = 0
|
|
||||||
)
|
|
||||||
switch runtime.GOARCH {
|
|
||||||
case "386":
|
|
||||||
arch = C.SCMP_ARCH_X86
|
|
||||||
case "amd64":
|
|
||||||
arch = C.SCMP_ARCH_X86_64
|
|
||||||
multiarch = C.SCMP_ARCH_X86
|
|
||||||
case "arm":
|
|
||||||
arch = C.SCMP_ARCH_ARM
|
|
||||||
case "arm64":
|
|
||||||
arch = C.SCMP_ARCH_AARCH64
|
|
||||||
multiarch = C.SCMP_ARCH_ARM
|
|
||||||
}
|
|
||||||
|
|
||||||
var ret C.int
|
|
||||||
|
|
||||||
rulesPinner := new(runtime.Pinner)
|
|
||||||
for i := range rules {
|
|
||||||
rule := &rules[i]
|
|
||||||
rulesPinner.Pin(rule)
|
|
||||||
if rule.Arg != nil {
|
|
||||||
rulesPinner.Pin(rule.Arg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
res, err := C.hakurei_export_filter(
|
|
||||||
&ret, C.int(fd),
|
|
||||||
arch, multiarch,
|
|
||||||
(*C.struct_hakurei_syscall_rule)(unsafe.Pointer(&rules[0])),
|
|
||||||
C.size_t(len(rules)),
|
|
||||||
flags,
|
|
||||||
)
|
|
||||||
rulesPinner.Unpin()
|
|
||||||
|
|
||||||
if prefix := resPrefix[res]; prefix != "" {
|
|
||||||
return &LibraryError{
|
|
||||||
prefix,
|
|
||||||
-syscall.Errno(ret),
|
|
||||||
err,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// ScmpCompare is the equivalent of scmp_compare;
|
|
||||||
// Comparison operators
|
|
||||||
type ScmpCompare = C.enum_scmp_compare
|
|
||||||
|
|
||||||
const (
|
|
||||||
_SCMP_CMP_MIN = C._SCMP_CMP_MIN
|
|
||||||
|
|
||||||
// not equal
|
|
||||||
SCMP_CMP_NE = C.SCMP_CMP_NE
|
|
||||||
// less than
|
|
||||||
SCMP_CMP_LT = C.SCMP_CMP_LT
|
|
||||||
// less than or equal
|
|
||||||
SCMP_CMP_LE = C.SCMP_CMP_LE
|
|
||||||
// equal
|
|
||||||
SCMP_CMP_EQ = C.SCMP_CMP_EQ
|
|
||||||
// greater than or equal
|
|
||||||
SCMP_CMP_GE = C.SCMP_CMP_GE
|
|
||||||
// greater than
|
|
||||||
SCMP_CMP_GT = C.SCMP_CMP_GT
|
|
||||||
// masked equality
|
|
||||||
SCMP_CMP_MASKED_EQ = C.SCMP_CMP_MASKED_EQ
|
|
||||||
|
|
||||||
_SCMP_CMP_MAX = C._SCMP_CMP_MAX
|
|
||||||
)
|
|
||||||
|
|
||||||
// ScmpDatum is the equivalent of scmp_datum_t;
|
|
||||||
// Argument datum
|
|
||||||
type ScmpDatum uint64
|
|
||||||
|
|
||||||
// ScmpArgCmp is the equivalent of struct scmp_arg_cmp;
|
|
||||||
// Argument / Value comparison definition
|
|
||||||
type ScmpArgCmp struct {
|
|
||||||
// argument number, starting at 0
|
|
||||||
Arg C.uint
|
|
||||||
// the comparison op, e.g. SCMP_CMP_*
|
|
||||||
Op ScmpCompare
|
|
||||||
|
|
||||||
DatumA, DatumB ScmpDatum
|
|
||||||
}
|
|
||||||
|
|
||||||
// only used for testing
|
|
||||||
func syscallResolveName(s string) (trap int) {
|
|
||||||
v := C.CString(s)
|
|
||||||
trap = int(C.seccomp_syscall_resolve_name(v))
|
|
||||||
C.free(unsafe.Pointer(v))
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
@ -1,83 +0,0 @@
|
|||||||
#!/usr/bin/env perl
|
|
||||||
# Copyright 2009 The Go Authors. All rights reserved.
|
|
||||||
# Use of this source code is governed by a BSD-style
|
|
||||||
# license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
use strict;
|
|
||||||
|
|
||||||
my $command = "mksysnum_linux.pl ". join(' ', @ARGV);
|
|
||||||
|
|
||||||
print <<EOF;
|
|
||||||
// $command
|
|
||||||
// Code generated by the command above; DO NOT EDIT.
|
|
||||||
|
|
||||||
package seccomp
|
|
||||||
|
|
||||||
import . "syscall"
|
|
||||||
|
|
||||||
var syscallNum = map[string]int{
|
|
||||||
EOF
|
|
||||||
|
|
||||||
my $offset = 0;
|
|
||||||
my $state = -1;
|
|
||||||
|
|
||||||
sub fmt {
|
|
||||||
my ($name, $num) = @_;
|
|
||||||
if($num > 999){
|
|
||||||
# ignore deprecated syscalls that are no longer implemented
|
|
||||||
# https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/include/uapi/asm-generic/unistd.h?id=refs/heads/master#n716
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
(my $name_upper = $name) =~ y/a-z/A-Z/;
|
|
||||||
$num = $num + $offset;
|
|
||||||
if($num > 302){ # not wired in Go standard library
|
|
||||||
if($state < 0){
|
|
||||||
print " \"$name\": SYS_$name_upper,\n";
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
print " SYS_$name_upper = $num;\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
elsif($state < 0){
|
|
||||||
print " \"$name\": SYS_$name_upper,\n";
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
GENERATE:
|
|
||||||
|
|
||||||
my $prev;
|
|
||||||
open(GCC, "gcc -E -dD $ARGV[0] |") || die "can't run gcc";
|
|
||||||
while(<GCC>){
|
|
||||||
if(/^#define __NR_Linux\s+([0-9]+)/){
|
|
||||||
# mips/mips64: extract offset
|
|
||||||
$offset = $1;
|
|
||||||
}
|
|
||||||
elsif(/^#define __NR_syscalls\s+/) {
|
|
||||||
# ignore redefinitions of __NR_syscalls
|
|
||||||
}
|
|
||||||
elsif(/^#define __NR_(\w+)\s+([0-9]+)/){
|
|
||||||
$prev = $2;
|
|
||||||
fmt($1, $2);
|
|
||||||
}
|
|
||||||
elsif(/^#define __NR3264_(\w+)\s+([0-9]+)/){
|
|
||||||
$prev = $2;
|
|
||||||
fmt($1, $2);
|
|
||||||
}
|
|
||||||
elsif(/^#define __NR_(\w+)\s+\(\w+\+\s*([0-9]+)\)/){
|
|
||||||
fmt($1, $prev+$2)
|
|
||||||
}
|
|
||||||
elsif(/^#define __NR_(\w+)\s+\(__NR_Linux \+ ([0-9]+)/){
|
|
||||||
fmt($1, $2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if($state < 0){
|
|
||||||
$state = $state + 1;
|
|
||||||
print "}\n\nconst (\n";
|
|
||||||
goto GENERATE;
|
|
||||||
}
|
|
||||||
|
|
||||||
print ")";
|
|
@ -1,229 +0,0 @@
|
|||||||
package seccomp
|
|
||||||
|
|
||||||
/* flatpak commit 4c3bf179e2e4a2a298cd1db1d045adaf3f564532 */
|
|
||||||
|
|
||||||
import (
|
|
||||||
. "syscall"
|
|
||||||
)
|
|
||||||
|
|
||||||
type FilterPreset int
|
|
||||||
|
|
||||||
const (
|
|
||||||
// PresetExt are project-specific extensions.
|
|
||||||
PresetExt FilterPreset = 1 << iota
|
|
||||||
// PresetDenyNS denies namespace setup syscalls.
|
|
||||||
PresetDenyNS
|
|
||||||
// PresetDenyTTY denies faking input.
|
|
||||||
PresetDenyTTY
|
|
||||||
// PresetDenyDevel denies development-related syscalls.
|
|
||||||
PresetDenyDevel
|
|
||||||
// PresetLinux32 sets PER_LINUX32.
|
|
||||||
PresetLinux32
|
|
||||||
)
|
|
||||||
|
|
||||||
func Preset(presets FilterPreset, flags ExportFlag) (rules []NativeRule) {
|
|
||||||
allowedPersonality := PER_LINUX
|
|
||||||
if presets&PresetLinux32 != 0 {
|
|
||||||
allowedPersonality = PER_LINUX32
|
|
||||||
}
|
|
||||||
presetDevelFinal := presetDevel(ScmpDatum(allowedPersonality))
|
|
||||||
|
|
||||||
l := len(presetCommon)
|
|
||||||
if presets&PresetDenyNS != 0 {
|
|
||||||
l += len(presetNamespace)
|
|
||||||
}
|
|
||||||
if presets&PresetDenyTTY != 0 {
|
|
||||||
l += len(presetTTY)
|
|
||||||
}
|
|
||||||
if presets&PresetDenyDevel != 0 {
|
|
||||||
l += len(presetDevelFinal)
|
|
||||||
}
|
|
||||||
if flags&AllowMultiarch == 0 {
|
|
||||||
l += len(presetEmu)
|
|
||||||
}
|
|
||||||
if presets&PresetExt != 0 {
|
|
||||||
l += len(presetCommonExt)
|
|
||||||
if presets&PresetDenyNS != 0 {
|
|
||||||
l += len(presetNamespaceExt)
|
|
||||||
}
|
|
||||||
if flags&AllowMultiarch == 0 {
|
|
||||||
l += len(presetEmuExt)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
rules = make([]NativeRule, 0, l)
|
|
||||||
rules = append(rules, presetCommon...)
|
|
||||||
if presets&PresetDenyNS != 0 {
|
|
||||||
rules = append(rules, presetNamespace...)
|
|
||||||
}
|
|
||||||
if presets&PresetDenyTTY != 0 {
|
|
||||||
rules = append(rules, presetTTY...)
|
|
||||||
}
|
|
||||||
if presets&PresetDenyDevel != 0 {
|
|
||||||
rules = append(rules, presetDevelFinal...)
|
|
||||||
}
|
|
||||||
if flags&AllowMultiarch == 0 {
|
|
||||||
rules = append(rules, presetEmu...)
|
|
||||||
}
|
|
||||||
if presets&PresetExt != 0 {
|
|
||||||
rules = append(rules, presetCommonExt...)
|
|
||||||
if presets&PresetDenyNS != 0 {
|
|
||||||
rules = append(rules, presetNamespaceExt...)
|
|
||||||
}
|
|
||||||
if flags&AllowMultiarch == 0 {
|
|
||||||
rules = append(rules, presetEmuExt...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
presetCommon = []NativeRule{
|
|
||||||
/* Block dmesg */
|
|
||||||
{ScmpSyscall(SYS_SYSLOG), ScmpErrno(EPERM), nil},
|
|
||||||
/* Useless old syscall */
|
|
||||||
{ScmpSyscall(SYS_USELIB), ScmpErrno(EPERM), nil},
|
|
||||||
/* Don't allow disabling accounting */
|
|
||||||
{ScmpSyscall(SYS_ACCT), ScmpErrno(EPERM), nil},
|
|
||||||
/* Don't allow reading current quota use */
|
|
||||||
{ScmpSyscall(SYS_QUOTACTL), ScmpErrno(EPERM), nil},
|
|
||||||
|
|
||||||
/* Don't allow access to the kernel keyring */
|
|
||||||
{ScmpSyscall(SYS_ADD_KEY), ScmpErrno(EPERM), nil},
|
|
||||||
{ScmpSyscall(SYS_KEYCTL), ScmpErrno(EPERM), nil},
|
|
||||||
{ScmpSyscall(SYS_REQUEST_KEY), ScmpErrno(EPERM), nil},
|
|
||||||
|
|
||||||
/* Scary VM/NUMA ops */
|
|
||||||
{ScmpSyscall(SYS_MOVE_PAGES), ScmpErrno(EPERM), nil},
|
|
||||||
{ScmpSyscall(SYS_MBIND), ScmpErrno(EPERM), nil},
|
|
||||||
{ScmpSyscall(SYS_GET_MEMPOLICY), ScmpErrno(EPERM), nil},
|
|
||||||
{ScmpSyscall(SYS_SET_MEMPOLICY), ScmpErrno(EPERM), nil},
|
|
||||||
{ScmpSyscall(SYS_MIGRATE_PAGES), ScmpErrno(EPERM), nil},
|
|
||||||
}
|
|
||||||
|
|
||||||
/* hakurei: project-specific extensions */
|
|
||||||
presetCommonExt = []NativeRule{
|
|
||||||
/* system calls for changing the system clock */
|
|
||||||
{ScmpSyscall(SYS_ADJTIMEX), ScmpErrno(EPERM), nil},
|
|
||||||
{ScmpSyscall(SYS_CLOCK_ADJTIME), ScmpErrno(EPERM), nil},
|
|
||||||
{ScmpSyscall(SYS_CLOCK_ADJTIME64), ScmpErrno(EPERM), nil},
|
|
||||||
{ScmpSyscall(SYS_CLOCK_SETTIME), ScmpErrno(EPERM), nil},
|
|
||||||
{ScmpSyscall(SYS_CLOCK_SETTIME64), ScmpErrno(EPERM), nil},
|
|
||||||
{ScmpSyscall(SYS_SETTIMEOFDAY), ScmpErrno(EPERM), nil},
|
|
||||||
|
|
||||||
/* loading and unloading of kernel modules */
|
|
||||||
{ScmpSyscall(SYS_DELETE_MODULE), ScmpErrno(EPERM), nil},
|
|
||||||
{ScmpSyscall(SYS_FINIT_MODULE), ScmpErrno(EPERM), nil},
|
|
||||||
{ScmpSyscall(SYS_INIT_MODULE), ScmpErrno(EPERM), nil},
|
|
||||||
|
|
||||||
/* system calls for rebooting and reboot preparation */
|
|
||||||
{ScmpSyscall(SYS_KEXEC_FILE_LOAD), ScmpErrno(EPERM), nil},
|
|
||||||
{ScmpSyscall(SYS_KEXEC_LOAD), ScmpErrno(EPERM), nil},
|
|
||||||
{ScmpSyscall(SYS_REBOOT), ScmpErrno(EPERM), nil},
|
|
||||||
|
|
||||||
/* system calls for enabling/disabling swap devices */
|
|
||||||
{ScmpSyscall(SYS_SWAPOFF), ScmpErrno(EPERM), nil},
|
|
||||||
{ScmpSyscall(SYS_SWAPON), ScmpErrno(EPERM), nil},
|
|
||||||
}
|
|
||||||
|
|
||||||
presetNamespace = []NativeRule{
|
|
||||||
/* Don't allow subnamespace setups: */
|
|
||||||
{ScmpSyscall(SYS_UNSHARE), ScmpErrno(EPERM), nil},
|
|
||||||
{ScmpSyscall(SYS_SETNS), ScmpErrno(EPERM), nil},
|
|
||||||
{ScmpSyscall(SYS_MOUNT), ScmpErrno(EPERM), nil},
|
|
||||||
{ScmpSyscall(SYS_UMOUNT), ScmpErrno(EPERM), nil},
|
|
||||||
{ScmpSyscall(SYS_UMOUNT2), ScmpErrno(EPERM), nil},
|
|
||||||
{ScmpSyscall(SYS_PIVOT_ROOT), ScmpErrno(EPERM), nil},
|
|
||||||
{ScmpSyscall(SYS_CHROOT), ScmpErrno(EPERM), nil},
|
|
||||||
{ScmpSyscall(SYS_CLONE), ScmpErrno(EPERM),
|
|
||||||
&ScmpArgCmp{cloneArg, SCMP_CMP_MASKED_EQ, CLONE_NEWUSER, CLONE_NEWUSER}},
|
|
||||||
|
|
||||||
/* seccomp can't look into clone3()'s struct clone_args to check whether
|
|
||||||
* the flags are OK, so we have no choice but to block clone3().
|
|
||||||
* Return ENOSYS so user-space will fall back to clone().
|
|
||||||
* (CVE-2021-41133; see also https://github.com/moby/moby/commit/9f6b562d)
|
|
||||||
*/
|
|
||||||
{ScmpSyscall(SYS_CLONE3), ScmpErrno(ENOSYS), nil},
|
|
||||||
|
|
||||||
/* New mount manipulation APIs can also change our VFS. There's no
|
|
||||||
* legitimate reason to do these in the sandbox, so block all of them
|
|
||||||
* rather than thinking about which ones might be dangerous.
|
|
||||||
* (CVE-2021-41133) */
|
|
||||||
{ScmpSyscall(SYS_OPEN_TREE), ScmpErrno(ENOSYS), nil},
|
|
||||||
{ScmpSyscall(SYS_MOVE_MOUNT), ScmpErrno(ENOSYS), nil},
|
|
||||||
{ScmpSyscall(SYS_FSOPEN), ScmpErrno(ENOSYS), nil},
|
|
||||||
{ScmpSyscall(SYS_FSCONFIG), ScmpErrno(ENOSYS), nil},
|
|
||||||
{ScmpSyscall(SYS_FSMOUNT), ScmpErrno(ENOSYS), nil},
|
|
||||||
{ScmpSyscall(SYS_FSPICK), ScmpErrno(ENOSYS), nil},
|
|
||||||
{ScmpSyscall(SYS_MOUNT_SETATTR), ScmpErrno(ENOSYS), nil},
|
|
||||||
}
|
|
||||||
|
|
||||||
/* hakurei: project-specific extensions */
|
|
||||||
presetNamespaceExt = []NativeRule{
|
|
||||||
/* changing file ownership */
|
|
||||||
{ScmpSyscall(SYS_CHOWN), ScmpErrno(EPERM), nil},
|
|
||||||
{ScmpSyscall(SYS_CHOWN32), ScmpErrno(EPERM), nil},
|
|
||||||
{ScmpSyscall(SYS_FCHOWN), ScmpErrno(EPERM), nil},
|
|
||||||
{ScmpSyscall(SYS_FCHOWN32), ScmpErrno(EPERM), nil},
|
|
||||||
{ScmpSyscall(SYS_FCHOWNAT), ScmpErrno(EPERM), nil},
|
|
||||||
{ScmpSyscall(SYS_LCHOWN), ScmpErrno(EPERM), nil},
|
|
||||||
{ScmpSyscall(SYS_LCHOWN32), ScmpErrno(EPERM), nil},
|
|
||||||
|
|
||||||
/* system calls for changing user ID and group ID credentials */
|
|
||||||
{ScmpSyscall(SYS_SETGID), ScmpErrno(EPERM), nil},
|
|
||||||
{ScmpSyscall(SYS_SETGID32), ScmpErrno(EPERM), nil},
|
|
||||||
{ScmpSyscall(SYS_SETGROUPS), ScmpErrno(EPERM), nil},
|
|
||||||
{ScmpSyscall(SYS_SETGROUPS32), ScmpErrno(EPERM), nil},
|
|
||||||
{ScmpSyscall(SYS_SETREGID), ScmpErrno(EPERM), nil},
|
|
||||||
{ScmpSyscall(SYS_SETREGID32), ScmpErrno(EPERM), nil},
|
|
||||||
{ScmpSyscall(SYS_SETRESGID), ScmpErrno(EPERM), nil},
|
|
||||||
{ScmpSyscall(SYS_SETRESGID32), ScmpErrno(EPERM), nil},
|
|
||||||
{ScmpSyscall(SYS_SETRESUID), ScmpErrno(EPERM), nil},
|
|
||||||
{ScmpSyscall(SYS_SETRESUID32), ScmpErrno(EPERM), nil},
|
|
||||||
{ScmpSyscall(SYS_SETREUID), ScmpErrno(EPERM), nil},
|
|
||||||
{ScmpSyscall(SYS_SETREUID32), ScmpErrno(EPERM), nil},
|
|
||||||
{ScmpSyscall(SYS_SETUID), ScmpErrno(EPERM), nil},
|
|
||||||
{ScmpSyscall(SYS_SETUID32), ScmpErrno(EPERM), nil},
|
|
||||||
}
|
|
||||||
|
|
||||||
presetTTY = []NativeRule{
|
|
||||||
/* Don't allow faking input to the controlling tty (CVE-2017-5226) */
|
|
||||||
{ScmpSyscall(SYS_IOCTL), ScmpErrno(EPERM),
|
|
||||||
&ScmpArgCmp{1, SCMP_CMP_MASKED_EQ, 0xFFFFFFFF, TIOCSTI}},
|
|
||||||
/* In the unlikely event that the controlling tty is a Linux virtual
|
|
||||||
* console (/dev/tty2 or similar), copy/paste operations have an effect
|
|
||||||
* similar to TIOCSTI (CVE-2023-28100) */
|
|
||||||
{ScmpSyscall(SYS_IOCTL), ScmpErrno(EPERM),
|
|
||||||
&ScmpArgCmp{1, SCMP_CMP_MASKED_EQ, 0xFFFFFFFF, TIOCLINUX}},
|
|
||||||
}
|
|
||||||
|
|
||||||
presetEmu = []NativeRule{
|
|
||||||
/* modify_ldt is a historic source of interesting information leaks,
|
|
||||||
* so it's disabled as a hardening measure.
|
|
||||||
* However, it is required to run old 16-bit applications
|
|
||||||
* as well as some Wine patches, so it's allowed in multiarch. */
|
|
||||||
{ScmpSyscall(SYS_MODIFY_LDT), ScmpErrno(EPERM), nil},
|
|
||||||
}
|
|
||||||
|
|
||||||
/* hakurei: project-specific extensions */
|
|
||||||
presetEmuExt = []NativeRule{
|
|
||||||
{ScmpSyscall(SYS_SUBPAGE_PROT), ScmpErrno(ENOSYS), nil},
|
|
||||||
{ScmpSyscall(SYS_SWITCH_ENDIAN), ScmpErrno(ENOSYS), nil},
|
|
||||||
{ScmpSyscall(SYS_VM86), ScmpErrno(ENOSYS), nil},
|
|
||||||
{ScmpSyscall(SYS_VM86OLD), ScmpErrno(ENOSYS), nil},
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
func presetDevel(allowedPersonality ScmpDatum) []NativeRule {
|
|
||||||
return []NativeRule{
|
|
||||||
/* Profiling operations; we expect these to be done by tools from outside
|
|
||||||
* the sandbox. In particular perf has been the source of many CVEs. */
|
|
||||||
{ScmpSyscall(SYS_PERF_EVENT_OPEN), ScmpErrno(EPERM), nil},
|
|
||||||
/* Don't allow you to switch to bsd emulation or whatnot */
|
|
||||||
{ScmpSyscall(SYS_PERSONALITY), ScmpErrno(EPERM),
|
|
||||||
&ScmpArgCmp{0, SCMP_CMP_NE, allowedPersonality, 0}},
|
|
||||||
|
|
||||||
{ScmpSyscall(SYS_PTRACE), ScmpErrno(EPERM), nil},
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,7 +0,0 @@
|
|||||||
//go:build s390 || s390x
|
|
||||||
|
|
||||||
package seccomp
|
|
||||||
|
|
||||||
/* Architectures with CONFIG_CLONE_BACKWARDS2: the child stack
|
|
||||||
* and flags arguments are reversed so the flags come second */
|
|
||||||
const cloneArg = 1
|
|
@ -1,6 +0,0 @@
|
|||||||
//go:build !s390 && !s390x
|
|
||||||
|
|
||||||
package seccomp
|
|
||||||
|
|
||||||
/* Normally the flags come first */
|
|
||||||
const cloneArg = 0
|
|
@ -1,28 +0,0 @@
|
|||||||
package seccomp
|
|
||||||
|
|
||||||
import "iter"
|
|
||||||
|
|
||||||
// Syscalls returns an iterator over all wired syscalls.
|
|
||||||
func Syscalls() iter.Seq2[string, int] {
|
|
||||||
return func(yield func(string, int) bool) {
|
|
||||||
for name, num := range syscallNum {
|
|
||||||
if !yield(name, num) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for name, num := range syscallNumExtra {
|
|
||||||
if !yield(name, num) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// SyscallResolveName resolves a syscall number from its string representation.
|
|
||||||
func SyscallResolveName(name string) (num int, ok bool) {
|
|
||||||
if num, ok = syscallNum[name]; ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
num, ok = syscallNumExtra[name]
|
|
||||||
return
|
|
||||||
}
|
|
@ -1,54 +0,0 @@
|
|||||||
package seccomp
|
|
||||||
|
|
||||||
/*
|
|
||||||
#cgo linux pkg-config: --static libseccomp
|
|
||||||
|
|
||||||
#include <seccomp.h>
|
|
||||||
#include <sys/personality.h>
|
|
||||||
*/
|
|
||||||
import "C"
|
|
||||||
|
|
||||||
const (
|
|
||||||
PER_LINUX = C.PER_LINUX
|
|
||||||
PER_LINUX32 = C.PER_LINUX32
|
|
||||||
)
|
|
||||||
|
|
||||||
var syscallNumExtra = map[string]int{
|
|
||||||
"umount": SYS_UMOUNT,
|
|
||||||
"subpage_prot": SYS_SUBPAGE_PROT,
|
|
||||||
"switch_endian": SYS_SWITCH_ENDIAN,
|
|
||||||
"vm86": SYS_VM86,
|
|
||||||
"vm86old": SYS_VM86OLD,
|
|
||||||
"clock_adjtime64": SYS_CLOCK_ADJTIME64,
|
|
||||||
"clock_settime64": SYS_CLOCK_SETTIME64,
|
|
||||||
"chown32": SYS_CHOWN32,
|
|
||||||
"fchown32": SYS_FCHOWN32,
|
|
||||||
"lchown32": SYS_LCHOWN32,
|
|
||||||
"setgid32": SYS_SETGID32,
|
|
||||||
"setgroups32": SYS_SETGROUPS32,
|
|
||||||
"setregid32": SYS_SETREGID32,
|
|
||||||
"setresgid32": SYS_SETRESGID32,
|
|
||||||
"setresuid32": SYS_SETRESUID32,
|
|
||||||
"setreuid32": SYS_SETREUID32,
|
|
||||||
"setuid32": SYS_SETUID32,
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
|
||||||
SYS_UMOUNT = C.__SNR_umount
|
|
||||||
SYS_SUBPAGE_PROT = C.__SNR_subpage_prot
|
|
||||||
SYS_SWITCH_ENDIAN = C.__SNR_switch_endian
|
|
||||||
SYS_VM86 = C.__SNR_vm86
|
|
||||||
SYS_VM86OLD = C.__SNR_vm86old
|
|
||||||
SYS_CLOCK_ADJTIME64 = C.__SNR_clock_adjtime64
|
|
||||||
SYS_CLOCK_SETTIME64 = C.__SNR_clock_settime64
|
|
||||||
SYS_CHOWN32 = C.__SNR_chown32
|
|
||||||
SYS_FCHOWN32 = C.__SNR_fchown32
|
|
||||||
SYS_LCHOWN32 = C.__SNR_lchown32
|
|
||||||
SYS_SETGID32 = C.__SNR_setgid32
|
|
||||||
SYS_SETGROUPS32 = C.__SNR_setgroups32
|
|
||||||
SYS_SETREGID32 = C.__SNR_setregid32
|
|
||||||
SYS_SETRESGID32 = C.__SNR_setresgid32
|
|
||||||
SYS_SETRESUID32 = C.__SNR_setresuid32
|
|
||||||
SYS_SETREUID32 = C.__SNR_setreuid32
|
|
||||||
SYS_SETUID32 = C.__SNR_setuid32
|
|
||||||
)
|
|
@ -1,459 +0,0 @@
|
|||||||
// mksysnum_linux.pl /usr/include/asm/unistd_64.h
|
|
||||||
// Code generated by the command above; DO NOT EDIT.
|
|
||||||
|
|
||||||
package seccomp
|
|
||||||
|
|
||||||
import . "syscall"
|
|
||||||
|
|
||||||
var syscallNum = map[string]int{
|
|
||||||
"read": SYS_READ,
|
|
||||||
"write": SYS_WRITE,
|
|
||||||
"open": SYS_OPEN,
|
|
||||||
"close": SYS_CLOSE,
|
|
||||||
"stat": SYS_STAT,
|
|
||||||
"fstat": SYS_FSTAT,
|
|
||||||
"lstat": SYS_LSTAT,
|
|
||||||
"poll": SYS_POLL,
|
|
||||||
"lseek": SYS_LSEEK,
|
|
||||||
"mmap": SYS_MMAP,
|
|
||||||
"mprotect": SYS_MPROTECT,
|
|
||||||
"munmap": SYS_MUNMAP,
|
|
||||||
"brk": SYS_BRK,
|
|
||||||
"rt_sigaction": SYS_RT_SIGACTION,
|
|
||||||
"rt_sigprocmask": SYS_RT_SIGPROCMASK,
|
|
||||||
"rt_sigreturn": SYS_RT_SIGRETURN,
|
|
||||||
"ioctl": SYS_IOCTL,
|
|
||||||
"pread64": SYS_PREAD64,
|
|
||||||
"pwrite64": SYS_PWRITE64,
|
|
||||||
"readv": SYS_READV,
|
|
||||||
"writev": SYS_WRITEV,
|
|
||||||
"access": SYS_ACCESS,
|
|
||||||
"pipe": SYS_PIPE,
|
|
||||||
"select": SYS_SELECT,
|
|
||||||
"sched_yield": SYS_SCHED_YIELD,
|
|
||||||
"mremap": SYS_MREMAP,
|
|
||||||
"msync": SYS_MSYNC,
|
|
||||||
"mincore": SYS_MINCORE,
|
|
||||||
"madvise": SYS_MADVISE,
|
|
||||||
"shmget": SYS_SHMGET,
|
|
||||||
"shmat": SYS_SHMAT,
|
|
||||||
"shmctl": SYS_SHMCTL,
|
|
||||||
"dup": SYS_DUP,
|
|
||||||
"dup2": SYS_DUP2,
|
|
||||||
"pause": SYS_PAUSE,
|
|
||||||
"nanosleep": SYS_NANOSLEEP,
|
|
||||||
"getitimer": SYS_GETITIMER,
|
|
||||||
"alarm": SYS_ALARM,
|
|
||||||
"setitimer": SYS_SETITIMER,
|
|
||||||
"getpid": SYS_GETPID,
|
|
||||||
"sendfile": SYS_SENDFILE,
|
|
||||||
"socket": SYS_SOCKET,
|
|
||||||
"connect": SYS_CONNECT,
|
|
||||||
"accept": SYS_ACCEPT,
|
|
||||||
"sendto": SYS_SENDTO,
|
|
||||||
"recvfrom": SYS_RECVFROM,
|
|
||||||
"sendmsg": SYS_SENDMSG,
|
|
||||||
"recvmsg": SYS_RECVMSG,
|
|
||||||
"shutdown": SYS_SHUTDOWN,
|
|
||||||
"bind": SYS_BIND,
|
|
||||||
"listen": SYS_LISTEN,
|
|
||||||
"getsockname": SYS_GETSOCKNAME,
|
|
||||||
"getpeername": SYS_GETPEERNAME,
|
|
||||||
"socketpair": SYS_SOCKETPAIR,
|
|
||||||
"setsockopt": SYS_SETSOCKOPT,
|
|
||||||
"getsockopt": SYS_GETSOCKOPT,
|
|
||||||
"clone": SYS_CLONE,
|
|
||||||
"fork": SYS_FORK,
|
|
||||||
"vfork": SYS_VFORK,
|
|
||||||
"execve": SYS_EXECVE,
|
|
||||||
"exit": SYS_EXIT,
|
|
||||||
"wait4": SYS_WAIT4,
|
|
||||||
"kill": SYS_KILL,
|
|
||||||
"uname": SYS_UNAME,
|
|
||||||
"semget": SYS_SEMGET,
|
|
||||||
"semop": SYS_SEMOP,
|
|
||||||
"semctl": SYS_SEMCTL,
|
|
||||||
"shmdt": SYS_SHMDT,
|
|
||||||
"msgget": SYS_MSGGET,
|
|
||||||
"msgsnd": SYS_MSGSND,
|
|
||||||
"msgrcv": SYS_MSGRCV,
|
|
||||||
"msgctl": SYS_MSGCTL,
|
|
||||||
"fcntl": SYS_FCNTL,
|
|
||||||
"flock": SYS_FLOCK,
|
|
||||||
"fsync": SYS_FSYNC,
|
|
||||||
"fdatasync": SYS_FDATASYNC,
|
|
||||||
"truncate": SYS_TRUNCATE,
|
|
||||||
"ftruncate": SYS_FTRUNCATE,
|
|
||||||
"getdents": SYS_GETDENTS,
|
|
||||||
"getcwd": SYS_GETCWD,
|
|
||||||
"chdir": SYS_CHDIR,
|
|
||||||
"fchdir": SYS_FCHDIR,
|
|
||||||
"rename": SYS_RENAME,
|
|
||||||
"mkdir": SYS_MKDIR,
|
|
||||||
"rmdir": SYS_RMDIR,
|
|
||||||
"creat": SYS_CREAT,
|
|
||||||
"link": SYS_LINK,
|
|
||||||
"unlink": SYS_UNLINK,
|
|
||||||
"symlink": SYS_SYMLINK,
|
|
||||||
"readlink": SYS_READLINK,
|
|
||||||
"chmod": SYS_CHMOD,
|
|
||||||
"fchmod": SYS_FCHMOD,
|
|
||||||
"chown": SYS_CHOWN,
|
|
||||||
"fchown": SYS_FCHOWN,
|
|
||||||
"lchown": SYS_LCHOWN,
|
|
||||||
"umask": SYS_UMASK,
|
|
||||||
"gettimeofday": SYS_GETTIMEOFDAY,
|
|
||||||
"getrlimit": SYS_GETRLIMIT,
|
|
||||||
"getrusage": SYS_GETRUSAGE,
|
|
||||||
"sysinfo": SYS_SYSINFO,
|
|
||||||
"times": SYS_TIMES,
|
|
||||||
"ptrace": SYS_PTRACE,
|
|
||||||
"getuid": SYS_GETUID,
|
|
||||||
"syslog": SYS_SYSLOG,
|
|
||||||
"getgid": SYS_GETGID,
|
|
||||||
"setuid": SYS_SETUID,
|
|
||||||
"setgid": SYS_SETGID,
|
|
||||||
"geteuid": SYS_GETEUID,
|
|
||||||
"getegid": SYS_GETEGID,
|
|
||||||
"setpgid": SYS_SETPGID,
|
|
||||||
"getppid": SYS_GETPPID,
|
|
||||||
"getpgrp": SYS_GETPGRP,
|
|
||||||
"setsid": SYS_SETSID,
|
|
||||||
"setreuid": SYS_SETREUID,
|
|
||||||
"setregid": SYS_SETREGID,
|
|
||||||
"getgroups": SYS_GETGROUPS,
|
|
||||||
"setgroups": SYS_SETGROUPS,
|
|
||||||
"setresuid": SYS_SETRESUID,
|
|
||||||
"getresuid": SYS_GETRESUID,
|
|
||||||
"setresgid": SYS_SETRESGID,
|
|
||||||
"getresgid": SYS_GETRESGID,
|
|
||||||
"getpgid": SYS_GETPGID,
|
|
||||||
"setfsuid": SYS_SETFSUID,
|
|
||||||
"setfsgid": SYS_SETFSGID,
|
|
||||||
"getsid": SYS_GETSID,
|
|
||||||
"capget": SYS_CAPGET,
|
|
||||||
"capset": SYS_CAPSET,
|
|
||||||
"rt_sigpending": SYS_RT_SIGPENDING,
|
|
||||||
"rt_sigtimedwait": SYS_RT_SIGTIMEDWAIT,
|
|
||||||
"rt_sigqueueinfo": SYS_RT_SIGQUEUEINFO,
|
|
||||||
"rt_sigsuspend": SYS_RT_SIGSUSPEND,
|
|
||||||
"sigaltstack": SYS_SIGALTSTACK,
|
|
||||||
"utime": SYS_UTIME,
|
|
||||||
"mknod": SYS_MKNOD,
|
|
||||||
"uselib": SYS_USELIB,
|
|
||||||
"personality": SYS_PERSONALITY,
|
|
||||||
"ustat": SYS_USTAT,
|
|
||||||
"statfs": SYS_STATFS,
|
|
||||||
"fstatfs": SYS_FSTATFS,
|
|
||||||
"sysfs": SYS_SYSFS,
|
|
||||||
"getpriority": SYS_GETPRIORITY,
|
|
||||||
"setpriority": SYS_SETPRIORITY,
|
|
||||||
"sched_setparam": SYS_SCHED_SETPARAM,
|
|
||||||
"sched_getparam": SYS_SCHED_GETPARAM,
|
|
||||||
"sched_setscheduler": SYS_SCHED_SETSCHEDULER,
|
|
||||||
"sched_getscheduler": SYS_SCHED_GETSCHEDULER,
|
|
||||||
"sched_get_priority_max": SYS_SCHED_GET_PRIORITY_MAX,
|
|
||||||
"sched_get_priority_min": SYS_SCHED_GET_PRIORITY_MIN,
|
|
||||||
"sched_rr_get_interval": SYS_SCHED_RR_GET_INTERVAL,
|
|
||||||
"mlock": SYS_MLOCK,
|
|
||||||
"munlock": SYS_MUNLOCK,
|
|
||||||
"mlockall": SYS_MLOCKALL,
|
|
||||||
"munlockall": SYS_MUNLOCKALL,
|
|
||||||
"vhangup": SYS_VHANGUP,
|
|
||||||
"modify_ldt": SYS_MODIFY_LDT,
|
|
||||||
"pivot_root": SYS_PIVOT_ROOT,
|
|
||||||
"_sysctl": SYS__SYSCTL,
|
|
||||||
"prctl": SYS_PRCTL,
|
|
||||||
"arch_prctl": SYS_ARCH_PRCTL,
|
|
||||||
"adjtimex": SYS_ADJTIMEX,
|
|
||||||
"setrlimit": SYS_SETRLIMIT,
|
|
||||||
"chroot": SYS_CHROOT,
|
|
||||||
"sync": SYS_SYNC,
|
|
||||||
"acct": SYS_ACCT,
|
|
||||||
"settimeofday": SYS_SETTIMEOFDAY,
|
|
||||||
"mount": SYS_MOUNT,
|
|
||||||
"umount2": SYS_UMOUNT2,
|
|
||||||
"swapon": SYS_SWAPON,
|
|
||||||
"swapoff": SYS_SWAPOFF,
|
|
||||||
"reboot": SYS_REBOOT,
|
|
||||||
"sethostname": SYS_SETHOSTNAME,
|
|
||||||
"setdomainname": SYS_SETDOMAINNAME,
|
|
||||||
"iopl": SYS_IOPL,
|
|
||||||
"ioperm": SYS_IOPERM,
|
|
||||||
"create_module": SYS_CREATE_MODULE,
|
|
||||||
"init_module": SYS_INIT_MODULE,
|
|
||||||
"delete_module": SYS_DELETE_MODULE,
|
|
||||||
"get_kernel_syms": SYS_GET_KERNEL_SYMS,
|
|
||||||
"query_module": SYS_QUERY_MODULE,
|
|
||||||
"quotactl": SYS_QUOTACTL,
|
|
||||||
"nfsservctl": SYS_NFSSERVCTL,
|
|
||||||
"getpmsg": SYS_GETPMSG,
|
|
||||||
"putpmsg": SYS_PUTPMSG,
|
|
||||||
"afs_syscall": SYS_AFS_SYSCALL,
|
|
||||||
"tuxcall": SYS_TUXCALL,
|
|
||||||
"security": SYS_SECURITY,
|
|
||||||
"gettid": SYS_GETTID,
|
|
||||||
"readahead": SYS_READAHEAD,
|
|
||||||
"setxattr": SYS_SETXATTR,
|
|
||||||
"lsetxattr": SYS_LSETXATTR,
|
|
||||||
"fsetxattr": SYS_FSETXATTR,
|
|
||||||
"getxattr": SYS_GETXATTR,
|
|
||||||
"lgetxattr": SYS_LGETXATTR,
|
|
||||||
"fgetxattr": SYS_FGETXATTR,
|
|
||||||
"listxattr": SYS_LISTXATTR,
|
|
||||||
"llistxattr": SYS_LLISTXATTR,
|
|
||||||
"flistxattr": SYS_FLISTXATTR,
|
|
||||||
"removexattr": SYS_REMOVEXATTR,
|
|
||||||
"lremovexattr": SYS_LREMOVEXATTR,
|
|
||||||
"fremovexattr": SYS_FREMOVEXATTR,
|
|
||||||
"tkill": SYS_TKILL,
|
|
||||||
"time": SYS_TIME,
|
|
||||||
"futex": SYS_FUTEX,
|
|
||||||
"sched_setaffinity": SYS_SCHED_SETAFFINITY,
|
|
||||||
"sched_getaffinity": SYS_SCHED_GETAFFINITY,
|
|
||||||
"set_thread_area": SYS_SET_THREAD_AREA,
|
|
||||||
"io_setup": SYS_IO_SETUP,
|
|
||||||
"io_destroy": SYS_IO_DESTROY,
|
|
||||||
"io_getevents": SYS_IO_GETEVENTS,
|
|
||||||
"io_submit": SYS_IO_SUBMIT,
|
|
||||||
"io_cancel": SYS_IO_CANCEL,
|
|
||||||
"get_thread_area": SYS_GET_THREAD_AREA,
|
|
||||||
"lookup_dcookie": SYS_LOOKUP_DCOOKIE,
|
|
||||||
"epoll_create": SYS_EPOLL_CREATE,
|
|
||||||
"epoll_ctl_old": SYS_EPOLL_CTL_OLD,
|
|
||||||
"epoll_wait_old": SYS_EPOLL_WAIT_OLD,
|
|
||||||
"remap_file_pages": SYS_REMAP_FILE_PAGES,
|
|
||||||
"getdents64": SYS_GETDENTS64,
|
|
||||||
"set_tid_address": SYS_SET_TID_ADDRESS,
|
|
||||||
"restart_syscall": SYS_RESTART_SYSCALL,
|
|
||||||
"semtimedop": SYS_SEMTIMEDOP,
|
|
||||||
"fadvise64": SYS_FADVISE64,
|
|
||||||
"timer_create": SYS_TIMER_CREATE,
|
|
||||||
"timer_settime": SYS_TIMER_SETTIME,
|
|
||||||
"timer_gettime": SYS_TIMER_GETTIME,
|
|
||||||
"timer_getoverrun": SYS_TIMER_GETOVERRUN,
|
|
||||||
"timer_delete": SYS_TIMER_DELETE,
|
|
||||||
"clock_settime": SYS_CLOCK_SETTIME,
|
|
||||||
"clock_gettime": SYS_CLOCK_GETTIME,
|
|
||||||
"clock_getres": SYS_CLOCK_GETRES,
|
|
||||||
"clock_nanosleep": SYS_CLOCK_NANOSLEEP,
|
|
||||||
"exit_group": SYS_EXIT_GROUP,
|
|
||||||
"epoll_wait": SYS_EPOLL_WAIT,
|
|
||||||
"epoll_ctl": SYS_EPOLL_CTL,
|
|
||||||
"tgkill": SYS_TGKILL,
|
|
||||||
"utimes": SYS_UTIMES,
|
|
||||||
"vserver": SYS_VSERVER,
|
|
||||||
"mbind": SYS_MBIND,
|
|
||||||
"set_mempolicy": SYS_SET_MEMPOLICY,
|
|
||||||
"get_mempolicy": SYS_GET_MEMPOLICY,
|
|
||||||
"mq_open": SYS_MQ_OPEN,
|
|
||||||
"mq_unlink": SYS_MQ_UNLINK,
|
|
||||||
"mq_timedsend": SYS_MQ_TIMEDSEND,
|
|
||||||
"mq_timedreceive": SYS_MQ_TIMEDRECEIVE,
|
|
||||||
"mq_notify": SYS_MQ_NOTIFY,
|
|
||||||
"mq_getsetattr": SYS_MQ_GETSETATTR,
|
|
||||||
"kexec_load": SYS_KEXEC_LOAD,
|
|
||||||
"waitid": SYS_WAITID,
|
|
||||||
"add_key": SYS_ADD_KEY,
|
|
||||||
"request_key": SYS_REQUEST_KEY,
|
|
||||||
"keyctl": SYS_KEYCTL,
|
|
||||||
"ioprio_set": SYS_IOPRIO_SET,
|
|
||||||
"ioprio_get": SYS_IOPRIO_GET,
|
|
||||||
"inotify_init": SYS_INOTIFY_INIT,
|
|
||||||
"inotify_add_watch": SYS_INOTIFY_ADD_WATCH,
|
|
||||||
"inotify_rm_watch": SYS_INOTIFY_RM_WATCH,
|
|
||||||
"migrate_pages": SYS_MIGRATE_PAGES,
|
|
||||||
"openat": SYS_OPENAT,
|
|
||||||
"mkdirat": SYS_MKDIRAT,
|
|
||||||
"mknodat": SYS_MKNODAT,
|
|
||||||
"fchownat": SYS_FCHOWNAT,
|
|
||||||
"futimesat": SYS_FUTIMESAT,
|
|
||||||
"newfstatat": SYS_NEWFSTATAT,
|
|
||||||
"unlinkat": SYS_UNLINKAT,
|
|
||||||
"renameat": SYS_RENAMEAT,
|
|
||||||
"linkat": SYS_LINKAT,
|
|
||||||
"symlinkat": SYS_SYMLINKAT,
|
|
||||||
"readlinkat": SYS_READLINKAT,
|
|
||||||
"fchmodat": SYS_FCHMODAT,
|
|
||||||
"faccessat": SYS_FACCESSAT,
|
|
||||||
"pselect6": SYS_PSELECT6,
|
|
||||||
"ppoll": SYS_PPOLL,
|
|
||||||
"unshare": SYS_UNSHARE,
|
|
||||||
"set_robust_list": SYS_SET_ROBUST_LIST,
|
|
||||||
"get_robust_list": SYS_GET_ROBUST_LIST,
|
|
||||||
"splice": SYS_SPLICE,
|
|
||||||
"tee": SYS_TEE,
|
|
||||||
"sync_file_range": SYS_SYNC_FILE_RANGE,
|
|
||||||
"vmsplice": SYS_VMSPLICE,
|
|
||||||
"move_pages": SYS_MOVE_PAGES,
|
|
||||||
"utimensat": SYS_UTIMENSAT,
|
|
||||||
"epoll_pwait": SYS_EPOLL_PWAIT,
|
|
||||||
"signalfd": SYS_SIGNALFD,
|
|
||||||
"timerfd_create": SYS_TIMERFD_CREATE,
|
|
||||||
"eventfd": SYS_EVENTFD,
|
|
||||||
"fallocate": SYS_FALLOCATE,
|
|
||||||
"timerfd_settime": SYS_TIMERFD_SETTIME,
|
|
||||||
"timerfd_gettime": SYS_TIMERFD_GETTIME,
|
|
||||||
"accept4": SYS_ACCEPT4,
|
|
||||||
"signalfd4": SYS_SIGNALFD4,
|
|
||||||
"eventfd2": SYS_EVENTFD2,
|
|
||||||
"epoll_create1": SYS_EPOLL_CREATE1,
|
|
||||||
"dup3": SYS_DUP3,
|
|
||||||
"pipe2": SYS_PIPE2,
|
|
||||||
"inotify_init1": SYS_INOTIFY_INIT1,
|
|
||||||
"preadv": SYS_PREADV,
|
|
||||||
"pwritev": SYS_PWRITEV,
|
|
||||||
"rt_tgsigqueueinfo": SYS_RT_TGSIGQUEUEINFO,
|
|
||||||
"perf_event_open": SYS_PERF_EVENT_OPEN,
|
|
||||||
"recvmmsg": SYS_RECVMMSG,
|
|
||||||
"fanotify_init": SYS_FANOTIFY_INIT,
|
|
||||||
"fanotify_mark": SYS_FANOTIFY_MARK,
|
|
||||||
"prlimit64": SYS_PRLIMIT64,
|
|
||||||
"name_to_handle_at": SYS_NAME_TO_HANDLE_AT,
|
|
||||||
"open_by_handle_at": SYS_OPEN_BY_HANDLE_AT,
|
|
||||||
"clock_adjtime": SYS_CLOCK_ADJTIME,
|
|
||||||
"syncfs": SYS_SYNCFS,
|
|
||||||
"sendmmsg": SYS_SENDMMSG,
|
|
||||||
"setns": SYS_SETNS,
|
|
||||||
"getcpu": SYS_GETCPU,
|
|
||||||
"process_vm_readv": SYS_PROCESS_VM_READV,
|
|
||||||
"process_vm_writev": SYS_PROCESS_VM_WRITEV,
|
|
||||||
"kcmp": SYS_KCMP,
|
|
||||||
"finit_module": SYS_FINIT_MODULE,
|
|
||||||
"sched_setattr": SYS_SCHED_SETATTR,
|
|
||||||
"sched_getattr": SYS_SCHED_GETATTR,
|
|
||||||
"renameat2": SYS_RENAMEAT2,
|
|
||||||
"seccomp": SYS_SECCOMP,
|
|
||||||
"getrandom": SYS_GETRANDOM,
|
|
||||||
"memfd_create": SYS_MEMFD_CREATE,
|
|
||||||
"kexec_file_load": SYS_KEXEC_FILE_LOAD,
|
|
||||||
"bpf": SYS_BPF,
|
|
||||||
"execveat": SYS_EXECVEAT,
|
|
||||||
"userfaultfd": SYS_USERFAULTFD,
|
|
||||||
"membarrier": SYS_MEMBARRIER,
|
|
||||||
"mlock2": SYS_MLOCK2,
|
|
||||||
"copy_file_range": SYS_COPY_FILE_RANGE,
|
|
||||||
"preadv2": SYS_PREADV2,
|
|
||||||
"pwritev2": SYS_PWRITEV2,
|
|
||||||
"pkey_mprotect": SYS_PKEY_MPROTECT,
|
|
||||||
"pkey_alloc": SYS_PKEY_ALLOC,
|
|
||||||
"pkey_free": SYS_PKEY_FREE,
|
|
||||||
"statx": SYS_STATX,
|
|
||||||
"io_pgetevents": SYS_IO_PGETEVENTS,
|
|
||||||
"rseq": SYS_RSEQ,
|
|
||||||
"uretprobe": SYS_URETPROBE,
|
|
||||||
"pidfd_send_signal": SYS_PIDFD_SEND_SIGNAL,
|
|
||||||
"io_uring_setup": SYS_IO_URING_SETUP,
|
|
||||||
"io_uring_enter": SYS_IO_URING_ENTER,
|
|
||||||
"io_uring_register": SYS_IO_URING_REGISTER,
|
|
||||||
"open_tree": SYS_OPEN_TREE,
|
|
||||||
"move_mount": SYS_MOVE_MOUNT,
|
|
||||||
"fsopen": SYS_FSOPEN,
|
|
||||||
"fsconfig": SYS_FSCONFIG,
|
|
||||||
"fsmount": SYS_FSMOUNT,
|
|
||||||
"fspick": SYS_FSPICK,
|
|
||||||
"pidfd_open": SYS_PIDFD_OPEN,
|
|
||||||
"clone3": SYS_CLONE3,
|
|
||||||
"close_range": SYS_CLOSE_RANGE,
|
|
||||||
"openat2": SYS_OPENAT2,
|
|
||||||
"pidfd_getfd": SYS_PIDFD_GETFD,
|
|
||||||
"faccessat2": SYS_FACCESSAT2,
|
|
||||||
"process_madvise": SYS_PROCESS_MADVISE,
|
|
||||||
"epoll_pwait2": SYS_EPOLL_PWAIT2,
|
|
||||||
"mount_setattr": SYS_MOUNT_SETATTR,
|
|
||||||
"quotactl_fd": SYS_QUOTACTL_FD,
|
|
||||||
"landlock_create_ruleset": SYS_LANDLOCK_CREATE_RULESET,
|
|
||||||
"landlock_add_rule": SYS_LANDLOCK_ADD_RULE,
|
|
||||||
"landlock_restrict_self": SYS_LANDLOCK_RESTRICT_SELF,
|
|
||||||
"memfd_secret": SYS_MEMFD_SECRET,
|
|
||||||
"process_mrelease": SYS_PROCESS_MRELEASE,
|
|
||||||
"futex_waitv": SYS_FUTEX_WAITV,
|
|
||||||
"set_mempolicy_home_node": SYS_SET_MEMPOLICY_HOME_NODE,
|
|
||||||
"cachestat": SYS_CACHESTAT,
|
|
||||||
"fchmodat2": SYS_FCHMODAT2,
|
|
||||||
"map_shadow_stack": SYS_MAP_SHADOW_STACK,
|
|
||||||
"futex_wake": SYS_FUTEX_WAKE,
|
|
||||||
"futex_wait": SYS_FUTEX_WAIT,
|
|
||||||
"futex_requeue": SYS_FUTEX_REQUEUE,
|
|
||||||
"statmount": SYS_STATMOUNT,
|
|
||||||
"listmount": SYS_LISTMOUNT,
|
|
||||||
"lsm_get_self_attr": SYS_LSM_GET_SELF_ATTR,
|
|
||||||
"lsm_set_self_attr": SYS_LSM_SET_SELF_ATTR,
|
|
||||||
"lsm_list_modules": SYS_LSM_LIST_MODULES,
|
|
||||||
"mseal": SYS_MSEAL,
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
|
||||||
SYS_NAME_TO_HANDLE_AT = 303
|
|
||||||
SYS_OPEN_BY_HANDLE_AT = 304
|
|
||||||
SYS_CLOCK_ADJTIME = 305
|
|
||||||
SYS_SYNCFS = 306
|
|
||||||
SYS_SENDMMSG = 307
|
|
||||||
SYS_SETNS = 308
|
|
||||||
SYS_GETCPU = 309
|
|
||||||
SYS_PROCESS_VM_READV = 310
|
|
||||||
SYS_PROCESS_VM_WRITEV = 311
|
|
||||||
SYS_KCMP = 312
|
|
||||||
SYS_FINIT_MODULE = 313
|
|
||||||
SYS_SCHED_SETATTR = 314
|
|
||||||
SYS_SCHED_GETATTR = 315
|
|
||||||
SYS_RENAMEAT2 = 316
|
|
||||||
SYS_SECCOMP = 317
|
|
||||||
SYS_GETRANDOM = 318
|
|
||||||
SYS_MEMFD_CREATE = 319
|
|
||||||
SYS_KEXEC_FILE_LOAD = 320
|
|
||||||
SYS_BPF = 321
|
|
||||||
SYS_EXECVEAT = 322
|
|
||||||
SYS_USERFAULTFD = 323
|
|
||||||
SYS_MEMBARRIER = 324
|
|
||||||
SYS_MLOCK2 = 325
|
|
||||||
SYS_COPY_FILE_RANGE = 326
|
|
||||||
SYS_PREADV2 = 327
|
|
||||||
SYS_PWRITEV2 = 328
|
|
||||||
SYS_PKEY_MPROTECT = 329
|
|
||||||
SYS_PKEY_ALLOC = 330
|
|
||||||
SYS_PKEY_FREE = 331
|
|
||||||
SYS_STATX = 332
|
|
||||||
SYS_IO_PGETEVENTS = 333
|
|
||||||
SYS_RSEQ = 334
|
|
||||||
SYS_URETPROBE = 335
|
|
||||||
SYS_PIDFD_SEND_SIGNAL = 424
|
|
||||||
SYS_IO_URING_SETUP = 425
|
|
||||||
SYS_IO_URING_ENTER = 426
|
|
||||||
SYS_IO_URING_REGISTER = 427
|
|
||||||
SYS_OPEN_TREE = 428
|
|
||||||
SYS_MOVE_MOUNT = 429
|
|
||||||
SYS_FSOPEN = 430
|
|
||||||
SYS_FSCONFIG = 431
|
|
||||||
SYS_FSMOUNT = 432
|
|
||||||
SYS_FSPICK = 433
|
|
||||||
SYS_PIDFD_OPEN = 434
|
|
||||||
SYS_CLONE3 = 435
|
|
||||||
SYS_CLOSE_RANGE = 436
|
|
||||||
SYS_OPENAT2 = 437
|
|
||||||
SYS_PIDFD_GETFD = 438
|
|
||||||
SYS_FACCESSAT2 = 439
|
|
||||||
SYS_PROCESS_MADVISE = 440
|
|
||||||
SYS_EPOLL_PWAIT2 = 441
|
|
||||||
SYS_MOUNT_SETATTR = 442
|
|
||||||
SYS_QUOTACTL_FD = 443
|
|
||||||
SYS_LANDLOCK_CREATE_RULESET = 444
|
|
||||||
SYS_LANDLOCK_ADD_RULE = 445
|
|
||||||
SYS_LANDLOCK_RESTRICT_SELF = 446
|
|
||||||
SYS_MEMFD_SECRET = 447
|
|
||||||
SYS_PROCESS_MRELEASE = 448
|
|
||||||
SYS_FUTEX_WAITV = 449
|
|
||||||
SYS_SET_MEMPOLICY_HOME_NODE = 450
|
|
||||||
SYS_CACHESTAT = 451
|
|
||||||
SYS_FCHMODAT2 = 452
|
|
||||||
SYS_MAP_SHADOW_STACK = 453
|
|
||||||
SYS_FUTEX_WAKE = 454
|
|
||||||
SYS_FUTEX_WAIT = 455
|
|
||||||
SYS_FUTEX_REQUEUE = 456
|
|
||||||
SYS_STATMOUNT = 457
|
|
||||||
SYS_LISTMOUNT = 458
|
|
||||||
SYS_LSM_GET_SELF_ATTR = 459
|
|
||||||
SYS_LSM_SET_SELF_ATTR = 460
|
|
||||||
SYS_LSM_LIST_MODULES = 461
|
|
||||||
SYS_MSEAL = 462
|
|
||||||
)
|
|
@ -1,20 +0,0 @@
|
|||||||
package seccomp
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestSyscallResolveName(t *testing.T) {
|
|
||||||
for name, want := range Syscalls() {
|
|
||||||
t.Run(name, func(t *testing.T) {
|
|
||||||
if got := syscallResolveName(name); got != want {
|
|
||||||
t.Errorf("syscallResolveName(%q) = %d, want %d",
|
|
||||||
name, got, want)
|
|
||||||
}
|
|
||||||
if got, ok := SyscallResolveName(name); !ok || got != want {
|
|
||||||
t.Errorf("SyscallResolveName(%q) = %d, want %d",
|
|
||||||
name, got, want)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
@ -5,7 +5,7 @@ import (
|
|||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"hakurei.app/system/dbus"
|
"git.gensokyo.uk/security/hakurei/dbus"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestParse(t *testing.T) {
|
func TestParse(t *testing.T) {
|
@ -9,7 +9,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"hakurei.app/system/dbus"
|
"git.gensokyo.uk/security/hakurei/dbus"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestConfig_Args(t *testing.T) {
|
func TestConfig_Args(t *testing.T) {
|
@ -13,11 +13,11 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"hakurei.app/container"
|
"git.gensokyo.uk/security/hakurei/dbus"
|
||||||
"hakurei.app/helper"
|
"git.gensokyo.uk/security/hakurei/helper"
|
||||||
"hakurei.app/internal"
|
"git.gensokyo.uk/security/hakurei/internal"
|
||||||
"hakurei.app/internal/hlog"
|
"git.gensokyo.uk/security/hakurei/internal/hlog"
|
||||||
"hakurei.app/system/dbus"
|
"git.gensokyo.uk/security/hakurei/sandbox"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestFinalise(t *testing.T) {
|
func TestFinalise(t *testing.T) {
|
||||||
@ -134,11 +134,11 @@ func testProxyFinaliseStartWaitCloseString(t *testing.T, useSandbox bool) {
|
|||||||
}
|
}
|
||||||
p.CmdF = func(v any) {
|
p.CmdF = func(v any) {
|
||||||
if useSandbox {
|
if useSandbox {
|
||||||
z := v.(*container.Container)
|
container := v.(*sandbox.Container)
|
||||||
if z.Args[0] != dbus.ProxyName {
|
if container.Args[0] != dbus.ProxyName {
|
||||||
panic(fmt.Sprintf("unexpected argv0 %q", os.Args[0]))
|
panic(fmt.Sprintf("unexpected argv0 %q", os.Args[0]))
|
||||||
}
|
}
|
||||||
z.Args = append([]string{os.Args[0], "-test.run=TestHelperStub", "--"}, z.Args[1:]...)
|
container.Args = append([]string{os.Args[0], "-test.run=TestHelperStub", "--"}, container.Args[1:]...)
|
||||||
} else {
|
} else {
|
||||||
cmd := v.(*exec.Cmd)
|
cmd := v.(*exec.Cmd)
|
||||||
if cmd.Args[0] != dbus.ProxyName {
|
if cmd.Args[0] != dbus.ProxyName {
|
||||||
@ -178,7 +178,7 @@ func testProxyFinaliseStartWaitCloseString(t *testing.T, useSandbox bool) {
|
|||||||
t.Run("string", func(t *testing.T) {
|
t.Run("string", func(t *testing.T) {
|
||||||
wantSubstr := fmt.Sprintf("%s -test.run=TestHelperStub -- --args=3 --fd=4", os.Args[0])
|
wantSubstr := fmt.Sprintf("%s -test.run=TestHelperStub -- --args=3 --fd=4", os.Args[0])
|
||||||
if useSandbox {
|
if useSandbox {
|
||||||
wantSubstr = fmt.Sprintf(`argv: ["%s" "-test.run=TestHelperStub" "--" "--args=3" "--fd=4"], filter: true, rules: 0, flags: 0x1, presets: 0xf`, os.Args[0])
|
wantSubstr = fmt.Sprintf(`argv: ["%s" "-test.run=TestHelperStub" "--" "--args=3" "--fd=4"], flags: 0x0, seccomp: 0x3e`, os.Args[0])
|
||||||
}
|
}
|
||||||
if got := p.String(); !strings.Contains(got, wantSubstr) {
|
if got := p.String(); !strings.Contains(got, wantSubstr) {
|
||||||
t.Errorf("String: %q, want %q",
|
t.Errorf("String: %q, want %q",
|
||||||
@ -208,6 +208,6 @@ func TestHelperInit(t *testing.T) {
|
|||||||
if len(os.Args) != 5 || os.Args[4] != "init" {
|
if len(os.Args) != 5 || os.Args[4] != "init" {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
container.SetOutput(hlog.Output{})
|
sandbox.SetOutput(hlog.Output{})
|
||||||
container.Init(hlog.Prepare, internal.InstallOutput)
|
sandbox.Init(hlog.Prepare, internal.InstallFmsg)
|
||||||
}
|
}
|
@ -11,10 +11,10 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"hakurei.app/container"
|
"git.gensokyo.uk/security/hakurei/helper"
|
||||||
"hakurei.app/container/seccomp"
|
"git.gensokyo.uk/security/hakurei/ldd"
|
||||||
"hakurei.app/helper"
|
"git.gensokyo.uk/security/hakurei/sandbox"
|
||||||
"hakurei.app/ldd"
|
"git.gensokyo.uk/security/hakurei/sandbox/seccomp"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Start starts and configures a D-Bus proxy process.
|
// Start starts and configures a D-Bus proxy process.
|
||||||
@ -65,22 +65,21 @@ func (p *Proxy) Start() error {
|
|||||||
p.helper = helper.New(
|
p.helper = helper.New(
|
||||||
ctx, toolPath,
|
ctx, toolPath,
|
||||||
p.final, true,
|
p.final, true,
|
||||||
argF, func(z *container.Container) {
|
argF, func(container *sandbox.Container) {
|
||||||
z.SeccompFlags |= seccomp.AllowMultiarch
|
container.Seccomp |= seccomp.FilterMultiarch
|
||||||
z.SeccompPresets |= seccomp.PresetStrict
|
container.Hostname = "hakurei-dbus"
|
||||||
z.Hostname = "hakurei-dbus"
|
container.CommandContext = p.CommandContext
|
||||||
z.CommandContext = p.CommandContext
|
|
||||||
if p.output != nil {
|
if p.output != nil {
|
||||||
z.Stdout, z.Stderr = p.output, p.output
|
container.Stdout, container.Stderr = p.output, p.output
|
||||||
}
|
}
|
||||||
|
|
||||||
if p.CmdF != nil {
|
if p.CmdF != nil {
|
||||||
p.CmdF(z)
|
p.CmdF(container)
|
||||||
}
|
}
|
||||||
|
|
||||||
// these lib paths are unpredictable, so mount them first so they cannot cover anything
|
// these lib paths are unpredictable, so mount them first so they cannot cover anything
|
||||||
for _, name := range libPaths {
|
for _, name := range libPaths {
|
||||||
z.Bind(name, name, 0)
|
container.Bind(name, name, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// upstream bus directories
|
// upstream bus directories
|
||||||
@ -101,7 +100,7 @@ func (p *Proxy) Start() error {
|
|||||||
slices.Sort(upstreamPaths)
|
slices.Sort(upstreamPaths)
|
||||||
upstreamPaths = slices.Compact(upstreamPaths)
|
upstreamPaths = slices.Compact(upstreamPaths)
|
||||||
for _, name := range upstreamPaths {
|
for _, name := range upstreamPaths {
|
||||||
z.Bind(name, name, 0)
|
container.Bind(name, name, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// parent directories of bind paths
|
// parent directories of bind paths
|
||||||
@ -115,12 +114,12 @@ func (p *Proxy) Start() error {
|
|||||||
slices.Sort(sockDirPaths)
|
slices.Sort(sockDirPaths)
|
||||||
sockDirPaths = slices.Compact(sockDirPaths)
|
sockDirPaths = slices.Compact(sockDirPaths)
|
||||||
for _, name := range sockDirPaths {
|
for _, name := range sockDirPaths {
|
||||||
z.Bind(name, name, container.BindWritable)
|
container.Bind(name, name, sandbox.BindWritable)
|
||||||
}
|
}
|
||||||
|
|
||||||
// xdg-dbus-proxy bin path
|
// xdg-dbus-proxy bin path
|
||||||
binPath := path.Dir(toolPath)
|
binPath := path.Dir(toolPath)
|
||||||
z.Bind(binPath, binPath, 0)
|
container.Bind(binPath, binPath, 0)
|
||||||
}, nil)
|
}, nil)
|
||||||
}
|
}
|
||||||
|
|
@ -8,7 +8,7 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"hakurei.app/helper"
|
"git.gensokyo.uk/security/hakurei/helper"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ProxyName is the file name or path to the proxy program.
|
// ProxyName is the file name or path to the proxy program.
|
@ -3,7 +3,7 @@ package dbus_test
|
|||||||
import (
|
import (
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"hakurei.app/system/dbus"
|
"git.gensokyo.uk/security/hakurei/dbus"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
@ -3,7 +3,7 @@ package dbus_test
|
|||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"hakurei.app/helper"
|
"git.gensokyo.uk/security/hakurei/helper"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestHelperStub(t *testing.T) { helper.InternalHelperStub() }
|
func TestHelperStub(t *testing.T) { helper.InternalHelperStub() }
|
6
dist/release.sh
vendored
6
dist/release.sh
vendored
@ -10,9 +10,9 @@ cp -rv "dist/comp" "${out}"
|
|||||||
|
|
||||||
go generate ./...
|
go generate ./...
|
||||||
go build -trimpath -v -o "${out}/bin/" -ldflags "-s -w -buildid= -extldflags '-static'
|
go build -trimpath -v -o "${out}/bin/" -ldflags "-s -w -buildid= -extldflags '-static'
|
||||||
-X hakurei.app/internal.version=${VERSION}
|
-X git.gensokyo.uk/security/hakurei/internal.version=${VERSION}
|
||||||
-X hakurei.app/internal.hmain=/usr/bin/hakurei
|
-X git.gensokyo.uk/security/hakurei/internal.hakurei=/usr/bin/hakurei
|
||||||
-X hakurei.app/internal.hsu=/usr/bin/hsu
|
-X git.gensokyo.uk/security/hakurei/internal.hsu=/usr/bin/hsu
|
||||||
-X main.hmain=/usr/bin/hakurei" ./...
|
-X main.hmain=/usr/bin/hakurei" ./...
|
||||||
|
|
||||||
rm -f "./${out}.tar.gz" && tar -C dist -czf "${out}.tar.gz" "${pname}"
|
rm -f "./${out}.tar.gz" && tar -C dist -czf "${out}.tar.gz" "${pname}"
|
||||||
|
12
flake.nix
12
flake.nix
@ -184,18 +184,6 @@
|
|||||||
exec cat ${docText} > options.md
|
exec cat ${docText} > options.md
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
generateSyscallTable = pkgs.mkShell {
|
|
||||||
# this should be made cross-platform via nix
|
|
||||||
shellHook = "exec ${pkgs.writeShellScript "generate-syscall-table" ''
|
|
||||||
set -e
|
|
||||||
${pkgs.perl}/bin/perl \
|
|
||||||
sandbox/seccomp/mksysnum_linux.pl \
|
|
||||||
${pkgs.linuxHeaders}/include/asm/unistd_64.h | \
|
|
||||||
${pkgs.go}/bin/gofmt > \
|
|
||||||
sandbox/seccomp/syscall_linux_amd64.go
|
|
||||||
''}";
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
2
go.mod
2
go.mod
@ -1,3 +1,3 @@
|
|||||||
module hakurei.app
|
module git.gensokyo.uk/security/hakurei
|
||||||
|
|
||||||
go 1.24
|
go 1.24
|
||||||
|
@ -7,7 +7,7 @@ import (
|
|||||||
"syscall"
|
"syscall"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"hakurei.app/helper"
|
"git.gensokyo.uk/security/hakurei/helper"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestArgsString(t *testing.T) {
|
func TestArgsString(t *testing.T) {
|
||||||
|
@ -10,7 +10,7 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"hakurei.app/helper/proc"
|
"git.gensokyo.uk/security/hakurei/helper/proc"
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewDirect initialises a new direct Helper instance with wt as the null-terminated argument writer.
|
// NewDirect initialises a new direct Helper instance with wt as the null-terminated argument writer.
|
||||||
|
@ -8,7 +8,7 @@ import (
|
|||||||
"os/exec"
|
"os/exec"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"hakurei.app/helper"
|
"git.gensokyo.uk/security/hakurei/helper"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestCmd(t *testing.T) {
|
func TestCmd(t *testing.T) {
|
||||||
|
@ -9,8 +9,8 @@ import (
|
|||||||
"slices"
|
"slices"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"hakurei.app/container"
|
"git.gensokyo.uk/security/hakurei/helper/proc"
|
||||||
"hakurei.app/helper/proc"
|
"git.gensokyo.uk/security/hakurei/sandbox"
|
||||||
)
|
)
|
||||||
|
|
||||||
// New initialises a Helper instance with wt as the null-terminated argument writer.
|
// New initialises a Helper instance with wt as the null-terminated argument writer.
|
||||||
@ -20,13 +20,13 @@ func New(
|
|||||||
wt io.WriterTo,
|
wt io.WriterTo,
|
||||||
stat bool,
|
stat bool,
|
||||||
argF func(argsFd, statFd int) []string,
|
argF func(argsFd, statFd int) []string,
|
||||||
cmdF func(z *container.Container),
|
cmdF func(container *sandbox.Container),
|
||||||
extraFiles []*os.File,
|
extraFiles []*os.File,
|
||||||
) Helper {
|
) Helper {
|
||||||
var args []string
|
var args []string
|
||||||
h := new(helperContainer)
|
h := new(helperContainer)
|
||||||
h.helperFiles, args = newHelperFiles(ctx, wt, stat, argF, extraFiles)
|
h.helperFiles, args = newHelperFiles(ctx, wt, stat, argF, extraFiles)
|
||||||
h.Container = container.New(ctx, name, args...)
|
h.Container = sandbox.New(ctx, name, args...)
|
||||||
h.WaitDelay = WaitDelay
|
h.WaitDelay = WaitDelay
|
||||||
if cmdF != nil {
|
if cmdF != nil {
|
||||||
cmdF(h.Container)
|
cmdF(h.Container)
|
||||||
@ -40,7 +40,7 @@ type helperContainer struct {
|
|||||||
|
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
*helperFiles
|
*helperFiles
|
||||||
*container.Container
|
*sandbox.Container
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *helperContainer) Start() error {
|
func (h *helperContainer) Start() error {
|
||||||
|
@ -7,10 +7,10 @@ import (
|
|||||||
"os/exec"
|
"os/exec"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"hakurei.app/container"
|
"git.gensokyo.uk/security/hakurei/helper"
|
||||||
"hakurei.app/helper"
|
"git.gensokyo.uk/security/hakurei/internal"
|
||||||
"hakurei.app/internal"
|
"git.gensokyo.uk/security/hakurei/internal/hlog"
|
||||||
"hakurei.app/internal/hlog"
|
"git.gensokyo.uk/security/hakurei/sandbox"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestContainer(t *testing.T) {
|
func TestContainer(t *testing.T) {
|
||||||
@ -34,13 +34,15 @@ func TestContainer(t *testing.T) {
|
|||||||
|
|
||||||
t.Run("implementation compliance", func(t *testing.T) {
|
t.Run("implementation compliance", func(t *testing.T) {
|
||||||
testHelper(t, func(ctx context.Context, setOutput func(stdoutP, stderrP *io.Writer), stat bool) helper.Helper {
|
testHelper(t, func(ctx context.Context, setOutput func(stdoutP, stderrP *io.Writer), stat bool) helper.Helper {
|
||||||
return helper.New(ctx, os.Args[0], argsWt, stat, argF, func(z *container.Container) {
|
return helper.New(ctx, os.Args[0], argsWt, stat, argF, func(container *sandbox.Container) {
|
||||||
setOutput(&z.Stdout, &z.Stderr)
|
setOutput(&container.Stdout, &container.Stderr)
|
||||||
z.CommandContext = func(ctx context.Context) (cmd *exec.Cmd) {
|
container.CommandContext = func(ctx context.Context) (cmd *exec.Cmd) {
|
||||||
return exec.CommandContext(ctx, os.Args[0], "-test.v",
|
return exec.CommandContext(ctx, os.Args[0], "-test.v",
|
||||||
"-test.run=TestHelperInit", "--", "init")
|
"-test.run=TestHelperInit", "--", "init")
|
||||||
}
|
}
|
||||||
z.Bind("/", "/", 0).Proc("/proc").Dev("/dev")
|
container.Bind("/", "/", 0)
|
||||||
|
container.Proc("/proc")
|
||||||
|
container.Dev("/dev")
|
||||||
}, nil)
|
}, nil)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -50,6 +52,6 @@ func TestHelperInit(t *testing.T) {
|
|||||||
if len(os.Args) != 5 || os.Args[4] != "init" {
|
if len(os.Args) != 5 || os.Args[4] != "init" {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
container.SetOutput(hlog.Output{})
|
sandbox.SetOutput(hlog.Output{})
|
||||||
container.Init(hlog.Prepare, func(bool) { internal.InstallOutput(false) })
|
sandbox.Init(hlog.Prepare, func(bool) { internal.InstallFmsg(false) })
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"hakurei.app/helper/proc"
|
"git.gensokyo.uk/security/hakurei/helper/proc"
|
||||||
)
|
)
|
||||||
|
|
||||||
var WaitDelay = 2 * time.Second
|
var WaitDelay = 2 * time.Second
|
||||||
|
@ -11,7 +11,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"hakurei.app/helper"
|
"git.gensokyo.uk/security/hakurei/helper"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -3,7 +3,7 @@ package helper_test
|
|||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"hakurei.app/helper"
|
"git.gensokyo.uk/security/hakurei/helper"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestHelperStub(t *testing.T) { helper.InternalHelperStub() }
|
func TestHelperStub(t *testing.T) { helper.InternalHelperStub() }
|
||||||
|
@ -2,8 +2,8 @@
|
|||||||
package hst
|
package hst
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"hakurei.app/system"
|
"git.gensokyo.uk/security/hakurei/dbus"
|
||||||
"hakurei.app/system/dbus"
|
"git.gensokyo.uk/security/hakurei/system"
|
||||||
)
|
)
|
||||||
|
|
||||||
const Tmp = "/.hakurei"
|
const Tmp = "/.hakurei"
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package hst
|
package hst
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"hakurei.app/container/seccomp"
|
"git.gensokyo.uk/security/hakurei/sandbox/seccomp"
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
@ -11,11 +11,7 @@ type (
|
|||||||
Hostname string `json:"hostname,omitempty"`
|
Hostname string `json:"hostname,omitempty"`
|
||||||
|
|
||||||
// extra seccomp flags
|
// extra seccomp flags
|
||||||
SeccompFlags seccomp.ExportFlag `json:"seccomp_flags"`
|
Seccomp seccomp.FilterOpts `json:"seccomp"`
|
||||||
// extra seccomp presets
|
|
||||||
SeccompPresets seccomp.FilterPreset `json:"seccomp_presets"`
|
|
||||||
// disable project-specific filter extensions
|
|
||||||
SeccompCompat bool `json:"seccomp_compat,omitempty"`
|
|
||||||
// allow ptrace and friends
|
// allow ptrace and friends
|
||||||
Devel bool `json:"devel,omitempty"`
|
Devel bool `json:"devel,omitempty"`
|
||||||
// allow userns creation in container
|
// allow userns creation in container
|
||||||
|
11
hst/paths.go
11
hst/paths.go
@ -1,11 +0,0 @@
|
|||||||
package hst
|
|
||||||
|
|
||||||
// Paths contains environment-dependent paths used by hakurei.
|
|
||||||
type Paths struct {
|
|
||||||
// path to shared directory (usually `/tmp/hakurei.%d`)
|
|
||||||
SharePath string `json:"share_path"`
|
|
||||||
// XDG_RUNTIME_DIR value (usually `/run/user/%d`)
|
|
||||||
RuntimePath string `json:"runtime_path"`
|
|
||||||
// application runtime directory (usually `/run/user/%d/hakurei`)
|
|
||||||
RunDirPath string `json:"run_dir_path"`
|
|
||||||
}
|
|
@ -1,9 +1,9 @@
|
|||||||
package hst
|
package hst
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"hakurei.app/container/seccomp"
|
"git.gensokyo.uk/security/hakurei/dbus"
|
||||||
"hakurei.app/system"
|
"git.gensokyo.uk/security/hakurei/sandbox/seccomp"
|
||||||
"hakurei.app/system/dbus"
|
"git.gensokyo.uk/security/hakurei/system"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Template returns a fully populated instance of Config.
|
// Template returns a fully populated instance of Config.
|
||||||
@ -62,8 +62,7 @@ func Template() *Config {
|
|||||||
Userns: true,
|
Userns: true,
|
||||||
Net: true,
|
Net: true,
|
||||||
Device: true,
|
Device: true,
|
||||||
SeccompFlags: seccomp.AllowMultiarch,
|
Seccomp: seccomp.FilterMultiarch,
|
||||||
SeccompPresets: seccomp.PresetExt,
|
|
||||||
Tty: true,
|
Tty: true,
|
||||||
Multiarch: true,
|
Multiarch: true,
|
||||||
MapRealUID: true,
|
MapRealUID: true,
|
||||||
|
@ -4,7 +4,7 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"hakurei.app/hst"
|
"git.gensokyo.uk/security/hakurei/hst"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestTemplate(t *testing.T) {
|
func TestTemplate(t *testing.T) {
|
||||||
@ -80,8 +80,7 @@ func TestTemplate(t *testing.T) {
|
|||||||
],
|
],
|
||||||
"container": {
|
"container": {
|
||||||
"hostname": "localhost",
|
"hostname": "localhost",
|
||||||
"seccomp_flags": 1,
|
"seccomp": 32,
|
||||||
"seccomp_presets": 1,
|
|
||||||
"devel": true,
|
"devel": true,
|
||||||
"userns": true,
|
"userns": true,
|
||||||
"net": true,
|
"net": true,
|
||||||
|
@ -5,7 +5,7 @@ import (
|
|||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"hakurei.app/hst"
|
"git.gensokyo.uk/security/hakurei/hst"
|
||||||
)
|
)
|
||||||
|
|
||||||
type App interface {
|
type App interface {
|
||||||
@ -47,3 +47,13 @@ func (rs *RunState) SetStart() {
|
|||||||
now := time.Now().UTC()
|
now := time.Now().UTC()
|
||||||
rs.Time = &now
|
rs.Time = &now
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Paths contains environment-dependent paths used by hakurei.
|
||||||
|
type Paths struct {
|
||||||
|
// path to shared directory (usually `/tmp/hakurei.%d`)
|
||||||
|
SharePath string `json:"share_path"`
|
||||||
|
// XDG_RUNTIME_DIR value (usually `/run/user/%d`)
|
||||||
|
RuntimePath string `json:"runtime_path"`
|
||||||
|
// application runtime directory (usually `/run/user/%d/hakurei`)
|
||||||
|
RunDirPath string `json:"run_dir_path"`
|
||||||
|
}
|
@ -4,7 +4,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
. "hakurei.app/cmd/hakurei/internal/app"
|
. "git.gensokyo.uk/security/hakurei/internal/app"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestParseAppID(t *testing.T) {
|
func TestParseAppID(t *testing.T) {
|
@ -8,11 +8,11 @@ import (
|
|||||||
"path"
|
"path"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"hakurei.app/container"
|
"git.gensokyo.uk/security/hakurei/dbus"
|
||||||
"hakurei.app/container/seccomp"
|
"git.gensokyo.uk/security/hakurei/hst"
|
||||||
"hakurei.app/hst"
|
"git.gensokyo.uk/security/hakurei/internal/sys"
|
||||||
"hakurei.app/internal/sys"
|
"git.gensokyo.uk/security/hakurei/sandbox"
|
||||||
"hakurei.app/system/dbus"
|
"git.gensokyo.uk/security/hakurei/sandbox/seccomp"
|
||||||
)
|
)
|
||||||
|
|
||||||
// in practice there should be less than 30 entries added by the runtime;
|
// in practice there should be less than 30 entries added by the runtime;
|
||||||
@ -21,61 +21,58 @@ const preallocateOpsCount = 1 << 5
|
|||||||
|
|
||||||
// NewContainer initialises [sandbox.Params] via [hst.ContainerConfig].
|
// NewContainer initialises [sandbox.Params] via [hst.ContainerConfig].
|
||||||
// Note that remaining container setup must be queued by the caller.
|
// Note that remaining container setup must be queued by the caller.
|
||||||
func NewContainer(s *hst.ContainerConfig, os sys.State, uid, gid *int) (*container.Params, map[string]string, error) {
|
func NewContainer(s *hst.ContainerConfig, os sys.State, uid, gid *int) (*sandbox.Params, map[string]string, error) {
|
||||||
if s == nil {
|
if s == nil {
|
||||||
return nil, nil, syscall.EBADE
|
return nil, nil, syscall.EBADE
|
||||||
}
|
}
|
||||||
|
|
||||||
params := &container.Params{
|
container := &sandbox.Params{
|
||||||
Hostname: s.Hostname,
|
Hostname: s.Hostname,
|
||||||
SeccompFlags: s.SeccompFlags,
|
Seccomp: s.Seccomp,
|
||||||
SeccompPresets: s.SeccompPresets,
|
|
||||||
RetainSession: s.Tty,
|
|
||||||
HostNet: s.Net,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
ops := make(container.Ops, 0, preallocateOpsCount+len(s.Filesystem)+len(s.Link)+len(s.Cover))
|
ops := make(sandbox.Ops, 0, preallocateOpsCount+len(s.Filesystem)+len(s.Link)+len(s.Cover))
|
||||||
params.Ops = &ops
|
container.Ops = &ops
|
||||||
}
|
}
|
||||||
|
|
||||||
if s.Multiarch {
|
if s.Multiarch {
|
||||||
params.SeccompFlags |= seccomp.AllowMultiarch
|
container.Seccomp |= seccomp.FilterMultiarch
|
||||||
}
|
}
|
||||||
|
|
||||||
if !s.SeccompCompat {
|
if s.Devel {
|
||||||
params.SeccompPresets |= seccomp.PresetExt
|
container.Flags |= sandbox.FAllowDevel
|
||||||
}
|
}
|
||||||
if !s.Devel {
|
if s.Userns {
|
||||||
params.SeccompPresets |= seccomp.PresetDenyDevel
|
container.Flags |= sandbox.FAllowUserns
|
||||||
}
|
}
|
||||||
if !s.Userns {
|
if s.Net {
|
||||||
params.SeccompPresets |= seccomp.PresetDenyNS
|
container.Flags |= sandbox.FAllowNet
|
||||||
}
|
}
|
||||||
if !s.Tty {
|
if s.Tty {
|
||||||
params.SeccompPresets |= seccomp.PresetDenyTTY
|
container.Flags |= sandbox.FAllowTTY
|
||||||
}
|
}
|
||||||
|
|
||||||
if s.MapRealUID {
|
if s.MapRealUID {
|
||||||
/* some programs fail to connect to dbus session running as a different uid
|
/* some programs fail to connect to dbus session running as a different uid
|
||||||
so this workaround is introduced to map priv-side caller uid in container */
|
so this workaround is introduced to map priv-side caller uid in container */
|
||||||
params.Uid = os.Getuid()
|
container.Uid = os.Getuid()
|
||||||
*uid = params.Uid
|
*uid = container.Uid
|
||||||
params.Gid = os.Getgid()
|
container.Gid = os.Getgid()
|
||||||
*gid = params.Gid
|
*gid = container.Gid
|
||||||
} else {
|
} else {
|
||||||
*uid = container.OverflowUid()
|
*uid = sandbox.OverflowUid()
|
||||||
*gid = container.OverflowGid()
|
*gid = sandbox.OverflowGid()
|
||||||
}
|
}
|
||||||
|
|
||||||
params.
|
container.
|
||||||
Proc("/proc").
|
Proc("/proc").
|
||||||
Tmpfs(hst.Tmp, 1<<12, 0755)
|
Tmpfs(hst.Tmp, 1<<12, 0755)
|
||||||
|
|
||||||
if !s.Device {
|
if !s.Device {
|
||||||
params.Dev("/dev").Mqueue("/dev/mqueue")
|
container.Dev("/dev").Mqueue("/dev/mqueue")
|
||||||
} else {
|
} else {
|
||||||
params.Bind("/dev", "/dev", container.BindWritable|container.BindDevice)
|
container.Bind("/dev", "/dev", sandbox.BindWritable|sandbox.BindDevice)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* retrieve paths and hide them if they're made available in the sandbox;
|
/* retrieve paths and hide them if they're made available in the sandbox;
|
||||||
@ -154,29 +151,29 @@ func NewContainer(s *hst.ContainerConfig, os sys.State, uid, gid *int) (*contain
|
|||||||
|
|
||||||
var flags int
|
var flags int
|
||||||
if c.Write {
|
if c.Write {
|
||||||
flags |= container.BindWritable
|
flags |= sandbox.BindWritable
|
||||||
}
|
}
|
||||||
if c.Device {
|
if c.Device {
|
||||||
flags |= container.BindDevice | container.BindWritable
|
flags |= sandbox.BindDevice | sandbox.BindWritable
|
||||||
}
|
}
|
||||||
if !c.Must {
|
if !c.Must {
|
||||||
flags |= container.BindOptional
|
flags |= sandbox.BindOptional
|
||||||
}
|
}
|
||||||
params.Bind(c.Src, dest, flags)
|
container.Bind(c.Src, dest, flags)
|
||||||
}
|
}
|
||||||
|
|
||||||
// cover matched paths
|
// cover matched paths
|
||||||
for i, ok := range hidePathMatch {
|
for i, ok := range hidePathMatch {
|
||||||
if ok {
|
if ok {
|
||||||
params.Tmpfs(hidePaths[i], 1<<13, 0755)
|
container.Tmpfs(hidePaths[i], 1<<13, 0755)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, l := range s.Link {
|
for _, l := range s.Link {
|
||||||
params.Link(l[0], l[1])
|
container.Link(l[0], l[1])
|
||||||
}
|
}
|
||||||
|
|
||||||
return params, maps.Clone(s.Env), nil
|
return container, maps.Clone(s.Env), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func evalSymlinks(os sys.State, v *string) error {
|
func evalSymlinks(os sys.State, v *string) error {
|
@ -3,8 +3,8 @@ package instance
|
|||||||
import (
|
import (
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"hakurei.app/cmd/hakurei/internal/app"
|
"git.gensokyo.uk/security/hakurei/internal/app"
|
||||||
"hakurei.app/cmd/hakurei/internal/app/internal/setuid"
|
"git.gensokyo.uk/security/hakurei/internal/app/internal/setuid"
|
||||||
)
|
)
|
||||||
|
|
||||||
func PrintRunStateErr(whence int, rs *app.RunState, runErr error) (code int) {
|
func PrintRunStateErr(whence int, rs *app.RunState, runErr error) (code int) {
|
@ -6,9 +6,9 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"hakurei.app/cmd/hakurei/internal/app"
|
"git.gensokyo.uk/security/hakurei/internal/app"
|
||||||
"hakurei.app/cmd/hakurei/internal/app/internal/setuid"
|
"git.gensokyo.uk/security/hakurei/internal/app/internal/setuid"
|
||||||
"hakurei.app/internal/sys"
|
"git.gensokyo.uk/security/hakurei/internal/sys"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
@ -1,6 +1,6 @@
|
|||||||
package instance
|
package instance
|
||||||
|
|
||||||
import "hakurei.app/cmd/hakurei/internal/app/internal/setuid"
|
import "git.gensokyo.uk/security/hakurei/internal/app/internal/setuid"
|
||||||
|
|
||||||
// ShimMain is the main function of the shim process and runs as the unconstrained target user.
|
// ShimMain is the main function of the shim process and runs as the unconstrained target user.
|
||||||
func ShimMain() { setuid.ShimMain() }
|
func ShimMain() { setuid.ShimMain() }
|
@ -5,10 +5,10 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
. "hakurei.app/cmd/hakurei/internal/app"
|
"git.gensokyo.uk/security/hakurei/hst"
|
||||||
"hakurei.app/hst"
|
. "git.gensokyo.uk/security/hakurei/internal/app"
|
||||||
"hakurei.app/internal/hlog"
|
"git.gensokyo.uk/security/hakurei/internal/hlog"
|
||||||
"hakurei.app/internal/sys"
|
"git.gensokyo.uk/security/hakurei/internal/sys"
|
||||||
)
|
)
|
||||||
|
|
||||||
func New(ctx context.Context, os sys.State) (App, error) {
|
func New(ctx context.Context, os sys.State) (App, error) {
|
@ -1,13 +1,12 @@
|
|||||||
package setuid_test
|
package setuid_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"hakurei.app/cmd/hakurei/internal/app"
|
"git.gensokyo.uk/security/hakurei/acl"
|
||||||
"hakurei.app/container"
|
"git.gensokyo.uk/security/hakurei/dbus"
|
||||||
"hakurei.app/container/seccomp"
|
"git.gensokyo.uk/security/hakurei/hst"
|
||||||
"hakurei.app/hst"
|
"git.gensokyo.uk/security/hakurei/internal/app"
|
||||||
"hakurei.app/system"
|
"git.gensokyo.uk/security/hakurei/sandbox"
|
||||||
"hakurei.app/system/acl"
|
"git.gensokyo.uk/security/hakurei/system"
|
||||||
"hakurei.app/system/dbus"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var testCasesNixos = []sealTestCase{
|
var testCasesNixos = []sealTestCase{
|
||||||
@ -94,9 +93,10 @@ var testCasesNixos = []sealTestCase{
|
|||||||
}).
|
}).
|
||||||
UpdatePerm("/tmp/hakurei.1971/8e2c76b066dabe574cf073bdb46eb5c1/bus", acl.Read, acl.Write).
|
UpdatePerm("/tmp/hakurei.1971/8e2c76b066dabe574cf073bdb46eb5c1/bus", acl.Read, acl.Write).
|
||||||
UpdatePerm("/tmp/hakurei.1971/8e2c76b066dabe574cf073bdb46eb5c1/system_bus_socket", acl.Read, acl.Write),
|
UpdatePerm("/tmp/hakurei.1971/8e2c76b066dabe574cf073bdb46eb5c1/system_bus_socket", acl.Read, acl.Write),
|
||||||
&container.Params{
|
&sandbox.Params{
|
||||||
Uid: 1971,
|
Uid: 1971,
|
||||||
Gid: 100,
|
Gid: 100,
|
||||||
|
Flags: sandbox.FAllowNet | sandbox.FAllowUserns,
|
||||||
Dir: "/var/lib/persist/module/hakurei/0/1",
|
Dir: "/var/lib/persist/module/hakurei/0/1",
|
||||||
Path: "/nix/store/yqivzpzzn7z5x0lq9hmbzygh45d8rhqd-chromium-start",
|
Path: "/nix/store/yqivzpzzn7z5x0lq9hmbzygh45d8rhqd-chromium-start",
|
||||||
Args: []string{"/nix/store/yqivzpzzn7z5x0lq9hmbzygh45d8rhqd-chromium-start"},
|
Args: []string{"/nix/store/yqivzpzzn7z5x0lq9hmbzygh45d8rhqd-chromium-start"},
|
||||||
@ -114,7 +114,7 @@ var testCasesNixos = []sealTestCase{
|
|||||||
"XDG_SESSION_CLASS=user",
|
"XDG_SESSION_CLASS=user",
|
||||||
"XDG_SESSION_TYPE=tty",
|
"XDG_SESSION_TYPE=tty",
|
||||||
},
|
},
|
||||||
Ops: new(container.Ops).
|
Ops: new(sandbox.Ops).
|
||||||
Proc("/proc").
|
Proc("/proc").
|
||||||
Tmpfs(hst.Tmp, 4096, 0755).
|
Tmpfs(hst.Tmp, 4096, 0755).
|
||||||
Dev("/dev").Mqueue("/dev/mqueue").
|
Dev("/dev").Mqueue("/dev/mqueue").
|
||||||
@ -122,18 +122,18 @@ var testCasesNixos = []sealTestCase{
|
|||||||
Bind("/usr/bin", "/usr/bin", 0).
|
Bind("/usr/bin", "/usr/bin", 0).
|
||||||
Bind("/nix/store", "/nix/store", 0).
|
Bind("/nix/store", "/nix/store", 0).
|
||||||
Bind("/run/current-system", "/run/current-system", 0).
|
Bind("/run/current-system", "/run/current-system", 0).
|
||||||
Bind("/sys/block", "/sys/block", container.BindOptional).
|
Bind("/sys/block", "/sys/block", sandbox.BindOptional).
|
||||||
Bind("/sys/bus", "/sys/bus", container.BindOptional).
|
Bind("/sys/bus", "/sys/bus", sandbox.BindOptional).
|
||||||
Bind("/sys/class", "/sys/class", container.BindOptional).
|
Bind("/sys/class", "/sys/class", sandbox.BindOptional).
|
||||||
Bind("/sys/dev", "/sys/dev", container.BindOptional).
|
Bind("/sys/dev", "/sys/dev", sandbox.BindOptional).
|
||||||
Bind("/sys/devices", "/sys/devices", container.BindOptional).
|
Bind("/sys/devices", "/sys/devices", sandbox.BindOptional).
|
||||||
Bind("/run/opengl-driver", "/run/opengl-driver", 0).
|
Bind("/run/opengl-driver", "/run/opengl-driver", 0).
|
||||||
Bind("/dev/dri", "/dev/dri", container.BindDevice|container.BindWritable|container.BindOptional).
|
Bind("/dev/dri", "/dev/dri", sandbox.BindDevice|sandbox.BindWritable|sandbox.BindOptional).
|
||||||
Etc("/etc", "8e2c76b066dabe574cf073bdb46eb5c1").
|
Etc("/etc", "8e2c76b066dabe574cf073bdb46eb5c1").
|
||||||
Tmpfs("/run/user", 4096, 0755).
|
Tmpfs("/run/user", 4096, 0755).
|
||||||
Bind("/tmp/hakurei.1971/runtime/1", "/run/user/1971", container.BindWritable).
|
Bind("/tmp/hakurei.1971/runtime/1", "/run/user/1971", sandbox.BindWritable).
|
||||||
Bind("/tmp/hakurei.1971/tmpdir/1", "/tmp", container.BindWritable).
|
Bind("/tmp/hakurei.1971/tmpdir/1", "/tmp", sandbox.BindWritable).
|
||||||
Bind("/var/lib/persist/module/hakurei/0/1", "/var/lib/persist/module/hakurei/0/1", container.BindWritable).
|
Bind("/var/lib/persist/module/hakurei/0/1", "/var/lib/persist/module/hakurei/0/1", sandbox.BindWritable).
|
||||||
Place("/etc/passwd", []byte("u0_a1:x:1971:100:Hakurei:/var/lib/persist/module/hakurei/0/1:/run/current-system/sw/bin/zsh\n")).
|
Place("/etc/passwd", []byte("u0_a1:x:1971:100:Hakurei:/var/lib/persist/module/hakurei/0/1:/run/current-system/sw/bin/zsh\n")).
|
||||||
Place("/etc/group", []byte("hakurei:x:100:\n")).
|
Place("/etc/group", []byte("hakurei:x:100:\n")).
|
||||||
Bind("/run/user/1971/wayland-0", "/run/user/1971/wayland-0", 0).
|
Bind("/run/user/1971/wayland-0", "/run/user/1971/wayland-0", 0).
|
||||||
@ -142,8 +142,6 @@ var testCasesNixos = []sealTestCase{
|
|||||||
Bind("/tmp/hakurei.1971/8e2c76b066dabe574cf073bdb46eb5c1/bus", "/run/user/1971/bus", 0).
|
Bind("/tmp/hakurei.1971/8e2c76b066dabe574cf073bdb46eb5c1/bus", "/run/user/1971/bus", 0).
|
||||||
Bind("/tmp/hakurei.1971/8e2c76b066dabe574cf073bdb46eb5c1/system_bus_socket", "/run/dbus/system_bus_socket", 0).
|
Bind("/tmp/hakurei.1971/8e2c76b066dabe574cf073bdb46eb5c1/system_bus_socket", "/run/dbus/system_bus_socket", 0).
|
||||||
Tmpfs("/var/run/nscd", 8192, 0755),
|
Tmpfs("/var/run/nscd", 8192, 0755),
|
||||||
SeccompPresets: seccomp.PresetExt | seccomp.PresetDenyTTY | seccomp.PresetDenyDevel,
|
|
||||||
HostNet: true,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
@ -3,13 +3,12 @@ package setuid_test
|
|||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"hakurei.app/cmd/hakurei/internal/app"
|
"git.gensokyo.uk/security/hakurei/acl"
|
||||||
"hakurei.app/container"
|
"git.gensokyo.uk/security/hakurei/dbus"
|
||||||
"hakurei.app/container/seccomp"
|
"git.gensokyo.uk/security/hakurei/hst"
|
||||||
"hakurei.app/hst"
|
"git.gensokyo.uk/security/hakurei/internal/app"
|
||||||
"hakurei.app/system"
|
"git.gensokyo.uk/security/hakurei/sandbox"
|
||||||
"hakurei.app/system/acl"
|
"git.gensokyo.uk/security/hakurei/system"
|
||||||
"hakurei.app/system/dbus"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var testCasesPd = []sealTestCase{
|
var testCasesPd = []sealTestCase{
|
||||||
@ -28,7 +27,8 @@ var testCasesPd = []sealTestCase{
|
|||||||
Ensure("/tmp/hakurei.1971/runtime/0", 0700).UpdatePermType(system.User, "/tmp/hakurei.1971/runtime/0", acl.Read, acl.Write, acl.Execute).
|
Ensure("/tmp/hakurei.1971/runtime/0", 0700).UpdatePermType(system.User, "/tmp/hakurei.1971/runtime/0", acl.Read, acl.Write, acl.Execute).
|
||||||
Ensure("/tmp/hakurei.1971/tmpdir", 0700).UpdatePermType(system.User, "/tmp/hakurei.1971/tmpdir", acl.Execute).
|
Ensure("/tmp/hakurei.1971/tmpdir", 0700).UpdatePermType(system.User, "/tmp/hakurei.1971/tmpdir", acl.Execute).
|
||||||
Ensure("/tmp/hakurei.1971/tmpdir/0", 01700).UpdatePermType(system.User, "/tmp/hakurei.1971/tmpdir/0", acl.Read, acl.Write, acl.Execute),
|
Ensure("/tmp/hakurei.1971/tmpdir/0", 01700).UpdatePermType(system.User, "/tmp/hakurei.1971/tmpdir/0", acl.Read, acl.Write, acl.Execute),
|
||||||
&container.Params{
|
&sandbox.Params{
|
||||||
|
Flags: sandbox.FAllowNet | sandbox.FAllowUserns | sandbox.FAllowTTY,
|
||||||
Dir: "/home/chronos",
|
Dir: "/home/chronos",
|
||||||
Path: "/run/current-system/sw/bin/zsh",
|
Path: "/run/current-system/sw/bin/zsh",
|
||||||
Args: []string{"/run/current-system/sw/bin/zsh"},
|
Args: []string{"/run/current-system/sw/bin/zsh"},
|
||||||
@ -41,36 +41,33 @@ var testCasesPd = []sealTestCase{
|
|||||||
"XDG_SESSION_CLASS=user",
|
"XDG_SESSION_CLASS=user",
|
||||||
"XDG_SESSION_TYPE=tty",
|
"XDG_SESSION_TYPE=tty",
|
||||||
},
|
},
|
||||||
Ops: new(container.Ops).
|
Ops: new(sandbox.Ops).
|
||||||
Proc("/proc").
|
Proc("/proc").
|
||||||
Tmpfs(hst.Tmp, 4096, 0755).
|
Tmpfs(hst.Tmp, 4096, 0755).
|
||||||
Dev("/dev").Mqueue("/dev/mqueue").
|
Dev("/dev").Mqueue("/dev/mqueue").
|
||||||
Bind("/bin", "/bin", container.BindWritable).
|
Bind("/bin", "/bin", sandbox.BindWritable).
|
||||||
Bind("/boot", "/boot", container.BindWritable).
|
Bind("/boot", "/boot", sandbox.BindWritable).
|
||||||
Bind("/home", "/home", container.BindWritable).
|
Bind("/home", "/home", sandbox.BindWritable).
|
||||||
Bind("/lib", "/lib", container.BindWritable).
|
Bind("/lib", "/lib", sandbox.BindWritable).
|
||||||
Bind("/lib64", "/lib64", container.BindWritable).
|
Bind("/lib64", "/lib64", sandbox.BindWritable).
|
||||||
Bind("/nix", "/nix", container.BindWritable).
|
Bind("/nix", "/nix", sandbox.BindWritable).
|
||||||
Bind("/root", "/root", container.BindWritable).
|
Bind("/root", "/root", sandbox.BindWritable).
|
||||||
Bind("/run", "/run", container.BindWritable).
|
Bind("/run", "/run", sandbox.BindWritable).
|
||||||
Bind("/srv", "/srv", container.BindWritable).
|
Bind("/srv", "/srv", sandbox.BindWritable).
|
||||||
Bind("/sys", "/sys", container.BindWritable).
|
Bind("/sys", "/sys", sandbox.BindWritable).
|
||||||
Bind("/usr", "/usr", container.BindWritable).
|
Bind("/usr", "/usr", sandbox.BindWritable).
|
||||||
Bind("/var", "/var", container.BindWritable).
|
Bind("/var", "/var", sandbox.BindWritable).
|
||||||
Bind("/dev/kvm", "/dev/kvm", container.BindWritable|container.BindDevice|container.BindOptional).
|
Bind("/dev/kvm", "/dev/kvm", sandbox.BindWritable|sandbox.BindDevice|sandbox.BindOptional).
|
||||||
Tmpfs("/run/user/1971", 8192, 0755).
|
Tmpfs("/run/user/1971", 8192, 0755).
|
||||||
Tmpfs("/run/dbus", 8192, 0755).
|
Tmpfs("/run/dbus", 8192, 0755).
|
||||||
Etc("/etc", "4a450b6596d7bc15bd01780eb9a607ac").
|
Etc("/etc", "4a450b6596d7bc15bd01780eb9a607ac").
|
||||||
Tmpfs("/run/user", 4096, 0755).
|
Tmpfs("/run/user", 4096, 0755).
|
||||||
Bind("/tmp/hakurei.1971/runtime/0", "/run/user/65534", container.BindWritable).
|
Bind("/tmp/hakurei.1971/runtime/0", "/run/user/65534", sandbox.BindWritable).
|
||||||
Bind("/tmp/hakurei.1971/tmpdir/0", "/tmp", container.BindWritable).
|
Bind("/tmp/hakurei.1971/tmpdir/0", "/tmp", sandbox.BindWritable).
|
||||||
Bind("/home/chronos", "/home/chronos", container.BindWritable).
|
Bind("/home/chronos", "/home/chronos", sandbox.BindWritable).
|
||||||
Place("/etc/passwd", []byte("chronos:x:65534:65534:Hakurei:/home/chronos:/run/current-system/sw/bin/zsh\n")).
|
Place("/etc/passwd", []byte("chronos:x:65534:65534:Hakurei:/home/chronos:/run/current-system/sw/bin/zsh\n")).
|
||||||
Place("/etc/group", []byte("hakurei:x:65534:\n")).
|
Place("/etc/group", []byte("hakurei:x:65534:\n")).
|
||||||
Tmpfs("/var/run/nscd", 8192, 0755),
|
Tmpfs("/var/run/nscd", 8192, 0755),
|
||||||
SeccompPresets: seccomp.PresetExt | seccomp.PresetDenyDevel,
|
|
||||||
HostNet: true,
|
|
||||||
RetainSession: true,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -166,7 +163,8 @@ var testCasesPd = []sealTestCase{
|
|||||||
}).
|
}).
|
||||||
UpdatePerm("/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/bus", acl.Read, acl.Write).
|
UpdatePerm("/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/bus", acl.Read, acl.Write).
|
||||||
UpdatePerm("/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/system_bus_socket", acl.Read, acl.Write),
|
UpdatePerm("/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/system_bus_socket", acl.Read, acl.Write),
|
||||||
&container.Params{
|
&sandbox.Params{
|
||||||
|
Flags: sandbox.FAllowNet | sandbox.FAllowUserns | sandbox.FAllowTTY,
|
||||||
Dir: "/home/chronos",
|
Dir: "/home/chronos",
|
||||||
Path: "/run/current-system/sw/bin/zsh",
|
Path: "/run/current-system/sw/bin/zsh",
|
||||||
Args: []string{"zsh", "-c", "exec chromium "},
|
Args: []string{"zsh", "-c", "exec chromium "},
|
||||||
@ -184,31 +182,31 @@ var testCasesPd = []sealTestCase{
|
|||||||
"XDG_SESSION_CLASS=user",
|
"XDG_SESSION_CLASS=user",
|
||||||
"XDG_SESSION_TYPE=tty",
|
"XDG_SESSION_TYPE=tty",
|
||||||
},
|
},
|
||||||
Ops: new(container.Ops).
|
Ops: new(sandbox.Ops).
|
||||||
Proc("/proc").
|
Proc("/proc").
|
||||||
Tmpfs(hst.Tmp, 4096, 0755).
|
Tmpfs(hst.Tmp, 4096, 0755).
|
||||||
Dev("/dev").Mqueue("/dev/mqueue").
|
Dev("/dev").Mqueue("/dev/mqueue").
|
||||||
Bind("/bin", "/bin", container.BindWritable).
|
Bind("/bin", "/bin", sandbox.BindWritable).
|
||||||
Bind("/boot", "/boot", container.BindWritable).
|
Bind("/boot", "/boot", sandbox.BindWritable).
|
||||||
Bind("/home", "/home", container.BindWritable).
|
Bind("/home", "/home", sandbox.BindWritable).
|
||||||
Bind("/lib", "/lib", container.BindWritable).
|
Bind("/lib", "/lib", sandbox.BindWritable).
|
||||||
Bind("/lib64", "/lib64", container.BindWritable).
|
Bind("/lib64", "/lib64", sandbox.BindWritable).
|
||||||
Bind("/nix", "/nix", container.BindWritable).
|
Bind("/nix", "/nix", sandbox.BindWritable).
|
||||||
Bind("/root", "/root", container.BindWritable).
|
Bind("/root", "/root", sandbox.BindWritable).
|
||||||
Bind("/run", "/run", container.BindWritable).
|
Bind("/run", "/run", sandbox.BindWritable).
|
||||||
Bind("/srv", "/srv", container.BindWritable).
|
Bind("/srv", "/srv", sandbox.BindWritable).
|
||||||
Bind("/sys", "/sys", container.BindWritable).
|
Bind("/sys", "/sys", sandbox.BindWritable).
|
||||||
Bind("/usr", "/usr", container.BindWritable).
|
Bind("/usr", "/usr", sandbox.BindWritable).
|
||||||
Bind("/var", "/var", container.BindWritable).
|
Bind("/var", "/var", sandbox.BindWritable).
|
||||||
Bind("/dev/dri", "/dev/dri", container.BindWritable|container.BindDevice|container.BindOptional).
|
Bind("/dev/dri", "/dev/dri", sandbox.BindWritable|sandbox.BindDevice|sandbox.BindOptional).
|
||||||
Bind("/dev/kvm", "/dev/kvm", container.BindWritable|container.BindDevice|container.BindOptional).
|
Bind("/dev/kvm", "/dev/kvm", sandbox.BindWritable|sandbox.BindDevice|sandbox.BindOptional).
|
||||||
Tmpfs("/run/user/1971", 8192, 0755).
|
Tmpfs("/run/user/1971", 8192, 0755).
|
||||||
Tmpfs("/run/dbus", 8192, 0755).
|
Tmpfs("/run/dbus", 8192, 0755).
|
||||||
Etc("/etc", "ebf083d1b175911782d413369b64ce7c").
|
Etc("/etc", "ebf083d1b175911782d413369b64ce7c").
|
||||||
Tmpfs("/run/user", 4096, 0755).
|
Tmpfs("/run/user", 4096, 0755).
|
||||||
Bind("/tmp/hakurei.1971/runtime/9", "/run/user/65534", container.BindWritable).
|
Bind("/tmp/hakurei.1971/runtime/9", "/run/user/65534", sandbox.BindWritable).
|
||||||
Bind("/tmp/hakurei.1971/tmpdir/9", "/tmp", container.BindWritable).
|
Bind("/tmp/hakurei.1971/tmpdir/9", "/tmp", sandbox.BindWritable).
|
||||||
Bind("/home/chronos", "/home/chronos", container.BindWritable).
|
Bind("/home/chronos", "/home/chronos", sandbox.BindWritable).
|
||||||
Place("/etc/passwd", []byte("chronos:x:65534:65534:Hakurei:/home/chronos:/run/current-system/sw/bin/zsh\n")).
|
Place("/etc/passwd", []byte("chronos:x:65534:65534:Hakurei:/home/chronos:/run/current-system/sw/bin/zsh\n")).
|
||||||
Place("/etc/group", []byte("hakurei:x:65534:\n")).
|
Place("/etc/group", []byte("hakurei:x:65534:\n")).
|
||||||
Bind("/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/wayland", "/run/user/65534/wayland-0", 0).
|
Bind("/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/wayland", "/run/user/65534/wayland-0", 0).
|
||||||
@ -217,9 +215,6 @@ var testCasesPd = []sealTestCase{
|
|||||||
Bind("/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/bus", "/run/user/65534/bus", 0).
|
Bind("/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/bus", "/run/user/65534/bus", 0).
|
||||||
Bind("/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/system_bus_socket", "/run/dbus/system_bus_socket", 0).
|
Bind("/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/system_bus_socket", "/run/dbus/system_bus_socket", 0).
|
||||||
Tmpfs("/var/run/nscd", 8192, 0755),
|
Tmpfs("/var/run/nscd", 8192, 0755),
|
||||||
SeccompPresets: seccomp.PresetExt | seccomp.PresetDenyDevel,
|
|
||||||
HostNet: true,
|
|
||||||
RetainSession: true,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
@ -7,7 +7,7 @@ import (
|
|||||||
"os/user"
|
"os/user"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"hakurei.app/hst"
|
"git.gensokyo.uk/security/hakurei/internal/app"
|
||||||
)
|
)
|
||||||
|
|
||||||
// fs methods are not implemented using a real FS
|
// fs methods are not implemented using a real FS
|
||||||
@ -125,8 +125,8 @@ func (s *stubNixOS) Open(name string) (fs.File, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *stubNixOS) Paths() hst.Paths {
|
func (s *stubNixOS) Paths() app.Paths {
|
||||||
return hst.Paths{
|
return app.Paths{
|
||||||
SharePath: "/tmp/hakurei.1971",
|
SharePath: "/tmp/hakurei.1971",
|
||||||
RuntimePath: "/run/user/1971",
|
RuntimePath: "/run/user/1971",
|
||||||
RunDirPath: "/run/user/1971/hakurei",
|
RunDirPath: "/run/user/1971/hakurei",
|
@ -7,12 +7,12 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"hakurei.app/cmd/hakurei/internal/app"
|
"git.gensokyo.uk/security/hakurei/hst"
|
||||||
"hakurei.app/cmd/hakurei/internal/app/internal/setuid"
|
"git.gensokyo.uk/security/hakurei/internal/app"
|
||||||
"hakurei.app/container"
|
"git.gensokyo.uk/security/hakurei/internal/app/internal/setuid"
|
||||||
"hakurei.app/hst"
|
"git.gensokyo.uk/security/hakurei/internal/sys"
|
||||||
"hakurei.app/internal/sys"
|
"git.gensokyo.uk/security/hakurei/sandbox"
|
||||||
"hakurei.app/system"
|
"git.gensokyo.uk/security/hakurei/system"
|
||||||
)
|
)
|
||||||
|
|
||||||
type sealTestCase struct {
|
type sealTestCase struct {
|
||||||
@ -21,7 +21,7 @@ type sealTestCase struct {
|
|||||||
config *hst.Config
|
config *hst.Config
|
||||||
id app.ID
|
id app.ID
|
||||||
wantSys *system.I
|
wantSys *system.I
|
||||||
wantContainer *container.Params
|
wantContainer *sandbox.Params
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestApp(t *testing.T) {
|
func TestApp(t *testing.T) {
|
||||||
@ -32,7 +32,7 @@ func TestApp(t *testing.T) {
|
|||||||
a := setuid.NewWithID(tc.id, tc.os)
|
a := setuid.NewWithID(tc.id, tc.os)
|
||||||
var (
|
var (
|
||||||
gotSys *system.I
|
gotSys *system.I
|
||||||
gotContainer *container.Params
|
gotContainer *sandbox.Params
|
||||||
)
|
)
|
||||||
if !t.Run("seal", func(t *testing.T) {
|
if !t.Run("seal", func(t *testing.T) {
|
||||||
if sa, err := a.Seal(tc.config); err != nil {
|
if sa, err := a.Seal(tc.config); err != nil {
|
@ -4,8 +4,8 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"log"
|
"log"
|
||||||
|
|
||||||
. "hakurei.app/cmd/hakurei/internal/app"
|
. "git.gensokyo.uk/security/hakurei/internal/app"
|
||||||
"hakurei.app/internal/hlog"
|
"git.gensokyo.uk/security/hakurei/internal/hlog"
|
||||||
)
|
)
|
||||||
|
|
||||||
func PrintRunStateErr(rs *RunState, runErr error) (code int) {
|
func PrintRunStateErr(rs *RunState, runErr error) (code int) {
|
@ -1,10 +1,10 @@
|
|||||||
package setuid
|
package setuid
|
||||||
|
|
||||||
import (
|
import (
|
||||||
. "hakurei.app/cmd/hakurei/internal/app"
|
. "git.gensokyo.uk/security/hakurei/internal/app"
|
||||||
"hakurei.app/container"
|
"git.gensokyo.uk/security/hakurei/internal/sys"
|
||||||
"hakurei.app/internal/sys"
|
"git.gensokyo.uk/security/hakurei/sandbox"
|
||||||
"hakurei.app/system"
|
"git.gensokyo.uk/security/hakurei/system"
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewWithID(id ID, os sys.State) App {
|
func NewWithID(id ID, os sys.State) App {
|
||||||
@ -14,7 +14,7 @@ func NewWithID(id ID, os sys.State) App {
|
|||||||
return a
|
return a
|
||||||
}
|
}
|
||||||
|
|
||||||
func AppIParams(a App, sa SealedApp) (*system.I, *container.Params) {
|
func AppIParams(a App, sa SealedApp) (*system.I, *sandbox.Params) {
|
||||||
v := a.(*app)
|
v := a.(*app)
|
||||||
seal := sa.(*outcome)
|
seal := sa.(*outcome)
|
||||||
if v.outcome != seal || v.id != seal.id {
|
if v.outcome != seal || v.id != seal.id {
|
@ -12,12 +12,12 @@ import (
|
|||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
. "hakurei.app/cmd/hakurei/internal/app"
|
"git.gensokyo.uk/security/hakurei/internal"
|
||||||
"hakurei.app/cmd/hakurei/internal/state"
|
. "git.gensokyo.uk/security/hakurei/internal/app"
|
||||||
"hakurei.app/container"
|
"git.gensokyo.uk/security/hakurei/internal/hlog"
|
||||||
"hakurei.app/internal"
|
"git.gensokyo.uk/security/hakurei/internal/state"
|
||||||
"hakurei.app/internal/hlog"
|
"git.gensokyo.uk/security/hakurei/sandbox"
|
||||||
"hakurei.app/system"
|
"git.gensokyo.uk/security/hakurei/system"
|
||||||
)
|
)
|
||||||
|
|
||||||
const shimWaitTimeout = 5 * time.Second
|
const shimWaitTimeout = 5 * time.Second
|
||||||
@ -94,7 +94,7 @@ func (seal *outcome) Run(rs *RunState) error {
|
|||||||
cmd.Cancel = func() error { return cmd.Process.Signal(syscall.SIGCONT) }
|
cmd.Cancel = func() error { return cmd.Process.Signal(syscall.SIGCONT) }
|
||||||
|
|
||||||
var e *gob.Encoder
|
var e *gob.Encoder
|
||||||
if fd, encoder, err := container.Setup(&cmd.ExtraFiles); err != nil {
|
if fd, encoder, err := sandbox.Setup(&cmd.ExtraFiles); err != nil {
|
||||||
return hlog.WrapErrSuffix(err,
|
return hlog.WrapErrSuffix(err,
|
||||||
"cannot create shim setup pipe:")
|
"cannot create shim setup pipe:")
|
||||||
} else {
|
} else {
|
@ -16,17 +16,17 @@ import (
|
|||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
. "hakurei.app/cmd/hakurei/internal/app"
|
"git.gensokyo.uk/security/hakurei/acl"
|
||||||
"hakurei.app/cmd/hakurei/internal/app/instance/common"
|
"git.gensokyo.uk/security/hakurei/dbus"
|
||||||
"hakurei.app/container"
|
"git.gensokyo.uk/security/hakurei/hst"
|
||||||
"hakurei.app/hst"
|
"git.gensokyo.uk/security/hakurei/internal"
|
||||||
"hakurei.app/internal"
|
. "git.gensokyo.uk/security/hakurei/internal/app"
|
||||||
"hakurei.app/internal/hlog"
|
"git.gensokyo.uk/security/hakurei/internal/app/instance/common"
|
||||||
"hakurei.app/internal/sys"
|
"git.gensokyo.uk/security/hakurei/internal/hlog"
|
||||||
"hakurei.app/system"
|
"git.gensokyo.uk/security/hakurei/internal/sys"
|
||||||
"hakurei.app/system/acl"
|
"git.gensokyo.uk/security/hakurei/sandbox"
|
||||||
"hakurei.app/system/dbus"
|
"git.gensokyo.uk/security/hakurei/sandbox/wl"
|
||||||
"hakurei.app/system/wayland"
|
"git.gensokyo.uk/security/hakurei/system"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -80,7 +80,7 @@ type outcome struct {
|
|||||||
sys *system.I
|
sys *system.I
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
|
|
||||||
container *container.Params
|
container *sandbox.Params
|
||||||
env map[string]string
|
env map[string]string
|
||||||
sync *os.File
|
sync *os.File
|
||||||
|
|
||||||
@ -97,7 +97,7 @@ type shareHost struct {
|
|||||||
runtimeSharePath string
|
runtimeSharePath string
|
||||||
|
|
||||||
seal *outcome
|
seal *outcome
|
||||||
sc hst.Paths
|
sc Paths
|
||||||
}
|
}
|
||||||
|
|
||||||
// ensureRuntimeDir must be called if direct access to paths within XDG_RUNTIME_DIR is required
|
// ensureRuntimeDir must be called if direct access to paths within XDG_RUNTIME_DIR is required
|
||||||
@ -183,7 +183,7 @@ func (seal *outcome) finalise(ctx context.Context, sys sys.State, config *hst.Co
|
|||||||
if seal.user.username == "" {
|
if seal.user.username == "" {
|
||||||
seal.user.username = "chronos"
|
seal.user.username = "chronos"
|
||||||
} else if !posixUsername.MatchString(seal.user.username) ||
|
} else if !posixUsername.MatchString(seal.user.username) ||
|
||||||
len(seal.user.username) >= internal.Sysconf(internal.SC_LOGIN_NAME_MAX) {
|
len(seal.user.username) >= internal.Sysconf_SC_LOGIN_NAME_MAX() {
|
||||||
return hlog.WrapErr(ErrName,
|
return hlog.WrapErr(ErrName,
|
||||||
fmt.Sprintf("invalid user name %q", seal.user.username))
|
fmt.Sprintf("invalid user name %q", seal.user.username))
|
||||||
}
|
}
|
||||||
@ -334,7 +334,7 @@ func (seal *outcome) finalise(ctx context.Context, sys sys.State, config *hst.Co
|
|||||||
seal.sys.Ensure(runtimeDirInst, 0700)
|
seal.sys.Ensure(runtimeDirInst, 0700)
|
||||||
seal.sys.UpdatePermType(system.User, runtimeDirInst, acl.Read, acl.Write, acl.Execute)
|
seal.sys.UpdatePermType(system.User, runtimeDirInst, acl.Read, acl.Write, acl.Execute)
|
||||||
seal.container.Tmpfs("/run/user", 1<<12, 0755)
|
seal.container.Tmpfs("/run/user", 1<<12, 0755)
|
||||||
seal.container.Bind(runtimeDirInst, innerRuntimeDir, container.BindWritable)
|
seal.container.Bind(runtimeDirInst, innerRuntimeDir, sandbox.BindWritable)
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
@ -345,7 +345,7 @@ func (seal *outcome) finalise(ctx context.Context, sys sys.State, config *hst.Co
|
|||||||
seal.sys.Ensure(tmpdirInst, 01700)
|
seal.sys.Ensure(tmpdirInst, 01700)
|
||||||
seal.sys.UpdatePermType(system.User, tmpdirInst, acl.Read, acl.Write, acl.Execute)
|
seal.sys.UpdatePermType(system.User, tmpdirInst, acl.Read, acl.Write, acl.Execute)
|
||||||
// mount inner /tmp from share so it shares persistence and storage behaviour of host /tmp
|
// mount inner /tmp from share so it shares persistence and storage behaviour of host /tmp
|
||||||
seal.container.Bind(tmpdirInst, "/tmp", container.BindWritable)
|
seal.container.Bind(tmpdirInst, "/tmp", sandbox.BindWritable)
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
@ -357,7 +357,7 @@ func (seal *outcome) finalise(ctx context.Context, sys sys.State, config *hst.Co
|
|||||||
if seal.user.username != "" {
|
if seal.user.username != "" {
|
||||||
username = seal.user.username
|
username = seal.user.username
|
||||||
}
|
}
|
||||||
seal.container.Bind(seal.user.data, homeDir, container.BindWritable)
|
seal.container.Bind(seal.user.data, homeDir, sandbox.BindWritable)
|
||||||
seal.container.Dir = homeDir
|
seal.container.Dir = homeDir
|
||||||
seal.env["HOME"] = homeDir
|
seal.env["HOME"] = homeDir
|
||||||
seal.env["USER"] = username
|
seal.env["USER"] = username
|
||||||
@ -377,23 +377,23 @@ func (seal *outcome) finalise(ctx context.Context, sys sys.State, config *hst.Co
|
|||||||
if config.Enablements&system.EWayland != 0 {
|
if config.Enablements&system.EWayland != 0 {
|
||||||
// outer wayland socket (usually `/run/user/%d/wayland-%d`)
|
// outer wayland socket (usually `/run/user/%d/wayland-%d`)
|
||||||
var socketPath string
|
var socketPath string
|
||||||
if name, ok := sys.LookupEnv(wayland.WaylandDisplay); !ok {
|
if name, ok := sys.LookupEnv(wl.WaylandDisplay); !ok {
|
||||||
hlog.Verbose(wayland.WaylandDisplay + " is not set, assuming " + wayland.FallbackName)
|
hlog.Verbose(wl.WaylandDisplay + " is not set, assuming " + wl.FallbackName)
|
||||||
socketPath = path.Join(share.sc.RuntimePath, wayland.FallbackName)
|
socketPath = path.Join(share.sc.RuntimePath, wl.FallbackName)
|
||||||
} else if !path.IsAbs(name) {
|
} else if !path.IsAbs(name) {
|
||||||
socketPath = path.Join(share.sc.RuntimePath, name)
|
socketPath = path.Join(share.sc.RuntimePath, name)
|
||||||
} else {
|
} else {
|
||||||
socketPath = name
|
socketPath = name
|
||||||
}
|
}
|
||||||
|
|
||||||
innerPath := path.Join(innerRuntimeDir, wayland.FallbackName)
|
innerPath := path.Join(innerRuntimeDir, wl.FallbackName)
|
||||||
seal.env[wayland.WaylandDisplay] = wayland.FallbackName
|
seal.env[wl.WaylandDisplay] = wl.FallbackName
|
||||||
|
|
||||||
if !config.DirectWayland { // set up security-context-v1
|
if !config.DirectWayland { // set up security-context-v1
|
||||||
appID := config.ID
|
appID := config.ID
|
||||||
if appID == "" {
|
if appID == "" {
|
||||||
// use instance ID in case app id is not set
|
// use instance ID in case app id is not set
|
||||||
appID = "app.hakurei." + seal.id.String()
|
appID = "uk.gensokyo.hakurei." + seal.id.String()
|
||||||
}
|
}
|
||||||
// downstream socket paths
|
// downstream socket paths
|
||||||
outerPath := path.Join(share.instance(), "wayland")
|
outerPath := path.Join(share.instance(), "wayland")
|
@ -10,10 +10,10 @@ import (
|
|||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"hakurei.app/container"
|
"git.gensokyo.uk/security/hakurei/internal"
|
||||||
"hakurei.app/container/seccomp"
|
"git.gensokyo.uk/security/hakurei/internal/hlog"
|
||||||
"hakurei.app/internal"
|
"git.gensokyo.uk/security/hakurei/sandbox"
|
||||||
"hakurei.app/internal/hlog"
|
"git.gensokyo.uk/security/hakurei/sandbox/seccomp"
|
||||||
)
|
)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -74,7 +74,7 @@ type shimParams struct {
|
|||||||
Monitor int
|
Monitor int
|
||||||
|
|
||||||
// finalised container params
|
// finalised container params
|
||||||
Container *container.Params
|
Container *sandbox.Params
|
||||||
// path to outer home directory
|
// path to outer home directory
|
||||||
Home string
|
Home string
|
||||||
|
|
||||||
@ -86,7 +86,7 @@ type shimParams struct {
|
|||||||
func ShimMain() {
|
func ShimMain() {
|
||||||
hlog.Prepare("shim")
|
hlog.Prepare("shim")
|
||||||
|
|
||||||
if err := container.SetDumpable(container.SUID_DUMP_DISABLE); err != nil {
|
if err := sandbox.SetDumpable(sandbox.SUID_DUMP_DISABLE); err != nil {
|
||||||
log.Fatalf("cannot set SUID_DUMP_DISABLE: %s", err)
|
log.Fatalf("cannot set SUID_DUMP_DISABLE: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -94,17 +94,17 @@ func ShimMain() {
|
|||||||
params shimParams
|
params shimParams
|
||||||
closeSetup func() error
|
closeSetup func() error
|
||||||
)
|
)
|
||||||
if f, err := container.Receive(shimEnv, ¶ms, nil); err != nil {
|
if f, err := sandbox.Receive(shimEnv, ¶ms, nil); err != nil {
|
||||||
if errors.Is(err, container.ErrInvalid) {
|
if errors.Is(err, sandbox.ErrInvalid) {
|
||||||
log.Fatal("invalid config descriptor")
|
log.Fatal("invalid config descriptor")
|
||||||
}
|
}
|
||||||
if errors.Is(err, container.ErrNotSet) {
|
if errors.Is(err, sandbox.ErrNotSet) {
|
||||||
log.Fatal("HAKUREI_SHIM not set")
|
log.Fatal("HAKUREI_SHIM not set")
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Fatalf("cannot receive shim setup params: %v", err)
|
log.Fatalf("cannot receive shim setup params: %v", err)
|
||||||
} else {
|
} else {
|
||||||
internal.InstallOutput(params.Verbose)
|
internal.InstallFmsg(params.Verbose)
|
||||||
closeSetup = f
|
closeSetup = f
|
||||||
|
|
||||||
// the Go runtime does not expose siginfo_t so SIGCONT is handled in C to check si_pid
|
// the Go runtime does not expose siginfo_t so SIGCONT is handled in C to check si_pid
|
||||||
@ -149,28 +149,25 @@ func ShimMain() {
|
|||||||
}
|
}
|
||||||
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
|
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
|
||||||
defer stop() // unreachable
|
defer stop() // unreachable
|
||||||
z := container.New(ctx, name)
|
container := sandbox.New(ctx, name)
|
||||||
z.Params = *params.Container
|
container.Params = *params.Container
|
||||||
z.Stdin, z.Stdout, z.Stderr = os.Stdin, os.Stdout, os.Stderr
|
container.Stdin, container.Stdout, container.Stderr = os.Stdin, os.Stdout, os.Stderr
|
||||||
z.Cancel = func(cmd *exec.Cmd) error { return cmd.Process.Signal(os.Interrupt) }
|
container.Cancel = func(cmd *exec.Cmd) error { return cmd.Process.Signal(os.Interrupt) }
|
||||||
z.WaitDelay = 2 * time.Second
|
container.WaitDelay = 2 * time.Second
|
||||||
|
|
||||||
if err := z.Start(); err != nil {
|
if err := container.Start(); err != nil {
|
||||||
hlog.PrintBaseError(err, "cannot start container:")
|
hlog.PrintBaseError(err, "cannot start container:")
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
if err := z.Serve(); err != nil {
|
if err := container.Serve(); err != nil {
|
||||||
hlog.PrintBaseError(err, "cannot configure container:")
|
hlog.PrintBaseError(err, "cannot configure container:")
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := seccomp.Load(
|
if err := seccomp.Load(seccomp.PresetCommon); err != nil {
|
||||||
seccomp.Preset(seccomp.PresetStrict, seccomp.AllowMultiarch),
|
|
||||||
seccomp.AllowMultiarch,
|
|
||||||
); err != nil {
|
|
||||||
log.Fatalf("cannot load syscall filter: %v", err)
|
log.Fatalf("cannot load syscall filter: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := z.Wait(); err != nil {
|
if err := container.Wait(); err != nil {
|
||||||
var exitError *exec.ExitError
|
var exitError *exec.ExitError
|
||||||
if !errors.As(err, &exitError) {
|
if !errors.As(err, &exitError) {
|
||||||
if errors.Is(err, context.Canceled) {
|
if errors.Is(err, context.Canceled) {
|
@ -3,7 +3,7 @@ package setuid
|
|||||||
import (
|
import (
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
. "hakurei.app/cmd/hakurei/internal/app"
|
. "git.gensokyo.uk/security/hakurei/internal/app"
|
||||||
)
|
)
|
||||||
|
|
||||||
func newInt(v int) *stringPair[int] { return &stringPair[int]{v, strconv.Itoa(v)} }
|
func newInt(v int) *stringPair[int] { return &stringPair[int]{v, strconv.Itoa(v)} }
|
@ -3,7 +3,7 @@ package internal
|
|||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"hakurei.app/internal/hlog"
|
"git.gensokyo.uk/security/hakurei/internal/hlog"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Exit(code int) { hlog.BeforeExit(); os.Exit(code) }
|
func Exit(code int) { hlog.BeforeExit(); os.Exit(code) }
|
||||||
|
@ -1,13 +1,17 @@
|
|||||||
package internal
|
package internal
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"hakurei.app/container"
|
"git.gensokyo.uk/security/hakurei/internal/hlog"
|
||||||
"hakurei.app/internal/hlog"
|
"git.gensokyo.uk/security/hakurei/sandbox"
|
||||||
"hakurei.app/system"
|
"git.gensokyo.uk/security/hakurei/sandbox/seccomp"
|
||||||
|
"git.gensokyo.uk/security/hakurei/system"
|
||||||
)
|
)
|
||||||
|
|
||||||
func InstallOutput(verbose bool) {
|
func InstallFmsg(verbose bool) {
|
||||||
hlog.Store(verbose)
|
hlog.Store(verbose)
|
||||||
container.SetOutput(hlog.Output{})
|
sandbox.SetOutput(hlog.Output{})
|
||||||
system.SetOutput(hlog.Output{})
|
system.SetOutput(hlog.Output{})
|
||||||
|
if verbose {
|
||||||
|
seccomp.SetOutput(hlog.Verbose)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,16 +4,16 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
"path"
|
"path"
|
||||||
|
|
||||||
"hakurei.app/internal/hlog"
|
"git.gensokyo.uk/security/hakurei/internal/hlog"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
hmain = compPoison
|
hakurei = compPoison
|
||||||
hsu = compPoison
|
hsu = compPoison
|
||||||
)
|
)
|
||||||
|
|
||||||
func MustHakureiPath() string {
|
func MustHakureiPath() string {
|
||||||
if name, ok := checkPath(hmain); ok {
|
if name, ok := checkPath(hakurei); ok {
|
||||||
return name
|
return name
|
||||||
}
|
}
|
||||||
hlog.BeforeExit()
|
hlog.BeforeExit()
|
||||||
|
@ -13,9 +13,9 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"hakurei.app/cmd/hakurei/internal/app"
|
"git.gensokyo.uk/security/hakurei/hst"
|
||||||
"hakurei.app/hst"
|
"git.gensokyo.uk/security/hakurei/internal/app"
|
||||||
"hakurei.app/internal/hlog"
|
"git.gensokyo.uk/security/hakurei/internal/hlog"
|
||||||
)
|
)
|
||||||
|
|
||||||
// fine-grained locking and access
|
// fine-grained locking and access
|
11
internal/state/multi_test.go
Normal file
11
internal/state/multi_test.go
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
package state_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"git.gensokyo.uk/security/hakurei/internal/state"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMulti(t *testing.T) {
|
||||||
|
testStore(t, state.NewMulti(t.TempDir()))
|
||||||
|
}
|
@ -5,8 +5,8 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"hakurei.app/cmd/hakurei/internal/app"
|
"git.gensokyo.uk/security/hakurei/hst"
|
||||||
"hakurei.app/hst"
|
"git.gensokyo.uk/security/hakurei/internal/app"
|
||||||
)
|
)
|
||||||
|
|
||||||
var ErrNoConfig = errors.New("state does not contain config")
|
var ErrNoConfig = errors.New("state does not contain config")
|
@ -10,9 +10,9 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"hakurei.app/cmd/hakurei/internal/app"
|
"git.gensokyo.uk/security/hakurei/hst"
|
||||||
"hakurei.app/cmd/hakurei/internal/state"
|
"git.gensokyo.uk/security/hakurei/internal/app"
|
||||||
"hakurei.app/hst"
|
"git.gensokyo.uk/security/hakurei/internal/state"
|
||||||
)
|
)
|
||||||
|
|
||||||
func testStore(t *testing.T, s state.Store) {
|
func testStore(t *testing.T, s state.Store) {
|
@ -1,4 +1,3 @@
|
|||||||
// Package sys wraps OS interaction library functions.
|
|
||||||
package sys
|
package sys
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@ -7,8 +6,8 @@ import (
|
|||||||
"path"
|
"path"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"hakurei.app/hst"
|
"git.gensokyo.uk/security/hakurei/internal/app"
|
||||||
"hakurei.app/internal/hlog"
|
"git.gensokyo.uk/security/hakurei/internal/hlog"
|
||||||
)
|
)
|
||||||
|
|
||||||
// State provides safe interaction with operating system state.
|
// State provides safe interaction with operating system state.
|
||||||
@ -41,15 +40,15 @@ type State interface {
|
|||||||
Println(v ...any)
|
Println(v ...any)
|
||||||
Printf(format string, v ...any)
|
Printf(format string, v ...any)
|
||||||
|
|
||||||
// Paths returns a populated [hst.Paths] struct.
|
// Paths returns a populated [Paths] struct.
|
||||||
Paths() hst.Paths
|
Paths() app.Paths
|
||||||
// Uid invokes hsu and returns target uid.
|
// Uid invokes hsu and returns target uid.
|
||||||
// Any errors returned by Uid is already wrapped [fmsg.BaseError].
|
// Any errors returned by Uid is already wrapped [fmsg.BaseError].
|
||||||
Uid(aid int) (int, error)
|
Uid(aid int) (int, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// CopyPaths is a generic implementation of [hst.Paths].
|
// CopyPaths is a generic implementation of [hst.Paths].
|
||||||
func CopyPaths(os State, v *hst.Paths) {
|
func CopyPaths(os State, v *app.Paths) {
|
||||||
v.SharePath = path.Join(os.TempDir(), "hakurei."+strconv.Itoa(os.Getuid()))
|
v.SharePath = path.Join(os.TempDir(), "hakurei."+strconv.Itoa(os.Getuid()))
|
||||||
|
|
||||||
hlog.Verbosef("process share directory at %q", v.SharePath)
|
hlog.Verbosef("process share directory at %q", v.SharePath)
|
||||||
|
@ -12,15 +12,15 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"hakurei.app/container"
|
"git.gensokyo.uk/security/hakurei/internal"
|
||||||
"hakurei.app/hst"
|
"git.gensokyo.uk/security/hakurei/internal/app"
|
||||||
"hakurei.app/internal"
|
"git.gensokyo.uk/security/hakurei/internal/hlog"
|
||||||
"hakurei.app/internal/hlog"
|
"git.gensokyo.uk/security/hakurei/sandbox"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Std implements System using the standard library.
|
// Std implements System using the standard library.
|
||||||
type Std struct {
|
type Std struct {
|
||||||
paths hst.Paths
|
paths app.Paths
|
||||||
pathsOnce sync.Once
|
pathsOnce sync.Once
|
||||||
|
|
||||||
uidOnce sync.Once
|
uidOnce sync.Once
|
||||||
@ -36,7 +36,7 @@ func (s *Std) Getgid() int { return os.Getgid()
|
|||||||
func (s *Std) LookupEnv(key string) (string, bool) { return os.LookupEnv(key) }
|
func (s *Std) LookupEnv(key string) (string, bool) { return os.LookupEnv(key) }
|
||||||
func (s *Std) TempDir() string { return os.TempDir() }
|
func (s *Std) TempDir() string { return os.TempDir() }
|
||||||
func (s *Std) LookPath(file string) (string, error) { return exec.LookPath(file) }
|
func (s *Std) LookPath(file string) (string, error) { return exec.LookPath(file) }
|
||||||
func (s *Std) MustExecutable() string { return container.MustExecutable() }
|
func (s *Std) MustExecutable() string { return sandbox.MustExecutable() }
|
||||||
func (s *Std) LookupGroup(name string) (*user.Group, error) { return user.LookupGroup(name) }
|
func (s *Std) LookupGroup(name string) (*user.Group, error) { return user.LookupGroup(name) }
|
||||||
func (s *Std) ReadDir(name string) ([]os.DirEntry, error) { return os.ReadDir(name) }
|
func (s *Std) ReadDir(name string) ([]os.DirEntry, error) { return os.ReadDir(name) }
|
||||||
func (s *Std) Stat(name string) (fs.FileInfo, error) { return os.Stat(name) }
|
func (s *Std) Stat(name string) (fs.FileInfo, error) { return os.Stat(name) }
|
||||||
@ -48,7 +48,7 @@ func (s *Std) Printf(format string, v ...any) { hlog.Verbosef(form
|
|||||||
|
|
||||||
const xdgRuntimeDir = "XDG_RUNTIME_DIR"
|
const xdgRuntimeDir = "XDG_RUNTIME_DIR"
|
||||||
|
|
||||||
func (s *Std) Paths() hst.Paths {
|
func (s *Std) Paths() app.Paths {
|
||||||
s.pathsOnce.Do(func() { CopyPaths(s, &s.paths) })
|
s.pathsOnce.Do(func() { CopyPaths(s, &s.paths) })
|
||||||
return s.paths
|
return s.paths
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,4 @@ package internal
|
|||||||
//#include <unistd.h>
|
//#include <unistd.h>
|
||||||
import "C"
|
import "C"
|
||||||
|
|
||||||
const SC_LOGIN_NAME_MAX = C._SC_LOGIN_NAME_MAX
|
func Sysconf_SC_LOGIN_NAME_MAX() int { return int(C.sysconf(C._SC_LOGIN_NAME_MAX)) }
|
||||||
|
|
||||||
func Sysconf(name C.int) int { return int(C.sysconf(name)) }
|
|
||||||
|
23
ldd/exec.go
23
ldd/exec.go
@ -8,8 +8,7 @@ import (
|
|||||||
"os/exec"
|
"os/exec"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"hakurei.app/container"
|
"git.gensokyo.uk/security/hakurei/sandbox"
|
||||||
"hakurei.app/container/seccomp"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const lddTimeout = 2 * time.Second
|
const lddTimeout = 2 * time.Second
|
||||||
@ -27,24 +26,22 @@ func ExecFilter(ctx context.Context,
|
|||||||
p string) ([]*Entry, error) {
|
p string) ([]*Entry, error) {
|
||||||
c, cancel := context.WithTimeout(ctx, lddTimeout)
|
c, cancel := context.WithTimeout(ctx, lddTimeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
z := container.New(c, "ldd", p)
|
container := sandbox.New(c, "ldd", p)
|
||||||
z.CommandContext = commandContext
|
container.CommandContext = commandContext
|
||||||
z.Hostname = "hakurei-ldd"
|
container.Hostname = "hakurei-ldd"
|
||||||
z.SeccompFlags |= seccomp.AllowMultiarch
|
|
||||||
z.SeccompPresets |= seccomp.PresetStrict
|
|
||||||
stdout, stderr := new(bytes.Buffer), new(bytes.Buffer)
|
stdout, stderr := new(bytes.Buffer), new(bytes.Buffer)
|
||||||
z.Stdout = stdout
|
container.Stdout = stdout
|
||||||
z.Stderr = stderr
|
container.Stderr = stderr
|
||||||
z.Bind("/", "/", 0).Proc("/proc").Dev("/dev")
|
container.Bind("/", "/", 0).Proc("/proc").Dev("/dev")
|
||||||
|
|
||||||
if err := z.Start(); err != nil {
|
if err := container.Start(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer func() { _, _ = io.Copy(os.Stderr, stderr) }()
|
defer func() { _, _ = io.Copy(os.Stderr, stderr) }()
|
||||||
if err := z.Serve(); err != nil {
|
if err := container.Serve(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if err := z.Wait(); err != nil {
|
if err := container.Wait(); err != nil {
|
||||||
m := stderr.Bytes()
|
m := stderr.Bytes()
|
||||||
if bytes.Contains(m, append([]byte(p+": "), msgStatic...)) ||
|
if bytes.Contains(m, append([]byte(p+": "), msgStatic...)) ||
|
||||||
bytes.Contains(m, msgStaticGlibc) {
|
bytes.Contains(m, msgStaticGlibc) {
|
||||||
|
@ -5,7 +5,7 @@ import (
|
|||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"hakurei.app/ldd"
|
"git.gensokyo.uk/security/hakurei/ldd"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestParseError(t *testing.T) {
|
func TestParseError(t *testing.T) {
|
||||||
|
@ -2,6 +2,8 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
_ "embed"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
@ -13,23 +15,59 @@ import (
|
|||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"hakurei.app/cmd/hakurei/internal/app"
|
"git.gensokyo.uk/security/hakurei/command"
|
||||||
"hakurei.app/cmd/hakurei/internal/app/instance"
|
"git.gensokyo.uk/security/hakurei/dbus"
|
||||||
"hakurei.app/cmd/hakurei/internal/state"
|
"git.gensokyo.uk/security/hakurei/hst"
|
||||||
"hakurei.app/command"
|
"git.gensokyo.uk/security/hakurei/internal"
|
||||||
"hakurei.app/hst"
|
"git.gensokyo.uk/security/hakurei/internal/app"
|
||||||
"hakurei.app/internal"
|
"git.gensokyo.uk/security/hakurei/internal/app/instance"
|
||||||
"hakurei.app/internal/hlog"
|
"git.gensokyo.uk/security/hakurei/internal/hlog"
|
||||||
"hakurei.app/system"
|
"git.gensokyo.uk/security/hakurei/internal/state"
|
||||||
"hakurei.app/system/dbus"
|
"git.gensokyo.uk/security/hakurei/internal/sys"
|
||||||
|
"git.gensokyo.uk/security/hakurei/sandbox"
|
||||||
|
"git.gensokyo.uk/security/hakurei/system"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
errSuccess = errors.New("success")
|
||||||
|
|
||||||
|
//go:embed LICENSE
|
||||||
|
license string
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() { hlog.Prepare("hakurei") }
|
||||||
|
|
||||||
|
var std sys.State = new(sys.Std)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// early init path, skips root check and duplicate PR_SET_DUMPABLE
|
||||||
|
sandbox.TryArgv0(hlog.Output{}, hlog.Prepare, internal.InstallFmsg)
|
||||||
|
|
||||||
|
if err := sandbox.SetDumpable(sandbox.SUID_DUMP_DISABLE); err != nil {
|
||||||
|
log.Printf("cannot set SUID_DUMP_DISABLE: %s", err)
|
||||||
|
// not fatal: this program runs as the privileged user
|
||||||
|
}
|
||||||
|
|
||||||
|
if os.Geteuid() == 0 {
|
||||||
|
log.Fatal("this program must not run as root")
|
||||||
|
}
|
||||||
|
|
||||||
|
buildCommand(os.Stderr).MustParse(os.Args[1:], func(err error) {
|
||||||
|
hlog.Verbosef("command returned %v", err)
|
||||||
|
if errors.Is(err, errSuccess) {
|
||||||
|
hlog.BeforeExit()
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
log.Fatal("unreachable")
|
||||||
|
}
|
||||||
|
|
||||||
func buildCommand(out io.Writer) command.Command {
|
func buildCommand(out io.Writer) command.Command {
|
||||||
var (
|
var (
|
||||||
flagVerbose bool
|
flagVerbose bool
|
||||||
flagJSON bool
|
flagJSON bool
|
||||||
)
|
)
|
||||||
c := command.New(out, log.Printf, "hakurei", func([]string) error { internal.InstallOutput(flagVerbose); return nil }).
|
c := command.New(out, log.Printf, "hakurei", func([]string) error { internal.InstallFmsg(flagVerbose); return nil }).
|
||||||
Flag(&flagVerbose, "v", command.BoolFlag(false), "Increase log verbosity").
|
Flag(&flagVerbose, "v", command.BoolFlag(false), "Increase log verbosity").
|
||||||
Flag(&flagJSON, "json", command.BoolFlag(false), "Serialise output in JSON when applicable")
|
Flag(&flagJSON, "json", command.BoolFlag(false), "Serialise output in JSON when applicable")
|
||||||
|
|
@ -6,7 +6,7 @@ import (
|
|||||||
"flag"
|
"flag"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"hakurei.app/command"
|
"git.gensokyo.uk/security/hakurei/command"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestHelp(t *testing.T) {
|
func TestHelp(t *testing.T) {
|
@ -65,7 +65,7 @@ buildGoModule rec {
|
|||||||
lib.attrsets.foldlAttrs
|
lib.attrsets.foldlAttrs
|
||||||
(
|
(
|
||||||
ldflags: name: value:
|
ldflags: name: value:
|
||||||
ldflags ++ [ "-X hakurei.app/internal.${name}=${value}" ]
|
ldflags ++ [ "-X git.gensokyo.uk/security/hakurei/internal.${name}=${value}" ]
|
||||||
)
|
)
|
||||||
(
|
(
|
||||||
[ "-s -w" ]
|
[ "-s -w" ]
|
||||||
@ -76,7 +76,7 @@ buildGoModule rec {
|
|||||||
)
|
)
|
||||||
{
|
{
|
||||||
version = "v${version}";
|
version = "v${version}";
|
||||||
hmain = "${placeholder "out"}/libexec/hakurei";
|
hakurei = "${placeholder "out"}/libexec/hakurei";
|
||||||
hsu = "/run/wrappers/bin/hsu";
|
hsu = "/run/wrappers/bin/hsu";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -10,9 +10,9 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"hakurei.app/cmd/hakurei/internal/state"
|
"git.gensokyo.uk/security/hakurei/hst"
|
||||||
"hakurei.app/hst"
|
"git.gensokyo.uk/security/hakurei/internal/hlog"
|
||||||
"hakurei.app/internal/hlog"
|
"git.gensokyo.uk/security/hakurei/internal/state"
|
||||||
)
|
)
|
||||||
|
|
||||||
func tryPath(name string) (config *hst.Config) {
|
func tryPath(name string) (config *hst.Config) {
|
@ -12,10 +12,10 @@ import (
|
|||||||
"text/tabwriter"
|
"text/tabwriter"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"hakurei.app/cmd/hakurei/internal/state"
|
"git.gensokyo.uk/security/hakurei/dbus"
|
||||||
"hakurei.app/hst"
|
"git.gensokyo.uk/security/hakurei/hst"
|
||||||
"hakurei.app/internal/hlog"
|
"git.gensokyo.uk/security/hakurei/internal/hlog"
|
||||||
"hakurei.app/system/dbus"
|
"git.gensokyo.uk/security/hakurei/internal/state"
|
||||||
)
|
)
|
||||||
|
|
||||||
func printShowSystem(output io.Writer, short, flagJSON bool) {
|
func printShowSystem(output io.Writer, short, flagJSON bool) {
|
||||||
@ -264,7 +264,7 @@ func printPs(output io.Writer, now time.Time, s state.Store, short, flagJSON boo
|
|||||||
as = strconv.Itoa(e.Config.Identity)
|
as = strconv.Itoa(e.Config.Identity)
|
||||||
id := e.Config.ID
|
id := e.Config.ID
|
||||||
if id == "" {
|
if id == "" {
|
||||||
id = "app.hakurei." + e.s[:8]
|
id = "uk.gensokyo.hakurei." + e.s[:8]
|
||||||
}
|
}
|
||||||
as += " (" + id + ")"
|
as += " (" + id + ")"
|
||||||
}
|
}
|
@ -5,10 +5,10 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"hakurei.app/cmd/hakurei/internal/app"
|
"git.gensokyo.uk/security/hakurei/dbus"
|
||||||
"hakurei.app/cmd/hakurei/internal/state"
|
"git.gensokyo.uk/security/hakurei/hst"
|
||||||
"hakurei.app/hst"
|
"git.gensokyo.uk/security/hakurei/internal/app"
|
||||||
"hakurei.app/system/dbus"
|
"git.gensokyo.uk/security/hakurei/internal/state"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -257,8 +257,7 @@ App
|
|||||||
],
|
],
|
||||||
"container": {
|
"container": {
|
||||||
"hostname": "localhost",
|
"hostname": "localhost",
|
||||||
"seccomp_flags": 1,
|
"seccomp": 32,
|
||||||
"seccomp_presets": 1,
|
|
||||||
"devel": true,
|
"devel": true,
|
||||||
"userns": true,
|
"userns": true,
|
||||||
"net": true,
|
"net": true,
|
||||||
@ -383,8 +382,7 @@ App
|
|||||||
],
|
],
|
||||||
"container": {
|
"container": {
|
||||||
"hostname": "localhost",
|
"hostname": "localhost",
|
||||||
"seccomp_flags": 1,
|
"seccomp": 32,
|
||||||
"seccomp_presets": 1,
|
|
||||||
"devel": true,
|
"devel": true,
|
||||||
"userns": true,
|
"userns": true,
|
||||||
"net": true,
|
"net": true,
|
||||||
@ -463,7 +461,7 @@ func Test_printPs(t *testing.T) {
|
|||||||
{"state corruption", state.Entries{app.ID{}: testState}, false, false, " Instance PID Application Uptime\n"},
|
{"state corruption", state.Entries{app.ID{}: testState}, false, false, " Instance PID Application Uptime\n"},
|
||||||
|
|
||||||
{"valid pd", state.Entries{testID: &state.State{ID: testID, PID: 1 << 8, Config: new(hst.Config), Time: testAppTime}}, false, false, ` Instance PID Application Uptime
|
{"valid pd", state.Entries{testID: &state.State{ID: testID, PID: 1 << 8, Config: new(hst.Config), Time: testAppTime}}, false, false, ` Instance PID Application Uptime
|
||||||
8e2c76b0 256 0 (app.hakurei.8e2c76b0) 1h2m32s
|
8e2c76b0 256 0 (uk.gensokyo.hakurei.8e2c76b0) 1h2m32s
|
||||||
`},
|
`},
|
||||||
|
|
||||||
{"valid", state.Entries{testID: testState}, false, false, ` Instance PID Application Uptime
|
{"valid", state.Entries{testID: testState}, false, false, ` Instance PID Application Uptime
|
||||||
@ -563,8 +561,7 @@ func Test_printPs(t *testing.T) {
|
|||||||
],
|
],
|
||||||
"container": {
|
"container": {
|
||||||
"hostname": "localhost",
|
"hostname": "localhost",
|
||||||
"seccomp_flags": 1,
|
"seccomp": 32,
|
||||||
"seccomp_presets": 1,
|
|
||||||
"devel": true,
|
"devel": true,
|
||||||
"userns": true,
|
"userns": true,
|
||||||
"net": true,
|
"net": true,
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user