forked from TrueCloudLab/certificates
Support Azure tokens from managed identities not associated with a VM
This commit is contained in:
parent
76ea1635a7
commit
7b605b2d16
3 changed files with 31 additions and 17 deletions
|
@ -30,7 +30,7 @@ const azureDefaultAudience = "https://management.azure.com/"
|
|||
|
||||
// azureXMSMirIDRegExp is the regular expression used to parse the xms_mirid claim.
|
||||
// Using case insensitive as resourceGroups appears as resourcegroups.
|
||||
var azureXMSMirIDRegExp = regexp.MustCompile(`(?i)^/subscriptions/([^/]+)/resourceGroups/([^/]+)/providers/Microsoft.Compute/virtualMachines/([^/]+)$`)
|
||||
var azureXMSMirIDRegExp = regexp.MustCompile(`(?i)^/subscriptions/([^/]+)/resourceGroups/([^/]+)/providers/Microsoft.(Compute/virtualMachines|ManagedIdentity/userAssignedIdentities)/([^/]+)$`)
|
||||
|
||||
type azureConfig struct {
|
||||
oidcDiscoveryURL string
|
||||
|
@ -260,11 +260,19 @@ func (p *Azure) authorizeToken(token string) (*azurePayload, string, string, str
|
|||
}
|
||||
|
||||
re := azureXMSMirIDRegExp.FindStringSubmatch(claims.XMSMirID)
|
||||
if len(re) != 4 {
|
||||
if len(re) != 5 {
|
||||
return nil, "", "", "", "", errs.Unauthorized("azure.authorizeToken; error parsing xms_mirid claim - %s", claims.XMSMirID)
|
||||
}
|
||||
|
||||
var subscription, group, name string
|
||||
identityObjectID := claims.ObjectID
|
||||
subscription, group, name := re[1], re[2], re[3]
|
||||
|
||||
if strings.Contains(claims.XMSMirID, "virtualMachines") {
|
||||
subscription, group, name = re[1], re[2], re[4]
|
||||
} else {
|
||||
// This is not a VM resource ID so we don't have the VM name so set that to the empty string
|
||||
subscription, group, name = re[1], re[2], ""
|
||||
}
|
||||
return &claims, name, group, subscription, identityObjectID, nil
|
||||
}
|
||||
|
||||
|
|
|
@ -95,7 +95,7 @@ func TestAzure_GetIdentityToken(t *testing.T) {
|
|||
assert.FatalError(t, err)
|
||||
|
||||
t1, err := generateAzureToken("subject", p1.oidcConfig.Issuer, azureDefaultAudience,
|
||||
p1.TenantID, "subscriptionID", "resourceGroup", "virtualMachine",
|
||||
p1.TenantID, "subscriptionID", "resourceGroup", "virtualMachine", "vm",
|
||||
time.Now(), &p1.keyStore.keySet.Keys[0])
|
||||
assert.FatalError(t, err)
|
||||
|
||||
|
@ -237,7 +237,7 @@ func TestAzure_authorizeToken(t *testing.T) {
|
|||
jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0)
|
||||
assert.FatalError(t, err)
|
||||
tok, err := generateAzureToken("subject", p.oidcConfig.Issuer, azureDefaultAudience,
|
||||
p.TenantID, "subscriptionID", "resourceGroup", "virtualMachine",
|
||||
p.TenantID, "subscriptionID", "resourceGroup", "virtualMachine", "vm",
|
||||
time.Now(), jwk)
|
||||
assert.FatalError(t, err)
|
||||
return test{
|
||||
|
@ -252,7 +252,7 @@ func TestAzure_authorizeToken(t *testing.T) {
|
|||
assert.FatalError(t, err)
|
||||
defer srv.Close()
|
||||
tok, err := generateAzureToken("subject", "bad-issuer", azureDefaultAudience,
|
||||
p.TenantID, "subscriptionID", "resourceGroup", "virtualMachine",
|
||||
p.TenantID, "subscriptionID", "resourceGroup", "virtualMachine", "vm",
|
||||
time.Now(), &p.keyStore.keySet.Keys[0])
|
||||
assert.FatalError(t, err)
|
||||
return test{
|
||||
|
@ -267,7 +267,7 @@ func TestAzure_authorizeToken(t *testing.T) {
|
|||
assert.FatalError(t, err)
|
||||
defer srv.Close()
|
||||
tok, err := generateAzureToken("subject", p.oidcConfig.Issuer, azureDefaultAudience,
|
||||
"foo", "subscriptionID", "resourceGroup", "virtualMachine",
|
||||
"foo", "subscriptionID", "resourceGroup", "virtualMachine", "vm",
|
||||
time.Now(), &p.keyStore.keySet.Keys[0])
|
||||
assert.FatalError(t, err)
|
||||
return test{
|
||||
|
@ -321,7 +321,7 @@ func TestAzure_authorizeToken(t *testing.T) {
|
|||
assert.FatalError(t, err)
|
||||
defer srv.Close()
|
||||
tok, err := generateAzureToken("subject", p.oidcConfig.Issuer, azureDefaultAudience,
|
||||
p.TenantID, "subscriptionID", "resourceGroup", "virtualMachine",
|
||||
p.TenantID, "subscriptionID", "resourceGroup", "virtualMachine", "vm",
|
||||
time.Now(), &p.keyStore.keySet.Keys[0])
|
||||
assert.FatalError(t, err)
|
||||
return test{
|
||||
|
@ -437,28 +437,28 @@ func TestAzure_AuthorizeSign(t *testing.T) {
|
|||
assert.FatalError(t, err)
|
||||
|
||||
t11, err := generateAzureToken("subject", p1.oidcConfig.Issuer, azureDefaultAudience,
|
||||
p1.TenantID, "subscriptionID", "resourceGroup", "virtualMachine",
|
||||
p1.TenantID, "subscriptionID", "resourceGroup", "virtualMachine", "vm",
|
||||
time.Now(), &p1.keyStore.keySet.Keys[0])
|
||||
assert.FatalError(t, err)
|
||||
|
||||
failIssuer, err := generateAzureToken("subject", "bad-issuer", azureDefaultAudience,
|
||||
p1.TenantID, "subscriptionID", "resourceGroup", "virtualMachine",
|
||||
p1.TenantID, "subscriptionID", "resourceGroup", "virtualMachine", "vm",
|
||||
time.Now(), &p1.keyStore.keySet.Keys[0])
|
||||
assert.FatalError(t, err)
|
||||
failAudience, err := generateAzureToken("subject", p1.oidcConfig.Issuer, "bad-audience",
|
||||
p1.TenantID, "subscriptionID", "resourceGroup", "virtualMachine",
|
||||
p1.TenantID, "subscriptionID", "resourceGroup", "virtualMachine", "vm",
|
||||
time.Now(), &p1.keyStore.keySet.Keys[0])
|
||||
assert.FatalError(t, err)
|
||||
failExp, err := generateAzureToken("subject", p1.oidcConfig.Issuer, azureDefaultAudience,
|
||||
p1.TenantID, "subscriptionID", "resourceGroup", "virtualMachine",
|
||||
p1.TenantID, "subscriptionID", "resourceGroup", "virtualMachine", "vm",
|
||||
time.Now().Add(-360*time.Second), &p1.keyStore.keySet.Keys[0])
|
||||
assert.FatalError(t, err)
|
||||
failNbf, err := generateAzureToken("subject", p1.oidcConfig.Issuer, azureDefaultAudience,
|
||||
p1.TenantID, "subscriptionID", "resourceGroup", "virtualMachine",
|
||||
p1.TenantID, "subscriptionID", "resourceGroup", "virtualMachine", "vm",
|
||||
time.Now().Add(360*time.Second), &p1.keyStore.keySet.Keys[0])
|
||||
assert.FatalError(t, err)
|
||||
failKey, err := generateAzureToken("subject", p1.oidcConfig.Issuer, azureDefaultAudience,
|
||||
p1.TenantID, "subscriptionID", "resourceGroup", "virtualMachine",
|
||||
p1.TenantID, "subscriptionID", "resourceGroup", "virtualMachine", "vm",
|
||||
time.Now(), badKey)
|
||||
assert.FatalError(t, err)
|
||||
|
||||
|
|
|
@ -656,7 +656,7 @@ func generateAzureWithServer() (*Azure, *httptest.Server, error) {
|
|||
w.Header().Add("Cache-Control", "max-age=5")
|
||||
writeJSON(w, getPublic(az.keyStore.keySet))
|
||||
case "/metadata/identity/oauth2/token":
|
||||
tok, err := generateAzureToken("subject", issuer, "https://management.azure.com/", az.TenantID, "subscriptionID", "resourceGroup", "virtualMachine", time.Now(), &az.keyStore.keySet.Keys[0])
|
||||
tok, err := generateAzureToken("subject", issuer, "https://management.azure.com/", az.TenantID, "subscriptionID", "resourceGroup", "virtualMachine", "vm", time.Now(), &az.keyStore.keySet.Keys[0])
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
} else {
|
||||
|
@ -994,7 +994,7 @@ func generateAWSToken(p *AWS, sub, iss, aud, accountID, instanceID, privateIP, r
|
|||
return jose.Signed(sig).Claims(claims).CompactSerialize()
|
||||
}
|
||||
|
||||
func generateAzureToken(sub, iss, aud, tenantID, subscriptionID, resourceGroup, virtualMachine string, iat time.Time, jwk *jose.JSONWebKey) (string, error) {
|
||||
func generateAzureToken(sub, iss, aud, tenantID, subscriptionID, resourceGroup, resourceName string, resourceType string, iat time.Time, jwk *jose.JSONWebKey) (string, error) {
|
||||
sig, err := jose.NewSigner(
|
||||
jose.SigningKey{Algorithm: jose.ES256, Key: jwk.Key},
|
||||
new(jose.SignerOptions).WithType("JWT").WithHeader("kid", jwk.KeyID),
|
||||
|
@ -1002,6 +1002,12 @@ func generateAzureToken(sub, iss, aud, tenantID, subscriptionID, resourceGroup,
|
|||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
var xmsMirID string
|
||||
if resourceType == "vm" {
|
||||
xmsMirID = fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Compute/virtualMachines/%s", subscriptionID, resourceGroup, resourceName)
|
||||
} else if resourceType == "uai" {
|
||||
xmsMirID = fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers/Microsoft.ManagedIdentity/userAssignedIdentities/%s", subscriptionID, resourceGroup, resourceName)
|
||||
}
|
||||
|
||||
claims := azurePayload{
|
||||
Claims: jose.Claims{
|
||||
|
@ -1019,7 +1025,7 @@ func generateAzureToken(sub, iss, aud, tenantID, subscriptionID, resourceGroup,
|
|||
ObjectID: "the-oid",
|
||||
TenantID: tenantID,
|
||||
Version: "the-version",
|
||||
XMSMirID: fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Compute/virtualMachines/%s", subscriptionID, resourceGroup, virtualMachine),
|
||||
XMSMirID: xmsMirID,
|
||||
}
|
||||
return jose.Signed(sig).Claims(claims).CompactSerialize()
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue