Setting up access to a private repository in ArgoCD with SSM Parameter Store and External Secrets Operator
ArgoCD’s documentation is quite good. I just feel there is one key question that is often left unanswered. How do I get my private SSH key into ArgoCD in a declarative way that doesn’t require hard coding the key into a secret YAML file?
In this post, we are going to use the External Secrets Operator (ESO) to get the private SSH key from AWS SSM Parameter Store and inject it into ArgoCD using a Kubernetes Secret.
If you already have ArgoCD setup, skip directly to the section Setting up ArgoCD for private repositories. If not, follow along.
Ok, let’s get started.
Bash Beyond Basics Increase your efficiency and understanding of the shell
If you are interested in this topic you might enjoy my course Bash Byond Basics. This course helps you level up your bash skills. This is not a course on shell-scripting, is a course on improving your efficiency by showing you the features of bash that are seldom discussed and often ignored.
Every day you spend many hours working in the shell, every little improvement in your worklflows will pay dividends many fold!
Learn moreInstalling ArgoCD
First, we need to install ArgoCD in our Kubernetes cluster. We will use Helm to do this, but we will do it in a declarative way so we can use our code as the source of truth. Let’s start by creating the directory and the required files for our Helm chart.
| |
The content for the Chart.yaml file is:
| |
The content for requirements.yaml is:
| |
Now we can install the chart:
| |
That should get ArgoCD set up in our Kubernetes Cluster. We need a repository with some Kubernetes templates. If you don’t have one, you can use the one I created for this post. It is a public repository. You can find it here. Fork it, and work with it. Later you can make it private or create a different private repository.
Create the ArgoCD Project and application
We will test that everything works correctly with ArgoCD using a public repository. Once ArgoCD runs correctly, we can focus on using a private repo. You can skip this section if you don’t want to run these tests. Let’s now create a new ArgoCD project and application.
The project:
| |
The application:
| |
Ok, we can create the project and the application:
kubectl apply -f project.yaml
kubectl apply -f application.yaml
Finally, we can view our application in the ArgoCD dashboard by doing a port forwarding to the argocd-server service:
| |
I run most of my tests in a VM, so when I do port forwarding, I need the port to be bound to all interfaces:
| |
We can now visit the ArgoCD dashboard at https://localhost:8080 and log in with the default username and password. The default username is admin. To obtain the default password, you need to read a secret created by default during installation.
| |
Log in, and you should see your application being created!
If we do a port forward to the newly created pod, we can see the Nginx welcome page:
| |
In another terminal:
| |
Ok, now we have a working ArgoCD installation. We can focus on setting up the private repository access.
Setting up the private repository access
We need to create an SSH key pair. We will use the key pair to access the private repository.
| |
Now we need to add the public key to the repository. In my case, I’m using GitHub, so I need to add the public key to the repository. If you also use GitHub, go to the repository Settings > Deploy Keys, and add the PUBLIC key. We only need read-only, so there is no need to add write permissions.
Let’s create a secure string parameter in AWS SSM parameter store that contains the PEM-encoded private key. We will use this as a secret to add the private key to ArgoCD.
| |
Finally, now we can look at setting up ArgoCD for our private repository.
Setting up ArgoCD for private repositories
We need to create a secret that will contain the private key. We will use the private key to access the private repository.
ArgoCD’s documentation explains that we need to create a secret with the private key so ArgoCD can access the repository. The secret looks like the following:
| |
This approach has the issue that we would have to write our private key in plain text in the secret template. We don’t want to do that. So we have a few options to avoid that. We can create a template and documentation that explains how to fill up this file and then manually apply it. That works, but it is a little bit cumbersome. We’ll use External Secrets Operator and avoid doing that.
Setting up External Secrets Operator
As we did before for the other dependencies, we will set up the External Secrets Operator in a declarative way using Helm.
| |
The content for the Chart.yaml file is:
| |
The content for requirements.yaml is:
| |
Now we can install the chart:
| |
Perfect, we have everything set up. Now we can create the secret that will contain the private key.
Creating the secret
Make sure that your IAM role has access to the SSM parameter store. The role will need a policy similar to the following:
resource "aws_iam_policy" "ssm_parameter_policy" {
name = "cluster-ssm-parameter-policy"
policy = jsonencode({
"Version" : "2012-10-17",
"Statement" : [
{
"Action" : [
"ssm:DescribeParameters",
"ssm:GetParameter",
"ssm:GetParameters",
"ssm:GetParametersByPath"
],
"Effect" : "Allow",
"Resource" : [
"arn:aws:ssm:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:/argo-cd/*"
]
},
]
})
}
With that out of the way, let’s define the Secret Store:
apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
name: my-aws-secret-store
# we need the secret to exist in the same namespace as ArgoCD
namespace: argocd
spec:
provider:
aws:
service: ParameterStore
# define a specific role to limit access
# to certain secrets
region: us-east-1
We are going to get the AWS credentials from the current environment. If you wish to use a secret and key stored in a Kubernetes secret, you could add the section:
# Auth defines the information necessary to authenticate against AWS by
# getting the accessKeyID and secretAccessKey from an already created Kubernetes Secret
auth:
secretRef:
accessKeyID:
name: awssm-secret
key: access-key
secretAccessKey:
name: awssm-secret
key: secret-access-key
Reference: https://external-secrets.io/v0.6.1/api/secretstore/
Ok, now we can create the external secret. If we take a look at the documentation, the secret should look like the following:
| |
To generate a secret with that structure, we will use the template feature of the External Secrets Operator to create the secret. The external secret will look like the following:
| |
Remember to update the url and your SSM parameter key to match your repositories. Remember you are going to be using ssh and not https.
With that out of the way, we can apply the two templates:
| |
If we get any errors and need to review what is going on, we can use the following command:
| |
And if we want to check if the secret created in Kubernetes matches our SSH key:
| |
That’s it. Now we can use our private repository in ArgoCD.
I created a private repository with the following url:
| |
And the project in ArgoCD looks like the following:
| |
The application looks like the following:
| |
Notice that the sourceRepos and the repoURL have changed from https to ssh, so update your repository URLs correctly to use ssh. If we apply the project and the application, we should see it working without a problem in the ArgoCD dashboard.
| |
And That’s it! We now have a private repository in ArgoCD.
Final thoughts
There are many moving parts when we are working with Kubernetes and ArgoCD, and we need to pay a lot of attention to all the small details. I got stuck in the past debugging for a long time until I realised that the repoURL was still using https instead of ssh. And I got stuck another time until I realised I had a typo on the Secret template.
Anyways, I hope this is useful.
References
- External Secrets Operator (ESO)
- ESO - AWS Parameter Store Provider
- ESO - ExternalSecret
- ESO - SecretStore
- ESO - advanced templating
- ArgoCD - declarative setup