Wire the config loader, backends, and HTTP server into an executable.
`serve` runs the resolve hop until SIGINT/SIGTERM (graceful shutdown);
`get` resolves one ref to stdout as a bare value with no trailing
newline for $(...) exec-style plumbing. Exit codes: 0 ok, 1 usage or
config error, 2 resolution failure.
💘 Generated with Crush
Assisted-by: Crush:glm-5.2
143 lines
3.8 KiB
Go
143 lines
3.8 KiB
Go
// Command keyproxy resolves opaque mpk- refs to key material at the
|
|
// wire. v0 backends: file (0600 env files) and env (process env);
|
|
// bitwarden and vault ship as explicit not-implemented stubs until
|
|
// phase 3. Material is memory-only: never persisted, never logged.
|
|
package main
|
|
|
|
import (
|
|
"context"
|
|
"flag"
|
|
"fmt"
|
|
"os"
|
|
"os/signal"
|
|
"syscall"
|
|
|
|
"git.knownelement.com/ukrrs/mopac-keyproxy/internal/backend"
|
|
"git.knownelement.com/ukrrs/mopac-keyproxy/internal/config"
|
|
"git.knownelement.com/ukrrs/mopac-keyproxy/internal/server"
|
|
)
|
|
|
|
const usage = `keyproxy: resolve mpk- placeholder refs to key material
|
|
|
|
Usage:
|
|
keyproxy serve [-config PATH] [-listen ADDR] run the resolve HTTP hop
|
|
keyproxy get REF [-config PATH] resolve one ref to stdout
|
|
keyproxy help print this usage
|
|
|
|
Backends (v0): file (0600 env files), env (process env); bitwarden and
|
|
vault are explicit not-implemented stubs until phase 3. Material is
|
|
memory-only: never persisted, never logged.
|
|
|
|
Exit codes: 0 ok, 1 usage/config error, 2 resolution failure.
|
|
`
|
|
|
|
func main() {
|
|
os.Exit(run(os.Args[1:]))
|
|
}
|
|
|
|
func run(args []string) int {
|
|
if len(args) == 0 {
|
|
fmt.Fprint(os.Stderr, usage)
|
|
return 1
|
|
}
|
|
switch args[0] {
|
|
case "help", "-h", "--help":
|
|
fmt.Print(usage)
|
|
return 0
|
|
case "serve":
|
|
return cmdServe(args[1:])
|
|
case "get":
|
|
return cmdGet(args[1:])
|
|
default:
|
|
fmt.Fprintf(os.Stderr, "keyproxy: unknown command %q\n\n%s", args[0], usage)
|
|
return 1
|
|
}
|
|
}
|
|
|
|
// newRegistry wires every backend behind one interface; phase-3
|
|
// connectors replace the stubs in this single place.
|
|
func newRegistry() *backend.Registry {
|
|
return backend.NewRegistry(
|
|
backend.NewFile(),
|
|
backend.NewEnv(),
|
|
backend.NewBitwarden(),
|
|
backend.NewVault(),
|
|
)
|
|
}
|
|
|
|
func loadConfig(path string) (*config.Config, error) {
|
|
if path == "" {
|
|
path = os.Getenv("KEYPROXY_CONFIG")
|
|
}
|
|
if path == "" {
|
|
path = "keyproxy.toml"
|
|
}
|
|
return config.Load(path)
|
|
}
|
|
|
|
func cmdServe(args []string) int {
|
|
fs := flag.NewFlagSet("serve", flag.ContinueOnError)
|
|
configPath := fs.String("config", "", "config file (default $KEYPROXY_CONFIG, then ./keyproxy.toml)")
|
|
listen := fs.String("listen", "", "bind address (overrides config listen)")
|
|
if err := fs.Parse(args); err != nil {
|
|
return 1
|
|
}
|
|
cfg, err := loadConfig(*configPath)
|
|
if err != nil {
|
|
fmt.Fprintf(os.Stderr, "keyproxy: %v\n", err)
|
|
return 1
|
|
}
|
|
if *listen != "" {
|
|
cfg.Listen = *listen
|
|
}
|
|
srv, err := server.New(cfg, newRegistry(), os.Stdout)
|
|
if err != nil {
|
|
fmt.Fprintf(os.Stderr, "keyproxy: %v\n", err)
|
|
return 1
|
|
}
|
|
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
|
|
defer stop()
|
|
if err := srv.ListenAndServe(ctx); err != nil {
|
|
fmt.Fprintf(os.Stderr, "keyproxy: %v\n", err)
|
|
return 1
|
|
}
|
|
return 0
|
|
}
|
|
|
|
func cmdGet(args []string) int {
|
|
fs := flag.NewFlagSet("get", flag.ContinueOnError)
|
|
configPath := fs.String("config", "", "config file (default $KEYPROXY_CONFIG, then ./keyproxy.toml)")
|
|
if err := fs.Parse(args); err != nil {
|
|
return 1
|
|
}
|
|
if fs.NArg() != 1 {
|
|
fmt.Fprintln(os.Stderr, "keyproxy get: exactly one ref required (mpk-<name>)")
|
|
return 1
|
|
}
|
|
name := fs.Arg(0)
|
|
cfg, err := loadConfig(*configPath)
|
|
if err != nil {
|
|
fmt.Fprintf(os.Stderr, "keyproxy: %v\n", err)
|
|
return 1
|
|
}
|
|
ref, ok := cfg.Refs[name]
|
|
if !ok {
|
|
fmt.Fprintf(os.Stderr, "keyproxy: ref %s: unknown_ref\n", name)
|
|
return 2
|
|
}
|
|
b, ok := newRegistry().Get(ref.Backend)
|
|
if !ok {
|
|
fmt.Fprintf(os.Stderr, "keyproxy: ref %s: backend %s not registered\n", name, ref.Backend)
|
|
return 2
|
|
}
|
|
value, err := b.Resolve(context.Background(), ref)
|
|
if err != nil {
|
|
fmt.Fprintf(os.Stderr, "keyproxy: %v\n", backend.AsResolveError(err, ref, ref.Backend))
|
|
return 2
|
|
}
|
|
// No trailing newline: exec-style plumbing ($(keyproxy get mpk-x))
|
|
// wants the bare value.
|
|
fmt.Print(value)
|
|
return 0
|
|
}
|