{"id":22725419,"url":"https://github.com/ovotech/cloud-key-rotator","last_synced_at":"2025-08-12T06:34:06.544Z","repository":{"id":34748717,"uuid":"144971168","full_name":"ovotech/cloud-key-rotator","owner":"ovotech","description":"A Golang program to rotate AWS \u0026 GCP account keys","archived":false,"fork":false,"pushed_at":"2025-03-13T01:42:17.000Z","size":77558,"stargazers_count":65,"open_issues_count":16,"forks_count":12,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-04-13T20:40:26.426Z","etag":null,"topics":["kaluza-to-migrate"],"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/ovotech.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"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}},"created_at":"2018-08-16T10:09:58.000Z","updated_at":"2025-02-17T03:23:00.000Z","dependencies_parsed_at":"2023-02-19T04:00:50.521Z","dependency_job_id":"97d913cc-1237-4cfd-9a01-35a9f654cd98","html_url":"https://github.com/ovotech/cloud-key-rotator","commit_stats":{"total_commits":581,"total_committers":16,"mean_commits":36.3125,"dds":0.6041308089500861,"last_synced_commit":"a7379561f6e97e283ef09427157a7233c5e0db71"},"previous_names":[],"tags_count":77,"template":false,"template_full_name":null,"purl":"pkg:github/ovotech/cloud-key-rotator","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ovotech%2Fcloud-key-rotator","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ovotech%2Fcloud-key-rotator/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ovotech%2Fcloud-key-rotator/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ovotech%2Fcloud-key-rotator/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ovotech","download_url":"https://codeload.github.com/ovotech/cloud-key-rotator/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ovotech%2Fcloud-key-rotator/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":270014373,"owners_count":24512637,"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","status":"online","status_checked_at":"2025-08-12T02:00:09.011Z","response_time":80,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["kaluza-to-migrate"],"created_at":"2024-12-10T16:10:40.120Z","updated_at":"2025-08-12T06:34:06.499Z","avatar_url":"https://github.com/ovotech.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Cloud-Key-Rotator\n\n\u003e _:warning:_ this is no longer supported. Security fixes will continue to be\n\u003e applied, but if you need any other changes, please fork.\n\n[![CircleCI](https://circleci.com/gh/ovotech/cloud-key-rotator/tree/master.svg?style=svg)](https://circleci.com/gh/ovotech/cloud-key-rotator/tree/master)\n\nThis is a Golang program to assist with the reporting of Service Account key\nages, and rotating said keys once they pass a specific age threshold.\n\nThe tool can update keys held in the following locations:\n\n- Atlas (mongoDB)\n- CircleCI env vars\n- CircleCI contexts\n- Datadog (GCP Integration)\n- GCS\n- Git\n- GitHub Secrets\n- GoCd\n- K8S (GKE only)\n- SSM (AWS Parameter Store)\n- AWS SecretsManager\n\nThe tool is packaged as an executable file for native invocation, and as a zip\nfile for deployment as an AWS Lambda.\n\n\u003e :information_source: where possible [OpenID Connect (OIDC)](https://openid.net/connect/)\n\u003e should be used instead of furnishing/storing long-lived credentials. Using OIDC\n\u003e will remove the need for running `cloud-key-rotator`.\n\n## Install\n\n### From Binary Releases\n\nDarwin, Linux and Windows Binaries can be downloaded from the\n[Releases](https://github.com/ovotech/cloud-key-rotator/releases) page.\n\nTry it out:\n\n```\n$ cloud-key-rotator -h\n```\n\n### Docker Image\n\nAn Alpine-based Docker image is available [here](https://hub.docker.com/r/ovotech/cloud-key-rotator).\n\n## Getting Started\n\n### Config\n\n`cloud-key-rotator` picks up details about which key(s) to rotate, and locations\nto update with new keys, from config.\n\nCheck out [examples](examples) for example config files. [Viper](https://github.com/spf13/viper)\nis used as the config framework, so config can be stored as JSON, TOML, YAML or\nHCL.\n\nFor native invocation, the file needs to be called \"config\" (before whatever\nextension you're using), and be present either in `/etc/cloud-key-rotator/` or\nin the same directory the binary runs in. For AWS Lambda invocation, the config needs\nto be set as a plaintext secret in the AWS Secrets Manager, using a default key name\nof \"ckr-config\".\n\n### Authentication/Authorisation\n\nYou'll need to provide `cloud-key-rotator` with the means of authenticating into\nany key provider that it'll be updating.\n\nAuthorisation is handled by the Default Credential Provider Chains for both\n[GCP](https://cloud.google.com/docs/authentication/production#auth-cloud-implicit-go) and\n[AWS](https://docs.aws.amazon.com/sdk-for-java/v1/developer-guide/credentials.html#credentials-default).\n\n### Mode Of Operation\n\n`cloud-key-rotator` can operate in two different modes:\n\n1. Rotation mode - in which keys are rotated; and\n2. Non-rotation mode - which only posts the ages of keys to the Datadog Metric API.\n\nThe boolean field `RotationMode` config controls the mode of operation.\n\n### Age Thresholds\n\nYou can set the age threshold to whatever you want in the config, using the\n`DefaultRotationAgeThresholdMins` field in config, or you can override on a\nper-service-account-basis with the `RotationAgeThresholdMins` field. Key ages\nare always measured in minutes.\n\n`cloud-key-rotator` will not attempt to rotate a key until it's passed the age\nthreshold you've set (either default or the key-specific). This allows you to\nrun the tool as frequently as you want without worrying about keys being rotated\nexcessively.\n\n### Key Locations\n\n\"Key locations\" is the term used for the places where keys are stored, which will\nultimately be updated with the new keys that are generated.\n\nCurrently, the following locations are supported:\n\n- Atlas (mongoDB)\n- CircleCI env vars\n- CircleCI contexts\n- Datadog (GCP Integration)\n- GCS\n- Git (files encrypted with [mantle](https://github.com/ovotech/mantle) which\n  integrates with KMS))\n- GitHub Secrets\n- GoCd\n- K8S (GKE only)\n- SSM (AWS Parameter Store)\n- AWS SecretsManager\n\n## Rotation Process\n\nThe tool attempts to verify its actions as much as possible and aborts\nimmediately if it encounters an error. By design, the tool does **not** attempt to\nhandle errors gracefully and continue, since this can lead to a \"split-brain effect\",\nwith keys out-of-sync in various locations.\n\nIt should be quick to re-run the tool (with new keys being created) once issues\nhave been resolved. Note that cloud providers usually limit the number of\nkeys you can have attached to a Service Account at any one time, so it is\nworth bearing this in mind when re-running manually after seeing errors.\n\nOnly the first key of a Service Account is handled by `cloud-key-rotator`. If\nit handled more than one key, it could lead to complications when updating\nsingle sources multiple times.\n\n## Key Sources\n\nThe `AccountKeyLocations` section of config holds details of the places where the keys\nare stored, e.g.:\n\n```JSON\n\"AccountKeyLocations\": [{\n  \"ServiceAccountName\": \"cloud-key-client-test\",\n  \"RotationAgeThresholdMins\": 60,\n  \"Git\": {\n    \"FilePath\": \"service-account.txt\",\n    \"OrgRepo\": \"ovotech/cloud-key-rotator\",\n    \"VerifyCircleCISuccess\": true,\n    \"CircleCIDeployJobName\": \"dummy_deploy_with_wait\"\n  },\n  \"CircleCI\": [{\n    \"UsernameProject\": \"ovotech/cloud-key-rotator\",\n    \"KeyEnvVar\": \"ENV_VAR_NAME\"\n  }],\n  \"K8s\": [{\n    \"Project\": \"my_project\",\n    \"Location\": \"europe-west2-b\",\n    \"ClusterName\": \"cluster_name\",\n    \"Namespace\": \"uat\",\n    \"SecretName\": \"key-rotate-test-secret\",\n    \"DataName\": \"my-key.json\"\n  }]\n}]\n```\n\n`cloud-key-rotator` has integrations into GitHub and CircleCI, which allows it\nnot only to update those sources with the new key, but also to verify that a\ndeployment has been successful after committing to a GitHub repository. If that\nverification isn't required, you can disable it using the `VerifyCircleCISuccess`\nboolean.\n\nFor any Git key location, the whole process will be aborted\nif there is no `KmsKey` value set. Unencrypted keys should **never** be committed\nto a Git repository.\n\n## GPG Commit Signing\n\nCommits to Git repositories are required to be GPG signed. In order to\nachieve this, you need to provide 4 things:\n\n- `Username` of the Git user commits will be made on behalf of, set in config\n- `Email` address of Git user, set in config\n- `ArmouredKeyRing`, aka GPG private key, stored in `/etc/cloud-key-rotator/akr.asc`\n- `Passphrase` to the ArmouredKeyRing\n\ne.g. along with the `akr.asc` file, you should set the following:\n\n```JSON\n\"AkrPass\": \"change_me\",\n\"GitName\": \"git-name\",\n\"GitEmail\": \"change_me@example.com\",\n```\n\n## Filtering Service Accounts\n\nYou may want to only include or exclude specific Service Accounts. This is\npossible using `AccountFilter`.\n\nE.g.:\n\n```JSON\n\"AccountFilter\": {\n  \"Mode\": \"include\",\n  \"Accounts\": [{\n    \"Provider\": {\n      \"Name\": \"gcp\",\n      \"Project\": \"my-project\"\n    },\n    \"ProviderAccounts\": [\n      \"cloud-key-client-test\"\n    ]\n  }]\n}\n```\n\n`Mode` field is either `include` or `exclude`. If you omit the `AccountFilter`,\nthe rotation process will fail.\n\nNotice how the name of the Service Account is used. In the case of GCP, this is\neverything preceding the `@[project].iam.gserviceaccount.com` string in the\nService Account's email address.\n\n## Rotation Flow\n\n1. Reduce keys to those of service accounts deemed to be valid (e.g. strip out\n   user accounts if in rotation-mode)\n2. Filter keys to those deemed to be eligible (e.g. according to filtering rules\n   configured by the user)\n3. For each eligible key:\n\n- Create new key\n- Update key locations\n- Verify update has worked (where possible)\n- Delete old key\n\n## Troubleshooting\n\n### GCP\n\n- I get an error when using my key: `Response: {\"error\":\"invalid_grant\",\n\"error_description\":\"Invalid JWT Signature.\"}`\n\nThe key you're trying to use isn't valid. It could be because a rotation\nhas happened since the process, e.g. CI/CD job, started using the key (hence\nthe key has been deleted in GCP). If you run the `cloud-key-rotator` very\nfrequently it increases your chance of seeing this.\n\nTry and schedule rotations for times that are unlikely to conflict with CI/CD\njobs.\n\n- When trying to create a GCP AppEngine App (a pre-requisite of being able to\n  create CloudScheduler jobs) I get an error: `Error waiting for App Engine app to\ncreate: Error code 13, message: AppEngine service account cannot be generated for e~\u003cproject_name\u003e`\n\nThis happens when the AppEngine default service account has been previously\ndeleted from your project. If this happeed recently (possibly last 30d) there's\na [gcloud cmd](https://cloud.google.com/sdk/gcloud/reference/beta/iam/service-accounts/undelete)\navailable to try. If that's not possible, GCP support should be able to restore\nthe service account.\n\n- CloudScheduler fails to invoke the `cloud-key-rotator` CloudFunction, showing\n  a `PERMISSION DENIED` error in logs\n\nFor the CloudScheduler to have permission to invoke CloudFunctions, the Cloud\nScheduler service account must be given the `Cloud Scheduler Service Agent`\nrole in your project IAMs.\n\nThe Cloud Scheduler service account will have an id of format:\n\n```\nservice-\u003cproject_number\u003e@gcp-sa-cloudscheduler.iam.gserviceaccount.com\n```\n\n### CircleCI\n\n- I get an error in `cloud-key-rotator` logs: `404: Project not found: APIError null`\n\nFirst thing to check is that there's no typo in the `UsernameProject` value\nthat you've set in config.\n\nThe user/owner (preferably a bot user) of the CircleCI API key that you pass to\n`cloud-key-rotator` must have write access to the GitHub repo that the CircleCI\njobs run from.\n\n## Contributions\n\nContributions are more than welcome from both internal (to ovotech) and external\ncontributors.\n\nIf you have write access to this repo, create a branch and PR,\notherwise fork and PR. Forked branches will be pushed to this repo by a\nreviewer so PR checks can run.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fovotech%2Fcloud-key-rotator","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fovotech%2Fcloud-key-rotator","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fovotech%2Fcloud-key-rotator/lists"}