Kubernetes secrets use Base64 encoding to encode the secrets. It is simple to decode a base64 encoded, but from a security point of view, storing the secret in the git repository in the base64 encoded format is not good enough. In this tutorial, you will see about storing the secrets in AWS secrets manager and use external secrets by GoDaddy to create that as a Kubernetes secret.

Pre-requisites

  • Localstack(to emulate AWS services secrets manager and STS in local)
  • A KinD cluster
  • Helm (installed in the KinD cluster)

1. Create kind cluster

To create a kind cluster, follow this tutorial here

2. Install helm

You can install Helm in your laptop with 2 different methods

  • Through binaries created for your OS using this link.
  • Through the install script as mentioned [here][helm-script-install]

3. Install localstack

For the purpose of this tutorial, we will be using localsatck, a mock implementation of many AWS services. Lets install localstack in the cluster using helm by running the below commands.

> helm repo add localstack-repo http://helm.localstack.cloud
> helm upgrade --install localstack localstack-repo/localstack

> kubectl get pods
NAME                                                            READY   STATUS    RESTARTS   AGE
localstack-859d64b555-hgzxh                                     1/1     Running   0          4m48s

If you want to know more about localstack check here.

4. Install external secrets

Use helm to install GoDaddy’s external secrets

> helm repo add external-secrets https://external-secrets.github.io/kubernetes-external-secrets/
> helm install external-secrets external-secrets/kubernetes-external-secrets

> kubectl get pods
NAME                                                            READY   STATUS    RESTARTS   AGE
external-secrets-kubernetes-external-secrets-7c565576fb-5x6tz   1/1     Running   0          3m22s

Now, edit the external secrets deployment to use localstack instead of AWS

kubectl edit deployment/external-secrets-kubernetes-external-secrets

In the deployment yaml, add the below environmental variables,

 - name: LOCALSTACK
   value: "1"
 - name: LOCALSTACK_STS_URL
   value: http://localstack:4566
 - name: LOCALSTACK_SM_URL
   value: http://localstack:4566
 - name: AWS_ACCESS_KEY_ID
   value: some-access-key
 - name: AWS_SECRET_ACCESS_KEY
   value: some-secret-key

The above variables instruct external secrets to refer to localstack services(sts and Secrets manager) running in our cluster instead of aws services. You can refer to the external secrets AWS config here to understand why we are setting these variables.

The above environment variables also includes some random access key and secret key to connect to localstack. This is needed by the external secrets pod to connect to localstack. Instead of the access & secret keys, in the AWS environment, you can use a node IAM role or service accounts as mentioned here.

5. Create secret in localstack secret manager

Now lets create a secret in secrets manager and create an IAM role to access the secret. The IAM role json should be like this,

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "secretsmanager:GetResourcePolicy",
        "secretsmanager:GetSecretValue",
        "secretsmanager:DescribeSecret",
        "secretsmanager:ListSecretVersionIds"
      ],
      "Resource": [
        *
      ]
    }
  ]
}

The above json specifies the permissions to read all the secrets in secrets manager. Store this in a file iam-role.json inside the container.

> kubectl exec -it localstack-859d64b555-hgzxh -- bash

bash-5.0# awslocal secretsmanager create-secret --name test-secret --secret-string "hello-1234" --region us-east-1
{
    "ARN": "arn:aws:secretsmanager:us-east-1:000000000000:secret:test-secret-Walhb",
    "Name": "test-secret",
    "VersionId": "81c8df61-dfa1-4518-93b1-8d90c23bd0f2"
}

bash-5.0# awslocal iam create-role --role-name Test-Secret-Role --assume-role-policy-document file://iam-role.json
{
    "Role": {
        "Path": "/",
        "RoleName": "Test-Secret-Role",
        "RoleId": "kvwu8ik8ivxgd6lzxnk9",
        "Arn": "arn:aws:iam::000000000000:role/Test-Secret-Role",
        "CreateDate": "2020-12-29T13:28:11.625Z",
        "AssumeRolePolicyDocument": "<role json>",
        "MaxSessionDuration": 3600
    }
}

6. Create external secret

apiVersion: 'kubernetes-client.io/v1'
kind: ExternalSecret
metadata:
  name: test-secret
spec:
  backendType: secretsManager
  roleArn: arn:aws:iam::000000000000:role/Test-Secret-Role
  region: us-east-1
  data:
    - key: test-secret
      name: password

The yaml specifies the backend type as secrets manager. The region we created the secret us-east-1 is mentioned. The role ARN of the created IAM role is mentioned in step 5. Then the secret is described under the .spec.data key. The key field specifies the name of the secret we created in localstack in step 5. The field name instructs external secrets to create a kubernetes secrets with the key password .

Now, apply the external secrets yaml and see a new secrets created.

> kubectl get externalsecrets                                                
NAME          LAST SYNC   STATUS    AGE
test-secret   0s          SUCCESS   21s

> kubectl get secrets test-secret -o yaml                                                      
apiVersion: v1
data:
  password: aGVsbG8tMTIzNA==
kind: Secret
metadata:
  creationTimestamp: "2020-12-30T01:39:36Z"
  name: test-secret
  namespace: default
  ownerReferences:
  - apiVersion: kubernetes-client.io/v1
    controller: true
    kind: ExternalSecret
    name: test-secret
    uid: 6bbc0343-75be-4aae-b6c4-d63d4d790fb7
  resourceVersion: "30419"
  selfLink: /api/v1/namespaces/default/secrets/test-secret
  uid: 6d032e09-1375-4ef2-9ae5-8545b4b5af76
type: Opaque

Note down the base64 encoded value of the secret password. Lets verify that the secret value is the same we created the secret in secrets manager.

echo aGVsbG8tMTIzNA== | base64 --decode
hello-1234

That’s it. Lets test external secrets in your local kind cluster without having to use AWS services.

Comments