hakurei/hst/config.go
Ophestra a341466942
All checks were successful
Test / Create distribution (push) Successful in 34s
Test / Sandbox (push) Successful in 2m11s
Test / Hakurei (push) Successful in 3m7s
Test / Sandbox (race detector) (push) Successful in 4m7s
Test / Hpkg (push) Successful in 4m9s
Test / Hakurei (race detector) (push) Successful in 4m47s
Test / Flake checks (push) Successful in 1m31s
hst: separate container config
The booleans are getting packed into a single field. This requires non-insignificant amount of code for JSON serialisation to stay compatible.

Signed-off-by: Ophestra <cat@gensokyo.uk>
2025-10-14 04:23:05 +09:00

120 lines
3.8 KiB
Go

package hst
import (
"errors"
"strconv"
"hakurei.app/container/check"
)
// Config configures an application container, implemented in internal/app.
type Config struct {
// Reverse-DNS style configured arbitrary identifier string.
// Passed to wayland security-context-v1 and used as part of defaults in dbus session proxy.
ID string `json:"id"`
// System services to make available in the container.
Enablements *Enablements `json:"enablements,omitempty"`
// Session D-Bus proxy configuration.
// If set to nil, session bus proxy assume built-in defaults.
SessionBus *BusConfig `json:"session_bus,omitempty"`
// System D-Bus proxy configuration.
// If set to nil, system bus proxy is disabled.
SystemBus *BusConfig `json:"system_bus,omitempty"`
// Direct access to wayland socket, no attempt is made to attach security-context-v1
// and the bare socket is made available to the container.
DirectWayland bool `json:"direct_wayland,omitempty"`
// Extra acl update ops to perform before setuid.
ExtraPerms []*ExtraPermConfig `json:"extra_perms,omitempty"`
// Numerical application id, passed to hsu, used to derive init user namespace credentials.
Identity int `json:"identity"`
// Init user namespace supplementary groups inherited by all container processes.
Groups []string `json:"groups"`
// High level configuration applied to the underlying [container].
Container *ContainerConfig `json:"container"`
}
var (
// ErrConfigNull is returned by [Config.Validate] for an invalid configuration that contains a null value for any
// field that must not be null.
ErrConfigNull = errors.New("unexpected null in config")
// ErrIdentityBounds is returned by [Config.Validate] for an out of bounds [Config.Identity] value.
ErrIdentityBounds = errors.New("identity out of bounds")
)
// Validate checks [Config] and returns [AppError] if an invalid value is encountered.
func (config *Config) Validate() error {
if config == nil {
return &AppError{Step: "validate configuration", Err: ErrConfigNull,
Msg: "invalid configuration"}
}
// this is checked again in hsu
if config.Identity < IdentityMin || config.Identity > IdentityMax {
return &AppError{Step: "validate configuration", Err: ErrIdentityBounds,
Msg: "identity " + strconv.Itoa(config.Identity) + " out of range"}
}
if err := config.SessionBus.CheckInterfaces("session"); err != nil {
return err
}
if err := config.SystemBus.CheckInterfaces("system"); err != nil {
return err
}
if config.Container == nil {
return &AppError{Step: "validate configuration", Err: ErrConfigNull,
Msg: "configuration missing container state"}
}
if config.Container.Home == nil {
return &AppError{Step: "validate configuration", Err: ErrConfigNull,
Msg: "container configuration missing path to home directory"}
}
if config.Container.Shell == nil {
return &AppError{Step: "validate configuration", Err: ErrConfigNull,
Msg: "container configuration missing path to shell"}
}
if config.Container.Path == nil {
return &AppError{Step: "validate configuration", Err: ErrConfigNull,
Msg: "container configuration missing path to initial program"}
}
return nil
}
// ExtraPermConfig describes an acl update op.
type ExtraPermConfig struct {
Ensure bool `json:"ensure,omitempty"`
Path *check.Absolute `json:"path"`
Read bool `json:"r,omitempty"`
Write bool `json:"w,omitempty"`
Execute bool `json:"x,omitempty"`
}
func (e *ExtraPermConfig) String() string {
if e == nil || e.Path == nil {
return "<invalid>"
}
buf := make([]byte, 0, 5+len(e.Path.String()))
buf = append(buf, '-', '-', '-')
if e.Ensure {
buf = append(buf, '+')
}
buf = append(buf, ':')
buf = append(buf, []byte(e.Path.String())...)
if e.Read {
buf[0] = 'r'
}
if e.Write {
buf[1] = 'w'
}
if e.Execute {
buf[2] = 'x'
}
return string(buf)
}