aboutsummaryrefslogtreecommitdiffhomepage
path: root/cmd/hsu/main.go
diff options
context:
space:
mode:
authorOphestra <cat@gensokyo.uk>2026-03-28 18:06:12 +0900
committerOphestra <cat@gensokyo.uk>2026-03-28 18:17:31 +0900
commit62002efd08158512f6015d47461d264a14961553 (patch)
tree723cb027a3aac2eb8e61062826884505479370bf /cmd/hsu/main.go
parente33294db9c334530582215b2c5dd23c0730bc162 (diff)
cmd/hsu: document hsurc format and internals
This was previously only documented via an unexported function. Signed-off-by: Ophestra <cat@gensokyo.uk>
Diffstat (limited to 'cmd/hsu/main.go')
-rw-r--r--cmd/hsu/main.go83
1 files changed, 72 insertions, 11 deletions
diff --git a/cmd/hsu/main.go b/cmd/hsu/main.go
index 5c626a86..497279b8 100644
--- a/cmd/hsu/main.go
+++ b/cmd/hsu/main.go
@@ -1,7 +1,58 @@
+// hsu starts the hakurei shim as the target subordinate user.
+//
+// The hsu program must be installed with the setuid and setgid bit set, and
+// owned by root. A configuration file must be installed at /etc/hsurc with
+// permission bits 0400, and owned by root. Each line of the file specifies a
+// hakurei userid to kernel uid mapping. A line consists of the decimal string
+// representation of the uid of the user wishing to start hakurei containers,
+// followed by a space, followed by the decimal string representation of its
+// userid. Duplicate uid entries are ignored, with the first occurrence taking
+// effect.
+//
+// For example, to map the kernel uid 1000 to the hakurei user id 0:
+//
+// 1000 0
+//
+// # Internals
+//
+// Hakurei and hsu holds pathnames pointing to each other set at link time. For
+// this reason, a distribution of hakurei has fixed installation prefix. Since
+// this program is never invoked by the user, behaviour described in the
+// following paragraphs are considered an internal detail and not covered by the
+// compatibility promise.
+//
+// After checking credentials, hsu checks via /proc/ the absolute pathname of
+// its parent process, and fails if it does not match the hakurei pathname set
+// at link time. This is not a security feature: the priv-side is considered
+// trusted, and this feature makes no attempt to address the racy nature of
+// querying /proc/, or debuggers attached to the parent process. Instead, this
+// aims to discourage misuse and reduce confusion if the user accidentally
+// stumbles upon this program. It also prevents accidental use of the incorrect
+// installation of hsu in some environments.
+//
+// Since target container environment variables are set up in shim via the
+// [container] infrastructure, the environment is used for parameters from the
+// parent process.
+//
+// HAKUREI_SHIM specifies a single byte between '3' and '9' representing the
+// setup pipe file descriptor. It is passed as is to the shim process and is the
+// only value in the environment of the shim process. Since hsurc is not
+// accessible to the parent process, leaving this unset causes hsu to print the
+// corresponding hakurei user id of the parent and terminate.
+//
+// HAKUREI_IDENTITY specifies the identity of the instance being started and is
+// used to produce the kernel uid alongside hakurei user id looked up from hsurc.
+//
+// HAKUREI_GROUPS specifies supplementary groups to inherit from the credentials
+// of the parent process in a ' ' separated list of decimal string
+// representations of gid. This has the unfortunate consequence of allowing
+// users mapped via hsurc to effectively drop group membership, so special care
+// must be taken to ensure this does not lead to an increase in access. This is
+// not applicable to Rosa OS since unsigned code execution is not permitted
+// outside hakurei containers, and is generally nonapplicable to the security
+// model of hakurei, where all untrusted code runs within containers.
package main
-// minimise imports to avoid inadvertently calling init or global variable functions
-
import (
"bytes"
"fmt"
@@ -16,10 +67,13 @@ import (
)
const (
- // envIdentity is the name of the environment variable holding a
- // single byte representing the shim setup pipe file descriptor.
+ // envShim is the name of the environment variable holding a single byte
+ // representing the shim setup pipe file descriptor.
envShim = "HAKUREI_SHIM"
- // envGroups holds a ' ' separated list of string representations of
+ // envIdentity is the name of the environment variable holding a decimal
+ // string representation of the current application identity.
+ envIdentity = "HAKUREI_IDENTITY"
+ // envGroups holds a ' ' separated list of decimal string representations of
// supplementary group gid. Membership requirements are enforced.
envGroups = "HAKUREI_GROUPS"
)
@@ -35,7 +89,6 @@ func main() {
log.SetFlags(0)
log.SetPrefix("hsu: ")
- log.SetOutput(os.Stderr)
if os.Geteuid() != 0 {
log.Fatal("this program must be owned by uid 0 and have the setuid bit set")
@@ -99,8 +152,6 @@ func main() {
// last possible uid outcome
uidEnd = 999919999
)
-
- // cast to int for use with library functions
uid := int(toUser(userid, identity))
// final bounds check to catch any bugs
@@ -136,7 +187,6 @@ func main() {
}
// careful! users in the allowlist is effectively allowed to drop groups via hsu
-
if err := syscall.Setresgid(uid, uid, uid); err != nil {
log.Fatalf("cannot set gid: %v", err)
}
@@ -146,10 +196,21 @@ func main() {
if err := syscall.Setresuid(uid, uid, uid); err != nil {
log.Fatalf("cannot set uid: %v", err)
}
- if _, _, errno := syscall.AllThreadsSyscall(syscall.SYS_PRCTL, PR_SET_NO_NEW_PRIVS, 1, 0); errno != 0 {
+
+ if _, _, errno := syscall.AllThreadsSyscall(
+ syscall.SYS_PRCTL,
+ PR_SET_NO_NEW_PRIVS, 1,
+ 0,
+ ); errno != 0 {
log.Fatalf("cannot set no_new_privs flag: %s", errno.Error())
}
- if err := syscall.Exec(toolPath, []string{"hakurei", "shim"}, []string{envShim + "=" + shimSetupFd}); err != nil {
+
+ if err := syscall.Exec(toolPath, []string{
+ "hakurei",
+ "shim",
+ }, []string{
+ envShim + "=" + shimSetupFd,
+ }); err != nil {
log.Fatalf("cannot start shim: %v", err)
}