github.com/choria-io/go-choria@v0.28.1-0.20240416190746-b3bf9c7d5a45/providers/security/choria/choria_security_test.go (about)

     1  // Copyright (c) 2022, R.I. Pienaar and the Choria Project contributors
     2  //
     3  // SPDX-License-Identifier: Apache-2.0
     4  
     5  package choria
     6  
     7  import (
     8  	"crypto/ed25519"
     9  	"crypto/tls"
    10  	"encoding/hex"
    11  	"math/rand"
    12  	"os"
    13  	"path/filepath"
    14  	"testing"
    15  	"time"
    16  
    17  	"github.com/choria-io/go-choria/inter"
    18  	imock "github.com/choria-io/go-choria/inter/imocks"
    19  	iu "github.com/choria-io/go-choria/internal/util"
    20  	"github.com/choria-io/tokens"
    21  	"github.com/golang/mock/gomock"
    22  	. "github.com/onsi/ginkgo/v2"
    23  	. "github.com/onsi/gomega"
    24  	"github.com/onsi/gomega/gbytes"
    25  )
    26  
    27  func TestFileSecurity(t *testing.T) {
    28  	RegisterFailHandler(Fail)
    29  	RunSpecs(t, "Providers/Security/Choria")
    30  }
    31  
    32  var _ = Describe("Providers/Security/Choria", func() {
    33  	var (
    34  		err     error
    35  		cfg     *Config
    36  		prov    *ChoriaSecurity
    37  		fw      inter.Framework
    38  		mockCtl *gomock.Controller
    39  		logbuf  *gbytes.Buffer
    40  	)
    41  
    42  	BeforeEach(func() {
    43  		cfg = &Config{}
    44  		mockCtl = gomock.NewController(GinkgoT())
    45  		logbuf = gbytes.NewBuffer()
    46  		fw, _ = imock.NewFrameworkForTests(mockCtl, logbuf)
    47  
    48  		prov, err = New(WithConfig(cfg), WithLog(fw.Logger("ginkgo")))
    49  		Expect(err).ToNot(HaveOccurred())
    50  	})
    51  
    52  	AfterEach(func() {
    53  		mockCtl.Finish()
    54  	})
    55  
    56  	It("Should be a valid provider", func() {
    57  		Expect(any(prov).(inter.SecurityProvider).Provider()).To(Equal("choria"))
    58  		Expect(prov.BackingTechnology()).To(Equal(inter.SecurityTechnologyED25519JWT))
    59  		Expect(prov.Provider()).To(Equal("choria"))
    60  	})
    61  
    62  	Describe("WithChoriaConfig", func() {
    63  		It("Should verify trusted signer key lengths", func() {
    64  			cc := fw.Configuration()
    65  			cc.Choria.ChoriaSecurityTrustedSigners = []string{hex.EncodeToString([]byte("x"))}
    66  			err = WithChoriaConfig(cc)(prov)
    67  			Expect(err).To(MatchError("invalid ed25519 public key size: 78: 1"))
    68  		})
    69  
    70  		It("Should support disabling reply signatures", func() {
    71  			cc := fw.Configuration()
    72  			Expect(WithChoriaConfig(cc)(prov)).To(Succeed())
    73  			Expect(prov.conf.SignedReplies).To(BeTrue())
    74  
    75  			cc.Choria.ChoriaSecuritySignReplies = false
    76  			Expect(WithChoriaConfig(cc)(prov)).To(Succeed())
    77  			Expect(prov.conf.SignedReplies).To(BeFalse())
    78  		})
    79  
    80  		It("Should load all signers", func() {
    81  			cc := fw.Configuration()
    82  
    83  			pk1, _, err := iu.Ed25519KeyPair()
    84  			Expect(err).ToNot(HaveOccurred())
    85  			cc.Choria.ChoriaSecurityTrustedSigners = append(cc.Choria.ChoriaSecurityTrustedSigners, hex.EncodeToString(pk1))
    86  			pk2, _, err := iu.Ed25519KeyPair()
    87  			Expect(err).ToNot(HaveOccurred())
    88  			cc.Choria.ChoriaSecurityTrustedSigners = append(cc.Choria.ChoriaSecurityTrustedSigners, hex.EncodeToString(pk2))
    89  
    90  			Expect(WithChoriaConfig(cc)(prov)).To(Succeed())
    91  			Expect(prov.conf.TrustedTokenSigners).To(HaveLen(2))
    92  			Expect(prov.conf.TrustedTokenSigners[0]).To(Equal(pk1))
    93  			Expect(prov.conf.TrustedTokenSigners[1]).To(Equal(pk2))
    94  			Expect(prov.conf.SignedReplies).To(BeTrue())
    95  		})
    96  	})
    97  
    98  	Describe("Validate", func() {
    99  		BeforeEach(func() {
   100  			cfg.Identity = "ginkgo"
   101  			cfg.TokenFile = "/nonexisting"
   102  			cfg.SeedFile = "/nonexisting"
   103  			cfg.TrustedTokenSigners = []ed25519.PublicKey{[]byte("x")}
   104  		})
   105  
   106  		It("Should ensure a valid configuration exist", func() {
   107  			prov.conf = nil
   108  			errs, ok := prov.Validate()
   109  			Expect(ok).To(BeFalse())
   110  			Expect(errs).To(Equal([]string{"configuration not given"}))
   111  		})
   112  
   113  		It("Should ensure a logger is given", func() {
   114  			prov.log = nil
   115  			errs, ok := prov.Validate()
   116  			Expect(ok).To(BeFalse())
   117  			Expect(errs).To(Equal([]string{"logger not given"}))
   118  		})
   119  
   120  		It("Should ensure a identity is set", func() {
   121  			cfg.Identity = ""
   122  			errs, ok := prov.Validate()
   123  			Expect(ok).To(BeFalse())
   124  			Expect(errs).To(Equal([]string{"identity could not be determine automatically via Choria or was not supplied"}))
   125  		})
   126  
   127  		It("Should ensure a JWT token path is set", func() {
   128  			cfg.TokenFile = ""
   129  			errs, ok := prov.Validate()
   130  			Expect(ok).To(BeFalse())
   131  			Expect(errs).To(Equal([]string{"the path to the JWT token is not configured"}))
   132  		})
   133  
   134  		It("Should ensure a ed25519 seed path is set", func() {
   135  			cfg.SeedFile = ""
   136  			errs, ok := prov.Validate()
   137  			Expect(ok).To(BeFalse())
   138  			Expect(errs).To(Equal([]string{"the path to the ed25519 seed is not configured"}))
   139  		})
   140  
   141  		It("Should ensure trusted token signers are set", func() {
   142  			cfg.TrustedTokenSigners = []ed25519.PublicKey{}
   143  			errs, ok := prov.Validate()
   144  			Expect(errs).To(BeNil())
   145  			Expect(ok).To(BeTrue())
   146  
   147  			cfg.InitiatedByServer = true
   148  			errs, ok = prov.Validate()
   149  			Expect(errs).To(Equal([]string{"no trusted token signers or issuers configured"}))
   150  			Expect(ok).To(BeFalse())
   151  		})
   152  
   153  		It("Should prevent issuers and trusted signers from being used concurrently", func() {
   154  			cfg.Issuers = map[string]ed25519.PublicKey{"choria": cfg.TrustedTokenSigners[0]}
   155  			cfg.InitiatedByServer = true
   156  			errs, ok := prov.Validate()
   157  			Expect(errs).To(Equal([]string{"can only configure one of trusted token signers or issuers"}))
   158  			Expect(ok).To(BeFalse())
   159  		})
   160  	})
   161  
   162  	Describe("SignBytes", func() {
   163  		It("Should sign using the seed file", func() {
   164  			tf, err := os.CreateTemp("", "")
   165  			Expect(err).ToNot(HaveOccurred())
   166  			defer os.Remove(tf.Name())
   167  			tf.Close()
   168  
   169  			prov.conf.SeedFile = tf.Name()
   170  
   171  			_, prik, err := iu.Ed25519KeyPairToFile(tf.Name())
   172  			Expect(err).ToNot(HaveOccurred())
   173  
   174  			sig, err := prov.SignBytes([]byte("too many secrets"))
   175  			Expect(err).ToNot(HaveOccurred())
   176  
   177  			esig := ed25519.Sign(prik, []byte("too many secrets"))
   178  
   179  			Expect(sig).To(Equal(esig))
   180  		})
   181  	})
   182  
   183  	Describe("VerifySignatureBytes", func() {
   184  		Describe("Issuer Based", func() {
   185  			var (
   186  				// the issuer seed
   187  				issuerSeedFile string
   188  				issuerPubk     ed25519.PublicKey
   189  				issuerPrik     ed25519.PrivateKey
   190  
   191  				// aaa login service can make new clients
   192  				aaaSvcSeedFile, aaaSvcJWTFile string
   193  				aaaServicePubk                ed25519.PublicKey
   194  				aaaServicePrik                ed25519.PrivateKey
   195  
   196  				// aaa signer service
   197  				aaaSignerSeedFile, aaaSignerJWTFile string
   198  				aaaSignerPubk                       ed25519.PublicKey
   199  				aaaSignerPrik                       ed25519.PrivateKey
   200  
   201  				// caller that needs a signer to authorize it
   202  				delegatedCallerSeedFile, delegatedCallerJWTFile string
   203  
   204  				// correctly signed but without fleet access
   205  				nonFleetSeedFile, nonFleetJWTFile string
   206  				nonFleetPubk                      ed25519.PublicKey
   207  				nonFleetPrik                      ed25519.PrivateKey
   208  
   209  				// a correctly signed client with fleet access
   210  				callerSeedFile, callerJWTFile string
   211  				callerPubk                    ed25519.PublicKey
   212  				callerPrik                    ed25519.PrivateKey
   213  
   214  				// server signed by the issuer
   215  				serverSeedFile, serverJWTFile string
   216  				serverPubk                    ed25519.PublicKey
   217  				serverPrik                    ed25519.PrivateKey
   218  
   219  				// provisioner.jwt
   220  				provJWTFile string
   221  			)
   222  
   223  			BeforeEach(func() {
   224  				td, err := os.MkdirTemp("", "")
   225  				Expect(err).ToNot(HaveOccurred())
   226  				DeferCleanup(func() { os.RemoveAll(td) })
   227  
   228  				issuerSeedFile = filepath.Join(td, "issuer.seed")
   229  				aaaSvcSeedFile = filepath.Join(td, "aaa-login.seed")
   230  				aaaSvcJWTFile = filepath.Join(td, "aaa-login.jwt")
   231  				aaaSignerSeedFile = filepath.Join(td, "aaa-signer.seed")
   232  				aaaSignerJWTFile = filepath.Join(td, "aaa-signer.jwt")
   233  				delegatedCallerSeedFile = filepath.Join(td, "delegated.seed")
   234  				delegatedCallerJWTFile = filepath.Join(td, "delegated.jwt")
   235  				provJWTFile = filepath.Join(td, "provisioner.jwt")
   236  				nonFleetSeedFile = filepath.Join(td, "nonfleet.seed")
   237  				nonFleetJWTFile = filepath.Join(td, "nonfleet.jwt")
   238  				callerSeedFile = filepath.Join(td, "caller.seed")
   239  				callerJWTFile = filepath.Join(td, "caller.jwt")
   240  				serverSeedFile = filepath.Join(td, "server.seed")
   241  				serverJWTFile = filepath.Join(td, "server.jwt")
   242  
   243  				// issuer mode org issuer
   244  				issuerPubk, issuerPrik, err = iu.Ed25519KeyPairToFile(issuerSeedFile)
   245  				Expect(err).ToNot(HaveOccurred())
   246  
   247  				// provisioner.jwt signed by the issuer directly
   248  				provToken, err := tokens.NewProvisioningClaims(true, true, "x", "x", "x", nil, "example.net", "", "", "ginkgo", "", time.Hour)
   249  				Expect(err).ToNot(HaveOccurred())
   250  				Expect(provToken.Purpose).To(Equal(tokens.ProvisioningPurpose))
   251  				Expect(tokens.SaveAndSignTokenWithKeyFile(provToken, issuerSeedFile, provJWTFile, 0600)).To(Succeed())
   252  
   253  				// chain issuer for aaa service
   254  				aaaServicePubk, aaaServicePrik, err = iu.Ed25519KeyPairToFile(aaaSvcSeedFile)
   255  				Expect(err).ToNot(HaveOccurred())
   256  				aaaToken, err := tokens.NewClientIDClaims("aaasvc-login", nil, "choria", nil, "", "Ginkgo Tests", time.Hour, nil, aaaServicePubk)
   257  				Expect(err).ToNot(HaveOccurred())
   258  				Expect(aaaToken.AddOrgIssuerData(issuerPrik)).To(Succeed())
   259  				Expect(tokens.SaveAndSignTokenWithKeyFile(aaaToken, issuerSeedFile, aaaSvcJWTFile, 0600)).To(Succeed())
   260  
   261  				// aaa signer
   262  				aaaSignerPubk, aaaSignerPrik, err = iu.Ed25519KeyPairToFile(aaaSignerSeedFile)
   263  				Expect(err).ToNot(HaveOccurred())
   264  				aaaSignerToken, err := tokens.NewClientIDClaims("aaasvc-signer", nil, "choria", nil, "", "Ginkgo Tests", time.Hour, &tokens.ClientPermissions{AuthenticationDelegator: true}, aaaSignerPubk)
   265  				Expect(err).ToNot(HaveOccurred())
   266  				Expect(aaaSignerToken.Permissions.AuthenticationDelegator).To(BeTrue())
   267  				Expect(aaaSignerToken.AddOrgIssuerData(issuerPrik)).To(Succeed())
   268  				Expect(tokens.SaveAndSignTokenWithKeyFile(aaaSignerToken, issuerSeedFile, aaaSignerJWTFile, 0600)).To(Succeed())
   269  
   270  				// clients thats correctly signed etc, but cant access the fleet
   271  				nonFleetPubk, nonFleetPrik, err = iu.Ed25519KeyPairToFile(nonFleetSeedFile)
   272  				Expect(err).ToNot(HaveOccurred())
   273  				nonFleetToken, err := tokens.NewClientIDClaims("delegated_caller", nil, "choria", nil, "", "Ginkgo Tests", time.Hour, nil, nonFleetPubk)
   274  				Expect(err).ToNot(HaveOccurred())
   275  				Expect(nonFleetToken.AddOrgIssuerData(issuerPrik)).To(Succeed())
   276  				Expect(tokens.SaveAndSignTokenWithKeyFile(nonFleetToken, issuerSeedFile, nonFleetJWTFile, 0600)).To(Succeed())
   277  
   278  				// a valid non delegated caller with fleet access
   279  				callerPubk, callerPrik, err = iu.Ed25519KeyPairToFile(callerSeedFile)
   280  				Expect(err).ToNot(HaveOccurred())
   281  				callerToken, err := tokens.NewClientIDClaims("caller", nil, "choria", nil, "", "Ginkgo Tests", time.Hour, &tokens.ClientPermissions{FleetManagement: true}, callerPubk)
   282  				Expect(err).ToNot(HaveOccurred())
   283  				Expect(callerToken.AddOrgIssuerData(issuerPrik)).To(Succeed())
   284  				Expect(tokens.SaveAndSignTokenWithKeyFile(callerToken, issuerSeedFile, callerJWTFile, 0600)).To(Succeed())
   285  
   286  				// client that requires delegated signer
   287  				delegatedCallerPubk, _, err := iu.Ed25519KeyPairToFile(delegatedCallerSeedFile)
   288  				Expect(err).ToNot(HaveOccurred())
   289  				delegatedCallerToken, err := tokens.NewClientIDClaims("delegated_caller", nil, "choria", nil, "", "Ginkgo Tests", time.Hour, &tokens.ClientPermissions{SignedFleetManagement: true}, delegatedCallerPubk)
   290  				Expect(err).ToNot(HaveOccurred())
   291  				Expect(delegatedCallerToken.Permissions.SignedFleetManagement).To(BeTrue())
   292  				Expect(delegatedCallerToken.AddChainIssuerData(aaaToken, aaaServicePrik)).To(Succeed())
   293  				Expect(tokens.SaveAndSignTokenWithKeyFile(delegatedCallerToken, aaaSvcSeedFile, delegatedCallerJWTFile, 0600)).To(Succeed())
   294  
   295  				// server token
   296  				serverPubk, serverPrik, err = iu.Ed25519KeyPairToFile(serverSeedFile)
   297  				Expect(err).ToNot(HaveOccurred())
   298  				serverToken, err := tokens.NewServerClaims("example.net", []string{"choria"}, "choria", nil, nil, serverPubk, "ginkgo", time.Hour)
   299  				Expect(err).ToNot(HaveOccurred())
   300  				Expect(serverToken.Purpose).To(Equal(tokens.ServerPurpose))
   301  				Expect(serverToken.AddChainIssuerData(aaaToken, aaaServicePrik)).To(Succeed())
   302  				Expect(tokens.SaveAndSignTokenWithKeyFile(serverToken, aaaSvcSeedFile, serverJWTFile, 0600)).To(Succeed())
   303  
   304  				cfg.TrustedTokenSigners = []ed25519.PublicKey{}
   305  				cfg.Issuers = map[string]ed25519.PublicKey{
   306  					"choria": issuerPubk,
   307  				}
   308  			})
   309  
   310  			It("Should fail for no public parts", func() {
   311  				should, signer := prov.VerifySignatureBytes(nil, nil)
   312  				Expect(should).To(BeFalse())
   313  				Expect(signer).To(Equal(""))
   314  				Expect(logbuf).To(gbytes.Say("Received a signature verification request with no public parts"))
   315  			})
   316  
   317  			Describe("Caller signatures", func() {
   318  				It("Should detect invalid token purposes", func() {
   319  					pub, err := os.ReadFile(provJWTFile)
   320  					Expect(err).ToNot(HaveOccurred())
   321  					should, signer := prov.VerifySignatureBytes(nil, nil, pub)
   322  					Expect(should).To(BeFalse())
   323  					Expect(signer).To(Equal(""))
   324  					Expect(logbuf).To(gbytes.Say("Cannot verify byte signatures using a choria_provisioning token"))
   325  				})
   326  
   327  				It("Should deny client tokens that require a delegator to sign requests", func() {
   328  					pub, err := os.ReadFile(delegatedCallerJWTFile)
   329  					Expect(err).ToNot(HaveOccurred())
   330  					should, signer := prov.VerifySignatureBytes(nil, nil, pub)
   331  					Expect(should).To(BeFalse())
   332  					Expect(signer).To(Equal(""))
   333  					Expect(logbuf).To(gbytes.Say("access denied: requires authority delegation"))
   334  				})
   335  
   336  				It("Should deny client tokens that does not have fleet management access", func() {
   337  					pub, err := os.ReadFile(nonFleetJWTFile)
   338  					Expect(err).ToNot(HaveOccurred())
   339  					should, signer := prov.VerifySignatureBytes(nil, nil, pub)
   340  					Expect(should).To(BeFalse())
   341  					Expect(signer).To(Equal(""))
   342  					Expect(logbuf).To(gbytes.Say("access denied: does not have fleet management access"))
   343  				})
   344  
   345  				It("Should support client tokens", func() {
   346  					sig, err := iu.Ed25519Sign(callerPrik, []byte("too many secrets"))
   347  					Expect(err).ToNot(HaveOccurred())
   348  
   349  					pub, err := os.ReadFile(callerJWTFile)
   350  					Expect(err).ToNot(HaveOccurred())
   351  
   352  					should, signer := prov.VerifySignatureBytes([]byte("too many secrets"), sig, pub)
   353  					Expect(should).To(BeTrue())
   354  					Expect(signer).To(Equal("caller"))
   355  				})
   356  
   357  				It("Should support server tokens", func() {
   358  					sig, err := iu.Ed25519Sign(serverPrik, []byte("too many secrets"))
   359  					Expect(err).ToNot(HaveOccurred())
   360  
   361  					pub, err := os.ReadFile(serverJWTFile)
   362  					Expect(err).ToNot(HaveOccurred())
   363  
   364  					should, signer := prov.VerifySignatureBytes([]byte("too many secrets"), sig, pub)
   365  					Expect(should).To(BeTrue())
   366  					Expect(signer).To(Equal("example.net"))
   367  				})
   368  			})
   369  
   370  			Describe("Delegated signatures", func() {
   371  				It("Should detect invalid token purposes", func() {
   372  					pub, err := os.ReadFile(provJWTFile)
   373  					Expect(err).ToNot(HaveOccurred())
   374  					should, signer := prov.VerifySignatureBytes(nil, nil, nil, pub)
   375  					Expect(should).To(BeFalse())
   376  					Expect(signer).To(Equal(""))
   377  					Expect(logbuf).To(gbytes.Say("Cannot verify byte signatures using a choria_provisioning token"))
   378  				})
   379  
   380  				It("Should ensure that the signer has delegated signature permissions", func() {
   381  					pub, err := os.ReadFile(callerJWTFile)
   382  					Expect(err).ToNot(HaveOccurred())
   383  					should, signer := prov.VerifySignatureBytes(nil, nil, nil, pub)
   384  					Expect(should).To(BeFalse())
   385  					Expect(signer).To(Equal(""))
   386  					Expect(logbuf).To(gbytes.Say("token attempted to sign a request as delegator without required delegator permission"))
   387  				})
   388  
   389  				It("Should ensure the caller has fleet access", func() {
   390  					nonFleet, err := os.ReadFile(nonFleetJWTFile)
   391  					Expect(err).ToNot(HaveOccurred())
   392  
   393  					signerToken, err := os.ReadFile(aaaSignerJWTFile)
   394  					Expect(err).ToNot(HaveOccurred())
   395  
   396  					sig, err := iu.Ed25519Sign(nonFleetPrik, []byte("too many secrets"))
   397  					Expect(err).ToNot(HaveOccurred())
   398  
   399  					should, signer := prov.VerifySignatureBytes([]byte("too many secrets"), sig, nonFleet, signerToken)
   400  					Expect(should).To(BeFalse())
   401  					Expect(signer).To(Equal(""))
   402  					Expect(logbuf).To(gbytes.Say("caller token cannot be used without fleet management access"))
   403  				})
   404  
   405  				It("Should fail for server tokens", func() {
   406  					signerToken, err := os.ReadFile(aaaSignerJWTFile)
   407  					Expect(err).ToNot(HaveOccurred())
   408  
   409  					server, err := os.ReadFile(serverJWTFile)
   410  					Expect(err).ToNot(HaveOccurred())
   411  
   412  					sig, err := iu.Ed25519Sign(aaaSignerPrik, []byte("too many secrets"))
   413  					Expect(err).ToNot(HaveOccurred())
   414  
   415  					should, signer := prov.VerifySignatureBytes([]byte("too many secrets"), sig, server, signerToken)
   416  					Expect(should).To(BeFalse())
   417  					Expect(signer).To(Equal(""))
   418  					Expect(logbuf).To(gbytes.Say("could not load caller token using the same signer as the delegator: not a client token"))
   419  				})
   420  
   421  				It("Should support client tokens", func() {
   422  					signerToken, err := os.ReadFile(aaaSignerJWTFile)
   423  					Expect(err).ToNot(HaveOccurred())
   424  
   425  					caller, err := os.ReadFile(callerJWTFile)
   426  					Expect(err).ToNot(HaveOccurred())
   427  
   428  					sig, err := iu.Ed25519Sign(aaaSignerPrik, []byte("too many secrets"))
   429  					Expect(err).ToNot(HaveOccurred())
   430  
   431  					should, signer := prov.VerifySignatureBytes([]byte("too many secrets"), sig, caller, signerToken)
   432  					Expect(should).To(BeTrue())
   433  					Expect(signer).To(Equal("aaasvc-signer"))
   434  				})
   435  			})
   436  		})
   437  
   438  		Describe("Trusted Signers Based", func() {
   439  			var (
   440  				// a delegator that signs other users requests, has no fleet management feature access
   441  				delegateSeedFile, delegateJWTFile string
   442  
   443  				// a caller that may only access the fleet via delegator signature
   444  				delegatedCallerSeedFile, delegatedCallerJWTFile string
   445  
   446  				// a caller that can make fleet requests without signature
   447  				callerSeedFile, callerJWTFile string
   448  
   449  				// a provisioner token
   450  				provJWTFile string
   451  
   452  				// a server purpose token
   453  				serverSeedFile, serverJWTFile string
   454  
   455  				// signs all the jwt files
   456  				signerSeedFile string
   457  
   458  				delegatePrik, callerPrik, serverPrik ed25519.PrivateKey
   459  				delegatePubk, callerPubk, serverPubk ed25519.PublicKey
   460  			)
   461  
   462  			BeforeEach(func() {
   463  				td, err := os.MkdirTemp("", "")
   464  				Expect(err).ToNot(HaveOccurred())
   465  				DeferCleanup(func() { os.RemoveAll(td) })
   466  
   467  				signerSeedFile = filepath.Join(td, "signer.seed")
   468  				delegateSeedFile = filepath.Join(td, "delegate.seed")
   469  				delegateJWTFile = filepath.Join(td, "delegate.jwt")
   470  				delegatedCallerSeedFile = filepath.Join(td, "delegated_caller.seed")
   471  				delegatedCallerJWTFile = filepath.Join(td, "delegated_caller.jwt")
   472  				callerSeedFile = filepath.Join(td, "caller.seed")
   473  				callerJWTFile = filepath.Join(td, "caller.jwt")
   474  				serverSeedFile = filepath.Join(td, "server.seed")
   475  				serverJWTFile = filepath.Join(td, "server.jwt")
   476  				provJWTFile = filepath.Join(td, "provisioner.jwt")
   477  
   478  				// signs all the jwt tokens
   479  				signerPubk, _, err := iu.Ed25519KeyPairToFile(signerSeedFile)
   480  				Expect(err).ToNot(HaveOccurred())
   481  
   482  				// some other seed just to test multi trusted signer feature
   483  				otherPubk, _, err := iu.Ed25519KeyPair()
   484  				Expect(err).ToNot(HaveOccurred())
   485  
   486  				// just shuffle the trusted tokens to test multi signer support automatically
   487  				if rand.Intn(10) <= 5 {
   488  					cfg.TrustedTokenSigners = []ed25519.PublicKey{otherPubk, signerPubk}
   489  				} else {
   490  					cfg.TrustedTokenSigners = []ed25519.PublicKey{signerPubk, otherPubk}
   491  				}
   492  
   493  				// signs delegated requests
   494  				delegatePubk, delegatePrik, err = iu.Ed25519KeyPairToFile(delegateSeedFile)
   495  				Expect(err).ToNot(HaveOccurred())
   496  				delegateToken, err := tokens.NewClientIDClaims("delegate", nil, "choria", nil, "", "Ginkgo Tests", time.Hour, &tokens.ClientPermissions{AuthenticationDelegator: true}, delegatePubk)
   497  				Expect(err).ToNot(HaveOccurred())
   498  				Expect(delegateToken.Permissions.AuthenticationDelegator).To(BeTrue())
   499  				Expect(tokens.SaveAndSignTokenWithKeyFile(delegateToken, signerSeedFile, delegateJWTFile, 0600)).To(Succeed())
   500  
   501  				// a caller that needs delegation
   502  				delegatedCallerPubk, _, err := iu.Ed25519KeyPairToFile(delegatedCallerSeedFile)
   503  				Expect(err).ToNot(HaveOccurred())
   504  				delegatedCallerToken, err := tokens.NewClientIDClaims("delegated_caller", nil, "choria", nil, "", "Ginkgo Tests", time.Hour, &tokens.ClientPermissions{SignedFleetManagement: true}, delegatedCallerPubk)
   505  				Expect(err).ToNot(HaveOccurred())
   506  				Expect(delegatedCallerToken.Permissions.SignedFleetManagement).To(BeTrue())
   507  				Expect(tokens.SaveAndSignTokenWithKeyFile(delegatedCallerToken, signerSeedFile, delegatedCallerJWTFile, 0600)).To(Succeed())
   508  
   509  				// caller that can sign itself
   510  				callerPubk, callerPrik, err = iu.Ed25519KeyPairToFile(callerSeedFile)
   511  				Expect(err).ToNot(HaveOccurred())
   512  				callerToken, err := tokens.NewClientIDClaims("caller", nil, "choria", nil, "", "Ginkgo Tests", time.Hour, &tokens.ClientPermissions{FleetManagement: true}, callerPubk)
   513  				Expect(err).ToNot(HaveOccurred())
   514  				Expect(callerToken.Permissions.FleetManagement).To(BeTrue())
   515  				Expect(tokens.SaveAndSignTokenWithKeyFile(callerToken, signerSeedFile, callerJWTFile, 0600)).To(Succeed())
   516  
   517  				// server token
   518  				serverPubk, serverPrik, err = iu.Ed25519KeyPairToFile(serverSeedFile)
   519  				Expect(err).ToNot(HaveOccurred())
   520  				serverToken, err := tokens.NewServerClaims("example.net", []string{"choria"}, "choria", nil, nil, serverPubk, "ginkgo", time.Hour)
   521  				Expect(err).ToNot(HaveOccurred())
   522  				Expect(serverToken.Purpose).To(Equal(tokens.ServerPurpose))
   523  				Expect(tokens.SaveAndSignTokenWithKeyFile(serverToken, signerSeedFile, serverJWTFile, 0600)).To(Succeed())
   524  
   525  				// a provisioner purpose token
   526  				provToken, err := tokens.NewProvisioningClaims(true, true, "x", "x", "x", nil, "example.net", "", "", "ginkgo", "", time.Hour)
   527  				Expect(err).ToNot(HaveOccurred())
   528  				Expect(provToken.Purpose).To(Equal(tokens.ProvisioningPurpose))
   529  				Expect(tokens.SaveAndSignTokenWithKeyFile(provToken, signerSeedFile, provJWTFile, 0600)).To(Succeed())
   530  			})
   531  
   532  			It("Should fail for no public parts", func() {
   533  				should, signer := prov.VerifySignatureBytes(nil, nil)
   534  				Expect(should).To(BeFalse())
   535  				Expect(signer).To(Equal(""))
   536  				Expect(logbuf).To(gbytes.Say("Received a signature verification request with no public parts"))
   537  			})
   538  
   539  			Describe("Caller signatures", func() {
   540  				It("Should detect invalid token purposes", func() {
   541  					pub, err := os.ReadFile(provJWTFile)
   542  					Expect(err).ToNot(HaveOccurred())
   543  					should, signer := prov.VerifySignatureBytes(nil, nil, pub)
   544  					Expect(should).To(BeFalse())
   545  					Expect(signer).To(Equal(""))
   546  					Expect(logbuf).To(gbytes.Say("Cannot verify byte signatures using a choria_provisioning token"))
   547  				})
   548  
   549  				It("Should deny client tokens that require a delegator to sign requests", func() {
   550  					pub, err := os.ReadFile(delegatedCallerJWTFile)
   551  					Expect(err).ToNot(HaveOccurred())
   552  					should, signer := prov.VerifySignatureBytes(nil, nil, pub)
   553  					Expect(should).To(BeFalse())
   554  					Expect(signer).To(Equal(""))
   555  					Expect(logbuf).To(gbytes.Say("Could not verify signature by caller: access denied: requires authority delegation"))
   556  				})
   557  
   558  				It("Should deny client tokens that does not have fleet management access", func() {
   559  					pub, err := os.ReadFile(delegateJWTFile)
   560  					Expect(err).ToNot(HaveOccurred())
   561  					should, signer := prov.VerifySignatureBytes(nil, nil, pub)
   562  					Expect(should).To(BeFalse())
   563  					Expect(signer).To(Equal(""))
   564  					Expect(logbuf).To(gbytes.Say("Could not verify signature by caller: access denied: does not have fleet management access"))
   565  				})
   566  
   567  				It("Should support client tokens", func() {
   568  					sig, err := iu.Ed25519Sign(callerPrik, []byte("too many secrets"))
   569  					Expect(err).ToNot(HaveOccurred())
   570  
   571  					pub, err := os.ReadFile(callerJWTFile)
   572  					Expect(err).ToNot(HaveOccurred())
   573  
   574  					should, signer := prov.VerifySignatureBytes([]byte("too many secrets"), sig, pub)
   575  					Expect(should).To(BeTrue())
   576  					Expect(signer).To(Equal("caller"))
   577  				})
   578  
   579  				It("Should support server tokens", func() {
   580  					sig, err := iu.Ed25519Sign(serverPrik, []byte("too many secrets"))
   581  					Expect(err).ToNot(HaveOccurred())
   582  
   583  					pub, err := os.ReadFile(serverJWTFile)
   584  					Expect(err).ToNot(HaveOccurred())
   585  
   586  					should, signer := prov.VerifySignatureBytes([]byte("too many secrets"), sig, pub)
   587  					Expect(should).To(BeTrue())
   588  					Expect(signer).To(Equal("example.net"))
   589  				})
   590  			})
   591  
   592  			Describe("Delegated signatures", func() {
   593  				It("Should detect invalid token purposes", func() {
   594  					pub, err := os.ReadFile(provJWTFile)
   595  					Expect(err).ToNot(HaveOccurred())
   596  					should, signer := prov.VerifySignatureBytes(nil, nil, nil, pub)
   597  					Expect(should).To(BeFalse())
   598  					Expect(signer).To(Equal(""))
   599  					Expect(logbuf).To(gbytes.Say("Cannot verify byte signatures using a choria_provisioning token"))
   600  				})
   601  
   602  				It("Should ensure that the signer has delegated signature permissions", func() {
   603  					pub, err := os.ReadFile(callerJWTFile)
   604  					Expect(err).ToNot(HaveOccurred())
   605  					should, signer := prov.VerifySignatureBytes(nil, nil, nil, pub)
   606  					Expect(should).To(BeFalse())
   607  					Expect(signer).To(Equal(""))
   608  					Expect(logbuf).To(gbytes.Say("token attempted to sign a request as delegator without required delegator permission"))
   609  				})
   610  
   611  				It("Should ensure the caller has fleet access", func() {
   612  					delegate, err := os.ReadFile(delegateJWTFile)
   613  					Expect(err).ToNot(HaveOccurred())
   614  
   615  					sig, err := iu.Ed25519Sign(delegatePrik, []byte("too many secrets"))
   616  					Expect(err).ToNot(HaveOccurred())
   617  
   618  					should, signer := prov.VerifySignatureBytes([]byte("too many secrets"), sig, delegate, delegate)
   619  					Expect(should).To(BeFalse())
   620  					Expect(signer).To(Equal(""))
   621  					Expect(logbuf).To(gbytes.Say("caller token cannot be used without fleet management access"))
   622  				})
   623  
   624  				It("Should fail for server tokens", func() {
   625  					delegate, err := os.ReadFile(delegateJWTFile)
   626  					Expect(err).ToNot(HaveOccurred())
   627  
   628  					server, err := os.ReadFile(serverJWTFile)
   629  					Expect(err).ToNot(HaveOccurred())
   630  
   631  					sig, err := iu.Ed25519Sign(delegatePrik, []byte("too many secrets"))
   632  					Expect(err).ToNot(HaveOccurred())
   633  
   634  					should, signer := prov.VerifySignatureBytes([]byte("too many secrets"), sig, server, delegate)
   635  					Expect(should).To(BeFalse())
   636  					Expect(signer).To(Equal(""))
   637  					Expect(logbuf).To(gbytes.Say("could not load caller token using the same signer as the delegator: not a client token"))
   638  				})
   639  
   640  				It("Should support client tokens", func() {
   641  					delegate, err := os.ReadFile(delegateJWTFile)
   642  					Expect(err).ToNot(HaveOccurred())
   643  
   644  					caller, err := os.ReadFile(callerJWTFile)
   645  					Expect(err).ToNot(HaveOccurred())
   646  
   647  					sig, err := iu.Ed25519Sign(delegatePrik, []byte("too many secrets"))
   648  					Expect(err).ToNot(HaveOccurred())
   649  
   650  					should, signer := prov.VerifySignatureBytes([]byte("too many secrets"), sig, caller, delegate)
   651  					Expect(should).To(BeTrue())
   652  					Expect(signer).To(Equal("delegate"))
   653  				})
   654  			})
   655  		})
   656  	})
   657  
   658  	Describe("PublicCert", func() {
   659  		It("Should load the correct public cert", func() {
   660  			pc, err := prov.PublicCert()
   661  			Expect(err).To(HaveOccurred())
   662  			Expect(pc).To(BeNil())
   663  
   664  			prov.conf.Key = filepath.Join("..", "testdata", "good", "private_keys", "rip.mcollective.pem")
   665  			prov.conf.Certificate = filepath.Join("..", "testdata", "good", "certs", "rip.mcollective.pem")
   666  
   667  			pc, err = prov.PublicCert()
   668  			Expect(err).ToNot(HaveOccurred())
   669  
   670  			Expect(pc.Subject.String()).To(Equal("CN=rip.mcollective"))
   671  		})
   672  	})
   673  
   674  	Describe("TLSConfig", func() {
   675  		It("Should support operating without cert/key/ca", func() {
   676  			c, err := prov.TLSConfig()
   677  			Expect(err).ToNot(HaveOccurred())
   678  
   679  			Expect(c.InsecureSkipVerify).To(BeTrue())
   680  			Expect(err).ToNot(HaveOccurred())
   681  
   682  			Expect(c.Certificates).To(BeEmpty())
   683  		})
   684  
   685  		It("Should produce a valid TLS Config", func() {
   686  			prov.conf.Key = filepath.Join("..", "testdata", "good", "private_keys", "rip.mcollective.pem")
   687  			prov.conf.Certificate = filepath.Join("..", "testdata", "good", "certs", "rip.mcollective.pem")
   688  			prov.conf.CA = filepath.Join("..", "testdata", "good", "certs", "ca.pem")
   689  
   690  			c, err := prov.TLSConfig()
   691  			Expect(err).ToNot(HaveOccurred())
   692  
   693  			Expect(c.InsecureSkipVerify).To(BeFalse())
   694  			Expect(err).ToNot(HaveOccurred())
   695  
   696  			cert, err := tls.LoadX509KeyPair(prov.conf.Certificate, prov.conf.Key)
   697  			Expect(err).ToNot(HaveOccurred())
   698  
   699  			Expect(c.Certificates).To(HaveLen(1))
   700  			Expect(c.Certificates[0].Certificate).To(Equal(cert.Certificate))
   701  		})
   702  
   703  		It("Should support disabling tls verify", func() {
   704  			cfg.DisableTLSVerify = true
   705  
   706  			c, err := prov.TLSConfig()
   707  			Expect(err).ToNot(HaveOccurred())
   708  
   709  			Expect(c.InsecureSkipVerify).To(BeTrue())
   710  		})
   711  	})
   712  
   713  })