diff --git a/Src/Support/Google.Apis.Auth.Tests/OAuth2/AwsSignedSubjectTokenTests.cs b/Src/Support/Google.Apis.Auth.Tests/OAuth2/AwsSignedSubjectTokenTests.cs new file mode 100644 index 00000000000..43a01c010f7 --- /dev/null +++ b/Src/Support/Google.Apis.Auth.Tests/OAuth2/AwsSignedSubjectTokenTests.cs @@ -0,0 +1,79 @@ +/* +Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +using Google.Apis.Json; +using Google.Apis.Tests.Mocks; +using System; +using Xunit; +using static Google.Apis.Auth.OAuth2.AwsExternalAccountCredential; + +namespace Google.Apis.Auth.Tests.OAuth2 +{ + public class AwsSignedSubjectTokenTests + { + private const string AwsSecretAcessKeyId = "BSIARD4OQDT6ATWAVS5C"; + private const string AwsSecretAccessKey = "CDNMV7GOT4h+PHxB/03/Zh4X7XZFOZRydtoMwsRS"; + private const string AwsToken = "GwoGZXIvYXdzEDQaDAVwVIzhCkoSIt9J6yLeAeVnJt7alj0kfoGSAYzy2uYkeOmLHgz8BxpbiFR1NwKd6QXDo09ZRVSoP1eUI286GXEX5PuY/kX3lctI6L" + + "YWhAxLl7d3xMNqcT+4Pw+aRvLNX6rCs71/gwZzOs7dkktam8j7zy5TWrWDy8JVU61P+iuAg+0gCW6dHzis/rj0ProFQ9SSGD2CpO8CWoF144i+pAUMWLD+2s02YTRFASZX8wVokTv81cMiraH3" + + "FwPzdlXQ6+cYhz4/mvfVRp5RL2BHjMACOo860fmKRpyIi8maaDL8jcVJhWiPkZZkAXVMvSiipNibBjJYih+X9LcsNFAjkSjlhiSBYms4Z9Mrbf5PAaMIIokO7B5/1VsaQjNfKY41DfitS3+VUz" + + "u2iCayn6mysrSsAaWeXZarieae+cWG3ihKq71AhOsDQkWoh8a+zw=="; + private const string AwsRegion = "us-east-2"; + private const string AwsVerificationUrl = "https://sts.us-east-2.amazonaws.com/?Action=GetCallerIdentity&Version=2011-06-15"; + private const string Audience = "//iam.googleapis.com/projects/123456789012/locations/global/workloadIdentityPools/pool-abc123d4/providers/aws-abc123d4"; + private static readonly DateTime SigningTimestampUtc = new DateTime(1987, 5, 4, 6, 36, 45, DateTimeKind.Utc); + + private const string SerializedSubjectToken = "%7B%22url%22%3A%22https%3A%2F%2Fsts.us-east-2.amazonaws.com%2F%3FAction%3DGetCallerIdentity%26Version%3D" + + "2011-06-15%22%2C%22method%22%3A%22POST%22%2C%22body%22%3A%22%22%2C%22headers%22%3A%5B%7B%22key%22%3A%22host%22%2C%22value%22%3A%22sts.us-east-2.am" + + "azonaws.com%22%7D%2C%7B%22key%22%3A%22x-amz-date%22%2C%22value%22%3A%2219870504T063645Z%22%7D%2C%7B%22key%22%3A%22x-goog-cloud-target-resource%22%" + + "2C%22value%22%3A%22%2F%2Fiam.googleapis.com%2Fprojects%2F123456789012%2Flocations%2Fglobal%2FworkloadIdentityPools%2Fpool-abc123d4%2Fproviders%2Fa" + + "ws-abc123d4%22%7D%2C%7B%22key%22%3A%22x-amz-security-token%22%2C%22value%22%3A%22GwoGZXIvYXdzEDQaDAVwVIzhCkoSIt9J6yLeAeVnJt7alj0kfoGSAYzy2uYkeOmLH" + + "gz8BxpbiFR1NwKd6QXDo09ZRVSoP1eUI286GXEX5PuY%2FkX3lctI6LYWhAxLl7d3xMNqcT%2B4Pw%2BaRvLNX6rCs71%2FgwZzOs7dkktam8j7zy5TWrWDy8JVU61P%2BiuAg%2B0gCW6dHzi" + + "s%2Frj0ProFQ9SSGD2CpO8CWoF144i%2BpAUMWLD%2B2s02YTRFASZX8wVokTv81cMiraH3FwPzdlXQ6%2BcYhz4%2FmvfVRp5RL2BHjMACOo860fmKRpyIi8maaDL8jcVJhWiPkZZkAXVMvSi" + + "ipNibBjJYih%2BX9LcsNFAjkSjlhiSBYms4Z9Mrbf5PAaMIIokO7B5%2F1VsaQjNfKY41DfitS3%2BVUzu2iCayn6mysrSsAaWeXZarieae%2BcWG3ihKq71AhOsDQkWoh8a%2Bzw%3D%3D%22" + + "%7D%2C%7B%22key%22%3A%22Authorization%22%2C%22value%22%3A%22AWS4-HMAC-SHA256%20Credential%3DBSIARD4OQDT6ATWAVS5C%2F19870504%2Fus-east-2%2Fsts%2Faw" + + "s4_request%2C%20SignedHeaders%3Dhost%3Bx-amz-date%3Bx-amz-security-token%3Bx-goog-cloud-target-resource%2C%20Signature%3D2e65b3fdfa86ba68f721bd458" + + "4f2fcbf8df9fad14ce3c7ea389c58af17bdc5a0%22%7D%5D%7D"; + + [Fact] + public void Create() + { + // This is the reverse of what we do in production code, but it's harder to open all the implementation up + // for proper testing than to just repeat this step here. This is by far the simplest step of subject + // token generation as compared to building and signing the canonical request, etc. for which we are + // testing production code. + AwsSignedSubjectToken expected = NewtonsoftJsonSerializer.Instance.Deserialize(Uri.UnescapeDataString(SerializedSubjectToken)); + + AwsSignedSubjectToken subjectToken = AwsSignedSubjectToken.Create( + new AwsSecurityCredentials(AwsSecretAcessKeyId, AwsSecretAccessKey, AwsToken), + new AwsRegion(AwsRegion), + new Uri(AwsVerificationUrl), + Audience, + new MockClock(SigningTimestampUtc)); + + Assert.Equal(expected.Url, subjectToken.Url); + Assert.Equal(expected.HttpMethod, subjectToken.HttpMethod); + Assert.Equal(expected.Body, subjectToken.Body); + + Assert.Equal(expected.Headers.Length, subjectToken.Headers.Length); + + foreach(var expectedHeader in expected.Headers) + { + var actual = Assert.Single(subjectToken.Headers, header => header.Key == expectedHeader.Key); + Assert.Equal(expectedHeader.Value, actual.Value); + } + } + } +} diff --git a/Src/Support/Google.Apis.Auth/OAuth2/AwsExternalAccountCredential.AwsMetadataServerClient.cs b/Src/Support/Google.Apis.Auth/OAuth2/AwsExternalAccountCredential.AwsMetadataServerClient.cs index 09a4821768b..f3e30a0cd52 100644 --- a/Src/Support/Google.Apis.Auth/OAuth2/AwsExternalAccountCredential.AwsMetadataServerClient.cs +++ b/Src/Support/Google.Apis.Auth/OAuth2/AwsExternalAccountCredential.AwsMetadataServerClient.cs @@ -28,7 +28,7 @@ public partial class AwsExternalAccountCredential /// Helper class for querying the AWS Metadata Server. /// It will fetch and use the sesion token if required. /// - private sealed class AwsMetadataServerClient + internal sealed class AwsMetadataServerClient { private const string AwsMetadataServerHostV4 = "169.254.169.254"; private const string AwsMetadataServerHostV6 = "fd00:ec2::254"; diff --git a/Src/Support/Google.Apis.Auth/OAuth2/AwsExternalAccountCredential.AwsRegion.cs b/Src/Support/Google.Apis.Auth/OAuth2/AwsExternalAccountCredential.AwsRegion.cs index 4c73faa880a..9861075ea33 100644 --- a/Src/Support/Google.Apis.Auth/OAuth2/AwsExternalAccountCredential.AwsRegion.cs +++ b/Src/Support/Google.Apis.Auth/OAuth2/AwsExternalAccountCredential.AwsRegion.cs @@ -14,9 +14,7 @@ You may obtain a copy of the License at limitations under the License. */ -using Google.Apis.Util; using System; -using System.Threading; using System.Threading.Tasks; namespace Google.Apis.Auth.OAuth2 @@ -26,7 +24,7 @@ public partial class AwsExternalAccountCredential /// /// Helper class to obtain the AWS region. /// - private sealed class AwsRegion + internal sealed class AwsRegion { private static readonly string[] OrderedRegionEnvVars = new string[] { "AWS_REGION", "AWS_DEFAULT_REGION" }; @@ -39,7 +37,7 @@ private sealed class AwsRegion /// internal string Region { get; } - private AwsRegion(string region) => Region = region; + internal AwsRegion(string region) => Region = region; /// /// Fetches the AWS instance region as per https://google.aip.dev/auth/4117#determining-the-subject-token-in-aws. diff --git a/Src/Support/Google.Apis.Auth/OAuth2/AwsExternalAccountCredential.AwsSecurityCredential.cs b/Src/Support/Google.Apis.Auth/OAuth2/AwsExternalAccountCredential.AwsSecurityCredential.cs index 4a10c8ab057..1e541ede3df 100644 --- a/Src/Support/Google.Apis.Auth/OAuth2/AwsExternalAccountCredential.AwsSecurityCredential.cs +++ b/Src/Support/Google.Apis.Auth/OAuth2/AwsExternalAccountCredential.AwsSecurityCredential.cs @@ -28,7 +28,7 @@ public partial class AwsExternalAccountCredential /// Represents AWS security credentials which are used to sign /// the subject token. /// - private sealed class AwsSecurityCredentials + internal sealed class AwsSecurityCredentials { private const string AccessKeyIdEnvVar = "AWS_ACCESS_KEY_ID"; private const string SecretAccessKeyEnvVar = "AWS_SECRET_ACCESS_KEY"; @@ -49,7 +49,7 @@ private sealed class AwsSecurityCredentials /// internal string Token { get; } - private AwsSecurityCredentials(string accessKeyId, string secretAccessKey, string token) + internal AwsSecurityCredentials(string accessKeyId, string secretAccessKey, string token) { AccessKeyId = accessKeyId; SecretAccessKey = secretAccessKey; diff --git a/Src/Support/Google.Apis.Auth/OAuth2/AwsExternalAccountCredential.AwsSignedSubjectToken.cs b/Src/Support/Google.Apis.Auth/OAuth2/AwsExternalAccountCredential.AwsSignedSubjectToken.cs index b49c87f7e43..0bbec69ec80 100644 --- a/Src/Support/Google.Apis.Auth/OAuth2/AwsExternalAccountCredential.AwsSignedSubjectToken.cs +++ b/Src/Support/Google.Apis.Auth/OAuth2/AwsExternalAccountCredential.AwsSignedSubjectToken.cs @@ -34,7 +34,7 @@ public partial class AwsExternalAccountCredential /// access tokens. Google STS triggers the request as specified by the signed /// request to verify the callers identity. /// - private sealed class AwsSignedSubjectToken + internal sealed class AwsSignedSubjectToken { private const string StVerificationHttpMethod = "POST"; private const string BasicFormatIso8601 = "yyyyMMdd'T'HHmmss'Z'"; @@ -43,22 +43,21 @@ private sealed class AwsSignedSubjectToken private const string AwsSha256Designation = "AWS4-HMAC-SHA256"; [JsonProperty("url")] - public string Url { get; } + public string Url { get; set; } [JsonProperty("method")] - public string HttpMethod { get; } + public string HttpMethod { get; set; } [JsonProperty("body")] - public string Body { get; } + public string Body { get; set; } [JsonProperty("headers")] - public AwsSignedSubjectTokenHeader[] Headers { get; } + public AwsSignedSubjectTokenHeader[] Headers { get; set; } + + public AwsSignedSubjectToken() { } private AwsSignedSubjectToken(string url, string httpMethod, string body, AwsSignedSubjectTokenHeader[] headers) { - // We have no public parameterless constructor because - // we build this manually on the Create method and then - // serialize it. Url = url; HttpMethod = httpMethod; Body = body; @@ -68,10 +67,12 @@ private AwsSignedSubjectToken(string url, string httpMethod, string body, AwsSig public sealed class AwsSignedSubjectTokenHeader { [JsonProperty("key")] - public string Key { get; } + public string Key { get; set; } [JsonProperty("value")] - public string Value { get; } + public string Value { get; set; } + + public AwsSignedSubjectTokenHeader() { } internal AwsSignedSubjectTokenHeader(string key, string value) { @@ -144,6 +145,7 @@ string GetHashedCanonicalRequest() canonicalRequest.Append(headerPair.Value); canonicalRequest.Append(newLine); } + canonicalRequest.Append(newLine); // Now the list of headers that will be signed. // See point 5 in https://docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-request.html diff --git a/Src/Support/IntegrationTests/ExternalAccountCredentialTests.cs b/Src/Support/IntegrationTests/ExternalAccountCredentialTests.cs new file mode 100644 index 00000000000..e2875a94a04 --- /dev/null +++ b/Src/Support/IntegrationTests/ExternalAccountCredentialTests.cs @@ -0,0 +1,116 @@ +/* +Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +using Google.Apis.Auth.OAuth2; +using Google.Apis.IAMCredentials.v1; +using Google.Apis.IAMCredentials.v1.Data; +using Google.Apis.Services; +using Google.Apis.Storage.v1; +using Google.Apis.Storage.v1.Data; +using IntegrationTests.Utils; +using System; +using System.Linq; +using System.Net.Http; +using System.Threading.Tasks; +using System.Xml.Linq; +using Xunit; + +namespace IntegrationTests +{ + public class ExternalAccountCredentialTests + { + [Fact(Skip = "Googlers see go/wif-integration-settings")] + public async Task AwsCredentials() + { + string gcpProjectNumber = ""; + string gcpPoolId = "pool-abc123d4"; + + string awsAccountId = ""; + string awsProviderId = "aws-abc123d4"; + string awsRoleName = ""; + string awsAudience = $"//iam.googleapis.com/projects/{gcpProjectNumber}/locations/global/workloadIdentityPools/{gcpPoolId}/providers/{awsProviderId}"; + string awsArn = $"arn:aws:iam::{awsAccountId}:role/{awsRoleName}"; + + // This really won't be running in AWS, so we need to obtain AWS credentials by using the AssumeRoleWithWebIdentity + // operation of AWS STS server. + // For this, we need an identity token to identify ourselves to AWS STS. + GoogleCredential gcpCredential = Helper.GetServiceCredential().CreateScoped("https://www.googleapis.com/auth/cloud-platform"); + string saEmail = (gcpCredential.UnderlyingCredential as ServiceAccountCredential).Id; + string saResourceName = $"projects/-/serviceAccounts/{saEmail}"; + IAMCredentialsService iamService = new IAMCredentialsService(new BaseClientService.Initializer + { + HttpClientInitializer = gcpCredential, + }); + var idToken = await iamService.Projects.ServiceAccounts.GenerateIdToken( + new GenerateIdTokenRequest + { + Audience = awsAudience, + IncludeEmail = true + }, + saResourceName).ExecuteAsync(); + + // Using the id token, we can now obtain AWS credentials. + string assumeRoleUrl = $"https://sts.amazonaws.com/?" + + $"Action=AssumeRoleWithWebIdentity" + + $"&Version=2011-06-15" + + $"&DurationSeconds=3600" + + $"&RoleSessionName={awsRoleName}" + + $"&RoleArn={awsArn}" + + $"&WebIdentityToken={idToken.Token}"; + + string content; + using (HttpClient httpClient = new HttpClient()) + { + HttpResponseMessage response = await httpClient.GetAsync(assumeRoleUrl); + content = await response.Content.ReadAsStringAsync(); + } + XElement awsCredentialXml = XElement.Parse(content); + + // We store AWS credentials in environment variables to mimic an AWS environment. + Environment.SetEnvironmentVariable("AWS_ACCESS_KEY_ID", AwsCredentialValue("AccessKeyId")); + Environment.SetEnvironmentVariable("AWS_SECRET_ACCESS_KEY", AwsCredentialValue("SecretAccessKey")); + Environment.SetEnvironmentVariable("AWS_SESSION_TOKEN", AwsCredentialValue("SessionToken")); + // We store the AWS region in an environment variable to mimic an AWS environment. + Environment.SetEnvironmentVariable("AWS_REGION", "us-east-2"); + + // Now we can create an AWS creential which will pick the relevant values from the environment variables. + AwsExternalAccountCredential awsToGcpCredential = new AwsExternalAccountCredential(new AwsExternalAccountCredential.Initializer( + tokenUrl: "https://sts.googleapis.com/v1/token", + audience: awsAudience, + subjectTokenType: "urn:ietf:params:aws:token-type:aws4_request", + regionalCredentialVerificationUrl: "https://sts.{region}.amazonaws.com?Action=GetCallerIdentity&Version=2011-06-15") + { + ServiceAccountImpersonationUrl = $"https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/{saEmail}:generateAccessToken", + Scopes = new string[] { StorageService.Scope.DevstorageFullControl } + }); + + // And we make a request to Storage to confirm that our AWS credential is indeed valid. + StorageService client = new StorageService(new BaseClientService.Initializer + { + HttpClientInitializer = awsToGcpCredential + }); + + // Following line will throw if authentication fails. + Buckets buckets = client.Buckets.List(Helper.GetProjectId()).Execute(); + + // A final sanity-check. + Assert.NotNull(buckets.Items); + + string AwsCredentialValue(string tagName) => + awsCredentialXml.Descendants().Single(element => element.Name.LocalName == tagName).Value; + } + } +} diff --git a/Src/Support/IntegrationTests/IntegrationTests.Utils/Google.Apis.IAMCredentials.v1.cs b/Src/Support/IntegrationTests/IntegrationTests.Utils/Google.Apis.IAMCredentials.v1.cs new file mode 100644 index 00000000000..42ea7110e2c --- /dev/null +++ b/Src/Support/IntegrationTests/IntegrationTests.Utils/Google.Apis.IAMCredentials.v1.cs @@ -0,0 +1,739 @@ +// Copyright 2022 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Generated code. DO NOT EDIT! + +namespace Google.Apis.IAMCredentials.v1 +{ + /// The IAMCredentials Service. + public class IAMCredentialsService : Google.Apis.Services.BaseClientService + { + /// The API version. + public const string Version = "v1"; + + /// The discovery version used to generate this service. + public static Google.Apis.Discovery.DiscoveryVersion DiscoveryVersionUsed = Google.Apis.Discovery.DiscoveryVersion.Version_1_0; + + /// Constructs a new service. + public IAMCredentialsService() : this(new Google.Apis.Services.BaseClientService.Initializer()) + { + } + + /// Constructs a new service. + /// The service initializer. + public IAMCredentialsService(Google.Apis.Services.BaseClientService.Initializer initializer) : base(initializer) + { + Projects = new ProjectsResource(this); + } + + /// Gets the service supported features. + public override System.Collections.Generic.IList Features => new string[0]; + + /// Gets the service name. + public override string Name => "iamcredentials"; + + /// Gets the service base URI. + public override string BaseUri => + #if NETSTANDARD1_3 || NETSTANDARD2_0 || NET45 + BaseUriOverride ?? "https://iamcredentials.googleapis.com/"; + #else + "https://iamcredentials.googleapis.com/"; + #endif + + /// Gets the service base path. + public override string BasePath => ""; + + #if !NET40 + /// Gets the batch base URI; null if unspecified. + public override string BatchUri => "https://iamcredentials.googleapis.com/batch"; + + /// Gets the batch base path; null if unspecified. + public override string BatchPath => "batch"; + #endif + + /// Available OAuth 2.0 scopes for use with the IAM Service Account Credentials API. + public class Scope + { + /// + /// See, edit, configure, and delete your Google Cloud data and see the email address for your Google + /// Account. + /// + public static string CloudPlatform = "https://www.googleapis.com/auth/cloud-platform"; + } + + /// Available OAuth 2.0 scope constants for use with the IAM Service Account Credentials API. + public static class ScopeConstants + { + /// + /// See, edit, configure, and delete your Google Cloud data and see the email address for your Google + /// Account. + /// + public const string CloudPlatform = "https://www.googleapis.com/auth/cloud-platform"; + } + + /// Gets the Projects resource. + public virtual ProjectsResource Projects { get; } + } + + /// A base abstract class for IAMCredentials requests. + public abstract class IAMCredentialsBaseServiceRequest : Google.Apis.Requests.ClientServiceRequest + { + /// Constructs a new IAMCredentialsBaseServiceRequest instance. + protected IAMCredentialsBaseServiceRequest(Google.Apis.Services.IClientService service) : base(service) + { + } + + /// V1 error format. + [Google.Apis.Util.RequestParameterAttribute("$.xgafv", Google.Apis.Util.RequestParameterType.Query)] + public virtual System.Nullable Xgafv { get; set; } + + /// V1 error format. + public enum XgafvEnum + { + /// v1 error format + [Google.Apis.Util.StringValueAttribute("1")] + Value1 = 0, + + /// v2 error format + [Google.Apis.Util.StringValueAttribute("2")] + Value2 = 1, + } + + /// OAuth access token. + [Google.Apis.Util.RequestParameterAttribute("access_token", Google.Apis.Util.RequestParameterType.Query)] + public virtual string AccessToken { get; set; } + + /// Data format for response. + [Google.Apis.Util.RequestParameterAttribute("alt", Google.Apis.Util.RequestParameterType.Query)] + public virtual System.Nullable Alt { get; set; } + + /// Data format for response. + public enum AltEnum + { + /// Responses with Content-Type of application/json + [Google.Apis.Util.StringValueAttribute("json")] + Json = 0, + + /// Media download with context-dependent Content-Type + [Google.Apis.Util.StringValueAttribute("media")] + Media = 1, + + /// Responses with Content-Type of application/x-protobuf + [Google.Apis.Util.StringValueAttribute("proto")] + Proto = 2, + } + + /// JSONP + [Google.Apis.Util.RequestParameterAttribute("callback", Google.Apis.Util.RequestParameterType.Query)] + public virtual string Callback { get; set; } + + /// Selector specifying which fields to include in a partial response. + [Google.Apis.Util.RequestParameterAttribute("fields", Google.Apis.Util.RequestParameterType.Query)] + public virtual string Fields { get; set; } + + /// + /// 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. + /// + [Google.Apis.Util.RequestParameterAttribute("key", Google.Apis.Util.RequestParameterType.Query)] + public virtual string Key { get; set; } + + /// OAuth 2.0 token for the current user. + [Google.Apis.Util.RequestParameterAttribute("oauth_token", Google.Apis.Util.RequestParameterType.Query)] + public virtual string OauthToken { get; set; } + + /// Returns response with indentations and line breaks. + [Google.Apis.Util.RequestParameterAttribute("prettyPrint", Google.Apis.Util.RequestParameterType.Query)] + public virtual System.Nullable PrettyPrint { get; set; } + + /// + /// 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. + /// + [Google.Apis.Util.RequestParameterAttribute("quotaUser", Google.Apis.Util.RequestParameterType.Query)] + public virtual string QuotaUser { get; set; } + + /// Legacy upload protocol for media (e.g. "media", "multipart"). + [Google.Apis.Util.RequestParameterAttribute("uploadType", Google.Apis.Util.RequestParameterType.Query)] + public virtual string UploadType { get; set; } + + /// Upload protocol for media (e.g. "raw", "multipart"). + [Google.Apis.Util.RequestParameterAttribute("upload_protocol", Google.Apis.Util.RequestParameterType.Query)] + public virtual string UploadProtocol { get; set; } + + /// Initializes IAMCredentials parameter list. + protected override void InitParameters() + { + base.InitParameters(); + RequestParameters.Add("$.xgafv", new Google.Apis.Discovery.Parameter + { + Name = "$.xgafv", + IsRequired = false, + ParameterType = "query", + DefaultValue = null, + Pattern = null, + }); + RequestParameters.Add("access_token", new Google.Apis.Discovery.Parameter + { + Name = "access_token", + IsRequired = false, + ParameterType = "query", + DefaultValue = null, + Pattern = null, + }); + RequestParameters.Add("alt", new Google.Apis.Discovery.Parameter + { + Name = "alt", + IsRequired = false, + ParameterType = "query", + DefaultValue = "json", + Pattern = null, + }); + RequestParameters.Add("callback", new Google.Apis.Discovery.Parameter + { + Name = "callback", + IsRequired = false, + ParameterType = "query", + DefaultValue = null, + Pattern = null, + }); + RequestParameters.Add("fields", new Google.Apis.Discovery.Parameter + { + Name = "fields", + IsRequired = false, + ParameterType = "query", + DefaultValue = null, + Pattern = null, + }); + RequestParameters.Add("key", new Google.Apis.Discovery.Parameter + { + Name = "key", + IsRequired = false, + ParameterType = "query", + DefaultValue = null, + Pattern = null, + }); + RequestParameters.Add("oauth_token", new Google.Apis.Discovery.Parameter + { + Name = "oauth_token", + IsRequired = false, + ParameterType = "query", + DefaultValue = null, + Pattern = null, + }); + RequestParameters.Add("prettyPrint", new Google.Apis.Discovery.Parameter + { + Name = "prettyPrint", + IsRequired = false, + ParameterType = "query", + DefaultValue = "true", + Pattern = null, + }); + RequestParameters.Add("quotaUser", new Google.Apis.Discovery.Parameter + { + Name = "quotaUser", + IsRequired = false, + ParameterType = "query", + DefaultValue = null, + Pattern = null, + }); + RequestParameters.Add("uploadType", new Google.Apis.Discovery.Parameter + { + Name = "uploadType", + IsRequired = false, + ParameterType = "query", + DefaultValue = null, + Pattern = null, + }); + RequestParameters.Add("upload_protocol", new Google.Apis.Discovery.Parameter + { + Name = "upload_protocol", + IsRequired = false, + ParameterType = "query", + DefaultValue = null, + Pattern = null, + }); + } + } + + /// The "projects" collection of methods. + public class ProjectsResource + { + private const string Resource = "projects"; + + /// The service which this resource belongs to. + private readonly Google.Apis.Services.IClientService service; + + /// Constructs a new resource. + public ProjectsResource(Google.Apis.Services.IClientService service) + { + this.service = service; + ServiceAccounts = new ServiceAccountsResource(service); + } + + /// Gets the ServiceAccounts resource. + public virtual ServiceAccountsResource ServiceAccounts { get; } + + /// The "serviceAccounts" collection of methods. + public class ServiceAccountsResource + { + private const string Resource = "serviceAccounts"; + + /// The service which this resource belongs to. + private readonly Google.Apis.Services.IClientService service; + + /// Constructs a new resource. + public ServiceAccountsResource(Google.Apis.Services.IClientService service) + { + this.service = service; + } + + /// Generates an OAuth 2.0 access token for a service account. + /// The body of the request. + /// + /// Required. The resource name of the service account for which the credentials are requested, in the + /// following format: `projects/-/serviceAccounts/{ACCOUNT_EMAIL_OR_UNIQUEID}`. The `-` wildcard character + /// is required; replacing it with a project ID is invalid. + /// + public virtual GenerateAccessTokenRequest GenerateAccessToken(Google.Apis.IAMCredentials.v1.Data.GenerateAccessTokenRequest body, string name) + { + return new GenerateAccessTokenRequest(service, body, name); + } + + /// Generates an OAuth 2.0 access token for a service account. + public class GenerateAccessTokenRequest : IAMCredentialsBaseServiceRequest + { + /// Constructs a new GenerateAccessToken request. + public GenerateAccessTokenRequest(Google.Apis.Services.IClientService service, Google.Apis.IAMCredentials.v1.Data.GenerateAccessTokenRequest body, string name) : base(service) + { + Name = name; + Body = body; + InitParameters(); + } + + /// + /// Required. The resource name of the service account for which the credentials are requested, in the + /// following format: `projects/-/serviceAccounts/{ACCOUNT_EMAIL_OR_UNIQUEID}`. The `-` wildcard + /// character is required; replacing it with a project ID is invalid. + /// + [Google.Apis.Util.RequestParameterAttribute("name", Google.Apis.Util.RequestParameterType.Path)] + public virtual string Name { get; private set; } + + /// Gets or sets the body of this request. + Google.Apis.IAMCredentials.v1.Data.GenerateAccessTokenRequest Body { get; set; } + + /// Returns the body of the request. + protected override object GetBody() => Body; + + /// Gets the method name. + public override string MethodName => "generateAccessToken"; + + /// Gets the HTTP method. + public override string HttpMethod => "POST"; + + /// Gets the REST path. + public override string RestPath => "v1/{+name}:generateAccessToken"; + + /// Initializes GenerateAccessToken parameter list. + protected override void InitParameters() + { + base.InitParameters(); + RequestParameters.Add("name", new Google.Apis.Discovery.Parameter + { + Name = "name", + IsRequired = true, + ParameterType = "path", + DefaultValue = null, + Pattern = @"^projects/[^/]+/serviceAccounts/[^/]+$", + }); + } + } + + /// Generates an OpenID Connect ID token for a service account. + /// The body of the request. + /// + /// Required. The resource name of the service account for which the credentials are requested, in the + /// following format: `projects/-/serviceAccounts/{ACCOUNT_EMAIL_OR_UNIQUEID}`. The `-` wildcard character + /// is required; replacing it with a project ID is invalid. + /// + public virtual GenerateIdTokenRequest GenerateIdToken(Google.Apis.IAMCredentials.v1.Data.GenerateIdTokenRequest body, string name) + { + return new GenerateIdTokenRequest(service, body, name); + } + + /// Generates an OpenID Connect ID token for a service account. + public class GenerateIdTokenRequest : IAMCredentialsBaseServiceRequest + { + /// Constructs a new GenerateIdToken request. + public GenerateIdTokenRequest(Google.Apis.Services.IClientService service, Google.Apis.IAMCredentials.v1.Data.GenerateIdTokenRequest body, string name) : base(service) + { + Name = name; + Body = body; + InitParameters(); + } + + /// + /// Required. The resource name of the service account for which the credentials are requested, in the + /// following format: `projects/-/serviceAccounts/{ACCOUNT_EMAIL_OR_UNIQUEID}`. The `-` wildcard + /// character is required; replacing it with a project ID is invalid. + /// + [Google.Apis.Util.RequestParameterAttribute("name", Google.Apis.Util.RequestParameterType.Path)] + public virtual string Name { get; private set; } + + /// Gets or sets the body of this request. + Google.Apis.IAMCredentials.v1.Data.GenerateIdTokenRequest Body { get; set; } + + /// Returns the body of the request. + protected override object GetBody() => Body; + + /// Gets the method name. + public override string MethodName => "generateIdToken"; + + /// Gets the HTTP method. + public override string HttpMethod => "POST"; + + /// Gets the REST path. + public override string RestPath => "v1/{+name}:generateIdToken"; + + /// Initializes GenerateIdToken parameter list. + protected override void InitParameters() + { + base.InitParameters(); + RequestParameters.Add("name", new Google.Apis.Discovery.Parameter + { + Name = "name", + IsRequired = true, + ParameterType = "path", + DefaultValue = null, + Pattern = @"^projects/[^/]+/serviceAccounts/[^/]+$", + }); + } + } + + /// Signs a blob using a service account's system-managed private key. + /// The body of the request. + /// + /// Required. The resource name of the service account for which the credentials are requested, in the + /// following format: `projects/-/serviceAccounts/{ACCOUNT_EMAIL_OR_UNIQUEID}`. The `-` wildcard character + /// is required; replacing it with a project ID is invalid. + /// + public virtual SignBlobRequest SignBlob(Google.Apis.IAMCredentials.v1.Data.SignBlobRequest body, string name) + { + return new SignBlobRequest(service, body, name); + } + + /// Signs a blob using a service account's system-managed private key. + public class SignBlobRequest : IAMCredentialsBaseServiceRequest + { + /// Constructs a new SignBlob request. + public SignBlobRequest(Google.Apis.Services.IClientService service, Google.Apis.IAMCredentials.v1.Data.SignBlobRequest body, string name) : base(service) + { + Name = name; + Body = body; + InitParameters(); + } + + /// + /// Required. The resource name of the service account for which the credentials are requested, in the + /// following format: `projects/-/serviceAccounts/{ACCOUNT_EMAIL_OR_UNIQUEID}`. The `-` wildcard + /// character is required; replacing it with a project ID is invalid. + /// + [Google.Apis.Util.RequestParameterAttribute("name", Google.Apis.Util.RequestParameterType.Path)] + public virtual string Name { get; private set; } + + /// Gets or sets the body of this request. + Google.Apis.IAMCredentials.v1.Data.SignBlobRequest Body { get; set; } + + /// Returns the body of the request. + protected override object GetBody() => Body; + + /// Gets the method name. + public override string MethodName => "signBlob"; + + /// Gets the HTTP method. + public override string HttpMethod => "POST"; + + /// Gets the REST path. + public override string RestPath => "v1/{+name}:signBlob"; + + /// Initializes SignBlob parameter list. + protected override void InitParameters() + { + base.InitParameters(); + RequestParameters.Add("name", new Google.Apis.Discovery.Parameter + { + Name = "name", + IsRequired = true, + ParameterType = "path", + DefaultValue = null, + Pattern = @"^projects/[^/]+/serviceAccounts/[^/]+$", + }); + } + } + + /// Signs a JWT using a service account's system-managed private key. + /// The body of the request. + /// + /// Required. The resource name of the service account for which the credentials are requested, in the + /// following format: `projects/-/serviceAccounts/{ACCOUNT_EMAIL_OR_UNIQUEID}`. The `-` wildcard character + /// is required; replacing it with a project ID is invalid. + /// + public virtual SignJwtRequest SignJwt(Google.Apis.IAMCredentials.v1.Data.SignJwtRequest body, string name) + { + return new SignJwtRequest(service, body, name); + } + + /// Signs a JWT using a service account's system-managed private key. + public class SignJwtRequest : IAMCredentialsBaseServiceRequest + { + /// Constructs a new SignJwt request. + public SignJwtRequest(Google.Apis.Services.IClientService service, Google.Apis.IAMCredentials.v1.Data.SignJwtRequest body, string name) : base(service) + { + Name = name; + Body = body; + InitParameters(); + } + + /// + /// Required. The resource name of the service account for which the credentials are requested, in the + /// following format: `projects/-/serviceAccounts/{ACCOUNT_EMAIL_OR_UNIQUEID}`. The `-` wildcard + /// character is required; replacing it with a project ID is invalid. + /// + [Google.Apis.Util.RequestParameterAttribute("name", Google.Apis.Util.RequestParameterType.Path)] + public virtual string Name { get; private set; } + + /// Gets or sets the body of this request. + Google.Apis.IAMCredentials.v1.Data.SignJwtRequest Body { get; set; } + + /// Returns the body of the request. + protected override object GetBody() => Body; + + /// Gets the method name. + public override string MethodName => "signJwt"; + + /// Gets the HTTP method. + public override string HttpMethod => "POST"; + + /// Gets the REST path. + public override string RestPath => "v1/{+name}:signJwt"; + + /// Initializes SignJwt parameter list. + protected override void InitParameters() + { + base.InitParameters(); + RequestParameters.Add("name", new Google.Apis.Discovery.Parameter + { + Name = "name", + IsRequired = true, + ParameterType = "path", + DefaultValue = null, + Pattern = @"^projects/[^/]+/serviceAccounts/[^/]+$", + }); + } + } + } + } +} +namespace Google.Apis.IAMCredentials.v1.Data +{ + public class GenerateAccessTokenRequest : Google.Apis.Requests.IDirectResponseSchema + { + /// + /// The sequence of service accounts in a delegation chain. This field is required for [delegated + /// requests](https://cloud.google.com/iam/help/credentials/delegated-request). For [direct + /// requests](https://cloud.google.com/iam/help/credentials/direct-request), which are more common, do not + /// specify this field. Each service account must be granted the `roles/iam.serviceAccountTokenCreator` role on + /// its next service account in the chain. The last service account in the chain must be granted the + /// `roles/iam.serviceAccountTokenCreator` role on the service account that is specified in the `name` field of + /// the request. The delegates must have the following format: + /// `projects/-/serviceAccounts/{ACCOUNT_EMAIL_OR_UNIQUEID}`. The `-` wildcard character is required; replacing + /// it with a project ID is invalid. + /// + [Newtonsoft.Json.JsonPropertyAttribute("delegates")] + public virtual System.Collections.Generic.IList Delegates { get; set; } + + /// + /// The desired lifetime duration of the access token in seconds. By default, the maximum allowed value is 1 + /// hour. To set a lifetime of up to 12 hours, you can add the service account as an allowed value in an + /// Organization Policy that enforces the `constraints/iam.allowServiceAccountCredentialLifetimeExtension` + /// constraint. See detailed instructions at https://cloud.google.com/iam/help/credentials/lifetime If a value + /// is not specified, the token's lifetime will be set to a default value of 1 hour. + /// + [Newtonsoft.Json.JsonPropertyAttribute("lifetime")] + public virtual object Lifetime { get; set; } + + /// + /// Required. Code to identify the scopes to be included in the OAuth 2.0 access token. See + /// https://developers.google.com/identity/protocols/googlescopes for more information. At least one value + /// required. + /// + [Newtonsoft.Json.JsonPropertyAttribute("scope")] + public virtual System.Collections.Generic.IList Scope { get; set; } + + /// The ETag of the item. + public virtual string ETag { get; set; } + } + + public class GenerateAccessTokenResponse : Google.Apis.Requests.IDirectResponseSchema + { + /// The OAuth 2.0 access token. + [Newtonsoft.Json.JsonPropertyAttribute("accessToken")] + public virtual string AccessToken { get; set; } + + /// Token expiration time. The expiration time is always set. + [Newtonsoft.Json.JsonPropertyAttribute("expireTime")] + public virtual object ExpireTime { get; set; } + + /// The ETag of the item. + public virtual string ETag { get; set; } + } + + public class GenerateIdTokenRequest : Google.Apis.Requests.IDirectResponseSchema + { + /// + /// Required. The audience for the token, such as the API or account that this token grants access to. + /// + [Newtonsoft.Json.JsonPropertyAttribute("audience")] + public virtual string Audience { get; set; } + + /// + /// The sequence of service accounts in a delegation chain. Each service account must be granted the + /// `roles/iam.serviceAccountTokenCreator` role on its next service account in the chain. The last service + /// account in the chain must be granted the `roles/iam.serviceAccountTokenCreator` role on the service account + /// that is specified in the `name` field of the request. The delegates must have the following format: + /// `projects/-/serviceAccounts/{ACCOUNT_EMAIL_OR_UNIQUEID}`. The `-` wildcard character is required; replacing + /// it with a project ID is invalid. + /// + [Newtonsoft.Json.JsonPropertyAttribute("delegates")] + public virtual System.Collections.Generic.IList Delegates { get; set; } + + /// + /// Include the service account email in the token. If set to `true`, the token will contain `email` and + /// `email_verified` claims. + /// + [Newtonsoft.Json.JsonPropertyAttribute("includeEmail")] + public virtual System.Nullable IncludeEmail { get; set; } + + /// The ETag of the item. + public virtual string ETag { get; set; } + } + + public class GenerateIdTokenResponse : Google.Apis.Requests.IDirectResponseSchema + { + /// The OpenId Connect ID token. + [Newtonsoft.Json.JsonPropertyAttribute("token")] + public virtual string Token { get; set; } + + /// The ETag of the item. + public virtual string ETag { get; set; } + } + + public class SignBlobRequest : Google.Apis.Requests.IDirectResponseSchema + { + /// + /// The sequence of service accounts in a delegation chain. Each service account must be granted the + /// `roles/iam.serviceAccountTokenCreator` role on its next service account in the chain. The last service + /// account in the chain must be granted the `roles/iam.serviceAccountTokenCreator` role on the service account + /// that is specified in the `name` field of the request. The delegates must have the following format: + /// `projects/-/serviceAccounts/{ACCOUNT_EMAIL_OR_UNIQUEID}`. The `-` wildcard character is required; replacing + /// it with a project ID is invalid. + /// + [Newtonsoft.Json.JsonPropertyAttribute("delegates")] + public virtual System.Collections.Generic.IList Delegates { get; set; } + + /// Required. The bytes to sign. + [Newtonsoft.Json.JsonPropertyAttribute("payload")] + public virtual string Payload { get; set; } + + /// The ETag of the item. + public virtual string ETag { get; set; } + } + + public class SignBlobResponse : Google.Apis.Requests.IDirectResponseSchema + { + /// + /// The ID of the key used to sign the blob. The key used for signing will remain valid for at least 12 hours + /// after the blob is signed. To verify the signature, you can retrieve the public key in several formats from + /// the following endpoints: - RSA public key wrapped in an X.509 v3 certificate: + /// `https://www.googleapis.com/service_accounts/v1/metadata/x509/{ACCOUNT_EMAIL}` - Raw key in JSON format: + /// `https://www.googleapis.com/service_accounts/v1/metadata/raw/{ACCOUNT_EMAIL}` - JSON Web Key (JWK): + /// `https://www.googleapis.com/service_accounts/v1/metadata/jwk/{ACCOUNT_EMAIL}` + /// + [Newtonsoft.Json.JsonPropertyAttribute("keyId")] + public virtual string KeyId { get; set; } + + /// + /// The signature for the blob. Does not include the original blob. After the key pair referenced by the + /// `key_id` response field expires, Google no longer exposes the public key that can be used to verify the + /// blob. As a result, the receiver can no longer verify the signature. + /// + [Newtonsoft.Json.JsonPropertyAttribute("signedBlob")] + public virtual string SignedBlob { get; set; } + + /// The ETag of the item. + public virtual string ETag { get; set; } + } + + public class SignJwtRequest : Google.Apis.Requests.IDirectResponseSchema + { + /// + /// The sequence of service accounts in a delegation chain. Each service account must be granted the + /// `roles/iam.serviceAccountTokenCreator` role on its next service account in the chain. The last service + /// account in the chain must be granted the `roles/iam.serviceAccountTokenCreator` role on the service account + /// that is specified in the `name` field of the request. The delegates must have the following format: + /// `projects/-/serviceAccounts/{ACCOUNT_EMAIL_OR_UNIQUEID}`. The `-` wildcard character is required; replacing + /// it with a project ID is invalid. + /// + [Newtonsoft.Json.JsonPropertyAttribute("delegates")] + public virtual System.Collections.Generic.IList Delegates { get; set; } + + /// + /// Required. The JWT payload to sign. Must be a serialized JSON object that contains a JWT Claims Set. For + /// example: `{"sub": "user@example.com", "iat": 313435}` If the JWT Claims Set contains an expiration time + /// (`exp`) claim, it must be an integer timestamp that is not in the past and no more than 12 hours in the + /// future. + /// + [Newtonsoft.Json.JsonPropertyAttribute("payload")] + public virtual string Payload { get; set; } + + /// The ETag of the item. + public virtual string ETag { get; set; } + } + + public class SignJwtResponse : Google.Apis.Requests.IDirectResponseSchema + { + /// + /// The ID of the key used to sign the JWT. The key used for signing will remain valid for at least 12 hours + /// after the JWT is signed. To verify the signature, you can retrieve the public key in several formats from + /// the following endpoints: - RSA public key wrapped in an X.509 v3 certificate: + /// `https://www.googleapis.com/service_accounts/v1/metadata/x509/{ACCOUNT_EMAIL}` - Raw key in JSON format: + /// `https://www.googleapis.com/service_accounts/v1/metadata/raw/{ACCOUNT_EMAIL}` - JSON Web Key (JWK): + /// `https://www.googleapis.com/service_accounts/v1/metadata/jwk/{ACCOUNT_EMAIL}` + /// + [Newtonsoft.Json.JsonPropertyAttribute("keyId")] + public virtual string KeyId { get; set; } + + /// + /// The signed JWT. Contains the automatically generated header; the client-supplied payload; and the signature, + /// which is generated using the key referenced by the `kid` field in the header. After the key pair referenced + /// by the `key_id` response field expires, Google no longer exposes the public key that can be used to verify + /// the JWT. As a result, the receiver can no longer verify the signature. + /// + [Newtonsoft.Json.JsonPropertyAttribute("signedJwt")] + public virtual string SignedJwt { get; set; } + + /// The ETag of the item. + public virtual string ETag { get; set; } + } +} diff --git a/WorkloadIdentityFederationSetup.sh b/WorkloadIdentityFederationSetup.sh new file mode 100644 index 00000000000..76c9a4f83b2 --- /dev/null +++ b/WorkloadIdentityFederationSetup.sh @@ -0,0 +1,127 @@ +#!/bin/bash + +set -e + +# This script is used to generate the project configurations needed to +# end-to-end test workload identity pools in the Auth library, specifically +# OIDC-based credentials and AWS credentials. This script only needs to be run once. +# +# In order to run this script, the GOOGLE_APPLICATION_CREDENTIALS environment +# variable needs to be set to point to a service account key file. +# Additional fields must be provided in this file. +# Detailed instructions are documented below. +# +# GCP project changes: +# -------------------- +# The following IAM roles need to be set on the service account: +# 1. IAM Workload Identity Pool Admin (needed to create resources for workload +# identity pools). +# 2. Security Admin (needed to get and set IAM policies). +# 3. Service Account Token Creator (needed to generate Google ID tokens and +# access tokens). +# +# The following APIs need to be enabled on the project: +# 1. Identity and Access Management (IAM) API. +# 2. IAM Service Account Credentials API. +# 3. Cloud Resource Manager API. +# 4. The API being accessed in the test, eg. DNS. +# +# AWS developer account changes: +# ------------------------------ +# For testing AWS credentials, the following are needed: +# 1. An AWS developer account is needed. The account ID will need to +# be provided in the configuration object below. +# 2. A role for web identity federation. This will also need to be provided +# in the configuration object below. +# - In AWS, an OIDC Google identity provider needs to be created with the following: +# issuer: accounts.google.com +# audience: Use the client_id of the service account. +# - A role for OIDC web identity federation is needed with the created +# Google provider as a trusted entity: +# "accounts.google.com:aud": "$CLIENT_ID" +# The role creation steps are documented at: +# https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_create_for-idp_oidc.html +# +# This script needs to be run once. It will do the following: +# 1. Create a random workload identity pool. +# 2. Create a random OIDC provider in that pool which uses the +# 3. Enable OIDC tokens generated by the current service account to impersonate +# the service account. (Identified by the OIDC token sub field which is the +# service account client ID). +# 4. Create a random AWS provider in that pool which uses the provided AWS +# account ID. +# 5. Enable AWS provider to impersonate the service account. (Principal is +# identified by the AWS role name). +# 6. Print out the STS audience fields associated with the created providers +# and AWS role name/arn after the setup completes successfully so that +# they can be used in the tests. +# +# The same service account used for this setup script should be used for +# the test script. +# +# It is safe to run the setup script again. A new pool is created and new +# audiences are printed. If run multiple times, it is advisable to delete +# unused pools. Note that deleted pools are soft deleted and may remain for +# a while before they are completely deleted. The old pool ID cannot be used +# in the meantime. + +suffix="" + +function generate_random_string () { + local valid_chars=abcdefghijklmnopqrstuvwxyz0123456789 + for i in {1..8} ; do + suffix+="${valid_chars:RANDOM%${#valid_chars}:1}" + done +} + +generate_random_string + +pool_id="pool-"${suffix} +oidc_provider_id="oidc-"${suffix} +aws_provider_id="aws-"${suffix} + +# Fill in (Googlers, see go/wif-integration-settings). +project_id="" +project_number="" +aws_account_id="" +aws_role_name="" +service_account_email="" +sub=""; # client_id from service account key file + +oidc_aud="//iam.googleapis.com/projects/${project_number}/locations/global/workloadIdentityPools/${pool_id}/providers/${oidc_provider_id}" +aws_aud="//iam.googleapis.com/projects/${project_number}/locations/global/workloadIdentityPools/${pool_id}/providers/${aws_provider_id}" + +gcloud config set project ${project_id} + +# Create the Workload Identity Pool. +gcloud iam workload-identity-pools create ${pool_id} \ + --location="global" \ + --description="Test pool" \ + --display-name="Test pool for .NET" + +# Create the OIDC Provider. +gcloud iam workload-identity-pools providers create-oidc ${oidc_provider_id} \ + --workload-identity-pool=${pool_id} \ + --issuer-uri="https://accounts.google.com" \ + --location="global" \ + --attribute-mapping="google.subject=assertion.sub" + +# Create the AWS Provider. +gcloud iam workload-identity-pools providers create-aws ${aws_provider_id} \ + --workload-identity-pool=${pool_id} \ + --account-id=${aws_account_id} \ + --location="global" + +# Give permission to impersonate the service account. +gcloud iam service-accounts add-iam-policy-binding ${service_account_email} \ +--role roles/iam.workloadIdentityUser \ +--member "principal://iam.googleapis.com/projects/${project_number}/locations/global/workloadIdentityPools/${pool_id}/subject/${sub}" + +gcloud iam service-accounts add-iam-policy-binding ${service_account_email} \ + --role roles/iam.workloadIdentityUser \ + --member "principalSet://iam.googleapis.com/projects/${project_number}/locations/global/workloadIdentityPools/${pool_id}/attribute.aws_role/arn:aws:sts::${aws_account_id}:assumed-role/${aws_role_name}" + +echo "OIDC audience:"${oidc_aud} +echo "AWS audience:"${aws_aud} +echo "AWS role name:"${aws_role_name} +echo "AWS role ARN: arn:aws:iam::${aws_account_id}:role/${aws_role_name}" \ No newline at end of file