github.com/choria-io/go-choria@v0.28.1-0.20240416190746-b3bf9c7d5a45/providers/security/choria/choria_security.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 "bytes" 9 "context" 10 "crypto/ed25519" 11 "crypto/sha256" 12 "crypto/tls" 13 "crypto/x509" 14 "encoding/hex" 15 "errors" 16 "fmt" 17 "net/http" 18 "net/url" 19 "os" 20 "regexp" 21 "sync" 22 "time" 23 24 "github.com/choria-io/go-choria/inter" 25 iu "github.com/choria-io/go-choria/internal/util" 26 "github.com/choria-io/go-choria/tlssetup" 27 "github.com/choria-io/tokens" 28 "github.com/sirupsen/logrus" 29 ) 30 31 var ( 32 callerFormat = "choria=%s" 33 callerIDRe = regexp.MustCompile(`^[a-z]+=([\w\.\-]+)`) 34 errPermissionDenied = errors.New("access denied") 35 ) 36 37 type ChoriaSecurity struct { 38 conf *Config 39 mu *sync.Mutex 40 log *logrus.Entry 41 } 42 43 type Config struct { 44 // Identity when not empty will force the identity to be used for validations etc 45 Identity string 46 47 // SeedFile is the file holding the ed25519 seed 48 SeedFile string 49 50 // TokenFile is the file holding the signed JWT file 51 TokenFile string 52 53 // Issuers are Organization issuers that may issue tokens 54 Issuers map[string]ed25519.PublicKey 55 56 // TrustedTokenSigners are keys allowed to sign tokens 57 TrustedTokenSigners []ed25519.PublicKey 58 59 // Is a URL where a remote signer is running 60 RemoteSignerURL string 61 62 // TLSSetup is the shared TLS configuration state between security providers 63 TLSConfig *tlssetup.Config 64 65 // RemoteSigner is the signer used to sign requests using a remote like AAA Service 66 RemoteSigner inter.RequestSigner 67 68 // DisableTLSVerify disables TLS verify in HTTP clients etc 69 DisableTLSVerify bool 70 71 // Certificate is the path to the public certificate 72 Certificate string 73 74 // Key is the path to the private key 75 Key string 76 77 // CA is the path to the Certificate Authority 78 CA string 79 80 // SignedReplies indicates that servers replying should sign their messages 81 SignedReplies bool 82 83 // InitiatedByServer indicates this is a server, it would require trusted signers 84 InitiatedByServer bool 85 } 86 87 func New(opts ...Option) (*ChoriaSecurity, error) { 88 s := &ChoriaSecurity{ 89 conf: &Config{ 90 SignedReplies: true, 91 }, 92 mu: &sync.Mutex{}, 93 } 94 95 for _, opt := range opts { 96 err := opt(s) 97 if err != nil { 98 return nil, err 99 } 100 } 101 102 if s.log == nil { 103 return nil, fmt.Errorf("logger is required") 104 } 105 106 s.log = s.log.WithFields(logrus.Fields{ 107 "mTLS": s.conf.CA != "", 108 "token": s.conf.TokenFile, 109 "seed": s.conf.SeedFile, 110 }) 111 112 s.log.Infof("Security provider initializing") 113 114 return s, nil 115 } 116 117 func (s *ChoriaSecurity) Provider() string { 118 return "choria" 119 } 120 121 func (s *ChoriaSecurity) BackingTechnology() inter.SecurityTechnology { 122 return inter.SecurityTechnologyED25519JWT 123 } 124 125 func (s *ChoriaSecurity) TokenBytes() ([]byte, error) { 126 return os.ReadFile(s.conf.TokenFile) 127 } 128 129 func (s *ChoriaSecurity) Validate() ([]string, bool) { 130 var errors []string 131 132 if s.log == nil { 133 errors = append(errors, "logger not given") 134 } 135 136 if s.conf == nil { 137 errors = append(errors, "configuration not given") 138 } else { 139 if s.conf.Identity == "" { 140 errors = append(errors, "identity could not be determine automatically via Choria or was not supplied") 141 } 142 143 if s.conf.TokenFile == "" { 144 errors = append(errors, "the path to the JWT token is not configured") 145 } 146 147 if s.conf.SeedFile == "" { 148 errors = append(errors, "the path to the ed25519 seed is not configured") 149 } 150 151 if s.conf.InitiatedByServer { 152 if len(s.conf.TrustedTokenSigners) == 0 && len(s.conf.Issuers) == 0 { 153 errors = append(errors, "no trusted token signers or issuers configured") 154 } 155 156 if len(s.conf.TrustedTokenSigners) > 0 && len(s.conf.Issuers) > 0 { 157 errors = append(errors, "can only configure one of trusted token signers or issuers") 158 } 159 } 160 } 161 162 return errors, len(errors) == 0 163 } 164 165 func (s *ChoriaSecurity) Identity() string { 166 // TODO: should load the token and figure it out from there 167 // ultimately in this case of identity probably just is a hint 168 // only as really this is used to find certs by name in other 169 // providers, so maybe this is fine 170 return s.conf.Identity 171 } 172 173 func (s *ChoriaSecurity) CallerName() string { 174 // TODO: since this calls identity the same concerns above apply 175 return fmt.Sprintf(callerFormat, s.Identity()) 176 } 177 178 func (s *ChoriaSecurity) CallerIdentity(caller string) (string, error) { 179 match := callerIDRe.FindStringSubmatch(caller) 180 181 if match == nil { 182 return "", fmt.Errorf("could not find a valid caller identity name in %s", caller) 183 } 184 185 return match[1], nil 186 } 187 188 func (s *ChoriaSecurity) SignBytes(b []byte) (signature []byte, err error) { 189 return iu.Ed25519SignWithSeedFile(s.conf.SeedFile, b) 190 } 191 192 func (s *ChoriaSecurity) VerifySignatureBytes(dat []byte, sig []byte, public ...[]byte) (should bool, signer string) { 193 switch len(public) { 194 case 0: 195 s.log.Warnf("Received a signature verification request with no public parts") 196 return false, "" 197 case 1: 198 // signature was made by the caller - first in the list of tokens - so it may not be one that requires delegated signatures 199 return s.verifyByteSignatureByCaller(dat, sig, public[0]) 200 case 2: 201 // signature was made by a delegation - it's the 2nd signature received. We try load it using all the trusted issuers 202 // and, we make sure when it loads that it has delegator permission 203 return s.verifyByteSignatureByDelegation(dat, sig, public[0], public[1]) 204 default: 205 s.log.Warnf("Received a signature verification request with %d public parts", len(public)) 206 return false, "" 207 } 208 } 209 210 func (s *ChoriaSecurity) orgPkForToken(token []byte) (pk ed25519.PublicKey, ou string, err error) { 211 uclaims, err := tokens.ParseTokenUnverified(string(token)) 212 if err != nil { 213 return nil, "", err 214 } 215 216 our, ok := uclaims["ou"] 217 if !ok { 218 return nil, "", fmt.Errorf("no ou found in client token") 219 } 220 221 ous, ok := our.(string) 222 if !ok { 223 return nil, "", fmt.Errorf("empty ou found in client token") 224 } 225 226 issuer, ok := s.conf.Issuers[ous] 227 if !ok { 228 s.log.Warnf("No issuer found for %s ou", ous) 229 return nil, "", fmt.Errorf("no issuer found for %s ou", ous) 230 } 231 232 return issuer, ous, nil 233 } 234 235 func (s *ChoriaSecurity) verifyByteSignatureByDelegation(dat []byte, sig []byte, caller []byte, delegate []byte) (bool, string) { 236 if len(delegate) == 0 { 237 s.log.Warnf("Received an invalid token for signature verification") 238 return false, "" 239 } 240 241 purpose := tokens.TokenPurpose(string(delegate)) 242 // delegate signers must be clients 243 if purpose != tokens.ClientIDPurpose { 244 s.log.Warnf("Cannot verify byte signatures using a %s token", purpose) 245 return false, "" 246 } 247 248 var pk ed25519.PublicKey 249 var pks string 250 var name string 251 var err error 252 253 checkDelegate := func(signer ed25519.PublicKey, delegate []byte) (pks string, name string, error error) { 254 st, err := tokens.ParseClientIDToken(string(delegate), signer, true) 255 if err != nil { 256 return "", "", fmt.Errorf("could not parse client token: %w", err) 257 } 258 259 // it successfully parsed but now must be a delegator else it's not allowed to sign this data 260 if st.Permissions == nil || !st.Permissions.AuthenticationDelegator { 261 return "", "", fmt.Errorf("%w: token attempted to sign a request as delegator without required delegator permission: %s", errPermissionDenied, hex.EncodeToString(signer)) 262 } 263 264 // this ensures/assumes the caller is always signed by the same signer as the delegator 265 ct, err := tokens.ParseClientIDToken(string(caller), signer, true) 266 if err != nil { 267 return "", "", fmt.Errorf("%w: could not load caller token using the same signer as the delegator: %v", errPermissionDenied, err) 268 } 269 270 if ct.Permissions == nil || !(ct.Permissions.FleetManagement || ct.Permissions.SignedFleetManagement) { 271 return "", "", fmt.Errorf("%w: caller token cannot be used without fleet management access: %s: %v", errPermissionDenied, string(caller), err) 272 } 273 274 if st.PublicKey == "" { 275 return "", "", fmt.Errorf("%w: no public key set", errPermissionDenied) 276 } 277 278 return st.PublicKey, st.CallerID, nil 279 } 280 281 if len(s.conf.Issuers) > 0 { 282 issuer, _, err := s.orgPkForToken(delegate) 283 if err != nil { 284 s.log.Warnf("Could not get ou from delegate token: %v", err) 285 return false, "" 286 } 287 288 pks, name, err = checkDelegate(issuer, delegate) 289 if err != nil { 290 s.log.Warn(err) 291 return false, "" 292 } 293 } else { 294 for _, signer := range s.conf.TrustedTokenSigners { 295 s.log.Warnf("Checking using signer %x", signer) 296 pks, name, err = checkDelegate(signer, delegate) 297 if errors.Is(err, errPermissionDenied) { 298 s.log.Warnf(err.Error()) 299 return false, "" 300 } else if err != nil { 301 s.log.Warnf(err.Error()) 302 continue 303 } else if err == nil { 304 break 305 } 306 } 307 } 308 309 if pks == "" { 310 s.log.Warnf("Signer token %s could not be loaded using %d authorized issuers", string(delegate), len(s.conf.TrustedTokenSigners)) 311 return false, "" 312 } 313 314 pk, err = hex.DecodeString(pks) 315 if err != nil { 316 s.log.Warnf("Could not extract public key from token") 317 return false, "" 318 } 319 320 ok, err := iu.Ed25519Verify(pk, dat, sig) 321 if err != nil { 322 s.log.Warnf("Could not verify signature: %v", err) 323 return false, "" 324 } 325 326 return ok, name 327 } 328 329 func (s *ChoriaSecurity) verifyByteSignatureByCaller(dat []byte, sig []byte, token []byte) (bool, string) { 330 if len(token) == 0 { 331 s.log.Warnf("Received an invalid token for signature verification") 332 return false, "" 333 } 334 335 purpose := tokens.TokenPurpose(string(token)) 336 if purpose != tokens.ServerPurpose && purpose != tokens.ClientIDPurpose { 337 s.log.Warnf("Cannot verify byte signatures using a %s token", purpose) 338 return false, "" 339 } 340 341 checkClient := func(token []byte, signer ed25519.PublicKey) (pks string, name string, err error) { 342 t, err := tokens.ParseClientIDToken(string(token), signer, true) 343 if err != nil { 344 return "", "", err 345 } 346 347 // it successfully parsed but now must not require delegation 348 if t.Permissions != nil && t.Permissions.SignedFleetManagement { 349 return "", "", fmt.Errorf("%w: requires authority delegation", errPermissionDenied) 350 } 351 352 if t.Permissions == nil || !t.Permissions.FleetManagement { 353 return "", "", fmt.Errorf("%w: does not have fleet management access", errPermissionDenied) 354 } 355 356 if t.PublicKey == "" { 357 return "", "", fmt.Errorf("%w: no public key in token", errPermissionDenied) 358 } 359 360 return t.PublicKey, t.CallerID, nil 361 } 362 363 var pk ed25519.PublicKey 364 var pks string 365 var name string 366 var err error 367 368 if len(s.conf.Issuers) > 0 { 369 issuer, ou, err := s.orgPkForToken(token) 370 if err != nil { 371 s.log.Warnf("Could not get ou from token: %v", err) 372 return false, "" 373 } 374 375 if purpose == tokens.ServerPurpose { 376 t, err := tokens.ParseServerToken(string(token), issuer) 377 if err != nil { 378 s.log.Warnf("Could not parse server token using issuer '%s': %v", ou, err) 379 return false, "" 380 } 381 382 if t.PublicKey == "" { 383 s.log.Warnf("Server token has no public key") 384 return false, "" 385 } 386 387 pks = t.PublicKey 388 name = t.ChoriaIdentity 389 } else { 390 pks, name, err = checkClient(token, issuer) 391 if err != nil { 392 s.log.Warnf("Could not verify signature by caller using issuer '%s': %v", ou, err) 393 return false, "" 394 } 395 } 396 } else { 397 for _, signer := range s.conf.TrustedTokenSigners { 398 if purpose == tokens.ServerPurpose { 399 t, err := tokens.ParseServerToken(string(token), signer) 400 if err != nil { 401 continue 402 } 403 404 if t.PublicKey != "" { 405 pks = t.PublicKey 406 name = t.ChoriaIdentity 407 break 408 } 409 } else { 410 pks, name, err = checkClient(token, signer) 411 if errors.Is(err, errPermissionDenied) { 412 s.log.Warnf("Could not verify signature by caller: %v", err) 413 return false, "" 414 } else if err != nil { 415 s.log.Warnf("Could not verify signature by caller: %v", err) 416 continue 417 } else if err == nil { 418 break 419 } 420 } 421 } 422 } 423 424 if pks == "" { 425 s.log.Warnf("Signer token %s could not be loaded using %d authorized issuers", string(token), len(s.conf.TrustedTokenSigners)) 426 return false, "" 427 } 428 429 pk, err = hex.DecodeString(pks) 430 if err != nil { 431 s.log.Warnf("Could not extract public key from token") 432 return false, "" 433 } 434 435 ok, err := iu.Ed25519Verify(pk, dat, sig) 436 if err != nil { 437 s.log.Warnf("Could not verify signature: %v", err) 438 return false, "" 439 } 440 441 return ok, name 442 } 443 444 func (s *ChoriaSecurity) RemoteSignRequest(ctx context.Context, request []byte) (signed []byte, err error) { 445 if s.conf.RemoteSigner == nil { 446 return nil, fmt.Errorf("remote signing not configured") 447 } 448 449 s.log.Infof("Signing request using %s", s.conf.RemoteSigner.Kind()) 450 return s.conf.RemoteSigner.Sign(ctx, request, s) 451 } 452 453 func (s *ChoriaSecurity) RemoteSignerSeedFile() (string, error) { 454 return s.conf.SeedFile, nil 455 } 456 457 func (s *ChoriaSecurity) RemoteSignerToken() ([]byte, error) { 458 if s.conf.TokenFile == "" { 459 return nil, fmt.Errorf("no token file defined") 460 } 461 462 tb, err := os.ReadFile(s.conf.TokenFile) 463 if err != nil { 464 return bytes.TrimSpace(tb), fmt.Errorf("could not read token file: %v", err) 465 } 466 467 return tb, err 468 } 469 470 func (s *ChoriaSecurity) RemoteSignerURL() (*url.URL, error) { 471 if s.conf.RemoteSignerURL == "" { 472 return nil, fmt.Errorf("no remote url configured") 473 } 474 475 return url.Parse(s.conf.RemoteSignerURL) 476 } 477 478 func (s *ChoriaSecurity) IsRemoteSigning() bool { 479 return s.conf.RemoteSigner != nil 480 } 481 482 func (s *ChoriaSecurity) ChecksumBytes(data []byte) []byte { 483 sum := sha256.Sum256(data) 484 485 return sum[:] 486 } 487 488 func (s *ChoriaSecurity) TLSConfig() (*tls.Config, error) { 489 tlsc := &tls.Config{ 490 MinVersion: tls.VersionTLS12, 491 CipherSuites: s.conf.TLSConfig.CipherSuites, 492 CurvePreferences: s.conf.TLSConfig.CurvePreferences, 493 } 494 495 if iu.FileExist(s.conf.Key) && iu.FileExist(s.conf.Certificate) { 496 cert, err := tls.LoadX509KeyPair(s.conf.Certificate, s.conf.Key) 497 if err != nil { 498 err = fmt.Errorf("could not load certificate %s and key %s: %s", s.conf.Certificate, s.conf.Key, err) 499 return nil, err 500 } 501 502 cert.Leaf, err = x509.ParseCertificate(cert.Certificate[0]) 503 if err != nil { 504 err = fmt.Errorf("error parsing certificate: %v", err) 505 return nil, err 506 } 507 508 tlsc.Certificates = []tls.Certificate{cert} 509 } 510 511 if iu.FileExist(s.conf.CA) { 512 caCert, err := os.ReadFile(s.conf.CA) 513 if err != nil { 514 return nil, err 515 } 516 517 caCertPool := x509.NewCertPool() 518 caCertPool.AppendCertsFromPEM(caCert) 519 520 tlsc.ClientCAs = caCertPool 521 tlsc.RootCAs = caCertPool 522 } else { 523 // in this security system we are specifically building a system 524 // where mTLS is optional, so when we do not have a CA we disable 525 // mutual verification 526 tlsc.InsecureSkipVerify = true 527 } 528 529 if s.conf.DisableTLSVerify { 530 tlsc.InsecureSkipVerify = true 531 } 532 533 return tlsc, nil 534 } 535 536 func (s *ChoriaSecurity) ClientTLSConfig() (*tls.Config, error) { 537 return s.TLSConfig() 538 } 539 540 func (s *ChoriaSecurity) SSLContext() (*http.Transport, error) { 541 tlsConfig, err := s.ClientTLSConfig() 542 if err != nil { 543 return nil, err 544 } 545 546 transport := &http.Transport{TLSClientConfig: tlsConfig} 547 548 return transport, nil 549 } 550 551 func (s *ChoriaSecurity) HTTPClient(secure bool) (*http.Client, error) { 552 client := &http.Client{} 553 554 if secure { 555 tlsc, err := s.TLSConfig() 556 if err != nil { 557 return nil, fmt.Errorf("could not set up HTTP connection: %s", err) 558 } 559 560 client.Transport = &http.Transport{TLSClientConfig: tlsc} 561 } 562 563 return client, nil 564 } 565 566 func (s *ChoriaSecurity) PublicCert() (*x509.Certificate, error) { 567 if s.conf.Key == "" || s.conf.Certificate == "" { 568 return nil, fmt.Errorf("no certificates configured") 569 } 570 571 cert, err := tls.LoadX509KeyPair(s.conf.Certificate, s.conf.Key) 572 if err != nil { 573 err = fmt.Errorf("could not load certificate %s and key %s: %s", s.conf.Certificate, s.conf.Key, err) 574 return nil, err 575 } 576 577 cert.Leaf, err = x509.ParseCertificate(cert.Certificate[0]) 578 if err != nil { 579 err = fmt.Errorf("error parsing certificate: %v", err) 580 return nil, err 581 } 582 583 return cert.Leaf, nil 584 } 585 586 func (s *ChoriaSecurity) PublicCertBytes() ([]byte, error) { 587 if s.conf.Key == "" || s.conf.Certificate == "" { 588 return nil, fmt.Errorf("no certificates configured") 589 } 590 591 return os.ReadFile(s.conf.Certificate) 592 } 593 594 func (s *ChoriaSecurity) ShouldAllowCaller(name string, callers ...[]byte) (privileged bool, err error) { 595 switch len(callers) { 596 case 1: 597 return s.shouldAllowCallerUnsigned(name, callers[0]) 598 case 2: 599 return s.shouldAllowSignedCaller(name, callers...) 600 default: 601 return false, fmt.Errorf("invalid caller data provided") 602 } 603 } 604 605 func (s *ChoriaSecurity) shouldAllowSignedCaller(name string, callers ...[]byte) (privileged bool, err error) { 606 if len(callers) != 2 { 607 return false, fmt.Errorf("invalid caller data") 608 } 609 610 signerT, err := tokens.ParseClientIDTokenUnverified(string(callers[1])) 611 if err != nil { 612 return false, fmt.Errorf("invalid signer token: %v", err) 613 } 614 615 if signerT.Permissions == nil || !signerT.Permissions.AuthenticationDelegator { 616 return false, fmt.Errorf("signer token does not have delegator permission") 617 } 618 619 callerT, err := tokens.ParseClientIDTokenUnverified(string(callers[0])) 620 if err != nil { 621 return false, fmt.Errorf("invalid caller token: %v", err) 622 } 623 624 if callerT.Permissions == nil || !(callerT.Permissions.SignedFleetManagement || callerT.Permissions.FleetManagement) { 625 return false, fmt.Errorf("caller does not have fleet management access") 626 } 627 628 // we do not check the name, delegators can override, but we log the delegation 629 s.log.Infof("Allowing delegator %s to authorize caller %s who holds token %s", signerT.CallerID, name, callerT.CallerID) 630 631 return true, nil 632 } 633 634 func (s *ChoriaSecurity) shouldAllowCallerUnsigned(name string, caller []byte) (privileged bool, err error) { 635 // will fail for non client tokens 636 // we do not verify since was all verified already in sig check 637 // TODO: we should think about servers making requests out to choria services or publishing registration data (1740) 638 token, err := tokens.ParseClientIDTokenUnverified(string(caller)) 639 if err != nil { 640 return false, err 641 } 642 643 // technically already done in sig verify but cant harm 644 if token.Permissions == nil || !(token.Permissions.SignedFleetManagement || token.Permissions.FleetManagement) { 645 if token.Permissions.SignedFleetManagement { 646 return false, fmt.Errorf("requires signed fleet management access") 647 } 648 return false, fmt.Errorf("does not have fleet management access") 649 } 650 651 if token.CallerID != name { 652 return false, fmt.Errorf("caller name does not match token") 653 } 654 655 return false, nil 656 } 657 658 func (s *ChoriaSecurity) Enroll(ctx context.Context, wait time.Duration, cb func(digest string, try int)) error { 659 return errors.New("the choria security provider does not support enrollment") 660 } 661 662 func (s *ChoriaSecurity) ShouldSignReplies() bool { return s.conf.SignedReplies }