// Hakurei runs user-specified containers as subordinate users. // // This program is generally invoked by another, higher level program, which // creates container configuration via package [hst] or an implementation of it. // // The parent may leave files open and specify their file descriptor for various // uses. In these cases, standard streams and netpoll files are treated as // invalid file descriptors and rejected. All string representations must be in // decimal. // // When specifying a [hst.Config] JSON stream or file to the run subcommand, the // argument "-" is equivalent to stdin. Otherwise, file descriptor rules // described above applies. Invalid file descriptors are treated as file names // in their string representation, with the exception that if a netpoll file // descriptor is attempted, the program fails. // // The flag --identifier-fd can be optionally specified to the run subcommand to // receive the identifier of the newly started instance. File descriptor rules // described above applies, and the file must be writable. This is sent after // its state is made available, so the client must not attempt to poll for it. // This uses the internal binary format of [hst.ID]. // // For the show and ps subcommands, the flag --json can be applied to the main // hakurei command to serialise output in JSON when applicable. Additionally, // the flag --short targeting each subcommand is used to omit some information // in both JSON and user-facing output. Only JSON-encoded output is covered // under the compatibility promise. // // A template for [hst.Config] demonstrating all available configuration fields // is returned by [hst.Template]. The JSON-encoded equivalent of this can be // obtained via the template subcommand. Fields left unpopulated in the template // (the direct_* family of fields, which are insecure under any configuration if // enabled) are unsupported. // // For simple (but insecure) testing scenarios, the exec subcommand can be used // to generate a simple, permissive configuration in-memory. See its help // message for all available options. package main import ( "context" _ "embed" "errors" "log" "os" "os/signal" "syscall" "hakurei.app/container" "hakurei.app/ext" "hakurei.app/message" ) //go:generate cp ../../LICENSE . //go:embed LICENSE var license string // earlyHardeningErrs are errors collected while setting up early hardening feature. type earlyHardeningErrs struct{ yamaLSM, dumpable error } func main() { // early init path, skips root check and duplicate PR_SET_DUMPABLE container.TryArgv0(nil) log.SetFlags(0) log.SetPrefix("hakurei: ") msg := message.New(log.Default()) early := earlyHardeningErrs{ yamaLSM: ext.SetPtracer(0), dumpable: ext.SetDumpable(ext.SUID_DUMP_DISABLE), } if os.Geteuid() == 0 { log.Fatal("this program must not run as root") } ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM) defer stop() // unreachable buildCommand(ctx, msg, &early, os.Stderr).MustParse(os.Args[1:], func(err error) { msg.Verbosef("command returned %v", err) if errors.Is(err, errSuccess) { msg.BeforeExit() os.Exit(0) } // this catches faulty command handlers that fail to return before this point }) log.Fatal("unreachable") }