spiffe.go

 1/*
 2 *
 3 * Copyright 2020 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 credentials defines APIs for parsing SPIFFE ID.
20//
21// All APIs in this package are experimental.
22package credentials
23
24import (
25	"crypto/tls"
26	"crypto/x509"
27	"net/url"
28
29	"google.golang.org/grpc/grpclog"
30)
31
32var logger = grpclog.Component("credentials")
33
34// SPIFFEIDFromState parses the SPIFFE ID from State. If the SPIFFE ID format
35// is invalid, return nil with warning.
36func SPIFFEIDFromState(state tls.ConnectionState) *url.URL {
37	if len(state.PeerCertificates) == 0 || len(state.PeerCertificates[0].URIs) == 0 {
38		return nil
39	}
40	return SPIFFEIDFromCert(state.PeerCertificates[0])
41}
42
43// SPIFFEIDFromCert parses the SPIFFE ID from x509.Certificate. If the SPIFFE
44// ID format is invalid, return nil with warning.
45func SPIFFEIDFromCert(cert *x509.Certificate) *url.URL {
46	if cert == nil || cert.URIs == nil {
47		return nil
48	}
49	var spiffeID *url.URL
50	for _, uri := range cert.URIs {
51		if uri == nil || uri.Scheme != "spiffe" || uri.Opaque != "" || (uri.User != nil && uri.User.Username() != "") {
52			continue
53		}
54		// From this point, we assume the uri is intended for a SPIFFE ID.
55		if len(uri.String()) > 2048 {
56			logger.Warning("invalid SPIFFE ID: total ID length larger than 2048 bytes")
57			return nil
58		}
59		if len(uri.Host) == 0 || len(uri.Path) == 0 {
60			logger.Warning("invalid SPIFFE ID: domain or workload ID is empty")
61			return nil
62		}
63		if len(uri.Host) > 255 {
64			logger.Warning("invalid SPIFFE ID: domain length larger than 255 characters")
65			return nil
66		}
67		// A valid SPIFFE certificate can only have exactly one URI SAN field.
68		if len(cert.URIs) > 1 {
69			logger.Warning("invalid SPIFFE ID: multiple URI SANs")
70			return nil
71		}
72		spiffeID = uri
73	}
74	return spiffeID
75}