github.com/choria-io/go-choria@v0.28.1-0.20240416190746-b3bf9c7d5a45/aagent/watchers/machineswatcher/cmd/mms.go (about) 1 // Copyright (c) 2021-2023, R.I. Pienaar and the Choria Project contributors 2 // 3 // SPDX-License-Identifier: Apache-2.0 4 5 package main 6 7 import ( 8 "crypto/ed25519" 9 "crypto/rand" 10 "encoding/hex" 11 "encoding/json" 12 "fmt" 13 "os" 14 15 "github.com/choria-io/fisk" 16 machines "github.com/choria-io/go-choria/aagent/watchers/machineswatcher" 17 iu "github.com/choria-io/go-choria/internal/util" 18 "github.com/sirupsen/logrus" 19 ) 20 21 var ( 22 managed string 23 key string 24 force bool 25 ) 26 27 func main() { 28 app := fisk.New("mms", "Manage machine specifications") 29 30 app.Command("keys", "Create a key ed25519 pair").Action(keysAction) 31 32 sign := app.Command("pack", "Packs and optionally signs a specification").Action(signAction) 33 sign.Arg("machines", "A file holding JSON data describing machines to manage").Required().ExistingFileVar(&managed) 34 sign.Arg("key", "The ED25519 private key to encode with").Envar("KEY").StringVar(&key) 35 sign.Flag("force", "Do not warn about no ed25519 key").BoolVar(&force) 36 37 verify := app.Command("verify", "Verifies a signature made using pack").Action(verifyAction) 38 verify.Arg("source", "The signed artifact to validate").ExistingFileVar(&managed) 39 verify.Arg("key", "The ed25519 public key to verify with").StringVar(&key) 40 41 fisk.MustParse(app.Parse(os.Args[1:])) 42 } 43 44 func verifyAction(_ *fisk.ParseContext) error { 45 data, err := os.ReadFile(managed) 46 if err != nil { 47 return err 48 } 49 50 var spec machines.Specification 51 err = json.Unmarshal(data, &spec) 52 if err != nil { 53 return err 54 } 55 56 var pk ed25519.PublicKey 57 if iu.FileExist(key) { 58 kdat, err := os.ReadFile(key) 59 if err != nil { 60 return err 61 } 62 pk, err = hex.DecodeString(string(kdat)) 63 if err != nil { 64 return fmt.Errorf("invalid public key data") 65 } 66 } else { 67 pk, err = hex.DecodeString(key) 68 if err != nil { 69 return err 70 } 71 } 72 73 sig, err := hex.DecodeString(spec.Signature) 74 if err != nil { 75 return fmt.Errorf("invalid signature: %v", err) 76 } 77 78 ok, err := iu.Ed25519Verify(pk, spec.Machines, sig) 79 if err != nil { 80 return err 81 } 82 83 if !ok { 84 fmt.Printf("Data %s does not have a valid signature for key %s", managed, hex.EncodeToString(pk)) 85 os.Exit(1) 86 } 87 88 fmt.Printf("Data file %s is signed using %s\n", managed, hex.EncodeToString(pk)) 89 90 return nil 91 } 92 93 func keysAction(_ *fisk.ParseContext) error { 94 pub, pri, err := ed25519.GenerateKey(rand.Reader) 95 if err != nil { 96 return err 97 } 98 99 fmt.Printf(" Public Key: %s\n", hex.EncodeToString(pub)) 100 fmt.Printf("Private Key: %s\n", hex.EncodeToString(pri)) 101 102 return nil 103 } 104 105 func signAction(_ *fisk.ParseContext) error { 106 data, err := os.ReadFile(managed) 107 if err != nil { 108 return err 109 } 110 111 spec := machines.Specification{ 112 Machines: data, 113 } 114 115 if key != "" { 116 pk, err := hex.DecodeString(key) 117 if err != nil { 118 return err 119 } 120 121 spec.Signature = hex.EncodeToString(ed25519.Sign(pk, data)) 122 } else if !force { 123 logrus.Warn("No ed25519 private key given encoding without signing") 124 } 125 126 j, err := json.Marshal(spec) 127 if err != nil { 128 return err 129 } 130 131 fmt.Println(string(j)) 132 133 return nil 134 }