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  }