-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add helm chart to rotate registry creds #29
Changes from 3 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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 |
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. |
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 }} |
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 }} |
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 }} |
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 }} |
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"] |
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: "" |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
apiVersion: v1 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Security control: Iac Misconfig Detection Kubernetes Type: Applications Credentials In Configuration Files Description: More details in the link below Severity: HIGH Jit Bot commands and options (e.g., ignore issue)You can trigger Jit actions by commenting on this PR review:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. #jit_ignore_fp |
||
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" |
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 }} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Security control: Iac Misconfig Detection Kubernetes
Type: Applications Credentials In Configuration Files
Description: More details in the link below
Severity: HIGH
Learn more about this issue
Jit Bot commands and options (e.g., ignore issue)
You can trigger Jit actions by commenting on this PR review:
#jit_ignore_fp
Ignore and mark this specific single instance of finding as “False Positive”#jit_ignore_accept
Ignore and mark this specific single instance of finding as “Accept Risk”#jit_ignore_type_in_file
Ignore any finding of type "Applications credentials in configuration files" in src/kubernetes/jit_ecr/templates/script-configmap.yaml; future occurrences will also be ignored.#jit_undo_ignore
Undo ignore commandThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
#jit_ignore_fp