Update Agent Auth with GCP to use new SignJWT endpoint (#11473)

* Update Agent Auth with GCP to use new SignJWT endpoint

* use iamcredentials name instead of renaming the package on import

* add changelog

* Update changelog/11473.txt

Co-authored-by: Theron Voran <tvoran@users.noreply.github.com>

Co-authored-by: Theron Voran <tvoran@users.noreply.github.com>
This commit is contained in:
Clint 2021-04-30 15:45:06 -05:00 committed by GitHub
parent fcb9bab51b
commit 59870ee0d3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 1506 additions and 12 deletions

4
changelog/11473.txt Normal file
View File

@ -0,0 +1,4 @@
```release-note:change
agent: Update to use IAM Service Account Credentials endpoint for signing JWTs
when using GCP Auto-Auth method
```

View File

@ -17,7 +17,7 @@ import (
"github.com/hashicorp/vault/command/agent/auth" "github.com/hashicorp/vault/command/agent/auth"
"github.com/hashicorp/vault/sdk/helper/parseutil" "github.com/hashicorp/vault/sdk/helper/parseutil"
"golang.org/x/oauth2" "golang.org/x/oauth2"
iam "google.golang.org/api/iam/v1" "google.golang.org/api/iamcredentials/v1"
) )
const ( const (
@ -161,7 +161,7 @@ func (g *gcpMethod) Authenticate(ctx context.Context, client *api.Client) (retPa
default: default:
ctx := context.WithValue(context.Background(), oauth2.HTTPClient, cleanhttp.DefaultClient()) ctx := context.WithValue(context.Background(), oauth2.HTTPClient, cleanhttp.DefaultClient())
credentials, tokenSource, err := gcputil.FindCredentials(g.credentials, ctx, iam.CloudPlatformScope) credentials, tokenSource, err := gcputil.FindCredentials(g.credentials, ctx, iamcredentials.CloudPlatformScope)
if err != nil { if err != nil {
retErr = errwrap.Wrapf("could not obtain credentials: {{err}}", err) retErr = errwrap.Wrapf("could not obtain credentials: {{err}}", err)
return return
@ -180,13 +180,6 @@ func (g *gcpMethod) Authenticate(ctx context.Context, client *api.Client) (retPa
return return
} }
project := "-"
if g.project != "" {
project = g.project
} else if credentials != nil {
project = credentials.ProjectId
}
ttlMin := int64(defaultIamMaxJwtExpMinutes) ttlMin := int64(defaultIamMaxJwtExpMinutes)
if g.jwtExp != 0 { if g.jwtExp != 0 {
ttlMin = g.jwtExp ttlMin = g.jwtExp
@ -204,17 +197,17 @@ func (g *gcpMethod) Authenticate(ctx context.Context, client *api.Client) (retPa
return return
} }
jwtReq := &iam.SignJwtRequest{ jwtReq := &iamcredentials.SignJwtRequest{
Payload: string(payloadBytes), Payload: string(payloadBytes),
} }
iamClient, err := iam.New(httpClient) iamClient, err := iamcredentials.New(httpClient)
if err != nil { if err != nil {
retErr = errwrap.Wrapf("could not create IAM client: {{err}}", err) retErr = errwrap.Wrapf("could not create IAM client: {{err}}", err)
return return
} }
resourceName := fmt.Sprintf("projects/%s/serviceAccounts/%s", project, serviceAccount) resourceName := fmt.Sprintf("projects/-/serviceAccounts/%s", serviceAccount)
resp, err := iamClient.Projects.ServiceAccounts.SignJwt(resourceName, jwtReq).Do() resp, err := iamClient.Projects.ServiceAccounts.SignJwt(resourceName, jwtReq).Do()
if err != nil { if err != nil {
retErr = errwrap.Wrapf(fmt.Sprintf("unable to sign JWT for %s using given Vault credentials: {{err}}", resourceName), err) retErr = errwrap.Wrapf(fmt.Sprintf("unable to sign JWT for %s using given Vault credentials: {{err}}", resourceName), err)

View File

@ -0,0 +1,372 @@
{
"auth": {
"oauth2": {
"scopes": {
"https://www.googleapis.com/auth/cloud-platform": {
"description": "View and manage your data across Google Cloud Platform services"
}
}
}
},
"basePath": "",
"baseUrl": "https://iamcredentials.googleapis.com/",
"batchPath": "batch",
"canonicalName": "IAM Credentials",
"description": "Creates short-lived credentials for impersonating IAM service accounts.",
"discoveryVersion": "v1",
"documentationLink": "https://cloud.google.com/iam/docs/creating-short-lived-service-account-credentials",
"fullyEncodeReservedExpansion": true,
"icons": {
"x16": "http://www.google.com/images/icons/product/search-16.gif",
"x32": "http://www.google.com/images/icons/product/search-32.gif"
},
"id": "iamcredentials:v1",
"kind": "discovery#restDescription",
"mtlsRootUrl": "https://iamcredentials.mtls.googleapis.com/",
"name": "iamcredentials",
"ownerDomain": "google.com",
"ownerName": "Google",
"parameters": {
"$.xgafv": {
"description": "V1 error format.",
"enum": [
"1",
"2"
],
"enumDescriptions": [
"v1 error format",
"v2 error format"
],
"location": "query",
"type": "string"
},
"access_token": {
"description": "OAuth access token.",
"location": "query",
"type": "string"
},
"alt": {
"default": "json",
"description": "Data format for response.",
"enum": [
"json",
"media",
"proto"
],
"enumDescriptions": [
"Responses with Content-Type of application/json",
"Media download with context-dependent Content-Type",
"Responses with Content-Type of application/x-protobuf"
],
"location": "query",
"type": "string"
},
"callback": {
"description": "JSONP",
"location": "query",
"type": "string"
},
"fields": {
"description": "Selector specifying which fields to include in a partial response.",
"location": "query",
"type": "string"
},
"key": {
"description": "API key. Your API key identifies your project and provides you with API access, quota, and reports. Required unless you provide an OAuth 2.0 token.",
"location": "query",
"type": "string"
},
"oauth_token": {
"description": "OAuth 2.0 token for the current user.",
"location": "query",
"type": "string"
},
"prettyPrint": {
"default": "true",
"description": "Returns response with indentations and line breaks.",
"location": "query",
"type": "boolean"
},
"quotaUser": {
"description": "Available to use for quota purposes for server-side applications. Can be any arbitrary string assigned to a user, but should not exceed 40 characters.",
"location": "query",
"type": "string"
},
"uploadType": {
"description": "Legacy upload protocol for media (e.g. \"media\", \"multipart\").",
"location": "query",
"type": "string"
},
"upload_protocol": {
"description": "Upload protocol for media (e.g. \"raw\", \"multipart\").",
"location": "query",
"type": "string"
}
},
"protocol": "rest",
"resources": {
"projects": {
"resources": {
"serviceAccounts": {
"methods": {
"generateAccessToken": {
"description": "Generates an OAuth 2.0 access token for a service account.",
"flatPath": "v1/projects/{projectsId}/serviceAccounts/{serviceAccountsId}:generateAccessToken",
"httpMethod": "POST",
"id": "iamcredentials.projects.serviceAccounts.generateAccessToken",
"parameterOrder": [
"name"
],
"parameters": {
"name": {
"description": "Required. The resource name of the service account for which the credentials\nare requested, in the following format:\n`projects/-/serviceAccounts/{ACCOUNT_EMAIL_OR_UNIQUEID}`. The `-` wildcard\ncharacter is required; replacing it with a project ID is invalid.",
"location": "path",
"pattern": "^projects/[^/]+/serviceAccounts/[^/]+$",
"required": true,
"type": "string"
}
},
"path": "v1/{+name}:generateAccessToken",
"request": {
"$ref": "GenerateAccessTokenRequest"
},
"response": {
"$ref": "GenerateAccessTokenResponse"
},
"scopes": [
"https://www.googleapis.com/auth/cloud-platform"
]
},
"generateIdToken": {
"description": "Generates an OpenID Connect ID token for a service account.",
"flatPath": "v1/projects/{projectsId}/serviceAccounts/{serviceAccountsId}:generateIdToken",
"httpMethod": "POST",
"id": "iamcredentials.projects.serviceAccounts.generateIdToken",
"parameterOrder": [
"name"
],
"parameters": {
"name": {
"description": "Required. The resource name of the service account for which the credentials\nare requested, in the following format:\n`projects/-/serviceAccounts/{ACCOUNT_EMAIL_OR_UNIQUEID}`. The `-` wildcard\ncharacter is required; replacing it with a project ID is invalid.",
"location": "path",
"pattern": "^projects/[^/]+/serviceAccounts/[^/]+$",
"required": true,
"type": "string"
}
},
"path": "v1/{+name}:generateIdToken",
"request": {
"$ref": "GenerateIdTokenRequest"
},
"response": {
"$ref": "GenerateIdTokenResponse"
},
"scopes": [
"https://www.googleapis.com/auth/cloud-platform"
]
},
"signBlob": {
"description": "Signs a blob using a service account's system-managed private key.",
"flatPath": "v1/projects/{projectsId}/serviceAccounts/{serviceAccountsId}:signBlob",
"httpMethod": "POST",
"id": "iamcredentials.projects.serviceAccounts.signBlob",
"parameterOrder": [
"name"
],
"parameters": {
"name": {
"description": "Required. The resource name of the service account for which the credentials\nare requested, in the following format:\n`projects/-/serviceAccounts/{ACCOUNT_EMAIL_OR_UNIQUEID}`. The `-` wildcard\ncharacter is required; replacing it with a project ID is invalid.",
"location": "path",
"pattern": "^projects/[^/]+/serviceAccounts/[^/]+$",
"required": true,
"type": "string"
}
},
"path": "v1/{+name}:signBlob",
"request": {
"$ref": "SignBlobRequest"
},
"response": {
"$ref": "SignBlobResponse"
},
"scopes": [
"https://www.googleapis.com/auth/cloud-platform"
]
},
"signJwt": {
"description": "Signs a JWT using a service account's system-managed private key.",
"flatPath": "v1/projects/{projectsId}/serviceAccounts/{serviceAccountsId}:signJwt",
"httpMethod": "POST",
"id": "iamcredentials.projects.serviceAccounts.signJwt",
"parameterOrder": [
"name"
],
"parameters": {
"name": {
"description": "Required. The resource name of the service account for which the credentials\nare requested, in the following format:\n`projects/-/serviceAccounts/{ACCOUNT_EMAIL_OR_UNIQUEID}`. The `-` wildcard\ncharacter is required; replacing it with a project ID is invalid.",
"location": "path",
"pattern": "^projects/[^/]+/serviceAccounts/[^/]+$",
"required": true,
"type": "string"
}
},
"path": "v1/{+name}:signJwt",
"request": {
"$ref": "SignJwtRequest"
},
"response": {
"$ref": "SignJwtResponse"
},
"scopes": [
"https://www.googleapis.com/auth/cloud-platform"
]
}
}
}
}
}
},
"revision": "20200612",
"rootUrl": "https://iamcredentials.googleapis.com/",
"schemas": {
"GenerateAccessTokenRequest": {
"id": "GenerateAccessTokenRequest",
"properties": {
"delegates": {
"description": "The sequence of service accounts in a delegation chain. Each service\naccount must be granted the `roles/iam.serviceAccountTokenCreator` role\non its next service account in the chain. The last service account in the\nchain must be granted the `roles/iam.serviceAccountTokenCreator` role\non the service account that is specified in the `name` field of the\nrequest.\n\nThe delegates must have the following format:\n`projects/-/serviceAccounts/{ACCOUNT_EMAIL_OR_UNIQUEID}`. The `-` wildcard\ncharacter is required; replacing it with a project ID is invalid.",
"items": {
"type": "string"
},
"type": "array"
},
"lifetime": {
"description": "The desired lifetime duration of the access token in seconds.\nMust be set to a value less than or equal to 3600 (1 hour). If a value is\nnot specified, the token's lifetime will be set to a default value of one\nhour.",
"format": "google-duration",
"type": "string"
},
"scope": {
"description": "Required. Code to identify the scopes to be included in the OAuth 2.0 access token.\nSee https://developers.google.com/identity/protocols/googlescopes for more\ninformation.\nAt least one value required.",
"items": {
"type": "string"
},
"type": "array"
}
},
"type": "object"
},
"GenerateAccessTokenResponse": {
"id": "GenerateAccessTokenResponse",
"properties": {
"accessToken": {
"description": "The OAuth 2.0 access token.",
"type": "string"
},
"expireTime": {
"description": "Token expiration time.\nThe expiration time is always set.",
"format": "google-datetime",
"type": "string"
}
},
"type": "object"
},
"GenerateIdTokenRequest": {
"id": "GenerateIdTokenRequest",
"properties": {
"audience": {
"description": "Required. The audience for the token, such as the API or account that this token\ngrants access to.",
"type": "string"
},
"delegates": {
"description": "The sequence of service accounts in a delegation chain. Each service\naccount must be granted the `roles/iam.serviceAccountTokenCreator` role\non its next service account in the chain. The last service account in the\nchain must be granted the `roles/iam.serviceAccountTokenCreator` role\non the service account that is specified in the `name` field of the\nrequest.\n\nThe delegates must have the following format:\n`projects/-/serviceAccounts/{ACCOUNT_EMAIL_OR_UNIQUEID}`. The `-` wildcard\ncharacter is required; replacing it with a project ID is invalid.",
"items": {
"type": "string"
},
"type": "array"
},
"includeEmail": {
"description": "Include the service account email in the token. If set to `true`, the\ntoken will contain `email` and `email_verified` claims.",
"type": "boolean"
}
},
"type": "object"
},
"GenerateIdTokenResponse": {
"id": "GenerateIdTokenResponse",
"properties": {
"token": {
"description": "The OpenId Connect ID token.",
"type": "string"
}
},
"type": "object"
},
"SignBlobRequest": {
"id": "SignBlobRequest",
"properties": {
"delegates": {
"description": "The sequence of service accounts in a delegation chain. Each service\naccount must be granted the `roles/iam.serviceAccountTokenCreator` role\non its next service account in the chain. The last service account in the\nchain must be granted the `roles/iam.serviceAccountTokenCreator` role\non the service account that is specified in the `name` field of the\nrequest.\n\nThe delegates must have the following format:\n`projects/-/serviceAccounts/{ACCOUNT_EMAIL_OR_UNIQUEID}`. The `-` wildcard\ncharacter is required; replacing it with a project ID is invalid.",
"items": {
"type": "string"
},
"type": "array"
},
"payload": {
"description": "Required. The bytes to sign.",
"format": "byte",
"type": "string"
}
},
"type": "object"
},
"SignBlobResponse": {
"id": "SignBlobResponse",
"properties": {
"keyId": {
"description": "The ID of the key used to sign the blob. The key used for signing will\nremain valid for at least 12 hours after the blob is signed. To verify the\nsignature, you can retrieve the public key in several formats from the\nfollowing endpoints:\n\n- RSA public key wrapped in an X.509 v3 certificate:\n`https://www.googleapis.com/service_accounts/v1/metadata/x509/{ACCOUNT_EMAIL}`\n- Raw key in JSON format:\n`https://www.googleapis.com/service_accounts/v1/metadata/raw/{ACCOUNT_EMAIL}`\n- JSON Web Key (JWK):\n`https://www.googleapis.com/service_accounts/v1/metadata/jwk/{ACCOUNT_EMAIL}`",
"type": "string"
},
"signedBlob": {
"description": "The signature for the blob. Does not include the original blob.\n\nAfter the key pair referenced by the `key_id` response field expires,\nGoogle no longer exposes the public key that can be used to verify the\nblob. As a result, the receiver can no longer verify the signature.",
"format": "byte",
"type": "string"
}
},
"type": "object"
},
"SignJwtRequest": {
"id": "SignJwtRequest",
"properties": {
"delegates": {
"description": "The sequence of service accounts in a delegation chain. Each service\naccount must be granted the `roles/iam.serviceAccountTokenCreator` role\non its next service account in the chain. The last service account in the\nchain must be granted the `roles/iam.serviceAccountTokenCreator` role\non the service account that is specified in the `name` field of the\nrequest.\n\nThe delegates must have the following format:\n`projects/-/serviceAccounts/{ACCOUNT_EMAIL_OR_UNIQUEID}`. The `-` wildcard\ncharacter is required; replacing it with a project ID is invalid.",
"items": {
"type": "string"
},
"type": "array"
},
"payload": {
"description": "Required. The JWT payload to sign. Must be a serialized JSON object that contains a\nJWT Claims Set. For example: `{\"sub\": \"user@example.com\", \"iat\": 313435}`\n\nIf the JWT Claims Set contains an expiration time (`exp`) claim, it must be\nan integer timestamp that is not in the past and no more than 12 hours in\nthe future.",
"type": "string"
}
},
"type": "object"
},
"SignJwtResponse": {
"id": "SignJwtResponse",
"properties": {
"keyId": {
"description": "The ID of the key used to sign the JWT. The key used for signing will\nremain valid for at least 12 hours after the JWT is signed. To verify the\nsignature, you can retrieve the public key in several formats from the\nfollowing endpoints:\n\n- RSA public key wrapped in an X.509 v3 certificate:\n`https://www.googleapis.com/service_accounts/v1/metadata/x509/{ACCOUNT_EMAIL}`\n- Raw key in JSON format:\n`https://www.googleapis.com/service_accounts/v1/metadata/raw/{ACCOUNT_EMAIL}`\n- JSON Web Key (JWK):\n`https://www.googleapis.com/service_accounts/v1/metadata/jwk/{ACCOUNT_EMAIL}`",
"type": "string"
},
"signedJwt": {
"description": "The signed JWT. Contains the automatically generated header; the\nclient-supplied payload; and the signature, which is generated using the\nkey referenced by the `kid` field in the header.\n\nAfter the key pair referenced by the `key_id` response field expires,\nGoogle no longer exposes the public key that can be used to verify the JWT.\nAs a result, the receiver can no longer verify the signature.",
"type": "string"
}
},
"type": "object"
}
},
"servicePath": "",
"title": "IAM Service Account Credentials API",
"version": "v1",
"version_module": true
}

File diff suppressed because it is too large Load Diff

1
vendor/modules.txt vendored
View File

@ -1441,6 +1441,7 @@ google.golang.org/api/compute/v1
google.golang.org/api/googleapi google.golang.org/api/googleapi
google.golang.org/api/googleapi/transport google.golang.org/api/googleapi/transport
google.golang.org/api/iam/v1 google.golang.org/api/iam/v1
google.golang.org/api/iamcredentials/v1
google.golang.org/api/internal google.golang.org/api/internal
google.golang.org/api/internal/gensupport google.golang.org/api/internal/gensupport
google.golang.org/api/internal/third_party/uritemplates google.golang.org/api/internal/third_party/uritemplates