github.com/choria-io/go-choria@v0.28.1-0.20240416190746-b3bf9c7d5a45/providers/security/pkcs11sec/pkcs11_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 pkcs11sec 6 7 import ( 8 "encoding/base64" 9 "fmt" 10 "os" 11 "path/filepath" 12 "testing" 13 14 "github.com/choria-io/go-choria/config" 15 . "github.com/onsi/ginkgo/v2" 16 . "github.com/onsi/gomega" 17 "github.com/sirupsen/logrus" 18 ) 19 20 func TestFileSecurity(t *testing.T) { 21 RegisterFailHandler(Fail) 22 if runPkcs11 := os.Getenv("RUN_PKCS11_TESTS"); runPkcs11 == "1" { 23 RunSpecs(t, "Security/Pkcs11Security") 24 } 25 } 26 27 var _ = Describe("Pkcs11Security", func() { 28 var err error 29 var prov *Pkcs11Security 30 var l *logrus.Logger 31 var lib string 32 var pin = "1234" 33 var c *config.Config 34 var testSlot = 374292918 35 36 BeforeEach(func() { 37 lib = "/usr/lib/softhsm/libsofthsm2.so" 38 if envLib := os.Getenv("SOFTHSM_LIB"); envLib != "" { 39 lib = envLib 40 } 41 42 wd, _ := os.Getwd() 43 err = os.Setenv("SOFTHSM2_CONF", wd+"/softhsm2.conf") 44 Expect(err).ToNot(HaveOccurred()) 45 46 l = logrus.New() 47 l.Out = GinkgoWriter 48 49 c, err = config.NewDefaultConfig() 50 Expect(err).ToNot(HaveOccurred()) 51 52 c.Choria.FileSecurityCA = filepath.Join("..", "testdata", "good", "certs", "ca.pem") 53 c.Choria.PKCS11Slot = testSlot 54 c.Choria.PKCS11DriverFile = lib 55 56 prov, err = New(WithChoriaConfig(c), WithLog(l.WithFields(logrus.Fields{})), WithPin(pin)) 57 Expect(err).ToNot(HaveOccurred()) 58 }) 59 60 Describe("WithChoriaConfig", func() { 61 It("Should copy all the relevant settings", func() { 62 c, err := config.NewDefaultConfig() 63 Expect(err).ToNot(HaveOccurred()) 64 65 c.Choria.FileSecurityCA = filepath.Join("..", "testdata", "good", "certs", "ca.pem") 66 c.Choria.PKCS11Slot = testSlot 67 c.Choria.PKCS11DriverFile = lib 68 c.DisableTLSVerify = true 69 70 prov, err := New(WithChoriaConfig(c), WithLog(l.WithFields(logrus.Fields{})), WithPin("1234")) 71 Expect(err).ToNot(HaveOccurred()) 72 73 Expect(prov.conf.AllowList).To(Equal([]string{"\\.mcollective$", "\\.choria$"})) 74 Expect(prov.conf.PrivilegedUsers).To(Equal([]string{"\\.privileged.mcollective$", "\\.privileged.choria$"})) 75 Expect(prov.conf.CAFile).To(Equal("../testdata/good/certs/ca.pem")) 76 Expect(prov.conf.DisableTLSVerify).To(BeTrue()) 77 }) 78 }) 79 80 Describe("Validate", func() { 81 It("Should return true if provider was successfully initialized", func() { 82 errs, ok := prov.Validate() 83 Expect(errs).To(BeEmpty()) 84 Expect(ok).To(BeTrue()) 85 }) 86 It("Should handle missing files", func() { 87 c, err := config.NewDefaultConfig() 88 Expect(err).ToNot(HaveOccurred()) 89 90 c.Choria.FileSecurityCA = "stub/ca.pem" 91 c.Choria.PKCS11Slot = testSlot 92 c.Choria.PKCS11DriverFile = lib 93 94 prov, err = New(WithChoriaConfig(c), WithLog(l.WithFields(logrus.Fields{})), WithPin(pin)) 95 Expect(err).ToNot(HaveOccurred()) 96 97 errs, ok := prov.Validate() 98 Expect(ok).To(BeFalse()) 99 Expect(errs).To(HaveLen(2)) 100 Expect(errs[0]).To(Equal(fmt.Sprintf("stat %s: no such file or directory", prov.conf.CAFile))) 101 }) 102 }) 103 104 Describe("Identity", func() { 105 It("Should return the identity", func() { 106 Expect(prov.Identity()).To(Equal("joeuser")) 107 }) 108 }) 109 110 Describe("CallerName", func() { 111 It("Should return the right caller name", func() { 112 Expect(prov.CallerName()).To(Equal("choria=joeuser")) 113 }) 114 }) 115 116 Describe("CallerIdentity", func() { 117 It("Should return the right caller ident, no matter the input", func() { 118 Expect(prov.CallerIdentity("choria=test.choria")).To(Equal("test.choria")) 119 Expect(prov.CallerIdentity("foo=test1.choria")).To(Equal("test1.choria")) 120 }) 121 }) 122 123 Describe("SignBytes", func() { 124 It("Should produce the right signature", func() { 125 sig, err := prov.SignBytes([]byte("too many secrets")) 126 Expect(err).ToNot(HaveOccurred()) 127 Expect(base64.StdEncoding.EncodeToString(sig)).To(Equal("PQlGnXt8jQ9N2WbghvKhH4qNTJcmTpbfspkT+9aSabivRbMGNIlMwDGMg8PQEC5AMF9eoxdaXuR/t2rbgUfqQrB3oI2YMD2clUtdVI1MIJ81ww90o0KHZa3C0N/OlshJVCDg1mUiget7rdfE5K3HARKbPZZbQFe/q5yPnjA7FGHEb1K+qnPyLGKD8WKIDTjHza16O6QWAcbyAWk2CP9ziLH5flVGMP0zMkdXQPiFfzexUG6iTIi64zVJ2k6E3k1JOGzRLeQfvUDNEQnmekH4w0iK0+uTZzBsQPr3jbd8xraTInv+v1CzrpBwoIP36Qlr296vxKngaqDSN2K3uSyKWg==")) 128 }) 129 }) 130 131 Describe("VerifySignatureBytes", func() { 132 It("Should validate correctly", func() { 133 sig, err := base64.StdEncoding.DecodeString("PQlGnXt8jQ9N2WbghvKhH4qNTJcmTpbfspkT+9aSabivRbMGNIlMwDGMg8PQEC5AMF9eoxdaXuR/t2rbgUfqQrB3oI2YMD2clUtdVI1MIJ81ww90o0KHZa3C0N/OlshJVCDg1mUiget7rdfE5K3HARKbPZZbQFe/q5yPnjA7FGHEb1K+qnPyLGKD8WKIDTjHza16O6QWAcbyAWk2CP9ziLH5flVGMP0zMkdXQPiFfzexUG6iTIi64zVJ2k6E3k1JOGzRLeQfvUDNEQnmekH4w0iK0+uTZzBsQPr3jbd8xraTInv+v1CzrpBwoIP36Qlr296vxKngaqDSN2K3uSyKWg==") 134 Expect(err).ToNot(HaveOccurred()) 135 136 valid, _ := prov.VerifySignatureBytes([]byte("too many secrets"), sig, nil) 137 Expect(valid).To(BeTrue()) 138 }) 139 140 It("Should fail for invalid sigs", func() { 141 valid, _ := prov.VerifySignatureBytes([]byte("too many secrets"), []byte("meh"), nil) 142 Expect(valid).To(BeFalse()) 143 }) 144 145 It("Should support cached certificates", func() { 146 prov, err = New(WithChoriaConfig(c), WithLog(l.WithFields(logrus.Fields{})), WithPin(pin)) 147 Expect(err).ToNot(HaveOccurred()) 148 149 sig, err := base64.StdEncoding.DecodeString("Zq1F2bdXOAvB5Ca+iYCZ/BLYz2ZzbQP/V8kwQY0E3cuDrBDArX7UhUnBakzN+Msr7UyF+EkYmzvIi4KHnFBrgi7otM8Q5YMh5IT+IPaoHj3Rj/jorqD4g8ltZINqCUBWDN4wvSG98SxLyawV69gAK4SnP+oy7SU7zxuQiPwIMJ7lVoiQ3t+tiQAHUxeykQPw7WElLb+wPTb1k4DM3yRkijA9OeUk+3SVyl2sTCu5h/Lg0lcI372bkLDESlnhnvw7yuLD2SSncrEQrBdv/N2yEpY2fx1UKGlTrn9GH4MGA1GuzE1F87RH9P8ieeul6vI13BkBAlMk5KaGlmWpgiGri5UjCHHXMxEnXfwUcKFE+E6yVg4SbrJknkuJzNJduypMIep7YOnPHVLNIBZLuOUdJrRgBQ+Yb9mxPnEQHhOHeN0XHUcseRJEISqPkagpNx1xhOb7g3hsNyEvqibT/DZsc/2hyU2I/wG9fl26CnN9c12r1zInyCQYsU/wuIvjDtRZvTpLGJSJdgjSmTPzGmA/fKpAfOWObdsoLeorjF/pNweuc0x0JZMsBrZauldLL53wnnvllsFEmIAxs+RusoJ2UfW7WugZ7lXGISHTef6IHjukHgDBSbeGawVCnAgPbPz1dy42x04koUW3Bmz89fJ4/j+e49ijz7z3W/IercNeke4=") 150 Expect(err).ToNot(HaveOccurred()) 151 152 cert, err := os.ReadFile("../testdata/good/certs/2.mcollective.pem") 153 Expect(err).ToNot(HaveOccurred()) 154 155 valid, _ := prov.VerifySignatureBytes([]byte("too many secrets"), sig, cert) 156 Expect(valid).To(BeTrue()) 157 }) 158 }) 159 160 Describe("ChecksumBytes", func() { 161 It("Should produce the right checksum", func() { 162 sum, err := base64.StdEncoding.DecodeString("Yk+jdKdZ3v8E2p6dmbfn+ZN9lBBAHEIcOMp4lzuYKTo=") 163 Expect(err).ToNot(HaveOccurred()) 164 165 Expect(prov.ChecksumBytes([]byte("too many secrets"))).To(Equal(sum)) 166 }) 167 }) 168 169 Describe("TLSConfig", func() { 170 It("Should produce a valid TLS Config", func() { 171 c, err := config.NewDefaultConfig() 172 Expect(err).ToNot(HaveOccurred()) 173 174 c.Choria.FileSecurityCA = filepath.Join("test_data", "ssl_dir", "certs", "ca.pem") 175 c.Choria.PKCS11Slot = testSlot 176 c.Choria.PKCS11DriverFile = lib 177 178 prov, err = New(WithChoriaConfig(c), WithLog(l.WithFields(logrus.Fields{})), WithPin(pin)) 179 180 Expect(err).ToNot(HaveOccurred()) 181 tlsC, err := prov.TLSConfig() 182 Expect(err).ToNot(HaveOccurred()) 183 184 Expect(tlsC.InsecureSkipVerify).To(BeFalse()) 185 Expect(err).ToNot(HaveOccurred()) 186 187 cert := prov.cert 188 189 Expect(tlsC.Certificates).To(HaveLen(1)) 190 Expect(tlsC.Certificates[0].Certificate).To(Equal(cert.Certificate)) 191 }) 192 193 It("Should support disabling tls verify", func() { 194 c, err := config.NewDefaultConfig() 195 Expect(err).ToNot(HaveOccurred()) 196 197 c.Choria.FileSecurityCA = filepath.Join("test_data", "ssl_dir", "certs", "ca.pem") 198 c.DisableTLSVerify = true 199 c.Choria.PKCS11Slot = testSlot 200 c.Choria.PKCS11DriverFile = lib 201 202 prov, err = New(WithChoriaConfig(c), WithLog(l.WithFields(logrus.Fields{})), WithPin(pin)) 203 204 Expect(err).ToNot(HaveOccurred()) 205 206 tlsC, err := prov.TLSConfig() 207 Expect(err).ToNot(HaveOccurred()) 208 209 Expect(tlsC.InsecureSkipVerify).To(BeTrue()) 210 211 }) 212 }) 213 214 Describe("VerifyCertificate", func() { 215 var pem []byte 216 var inter *config.Config 217 218 BeforeEach(func() { 219 inter, err = config.NewDefaultConfig() 220 Expect(err).ToNot(HaveOccurred()) 221 222 inter.Choria.FileSecurityCA = filepath.Join("..", "testdata", "intermediate", "certs", "ca_chain_ca.pem") 223 inter.Choria.PKCS11Slot = testSlot 224 inter.Choria.PKCS11DriverFile = lib 225 226 pem, err = prov.PublicCertBytes() 227 Expect(err).ToNot(HaveOccurred()) 228 }) 229 230 It("Should fail for foreign certs", func() { 231 pem, err = os.ReadFile(filepath.Join("..", "testdata", "foreign.pem")) 232 Expect(err).ToNot(HaveOccurred()) 233 err := prov.VerifyCertificate(pem, "rip.mcollective") 234 Expect(err).To(MatchError("x509: certificate signed by unknown authority")) 235 236 }) 237 238 It("Should fail for invalid names", func() { 239 prov, err = New(WithChoriaConfig(c), WithLog(l.WithFields(logrus.Fields{})), WithPin(pin)) 240 Expect(err).ToNot(HaveOccurred()) 241 242 err := prov.VerifyCertificate(pem, "bob") 243 Expect(err).To(MatchError("x509: certificate is valid for joeuser, not bob")) 244 }) 245 246 It("Should accept valid certs", func() { 247 c, err := config.NewDefaultConfig() 248 Expect(err).ToNot(HaveOccurred()) 249 250 c.Choria.FileSecurityCA = filepath.Join("test_data", "ssl_dir", "certs", "ca.pem") 251 c.Choria.PKCS11Slot = testSlot 252 c.Choria.PKCS11DriverFile = lib 253 254 prov, err = New(WithChoriaConfig(c), WithLog(l.WithFields(logrus.Fields{})), WithPin(pin)) 255 Expect(err).ToNot(HaveOccurred()) 256 257 err = prov.VerifyCertificate(pem, "joeuser") 258 Expect(err).ToNot(HaveOccurred()) 259 }) 260 261 It("Should work with client provided intermediate chains", func() { 262 c, err := config.NewDefaultConfig() 263 Expect(err).ToNot(HaveOccurred()) 264 265 c.Choria.FileSecurityCA = filepath.Join("..", "testdata", "intermediate", "certs", "ca.pem") 266 c.Choria.SSLDir = filepath.Join("..", "testdata", "intermediate") 267 c.Choria.PKCS11Slot = testSlot 268 c.Choria.PKCS11DriverFile = lib 269 270 prov, err := New(WithChoriaConfig(c), WithLog(l.WithFields(logrus.Fields{})), WithPin("1234")) 271 Expect(err).ToNot(HaveOccurred()) 272 273 pem, err = os.ReadFile(filepath.Join("..", "testdata", "intermediate", "certs", "rip.mcollective.pem")) 274 Expect(err).ToNot(HaveOccurred()) 275 276 err = prov.VerifyCertificate(pem, "rip.mcollective") 277 Expect(err).ToNot(HaveOccurred()) 278 }) 279 280 It("Should work with server side ca intermediate chains", func() { 281 prov, err := New(WithChoriaConfig(inter), WithLog(l.WithFields(logrus.Fields{})), WithPin("1234")) 282 Expect(err).ToNot(HaveOccurred()) 283 284 pem, err = os.ReadFile(filepath.Join("..", "testdata", "intermediate", "certs", "ca_chain_rip.mcollective.pem")) 285 Expect(err).ToNot(HaveOccurred()) 286 287 err = prov.VerifyCertificate(pem, "rip.mcollective") 288 Expect(err).ToNot(HaveOccurred()) 289 }) 290 291 It("Should work with email addresses", func() { 292 prov, err := New(WithChoriaConfig(inter), WithLog(l.WithFields(logrus.Fields{})), WithPin(pin)) 293 Expect(err).ToNot(HaveOccurred()) 294 295 pem, err = os.ReadFile(filepath.Join("..", "testdata", "intermediate", "certs", "email-chain-rip.mcollective.pem")) 296 Expect(err).ToNot(HaveOccurred()) 297 298 err = prov.VerifyCertificate(pem, "email:test@choria-io.com") 299 Expect(err).ToNot(HaveOccurred()) 300 }) 301 302 It("Should not work with wrong addresses", func() { 303 prov, err := New(WithChoriaConfig(inter), WithLog(l.WithFields(logrus.Fields{})), WithPin(pin)) 304 Expect(err).ToNot(HaveOccurred()) 305 306 pem, err = os.ReadFile(filepath.Join("..", "testdata", "intermediate", "certs", "email-chain-rip.mcollective.pem")) 307 Expect(err).ToNot(HaveOccurred()) 308 309 err = prov.VerifyCertificate(pem, "email:bad@choria-io.com") 310 Expect(err).To(HaveOccurred()) 311 }) 312 }) 313 })