Skip to content

Commit 0827344

Browse files
authored
Merge pull request smallstep#845 from vijayjt/azure-user-mi-token
WIP: Support Azure tokens generated by managed identities
2 parents f3bade4 + 24a9637 commit 0827344

File tree

3 files changed

+26
-17
lines changed

3 files changed

+26
-17
lines changed

Diff for: authority/provisioner/azure.go

+6-3
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ const azureDefaultAudience = "https://management.azure.com/"
3030

3131
// azureXMSMirIDRegExp is the regular expression used to parse the xms_mirid claim.
3232
// Using case insensitive as resourceGroups appears as resourcegroups.
33-
var azureXMSMirIDRegExp = regexp.MustCompile(`(?i)^/subscriptions/([^/]+)/resourceGroups/([^/]+)/providers/Microsoft.Compute/virtualMachines/([^/]+)$`)
33+
var azureXMSMirIDRegExp = regexp.MustCompile(`(?i)^/subscriptions/([^/]+)/resourceGroups/([^/]+)/providers/Microsoft.(Compute/virtualMachines|ManagedIdentity/userAssignedIdentities)/([^/]+)$`)
3434

3535
type azureConfig struct {
3636
oidcDiscoveryURL string
@@ -260,11 +260,14 @@ func (p *Azure) authorizeToken(token string) (*azurePayload, string, string, str
260260
}
261261

262262
re := azureXMSMirIDRegExp.FindStringSubmatch(claims.XMSMirID)
263-
if len(re) != 4 {
263+
if len(re) != 5 {
264264
return nil, "", "", "", "", errs.Unauthorized("azure.authorizeToken; error parsing xms_mirid claim - %s", claims.XMSMirID)
265265
}
266+
267+
var subscription, group, name string
266268
identityObjectID := claims.ObjectID
267-
subscription, group, name := re[1], re[2], re[3]
269+
subscription, group, name = re[1], re[2], re[4]
270+
268271
return &claims, name, group, subscription, identityObjectID, nil
269272
}
270273

Diff for: authority/provisioner/azure_test.go

+11-11
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ func TestAzure_GetIdentityToken(t *testing.T) {
9595
assert.FatalError(t, err)
9696

9797
t1, err := generateAzureToken("subject", p1.oidcConfig.Issuer, azureDefaultAudience,
98-
p1.TenantID, "subscriptionID", "resourceGroup", "virtualMachine",
98+
p1.TenantID, "subscriptionID", "resourceGroup", "virtualMachine", "vm",
9999
time.Now(), &p1.keyStore.keySet.Keys[0])
100100
assert.FatalError(t, err)
101101

@@ -237,7 +237,7 @@ func TestAzure_authorizeToken(t *testing.T) {
237237
jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0)
238238
assert.FatalError(t, err)
239239
tok, err := generateAzureToken("subject", p.oidcConfig.Issuer, azureDefaultAudience,
240-
p.TenantID, "subscriptionID", "resourceGroup", "virtualMachine",
240+
p.TenantID, "subscriptionID", "resourceGroup", "virtualMachine", "vm",
241241
time.Now(), jwk)
242242
assert.FatalError(t, err)
243243
return test{
@@ -252,7 +252,7 @@ func TestAzure_authorizeToken(t *testing.T) {
252252
assert.FatalError(t, err)
253253
defer srv.Close()
254254
tok, err := generateAzureToken("subject", "bad-issuer", azureDefaultAudience,
255-
p.TenantID, "subscriptionID", "resourceGroup", "virtualMachine",
255+
p.TenantID, "subscriptionID", "resourceGroup", "virtualMachine", "vm",
256256
time.Now(), &p.keyStore.keySet.Keys[0])
257257
assert.FatalError(t, err)
258258
return test{
@@ -267,7 +267,7 @@ func TestAzure_authorizeToken(t *testing.T) {
267267
assert.FatalError(t, err)
268268
defer srv.Close()
269269
tok, err := generateAzureToken("subject", p.oidcConfig.Issuer, azureDefaultAudience,
270-
"foo", "subscriptionID", "resourceGroup", "virtualMachine",
270+
"foo", "subscriptionID", "resourceGroup", "virtualMachine", "vm",
271271
time.Now(), &p.keyStore.keySet.Keys[0])
272272
assert.FatalError(t, err)
273273
return test{
@@ -321,7 +321,7 @@ func TestAzure_authorizeToken(t *testing.T) {
321321
assert.FatalError(t, err)
322322
defer srv.Close()
323323
tok, err := generateAzureToken("subject", p.oidcConfig.Issuer, azureDefaultAudience,
324-
p.TenantID, "subscriptionID", "resourceGroup", "virtualMachine",
324+
p.TenantID, "subscriptionID", "resourceGroup", "virtualMachine", "vm",
325325
time.Now(), &p.keyStore.keySet.Keys[0])
326326
assert.FatalError(t, err)
327327
return test{
@@ -437,28 +437,28 @@ func TestAzure_AuthorizeSign(t *testing.T) {
437437
assert.FatalError(t, err)
438438

439439
t11, err := generateAzureToken("subject", p1.oidcConfig.Issuer, azureDefaultAudience,
440-
p1.TenantID, "subscriptionID", "resourceGroup", "virtualMachine",
440+
p1.TenantID, "subscriptionID", "resourceGroup", "virtualMachine", "vm",
441441
time.Now(), &p1.keyStore.keySet.Keys[0])
442442
assert.FatalError(t, err)
443443

444444
failIssuer, err := generateAzureToken("subject", "bad-issuer", azureDefaultAudience,
445-
p1.TenantID, "subscriptionID", "resourceGroup", "virtualMachine",
445+
p1.TenantID, "subscriptionID", "resourceGroup", "virtualMachine", "vm",
446446
time.Now(), &p1.keyStore.keySet.Keys[0])
447447
assert.FatalError(t, err)
448448
failAudience, err := generateAzureToken("subject", p1.oidcConfig.Issuer, "bad-audience",
449-
p1.TenantID, "subscriptionID", "resourceGroup", "virtualMachine",
449+
p1.TenantID, "subscriptionID", "resourceGroup", "virtualMachine", "vm",
450450
time.Now(), &p1.keyStore.keySet.Keys[0])
451451
assert.FatalError(t, err)
452452
failExp, err := generateAzureToken("subject", p1.oidcConfig.Issuer, azureDefaultAudience,
453-
p1.TenantID, "subscriptionID", "resourceGroup", "virtualMachine",
453+
p1.TenantID, "subscriptionID", "resourceGroup", "virtualMachine", "vm",
454454
time.Now().Add(-360*time.Second), &p1.keyStore.keySet.Keys[0])
455455
assert.FatalError(t, err)
456456
failNbf, err := generateAzureToken("subject", p1.oidcConfig.Issuer, azureDefaultAudience,
457-
p1.TenantID, "subscriptionID", "resourceGroup", "virtualMachine",
457+
p1.TenantID, "subscriptionID", "resourceGroup", "virtualMachine", "vm",
458458
time.Now().Add(360*time.Second), &p1.keyStore.keySet.Keys[0])
459459
assert.FatalError(t, err)
460460
failKey, err := generateAzureToken("subject", p1.oidcConfig.Issuer, azureDefaultAudience,
461-
p1.TenantID, "subscriptionID", "resourceGroup", "virtualMachine",
461+
p1.TenantID, "subscriptionID", "resourceGroup", "virtualMachine", "vm",
462462
time.Now(), badKey)
463463
assert.FatalError(t, err)
464464

Diff for: authority/provisioner/utils_test.go

+9-3
Original file line numberDiff line numberDiff line change
@@ -656,7 +656,7 @@ func generateAzureWithServer() (*Azure, *httptest.Server, error) {
656656
w.Header().Add("Cache-Control", "max-age=5")
657657
writeJSON(w, getPublic(az.keyStore.keySet))
658658
case "/metadata/identity/oauth2/token":
659-
tok, err := generateAzureToken("subject", issuer, "https://management.azure.com/", az.TenantID, "subscriptionID", "resourceGroup", "virtualMachine", time.Now(), &az.keyStore.keySet.Keys[0])
659+
tok, err := generateAzureToken("subject", issuer, "https://management.azure.com/", az.TenantID, "subscriptionID", "resourceGroup", "virtualMachine", "vm", time.Now(), &az.keyStore.keySet.Keys[0])
660660
if err != nil {
661661
http.Error(w, err.Error(), http.StatusInternalServerError)
662662
} else {
@@ -994,14 +994,20 @@ func generateAWSToken(p *AWS, sub, iss, aud, accountID, instanceID, privateIP, r
994994
return jose.Signed(sig).Claims(claims).CompactSerialize()
995995
}
996996

997-
func generateAzureToken(sub, iss, aud, tenantID, subscriptionID, resourceGroup, virtualMachine string, iat time.Time, jwk *jose.JSONWebKey) (string, error) {
997+
func generateAzureToken(sub, iss, aud, tenantID, subscriptionID, resourceGroup, resourceName, resourceType string, iat time.Time, jwk *jose.JSONWebKey) (string, error) {
998998
sig, err := jose.NewSigner(
999999
jose.SigningKey{Algorithm: jose.ES256, Key: jwk.Key},
10001000
new(jose.SignerOptions).WithType("JWT").WithHeader("kid", jwk.KeyID),
10011001
)
10021002
if err != nil {
10031003
return "", err
10041004
}
1005+
var xmsMirID string
1006+
if resourceType == "vm" {
1007+
xmsMirID = fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Compute/virtualMachines/%s", subscriptionID, resourceGroup, resourceName)
1008+
} else if resourceType == "uai" {
1009+
xmsMirID = fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers/Microsoft.ManagedIdentity/userAssignedIdentities/%s", subscriptionID, resourceGroup, resourceName)
1010+
}
10051011

10061012
claims := azurePayload{
10071013
Claims: jose.Claims{
@@ -1019,7 +1025,7 @@ func generateAzureToken(sub, iss, aud, tenantID, subscriptionID, resourceGroup,
10191025
ObjectID: "the-oid",
10201026
TenantID: tenantID,
10211027
Version: "the-version",
1022-
XMSMirID: fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Compute/virtualMachines/%s", subscriptionID, resourceGroup, virtualMachine),
1028+
XMSMirID: xmsMirID,
10231029
}
10241030
return jose.Signed(sig).Claims(claims).CompactSerialize()
10251031
}

0 commit comments

Comments
 (0)