Initial commit

This commit is contained in:
Martin Šalata
2021-04-02 21:53:17 +02:00
commit da0fc5b188
27 changed files with 2162 additions and 0 deletions

View File

@@ -0,0 +1,23 @@
# Patterns to ignore when building packages.
# This supports shell glob matching, relative path matching, and
# negation (prefixed with !). Only one pattern per line.
.DS_Store
# Common VCS dirs
.git/
.gitignore
.bzr/
.bzrignore
.hg/
.hgignore
.svn/
# Common backup files
*.swp
*.bak
*.tmp
*.orig
*~
# Various IDEs
.project
.idea/
*.tmproj
.vscode/

View File

@@ -0,0 +1,23 @@
apiVersion: v2
name: imagepullsecret-injector
description: A Helm chart for Kubernetes
# A chart can be either an 'application' or a 'library' chart.
#
# Application charts are a collection of templates that can be packaged into versioned archives
# to be deployed.
#
# Library charts provide useful utilities or functions for the chart developer. They're included as
# a dependency of application charts to inject those utilities and functions into the rendering
# pipeline. Library charts do not define any templates and therefore cannot be deployed.
type: application
# This is the chart version. This version number should be incremented each time you make changes
# to the chart and its templates, including the app version.
# Versions are expected to follow Semantic Versioning (https://semver.org/)
version: 0.0.1
# This is the version number of the application being deployed. This version number should be
# incremented each time you make changes to the application. Versions are not expected to
# follow Semantic Versioning. They should reflect the version the application is using.
appVersion: v0.0.1-b326755-dirty

View File

@@ -0,0 +1,137 @@
#!/bin/bash
set -e
usage() {
cat <<EOF
Generate certificate suitable for use with an imagepullsecret-injector webhook service.
This script uses k8s' CertificateSigningRequest API to a generate a
certificate signed by k8s CA suitable for use with imagepullsecret-injector webhook
services. This requires permissions to create and approve CSR. See
https://kubernetes.io/docs/tasks/tls/managing-tls-in-a-cluster for
detailed explanation and additional instructions.
The server key/cert k8s CA cert are stored in a k8s secret.
usage: ${0} [OPTIONS]
The following flags are required.
--service Service name of webhook.
--namespace Namespace where webhook service and secret reside.
--secret Secret name for CA certificate and server certificate/key pair.
EOF
exit 1
}
while [[ $# -gt 0 ]]; do
case ${1} in
--service)
service="$2"
shift
;;
--secret)
secret="$2"
shift
;;
--namespace)
namespace="$2"
shift
;;
*)
usage
;;
esac
shift
done
[ -z "${service}" ] && service=ips-injector-svc
[ -z "${secret}" ] && secret=imagepullsecret-injector-webhook-certs
[ -z "${namespace}" ] && namespace=imagepullsecret-injector
if [ ! -x "$(command -v openssl)" ]; then
echo "openssl not found"
exit 1
fi
csrName=${service}.${namespace}
tmpdir=$(mktemp -d)
echo "Creating certs in tmpdir ${tmpdir}"
cat <<EOF >> "${tmpdir}"/csr.conf
[req]
req_extensions = v3_req
distinguished_name = req_distinguished_name
[req_distinguished_name]
[ v3_req ]
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth
subjectAltName = @alt_names
[alt_names]
DNS.1 = ${service}
DNS.2 = ${service}.${namespace}
DNS.3 = ${service}.${namespace}.svc
EOF
openssl genrsa -out "${tmpdir}"/server-key.pem 2048
openssl req -new -key "${tmpdir}"/server-key.pem -subj "/O=system:nodes/CN=system:node:${service}.${namespace}.svc" -out "${tmpdir}"/server.csr -config "${tmpdir}"/csr.conf
# clean-up any previously created CSR for our service. Ignore errors if not present.
echo "Deleting old CertificateSigningRequests"
kubectl delete csr ${csrName} 2>/dev/null || true
echo "Creating new CertificateSigningRequests"
# create server cert/key CSR and send to k8s API
cat <<EOF | kubectl create -f -
apiVersion: certificates.k8s.io/v1
kind: CertificateSigningRequest
metadata:
name: ${csrName}
spec:
signerName: kubernetes.io/kubelet-serving
groups:
- system:authenticated
request: $(< "${tmpdir}"/server.csr base64 | tr -d '\n')
usages:
- digital signature
- key encipherment
- server auth
EOF
# verify CSR has been created
while true; do
if kubectl get csr ${csrName}; then
break
else
sleep 1
fi
done
echo "Approving CertificateSigningRequests"
# approve and fetch the signed certificate
kubectl certificate approve ${csrName}
echo "Fetching certificate from approved CertificateSigningRequests"
# verify certificate has been signed
for _ in $(seq 10); do
serverCert=$(kubectl get csr ${csrName} -o jsonpath='{.status.certificate}')
if [[ ${serverCert} != '' ]]; then
break
fi
sleep 1
done
if [[ ${serverCert} == '' ]]; then
echo "ERROR: After approving csr ${csrName}, the signed certificate did not appear on the resource. Giving up after 10 attempts." >&2
exit 1
fi
echo "${serverCert}" | openssl base64 -d -A -out "${tmpdir}"/server-cert.pem
echo "Creating secret $secret based on the retrieved certificate"
# create the secret with CA cert and server cert/key
kubectl create secret generic ${secret} \
--from-file=key.pem="${tmpdir}"/server-key.pem \
--from-file=cert.pem="${tmpdir}"/server-cert.pem \
--dry-run=client -o yaml |
kubectl -n ${namespace} apply -f -

View File

@@ -0,0 +1,77 @@
{{/* vim: set filetype=mustache: */}}
{{/*
Expand the name of the chart.
*/}}
{{- define "imagepullsecret-injector.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- define "imagepullsecret-injector.serviceName" -}}
ips-injector-svc
{{- end }}
{{- define "imagepullsecret-injector.lookupCaBundle" -}}
{{- /* Find the name of the secret corresponding to the default SA in the default namespace */ -}}
{{- /* Equivalent to `kubectl get sa -n default default -ojsonpath='{.secrets[0].name}'` */ -}}
{{- $defaultSecretName := ((lookup "v1" "ServiceAccount" "default" "default").secrets | first).name -}}
{{- /* Fetch the ca.crt from the default secret (still base64-encoded)*/ -}}
{{- /* Equivalent to `kubectl get secret -n default $defaultSecretName -ojsonpath='{.data.ca\.crt}'` */ -}}
{{- $caBundle := get (lookup "v1" "Secret" "default" $defaultSecretName ).data "ca.crt" -}}
{{- $caBundle -}}
{{- end }}
{{/*
Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
If release name contains chart name it will be used as a full name.
*/}}
{{- define "imagepullsecret-injector.fullname" -}}
{{- if .Values.fullnameOverride }}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- $name := default .Chart.Name .Values.nameOverride }}
{{- if contains $name .Release.Name }}
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- end }}
{{- end }}
{{/*
Create chart name and version as used by the chart label.
*/}}
{{- define "imagepullsecret-injector.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Common labels
*/}}
{{- define "imagepullsecret-injector.labels" -}}
helm.sh/chart: {{ include "imagepullsecret-injector.chart" . }}
{{ include "imagepullsecret-injector.selectorLabels" . }}
{{- if .Chart.AppVersion }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end }}
{{/*
Selector labels
*/}}
{{- define "imagepullsecret-injector.selectorLabels" -}}
app.kubernetes.io/name: {{ include "imagepullsecret-injector.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}
{{/*
Create the name of the service account to use
*/}}
{{- define "imagepullsecret-injector.serviceAccountName" -}}
{{- if .Values.serviceAccount.create }}
{{- default (include "imagepullsecret-injector.fullname" .) .Values.serviceAccount.name }}
{{- else }}
{{- default "default" .Values.serviceAccount.name }}
{{- end }}
{{- end }}

View File

@@ -0,0 +1,9 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: imagepullsecret-injector-cert-gen-entrypoint
namespace: {{ .Release.Namespace }}
labels:
{{- include "imagepullsecret-injector.labels" . | nindent 4 }}
data:
entrypoint.sh: | {{ .Files.Get "scripts/create-signed-cert.sh" | nindent 4 }}

View File

@@ -0,0 +1,33 @@
apiVersion: batch/v1beta1
kind: CronJob
metadata:
name: "{{ .Release.Name }}"
labels:
{{- include "imagepullsecret-injector.labels" . | nindent 4 }}
spec:
schedule: '* * * * *'
jobTemplate:
metadata:
name: "{{ .Release.Name }}"
labels:
{{- include "imagepullsecret-injector.labels" . | nindent 8 }}
spec:
template:
spec:
serviceAccountName: imagepullsecret-injector-cert-gen
restartPolicy: Never
containers:
- name: pre-install-job
image: "{{ .Values.certificateGeneratorImage.registry }}/{{ .Values.certificateGeneratorImage.repository }}:{{ .Values.certificateGeneratorImage.tag | default .Chart.AppVersion }}"
command: ["/entrypoint/entrypoint.sh"]
volumeMounts:
- mountPath: "/entrypoint"
name: entrypoint
volumes:
- name: entrypoint
configMap:
name: imagepullsecret-injector-cert-gen-entrypoint
items:
- key: entrypoint.sh
path: entrypoint.sh
mode: 0755

View File

@@ -0,0 +1,26 @@
apiVersion: batch/v1
kind: Job
metadata:
name: "{{ .Release.Name }}"
labels:
{{- include "imagepullsecret-injector.labels" . | nindent 4 }}
spec:
template:
spec:
serviceAccountName: imagepullsecret-injector-cert-gen
restartPolicy: Never
containers:
- name: pre-install-job
image: "{{ .Values.certificateGeneratorImage.registry }}/{{ .Values.certificateGeneratorImage.repository }}:{{ .Values.certificateGeneratorImage.tag | default .Chart.AppVersion }}"
command: ["/entrypoint/entrypoint.sh"]
volumeMounts:
- mountPath: "/entrypoint"
name: entrypoint
volumes:
- name: entrypoint
configMap:
name: imagepullsecret-injector-cert-gen-entrypoint
items:
- key: entrypoint.sh
path: entrypoint.sh
mode: 0755

View File

@@ -0,0 +1,68 @@
apiVersion: v1
kind: ServiceAccount
metadata:
name: imagepullsecret-injector-cert-gen
namespace : {{ .Release.Namespace }}
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
labels:
k8s-app: imagepullsecret-injector-cert-gen
name: imagepullsecret-injector-cert-gen
rules:
- apiGroups:
- ""
resources:
- secrets
- serviceaccounts
verbs:
- list
- patch
- create
- get
- delete
- apiGroups:
- ""
resources:
- namespaces
verbs:
- list
- get
- apiGroups:
- "certificates.k8s.io/v1"
resources:
- certificatesigningrequests
verbs:
- create
- list
- get
- apiGroups:
- "certificates.k8s.io/v1"
resources:
- certificatesigningrequests/approval
verbs:
- update
- apiGroups:
- certificates.k8s.io
resources:
- signers
resourceNames:
- kubernetes.io/kubelet-serving
verbs:
- approve
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: imagepullsecret-injector-cert-gen
labels:
{{- include "imagepullsecret-injector.labels" . | nindent 4 }}
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: imagepullsecret-injector-cert-gen
subjects:
- kind: ServiceAccount
name: imagepullsecret-injector-cert-gen
namespace : {{ .Release.Namespace }}

View File

@@ -0,0 +1,7 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: tmp
namespace: {{ .Release.Namespace }}
data:
caBundle: {{ include "imagepullsecret-injector.lookupCaBundle" . | quote }}

View File

@@ -0,0 +1,52 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: imagepullsecret-injector-webhook-deployment
namespace : {{ .Release.Namespace }}
labels:
app: imagepullsecret-injector
{{- include "imagepullsecret-injector.labels" . | nindent 4 }}
spec:
replicas: 1
selector:
matchLabels:
app: imagepullsecret-injector
template:
metadata:
labels:
app: imagepullsecret-injector
spec:
serviceAccountName: imagepullsecret-injector
containers:
- name: imagepullsecret-injector
image: "{{ .Values.image.registry }}/{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: IfNotPresent
args:
- -alsologtostderr
- -v=4
- 2>&1
env:
- name: CONFIG_PORT
value: "8443"
- name: CONFIG_CERT_PATH
value: "/etc/webhook/certs/cert.pem"
- name: CONFIG_KEY_PATH
value: "/etc/webhook/certs/key.pem"
- name: CONFIG_EXCLUDE_NAMESPACES
value: {{ join "," .Values.imagepullsecretInjector.excludeNamespaces | quote }}
- name: CONFIG_SERVICE_ACCOUNTS
value: {{ join "," .Values.imagepullsecretInjector.saNames | quote }}
- name: CONFIG_SOURCE_IMAGE_PULL_SECRET_NAME
value: {{ .Values.imagepullsecretInjector.dockerconfigjsonRef.secretName | quote }}
- name: CONFIG_SOURCE_IMAGE_PULL_SECRET_NAMESPACE
value: {{ .Values.imagepullsecretInjector.dockerconfigjsonRef.secretNamespace | default .Release.Namespace | quote }}
- name: CONFIG_ALL_SERVICE_ACCOUNTS
value: {{ .Values.imagepullsecretInjector.allSaNames | quote }}
volumeMounts:
- name: webhook-certs
mountPath: /etc/webhook/certs
readOnly: true
volumes:
- name: webhook-certs
secret:
secretName: imagepullsecret-injector-webhook-certs

View File

@@ -0,0 +1,23 @@
apiVersion: admissionregistration.k8s.io/v1
kind: MutatingWebhookConfiguration
metadata:
name: imagepullsecret-injector-webhook-cfg
namespace: {{ .Release.Namespace }}
labels:
app: imagepullsecret-injector
{{- include "imagepullsecret-injector.labels" . | nindent 4 }}
webhooks:
- name: imagepullsecret-injector.ysoftdevs.github.com
clientConfig:
service:
name: {{ include "imagepullsecret-injector.serviceName" . }}
namespace : {{ .Release.Namespace }}
path: "/mutate"
caBundle: {{ include "imagepullsecret-injector.lookupCaBundle" . }}
rules:
- operations: ["CREATE", "UPDATE"]
apiGroups: [""]
apiVersions: ["v1"]
resources: ["serviceaccounts"]
admissionReviewVersions: ["v1", "v1beta1"]
sideEffects: None

View File

@@ -0,0 +1,71 @@
apiVersion: v1
kind: ServiceAccount
metadata:
name: imagepullsecret-injector
namespace : {{ .Release.Namespace }}
labels:
{{- include "imagepullsecret-injector.labels" . | nindent 4 }}
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
labels:
k8s-app: imagepullsecret-injector
{{- include "imagepullsecret-injector.labels" . | nindent 4 }}
name: imagepullsecret-injector
rules:
- apiGroups:
- ""
resources:
- secrets
- serviceaccounts
verbs:
- list
- patch
- create
- get
- delete
- apiGroups:
- ""
resources:
- namespaces
verbs:
- list
- get
- apiGroups:
- "certificates.k8s.io/v1"
resources:
- certificatesigningrequests
verbs:
- create
- list
- get
- apiGroups:
- "certificates.k8s.io/v1"
resources:
- certificatesigningrequests/approval
verbs:
- update
- apiGroups:
- certificates.k8s.io
resources:
- signers
resourceNames:
- kubernetes.io/kubelet-serving
verbs:
- approve
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: imagepullsecret-injector
labels:
{{- include "imagepullsecret-injector.labels" . | nindent 4 }}
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: imagepullsecret-injector
subjects:
- kind: ServiceAccount
name: imagepullsecret-injector
namespace : {{ .Release.Namespace }}

View File

@@ -0,0 +1,14 @@
apiVersion: v1
kind: Service
metadata:
name: {{ include "imagepullsecret-injector.serviceName" . }}
namespace : {{ .Release.Namespace }}
labels:
app: imagepullsecret-injector
{{- include "imagepullsecret-injector.labels" . | nindent 4 }}
spec:
ports:
- port: 443
targetPort: 8443
selector:
app: imagepullsecret-injector

View File

@@ -0,0 +1,24 @@
image:
registry: marshallmarshall
repository: imagepullsecret-injector
pullPolicy: Always
# Overrides the image tag whose default is the chart appVersion.
tag: ""
certificateGeneratorImage:
registry: marshallmarshall
repository: webhook-cert-generator
tag: ""
imagepullsecretInjector:
dockerconfigjsonRef:
secretName: my-cool-secret-source
secretNamespace: ""
allSaNames: false
saNames:
- default
excludeNamespaces:
- kube-system
- traefik
- datadog