From 59e745c082184bca912a7ba5a40939ad3f8d3b38 Mon Sep 17 00:00:00 2001 From: eversC Date: Wed, 28 Jun 2023 18:20:19 +0100 Subject: [PATCH 1/3] Set sm client config --- pkg/config/config.go | 1 + pkg/location/secretsmanager.go | 70 ++++++++++++++++++++++++++++++++++ pkg/rotate/rotatekeys.go | 4 ++ 3 files changed, 75 insertions(+) create mode 100644 pkg/location/secretsmanager.go diff --git a/pkg/config/config.go b/pkg/config/config.go index 21611873..10010c3e 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -80,6 +80,7 @@ type KeyLocations struct { Gocd []location.Gocd K8s []location.K8s SSM []location.Ssm + SecretsManager []location.SecretsManager } //ProviderServiceAccounts type diff --git a/pkg/location/secretsmanager.go b/pkg/location/secretsmanager.go new file mode 100644 index 00000000..a25a9943 --- /dev/null +++ b/pkg/location/secretsmanager.go @@ -0,0 +1,70 @@ +package location + +import ( + "fmt" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/session" + awsSm "github.com/aws/aws-sdk-go/service/secretsmanager" + "github.com/ovotech/cloud-key-rotator/pkg/cred" +) + +// SecretsManager type +type SecretsManager struct { + KeyParamName string + KeyIDParamName string + Region string + ConvertToFile bool + FileType string +} + +func (sm SecretsManager) Write(serviceAccountName string, keyWrapper KeyWrapper, creds cred.Credentials) (updated UpdatedLocation, err error) { + provider := keyWrapper.KeyProvider + var key string + var keyEnvVar string + var keyIDEnvVar string + var idValue bool + + if keyEnvVar, err = getVarNameFromProvider(provider, sm.KeyParamName, idValue); err != nil { + return + } + + if sm.ConvertToFile || provider == "gcp" { + if key, err = getKeyForFileBasedLocation(keyWrapper, sm.FileType); err != nil { + return + } + } else { + key = keyWrapper.Key + idValue = true + if keyIDEnvVar, err = getVarNameFromProvider(provider, sm.KeyIDParamName, idValue); err != nil { + return + } + } + svc := awsSm.New( + session.New(), + aws.NewConfig(). + WithRegion(sm.Region). + WithEndpoint(fmt.Sprintf("secretsmanager.%s.amazonaws.com", sm.Region)), + ) + + if len(keyIDEnvVar) > 0 { + if err = updateSecretsManagerSecret(keyIDEnvVar, keyWrapper.KeyID, *svc); err != nil { + return + } + } + if err = updateSecretsManagerSecret(keyEnvVar, key, *svc); err != nil { + return + } + + updated = UpdatedLocation{ + LocationType: "secretsmanager", + LocationURI: sm.Region, + LocationIDs: []string{keyIDEnvVar, keyEnvVar}} + return +} + +func updateSecretsManagerSecret(paramName, paramValue string, svc awsSm.SecretsManager) (err error) { + input := &awsSm.PutSecretValueInput{SecretId: ¶mName, SecretString: ¶mValue} + _, err = svc.PutSecretValue(input) + return +} diff --git a/pkg/rotate/rotatekeys.go b/pkg/rotate/rotatekeys.go index cf75f3b8..09627163 100644 --- a/pkg/rotate/rotatekeys.go +++ b/pkg/rotate/rotatekeys.go @@ -352,6 +352,10 @@ func locationsToUpdate(keyLocation config.KeyLocations) (kws []location.KeyWrite kws = append(kws, ssm) } + for _, secretsmanager := range keyLocation.SecretsManager { + kws = append(kws, secretsmanager) + } + if googleAppCredsRequired { ensureGoogleAppCreds() } From 02b1415c7fa66a3d5e4786241853fb716b274c5f Mon Sep 17 00:00:00 2001 From: eversC Date: Wed, 28 Jun 2023 18:23:40 +0100 Subject: [PATCH 2/3] Add sm as location in readme --- README.md | 98 +++++++++++++++++++++++++++---------------------------- 1 file changed, 49 insertions(+), 49 deletions(-) diff --git a/README.md b/README.md index 00f565b9..b1c11d18 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ # Cloud-Key-Rotator + [![CircleCI](https://circleci.com/gh/ovotech/cloud-key-rotator/tree/master.svg?style=svg)](https://circleci.com/gh/ovotech/cloud-key-rotator/tree/master) This is a Golang program to assist with the reporting of Service Account key @@ -6,30 +7,31 @@ ages, and rotating said keys once they pass a specific age threshold. The tool can update keys held in the following locations: -* Atlas (mongoDB) -* CircleCI env vars -* CircleCI contexts -* Datadog (GCP Integration) -* GCS -* Git -* GitHub Secrets -* GoCd -* K8S (GKE only) -* SSM (AWS Parameter Store) +- Atlas (mongoDB) +- CircleCI env vars +- CircleCI contexts +- Datadog (GCP Integration) +- GCS +- Git +- GitHub Secrets +- GoCd +- K8S (GKE only) +- SSM (AWS Parameter Store) +- AWS SecretsManager The tool is packaged as an executable file for native invocation, and as a zip file for deployment as an AWS Lambda. > :information_source: where possible [OpenID Connect (OIDC)](https://openid.net/connect/) -should be used instead of furnishing/storing long-lived credentials. Using OIDC -will remove the need for running `cloud-key-rotator`. +> should be used instead of furnishing/storing long-lived credentials. Using OIDC +> will remove the need for running `cloud-key-rotator`. ## Install ### From Binary Releases Darwin, Linux and Windows Binaries can be downloaded from the - [Releases](https://github.com/ovotech/cloud-key-rotator/releases) page. +[Releases](https://github.com/ovotech/cloud-key-rotator/releases) page. Try it out: @@ -54,9 +56,9 @@ HCL. For native invocation, the file needs to be called "config" (before whatever extension you're using), and be present either in `/etc/cloud-key-rotator/` or -in the same directory the binary runs in. For AWS Lambda invocation, the config needs +in the same directory the binary runs in. For AWS Lambda invocation, the config needs to be set as a plaintext secret in the AWS Secrets Manager, using a default key name - of "ckr-config". +of "ckr-config". ### Authentication/Authorisation @@ -65,7 +67,7 @@ any key provider that it'll be updating. Authorisation is handled by the Default Credential Provider Chains for both [GCP](https://cloud.google.com/docs/authentication/production#auth-cloud-implicit-go) and - [AWS](https://docs.aws.amazon.com/sdk-for-java/v1/developer-guide/credentials.html#credentials-default). +[AWS](https://docs.aws.amazon.com/sdk-for-java/v1/developer-guide/credentials.html#credentials-default). ### Mode Of Operation @@ -95,29 +97,29 @@ ultimately be updated with the new keys that are generated. Currently, the following locations are supported: -* Atlas (mongoDB) -* CircleCI env vars -* CircleCI contexts -* Datadog (GCP Integration) -* GCS -* Git (files encrypted with [mantle](https://github.com/ovotech/mantle) which -integrates with KMS)) -* GitHub Secrets -* GoCd -* K8S (GKE only) -* SSM (AWS Parameter Store) +- Atlas (mongoDB) +- CircleCI env vars +- CircleCI contexts +- Datadog (GCP Integration) +- GCS +- Git (files encrypted with [mantle](https://github.com/ovotech/mantle) which + integrates with KMS)) +- GitHub Secrets +- GoCd +- K8S (GKE only) +- SSM (AWS Parameter Store) ## Rotation Process The tool attempts to verify its actions as much as possible and aborts -immediately if it encounters an error. By design, the tool does **not** attempt to +immediately if it encounters an error. By design, the tool does **not** attempt to handle errors gracefully and continue, since this can lead to a "split-brain effect", with keys out-of-sync in various locations. It should be quick to re-run the tool (with new keys being created) once issues - have been resolved. Note that cloud providers usually limit the number of - keys you can have attached to a Service Account at any one time, so it is - worth bearing this in mind when re-running manually after seeing errors. +have been resolved. Note that cloud providers usually limit the number of +keys you can have attached to a Service Account at any one time, so it is +worth bearing this in mind when re-running manually after seeing errors. Only the first key of a Service Account is handled by `cloud-key-rotator`. If it handled more than one key, it could lead to complications when updating @@ -168,12 +170,13 @@ to a Git repository. Commits to Git repositories are required to be GPG signed. In order to achieve this, you need to provide 4 things: -* `Username` of the Git user commits will be made on behalf of, set in config -* `Email` address of Git user, set in config -* `ArmouredKeyRing`, aka GPG private key, stored in `/etc/cloud-key-rotator/akr.asc` -* `Passphrase` to the ArmouredKeyRing +- `Username` of the Git user commits will be made on behalf of, set in config +- `Email` address of Git user, set in config +- `ArmouredKeyRing`, aka GPG private key, stored in `/etc/cloud-key-rotator/akr.asc` +- `Passphrase` to the ArmouredKeyRing e.g. along with the `akr.asc` file, you should set the following: + ```JSON "AkrPass": "change_me", "GitName": "git-name", @@ -212,16 +215,15 @@ Service Account's email address. ## Rotation Flow 1. Reduce keys to those of service accounts deemed to be valid (e.g. strip out - user accounts if in rotation-mode) + user accounts if in rotation-mode) 2. Filter keys to those deemed to be eligible (e.g. according to filtering rules - configured by the user) + configured by the user) 3. For each eligible key: - * Create new key - * Update key locations - * Verify update has worked (where possible) - * Delete old key - +- Create new key +- Update key locations +- Verify update has worked (where possible) +- Delete old key ## Troubleshooting @@ -239,7 +241,7 @@ Try and schedule rotations for times that are unlikely to conflict with CI/CD jobs. - When trying to create a GCP AppEngine App (a pre-requisite of being able to -create CloudScheduler jobs) I get an error: `Error waiting for App Engine app to + create CloudScheduler jobs) I get an error: `Error waiting for App Engine app to create: Error code 13, message: AppEngine service account cannot be generated for e~` This happens when the AppEngine default service account has been previously @@ -249,7 +251,7 @@ available to try. If that's not possible, GCP support should be able to restore the service account. - CloudScheduler fails to invoke the `cloud-key-rotator` CloudFunction, showing -a `PERMISSION DENIED` error in logs + a `PERMISSION DENIED` error in logs For the CloudScheduler to have permission to invoke CloudFunctions, the Cloud Scheduler service account must be given the `Cloud Scheduler Service Agent` @@ -272,13 +274,11 @@ The user/owner (preferably a bot user) of the CircleCI API key that you pass to `cloud-key-rotator` must have write access to the GitHub repo that the CircleCI jobs run from. - - ## Contributions Contributions are more than welcome from both internal (to ovotech) and external contributors. -If you have write access to this repo, create a branch and PR, -otherwise fork and PR. Forked branches will be pushed to this repo by a -reviewer so PR checks can run. \ No newline at end of file +If you have write access to this repo, create a branch and PR, +otherwise fork and PR. Forked branches will be pushed to this repo by a +reviewer so PR checks can run. From 2442d63136be4ab24638268b4d432d577800fc31 Mon Sep 17 00:00:00 2001 From: eversC Date: Wed, 28 Jun 2023 20:08:05 +0100 Subject: [PATCH 3/3] Add sm as location in readme --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index b1c11d18..94985040 100644 --- a/README.md +++ b/README.md @@ -108,6 +108,7 @@ Currently, the following locations are supported: - GoCd - K8S (GKE only) - SSM (AWS Parameter Store) +- AWS SecretsManager ## Rotation Process