// Command msa-keygen prints the key material, identifiers and secrets a node needs, as a config file // and a relay registration entry. // // It is a thin wrapper over internal/enroll, which is also what `msa-agent enroll` calls. The two must // not drift: one writes the config an operator places, the other writes it on the node, and a // disagreement between them would show up as a node that pairs and then cannot authenticate. // // It generates the *agent's* keys only. The phone's keys are generated on the phone, at pairing time, // and never leave it — a tool that could mint a device key would be a tool that can impersonate a // phone. package main import ( "flag" "fmt" "os" "path/filepath" "fun.chick/msa-agent/internal/enroll" ) func main() { var ( nodeID = flag.String("node", "", "node id (nd_...); generated when empty") relayURL = flag.String("relay", "https://relay.example", "relay base URL, https") localPort = flag.Int("port", 3000, "the local SimAdmin port on 127.0.0.1") auditPath = flag.String("audit", "/var/log/msa-agent/audit.log", "audit log path") agentToken = flag.String("token", "", "relay bearer for this agent; generated when empty") secret = flag.String("provision-secret", "", "pairing secret for an unprotected node; generated when empty") write = flag.String("write", "", "write the config here instead of stdout (mode 0600)") ) flag.Parse() result, err := enroll.Generate(enroll.Options{ RelayURL: *relayURL, LocalPort: *localPort, AuditPath: *auditPath, NodeID: *nodeID, AgentToken: *agentToken, ProvisionSecret: *secret, }) check(err) if *write == "" { // The relay's own registration is printed alongside, because the two files have to agree on the // node id, the token and the signing key, and copying them by hand is where that agreement breaks. fmt.Fprintf(os.Stderr, "# agent config (paste into %s, then add \"nodePassword\" if the node is protected)\n", "/etc/msa-agent/config.json") os.Stdout.Write(result.Config) fmt.Fprintf(os.Stderr, "\n# relay registration: add this entry to MSA_AGENTS\n") fmt.Fprintf(os.Stderr, "%s\n", result.Registration) fmt.Fprintf(os.Stderr, "\n# pairing secret (unprotected nodes only; protected ones use nodePassword): %s\n", result.ProvisionSecret) fmt.Fprintf(os.Stderr, "# next: run `msa-agent --pair` on the node once, then pair the phone from the app\n") return } if err := os.MkdirAll(filepath.Dir(*write), 0o755); err != nil { check(err) } // 0600: this file holds the agent's private keys and, on a protected node, the node password. if err := os.WriteFile(*write, result.Config, 0o600); err != nil { check(err) } fmt.Printf("wrote %s (mode 0600)\n", *write) fmt.Printf("relay registration: %s\n", result.Registration) } func check(err error) { if err != nil { fmt.Fprintln(os.Stderr, "msa-keygen:", err) os.Exit(1) } }