1
16
17 package serviceaccount
18
19 import (
20 "crypto"
21 "crypto/ecdsa"
22 "crypto/elliptic"
23 "crypto/rsa"
24 "encoding/json"
25 "fmt"
26 "net/url"
27
28 jose "gopkg.in/square/go-jose.v2"
29
30 "k8s.io/apimachinery/pkg/util/errors"
31 "k8s.io/apimachinery/pkg/util/sets"
32 )
33
34 const (
35
36
37
38
39 OpenIDConfigPath = "/.well-known/openid-configuration"
40
41
42
43
44 JWKSPath = "/openid/v1/jwks"
45 )
46
47
48 type OpenIDMetadata struct {
49 ConfigJSON []byte
50 PublicKeysetJSON []byte
51 }
52
53
54
55
56
57
58 func NewOpenIDMetadata(issuerURL, jwksURI, defaultExternalAddress string, pubKeys []interface{}) (*OpenIDMetadata, error) {
59 if issuerURL == "" {
60 return nil, fmt.Errorf("empty issuer URL")
61 }
62 if jwksURI == "" && defaultExternalAddress == "" {
63 return nil, fmt.Errorf("either the JWKS URI or the default external address, or both, must be set")
64 }
65 if len(pubKeys) == 0 {
66 return nil, fmt.Errorf("no keys provided for validating keyset")
67 }
68
69
70
71
72 iss, err := url.Parse(issuerURL)
73 if err != nil {
74 return nil, err
75 }
76 if iss.Scheme != "https" {
77 return nil, fmt.Errorf("issuer URL must use https scheme, got: %s", issuerURL)
78 }
79 if iss.RawQuery != "" {
80 return nil, fmt.Errorf("issuer URL may not include a query, got: %s", issuerURL)
81 }
82 if iss.Fragment != "" {
83 return nil, fmt.Errorf("issuer URL may not include a fragment, got: %s", issuerURL)
84 }
85
86
87
88 if jwksURI == "" {
89 const msg = "attempted to build jwks_uri from external " +
90 "address %s, but could not construct a valid URL. Error: %v"
91
92 if defaultExternalAddress == "" {
93 return nil, fmt.Errorf(msg, defaultExternalAddress,
94 fmt.Errorf("empty address"))
95 }
96
97 u := &url.URL{
98 Scheme: "https",
99 Host: defaultExternalAddress,
100 Path: JWKSPath,
101 }
102 jwksURI = u.String()
103
104
105
106
107
108
109
110
111 parsed, err := url.Parse(jwksURI)
112 if err != nil {
113 return nil, fmt.Errorf(msg, defaultExternalAddress, err)
114 } else if u.Scheme != parsed.Scheme ||
115 u.Host != parsed.Host ||
116 u.Path != parsed.Path {
117 return nil, fmt.Errorf(msg, defaultExternalAddress,
118 fmt.Errorf("got %v, expected %v", parsed, u))
119 }
120 } else {
121
122 if u, err := url.Parse(jwksURI); err != nil {
123 return nil, err
124 } else if u.Scheme != "https" {
125 return nil, fmt.Errorf("jwksURI requires https scheme, parsed as: %v", u.String())
126 }
127 }
128
129 configJSON, err := openIDConfigJSON(issuerURL, jwksURI, pubKeys)
130 if err != nil {
131 return nil, fmt.Errorf("could not marshal issuer discovery JSON, error: %v", err)
132 }
133
134 keysetJSON, err := openIDKeysetJSON(pubKeys)
135 if err != nil {
136 return nil, fmt.Errorf("could not marshal issuer keys JSON, error: %v", err)
137 }
138
139 return &OpenIDMetadata{
140 ConfigJSON: configJSON,
141 PublicKeysetJSON: keysetJSON,
142 }, nil
143 }
144
145
146
147 type openIDMetadata struct {
148 Issuer string `json:"issuer"`
149
150
151
152
153
154 JWKSURI string `json:"jwks_uri"`
155 ResponseTypes []string `json:"response_types_supported"`
156 SubjectTypes []string `json:"subject_types_supported"`
157 SigningAlgs []string `json:"id_token_signing_alg_values_supported"`
158 }
159
160
161
162 func openIDConfigJSON(iss, jwksURI string, keys []interface{}) ([]byte, error) {
163 keyset, errs := publicJWKSFromKeys(keys)
164 if errs != nil {
165 return nil, errs
166 }
167
168 metadata := openIDMetadata{
169 Issuer: iss,
170 JWKSURI: jwksURI,
171 ResponseTypes: []string{"id_token"},
172 SubjectTypes: []string{"public"},
173 SigningAlgs: getAlgs(keyset),
174 }
175
176 metadataJSON, err := json.Marshal(metadata)
177 if err != nil {
178 return nil, fmt.Errorf("failed to marshal service account issuer metadata: %v", err)
179 }
180
181 return metadataJSON, nil
182 }
183
184
185
186 func openIDKeysetJSON(keys []interface{}) ([]byte, error) {
187 keyset, errs := publicJWKSFromKeys(keys)
188 if errs != nil {
189 return nil, errs
190 }
191
192 keysetJSON, err := json.Marshal(keyset)
193 if err != nil {
194 return nil, fmt.Errorf("failed to marshal service account issuer JWKS: %v", err)
195 }
196
197 return keysetJSON, nil
198 }
199
200 func getAlgs(keys *jose.JSONWebKeySet) []string {
201 algs := sets.NewString()
202 for _, k := range keys.Keys {
203 algs.Insert(k.Algorithm)
204 }
205
206 return algs.List()
207 }
208
209 type publicKeyGetter interface {
210 Public() crypto.PublicKey
211 }
212
213
214
215 func publicJWKSFromKeys(in []interface{}) (*jose.JSONWebKeySet, errors.Aggregate) {
216
217 var keys jose.JSONWebKeySet
218 var errs []error
219 for i, key := range in {
220 var pubkey *jose.JSONWebKey
221 var err error
222
223 switch k := key.(type) {
224 case publicKeyGetter:
225
226 pubkey, err = jwkFromPublicKey(k.Public())
227 default:
228 pubkey, err = jwkFromPublicKey(k)
229 }
230 if err != nil {
231 errs = append(errs, fmt.Errorf("error constructing JWK for key #%d: %v", i, err))
232 continue
233 }
234
235 if !pubkey.Valid() {
236 errs = append(errs, fmt.Errorf("key #%d not valid", i))
237 continue
238 }
239 keys.Keys = append(keys.Keys, *pubkey)
240 }
241 if len(errs) != 0 {
242 return nil, errors.NewAggregate(errs)
243 }
244 return &keys, nil
245 }
246
247 func jwkFromPublicKey(publicKey crypto.PublicKey) (*jose.JSONWebKey, error) {
248 alg, err := algorithmFromPublicKey(publicKey)
249 if err != nil {
250 return nil, err
251 }
252
253 keyID, err := keyIDFromPublicKey(publicKey)
254 if err != nil {
255 return nil, err
256 }
257
258 jwk := &jose.JSONWebKey{
259 Algorithm: string(alg),
260 Key: publicKey,
261 KeyID: keyID,
262 Use: "sig",
263 }
264
265 if !jwk.IsPublic() {
266 return nil, fmt.Errorf("JWK was not a public key! JWK: %v", jwk)
267 }
268
269 return jwk, nil
270 }
271
272 func algorithmFromPublicKey(publicKey crypto.PublicKey) (jose.SignatureAlgorithm, error) {
273 switch pk := publicKey.(type) {
274 case *rsa.PublicKey:
275
276
277
278 return jose.RS256, nil
279 case *ecdsa.PublicKey:
280 switch pk.Curve {
281 case elliptic.P256():
282 return jose.ES256, nil
283 case elliptic.P384():
284 return jose.ES384, nil
285 case elliptic.P521():
286 return jose.ES512, nil
287 default:
288 return "", fmt.Errorf("unknown private key curve, must be 256, 384, or 521")
289 }
290 case jose.OpaqueSigner:
291 return jose.SignatureAlgorithm(pk.Public().Algorithm), nil
292 default:
293 return "", fmt.Errorf("unknown public key type, must be *rsa.PublicKey, *ecdsa.PublicKey, or jose.OpaqueSigner")
294 }
295 }
296
View as plain text