Files
orca/internal/cli/root.go
T
Jon Chery 5232fcb808 fix(P04): wire ACL enforcement + WebAuthn reg auth + audit actor (REQ-153)
R-023: Zero-trust enforcement operationally wired.

ACL enforcement (C-45 staged rollout):
- acl.Check wired into all 5 daemon handlers (dispatch/jobs/nodes/tasks)
- health endpoints exempt (liveness probes not gated)
- ACL log-only mode default (config acl.enforce=false); enforce after
  bootstrap ACL verified
- sshpush auth: ORCA_OIDC_TOKEN validated against JWKS before apply
- txn apply: Authorize hook validates OIDC token before running pull
- acl.json mode 0600 (was 0644)
- flock on acl.json for concurrent grant/revoke
- bootstrap ACL: init grants cluster-admin to orca-admins group + SVID

Audit actor identity:
- currentActor reads OIDC sub from credentials.json (was hardcoded "cli")
- threaded through all audit.Record calls via context

WebAuthn registration auth:
- BeginRegistration/FinishRegistration require authenticated session
- fail-closed 401 when no authFunc configured

New files: internal/daemon/acl.go, internal/cli/authactor.go,
internal/engine/actor.go, internal/identity/authtoken.go,
internal/sshpush/auth.go, internal/txn/auth_test.go

---ci---
project: orca
phase: 4
milestone: v0.13
status: complete
requirements:
  covered: [153]
---/ci---
2026-08-07 20:33:39 +00:00

110 lines
3.1 KiB
Go

package cli
import (
"context"
"encoding/json"
"fmt"
"log/slog"
"os"
"github.com/spf13/cobra"
"git.cloudinit.dev/coreci/orca/internal/config"
)
type configCtxKey struct{}
var (
version = "0.1.0-dev"
gitCommit = "unknown"
buildTime = "unknown"
)
const systemNamespaceRoot = "/root/.orca"
var rootCmd = &cobra.Command{
Use: "orca",
Short: "Orca — offline/CLI-first orchestration engine",
Long: `Orca is a minimalist, offline-first, CLI-first orchestration engine
inspired by HashiCorp Nomad, prioritizing stability, security, and simplicity
over feature richness.`,
SilenceUsage: true,
SilenceErrors: true,
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
if systemNamespace {
if existing := os.Getenv("ORCA_HOME"); existing != "" && existing != systemNamespaceRoot {
return fmt.Errorf("--system conflicts with ORCA_HOME=%q (already set); unset ORCA_HOME or drop --system", existing)
}
if err := os.Setenv("ORCA_HOME", systemNamespaceRoot); err != nil {
return fmt.Errorf("set ORCA_HOME for --system: %w", err)
}
}
if configPath != "" {
cfg, err := config.Load(configPath)
if err != nil {
return fmt.Errorf("load config %s: %w", configPath, err)
}
cmd.SetContext(context.WithValue(cmd.Context(), configCtxKey{}, cfg))
}
// P04 (T5): thread the verified operator identity into the
// command context so audit entries attribute actions to the
// real OIDC sub (or SPIFFE SVID) instead of the hardcoded
// "cli" string. currentActor reads ~/.orca/credentials.json.
cmd.SetContext(withActor(cmd.Context(), currentActor(context.Background())))
return nil
},
}
var (
jsonOutput bool
systemNamespace bool
configPath string
noDeprecationWarnings bool
)
func init() {
rootCmd.PersistentFlags().BoolVar(&jsonOutput, "json", false, "output in JSON format")
rootCmd.PersistentFlags().BoolVar(&systemNamespace, "system", false, "use system-level namespace root (/root/.orca) instead of user-level (~/.orca)")
rootCmd.PersistentFlags().StringVar(&configPath, "config", "", "path to config.hcl (overrides ~/.orca/config.hcl)")
rootCmd.PersistentFlags().BoolVar(&noDeprecationWarnings, "no-deprecation-warnings", false, "suppress v0.9 deprecation warnings (use during `orca upgrade` migrations)")
}
// warnDeprecated emits a v0.9 deprecation warning via slog.Warn unless
// the --no-deprecation-warnings global flag is set. Callers pass a
// human-readable message describing what changed. REQ-068.
func warnDeprecated(msg string) {
if noDeprecationWarnings {
return
}
slog.Warn(msg)
}
func configFromCtx(ctx context.Context) *config.Config {
if v, ok := ctx.Value(configCtxKey{}).(*config.Config); ok {
return v
}
return nil
}
func Execute() error {
return rootCmd.Execute()
}
func printJSON(v any) error {
enc := json.NewEncoder(rootCmd.OutOrStdout())
enc.SetIndent("", " ")
return enc.Encode(v)
}
func printText(format string, args ...any) {
fmt.Fprintf(rootCmd.OutOrStdout(), format, args...)
}
func printResult(text string, jsonObj any) {
if jsonOutput {
_ = printJSON(jsonObj)
return
}
printText("%s\n", text)
}