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

     1  // Copyright (c) 2020-2022, R.I. Pienaar and the Choria Project contributors
     2  //
     3  // SPDX-License-Identifier: Apache-2.0
     4  
     5  package filesec
     6  
     7  import (
     8  	"crypto/tls"
     9  	"encoding/base64"
    10  	"encoding/pem"
    11  	"fmt"
    12  	"io"
    13  	"os"
    14  	"path/filepath"
    15  	"testing"
    16  
    17  	"github.com/choria-io/go-choria/build"
    18  	"github.com/choria-io/go-choria/inter"
    19  	"github.com/choria-io/go-choria/tlssetup"
    20  
    21  	"github.com/choria-io/go-choria/config"
    22  	. "github.com/onsi/ginkgo/v2"
    23  	. "github.com/onsi/gomega"
    24  	"github.com/sirupsen/logrus"
    25  )
    26  
    27  func TestFileSecurity(t *testing.T) {
    28  	RegisterFailHandler(Fail)
    29  	RunSpecs(t, "Providers/Security/File")
    30  }
    31  
    32  func setTLS(c *Config, parent string, id string, privateExtension string) {
    33  	if privateExtension == "" {
    34  		privateExtension = "pem"
    35  	}
    36  	c.Certificate = filepath.Join(parent, "certs", fmt.Sprintf("%s.pem", id))
    37  	c.CA = filepath.Join(parent, "certs", "ca.pem")
    38  	c.Key = filepath.Join(parent, "private_keys", fmt.Sprintf("%s.%s", id, privateExtension))
    39  	c.AllowList = []string{"\\.mcollective$"}
    40  	c.PrivilegedUsers = []string{"\\.privileged.mcollective$"}
    41  	c.DisableTLSVerify = false
    42  	c.Identity = id
    43  
    44  	useFakeUID = true
    45  	fakeUID = 500
    46  }
    47  
    48  var _ = Describe("FileSecurity", func() {
    49  	var cfg *Config
    50  	var err error
    51  	var prov *FileSecurity
    52  	var l *logrus.Logger
    53  
    54  	var goodStub string
    55  	var nonexistingStub string
    56  
    57  	BeforeEach(func() {
    58  		os.Setenv("MCOLLECTIVE_CERTNAME", "rip.mcollective")
    59  
    60  		goodStub = filepath.Join("..", "testdata", "good")
    61  		nonexistingStub = filepath.Join("..", "testdata", "nonexisting")
    62  
    63  		cfg = &Config{}
    64  		setTLS(cfg, goodStub, "rip.mcollective", "")
    65  
    66  		l = logrus.New()
    67  
    68  		l.Out = io.Discard
    69  
    70  		prov, err = New(WithConfig(cfg), WithLog(l.WithFields(logrus.Fields{})))
    71  		Expect(err).ToNot(HaveOccurred())
    72  	})
    73  
    74  	It("Should implement the provider interface", func() {
    75  		f := func(p inter.SecurityProvider) {}
    76  		f(prov)
    77  		Expect(prov.Provider()).To(Equal("file"))
    78  	})
    79  
    80  	Describe("WithChoriaConfig", func() {
    81  		BeforeEach(func() {
    82  			os.Unsetenv("MCOLLECTIVE_CERTNAME")
    83  		})
    84  
    85  		It("Should support OverrideCertname", func() {
    86  			c, err := config.NewDefaultConfig()
    87  			Expect(err).ToNot(HaveOccurred())
    88  			c.OverrideCertname = "override.choria"
    89  			prov, err := New(WithChoriaConfig(&build.Info{}, c), WithLog(l.WithFields(logrus.Fields{})))
    90  			Expect(err).ToNot(HaveOccurred())
    91  
    92  			Expect(prov.conf.Identity).To(Equal("override.choria"))
    93  		})
    94  
    95  		It("Should support MCOLLECTIVE_CERTNAME", func() {
    96  			os.Setenv("MCOLLECTIVE_CERTNAME", "bob.mcollective")
    97  			c, err := config.NewDefaultConfig()
    98  			Expect(err).ToNot(HaveOccurred())
    99  			prov, err := New(WithChoriaConfig(&build.Info{}, c), WithLog(l.WithFields(logrus.Fields{})))
   100  			Expect(err).ToNot(HaveOccurred())
   101  
   102  			Expect(prov.conf.Identity).To(Equal("bob.mcollective"))
   103  		})
   104  
   105  		It("Should copy all the relevant settings", func() {
   106  			c, err := config.NewDefaultConfig()
   107  			Expect(err).ToNot(HaveOccurred())
   108  
   109  			fakeUID = 0
   110  			c.Choria.FileSecurityCA = "stub/ca.pem"
   111  			c.Choria.FileSecurityCertificate = "stub/cert.pem"
   112  			c.Choria.FileSecurityKey = "stub/key.pem"
   113  			c.DisableTLSVerify = true
   114  			c.Identity = "test.identity"
   115  
   116  			prov, err := New(WithChoriaConfig(&build.Info{}, c), WithLog(l.WithFields(logrus.Fields{})))
   117  			Expect(err).ToNot(HaveOccurred())
   118  
   119  			Expect(prov.conf.AllowList).To(Equal([]string{"\\.mcollective$", "\\.choria$"}))
   120  			Expect(prov.conf.PrivilegedUsers).To(Equal([]string{"\\.privileged.mcollective$", "\\.privileged.choria$"}))
   121  			Expect(prov.conf.CA).To(Equal("stub/ca.pem"))
   122  			Expect(prov.conf.Certificate).To(Equal("stub/cert.pem"))
   123  			Expect(prov.conf.Key).To(Equal("stub/key.pem"))
   124  			Expect(prov.conf.DisableTLSVerify).To(BeTrue())
   125  			Expect(prov.conf.Identity).To(Equal("test.identity"))
   126  		})
   127  
   128  		It("Should support override certname", func() {
   129  			c, err := config.NewDefaultConfig()
   130  			Expect(err).ToNot(HaveOccurred())
   131  
   132  			c.Choria.FileSecurityCA = "stub/ca.pem"
   133  			c.Choria.FileSecurityCertificate = "stub/cert.pem"
   134  			c.Choria.FileSecurityKey = "stub/key.pem"
   135  			c.DisableTLSVerify = true
   136  			c.Identity = "test.identity"
   137  			c.OverrideCertname = "bob.identity"
   138  
   139  			prov, err := New(WithChoriaConfig(&build.Info{}, c), WithLog(l.WithFields(logrus.Fields{})))
   140  			Expect(err).ToNot(HaveOccurred())
   141  
   142  			Expect(prov.conf.Identity).To(Equal("bob.identity"))
   143  		})
   144  
   145  		It("Should support root and windows", func() {
   146  			c, err := config.NewDefaultConfig()
   147  			Expect(err).ToNot(HaveOccurred())
   148  
   149  			c.Choria.FileSecurityCA = "stub/ca.pem"
   150  			c.Choria.FileSecurityCertificate = "stub/cert.pem"
   151  			c.Choria.FileSecurityKey = "stub/key.pem"
   152  			c.DisableTLSVerify = true
   153  			c.Identity = "test.identity"
   154  
   155  			useFakeOS = true
   156  			defer func() { useFakeOS = false }()
   157  			fakeOS = "windows"
   158  			Expect(runtimeOs()).To(Equal("windows"))
   159  
   160  			prov, err = New(WithChoriaConfig(&build.Info{}, c), WithLog(l.WithFields(logrus.Fields{})))
   161  			Expect(err).ToNot(HaveOccurred())
   162  
   163  			Expect(prov.conf.Identity).To(Equal("test.identity"))
   164  		})
   165  	})
   166  
   167  	Describe("Validate", func() {
   168  		It("Should handle missing files", func() {
   169  			setTLS(cfg, nonexistingStub, "test.mcollective", "")
   170  			prov, err = New(WithConfig(cfg), WithLog(l.WithFields(logrus.Fields{})))
   171  			Expect(err).ToNot(HaveOccurred())
   172  
   173  			errs, ok := prov.Validate()
   174  
   175  			Expect(ok).To(BeFalse())
   176  			Expect(errs).To(HaveLen(3))
   177  			Expect(errs[0]).To(Equal(fmt.Sprintf("public certificate %s does not exist", cfg.Certificate)))
   178  			Expect(errs[1]).To(Equal(fmt.Sprintf("private key %s does not exist", cfg.Key)))
   179  			Expect(errs[2]).To(Equal(fmt.Sprintf("CA %s does not exist", cfg.CA)))
   180  		})
   181  
   182  		It("Should accept valid directories", func() {
   183  			setTLS(cfg, goodStub, "rip.mcollective", "")
   184  
   185  			errs, ok := prov.Validate()
   186  			Expect(errs).To(BeEmpty())
   187  			Expect(ok).To(BeTrue())
   188  		})
   189  	})
   190  
   191  	Describe("Identity", func() {
   192  		It("Should return the identity", func() {
   193  			cfg.Identity = "bob.choria"
   194  			Expect(prov.Identity()).To(Equal("bob.choria"))
   195  		})
   196  	})
   197  
   198  	Describe("CallerName", func() {
   199  		It("Should return the right caller name", func() {
   200  			cfg.Identity = "test.choria"
   201  			Expect(prov.CallerName()).To(Equal("choria=test.choria"))
   202  		})
   203  	})
   204  
   205  	Describe("CallerIdentity", func() {
   206  		It("Should return the right caller ident", func() {
   207  			Expect(prov.CallerIdentity("choria=test.choria")).To(Equal("test.choria"))
   208  			Expect(prov.CallerIdentity("foo=test1.choria")).To(Equal("test1.choria"))
   209  		})
   210  
   211  		It("Should handle invalid caller ident", func() {
   212  			_, err := prov.CallerIdentity("test.choria")
   213  			Expect(err).To(MatchError("could not find a valid caller identity name in test.choria"))
   214  
   215  			_, err = prov.CallerIdentity("fooBar=test.choria")
   216  			Expect(err).To(MatchError("could not find a valid caller identity name in fooBar=test.choria"))
   217  		})
   218  	})
   219  
   220  	Describe("SignBytes", func() {
   221  		It("Should produce the right signature", func() {
   222  			sig, err := prov.SignBytes([]byte("too many secrets"))
   223  			Expect(err).ToNot(HaveOccurred())
   224  			Expect(base64.StdEncoding.EncodeToString(sig)).To(Equal("kh5PlHXcht+FeyPdlNdpYjsW4AtOp9lRo6z3NWMcjxZq15mknzXOjkbYT1J4pp627tnlzbSC0dohP7YffGfNv5zJotx8QaIrVm2akSpWjf+M2xBf5V72f3Prn/f4dzZTP6EClM8L6SWxjQHiDamMGyT+6ZCja7Ld9TmgZV5Mx9t66pDu0OgZdi6k45/SRpLdNISnhGWpRQ5KIXgaf9gNqABNPtTstPS9i9PYNYQP6sucZPAzRa9zeyZXlKkxuBLqk4cdMUD9LgtGTy7BaAV/ZG1fzGyybw1swDAMp6x06428R+TCOaystOEbaSTqR1D1/qTDu0xUpA/izN0ZSW1g5f8K2xxv5NHoFbUyWCPGRozbrBc83uJMxhOgkeS6A2ABw1uP2vzm1zdsrj+jTj8BMHHKFn+KEkitXeImEWvWg8JvMeD8arqt0GsDBqgqGjXrlHog4y0cZvv+Yuhya2CJl77BNl08urIl0qonbCiNElB8mMvWcMoyTWo7ksWD27Ao/+oOjN+/Kek132g1PV3AK8gAnJ2RPZy/bT5qZMre0vg4PdVgL6UI3afqLOQs8AvL3KG+RFg1Lw3lG/Obmitoa2+0VJrwEN+WO6D/huGn6B7v3yzuu5UrUwkZhd2/yUbnET7OdpfalqcbTbdq/teeo7TUFNp/OrNLhVb8o63mpQQ="))
   225  		})
   226  
   227  		It("Should work with PKCS8 files", func() {
   228  			setTLS(cfg, goodStub, "rip.mcollective", "p8")
   229  			sig, err := prov.SignBytes([]byte("too many secrets"))
   230  			Expect(err).ToNot(HaveOccurred())
   231  			Expect(base64.StdEncoding.EncodeToString(sig)).To(Equal("kh5PlHXcht+FeyPdlNdpYjsW4AtOp9lRo6z3NWMcjxZq15mknzXOjkbYT1J4pp627tnlzbSC0dohP7YffGfNv5zJotx8QaIrVm2akSpWjf+M2xBf5V72f3Prn/f4dzZTP6EClM8L6SWxjQHiDamMGyT+6ZCja7Ld9TmgZV5Mx9t66pDu0OgZdi6k45/SRpLdNISnhGWpRQ5KIXgaf9gNqABNPtTstPS9i9PYNYQP6sucZPAzRa9zeyZXlKkxuBLqk4cdMUD9LgtGTy7BaAV/ZG1fzGyybw1swDAMp6x06428R+TCOaystOEbaSTqR1D1/qTDu0xUpA/izN0ZSW1g5f8K2xxv5NHoFbUyWCPGRozbrBc83uJMxhOgkeS6A2ABw1uP2vzm1zdsrj+jTj8BMHHKFn+KEkitXeImEWvWg8JvMeD8arqt0GsDBqgqGjXrlHog4y0cZvv+Yuhya2CJl77BNl08urIl0qonbCiNElB8mMvWcMoyTWo7ksWD27Ao/+oOjN+/Kek132g1PV3AK8gAnJ2RPZy/bT5qZMre0vg4PdVgL6UI3afqLOQs8AvL3KG+RFg1Lw3lG/Obmitoa2+0VJrwEN+WO6D/huGn6B7v3yzuu5UrUwkZhd2/yUbnET7OdpfalqcbTbdq/teeo7TUFNp/OrNLhVb8o63mpQQ="))
   232  		})
   233  	})
   234  
   235  	Describe("VerifySignatureBytes", func() {
   236  		It("Should validate correctly", func() {
   237  			sig, err := base64.StdEncoding.DecodeString("kh5PlHXcht+FeyPdlNdpYjsW4AtOp9lRo6z3NWMcjxZq15mknzXOjkbYT1J4pp627tnlzbSC0dohP7YffGfNv5zJotx8QaIrVm2akSpWjf+M2xBf5V72f3Prn/f4dzZTP6EClM8L6SWxjQHiDamMGyT+6ZCja7Ld9TmgZV5Mx9t66pDu0OgZdi6k45/SRpLdNISnhGWpRQ5KIXgaf9gNqABNPtTstPS9i9PYNYQP6sucZPAzRa9zeyZXlKkxuBLqk4cdMUD9LgtGTy7BaAV/ZG1fzGyybw1swDAMp6x06428R+TCOaystOEbaSTqR1D1/qTDu0xUpA/izN0ZSW1g5f8K2xxv5NHoFbUyWCPGRozbrBc83uJMxhOgkeS6A2ABw1uP2vzm1zdsrj+jTj8BMHHKFn+KEkitXeImEWvWg8JvMeD8arqt0GsDBqgqGjXrlHog4y0cZvv+Yuhya2CJl77BNl08urIl0qonbCiNElB8mMvWcMoyTWo7ksWD27Ao/+oOjN+/Kek132g1PV3AK8gAnJ2RPZy/bT5qZMre0vg4PdVgL6UI3afqLOQs8AvL3KG+RFg1Lw3lG/Obmitoa2+0VJrwEN+WO6D/huGn6B7v3yzuu5UrUwkZhd2/yUbnET7OdpfalqcbTbdq/teeo7TUFNp/OrNLhVb8o63mpQQ=")
   238  			Expect(err).ToNot(HaveOccurred())
   239  
   240  			valid, _ := prov.VerifySignatureBytes([]byte("too many secrets"), sig, nil)
   241  			Expect(valid).To(BeTrue())
   242  		})
   243  
   244  		It("Should fail for invalid sigs", func() {
   245  			valid, _ := prov.VerifySignatureBytes([]byte("too many secrets"), []byte("meh"), nil)
   246  			Expect(valid).To(BeFalse())
   247  		})
   248  
   249  		It("Should support cached certificates", func() {
   250  			cfg.Identity = "2.mcollective"
   251  			sig, err := base64.StdEncoding.DecodeString("a2FyZoRm4wIojH+s6qSo1ghOkeKOhayiMV44I03bRtlbYnBAZJQMzZ3GvA93w5ZDaEWndZIxxLOtzfVfLjJbF+uII2KJandWBd7nR7yByxOlpOdw0sIiBKWtiiugOnXQpbPNRyQxxyLFbmYo4bO/auraqZ8+AYl0nll8I5A0mZ2y6HIo8MdXu1+l1UTX8/Ji6G7f404Mw9CsXjo4EAfjtu/9i+cYMhqlv9lxobsuFzfA+lx/X1dtYmbW/pZ/ClnuydUdA5UV07Mf2iXjZ5c8xutLxnP+xhbQf7ql+yt9DaSX+RMwB+5ntGatRgYS/h8ihQZ970tCrCY456Uosa+xEmvfnZqoL++ja0pIgMJ4h7spQdCrjN2aXnL5IdiFROki1CPhJMaqipCb9kM8+ZtFehFh5Jx6WzekLCqkgKgZshYmJNQ4esAxWGNGxxSkiyYp5jea9qE5fLeidZrLfixNGfyXYfs75fUK9KZo3FkoPq4xFovWNr9KOXGKCT68dfg8S2SmV10CGGQ2wU1atYcpMz9Bua+3oDGpIt7OiDwOFBFHn8d8Nnm5qC2MQdn5Ys9PGCpAMh8b+P886mJlexVl18qXbcnPmM+acYFHMtZgH609w50l1zkd9MpdBqWcfH3r52VvAFh5SKAaWuVedBWea04tlx6E+UMjJvrK03aUJ9w=")
   252  			Expect(err).ToNot(HaveOccurred())
   253  
   254  			cert, err := os.ReadFile("../testdata/good/certs/2.mcollective.pem")
   255  			Expect(err).ToNot(HaveOccurred())
   256  
   257  			valid, _ := prov.VerifySignatureBytes([]byte("too many secrets"), sig, cert)
   258  			Expect(valid).To(BeTrue())
   259  		})
   260  	})
   261  
   262  	Describe("ChecksumBytes", func() {
   263  		It("Should produce the right checksum", func() {
   264  			sum, err := base64.StdEncoding.DecodeString("Yk+jdKdZ3v8E2p6dmbfn+ZN9lBBAHEIcOMp4lzuYKTo=")
   265  			Expect(err).ToNot(HaveOccurred())
   266  
   267  			Expect(prov.ChecksumBytes([]byte("too many secrets"))).To(Equal(sum))
   268  		})
   269  	})
   270  
   271  	Describe("TLSConfig", func() {
   272  		It("Should produce a valid TLS Config", func() {
   273  			c, err := prov.TLSConfig()
   274  			Expect(err).ToNot(HaveOccurred())
   275  
   276  			Expect(c.InsecureSkipVerify).To(BeFalse())
   277  			Expect(err).ToNot(HaveOccurred())
   278  
   279  			pub := prov.publicCertPath()
   280  			pri := prov.privateKeyPath()
   281  
   282  			cert, err := tls.LoadX509KeyPair(pub, pri)
   283  			Expect(err).ToNot(HaveOccurred())
   284  
   285  			Expect(c.Certificates).To(HaveLen(1))
   286  			Expect(c.Certificates[0].Certificate).To(Equal(cert.Certificate))
   287  		})
   288  
   289  		It("Should support disabling tls verify", func() {
   290  			cfg.DisableTLSVerify = true
   291  
   292  			c, err := prov.TLSConfig()
   293  			Expect(err).ToNot(HaveOccurred())
   294  
   295  			Expect(c.InsecureSkipVerify).To(BeTrue())
   296  
   297  		})
   298  	})
   299  
   300  	Describe("VerifyCertificate", func() {
   301  		var pem []byte
   302  
   303  		BeforeEach(func() {
   304  			pub := prov.publicCertPath()
   305  			pem, err = os.ReadFile(pub)
   306  			Expect(err).ToNot(HaveOccurred())
   307  		})
   308  
   309  		It("Should fail for foreign certs", func() {
   310  			pem, err = os.ReadFile(filepath.Join("..", "testdata", "foreign.pem"))
   311  			Expect(err).ToNot(HaveOccurred())
   312  			err := prov.VerifyCertificate(pem, "rip.mcollective")
   313  			Expect(err).To(MatchError("x509: certificate signed by unknown authority"))
   314  
   315  		})
   316  
   317  		It("Should fail for invalid names", func() {
   318  			err := prov.VerifyCertificate(pem, "bob")
   319  			Expect(err).To(MatchError("x509: certificate is valid for rip.mcollective, not bob"))
   320  		})
   321  
   322  		It("Should accept valid certs", func() {
   323  			err := prov.VerifyCertificate(pem, "rip.mcollective")
   324  			Expect(err).ToNot(HaveOccurred())
   325  		})
   326  
   327  		It("Should work with client provided intermediate chains", func() {
   328  			c, err := config.NewDefaultConfig()
   329  			Expect(err).ToNot(HaveOccurred())
   330  
   331  			c.Choria.FileSecurityCA = filepath.Join("..", "testdata", "intermediate", "certs", "ca.pem")
   332  
   333  			prov, err := New(WithChoriaConfig(&build.Info{}, c), WithLog(l.WithFields(logrus.Fields{})))
   334  			Expect(err).ToNot(HaveOccurred())
   335  
   336  			pem, err = os.ReadFile(filepath.Join("..", "testdata", "intermediate", "certs", "rip.mcollective.pem"))
   337  			Expect(err).ToNot(HaveOccurred())
   338  
   339  			err = prov.VerifyCertificate(pem, "rip.mcollective")
   340  			Expect(err).ToNot(HaveOccurred())
   341  		})
   342  
   343  		It("Should work with server side ca intermediate chains", func() {
   344  			c, err := config.NewDefaultConfig()
   345  			Expect(err).ToNot(HaveOccurred())
   346  
   347  			c.Choria.FileSecurityCA = filepath.Join("..", "testdata", "intermediate", "certs", "ca_chain_ca.pem")
   348  
   349  			prov, err := New(WithChoriaConfig(&build.Info{}, c), WithLog(l.WithFields(logrus.Fields{})))
   350  			Expect(err).ToNot(HaveOccurred())
   351  
   352  			pem, err = os.ReadFile(filepath.Join("..", "testdata", "intermediate", "certs", "ca_chain_rip.mcollective.pem"))
   353  			Expect(err).ToNot(HaveOccurred())
   354  
   355  			err = prov.VerifyCertificate(pem, "rip.mcollective")
   356  			Expect(err).ToNot(HaveOccurred())
   357  		})
   358  
   359  		It("Should work with email addresses", func() {
   360  			c, err := config.NewDefaultConfig()
   361  			Expect(err).ToNot(HaveOccurred())
   362  
   363  			c.Choria.FileSecurityCA = filepath.Join("..", "testdata", "intermediate", "certs", "ca_chain_ca.pem")
   364  
   365  			prov, err := New(WithChoriaConfig(&build.Info{}, c), WithLog(l.WithFields(logrus.Fields{})))
   366  			Expect(err).ToNot(HaveOccurred())
   367  
   368  			pem, err = os.ReadFile(filepath.Join("..", "testdata", "intermediate", "certs", "email-chain-rip.mcollective.pem"))
   369  			Expect(err).ToNot(HaveOccurred())
   370  
   371  			err = prov.VerifyCertificate(pem, "email:test@choria-io.com")
   372  			Expect(err).ToNot(HaveOccurred())
   373  		})
   374  
   375  		It("Should not work with wrong addresses", func() {
   376  			c, err := config.NewDefaultConfig()
   377  			Expect(err).ToNot(HaveOccurred())
   378  
   379  			c.Choria.FileSecurityCA = filepath.Join("..", "testdata", "intermediate", "certs", "ca_chain_ca.pem")
   380  
   381  			prov, err := New(WithChoriaConfig(&build.Info{}, c), WithLog(l.WithFields(logrus.Fields{})))
   382  			Expect(err).ToNot(HaveOccurred())
   383  
   384  			pem, err = os.ReadFile(filepath.Join("..", "testdata", "intermediate", "certs", "email-chain-rip.mcollective.pem"))
   385  			Expect(err).ToNot(HaveOccurred())
   386  
   387  			err = prov.VerifyCertificate(pem, "email:bad@choria-io.com")
   388  			Expect(err).To(HaveOccurred())
   389  		})
   390  	})
   391  
   392  	Describe("publicCertPem", func() {
   393  		It("Should return the correct pem data", func() {
   394  			dat, err := os.ReadFile(cfg.Certificate)
   395  			Expect(err).ToNot(HaveOccurred())
   396  			pb, _ := pem.Decode(dat)
   397  			Expect(err).ToNot(HaveOccurred())
   398  
   399  			block, err := prov.publicCertPem()
   400  			Expect(err).ToNot(HaveOccurred())
   401  			Expect(block.Bytes).To(Equal(pb.Bytes))
   402  		})
   403  	})
   404  
   405  	Describe("ShouldAllowCaller", func() {
   406  		It("Should only accept valid certs signed by our ca", func() {
   407  			pd, err := os.ReadFile(filepath.Join("..", "testdata", "foreign.pem"))
   408  			Expect(err).ToNot(HaveOccurred())
   409  
   410  			priv, err := prov.ShouldAllowCaller("foo", pd)
   411  			Expect(err).To(HaveOccurred())
   412  			Expect(priv).To(BeFalse())
   413  
   414  			pub := prov.publicCertPath()
   415  			pd, err = os.ReadFile(pub)
   416  			Expect(err).ToNot(HaveOccurred())
   417  
   418  			priv, err = prov.ShouldAllowCaller("rip.mcollective", pd)
   419  			Expect(err).ToNot(HaveOccurred())
   420  			Expect(priv).To(BeFalse())
   421  		})
   422  
   423  		It("Should accept privileged certs", func() {
   424  			pd, err := os.ReadFile(filepath.Join("..", "testdata", "good", "certs", "1.privileged.mcollective.pem"))
   425  			Expect(err).ToNot(HaveOccurred())
   426  
   427  			priv, err := prov.ShouldAllowCaller("bob", pd)
   428  			Expect(err).ToNot(HaveOccurred())
   429  			Expect(priv).To(BeTrue())
   430  		})
   431  
   432  		It("Should not accept certs with wrong names", func() {
   433  			pub := prov.publicCertPath()
   434  
   435  			pd, err := os.ReadFile(pub)
   436  			Expect(err).ToNot(HaveOccurred())
   437  
   438  			priv, err := prov.ShouldAllowCaller("bob", pd)
   439  			Expect(err).To(HaveOccurred())
   440  			Expect(priv).To(BeFalse())
   441  		})
   442  
   443  		It("Should only accept certs that's on the allowed list", func() {
   444  			cfg.AllowList = []string{"bob"}
   445  			pub := prov.publicCertPath()
   446  
   447  			pd, err := os.ReadFile(pub)
   448  			Expect(err).ToNot(HaveOccurred())
   449  
   450  			priv, err := prov.ShouldAllowCaller("rip.mcollective", pd)
   451  			Expect(priv).To(BeFalse())
   452  			Expect(err).To(MatchError("not on allow list"))
   453  		})
   454  
   455  		It("Should accept valid certs", func() {
   456  			pub := prov.publicCertPath()
   457  
   458  			pd, err := os.ReadFile(pub)
   459  			Expect(err).ToNot(HaveOccurred())
   460  
   461  			priv, err := prov.ShouldAllowCaller("rip.mcollective", pd)
   462  			Expect(err).ToNot(HaveOccurred())
   463  			Expect(priv).To(BeFalse())
   464  		})
   465  	})
   466  
   467  	Describe("privateKeyExists", func() {
   468  		It("Should detect existing keys", func() {
   469  			setTLS(cfg, goodStub, "rip.mcollective", "")
   470  
   471  			Expect(prov.privateKeyExists()).To(BeTrue())
   472  		})
   473  
   474  		It("Should detect absent keys", func() {
   475  			setTLS(cfg, goodStub, "na.mcollective", "")
   476  
   477  			Expect(prov.privateKeyExists()).To(BeFalse())
   478  		})
   479  	})
   480  
   481  	Describe("Configurable CipherSuites", func() {
   482  		var cipher string
   483  		var curve string
   484  		var c *config.Config
   485  
   486  		BeforeEach(func() {
   487  			_c, err := config.NewDefaultConfig()
   488  			Expect(err).ToNot(HaveOccurred())
   489  
   490  			c = _c
   491  		})
   492  
   493  		It("Should work with just one cipher", func() {
   494  			for _, cm := range tls.CipherSuites() {
   495  				cipher = cm.Name
   496  				break
   497  			}
   498  
   499  			c.Choria.CipherSuites = []string{cipher}
   500  
   501  			prov, err := New(WithChoriaConfig(&build.Info{}, c), WithLog(l.WithFields(logrus.Fields{})))
   502  			Expect(err).ToNot(HaveOccurred())
   503  
   504  			Expect(prov.conf.TLSConfig.CipherSuites).ToNot(BeNil())
   505  			Expect(prov.conf.TLSConfig.CipherSuites).To(HaveLen(1))
   506  		})
   507  
   508  		It("Should work with one curve", func() {
   509  			for cp := range tlssetup.CurvePreferenceMap {
   510  				curve = cp
   511  				break
   512  			}
   513  
   514  			c.Choria.ECCCurves = []string{curve}
   515  
   516  			prov, err := New(WithChoriaConfig(&build.Info{}, c), WithLog(l.WithFields(logrus.Fields{})))
   517  			Expect(err).ToNot(HaveOccurred())
   518  
   519  			Expect(prov.conf.TLSConfig.CurvePreferences).ToNot(BeNil())
   520  			Expect(prov.conf.TLSConfig.CurvePreferences).To(HaveLen(1))
   521  
   522  		})
   523  
   524  		It("Should have a default list cipher and curve list when not overridden", func() {
   525  			prov, err := New(WithChoriaConfig(&build.Info{}, c), WithLog(l.WithFields(logrus.Fields{})))
   526  			Expect(err).ToNot(HaveOccurred())
   527  
   528  			Expect(prov.conf.TLSConfig.CipherSuites).To(Equal(tlssetup.DefaultCipherSuites()))
   529  
   530  			Expect(prov.conf.TLSConfig.CurvePreferences).To(Equal(tlssetup.DefaultCurvePreferences()))
   531  		})
   532  	})
   533  })