Setup your secrets in AWS

August 11, 2026 ยท View on GitHub

In this setup we integrate the secrets-exercise online with AWS EKS and let Pods consume secrets from the AWS Parameter Store and AWS Secrets Manager. We use managed node groups so as we don't want the hassle of managing the EC2 instances ourselves, and Fargate doesn't suit our needs since we use a StatefulSet. If you want to know more about integrating secrets with EKS, check EKS and SSM Parameter Store and EKS and Secrets Manager. Please make sure that the account in which you run this exercise has either CloudTrail enabled, or is not linked to your current organization and/or DTAP environment.

Pre-requisites

Have the following tools installed:

Make sure you have an active account at AWS for which you have configured the credentials on the system where you will execute the steps below. In this example we stored the credentials under an aws profile as awsuser.

Installation

First, we want to create a shared state. We've provided the terraform code for this in the shared-state subfolder.

To create an s3 bucket (optionally add -var="region=YOUR_DESIRED_REGION" to the apply to use a region other than the default eu-west-1):

cd shared-state
terraform init
terraform apply

The bucket name should be in the output. Please use that to configure the Terraform backend in main.tf. The bucket ARN will be printed, make a note of this as it will be used in the next steps. It should look something like arn:aws:s3:::terraform-20230102231352749300000001.

The terraform code is loosely based on this EKS managed Node Group TF example.

Note: Applying the Terraform means you are creating cloud infrastructure which actually costs you money. the current boundary is 50 t3a-(X)large nodes. Please adapt the servers you deploy to in main.tf in this folder to your liking to reduce possible costs. Note that this project can run on a single T3A-Large instance, but this would require reducing the amount of wrongsecretbalancers to 1 (balancer.replicas=1). The authors are not responsible for any cost coming from following the instructions below.

Note-II: The cluster you create has its access bound to the public IP of the creator. In other words: the cluster you create with this code has its access bound to your public IP-address if you apply it locally.

  1. export your AWS credentials (export AWS_PROFILE=awsuser)
  2. check whether you have the right profile by doing aws sts get-caller-identity. Make sure you have the right account and have the rights to do this.
  3. Ensure you have set all the right variables in terraform.tfvars. Optional: If you want to use a custom domain with TLS, also fill out your domain name(s) and Route53 hosted zone here. Delegate (sub)domains to Route53 nameservers if you're not hosting your domain with Route53: using the AWS docs
  4. Do terraform init (if required, use tfenv to select TF 0.14.0 or higher )
  5. The bucket ARN will be asked in the next 2 steps. Take the one provided to you in the output earlier (e.g., arn:aws:s3:::terraform-20230102231352749300000001).
  6. Do terraform plan
  7. Do terraform apply. Note: the apply will take 10 to 20 minutes depending on the speed of the AWS backplane.
  8. When creation is done, do aws eks update-kubeconfig --region eu-west-1 --name wrongsecrets-exercise-cluster --kubeconfig ~/.kube/wrongsecrets
  9. Do export KUBECONFIG=~/.kube/wrongsecrets
  10. Run ./build-and-deploy-aws.sh to install all the required materials (helm for calico, secrets management, autoscaling, etc.)

Your EKS cluster should be visible in eu-west-1 by default. Want a different region? You can modify terraform.tfvars or input it directly using the region variable in plan/apply.

Are you done playing? Please run terraform destroy twice to clean up (first in the main aws folder, then the shared-state subfolder).

Test it

When you have completed the installation steps, you can do kubectl port-forward service/wrongsecrets-balancer 3000:3000 and then go to http://localhost:3000.

Want to know how well your cluster is holding up? Check with

    kubectl top nodes
    kubectl top pods

Configuring CTFd

You can use the Juiceshop CTF CLI to generate CTFd configuration files.

Follow the following steps:

  # Use Node.js 24 for juice-shop-ctf-cli v12
  nvm install 24 && nvm use 24
  npm install -g juice-shop-ctf-cli@12
    juice-shop-ctf

When you run the CLI, keep in mind:

  • http://localhost:3000 points to the balancer UI, not directly to a WrongSecrets instance.
  • Without a team cookie, requests to /api/Challenges are redirected to /balancer/ (HTML), which causes JSON parsing errors like Unexpected token '<'.
  • For local generation, create a team first and then target that team's WrongSecrets service directly.

Example local workflow:

TEAM=ctfgen$RANDOM
HMAC_KEY=$(kubectl get secret wrongsecrets-balancer-secret -o=jsonpath='{.data.createTeamHmacKey}' | base64 --decode)
HMAC=$(printf "%s" "$TEAM" | openssl dgst -sha256 -hmac "$HMAC_KEY" | awk '{print \$2}')

# Create or join the team via balancer
curl -sS -H 'Content-Type: application/json' \
  -d "{\"hmacvalue\":\"$HMAC\"}" \
  "http://localhost:3000/balancer/teams/$TEAM/join"

# Wait for WrongSecrets instance readiness
curl -sS "http://localhost:3000/balancer/teams/$TEAM/wait-till-ready"

# Port-forward directly to the team service and keep this running
kubectl -n "t-$TEAM" port-forward "svc/t-$TEAM-wrongsecrets" 18080:8080

Then run juice-shop-ctf and use:

  • CTF framework: CTFd
  • Juice Shop URL to retrieve challenges: http://localhost:18080
  • ctf.key / secret key: your actual WrongSecrets CTF key (for default installs this is often test)
  • hints: your preference

Now visit the CTFd instance and setup your CTF. If you haven't set up a load balancer/ingress, the you can use kubectl port-forward -n ctfd $(kubectl get pods --namespace ctfd -l "app.kubernetes.io/name=ctfd,app.kubernetes.io/instance=ctfd" -o jsonpath="{.items[0].metadata.name}") 8000:8000 and go to localhost:8000 to visit CTFd.

!!NOTE: The following can be dangerous if you use CTFd >= 3.5.0 with wrongsecrets < 1.5.11. Check the challenges.json and make sure it's 1-indexed - a 0-indexed file will break CTFd! /NOTE!!

Then use the administrative backup function to import the zipfile you created with the juice-shop-ctf command. After that you will still need to override the flags with their actual values if you do use the 2-domain configuration. For a guide on how to do this see the 2-domain setup steps in the general README Want to setup your own? You can! Watch out for people finding your key though, so secure it properly: make sure the running container with the actual ctf-key is not exposed to the audience, similar to our heroku container.

Want to make the CTFD instance look pretty? Include the fragment located at ./k8s/ctfd_resources/index_fragment.html in your index.html via the admin panel.

Configuring the application

In the front page of the application you can edit the description to reference the right urls and the desplayed image. Use the following:

helm upgrade --install wrongsecrets ../helm/wrongsecrets-ctf-party \
  --set="balancer.env.REACT_APP_MOVING_GIF_LOGO=<>" \
  --set="balancer.env.REACT_APP_HEROKU_WRONGSECRETS_URL=<>" \
  --set="balancer.env.REACT_APP_CTFD_URL='<>'" \

Monitoring the cluster

For a guide on how to use the monitoring setup, see the monitoring guide.

Clean it up

When you're done:

  1. Kill the port forward.
  2. Run the cleanup script: ./cleanup-aws-autoscaling-and-helm.sh
  3. Run terraform destroy to clean up the infrastructure.
    1. If you've deployed the shared-state s3 bucket, also cd shared-state and terraform destroy there.
  4. Run unset KUBECONFIG to unset the KUBECONFIG env var.
  5. Run rm ~/.kube/wrongsecrets to remove the kubeconfig file.
  6. Run rm terraform.tfstate* to remove local state files.

A few things to consider

  1. Does your worker node now have access as well?
  2. Can you easily obtain the instance profile of the Node?
  3. Can you get the secrets in the SSM Parameter Store and Secrets Manager easily? Which paths do you see?
  4. Which of the 2 (SSM Parameter Store and Secrets Manager) works cross-account?
  5. If you have applied the secrets to the cluster, you should see at the configuration details of the cluster that Secrets encryption is "Disabled", what does that mean?

When you want to share your environment with others (experimental)

We added additional scripts for adding an ALB and ingress so that you can use your cloudsetup with multiple people. Do the following:

  1. Follow the installation section first.
  2. Run ./k8s-aws-alb-script.sh and the script will return the url at which you can reach the application. (Be aware this opens the url's to the internet in general, if you'd like to limit the access please do this using the security groups in AWS)
  3. When you are done, before you do cleanup, first run ./k8s-aws-alb-script-cleanup.sh.

Note that you might have to do some manual cleanups after that.

Terraform documentation

The documentation below is auto-generated to give insight on what's created via Terraform.

Resources

NameType
aws_iam_access_key.state_user_keyresource
aws_iam_policy.secret_denyresource
aws_iam_policy.secret_managerresource
aws_iam_role.irsa_roleresource
aws_iam_role.secret_readerresource
aws_iam_role.user_roleresource
aws_iam_role_policy.user_secret_readerresource
aws_iam_role_policy_attachment.irsa_role_attachmentresource
aws_iam_role_policy_attachment.user_role_attachmentresource
aws_iam_user.state_userresource
aws_iam_user_policy.state_user_policyresource
aws_secretsmanager_secret.secretresource
aws_secretsmanager_secret.secret_2resource
aws_secretsmanager_secret.state_user_access_keysresource
aws_secretsmanager_secret_policy.policyresource
aws_secretsmanager_secret_policy.policy_2resource
aws_secretsmanager_secret_version.secretresource
aws_secretsmanager_secret_version.state_user_access_keysresource
aws_ssm_parameter.secretresource
random_password.passwordresource
random_password.password2resource
aws_availability_zones.availabledata source
aws_caller_identity.currentdata source
aws_iam_policy_document.assume_role_for_secret_readerdata source
aws_iam_policy_document.assume_role_with_oidcdata source
aws_iam_policy_document.secret_managerdata source
aws_iam_policy_document.state_user_policydata source
aws_iam_policy_document.user_assume_roledata source
aws_iam_policy_document.user_policydata source
aws_iam_policy_document.user_secret_readerdata source
http_http.ipdata source

Inputs

NameDescriptionTypeDefaultRequired
balancer_domain_nameThe domain name to usestring""no
cluster_nameThe EKS cluster namestring"wrongsecrets-exercise-cluster"no
cluster_versionThe EKS cluster version to usestring"1.30"no
ctfd_domain_nameThe domain name to usestring""no
extra_allowed_ip_rangesAllowed IP ranges in addition to creator IPlist(string)[]no
hosted_zone_idThe ID of the Route53 Hosted Zone to usestring""no
regionThe AWS region to usestring"eu-west-1"no
state_bucket_arnARN of the state bucket to grant access to the s3 userstringn/ayes

Outputs

NameDescription
balancer_acm_cert_arnBalancer ACM certificate ARN
balancer_domain_nameBalancer domain name
cluster_autoscaler_roleCluster autoscaler role
cluster_autoscaler_role_arnCluster autoscaler role arn
cluster_endpointEndpoint for EKS control plane.
cluster_idThe id of the cluster
cluster_nameThe EKS cluster name
cluster_security_group_idSecurity group ids attached to the cluster control plane.
ctfd_acm_cert_arnCTFd ACM certificate ARN
ctfd_domain_nameCTFd domain name
ebs_roleEBS CSI driver role
ebs_role_arnEBS CSI driver role
external_dns_role_arnExternal DNS role
irsa_roleThe role name used in the IRSA setup
irsa_role_arnThe role ARN used in the IRSA setup
load_balancer_controller_roleLoad balancer controller role
load_balancer_controller_role_arnLoad balancer controller role arn
secrets_manager_secret_nameThe name of the secrets manager secret
state_bucket_nameTerraform s3 state bucket name

Resources

NameType
aws_iam_access_key.state_user_keyresource
aws_iam_policy.secret_denyresource
aws_iam_policy.secret_managerresource
aws_iam_role.irsa_roleresource
aws_iam_role.secret_readerresource
aws_iam_role.user_roleresource
aws_iam_role_policy.user_secret_readerresource
aws_iam_role_policy_attachment.irsa_role_attachmentresource
aws_iam_role_policy_attachment.user_role_attachmentresource
aws_iam_user.state_userresource
aws_iam_user_policy.state_user_policyresource
aws_secretsmanager_secret.secretresource
aws_secretsmanager_secret.secret_2resource
aws_secretsmanager_secret.state_user_access_keysresource
aws_secretsmanager_secret_policy.policyresource
aws_secretsmanager_secret_policy.policy_2resource
aws_secretsmanager_secret_version.secretresource
aws_secretsmanager_secret_version.state_user_access_keysresource
aws_ssm_parameter.secretresource
random_password.passwordresource
random_password.password2resource
aws_availability_zones.availabledata source
aws_caller_identity.currentdata source
aws_iam_policy_document.assume_role_for_secret_readerdata source
aws_iam_policy_document.assume_role_with_oidcdata source
aws_iam_policy_document.secret_managerdata source
aws_iam_policy_document.state_user_policydata source
aws_iam_policy_document.user_assume_roledata source
aws_iam_policy_document.user_policydata source
aws_iam_policy_document.user_secret_readerdata source
http_http.ipdata source

Inputs

NameDescriptionTypeDefaultRequired
balancer_domain_nameThe domain name to usestring""no
cluster_nameThe EKS cluster namestring"wrongsecrets-exercise-cluster"no
cluster_versionThe EKS cluster version to usestring"1.30"no
ctfd_domain_nameThe domain name to usestring""no
extra_allowed_ip_rangesAllowed IP ranges in addition to creator IPlist(string)[]no
hosted_zone_idThe ID of the Route53 Hosted Zone to usestring""no
regionThe AWS region to usestring"eu-west-1"no
state_bucket_arnARN of the state bucket to grant access to the s3 userstringn/ayes

Outputs

NameDescription
balancer_acm_cert_arnBalancer ACM certificate ARN
balancer_domain_nameBalancer domain name
cluster_autoscaler_roleCluster autoscaler role
cluster_autoscaler_role_arnCluster autoscaler role arn
cluster_endpointEndpoint for EKS control plane.
cluster_idThe id of the cluster
cluster_nameThe EKS cluster name
cluster_security_group_idSecurity group ids attached to the cluster control plane.
ctfd_acm_cert_arnCTFd ACM certificate ARN
ctfd_domain_nameCTFd domain name
ebs_roleEBS CSI driver role
ebs_role_arnEBS CSI driver role
external_dns_role_arnExternal DNS role
irsa_roleThe role name used in the IRSA setup
irsa_role_arnThe role ARN used in the IRSA setup
load_balancer_controller_roleLoad balancer controller role
load_balancer_controller_role_arnLoad balancer controller role arn
secrets_manager_secret_nameThe name of the secrets manager secret
state_bucket_nameTerraform s3 state bucket name