github.com/choria-io/go-choria@v0.28.1-0.20240416190746-b3bf9c7d5a45/providers/security/choria/option.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  	"encoding/hex"
    10  	"fmt"
    11  	"os"
    12  	"path/filepath"
    13  	"runtime"
    14  
    15  	"github.com/choria-io/go-choria/config"
    16  	"github.com/choria-io/go-choria/inter"
    17  	"github.com/choria-io/go-choria/tlssetup"
    18  	"github.com/sirupsen/logrus"
    19  )
    20  
    21  // Option is a function that can configure the Security Provider
    22  type Option func(*ChoriaSecurity) error
    23  
    24  // BuildInfoProvider provides info about the build
    25  type BuildInfoProvider interface {
    26  	ClientIdentitySuffix() string
    27  }
    28  
    29  // WithChoriaConfig optionally configures the Security Provider from settings found in a typical Choria configuration
    30  func WithChoriaConfig(c *config.Config) Option {
    31  	return func(s *ChoriaSecurity) error {
    32  		if c.Choria.ServerTokenFile != "" {
    33  			return fmt.Errorf("plugin.choria.security.server.token_file can not be set when the choria security provider is used")
    34  		}
    35  
    36  		if c.Choria.ServerTokenSeedFile != "" {
    37  			return fmt.Errorf("plugin.choria.security.server.seed_file can not be set when the choria security provider is used")
    38  		}
    39  
    40  		if c.Choria.ServerAnonTLS {
    41  			return fmt.Errorf("plugin.security.server_anon_tls can not be set when the choria security provider is used")
    42  		}
    43  
    44  		if c.Choria.ClientAnonTLS {
    45  			return fmt.Errorf("plugin.security.client_anon_tls can not be set when the choria security provider is used")
    46  		}
    47  
    48  		if c.Choria.RemoteSignerTokenSeedFile != "" {
    49  			return fmt.Errorf("plugin.choria.security.request_signer.seed_file can not be used when the choria security provider is used")
    50  		}
    51  
    52  		if c.Choria.RemoteSignerTokenFile != "" {
    53  			return fmt.Errorf("plugin.choria.security.request_signer.token_file can not be used when the choria security provider is used")
    54  		}
    55  
    56  		cfg := Config{
    57  			TLSConfig:         tlssetup.TLSConfig(c),
    58  			RemoteSignerURL:   c.Choria.RemoteSignerURL,
    59  			SeedFile:          filepath.FromSlash(c.Choria.ChoriaSecuritySeedFile),
    60  			TokenFile:         filepath.FromSlash(c.Choria.ChoriaSecurityTokenFile),
    61  			CA:                filepath.FromSlash(c.Choria.ChoriaSecurityCA),
    62  			Certificate:       filepath.FromSlash(c.Choria.ChoriaSecurityCertificate),
    63  			Key:               filepath.FromSlash(c.Choria.ChoriaSecurityKey),
    64  			DisableTLSVerify:  c.DisableTLSVerify,
    65  			InitiatedByServer: c.InitiatedByServer,
    66  			SignedReplies:     c.Choria.ChoriaSecuritySignReplies,
    67  			Issuers:           make(map[string]ed25519.PublicKey),
    68  		}
    69  
    70  		for _, signer := range c.Choria.ChoriaSecurityTrustedSigners {
    71  			pk, err := hex.DecodeString(signer)
    72  			if err != nil {
    73  				return fmt.Errorf("invalid ed25519 public key: %v: %v", signer, err)
    74  			}
    75  			if len(pk) != ed25519.PublicKeySize {
    76  				return fmt.Errorf("invalid ed25519 public key size: %v: %v", signer, len(pk))
    77  			}
    78  
    79  			cfg.TrustedTokenSigners = append(cfg.TrustedTokenSigners, pk)
    80  		}
    81  
    82  		for _, issuer := range c.Choria.IssuerNames {
    83  			name := fmt.Sprintf("plugin.security.issuer.%s.public", issuer)
    84  			pks := c.Option(name, "")
    85  			if pks == "" {
    86  				return fmt.Errorf("could not find option %s while adding issuer %s", name, issuer)
    87  			}
    88  
    89  			pk, err := hex.DecodeString(pks)
    90  			if err != nil {
    91  				return fmt.Errorf("invalid ed25519 public key in %s: %v", name, err)
    92  			}
    93  
    94  			cfg.Issuers[issuer] = pk
    95  		}
    96  
    97  		if c.InitiatedByServer {
    98  			cfg.Identity = c.Identity
    99  		} else {
   100  			userEnvVar := "USER"
   101  			if runtime.GOOS == "windows" {
   102  				userEnvVar = "USERNAME"
   103  			}
   104  
   105  			u, ok := os.LookupEnv(userEnvVar)
   106  			if !ok {
   107  				return fmt.Errorf("could not determine client identity, ensure %s environment variable is set", userEnvVar)
   108  			}
   109  
   110  			cfg.Identity = u
   111  		}
   112  
   113  		s.conf = &cfg
   114  
   115  		return nil
   116  	}
   117  }
   118  
   119  // WithTokenFile sets the path to the JWT token stored in a file
   120  func WithTokenFile(f string) Option {
   121  	return func(s *ChoriaSecurity) error {
   122  		s.conf.TokenFile = f
   123  		return nil
   124  	}
   125  }
   126  
   127  // WithSeedFile sets the path to the ed25519 seed stored in a file
   128  func WithSeedFile(f string) Option {
   129  	return func(s *ChoriaSecurity) error {
   130  		s.conf.SeedFile = f
   131  		return nil
   132  	}
   133  }
   134  
   135  // WithSigner configures a remote request signer
   136  func WithSigner(signer inter.RequestSigner) Option {
   137  	return func(s *ChoriaSecurity) error {
   138  		s.conf.RemoteSigner = signer
   139  
   140  		return nil
   141  	}
   142  }
   143  
   144  // WithConfig optionally configures the Security Provider using its native configuration format
   145  func WithConfig(c *Config) Option {
   146  	return func(s *ChoriaSecurity) error {
   147  		s.conf = c
   148  
   149  		if s.conf.TLSConfig == nil {
   150  			s.conf.TLSConfig = tlssetup.TLSConfig(nil)
   151  		}
   152  
   153  		return nil
   154  	}
   155  }
   156  
   157  // WithLog configures a logger for the Security Provider
   158  func WithLog(l *logrus.Entry) Option {
   159  	return func(s *ChoriaSecurity) error {
   160  		s.log = l.WithFields(logrus.Fields{"security": "choria"})
   161  
   162  		if s.conf.TLSConfig == nil {
   163  			s.conf.TLSConfig = tlssetup.TLSConfig(nil)
   164  		}
   165  
   166  		return nil
   167  	}
   168  }