-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #29 from jitsecurity/sc-27540-helm-chart-to-rotate…
…-ecr-secrets Add helm chart to rotate registry creds
- Loading branch information
Showing
11 changed files
with
401 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
apiVersion: v2 | ||
name: jit_ecr | ||
version: 0.1.0 | ||
description: Helm chart to manage registry credentials for JIT using authentication APIs |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,129 @@ | ||
# JIT Container Registry Credentials Manager | ||
|
||
## Overview | ||
|
||
This Helm chart deploys a system to manage and rotate Jit container registry secrets automatically. It ensures that containers (which are controls) can be pulled from the Jit Container Registry by maintaining up-to-date credentials. | ||
|
||
## Purpose | ||
|
||
The main purpose of this chart is to: | ||
|
||
1. Authenticate with the Jit API to obtain container registry credentials. | ||
2. Create and update a Kubernetes secret containing these credentials. | ||
3. Periodically refresh the credentials to ensure continued access to the Jit Container Registry. | ||
|
||
## Components | ||
|
||
The chart consists of the following main components: | ||
|
||
1. **Initial Login Job**: A one-time job that runs immediately after chart installation to set up the initial container registry credentials. | ||
2. **Refresh CronJob**: A periodically running job that refreshes the container registry credentials to ensure they remain valid. | ||
3. **ConfigMap**: Contains the script used by both the initial job and the CronJob to fetch and update the credentials. | ||
4. **ServiceAccount and RBAC**: Provides necessary permissions for the jobs to create and update secrets in the specified namespace. | ||
|
||
## Prerequisites | ||
|
||
- Kubernetes 1.16+ | ||
- Helm 3.0+ | ||
- A valid Jit account with API credentials (Client ID and Secret) | ||
- These can be obtained from https://docs.jit.io/docs/managing-users#generating-api-tokens | ||
- The API credentials should be created with the Member role | ||
- The secret should be kept securely and not shared or exposed publicly | ||
|
||
## Installation | ||
|
||
To install the chart with the release name `jit-registry`: | ||
|
||
```bash | ||
helm install jit-registry . \ | ||
--set client_id=your-client-id \ | ||
--set secret=your-secret \ | ||
--set namespace=your-namespace | ||
``` | ||
|
||
## Configuration | ||
|
||
The following table lists the configurable parameters of the JIT Container Registry Credentials Manager chart and their default values. | ||
|
||
| Parameter | Description | Default | | ||
|-----------|-------------|---------| | ||
| `client_id` | Jit API Client ID (Member role) | `"<JIT_API_CLIENT_ID>"` | | ||
| `secret` | Jit API Secret | `"<JIT_API_SECRET>"` | | ||
| `jit_base_url` | Jit API Base URL | `"https://api.jit.io"` | | ||
| `registry_name` | Jit Container Registry Name | `"registry.jit.io"` | | ||
| `keep_job_history_seconds` | Time (in seconds) to keep job history | `86400` | | ||
| `namespace` | Kubernetes namespace to deploy to | `"default"` | | ||
| `jit_ecr_secret_name` | Name of the Kubernetes secret for container registry credentials | `"jit-registry-creds"` | | ||
|
||
To modify any of these parameters, you can use the `--set key=value[,key=value]` argument to `helm install` or `helm upgrade`, or modify the `values.yaml` file directly. | ||
|
||
Note: The `client_id` and `secret` should be obtained from https://docs.jit.io/docs/managing-users#generating-api-tokens. Make sure to use the "Member" role when generating these credentials. Please store these values in a secure place and never expose them publicly. | ||
|
||
The `jit_ecr_secret_name` should match the Kubernetes runner configuration. For example, in GitLab: | ||
|
||
```yaml | ||
[runners.kubernetes] | ||
poll_timeout = 2000 | ||
node_selector_overwrite_allowed = ".*" | ||
image_pull_secrets=["jit-registry-creds"] | ||
``` | ||
|
||
## Usage | ||
|
||
After installation, the chart will: | ||
|
||
1. Create an initial Kubernetes secret with container registry credentials. | ||
2. Set up a CronJob to refresh these credentials periodically. | ||
|
||
You can use the created secret (`jit-registry-creds` by default) in your pod specifications to pull images from the Jit Container Registry: | ||
|
||
```yaml | ||
spec: | ||
imagePullSecrets: | ||
- name: jit-registry-creds | ||
containers: | ||
- name: your-container | ||
image: registry.jit.io/your-image:tag | ||
``` | ||
## Monitoring and Troubleshooting | ||
To check the status of the initial login job: | ||
```bash | ||
kubectl get jobs -n your-namespace jit-registry-initial-login | ||
``` | ||
|
||
To check the status of the refresh CronJob: | ||
|
||
```bash | ||
kubectl get cronjobs -n your-namespace jit-registry-refresh | ||
``` | ||
|
||
To view logs of the most recent job execution: | ||
|
||
```bash | ||
kubectl logs -n your-namespace job/jit-registry-refresh-<job-id> | ||
``` | ||
|
||
For more detailed instructions, please refer to the NOTES.txt file that is displayed after chart installation. | ||
|
||
## Uninstalling the Chart | ||
|
||
To uninstall/delete the `jit-registry` deployment: | ||
|
||
```bash | ||
helm delete jit-registry | ||
``` | ||
|
||
This command removes all the Kubernetes components associated with the chart and deletes the release. | ||
|
||
## Security Considerations | ||
|
||
- The Jit API Secret is sensitive information. Always handle it securely and avoid exposing it in logs, command-line arguments, or version control systems. | ||
- Use Kubernetes Secrets or a secure secrets management system to store the `client_id` and `secret`. | ||
- Regularly rotate your Jit API credentials as per your organization's security policies. | ||
|
||
## Support | ||
|
||
For any issues or questions, please contact Jit support or open an issue in the chart's repository. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
{{- if .Values.client_id }} | ||
{{- if .Values.secret }} | ||
Congratulations! You've installed the JIT registry credentials manager. | ||
|
||
To verify that the initial login was successful, run the following command: | ||
|
||
kubectl logs -n {{ .Values.namespace }} $(kubectl get pods -n {{ .Values.namespace }} -l job-name=jit-ecr-initial-login --sort-by=.metadata.creationTimestamp --output=jsonpath='{.items[-1:].metadata.name}') | ||
|
||
This command will display the logs of the most recently created pod for the initial login job. Look for the message "registry credentials updated successfully on <date>" at the end of the logs. | ||
|
||
If you don't see the success message or encounter any errors, you can describe the job for more information: | ||
kubectl describe job jit-ecr-initial-login -n {{ .Values.namespace }} | ||
|
||
The registry credentials secret has been created as {{ .Values.jit_ecr_secret_name }} in the {{ .Values.namespace }} namespace. | ||
|
||
To verify the created secret, run: | ||
kubectl get secret {{ .Values.jit_ecr_secret_name }} --namespace {{ .Values.namespace }} | ||
|
||
A CronJob has also been set up to refresh these credentials periodically. You can check its status and logs with: | ||
kubectl get cronjob -n {{ .Values.namespace }} jit-ecr-refresh | ||
kubectl logs -n {{ .Values.namespace }} $(kubectl get pods -n {{ .Values.namespace }} -l cronjob-name=jit-ecr-refresh --sort-by=.metadata.creationTimestamp --output=jsonpath='{.items[-1:].metadata.name}') | ||
|
||
{{- else }} | ||
Error: "secret" value is mandatory. | ||
{{- end }} | ||
{{- else }} | ||
Error: "client_id" value is mandatory. | ||
{{- end }} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
{{- if .Values.client_id }} | ||
{{- if .Values.secret }} | ||
--- | ||
apiVersion: batch/v1 | ||
kind: CronJob | ||
metadata: | ||
name: jit-ecr-refresh | ||
namespace: {{ .Values.namespace }} | ||
spec: | ||
schedule: "0 */10 * * *" | ||
successfulJobsHistoryLimit: 1 | ||
failedJobsHistoryLimit: 1 | ||
jobTemplate: | ||
spec: | ||
ttlSecondsAfterFinished: {{ .Values.keep_job_history_seconds }} | ||
template: | ||
spec: | ||
serviceAccountName: sa-jit-ecr | ||
containers: | ||
- name: jit-ecr-refresh | ||
image: alpine/k8s:1.26.8 | ||
command: ["/bin/sh", "/scripts/jit-ecr-script.sh"] | ||
volumeMounts: | ||
- name: secrets | ||
mountPath: /secrets | ||
readOnly: true | ||
- name: script-volume | ||
mountPath: /scripts | ||
readOnly: true | ||
resources: | ||
limits: | ||
cpu: 100m | ||
memory: 128Mi | ||
volumes: | ||
- name: secrets | ||
secret: | ||
secretName: ecr-registry-helper-secrets | ||
- name: script-volume | ||
configMap: | ||
name: jit-ecr-script | ||
restartPolicy: OnFailure | ||
{{- end }} | ||
{{- end }} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
{{- if .Values.client_id }} | ||
{{- if .Values.secret }} | ||
--- | ||
apiVersion: v1 | ||
kind: Secret | ||
metadata: | ||
name: ecr-registry-helper-secrets | ||
namespace: {{ .Values.namespace }} | ||
stringData: | ||
client_id: "{{ .Values.client_id }}" | ||
secret: "{{ .Values.secret }}" | ||
{{- end }} | ||
{{- end }} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
{{- if .Values.client_id }} | ||
{{- if .Values.secret }} | ||
--- | ||
apiVersion: batch/v1 | ||
kind: Job | ||
metadata: | ||
name: jit-ecr-initial-login | ||
namespace: {{ .Values.namespace }} | ||
spec: | ||
ttlSecondsAfterFinished: {{ .Values.keep_job_history_seconds }} | ||
template: | ||
spec: | ||
serviceAccountName: sa-jit-ecr | ||
containers: | ||
- name: jit-ecr-initial-login | ||
image: alpine/k8s:1.26.8 | ||
command: ["/bin/sh", "/scripts/jit-ecr-script.sh"] | ||
volumeMounts: | ||
- name: secrets | ||
mountPath: /secrets | ||
readOnly: true | ||
- name: script-volume | ||
mountPath: /scripts | ||
readOnly: true | ||
resources: | ||
limits: | ||
cpu: 100m | ||
memory: 128Mi | ||
volumes: | ||
- name: secrets | ||
secret: | ||
secretName: ecr-registry-helper-secrets | ||
- name: script-volume | ||
configMap: | ||
name: jit-ecr-script | ||
restartPolicy: OnFailure | ||
{{- end }} | ||
{{- end }} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
apiVersion: rbac.authorization.k8s.io/v1 | ||
kind: Role | ||
metadata: | ||
namespace: {{ .Values.namespace }} | ||
name: role-access-to-jit-ecr-secret | ||
rules: | ||
- apiGroups: [""] | ||
resources: ["secrets"] | ||
verbs: ["delete"] | ||
resourceNames: ["{{ .Values.jit_ecr_secret_name }}"] | ||
- apiGroups: [""] | ||
resources: ["secrets"] | ||
verbs: ["create"] | ||
- apiGroups: [""] | ||
resources: ["secrets"] | ||
verbs: ["get"] | ||
resourceNames: ["ecr-registry-helper-secrets"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
kind: RoleBinding | ||
apiVersion: rbac.authorization.k8s.io/v1 | ||
metadata: | ||
name: jit-ecr-role-binding | ||
namespace: {{ .Values.namespace }} | ||
subjects: | ||
- kind: ServiceAccount | ||
name: sa-jit-ecr | ||
namespace: {{ .Values.namespace }} | ||
apiGroup: "" | ||
roleRef: | ||
kind: Role | ||
name: role-access-to-jit-ecr-secret | ||
apiGroup: "" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
apiVersion: v1 | ||
kind: ConfigMap | ||
metadata: | ||
name: jit-ecr-script | ||
namespace: {{ .Values.namespace }} | ||
data: | ||
jit-ecr-script.sh: | | ||
#!/bin/sh | ||
set -e | ||
# Install jq | ||
apk add --no-cache jq | ||
# Wait for the secret to be available | ||
until kubectl get secret ecr-registry-helper-secrets -n {{ .Values.namespace }}; do | ||
echo "Waiting for secret ecr-registry-helper-secrets to be created..." | ||
sleep 5 | ||
done | ||
# Read credentials from the secret | ||
CLIENT_ID=$(kubectl get secret ecr-registry-helper-secrets -n {{ .Values.namespace }} -o jsonpath='{.data.client_id}' | base64 -d) | ||
SECRET=$(kubectl get secret ecr-registry-helper-secrets -n {{ .Values.namespace }} -o jsonpath='{.data.secret}' | base64 -d) | ||
# Perform login to get access token | ||
echo "Requesting access token..." | ||
AUTH_RESPONSE=$(curl -s -w "\n%{http_code}" -X POST {{ .Values.jit_base_url }}/authentication/login \ | ||
-H "Content-Type: application/json" \ | ||
-d "{\"clientId\": \"$CLIENT_ID\", \"secret\": \"$SECRET\"}") | ||
AUTH_STATUS=$(echo "$AUTH_RESPONSE" | tail -n1) | ||
AUTH_BODY=$(echo "$AUTH_RESPONSE" | sed '$d') | ||
if [ "$AUTH_STATUS" -ne 200 ]; then | ||
echo "Failed to obtain access token. HTTP Status: $AUTH_STATUS" | ||
echo "Response body: $AUTH_BODY" | ||
exit 1 | ||
fi | ||
ACCESS_TOKEN=$(echo "$AUTH_BODY" | jq -r '.accessToken') | ||
if [ -z "$ACCESS_TOKEN" ] || [ "$ACCESS_TOKEN" = "null" ]; then | ||
echo "Failed to extract access token from response" | ||
echo "Response body: $AUTH_BODY" | ||
exit 1 | ||
fi | ||
echo "Access token obtained successfully" | ||
# Use access token to get registry credentials | ||
echo "Requesting registry token..." | ||
REGISTRY_RESPONSE=$(curl -s -w "\n%{http_code}" -X POST {{ .Values.jit_base_url }}/authentication/registry/login \ | ||
-H "Authorization: Bearer $ACCESS_TOKEN") | ||
REGISTRY_STATUS=$(echo "$REGISTRY_RESPONSE" | tail -n1) | ||
REGISTRY_BODY=$(echo "$REGISTRY_RESPONSE" | sed '$d') | ||
if [ "$REGISTRY_STATUS" -ne 200 ]; then | ||
echo "Failed to obtain registry token. HTTP Status: $REGISTRY_STATUS" | ||
echo "Response body: $REGISTRY_BODY" | ||
exit 1 | ||
fi | ||
REGISTRY_TOKEN="$REGISTRY_BODY" | ||
if [ -z "$REGISTRY_TOKEN" ]; then | ||
echo "Failed to obtain registry token" | ||
echo "Response body: $REGISTRY_BODY" | ||
exit 1 | ||
fi | ||
echo "registry token obtained successfully" | ||
# Delete existing Kubernetes Docker registry secret (ignore if not found) | ||
echo "Deleting existing Kubernetes secret (if any)..." | ||
kubectl delete secret {{ .Values.jit_ecr_secret_name }} -n {{ .Values.namespace }} 2>/dev/null || true | ||
# Create new Kubernetes Docker registry secret | ||
echo "Creating new Kubernetes secret..." | ||
kubectl create secret docker-registry {{ .Values.jit_ecr_secret_name }} \ | ||
--docker-server={{ .Values.registry_name }} \ | ||
--docker-username=AWS \ | ||
--docker-password="$REGISTRY_TOKEN" \ | ||
--namespace={{ .Values.namespace }} | ||
# Get current date and time | ||
CURRENT_DATE=$(date "+%Y-%m-%d %H:%M:%S") | ||
echo "Registry credentials updated successfully on $CURRENT_DATE" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
apiVersion: v1 | ||
kind: ServiceAccount | ||
metadata: | ||
name: sa-jit-ecr | ||
namespace: {{ .Values.namespace }} |
Oops, something went wrong.