{"id":21354853,"url":"https://github.com/salrashid123/gce_metadata_server","last_synced_at":"2025-04-05T23:08:45.575Z","repository":{"id":56595857,"uuid":"65086178","full_name":"salrashid123/gce_metadata_server","owner":"salrashid123","description":"Simple emulator for the Google Compute Engine Metadata Server","archived":false,"fork":false,"pushed_at":"2025-03-26T16:18:36.000Z","size":12548,"stargazers_count":89,"open_issues_count":0,"forks_count":21,"subscribers_count":6,"default_branch":"master","last_synced_at":"2025-03-29T22:06:55.790Z","etag":null,"topics":["authentication","emulator","gcp","golang","google-cloud-run","google-compute-engine","kubernetes"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/salrashid123.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2016-08-06T14:26:45.000Z","updated_at":"2025-03-26T16:18:40.000Z","dependencies_parsed_at":"2023-10-14T22:45:38.770Z","dependency_job_id":"fca39427-6aed-4a8a-9db0-accff4f3516b","html_url":"https://github.com/salrashid123/gce_metadata_server","commit_stats":null,"previous_names":[],"tags_count":30,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/salrashid123%2Fgce_metadata_server","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/salrashid123%2Fgce_metadata_server/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/salrashid123%2Fgce_metadata_server/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/salrashid123%2Fgce_metadata_server/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/salrashid123","download_url":"https://codeload.github.com/salrashid123/gce_metadata_server/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247411234,"owners_count":20934653,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["authentication","emulator","gcp","golang","google-cloud-run","google-compute-engine","kubernetes"],"created_at":"2024-11-22T04:14:45.941Z","updated_at":"2025-04-05T23:08:45.564Z","avatar_url":"https://github.com/salrashid123.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# GCE Metadata Server Emulator\n\nThis script acts as a GCE's internal metadata server.\n\nIt returns a live `access_token` that can be used directly by [Application Default Credentials](https://developers.google.com/identity/protocols/application-default-credentials) from any SDK library or return any GCE metadata key-value pairs and attributes.\n\nFor example, you can call `ADC` using default credentials or specifically with `ComputeCredentials` and also recall any GCE project or instance attribute.\n\nTo use, first run the emulator:\n\n```bash\n./gce_metadata_server -logtostderr --configFile=config.json \\\n  -alsologtostderr -v 5 \\\n  -port :8080 \\\n  --serviceAccountFile certs/metadata-sa.json \n```\n\nNote the credentials for the server can be sourced from a service account key, workload federation, `Trusted Platform Module (TPM)` or statically provided as environment variable.  The example above uses a key.\n\nThen in a new window, export some env vars google SDK's understands\n\n```bash\nexport GCE_METADATA_HOST=localhost:8080\nexport GCE_METADATA_IP=127.0.0.1:8080\n```\n\nand run any application using ADC:\n\n```python\n#!/usr/bin/python\n\nfrom google.cloud import storage\nimport google.auth\nimport google.auth.compute_engine\nimport google.auth.transport.requests\nfrom google.auth.compute_engine import _metadata\n\n\n## with ADC metadata server\n\ncredentials, project = google.auth.default()    \nclient = storage.Client(credentials=credentials)\nbuckets = client.list_buckets()\nfor bkt in buckets:\n  print(bkt)\n\n\n## as compute credential\n\ncreds = google.auth.compute_engine.Credentials()\nsession = google.auth.transport.requests.AuthorizedSession(creds)\nr = session.get('https://www.googleapis.com/userinfo/v2/me').json()\nprint(str(r))\n\n\n## get arbitrary metadata values directly \n\nrequest = google.auth.transport.requests.Request()\nprint(_metadata.get_project_id(request))\nprint(_metadata.get(request,\"instance/id\"))\n```\n\nYou can also launch the metadata server directly from your app or use in unit tests:\n\n```golang\npackage main\n\nimport (\n  \tmds \"github.com/salrashid123/gce_metadata_server\"    \n)\n\nfunc TestSomething(t *testing.T) {\n\n  // use any any credentials (static, real or fake)\n  creds, _ := google.FindDefaultCredentials(ctx, \"https://www.googleapis.com/auth/cloud-platform\")\n\n  serverConfig := \u0026mds.ServerConfig{\n\t\tBindInterface: \"127.0.0.1\",\n\t\tPort:          \":8080\",\n  }\n\n  claims := \u0026mds.Claims{\n\t\tComputeMetadata: mds.ComputeMetadata{\n\t\t\tV1: mds.V1{\n\t\t\t\tProject: mds.Project{\n\t\t\t\t\tProjectID: \"some_project_id\",\n\t\t\t\t},\n\t\t\t},\n\t\t},\n  }\n\n  f, _ := mds.NewMetadataServer(ctx, serverConfig, creds, claims)\n\n  err = f.Start()\n  defer f.Shutdown()\n\n  // optionally set a env var google sdk libraries understand\n  // t.Setenv(\"GCE_METADATA_HOST\", \"127.0.0.1:8080\")\n  // do tests here, eg with \"cloud.google.com/go/compute/metadata\"\n  // mid, _ := metadata.ProjectID()\n\n  // or call it directly\n  // client := \u0026http.Client{}\n  // req, _ := http.NewRequest(http.MethodGet, \"http://127.0.0.1:8080/computeMetadata/v1/project/project-id\", nil)\n  // req.Header.Set(\"Metadata-Flavor\", \"Google\")\n  // res, _ := client.Do(req)  \n}\n```\n\nThe metadata server supports additional endpoints that simulate other instance attributes normally only visible inside a GCE instance like `instance_id`, `disks`, `network-interfaces` and so on.\n\nFor more information on the request-response characteristics:\n* [GCE Metadata Server](https://cloud.google.com/compute/docs/storing-retrieving-metadata)\n* [Predefined metadata keys](https://cloud.google.com/compute/docs/metadata/predefined-metadata-keys)\n* [Set and remove custom metadata](https://cloud.google.com/compute/docs/metadata/setting-custom-metadata)\n\n The script performs the following:\n * returns the `access_token` and `id_token` provided by either\n   * the serviceAccount JSON file you specify.\n   * [workload identity federation](https://cloud.google.com/iam/docs/how-to#using-workload-identity-federation) configuration\n   * service account impersonation\n   * statically from a provided environment variable\n   * service account RSA key on `HSM` or `Trusted Platform Module (TPM)`\n * return project attributes (`project_id`, `numeric-project-id`)\n * return instance attributes (`instance-id`, `tags`, `network-interfaces`, `disks`)\n\nYou can run the emulator:\n\n1.  directly on your laptop\n2.  within a docker container locally.\n3.  as a kubernetes service\n4.  with some difficulty, bound to the link-local address (`169.254.169.254`)\n5.  within unit tests\n\nThe endpoints that are exposed are:\n\n ```golang\nr.Handle(\"/computeMetadata/v1/project/project-id\")\nr.Handle(\"/computeMetadata/v1/project/numeric-project-id\")\nr.Handle(\"/computeMetadata/v1/project/attributes/{key}\")\n\nr.Handle(\"/computeMetadata/v1/instance/service-accounts/\")\nr.Handle(\"/computeMetadata/v1/instance/service-accounts/{acct}/\")\nr.Handle(\"/computeMetadata/v1/instance/service-accounts/{acct}/{key}\")\nr.Handle(\"/computeMetadata/v1/instance/network-interfaces/{index}/access-configs/{index2}/{key}\")\nr.Handle(\"/computeMetadata/v1/instance/attributes/{key}\")\nr.Handle(\"/computeMetadata/v1/instance/{key}\")\nr.Handle(\"/\")\n```\n\n---\n\n\u003e\u003e This is not an officially supported Google product\n\n---\n\n* [Configuration](#configuration)\n  - [With JSON ServiceAccount file](#with-json-serviceaccount-file)\n  - [With Impersonation](#with-impersonation)\n  - [With Workload Federation](#with-workload-federation)\n  - [With TPM](#with-trusted-platform-module-tpm)\n* [Usage](#usage)      \n* [Startup](#startup)\n  - [AccessToken](#accesstoken)\n  - [IDToken](#idtoken)\n  - [Attributes](#attributes)\n* [Using Google Auth clients](#using-google-auth-clients)\n  - [python](#python)\n  - [java](#java)\n  - [golang](#golang)\n  - [nodejs](#nodejs) \n  - [dotnet](#dotnet)  \n  - [gcloud](#gcloud)      \n* [Other Runtimes](#other-runtimes)\n    - [Run emulator as container](#run-emulator-as-container)    \n    - [Run with containers](#run-with-containers)\n    - [Running as Kubernetes Service](#running-as-kubernetes-service)\n    - [Static environment variables](#static-environment-variables)\n* [Dynamic Configuration File Updates](#dynamic-configuration-file-updates)\n* [ETag](#etag)    \n* [Extending the sample](#extending-the-sample)\n* [Using link-local address](#using-link-local-address)\n* [Using domain sockets](#using-domain-sockets)\n* [Building with Bazel](#building-with-bazel)\n* [Building with Kaniko](#building-with-kaniko)\n* [Verify Release Binary](#verify-release-binary)\n* [Verify Release Binary with github Attestation](#verify-release-binary-with-github-attestation)\n* [Verify Container Image Signature](#verify-container-image-signature)\n* [GCE mTLS](#gce-mtls)\n* [Envoy Authentication Filter](#envoy-authentication-filter)  \n* [Metrics](#metrics)\n* [Testing](#testing)\n\n---\n\nNote, the real metadata server has some additional query parameters which are either partially or not implemented:\n\n- [recursive=true](https://cloud.google.com/compute/docs/metadata/querying-metadata#aggcontents) partially implemented\n- [?alt=json](https://cloud.google.com/compute/docs/metadata/querying-metadata#format_query_output) not implemented\n- [?wait_for_change=true](https://cloud.google.com/compute/docs/metadata/querying-metadata#waitforchange) not implemented\n\nYou are free to expand on the endpoints surfaced here..pls feel free to file a PR!\n\n ![images/metadata_proxy.png](images/metadata_proxy.png)\n\n---\n\n## Configuration \n\nThe metadata server reads a configuration file for static values and uses a service account for dynamically getting `access_token` and `id_token`.\n\nThe basic config file format roughly maps the uri path of the actual metadata server and the emulator uses these values to populate responses.\n\nFor example, the `instance_id`, `project_id`, `serviceAccountEmail` and other files are read from the values here, for example, see [config.json](config.json):\n\n```json\n{\n  \"computeMetadata\": {\n    \"v1\": {\n      \"instance\": {\n        \"id\": 5775171277418378000,\n        \"serviceAccounts\": {\n          \"default\": {\n            \"aliases\": [\n              \"default\"\n            ],\n            \"email\": \"metadata-sa@your-project.iam.gserviceaccount.com\",\n            \"scopes\": [\n              \"https://www.googleapis.com/auth/cloud-platform\",\n              \"https://www.googleapis.com/auth/userinfo.email\"\n            ]\n          }\n        }\n      },\n      \"oslogin\": {},\n      \"project\": {\n        \"numericProjectId\": 708288290784,\n        \"projectId\": \"your-project\"\n      }\n    }\n  }\n}\n```\n\nThe field are basically a JSON representation of what the real metadata server returns recursively\n\n```bash\n$ curl -v -H 'Metadata-Flavor: Google' http://metadata/computeMetadata/v1/?recursive=true | jq '.'\n```\n\nAny requests for an `access_token` or an `id_token` are dynamically generated using the credential provided.  The scopes for any token uses the values set in the config file\n\n## Usage\n\nThe following steps details how you can run the emulator on your laptop.\n\nYou can either build from source:\n\n```bash\ngo build -o gce_metadata_server cmd/main.go\n```\n\nOr download an appropriate binary from the [Releases](https://github.com/salrashid123/gce_metadata_server/releases) page\n\nYou can set the following options on usage:\n\n| Option | Description |\n|:------------|-------------|\n| **`-configFile`** | configuration File (default: `config.json`) |\n| **`-interface`** | interface to bind to (default: `127.0.0.1`) |\n| **`-port`** | port to listen on (default: `:8080`) |\n| **`-serviceAccountFile`** | path to serviceAccount json Key file |\n| **`-impersonate`** | use impersonation |\n| **`-federate`** | use workload identity federation |\n| **`-tpm`** | use TPM |\n| **`-persistentHandle`** | TPM persistentHandle (default: none) |\n| **`-tpmKeyFile`** | TPM Encrypted private key (default: none) |\n| **`-tpmPath`** |\"Path to the TPM device (character device or a Unix socket). (default: `/dev/tpmrm0`)\" |\n| **`-parentPass`** | TPM Parent key password (default: \"\") |\n| **`-keyPass`** | TPM key password (default: \"\") |\n| **`-pcrs`** | TPM PCR values the key is bound to (comma separated pcrs in ascending order) |\n| **`--tpm-session-encrypt-with-name`** | hex encoded TPM object 'name' to use with an encrypted session |\n| **`-domainsocket`** | listen on unix socket |\n| **`GOOGLE_PROJECT_ID`** | static environment variable for PROJECT_ID to return |\n| **`GOOGLE_NUMERIC_PROJECT_ID`** | static environment variable for the numeric project id to return |\n| **`GOOGLE_ACCESS_TOKEN`** | static environment variable for access_token to return |\n| **`GOOGLE_ID_TOKEN`** | static environment variable for id_token to return |\n| **`-metricsEnabled`** | Enable prometheus metrics endpoint (default: false) |\n| **`-metricsInterface`** | Prometheus metrics interface (default: 127.0.0.1) |\n| **`-metricsPort`** | Prometheus metrics port (default: 9000) |\n| **`-metricsPath`** | Prometheus metrics path (default: /metrics) |\n| **`-usemTLS`** | Start server with mtls (default: false) |\n| **`-rootCAmTLS`** | Root CA for mtls client validation (default: `certs/root.crt`) |\n| **`-serverCert`** | Server certificate for mtls (default: `certs/server.crt`) |\n| **`-serverKey`** | Server key for mtls (default: `certs/server.key`) |\n| **`-version`** | Print version |\n\n### With JSON ServiceAccount file\n\nCreate a GCP Service Account JSON file (you should strongly prefer using impersonation..)\n\n```bash\nexport PROJECT_ID=`gcloud config get-value core/project`\ngcloud iam service-accounts create metadata-sa\n```\n\nYou can either create a key that represents this service account and download it locally\n\n```bash\ngcloud iam service-accounts keys create metadata-sa.json \\\n   --iam-account=metadata-sa@$PROJECT_ID.iam.gserviceaccount.com\n```\n\nor preferably assign your user impersonation capabilities on it (see section below)\n\nYou can assign IAM permissions now to the service account for whatever resources it may need to access and then run:\n\n```bash\nmkdir certs/\nmv metadata-sa.json certs\n\n./gce_metadata_server -logtostderr --configFile=config.json \\\n  -alsologtostderr -v 5 \\\n  -port :8080 \\\n  --serviceAccountFile certs/metadata-sa.json \n```\n\n### With Impersonation\n\nIf you use impersonation, the `serviceAccountEmail` and `scopes` are taken from the config file's default service account.\n\nFirst setup impersonation for your user account:\n\n```bash\ngcloud iam service-accounts \\\n  add-iam-policy-binding metadata-sa@$PROJECT_ID.iam.gserviceaccount.com \\\n  --member=user:`gcloud config get-value core/account` \\\n  --role=roles/iam.serviceAccountTokenCreator\n```\n\nthen,\n\n```bash\n./gce_metadata_server -logtostderr \\\n     -alsologtostderr -v 5  -port :8080 \\\n     --impersonate --configFile=config.json\n```\n\n### With Workload Federation\n\nFor [workload identity federation](https://cloud.google.com/iam/docs/how-to#using-workload-identity-federation), you need to reference the credentials.json file as usual:\n\nthen just use the default env-var and run:\n\n```bash\nexport GOOGLE_APPLICATION_CREDENTIALS=`pwd`/sts-creds.json\n./gce_metadata_server -logtostderr --configFile=config.json \\\n  -alsologtostderr -v 5 \\\n  -port :8080 --federate \n```\n\nTo use this mode, you must first setup the Federation and then set the environment variable pointing to the [ADC file](https://cloud.google.com/iam/docs/configuring-workload-identity-federation#aws).\n\nfor reference, see\n\n* [Exchange Generic OIDC Credentials for GCP Credentials using GCP STS Service](https://github.com/salrashid123/gcpcompat-oidc)\n* [Exchange AWS Credentials for GCP Credentials using GCP STS Service](https://github.com/salrashid123/gcpcompat-aws)\n\nwhere the `sts-creds.json` file is the generated one you created.  For example using the OIDC tutorial above, it may look like\n\nfor example, if the workload federation user is mapped to\n\n```\nprincipal://iam.googleapis.com/projects/1071284184436/locations/global/workloadIdentityPools/oidc-pool-1/subject/alice@domain.com\n```\n\nthen that identity should have the binding to use the metadata service account:\n\n```bash\n# enable federation for principal://\ngcloud iam service-accounts add-iam-policy-binding metadata-sa@$PROJECT_ID.iam.gserviceaccount.com \\\n    --role roles/iam.workloadIdentityUser \\\n    --member \"principal://iam.googleapis.com/projects/$GOOGLE_NUMERIC_PROJECT_ID/locations/global/workloadIdentityPools/oidc-pool-1/subject/alice@domain.com\"\n```\n\nultimately, the `sts-creds.json` will look like (note:, the `service_account_impersonation_url` value is not present)\n\n```json\n{\n  \"type\": \"external_account\",\n  \"audience\": \"//iam.googleapis.com/projects/1071284184436/locations/global/workloadIdentityPools/oidc-pool-1/providers/oidc-provider-1\",\n  \"subject_token_type\": \"urn:ietf:params:oauth:token-type:jwt\",\n  \"token_url\": \"https://sts.googleapis.com/v1/token\",\n  \"credential_source\": {\n    \"file\": \"/tmp/oidccred.txt\"\n  }\n}\n```\n\nwhere `/tmp/oidcred.txt` contains the original oidc token\n\n### With Trusted Platform Module (TPM)\n\nIf the service account private key is bound inside a `Trusted Platform Module (TPM)`, the metadata server can use that key to issue an `access_token` or an `id_token`\n\n\u003e\u003e Note: not all platforms supports this mode.  The underlying go-tpm library is only supported on a few of the targets (`linux/darwin + amd64,arm64`).  If you need support for other platforms, one option is to comment the sections for the TPM, remove the library bindings and compile.\n\nBefore using this mode, the key _must be_ sealed into the TPM and surfaced as a `persistentHandle` or as a [PEM encoded TPM Keyfile](https://github.com/salrashid123/tpm2/tree/master/tpm-key).  This can be done in a number of ways described [here](https://github.com/salrashid123/oauth2/blob/master/README.md#usage-tpmtokensource): \n\nBasically, you can either\n\n- `A` download a Google ServiceAccount's json file and embed the private part to the TPM. [example](https://github.com/salrashid123/oauth2/blob/master/README.md#a-import-service-account-json-to-tpm)\n- `B` Generate a Key _on the TPM_ and then [import the public part to GCP](https://cloud.google.com/iam/docs/keys-upload). [example](https://github.com/salrashid123/oauth2/blob/master/README.md#b-generate-key-on-tpm-and-export-public-x509-certificate-to-gcp).  Note that you can [upload atmost 10 keys per service account](https://cloud.google.com/iam/quotas#limits)\n- `C` remote seal the service accounts RSA Private key, encrypt it with TPM's Endorsement Key and load it securely inside the TPM. [example](https://gist.github.com/salrashid123/9e4a0328fd8c84374ace78c76a1e34cb)\n\n`A` is the easiest for a demo\n\n`B` is the most secure\n\n`C` allows for multiple TPMs to use the same key \n\nAnyway, once the RSA key is present as a handle, start the metadata server using the `--tpm` flag and set the `--persistentHandle=` value.\n\nTPM based tokens derives the serivceAccount email from the configuration file.   You must first edit `config.json` and set the value of `Claims.ComputeMetadata.V1.Instance.ServiceAccounts[\"default\"].Email`.\n\nFor a full example with `A`, you'll need a serviceAccount key file first which you'll embed into the TPM.\n\nUsing `tpm2_tools`:\n\n```bash\n## prepare they key\n## extract just the private key from the json keyfile\n\ncat tpm-svc-account.json | jq -r '.private_key' \u003e /tmp/f.json\nopenssl rsa -in /tmp/f.json -out /tmp/key_rsa.pem \n\n## create the primary\n### the specific primary here happens to be the h2 template described later on but you are free to define any template and policy\n\nprintf '\\x00\\x00' \u003e unique.dat\ntpm2_createprimary -C o -G ecc  -g sha256  -c primary.ctx -a \"fixedtpm|fixedparent|sensitivedataorigin|userwithauth|noda|restricted|decrypt\" -u unique.dat\n \ntpm2_import -C primary.ctx -G rsa2048:rsassa:null -g sha256 -i /tmp/key_rsa.pem -u key.pub -r key.prv\ntpm2_flushcontext -t\ntpm2_load -C primary.ctx -u key.pub -r key.prv -c key.ctx\ntpm2_flushcontext -t\n\n## either persist the key to a handle\ntpm2_evictcontrol -C o -c key.ctx 0x81010002\n\n### or as PEM format file\n## to create a TPM PEM formatted file,\n## either use https://github.com/salrashid123/tpm2genkey\n### ref https://github.com/salrashid123/tpm2/tree/master/tpm-key\n## or just use tpm2_encodeobject\ntpm2_encodeobject -C primary.ctx -u key.pub -r key.prv -o private.pem\n\n## this formats it as TPM-encrypted PEM:\ncat private.pem \n-----BEGIN TSS2 PRIVATE KEY-----\nMIICNQYGZ4EFCgEDoAMBAf8CBEAAAAEEggEaARgAAQALAAQAQAAAABAAFAALCAAA\nAQABAQDqKVruwZ6amTB9OFXwOqNkl7Zaxh0jD1AXbnD9uvnk0z18tGOHxzsP6lsm\nLJ8ywnMkomdbDP78dZlHEC3sn/7ustRUTwHb9UV/gc875gMJ0qsrbRajsH1J7tQB\nS4ezEf8MKoBi9ogUx7g21z7cytiK46nr08J3yyZHvXVuCklncXBD8TM9ZlHVdDeM\nICMOzXg6d0fL0UvujGPSIEYnqbmY4DlpI0RudMAsOtActbo7Dq7xuiSBcW9slxxS\ne18mO6/3IJANKVlHkynpjTEkzzchKR5brCoteukcLhSPTlSNmkvzBOXbDTyRhrrs\n8HEyufQGc4MGLjStpTFNsOHy1xqnBIIBAAD+ACDtgAG7hcbIVsgW1JHzyZcWQRdv\nTntWp4sacW0ltVvMLwAQvxAAj4Y0E9FyZesU/urN7896vACshaTw5lNuV7hr9ZKr\noWjGMcFo9r+H4OvshONF/GTc3ggp7UlbBo5+V5UlcQrUbk3dSGEstVgA+Wf4upoM\nQ9jCmwuljqFRG7afs6js5CWfXn+z6bKIewa9mTIkjXa7GhDCHBTRO5LVn68L5dFS\n0ddxx3FNZ7W4S+Md8jG19TU2oagKyrH4cXObRL1dlWSiDB0U62LHIzQcdKUENv4Y\n2GDcvBxUtXWp/kBhZ5EaNOoH31njN1Pi8bZQE86j/JDNuC3i4TKdGCA=\n-----END TSS2 PRIVATE KEY-----\n```\n\nAfter that, run\n\n```bash\n./gce_metadata_server -logtostderr --configFile=config.json \\\n  -alsologtostderr -v 5 \\\n  -port :8080 \\\n  --tpm --persistentHandle=0x81010002 \n```\n\nor with files\n\n```bash\n./gce_metadata_server -logtostderr --configFile=config.json \\\n  -alsologtostderr -v 5 \\\n  -port :8080 \\\n  --tpm --keyfile=/path/to/private.pem \n```\n\nThe TPM based credentials imports a JWT generator library to perform the oauth and id_token exchanges: \n\n* [salrashid123/golang-jwt-tpm](https://github.com/salrashid123/golang-jwt-tpm)\n* [salrashid123/oauth2](https://github.com/salrashid123/oauth2)\n\nIf the TPM based key is restricted through a PCR policy, you will need to supply the list of PCRs its bound to using the `--pcrs` flag: (eg `--pcrs=2,3,23`).  See examples [here](https://github.com/salrashid123/gcp-adc-tpm?tab=readme-ov-file#pcr-policy)\n\nNote that if you are using PCR policy, the metadata server cache's the credential values until it expires (which is typically an hour). If you enable a PCR policy and then change it to invalidate the TPM-based key's usage, the server will return the same token until it needs to referesh it.\n\nIf you want to enable [TPM Encrypted sessions](https://github.com/salrashid123/tpm2/tree/master/tpm_encrypted_session), you should provide the \"name\" of a trusted key on the TPM for each call.\n\nA trusted key can be the EK Key. You can get the name using `tpm2_tools`:\n\n```bash\ntpm2_createek -c primary.ctx -G rsa -u ek.pub -Q\ntpm2_readpublic -c primary.ctx -o ek.pem -n name.bin -f pem -Q\nxxd -p -c 100 name.bin \n  000bb50d34f6377bb3c2f41a1b4b6094ed6efcd7032d28054566db0766879dad1ee0\n```\n\nThen use the hex value returned in the `--tpm-session-encrypt-with-name=` argument.\n\nFor example:\n\n```bash\n   --tpm-session-encrypt-with-name=000bb50d34f6377bb3c2f41a1b4b6094ed6efcd7032d28054566db0766879dad1ee0\n```\n\nYou can also derive the \"name\" from a public key of a known template.  see [go-tpm.tpm2_get_name](https://github.com/salrashid123/tpm2/tree/master/tpm2_get_name)\n\nA TODO enhancement could be to add on support for `PKCS-11` systems:  eg [salrashid123/golang-jwt-pkcs11](https://github.com/salrashid123/golang-jwt-pkcs11)\n\nalso see:\n\n* [TPM Credential Source for Google Cloud SDK](https://github.com/salrashid123/gcp-adc-tpm)\n* [PKCS-11 Credential Source for Google Cloud SDK](https://github.com/salrashid123/gcp-adc-pkcs)\n\n## Startup\n\nUse any of the credential initializations described above and on startup, you will see something like:\n\n```bash\n./gce_metadata_server -logtostderr --configFile=config.json \\\n  -alsologtostderr -v 5 \\\n  -port :8080 \\\n  --serviceAccountFile certs/metadata-sa.json \n```\n\n![images/setup_2.png](images/setup_2.png)\n\n### AccessToken\n\nIn a new window, run\n\n```bash\ncurl -s -H 'Metadata-Flavor: Google' --connect-to metadata.google.internal:80:127.0.0.1:8080 \\\n   http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token\n\n{\n  \"access_token\": \"ya29.c.EltxByD8vfv2ACageADlorFHWd2ZUIgGdU-redacted\",\n  \"expires_in\": 3600,\n  \"token_type\": \"Bearer\"\n}\n```\n\n### IDToken\n\nThe following endpoints shows how to acquire an IDToken\n\n```bash\ncurl -H \"Metadata-Flavor: Google\" --connect-to metadata.google.internal:80:127.0.0.1:8080 \\\n'http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/identity?audience=https://foo.bar'\n```\n\nThe `id_token` will be signed by google but issued by the service account you used\n\n```json\n{\n  \"alg\": \"RS256\",\n  \"kid\": \"178ab1dc5913d929d37c23dcaa961872f8d70b68\",\n  \"typ\": \"JWT\"\n}.\n{\n  \"aud\": \"https://foo.bar\",\n  \"azp\": \"metadata-sa@$PROJECT.iam.gserviceaccount.com\",\n  \"email\": \"metadata-sa@PROJECT.iam.gserviceaccount.com\",\n  \"email_verified\": true,\n  \"exp\": 1603550806,\n  \"iat\": 1603547206,\n  \"iss\": \"https://accounts.google.com\",\n  \"sub\": \"117605711420724299222\"\n}\n```\n\n*Important:* To get `id_tokens`, you must edit `config.json` and set the value of\n\n- `Claims.ComputeMetadata.V1.Instance.ServiceAccounts[\"default\"].Email`\n\nto the value present for the credentials you are using (eg set it to `metadata-sa@$PROJECT.iam.gserviceaccount.com` (substituting in value for your real $PROJECT))\n\n\u003e\u003e\u003e Unlike the _real_ gce metadataserver, this will **NOT** return the full identity document or license info :(`\u0026format=[FORMAT]\u0026licenses=[LICENSES]`)\n\n\n### Attributes\n\nTo acquire instance or project attributes, simply call the endpoint:\n\nFor example, to get the instance id:\n\n```bash\ncurl -s -H 'Metadata-Flavor: Google' --connect-to metadata.google.internal:80:127.0.0.1:8080 \\\n      http://metadata.google.internal/computeMetadata/v1/instance/id\n\n5775171277418378000\n```\n\n## Using Google Auth clients\n\nGCP Auth libraries support overriding the host/port for the metadata server.  \n\n\nEach language library has their own nuances so please read the sections elow\n\n\nThese are not documented but you can _generally_ just set the value of.\n\nIf you intend to use the samples in the `examples/` folder, add some viewer permission to list gcs buckets (because this is what all the stuff in the `examples/` folder shows)\n\n```bash\n# note roles/storage.admin is over-permissioned...we only need storage.buckets.list on the project...\ngcloud projects add-iam-policy-binding $PROJECT_ID  \\\n     --member=\"serviceAccount:metadata-sa@$PROJECT_ID.iam.gserviceaccount.com\"  \\\n     --role=roles/storage.admin\n```\n\nthen usually just,\n\n```bash\nexport GCE_METADATA_HOST=localhost:8080\n```\n\nand use this emulator.  The `examples/` folder shows several clients taken from [gcpsamples](https://github.com/salrashid123/gcpsamples/tree/master/auth/compute).\n\nRemember to run `gcloud auth application-default revoke` in any new client library test to make sure your residual creds are not used.\n\n##### [python](https://github.com/googleapis/google-auth-library-python/blob/main/google/auth/compute_engine/_metadata.py#L35-L50)\n\nsee [examples/pyapp](examples/pyapp/)\n\n```bash\n  export GCE_METADATA_HOST=localhost:8080\n  export GCE_METADATA_IP=127.0.0.1:8080\n\n  virtualenv env\n  source env/bin/activate\n  pip3 install -r requirements.txt\n\n  python3 main.py\n```\n\nUnlike the other language SDK's, for python we need to set `GCE_METADATA_IP` (see [google-auth-library-python #1505](https://github.com/googleapis/google-auth-library-python/issues/1505)).\n\n##### [java](https://github.com/googleapis/google-auth-library-java/blob/main/oauth2_http/java/com/google/auth/oauth2/DefaultCredentialsProvider.java#L71)\n\nsee [examples/javaapp](examples/javapp/)\n\n```bash\n   export GCE_METADATA_HOST=localhost:8080\n\n   mvn clean install exec:java  -q\n```\n\n##### [golang](https://github.com/googleapis/google-cloud-go/blob/main/compute/metadata/metadata.go#L41-L46)\n\nsee [examples/goapp](examples/goapp/)\n\n```bash\n  export GCE_METADATA_HOST=localhost:8080\n\n  go run main.go\n```\n\n##### [nodejs](https://github.com/googleapis/gcp-metadata/blob/main/src/index.ts#L36-L37)\n\nsee [examples/nodeapp](examples/nodeapp/)\n\n```bash\n  export GCE_METADATA_HOST=localhost:8080\n\n  npm i\n  node app.js  \n```\n\n##### [dotnet](https://github.com/googleapis/google-api-dotnet-client/blob/main/Src/Support/Google.Apis.Auth/OAuth2/GoogleAuthConsts.cs#L136)\n\nsee [examples/dotnet](examples/dotnet/)\n\n```bash\n  export GCE_METADATA_HOST=localhost:8080\n\n  dotnet restore\n  dotnet run\n```\n\nNote, `Google.Api.Gax.Platform.Instance().ProjectId` requests the full [recursive path](https://github.com/googleapis/gax-dotnet/blob/main/Google.Api.Gax/Platform.cs#LL61C69-L61C103)\n\n\n#### gcloud\n\n```bash\nexport GCE_METADATA_ROOT=localhost:8080\n\n$ gcloud config list\n[component_manager]\ndisable_update_check = True\n[core]\naccount = metadata-sa@mineral-minutia-820.iam.gserviceaccount.com\nproject = mineral-minutia-820\n```\n\n`gcloud` uses a different env-var but if you want to use `gcloud auth application-default print-access-token`, you need to _also_ use `GCE_METADATA_HOST` and `GCE_METADATA_IP`\n\n\n## Other Runtimes\n\n### Run emulator as container\n\nThis emulator is also published as a release-tagged container to dockerhub:\n\n* [https://hub.docker.com/r/salrashid123/gcemetadataserver](https://hub.docker.com/r/salrashid123/gcemetadataserver)\n\nYou can verify the image were signed by the repo owner if you really want to (see section below). \n\n### Run with containers\n\nTo access the local emulator _from_ containers\n\n```bash\ncd examples/container\ndocker build -t myapp .\ndocker run -t --net=host -e GCE_METADATA_HOST=localhost:8080  myapp\n```\n\nthen run the emulator standalone or as a container itself:\n\n```bash\ndocker run \\\n  -v `pwd`/certs/:/certs/ \\\n  -v `pwd`/config.json:/config.json \\\n  -p 8080:8080 \\\n  -t salrashid123/gcemetadataserver  \\\n      -serviceAccountFile /certs/metadata-sa.json \\\n      --configFile=/config.json \\\n      -logtostderr -alsologtostderr -v 5 \\\n      -interface 0.0.0.0 -port :8080\n```\n\n### Running as Kubernetes Service\n\nYou can run the emulator as a kubernetes `Service`  and reference it from other pods address by injecting `GCE_METADATA_HOST` environment variable to the containers:\n\nIf you want test this with `minikube` locally,\n\n```bash\n## first create the base64encoded form of the service account key\ncat certs/metadata-sa.json | base64  --wrap=0 -\ncd examples/kubernetes\n```\n\nthen edit metadata.yaml and replace the values: \n\n```yaml\napiVersion: v1\nkind: Secret\nmetadata:\n  name: gcp-svc-account\ntype: Opaque\ndata:\n  metadata-sa.json: \"replace with contents of cat certs/metadata-sa.json | base64  --wrap=0 -\"\n---\napiVersion: v1\nkind: ConfigMap\nmetadata:\n  name: mds-config\ndata:\n  config.json: |\n     \"replace with contents of config.json\"  \n```\n\nFinally test\n\n```bash\nminikube start\nkubectl apply -f .\nminikube dashboard --url\nminikube service app-service --url\n\n$ curl -s `minikube service app-service --url`\n\nNumber of Buckets: 62\n```\n\n\u003e\u003e needless to say, the metadata Service should be accessed only form authorized pods\n\n### Dynamic Configuration File Updates\n\nChanges to the claims configuration file (`--configFile=`) while the metadata server is running will automatically update values returned by the server.\n\nOn startup, the metadata server sets a file listener on that config file and any updates to the values will propagate back to the server without requiring a restart.\n\n### ETag\n\nGCE metadata servers return values with [ETag](https://cloud.google.com/compute/docs/metadata/querying-metadata#etags) headers.  The ETag is used to check if a specific attribute or value has changed.  \n\nThis metadata server will hash the value for the body to return and use that as the ETag.  If you update the configuration file with new attributes or values, the ETag for that node will change.  The `ETag` header key is returned in non-canonical format.\n\nNote `wait-for-change` value is not supported currently so while you can poll for etag changes, you cannot listen and hold.\n\nFinally, since the etag is just a hash of the node, if you change a value then back again, the same etag will get returned for that node. \n\n### Static environment variables\n\nIf you do not have access to certificate file or would like to specify **static** token values via env-var, the metadata server supports the following environment variables as substitutions.  Once you set these environment variables, the service will not look for anything using the service Account JSON file (even if specified)\n\n```bash\nexport GOOGLE_PROJECT_ID=`gcloud config get-value core/project`\nexport GOOGLE_NUMERIC_PROJECT_ID=`gcloud projects describe $GOOGLE_PROJECT_ID --format=\"value(projectNumber)\"`\nexport GOOGLE_ACCESS_TOKEN=\"some_static_token\"\nexport GOOGLE_ID_TOKEN=\"some_id_token\"\nexport GOOGLE_ACCOUNT_EMAIL=\"metadata-sa@PROJECT.iam.gserviceaccount.com\"\n```\n\nfor example you can use those env vars and specify a fake svc account json key file (fake since its not actually even used)\n\n```bash\n./gce_metadata_server -logtostderr  \\\n   -alsologtostderr -v 5 \\\n   -port :8080 --configFile=`pwd`/config.json  --serviceAccountFile=certs/fake_sa.json\n```\n\nor\n\n```bash\ndocker run \\\n  -p 8080:8080 \\\n  -e GOOGLE_ACCESS_TOKEN=$GOOGLE_ACCESS_TOKEN \\\n  -e GOOGLE_NUMERIC_PROJECT_ID=$GOOGLE_NUMERIC_PROJECT_ID \\\n  -e GOOGLE_PROJECT_ID=$GOOGLE_PROJECT_ID \\\n  -e GOOGLE_ACCOUNT_EMAIL=$GOOGLE_ACCOUNT_EMAIL \\\n  -e GOOGLE_ID_TOKEN=$GOOGLE_ID_TOKEN \\  \n  -v `pwd`/config.json:/config.json \\\n  -v `pwd`/certs/fake_sa.json:/certs/fake_sa.json \\\n  -t salrashid123/gcemetadataserver \\\n  -port :8080 --configFile=/config.json --serviceAccountFile=/certs/fake_sa.json \\\n  --interface=0.0.0.0 -logtostderr -alsologtostderr -v 5\n```\n\n```bash\ncurl -v -H \"Metadata-Flavor: Google\" \\\n  --connect-to metadata.google.internal:80:127.0.0.1:8080 \\\n   http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token\n\nsome_static_token\n```\n\n#### Extending the sample\n\nYou can extend this sample for any arbitrary metadata you are interested in emulating (eg, disks, hostname, etc).\nSimply add the routes to the webserver and handle the responses accordingly.  It is recommended to view the request-response format directly on the metadata server to compare against.\n\n#### Using Link-Local address\n\nGCE's metadata server's IP address on GCE is a special link-local address: `169.254.169.254`.  Certain application default credential libraries for google cloud _may_ reference the metadata server by IP address so we're adding this in.\n\nIf you use the link-local address, do *not* set `GCE_METADATA_HOST`\n\nif you really want to use the link local address, you have two options:  use `iptables` or `socat`.  Both require some setup as root\n\nfirst create `/etc/hosts`:\n\n```bash\n169.254.169.254       metadata metadata.google.internal\n```\n\nfor `socat`\n\ncreate an IP alias:\n\n```bash\nsudo ifconfig lo:0 169.254.169.254 up\n```\n\nrelay using `socat`:\n\n```bash\nsudo apt-get install socat\n\nsudo socat TCP4-LISTEN:80,fork TCP4:127.0.0.1:8080\n```\n\nfor  `iptables`\n\nconfigure iptables:\n\n```bash\niptables -t nat -A OUTPUT -p tcp -d 169.254.169.254 --dport 80 -j DNAT --to-destination 127.0.0.1:8080\n```\n\nFinally, access the endpoint via IP or alias over port `:80`\n\n```bash\ncurl -v -H 'Metadata-Flavor: Google' \\\n     http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token\n```\n\nIf you don't mind running the program on port `:80` directly, you can skip the socat and iptables and simply start the emulator to on the link address (`-port :80 --interface=169.254.169.254`)  after setting the `/etc/hosts` variable.\n\n#### Using Domain Sockets\n\nYou can also start the metadata server to listen on a [unix domain socket](https://en.wikipedia.org/wiki/Unix_domain_socket).\n\nTo do this, simply specify `--domainsocket=` flag pointing to some file (eg ` --domainsocket=/tmp/metadata.sock`).  Once you do this, all tcp listeners will be disabled.\n\nTo access using curl, use its `--unix-socket` flag\n\n```bash\ncurl -v --unix-socket /tmp/metadata.sock \\\n -H 'Metadata-Flavor: Google' \\\n   http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token\n```\n\nWhile it works fine with things like curl, the main issue with using domain sockets is that the default `GCE_METADATA_HOST` variable just [listens on tcp](https://github.com/googleapis/google-cloud-go/blob/3a4ec650177be4d48aa7a0b8a22ea2b211522d80/compute/metadata/metadata.go#L308)  \n\nAnd its awkward to do all the overrides for a GCP SDK to \"just use\" a domain socket...\n\nIf you really wanted to use unix sockets, you can find an example of how to do this in the `examples/goapp_unix` folder\n\nanyway, just for fun, you can pipe a tcp socket to domain using `socat` (or vice versa) but TBH, you're now back to where you started with a tcp listener..\n\n```bash\nsocat TCP-LISTEN:8080,fork,reuseaddr UNIX-CONNECT:/tmp/metadata.sock\n```\n\n#### Building with Bazel\n\nIf you want to build the server using bazel (eg, [deterministic](https://github.com/salrashid123/go-grpc-bazel-docker)),\n\n```bash\n$ bazel version\n    Build label: 8.0.1\n    Build target: @@//src/main/java/com/google/devtools/build/lib/bazel:BazelServer\n    Build time: Fri Jan 17 19:16:16 2025 (1737141376)\n    Build timestamp: 1737141376\n    Build timestamp as int: 1737141376\n\n## generate dependencies\n# bazel run :gazelle -- update-repos -from_file=go.mod -prune=true -to_macro=repositories.bzl%go_repositories\n\n## run\nbazel run cmd:main -- --configFile=`pwd`/config.json   -alsologtostderr -v 5 -port :8080 --serviceAccountFile=`pwd`/certs/metadata-sa.json \n\n## to build the oci image tar\n# bazel build cmd:tar-oci-index\n\n## to push the image a repo\nbazel run cmd:push-image  \n```\n\n#### Building with Kaniko\n\nThe container image can also be built using kaniko with the `--reproducible` flag enabled:\n\n```bash\nexport TAG=...\ndocker run    -v `pwd`:/workspace -v $HOME/.docker/config.json:/kaniko/.docker/config.json:ro    -v /var/run/docker.sock:/var/run/docker.sock   \\\n      gcr.io/kaniko-project/executor@sha256:034f15e6fe235490e64a4173d02d0a41f61382450c314fffed9b8ca96dff66b2  \\\n      --dockerfile=Dockerfile \\\n      --reproducible \\\n      --destination \"docker.io/salrashid123/gcemetadataserver:$TAG\" \\\n      --context dir:///workspace/\n\nsyft packages docker.io/salrashid123/gcemetadataserver:$TAG\nskopeo copy  --preserve-digests  docker://docker.io/salrashid123/gcemetadataserver:$TAG docker://docker.io/salrashid123/gcemetadataserver:latest\n```\n\n#### Verify Release Binary\n\nIf you download a binary from the \"Releases\" page, you can verify the signature with GPG:\n\n```bash\ngpg --keyserver keys.openpgp.org --recv-keys 3FCD7ECFB7345F2A98F9F346285AEDB3D5B5EF74\n\n## to verify the checksum file for a given release:\nwget https://github.com/salrashid123/gce_metadata_server/releases/download/v3.93.0/gce_metadata_server_3.93.0_checksums.txt\nwget https://github.com/salrashid123/gce_metadata_server/releases/download/v3.93.0/gce_metadata_server_3.93.0_checksums.txt.sig\n\ngpg --verify gce_metadata_server_3.93.0_checksums.txt.sig gce_metadata_server_3.93.0_checksums.txt\n```\n\n#### Verify Release Binary with github Attestation\n\nYou can also verify the binary using [github attestation](https://github.blog/news-insights/product-news/introducing-artifact-attestations-now-in-public-beta/)\n\nFor example, the attestation for releases `[@refs/tags/v3.93.5]` can be found at\n\n* [https://github.com/salrashid123/gce_metadata_server/attestations](https://github.com/salrashid123/gce_metadata_server/attestations)\n\nThen to verify:\n\n```bash\n$ wget https://github.com/salrashid123/gce_metadata_server/releases/download/v3.93.5/gce_metadata_server_3.93.5_linux_amd64\n$ wget https://github.com/salrashid123/gce_metadata_server/attestations/4853131/download -O salrashid123-gce_metadata_server-attestation-4853131.json\n\n$ gh attestation verify --owner salrashid123 --bundle salrashid123-gce_metadata_server-attestation-4853131.json  gce_metadata_server_3.93.5_linux_amd64 \n\nLoaded digest sha256:1be0046bd047431ae0933e09e95e21e3146bff112099b08a54be1141b1576f92 for file://gce_metadata_server_3.93.5_linux_amd64\nLoaded 1 attestation from salrashid123-gce_metadata_server-attestation-4853131.json\n\nThe following policy criteria will be enforced:\n- Predicate type must match:................ https://slsa.dev/provenance/v1\n- Source Repository Owner URI must match:... https://github.com/salrashid123\n- Subject Alternative Name must match regex: (?i)^https://github.com/salrashid123/\n- OIDC Issuer must match:................... https://token.actions.githubusercontent.com\n\n✓ Verification succeeded!\n\nThe following 1 attestation matched the policy criteria\n\n- Attestation #1\n  - Build repo:..... salrashid123/gce_metadata_server\n  - Build workflow:. .github/workflows/release.yaml@refs/tags/v3.93.5\n  - Signer repo:.... salrashid123/gce_metadata_server\n  - Signer workflow: .github/workflows/release.yaml@refs/tags/v3.93.5\n```\n\n\n#### Verify Container Image Signature\n\nThe images are also signed using my github address (`salrashid123@gmail`).  If you really want to, you can verify each signature usign `cosign`:\n\n```bash\n## for tag/version  3.4.0:\nIMAGE=\"index.docker.io/salrashid123/gcemetadataserver@sha256:c3cec9e18adb87a14889f19ab0c3c87d66339284b35ca72135ff9dcd58a59671\"\n\n## i signed it directly, keyless:\nexport COSIGN_EXPERIMENTAL=1\n# $ cosign sign $IMAGE\n\n## which you can verify:\n$ cosign verify --certificate-identity=salrashid123@gmail.com  --certificate-oidc-issuer=https://github.com/login/oauth $IMAGE | jq '.'\n\n## search and get \n# $ rekor-cli search --rekor_server https://rekor.sigstore.dev  --email salrashid123@gmail.com\n# $ rekor-cli get --rekor_server https://rekor.sigstore.dev  --log-index $LogIndex  --format=json | jq '.'\n```\n\n#### GCE mTLS\n\nGCE metadata server also supports a mode where [mTLS is used](https://cloud.google.com/compute/docs/metadata/overview#https-mds)\n\nYou can enable this mode with the following flags but be aware, no client library supports it afaik. \n\n```bash\n./gce_metadata_server -logtostderr --configFile=config.json \\\n  -alsologtostderr -v 5 \\\n  -port :8080 --usemTLS \\\n  --serverCert certs/server.crt \\\n  --serverKey certs/server.key --rootCAmTLS certs/root.crt  \\\n  --serviceAccountFile certs/metadata-sa.json \n\ncurl -s -H 'Metadata-Flavor: Google' --connect-to metadata.google.internal:443:127.0.0.1:8080 \\\n   --cert certs/client.crt --key certs/client.key     --cacert certs/root.crt \\\n   https://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token\n```\n\nFor an example on how the [GCE guest agent](https://github.com/GoogleCloudPlatform/guest-agent/blob/main/google_guest_agent/agentcrypto/mtls_mds.go#L136) extracts the root ca from UEFI and decrypts the client cert/key from metadata server, see [certextract.go](https://gist.github.com/salrashid123/c1de41bf380c1f9a3602675276977e48)\n\nFor an example of how to invoke the mTLS endpoint and use it with a client library, see [examples/goapp_mtls/main.go](examples/goapp_mtls/main.go)\n\nNote that GCE issues client certificates that are rotated periodically.  Infact, the client certificate is set to expire in a week:\n\nFor example, the client certificate from a real GCE instance with metadata TLS shows a validity for about a week.\n\n```bash\nCertificate:\n    Data:\n        Version: 3 (0x2)\n        Serial Number:\n            82:f6:44:68:9e:b5:b2:cc:81:35:ff:29:61:1d:bf:9e\n        Signature Algorithm: ecdsa-with-SHA256\n        Issuer: C=US, O=Google Compute Internal, CN=google.internal\n        Validity\n            Not Before: Aug 13 23:05:26 2024 GMT\n            Not After : Aug 20 23:10:26 2024 GMT\n        Subject: C=US, O=Google Compute Engine, CN=instance-1\n        Subject Public Key Info:\n            Public Key Algorithm: id-ecPublicKey\n                Public-Key: (256 bit)\n                ASN1 OID: prime256v1\n                NIST CURVE: P-256\n        X509v3 extensions:\n            X509v3 Basic Constraints: critical\n                CA:FALSE\n            X509v3 Key Usage: critical\n                Digital Signature\n            X509v3 Subject Alternative Name: \n                DNS:instance-1.c.srashid-test2.internal\n            X509v3 Extended Key Usage: critical\n                TLS Web Client Authentication\n    Signature Algorithm: ecdsa-with-SHA256\n```\n\n## Envoy Authentication Filter\n\n[GCP Authentication Filter](https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/gcp_authn_filter) provides a way to for envoy to automatically inject an `id_token` into the upstream request.\n\nIt does this as an http filter that first acquires the token from a metadata service.  If you want to use this repos' metadata service to test with, \n\n\nrun enovy \n\n```bash\ncd example/envoy_gcp_authentication/\n\ndocker cp `docker create  envoyproxy/envoy-dev:latest`:/usr/local/bin/envoy /tmp/\n\n/tmp/envoy -c sidecar.yaml -l debug\n```\n\nthen when you invoke envoy, the request has the id_token added on by envoy.  The echo response in this example shows the headers upstream:\n\n```bash\n$ curl -v  http://localhost:18080/get\n{\n  \"args\": {}, \n  \"headers\": {\n    \"Accept\": \"*/*\", \n    \"Authorization\": \"Bearer eyJhbGciOiJSU...\", \n    \"Host\": \"localhost\", \n    \"User-Agent\": \"curl/8.8.0\", \n    \"X-Amzn-Trace-Id\": \"Root=1-672a30f1-74e63bf55e1f189f3eedac33\", \n    \"X-Envoy-Expected-Rq-Timeout-Ms\": \"15000\"\n  }, \n  \"origin\": \"71.127.34.114\", \n  \"url\": \"https://localhost/get\"\n}\n```\n\nthe token has the audience set to the envoy configuration file\n\n```json\n{\n  \"aud\": \"http://test.com\",\n  \"azp\": \"metadata-sa@$PROJECT.iam.gserviceaccount.com\",\n  \"email\": \"metadata-sa@$PROJECT.iam.gserviceaccount.com\",\n  \"email_verified\": true,\n  \"exp\": 1730821889,\n  \"iat\": 1730818289,\n  \"iss\": \"https://accounts.google.com\",\n  \"sub\": \"100890260483227123111\"\n}\n```\n\n## Metrics\n\nBasic latency and counter Prometheus metrics are enabled using the `--metrisEnabled` flag.\n\nOnce enabled, path latency is recoreded at the default prometheus endpoint at `http://localhost:9000/metrics`.\n\nApart from latency, any dynamic field for access or identity tokens also has a counter and status metric surfaced.\n\n## Testing\n\na lot todo here, right...thats just life\n\n```bash\n$ go test -v \n\n=== RUN   TestBasePathRedirectHandler\n--- PASS: TestBasePathRedirectHandler (0.00s)\n=== RUN   TestProjectIDHandler\n--- PASS: TestProjectIDHandler (0.00s)\n=== RUN   TestAccessTokenHandler\n--- PASS: TestAccessTokenHandler (0.00s)\n=== RUN   TestAccessTokenDefaultCredentialHandler\n--- PASS: TestAccessTokenDefaultCredentialHandler (0.00s)\n=== RUN   TestAccessTokenComputeCredentialHandler\n--- PASS: TestAccessTokenComputeCredentialHandler (0.00s)\n=== RUN   TestAccessTokenEnvironmentCredentialHandler\n--- PASS: TestAccessTokenEnvironmentCredentialHandler (0.00s)\n=== RUN   TestOnGCEHandler\n--- PASS: TestOnGCEHandler (0.00s)\n=== RUN   TestProjectNumberHandler\n--- PASS: TestProjectNumberHandler (0.00s)\n=== RUN   TestInstanceIDHandler\n--- PASS: TestInstanceIDHandler (0.00s)\nPASS\nok  \tgithub.com/salrashid123/gce_metadata_server\t0.053s\n```\n\nas bazel\n\n```bash\n$ bazel test :go_default_test \n\n  INFO: Analyzed target //:go_default_test (0 packages loaded, 0 targets configured).\n  INFO: Found 1 test target...\n  Target //:go_default_test up-to-date:\n    bazel-bin/go_default_test_/go_default_test\n  INFO: Elapsed time: 0.364s, Critical Path: 0.00s\n  INFO: 1 process: 1 action cache hit, 1 internal.\n  INFO: Build completed successfully, 1 total action\n  //:go_default_test                                              (cached) PASSED in 0.1s\n\n  Executed 0 out of 1 test: 1 test passes.\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsalrashid123%2Fgce_metadata_server","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsalrashid123%2Fgce_metadata_server","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsalrashid123%2Fgce_metadata_server/lists"}