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 })