235 lines
9.1 KiB
Go
Executable File
235 lines
9.1 KiB
Go
Executable File
// Package config holds the runtime configuration for a Sorcery-Go process.
|
|
//
|
|
// One Config is constructed at startup (from CLI flags + env vars + a small
|
|
// /etc/sorcery-go/config.yaml if present) and threaded through every pkg/*
|
|
// call site. This replaces the global Bash variables that the original
|
|
// Sorcery used.
|
|
//
|
|
// Paths default to /var/lib/sorcery-go so a Sorcery-Go binary can be
|
|
// dropped into an existing Source Mage chroot WITHOUT overwriting the
|
|
// legacy Bash state under /var/lib/sorcery. Both tools can coexist while
|
|
// the migration is in progress.
|
|
package config
|
|
|
|
import (
|
|
"fmt"
|
|
"log"
|
|
"net/http"
|
|
"os"
|
|
"path/filepath"
|
|
"runtime"
|
|
"time"
|
|
)
|
|
|
|
// Config is the runtime configuration.
|
|
type Config struct {
|
|
// RootDir is the engine's state root. Defaults to /var/lib/sorcery-go
|
|
// so we never clobber the legacy /var/lib/sorcery tree.
|
|
RootDir string
|
|
|
|
// GrimoirePath is the directory containing the spell tree. We default
|
|
// to the real Source Mage location so an existing chroot "just works".
|
|
GrimoirePath string
|
|
|
|
// StateDB is the bbolt file path.
|
|
StateDB string
|
|
|
|
// TombRoot is the content-addressable blob store root.
|
|
TombRoot string
|
|
|
|
// BuildRoot is where sandbox overlays are mounted.
|
|
BuildRoot string
|
|
|
|
// SpoolDir is where downloaded source tarballs land.
|
|
SpoolDir string
|
|
|
|
// LogDir is where per-spell build logs are persisted.
|
|
LogDir string
|
|
|
|
// Concurrency is the worker-pool size for parallel casts.
|
|
Concurrency int
|
|
|
|
// HostArch is the architecture of the running host.
|
|
HostArch string
|
|
|
|
// ActivePosture is the active legal profile name
|
|
// (strict_copyleft | corporate_lite | lawless).
|
|
ActivePosture string
|
|
|
|
// Firewall is which network gatekeeper is active
|
|
// (opensnitch | portmaster | none).
|
|
Firewall string
|
|
|
|
// PGPKeyring is the GnuPG keyring used to verify signed DETAILS files.
|
|
// Empty disables PGP attestation (the Warding will warn but not block).
|
|
PGPKeyring string
|
|
|
|
// Runtime is the container runtime to use for Sanctum management.
|
|
// (lxc | podman | firecracker | baremetal | auto)
|
|
// "auto" probes for available runtimes in order.
|
|
Runtime string
|
|
|
|
// EBPFEnforce controls whether the eBPF Tomb Guard operates in
|
|
// enforcing mode (true) or permissive/log-only mode (false).
|
|
EBPFEnforce bool
|
|
|
|
// FirecrackerBin is the path to the firecracker VMM binary.
|
|
// Used only when Runtime == "firecracker".
|
|
FirecrackerBin string
|
|
|
|
// FirecrackerKernel is the path to the kernel image for Firecracker VMs.
|
|
FirecrackerKernel string
|
|
|
|
// NetworkBridge is the host bridge interface for container networking.
|
|
// Defaults to "br0".
|
|
NetworkBridge string
|
|
|
|
// FesterURL is the URL of the Fester cluster controller.
|
|
// When set, distributed build scheduling and node telemetry are
|
|
// delegated to Fester. Empty means standalone mode.
|
|
// Example: "http://192.168.1.100:8787"
|
|
FesterURL string
|
|
|
|
// Toolchain selects which compiler toolchain to use.
|
|
// "gcc" (default) uses the host system GCC.
|
|
// "btc" uses a BTC.sh sovereign-forged toolchain.
|
|
Toolchain string
|
|
|
|
// BTCPath is the path to the BTC.sh script.
|
|
BTCPath string
|
|
|
|
// BTCRoot is the BTC persistent archive root directory.
|
|
BTCRoot string
|
|
|
|
// BTCSYSLabel is the pre-detected SYS_LABEL for the BTC toolchain.
|
|
// When empty, sorcery-go will auto-detect it from the golden image
|
|
// filename at startup.
|
|
BTCSYSLabel string
|
|
}
|
|
|
|
// Default returns a Config wired for an existing Source Mage chroot.
|
|
// Every path is overridable via env var (SORCERY_GO_<NAME>) so the same
|
|
// binary can run in CI, in a chroot, or in any supported container runtime.
|
|
func Default() *Config {
|
|
cfg := &Config{
|
|
RootDir: envOr("SORCERY_GO_ROOT", "/var/lib/sorcery-go"),
|
|
GrimoirePath: envOr("SORCERY_GO_GRIMOIRE", "/var/lib/sorcery/codex/grimoire"),
|
|
Concurrency: runtime.NumCPU(),
|
|
HostArch: hostArch(),
|
|
ActivePosture: envOr("SORCERY_GO_POSTURE", "strict_copyleft"),
|
|
Firewall: envOr("SORCERY_GO_FIREWALL", "none"),
|
|
PGPKeyring: envOr("SORCERY_GO_PGP_KEYRING", "/etc/sorcery-go/keyring.gpg"),
|
|
Runtime: envOr("SORCERY_GO_RUNTIME", "auto"),
|
|
EBPFEnforce: envBool("SORCERY_GO_EBPF_ENFORCE", false),
|
|
FirecrackerBin: envOr("SORCERY_GO_FIRECRACKER_BIN", "firecracker"),
|
|
FirecrackerKernel: envOr("SORCERY_GO_FIRECRACKER_KERNEL", ""),
|
|
NetworkBridge: envOr("SORCERY_GO_NETWORK_BRIDGE", "br0"),
|
|
FesterURL: envOr("SORCERY_GO_FESTER_URL", ""),
|
|
Toolchain: envOr("SORCERY_GO_TOOLCHAIN", "gcc"),
|
|
BTCPath: envOr("SORCERY_GO_BTC_PATH", "/opt/BTC/BTC.sh"),
|
|
BTCRoot: envOr("SORCERY_GO_BTC_ROOT", "/opt/BTC"),
|
|
BTCSYSLabel: envOr("SORCERY_GO_BTC_SYS_LABEL", ""),
|
|
}
|
|
cfg.StateDB = filepath.Join(cfg.RootDir, "state", "state.db")
|
|
cfg.TombRoot = filepath.Join(cfg.RootDir, "tomb")
|
|
cfg.BuildRoot = filepath.Join(cfg.RootDir, "build")
|
|
cfg.SpoolDir = envOr("SORCERY_GO_SPOOL", "/var/spool/sorcery-go")
|
|
cfg.LogDir = filepath.Join(cfg.RootDir, "log")
|
|
return cfg
|
|
}
|
|
|
|
// EnsureDirs creates every state directory with the right mode. Idempotent.
|
|
// Must be called as root (or with write access to RootDir).
|
|
func (c *Config) EnsureDirs() error {
|
|
dirs := []struct {
|
|
path string
|
|
mode os.FileMode
|
|
}{
|
|
{c.RootDir, 0755},
|
|
{filepath.Dir(c.StateDB), 0700},
|
|
{c.TombRoot, 0700},
|
|
{filepath.Join(c.TombRoot, "epitaphs"), 0700},
|
|
{filepath.Join(c.TombRoot, "blobs"), 0700},
|
|
{c.BuildRoot, 0755},
|
|
{c.SpoolDir, 0755},
|
|
{c.LogDir, 0755},
|
|
}
|
|
for _, d := range dirs {
|
|
if err := os.MkdirAll(d.path, d.mode); err != nil {
|
|
return fmt.Errorf("config: mkdir %s: %w", d.path, err)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// IsRoot reports whether the process is running as UID 0. Required for
|
|
// OverlayFS mounts and atomic commits to the live root.
|
|
func IsRoot() bool { return os.Geteuid() == 0 }
|
|
|
|
// ValidateFesterURL checks whether the configured FesterURL is reachable.
|
|
// It logs a warning if the URL is set but the host does not respond.
|
|
// This is a non-blocking best-effort check — failure does not prevent startup.
|
|
func (c *Config) ValidateFesterURL() {
|
|
if c.FesterURL == "" {
|
|
return
|
|
}
|
|
client := &http.Client{Timeout: 3 * time.Second}
|
|
resp, err := client.Get(c.FesterURL + "/api/health")
|
|
if err != nil {
|
|
log.Printf("config: warning: FesterURL %s is not reachable: %v", c.FesterURL, err)
|
|
log.Printf("config: distributed build features will be unavailable until Fester is accessible")
|
|
return
|
|
}
|
|
resp.Body.Close()
|
|
if resp.StatusCode != http.StatusOK {
|
|
log.Printf("config: warning: FesterURL %s returned status %d", c.FesterURL, resp.StatusCode)
|
|
} else {
|
|
log.Printf("config: FesterURL %s is reachable", c.FesterURL)
|
|
}
|
|
}
|
|
|
|
// ValidatePaths checks that critical paths exist and logs warnings
|
|
// for any that are missing. This gives operators clear error messages
|
|
// instead of cryptic "file not found" errors later.
|
|
func (c *Config) ValidatePaths() {
|
|
if _, err := os.Stat(c.GrimoirePath); os.IsNotExist(err) {
|
|
log.Printf("config: warning: GrimoirePath %s does not exist", c.GrimoirePath)
|
|
log.Printf("config: no spell definitions will be available. Set SORCERY_GO_GRIMOIRE or install a grimoire.")
|
|
}
|
|
if c.Toolchain == "btc" {
|
|
if _, err := os.Stat(c.BTCRoot); os.IsNotExist(err) {
|
|
log.Printf("config: warning: BTCRoot %s does not exist", c.BTCRoot)
|
|
log.Printf("config: BTC toolchain will not be available. Run BTC.sh first or set SORCERY_GO_BTC_ROOT.")
|
|
}
|
|
}
|
|
}
|
|
|
|
func envOr(key, def string) string {
|
|
if v := os.Getenv(key); v != "" {
|
|
return v
|
|
}
|
|
return def
|
|
}
|
|
|
|
func envBool(key string, def bool) bool {
|
|
v := os.Getenv(key)
|
|
if v == "" {
|
|
return def
|
|
}
|
|
return v == "1" || v == "true" || v == "yes"
|
|
}
|
|
|
|
// archMap maps runtime.GOARCH to Source Mage canonical arch names.
|
|
var archMap = map[string]string{
|
|
"arm64": "aarch64",
|
|
"amd64": "x86_64",
|
|
"386": "i686",
|
|
}
|
|
|
|
func hostArch() string {
|
|
if arch, ok := archMap[runtime.GOARCH]; ok {
|
|
return arch
|
|
}
|
|
return runtime.GOARCH
|
|
}
|