Skip to content

Commit

Permalink
Adding external secrets
Browse files Browse the repository at this point in the history
  • Loading branch information
rodrigobersa committed Oct 12, 2023
1 parent 8548fa2 commit 03c43c0
Show file tree
Hide file tree
Showing 8 changed files with 217 additions and 24 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,16 @@ module "secrets_store_csi_driver_provider_aws" {
addon_context = local.addon_context
}

module "external_secrets" {
source = "github.com/aws-ia/terraform-aws-eks-blueprints?ref=v4.32.1//modules/kubernetes-addons/external-secrets"

helm_config = {
version = "0.9.5"
}

addon_context = local.addon_context
}

module "secrets_manager_role" {
source = "terraform-aws-modules/iam/aws//modules/iam-role-for-service-accounts-eks"
version = "~> 5.30"
Expand Down Expand Up @@ -83,4 +93,31 @@ resource "kubernetes_annotations" "catalog-sa" {
"eks.amazonaws.com/role-arn" = "${module.secrets_manager_role.iam_role_arn}"
}
force = true
}
}

resource "kubernetes_manifest" "cluster_secretstore" {
manifest = {
"apiVersion" = "external-secrets.io/v1beta1"
"kind" = "ClusterSecretStore"
"metadata" = {
"name" = "cluster-secret-store"
}
"spec" = {
"provider" = {
"aws" = {
"service" = "SecretsManager"
"region" = "${data.aws_region.current.name}"
"auth" = {
"jwt" = {
"serviceAccountRef" = {
"name" = "external-secrets-sa"
"namespace" = "external-secrets"
}
}
}
}
}
}
}
depends_on = [module.external_secrets]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: catalog
namespace: catalog
spec:
template:
spec:
containers:
- name: catalog
env:
- name: DB_USER
valueFrom:
secretKeyRef:
name: catalog-external-secret
key: username
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: catalog-external-secret
key: password
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: "catalog-external-secret"
namespace: "catalog"
spec:
refreshInterval: 1h
secretStoreRef:
name: "cluster-secret-store"
kind: ClusterSecretStore
dataFrom:
- extract:
key: "eks-workshop/catalog-secret"
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
bases:
- ../../../../base-application/catalog
patches:
- deployment.yaml
resources:
- external-secret.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
title: "Validating AWS Secrets and Configuration Provider (ASCP)"
title: "AWS Secrets and Configuration Provider (ASCP)"
sidebar_position: 61
---

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,132 @@
---
title: "Using External Secrets"
title: "External Secrets Operator"
sidebar_position: 64
---

As seen in earlier steps, we have a `Deployment` in the **Catalog** `Namespace` with some credentials declared as environment variables, using values stored in a `Secret` in the same `Namespace`. We could also check that this is not the best approach to store sensitive information, since the secret values are just encoded using *base64*, and can be easily decoded in the command line. We will then modify the **catalog-db** `Deployment` to use the secret stored in AWS Secrets Manager, as the source for the sensitive credentials information.
The `prepare-environment` script that you ran in a [previous step](./index.md), has already deployed the External Secrets Operator addon required for this lab.

Let's validate the created addon.

```bash
$ kubectl -n external-secrets get pods
NAME READY STATUS RESTARTS AGE
external-secrets-6d95d66dc8-5trlv 1/1 Running 0 7m
external-secrets-cert-controller-774dff987b-krnp7 1/1 Running 0 7m
external-secrets-webhook-6565844f8f-jxst8 1/1 Running 0 7m
$ kubectl -n external-secrets get sa
NAME SECRETS AGE
default 0 7m
external-secrets-sa 0 7m
```

As you can see, you have a ServiceAccount named `external-secrets-sa`, this SA is tied to an [IRSA](../../iam-roles-for-service-accounts/), with access to AWS Secrets Manager, for retrieving secrets information.

```bash
$ kubectl -n external-secrets describe sa external-secrets-sa | grep Annotations
Annotations: eks.amazonaws.com/role-arn: arn:aws:iam::068535243777:role/eks-workshop-external-secrets-sa-irsa
```

There is also a ClusterResource called `ClusterSecretStore` which is a cluster wide SecretStore that can be referenced by all ExternalSecrets from all namespaces.

```bash
$ kubectl get clustersecretstores.external-secrets.io
NAME AGE STATUS CAPABILITIES READY
cluster-secret-store 81s Valid ReadWrite True
$ kubectl get clustersecretstores.external-secrets.io cluster-secret-store -o yaml | yq '.spec'
provider:
aws:
auth:
jwt:
serviceAccountRef:
name: external-secrets-sa
namespace: external-secrets
region: us-west-2
service: SecretsManager

```

You can see here, that it's using a [JSON Web Token (jwt)](https://jwt.io/), referenced to the ServiceAccount we just checked, to sync with AWS Secrets Manager.

Let's move forward and create an `ExternalSecret`, that describes what data should be fetched from AWS Secrets Manager, how the data should be transformed and saved as a Kubernetes Secret. And also patch our `catalog` Deployment to use the External Secret as source for the credentials.

```kustomization
modules/security/secrets-manager/external-secrets/kustomization.yaml
Deployment/catalog
ExternalSecret/catalog-external-secret
```

```bash
$ kubectl apply -k eks-workshop/modules/security/secrets-manager/external-secrets/
```

Check the newly created `ExternalSecret` resouce.

```bash
$ kubectl -n catalog get externalsecrets.external-secrets.io
NAME STORE REFRESH INTERVAL STATUS READY
catalog-external-secret cluster-secret-store 1h SecretSynced True
```

Verify that the resource has a `SecretSynced` status, which means that it was successfully syncronized from AWS Secrets Manager. Let's take a closer look to this resource specifications.

```bash
$ kubectl -n catalog get externalsecrets.external-secrets.io catalog-external-secret -o yaml | yq '.spec'
dataFrom:
- extract:
conversionStrategy: Default
decodingStrategy: None
key: eks-workshop/catalog-secret
refreshInterval: 1h
secretStoreRef:
kind: ClusterSecretStore
name: cluster-secret-store
target:
creationPolicy: Owner
deletionPolicy: Retain
```

Notice the `key` and the `secretStoreRef` parameter, pointing to the secret we stored on AWS Secrets Manager, and the `ClusterSecretStore` previously created. Also the `refreshInterval` is set to 1 hours, which means that the value from this secret will be checked and refreshed every hour.

But how do we use this ExternalSecret in our Pods? After we create this resouces, it automatically created a Kubernetes secret with the same name in the Namespace.

```bash
$ kubectl -n catalog get secrets
NAME TYPE DATA AGE
catalog-db Opaque 2 21h
catalog-external-secret Opaque 2 1m
catalog-secret Opaque 2 5h40m
```

Take a deeper look in this secret.

```bash
$ kubectl -n catalog get secret catalog-external-secret -o yaml | yq '.metadata.ownerReferences'
- apiVersion: external-secrets.io/v1beta1
blockOwnerDeletion: true
controller: true
kind: ExternalSecret
name: catalog-external-secret
uid: b8710001-366c-44c2-8e8d-462d85b1b8d7
```

See that it has an `ownerReference` that points to External Secrets Operator.

Now check that the `catalog` Pod, is already updated with the values from this new secret, and it's up and running!

```bash
$ kubectl -n catalog get pods
NAME READY STATUS RESTARTS AGE
catalog-777c4d5dc8-lmf6v 1/1 Running 0 14m
catalog-mysql-0 1/1 Running 0 24h
$ kubectl -n catalog get deployment catalog -o yaml | yq '.spec.template.spec.containers[] | .env'
- name: DB_USER
valueFrom:
secretKeyRef:
key: username
name: catalog-external-secret
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
key: password
name: catalog-external-secret
```
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,20 @@ This will make the following changes to your lab environment:

Install the following Kuberentes Addons in your EKS Cluter:
* Kubernetes Secrets Store CSI Driver
* AWS Secrets and Configuration Provider
* AWS Secrets and Configuration Provider
* External Secrets Operator

You can view the Terraform that applies these changes [here](https://github.com/aws-samples/eks-workshop-v2/tree/main/manifests/modules/security/secrets/secrets-manager/.workshop/terraform).

:::

[AWS Secrets Manager](https://aws.amazon.com/secrets-manager/) allows you to easily rotate, manage, and retrieve sensitive data such as credentials, API keys, certificates, among others. You can use [AWS Secrets and Configuration Provider (ASCP)](https://github.com/aws/secrets-store-csi-driver-provider-aws) for [Kubernetes Secrets Store CSI Driver](https://secrets-store-csi-driver.sigs.k8s.io/) to mount secrets stored in Secrets Manager as volumes in Kubernetes Pods.

With the ASCP, you can store and manage your secrets in Secrets Manager and then retrieve them through your workloads running on Amazon EKS. You can use IAM roles and policies to limit access to your secrets to specific Kubernetes Pods in a cluster. The ASCP retrieves the Pod identity and exchanges the identity for an IAM role. ASCP assumes the IAM role of the Pod, and then it can retrieve secrets from Secrets Manager that are authorized for that role.
With the ASCP, you can store and manage your secrets in Secrets Manager and then retrieve them through your workloads running on Amazon EKS. You can use IAM roles and policies to limit access to your secrets to specific Kubernetes Pods in a cluster.
The ASCP retrieves the Pod identity and exchanges the identity for an IAM role. ASCP assumes the IAM role of the Pod, and then it can retrieve secrets from Secrets Manager that are authorized for that role.

If you use Secrets Manager automatic rotation for your secrets, you can also use the Secrets Store CSI Driver rotation reconciler feature to ensure you are retrieving the latest secret from Secrets Manager.
Another way to integrate AWS Secrets Manager with Kubernetes Secrets, is through [External Secrets](https://external-secrets.io/). External Secrets is an operator that can integrate and synchronize secrets from AWS Secrets Manager reading the information from it and automatically injecting the values into a Kubernetes Secret with an abstraction that stores and manages the lifecycle of the secrets for you.

In this lab following section, we will create an example scenario of using secrets from AWS Secrets Manager.
If you use Secrets Manager automatic rotation for your secrets, you can rely on External Secrets refresh interaval or use the Secrets Store CSI Driver rotation reconciler feature to ensure you are retrieving the latest secret from Secrets Manager, depending on the tool you choose to manage secrets inside your Amazon EKS Cluster.

In this lab following section, we will create a couple of example scenarios of using secrets from AWS Secrets Manager and External Secrets.
Original file line number Diff line number Diff line change
Expand Up @@ -25,22 +25,6 @@ $ kubectl -n catalog get deployment catalog -o yaml | yq '.spec.template.spec.co
name: catalog-db
```

If you take a closer look to the `catalog-db` Secret you can see that it is only encoded with *base64* which can be easily decoded as follows. Also the `catalog-db` is the only Secret in the Namespace.

```file
manifests/base-application/catalog/secrets.yaml
```

```bash
$ kubectl -n catalog get secrets
NAME TYPE DATA AGE
catalog-db Opaque 2 15h
$ kubectl -n catalog get secrets catalog-db --template {{.data.username}} | base64 -d
catalog_user
$ kubectl -n catalog get secrets catalog-db --template {{.data.password}} | base64 -d
default_password
```

Notice that the `catalog` Deployment don't have any `volumes` or `volumeMounts` other than an `emptyDir` mounted on `/tmp`

```bash
Expand Down

0 comments on commit 03c43c0

Please sign in to comment.