google.golang.org/grpc@v1.74.2/internal/credentials/spiffe/spiffe_test.go (about) 1 /* 2 * 3 * Copyright 2025 gRPC authors. 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 * 17 */ 18 19 package spiffe 20 21 import ( 22 "crypto/x509" 23 "encoding/pem" 24 "io" 25 "net/url" 26 "os" 27 "strings" 28 "testing" 29 30 "google.golang.org/grpc/testdata" 31 ) 32 33 const wantURI = "spiffe://foo.bar.com/client/workload/1" 34 35 func loadFileBytes(t *testing.T, filePath string) []byte { 36 bytes, err := os.ReadFile(filePath) 37 if err != nil { 38 t.Fatalf("Error reading file: %v", err) 39 } 40 return bytes 41 } 42 43 func TestKnownSPIFFEBundle(t *testing.T) { 44 spiffeBundleFile := testdata.Path("spiffe/spiffebundle.json") 45 spiffeBundleBytes := loadFileBytes(t, spiffeBundleFile) 46 bundles, err := BundleMapFromBytes(spiffeBundleBytes) 47 if err != nil { 48 t.Fatalf("BundleMapFromBytes(%v) error during parsing: %v", spiffeBundleFile, err) 49 } 50 const wantBundleSize = 2 51 if len(bundles) != wantBundleSize { 52 t.Fatalf("BundleMapFromBytes(%v) did not parse correct bundle length. got %v want %v", spiffeBundleFile, len(bundles), wantBundleSize) 53 } 54 if bundles["example.com"] == nil { 55 t.Fatalf("BundleMapFromBytes(%v) got no bundle for example.com", spiffeBundleFile) 56 } 57 if bundles["test.example.com"] == nil { 58 t.Fatalf("BundleMapFromBytes(%v) got no bundle for test.example.com", spiffeBundleFile) 59 } 60 61 wantExampleComCert := loadX509Cert(t, testdata.Path("spiffe/spiffe_cert.pem")) 62 wantTestExampleComCert := loadX509Cert(t, testdata.Path("spiffe/server1_spiffe.pem")) 63 if !bundles["example.com"].X509Authorities()[0].Equal(wantExampleComCert) { 64 t.Errorf("BundleMapFromBytes(%v) parsed wrong cert for example.com.", spiffeBundleFile) 65 } 66 if !bundles["test.example.com"].X509Authorities()[0].Equal(wantTestExampleComCert) { 67 t.Errorf("BundleMapFromBytes(%v) parsed wrong cert for test.example.com", spiffeBundleFile) 68 } 69 70 } 71 72 func loadX509Cert(t *testing.T, filePath string) *x509.Certificate { 73 t.Helper() 74 certFile, _ := os.Open(filePath) 75 certRaw, _ := io.ReadAll(certFile) 76 block, _ := pem.Decode([]byte(certRaw)) 77 if block == nil { 78 t.Fatalf("pem.Decode(%v) = nil. Want a value.", certRaw) 79 } 80 cert, err := x509.ParseCertificate(block.Bytes) 81 if err != nil { 82 t.Fatalf("x509.ParseCertificate(%v) failed %v", block.Bytes, err.Error()) 83 } 84 return cert 85 } 86 87 func TestSPIFFEBundleMapFailures(t *testing.T) { 88 filePaths := []string{ 89 testdata.Path("spiffe/spiffebundle_corrupted_cert.json"), 90 testdata.Path("spiffe/spiffebundle_malformed.json"), 91 testdata.Path("spiffe/spiffebundle_wrong_kid.json"), 92 testdata.Path("spiffe/spiffebundle_wrong_kty.json"), 93 testdata.Path("spiffe/spiffebundle_wrong_multi_certs.json"), 94 testdata.Path("spiffe/spiffebundle_wrong_root.json"), 95 testdata.Path("spiffe/spiffebundle_wrong_seq_type.json"), 96 testdata.Path("spiffe/spiffebundle_invalid_trustdomain.json"), 97 testdata.Path("spiffe/spiffebundle_empty_string_key.json"), 98 testdata.Path("spiffe/spiffebundle_empty_keys.json"), 99 } 100 for _, path := range filePaths { 101 t.Run(path, func(t *testing.T) { 102 bundleBytes := loadFileBytes(t, path) 103 if _, err := BundleMapFromBytes(bundleBytes); err == nil { 104 t.Fatalf("BundleMapFromBytes(%v) did not fail but should have", path) 105 } 106 }) 107 } 108 } 109 110 func TestSPIFFEBundleMapX509Failures(t *testing.T) { 111 // SPIFFE Bundles only support a use of x509-svid and jwt-svid. If a 112 // use other than this is specified, the parser does not fail, it 113 // just doesn't add an x509 authority or jwt authority to the bundle 114 filePath := testdata.Path("spiffe/spiffebundle_wrong_use.json") 115 bundleBytes := loadFileBytes(t, filePath) 116 bundle, err := BundleMapFromBytes(bundleBytes) 117 if err != nil { 118 t.Fatalf("BundleMapFromBytes(%v) failed with error: %v", filePath, err) 119 } 120 if len(bundle["example.com"].X509Authorities()) != 0 { 121 t.Fatalf("BundleMapFromBytes(%v) did not have empty bundle but should have", filePath) 122 } 123 } 124 125 func TestGetRootsFromSPIFFEBundleMapSuccess(t *testing.T) { 126 bundleMapFile := testdata.Path("spiffe/spiffebundle_match_client_spiffe.json") 127 bundleBytes := loadFileBytes(t, bundleMapFile) 128 bundle, err := BundleMapFromBytes(bundleBytes) 129 if err != nil { 130 t.Fatalf("BundleMapFromBytes(%v) failed with error: %v", bundleMapFile, err) 131 } 132 133 cert := loadX509Cert(t, testdata.Path("spiffe/client_spiffe.pem")) 134 gotRoots, err := GetRootsFromSPIFFEBundleMap(bundle, cert) 135 if err != nil { 136 t.Fatalf("GetRootsFromSPIFFEBundleMap() failed with err %v", err) 137 } 138 wantRoot := loadX509Cert(t, testdata.Path("spiffe/spiffe_cert.pem")) 139 wantRoots := x509.NewCertPool() 140 wantRoots.AddCert(wantRoot) 141 if !gotRoots.Equal(wantRoots) { 142 t.Fatalf("GetRootsFromSPIFFEBundleMap() got %v want %v", gotRoots, wantRoots) 143 } 144 } 145 146 func TestGetRootsFromSPIFFEBundleMapFailures(t *testing.T) { 147 bundleMapFile := testdata.Path("spiffe/spiffebundle.json") 148 bundleBytes := loadFileBytes(t, bundleMapFile) 149 bundle, err := BundleMapFromBytes(bundleBytes) 150 certWithTwoURIs := loadX509Cert(t, testdata.Path("spiffe/client_spiffe.pem")) 151 certWithTwoURIs.URIs = append(certWithTwoURIs.URIs, certWithTwoURIs.URIs[0]) 152 certWithNoURIs := loadX509Cert(t, testdata.Path("spiffe/client_spiffe.pem")) 153 certWithNoURIs.URIs = nil 154 if err != nil { 155 t.Fatalf("BundleMapFromBytes(%v) failed with error: %v", bundleMapFile, err) 156 } 157 tests := []struct { 158 name string 159 bundleMapFile string 160 leafCert *x509.Certificate 161 wantErr string 162 }{ 163 { 164 name: "no bundle for peer cert spiffeID", 165 leafCert: loadX509Cert(t, testdata.Path("spiffe/client_spiffe.pem")), 166 wantErr: "no bundle found for peer certificates", 167 }, 168 { 169 name: "cert has invalid SPIFFE id", 170 leafCert: loadX509Cert(t, testdata.Path("ca.pem")), 171 wantErr: "could not get spiffe ID from peer leaf cert", 172 }, 173 { 174 name: "nil cert", 175 leafCert: nil, 176 wantErr: "input cert is nil", 177 }, 178 { 179 name: "cert has multiple URIs", 180 leafCert: certWithTwoURIs, 181 wantErr: "input cert has 2 URIs but should have 1", 182 }, 183 { 184 name: "cert has no URIs", 185 leafCert: certWithNoURIs, 186 wantErr: "input cert has 0 URIs but should have 1", 187 }, 188 } 189 for _, tc := range tests { 190 t.Run(tc.name, func(t *testing.T) { 191 _, err = GetRootsFromSPIFFEBundleMap(bundle, tc.leafCert) 192 if err == nil { 193 t.Fatalf("GetRootsFromSPIFFEBundleMap() got no error but want error containing %v.", tc.wantErr) 194 } 195 if !strings.Contains(err.Error(), tc.wantErr) { 196 t.Fatalf("GetRootsFromSPIFFEBundleMap() got error: %v. want error to contain %v", err, tc.wantErr) 197 } 198 }) 199 } 200 } 201 202 func TestIDFromCert(t *testing.T) { 203 cert := loadX509Cert(t, testdata.Path("x509/spiffe_cert.pem")) 204 uri, err := idFromCert(cert) 205 if err != nil { 206 t.Fatalf("idFromCert() failed with err: %v", err) 207 } 208 if uri != nil && uri.String() != wantURI { 209 t.Fatalf("ID not expected, got %s, want %s", uri.String(), wantURI) 210 } 211 } 212 213 func TestIDFromCertFileFailures(t *testing.T) { 214 certWithNoURIs := loadX509Cert(t, testdata.Path("spiffe/client_spiffe.pem")) 215 certWithNoURIs.URIs = nil 216 certWithInvalidSPIFFEID := loadX509Cert(t, testdata.Path("spiffe/client_spiffe.pem")) 217 certWithInvalidSPIFFEID.URIs = []*url.URL{{Path: "non-spiffe.bad"}} 218 tests := []struct { 219 name string 220 cert *x509.Certificate 221 }{ 222 { 223 name: "certificate with multiple URIs", 224 cert: loadX509Cert(t, testdata.Path("x509/multiple_uri_cert.pem")), 225 }, 226 { 227 name: "certificate with invalidSPIFFE ID", 228 cert: certWithInvalidSPIFFEID, 229 }, 230 { 231 name: "nil cert", 232 cert: nil, 233 }, 234 { 235 name: "cert with no URIs", 236 cert: certWithNoURIs, 237 }, 238 } 239 for _, tt := range tests { 240 t.Run(tt.name, func(t *testing.T) { 241 if _, err := idFromCert(tt.cert); err == nil { 242 t.Fatalf("idFromCert() succeeded but want error") 243 } 244 }) 245 } 246 }