go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/tokenserver/appengine/impl/machinetoken/machinetoken_test.go (about) 1 // Copyright 2016 The LUCI Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package machinetoken 16 17 import ( 18 "context" 19 "crypto/x509" 20 "crypto/x509/pkix" 21 "math/big" 22 "testing" 23 "time" 24 25 "go.chromium.org/luci/common/clock/testclock" 26 "go.chromium.org/luci/server/auth/signing" 27 28 tokenserver "go.chromium.org/luci/tokenserver/api" 29 "go.chromium.org/luci/tokenserver/api/admin/v1" 30 31 . "github.com/smartystreets/goconvey/convey" 32 . "go.chromium.org/luci/common/testing/assertions" 33 ) 34 35 func TestMachineFQDN(t *testing.T) { 36 // See rpc_mocks_test.go for getTestCert, certWithCN and certWithSAN. 37 38 // Test parsing of the real certs. 39 40 Convey("MachineFQDN works for cert without SAN", t, func() { 41 params := MintParams{Cert: getTestCert(certWithCN)} 42 fqdn, err := params.MachineFQDN() 43 So(err, ShouldBeNil) 44 So(fqdn, ShouldEqual, "luci-token-server-test-1.fake.domain") 45 }) 46 47 Convey("MachineFQDN works for cert with SAN", t, func() { 48 params := MintParams{Cert: getTestCert(certWithSAN)} 49 fqdn, err := params.MachineFQDN() 50 So(err, ShouldBeNil) 51 So(fqdn, ShouldEqual, "fuchsia-debian-dev-141242e1-us-central1-f-0psd.c.fuchsia-infra.internal") 52 }) 53 54 Convey("MachineFQDN works for cert where CN == SAN", t, func() { 55 params := MintParams{Cert: getTestCert(certWithCNEqualSAN)} 56 fqdn, err := params.MachineFQDN() 57 So(err, ShouldBeNil) 58 So(fqdn, ShouldEqual, "proto-chrome-focal.c.chromecompute.google.com.internal") 59 }) 60 61 Convey("MachineFQDN with more than one SAN", t, func() { 62 params := MintParams{ 63 Cert: &x509.Certificate{ 64 Subject: pkix.Name{CommonName: "name1"}, 65 DNSNames: []string{"name1.example.com", "name2.example.com"}, 66 }, 67 } 68 fqdn, err := params.MachineFQDN() 69 So(fqdn, ShouldEqual, "name1.example.com") 70 So(err, ShouldBeNil) 71 }) 72 73 // Test some synthetic cases. 74 75 Convey("MachineFQDN with empty CN", t, func() { 76 params := MintParams{ 77 Cert: &x509.Certificate{ 78 DNSNames: []string{"name1.example.com"}, 79 }, 80 } 81 _, err := params.MachineFQDN() 82 So(err, ShouldErrLike, "unsupported cert, Subject CN field is required") 83 }) 84 } 85 86 func TestMintParamsValidation(t *testing.T) { 87 Convey("with token params", t, func() { 88 params := MintParams{ 89 Cert: &x509.Certificate{ 90 Subject: pkix.Name{CommonName: "host.domain"}, 91 SerialNumber: big.NewInt(12345), 92 }, 93 Config: &admin.CertificateAuthorityConfig{ 94 KnownDomains: []*admin.DomainConfig{ 95 { 96 Domain: []string{"domain"}, 97 MachineTokenLifetime: 3600, 98 }, 99 }, 100 }, 101 } 102 103 Convey("good params", func() { 104 So(params.Validate(), ShouldBeNil) 105 fqdn, err := params.MachineFQDN() 106 So(err, ShouldBeNil) 107 So(fqdn, ShouldEqual, "host.domain") 108 }) 109 110 Convey("good params with subdomain", func() { 111 params.Cert.Subject.CommonName = "host.subdomain.domain" 112 So(params.Validate(), ShouldBeNil) 113 fqdn, err := params.MachineFQDN() 114 So(err, ShouldBeNil) 115 So(fqdn, ShouldEqual, "host.subdomain.domain") 116 }) 117 118 Convey("bad FQDN case is converted to lowercase", func() { 119 params.Cert.Subject.CommonName = "HOST.domain" 120 So(params.Validate(), ShouldBeNil) 121 fqdn, err := params.MachineFQDN() 122 So(err, ShouldBeNil) 123 So(fqdn, ShouldEqual, "host.domain") 124 }) 125 126 Convey("bad FQDN", func() { 127 params.Cert.Subject.CommonName = "host" 128 So(params.Validate(), ShouldErrLike, "not a valid FQDN") 129 }) 130 131 Convey("not listed", func() { 132 params.Cert.Subject.CommonName = "host.blah" 133 So(params.Validate(), ShouldErrLike, "not listed in the config") 134 }) 135 136 Convey("tokens are not allowed", func() { 137 params.Config.KnownDomains[0].MachineTokenLifetime = 0 138 So(params.Validate(), ShouldErrLike, "are not allowed") 139 }) 140 141 Convey("bad SN", func() { 142 params.Cert.SerialNumber = big.NewInt(-1) 143 So(params.Validate(), ShouldErrLike, "invalid certificate serial number") 144 }) 145 }) 146 } 147 148 func TestMint(t *testing.T) { 149 Convey("with mock context", t, func() { 150 ctx := context.Background() 151 ctx, _ = testclock.UseTime(ctx, time.Date(2015, time.February, 3, 4, 5, 6, 7, time.UTC)) 152 153 Convey("works", func() { 154 params := MintParams{ 155 Cert: &x509.Certificate{ 156 Subject: pkix.Name{CommonName: "host.domain"}, 157 SerialNumber: big.NewInt(12345), 158 }, 159 Config: &admin.CertificateAuthorityConfig{ 160 KnownDomains: []*admin.DomainConfig{ 161 { 162 Domain: []string{"domain"}, 163 MachineTokenLifetime: 3600, 164 }, 165 }, 166 }, 167 Signer: fakeSigner{}, 168 } 169 body, token, err := Mint(ctx, ¶ms) 170 So(err, ShouldBeNil) 171 So(body, ShouldResembleProto, &tokenserver.MachineTokenBody{ 172 MachineFqdn: "host.domain", 173 IssuedBy: "token-server@example.com", 174 IssuedAt: 1422936306, 175 Lifetime: 3600, 176 CaId: 0, 177 CertSn: big.NewInt(12345).Bytes(), 178 }) 179 So(token, ShouldEqual, "CjQKC2hvc3QuZG9tYWluEhh0b2tlbi1zZXJ2ZXJAZXhhbXB"+ 180 "sZS5jb20Y8pHBpgUgkBw6AjA5EgZrZXlfaWQaCXNpZ25hdHVyZQ") 181 }) 182 }) 183 } 184 185 type fakeSigner struct{} 186 187 func (fakeSigner) SignBytes(c context.Context, blob []byte) (keyID string, sig []byte, err error) { 188 return "key_id", []byte("signature"), nil 189 } 190 191 func (fakeSigner) Certificates(c context.Context) (*signing.PublicCertificates, error) { 192 panic("not implemented yet") 193 } 194 195 func (fakeSigner) ServiceInfo(c context.Context) (*signing.ServiceInfo, error) { 196 return &signing.ServiceInfo{ 197 ServiceAccountName: "token-server@example.com", 198 }, nil 199 }