UNCLASSIFIED

Commit edafb27c authored by Kavitha Thulasiraman's avatar Kavitha Thulasiraman
Browse files

add anchore-admission-controller

parent ec16633a
Pipeline #181437 failed with stages
in 6 seconds
# 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
*~
# Various IDEs
.project
.idea/
*.tmproj
name: anchore-admission-controller
description: A kubernetes admission controller for validating and mutating webhooks that operates against Anchore Engine to make access decisions and annotations
apiVersion: v1
appVersion: 0.2.2
version: 0.2.11
home: https://github.com/anchore/kubernetes-admission-controller
maintainers:
- name: zhill
email: zach@anchore.com
- name: btodhunter
email: bradyt@anchore.com
icon: https://anchore.com/wp-content/uploads/2016/08/anchore.png
# Anchore Admission Controller
This chart deploys an admission controller for kubernetes that makes admission decisions based on policy-based evaluation of image content.
The controller's code is at: https://github.com/anchore/kubernetes-admission-controller , with more details on the implementation and config there.
This chart is a simple wrapper to wire up credentials, configuration, and setup rbac, tls, and api service config for the controller.
## Running the chart
1. The chart does not deploy an anchore engine service, if you don't already have anchore running, you can use the anchore chart
to deploy one with:
```
helm install --name anchore stable/anchore-engine
```
Setup of policies and users is covered in the anchore documentation, for this readme we'll use admin user credentials, but it
is *strongly* suggested that you use a non-admin user for the controller credential.
1. Create a secret for the anchore credentials that the controller will use to make api calls to Anchore. This must be done out-of-band of the chart creation and should be in the
same namespace you will deploy the chart to. The file must be a json file with the format:
```
{
"users": [
{ "username": "user1", "password": "password"},
{ "uesrname": "user2", "password": "password2"},
...
]
}
```
The file *must* be named `credentials.json` in the secret so that it mounts properly in the pod.
Not all users in the anchore engine need to be specified, only those that will be referenced in the controller configuration.
To create the secret:
```
kubectl create secret generic anchore-credentials --from-file=credentials.json
```
Next, create a _values.yaml_ for the chart with a minimum set of keys:
```
existingCredentialsSecret: anchore-credentials
anchoreEndpoint: <anchore service endpoint for external api>
policySelectors:
- Selector:
ResourceType: "image"
SelectorKeyRegex: ".*"
SelectorValueRegex: ".*"
PolicyReference:
Username: "admin"
# This is the default bundle id in anchore engine
PolicyBundleId: "2c53a13c-1765-11e8-82ef-23527761d060"
# Mode is one of: "policy", "analysis", or "breakglass". policy=>require policy pass, analysis=>require image analyzed, breakglass=>do nothing
Mode: breakglass
```
Finally install the chart with:
```
helm install --name <release name> --repo https://charts.anchore.io/stable anchore-admission-controller -f <path to values.yaml>
```
If you need to delete and re-install the chart, you will find the [cleanup script](files/cleanup.sh) useful.
It will remove kubernetes objects which are not removed by a helm delete. Pass the release name as an argument.
## Chart Configuration
| Key | Expected Type | Default Value | Description |
|---|---|---|---|
|replicaCount | int | 1 | replicas, should generally only need one
|---|---|---|---|
|logVerbosity | int | 6 | log verbosity of controller, 1 = error, 2 warn, 3 debug....
|---|---|---|---|
|image | str | release tag | Tag including registry and repository for image to use
|---|---|---|---|
|imagePullPolicy | str | IfNotPresent | Standard k8s pull policy setting
|---|---|---|---|
|service.name | str | anchoreadmissioncontroller | Name for the svc instance
|---|---|---|---|
|service.type | str | ClusterIp | Type to use for k8s service definition
|---|---|---|---|
|service.internalPort | int | 443 | Port the pod listens on
|---|---|---|---|
|service.externalPort | int | 443 | Port to expose to service clients
|---|---|---|---|
|apiService.group | str | admission.anchore.io | Service group implemented by the service image (must match that presented by controller)
|---|---|---|---|
|apiService.version | str | v1beta1 | api service version, should not need to be updated
|---|---|---|---|
|credentialSecret | str | null | Name of the secret to use for credentials
|---|---|---|---|
|anchoreEndpoint | str | "" | Anchore URL to use for api access
|---|---|---|---|
|policySelectors | array | default catch-all | Selector rules, see the project github page for detail on format and options.
|---|---|---|---|
|requestAnalysis | boolean | true | Ask anchore to analyze an image that isn't already analyzed
|---|---|---|---|
|initCa.extraEnv | array | [] | Define custom environment variables to pass to init-ca pod |
|---|---|---|---|
## Updating configuration
Updates to configuration are handled dynamically by the service, so updates to the chart can be applied without restarting
the pods.
Modify the values.yaml you're using and simply run: `helm upgrade <release> -f values.yaml`
Using the '--recreate-pods' is not required to get updates of config to the running controller.
credentials:
users:
- username: user1
password: password1
- username: user2
password: password2
#!/bin/bash
ns="default"
if [ -n "${2}" ]
then
ns="${2}"
fi
echo "Using ns = ${ns}"
kubectl -n "${ns}" delete clusterrolebinding/extension-"${1}"-anchore-admission-controller-init-ca-cluster
kubectl -n "${ns}" delete rolebinding/extension-"${1}"-anchore-admission-controller-init-ca-admin
kubectl -n "${ns}" delete role/"${1}"-anchore-admission-controller-init-ca
kubectl -n "${ns}" delete clusterrole/"${1}"-anchore-admission-controller-init-ca-cluster
kubectl -n "${ns}" delete serviceaccount/"${1}"-anchore-admission-controller-init-ca
kubectl -n "${ns}" delete cm/"${1}"-init-ca
kubectl -n "${ns}" delete job/"${1}"-init-ca
kubectl delete APIService/v1beta1.admission.anchore.io
kubectl -n "${ns}" delete secret/"${1}"-anchore-admission-controller-certs
Anchore admission controller is now installed.
{{/* vim: set filetype=mustache: */}}
{{/*
Expand the name of the chart.
*/}}
{{- define "anchore-admission-controller.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
{{- 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).
*/}}
{{- define "anchore-admission-controller.fullname" -}}
{{- $name := default .Chart.Name .Values.nameOverride -}}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{/*
Create chart name and version as used by the chart label.
*/}}
{{- define "anchore-admission-controller.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{/*
Common labels
*/}}
{{- define "anchore-admission-controller.labels" -}}
app.kubernetes.io/name: {{ include "anchore-admission-controller.name" . }}
helm.sh/chart: {{ include "anchore-admission-controller.chart" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- if .Chart.AppVersion }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end -}}
# to let the admission server read the namespace reservations
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
labels: {{- include "anchore-admission-controller.labels" . | nindent 4 }}
name: {{ template "anchore-admission-controller.fullname" . }}
rules:
- apiGroups:
- {{ .Values.apiService.group }}
resources:
- {{ template "anchore-admission-controller.fullname" . }}
verbs:
- get
- list
- watch
- create
# Allow delegate authentication and authorization to the service account
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: auth-delegator-{{ template "anchore-admission-controller.fullname" . }}-default
labels: {{- include "anchore-admission-controller.labels" . | nindent 4 }}
roleRef:
kind: ClusterRole
apiGroup: rbac.authorization.k8s.io
name: system:auth-delegator
subjects:
- kind: ServiceAccount
namespace: {{ .Release.Namespace }}
name: {{ template "anchore-admission-controller.fullname" . }}
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: auth-delegator-{{ template "anchore-admission-controller.fullname" . }}-admin
labels: {{- include "anchore-admission-controller.labels" . | nindent 4 }}
roleRef:
kind: ClusterRole
apiGroup: rbac.authorization.k8s.io
name: cluster-admin
subjects:
- kind: ServiceAccount
namespace: {{ .Release.Namespace }}
name: {{ template "anchore-admission-controller.fullname" . }}
---
# to let the admission server read the namespace reservations
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: {{ template "anchore-admission-controller.fullname" . }}-default
labels: {{- include "anchore-admission-controller.labels" . | nindent 4 }}
roleRef:
kind: ClusterRole
apiGroup: rbac.authorization.k8s.io
name: {{ template "anchore-admission-controller.fullname" . }}
subjects:
- kind: ServiceAccount
namespace: {{ .Release.Namespace }}
name: {{ template "anchore-admission-controller.fullname" . }}
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-controller-config
labels: {{- include "anchore-admission-controller.labels" . | nindent 4 }}
data:
config.json: |-
{
"validator": {
"requestanalysis": {{ .Values.requestAnalysis }}
},
"AnchoreEndpoint": "{{ .Values.anchoreEndpoint }}",
"PolicySelectors": {{ toJson .Values.policySelectors }}
}
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ template "anchore-admission-controller.fullname" . }}
labels: {{- include "anchore-admission-controller.labels" . | nindent 4 }}
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
app.kubernetes.io/name: {{ include "anchore-admission-controller.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
template:
metadata:
labels: {{- include "anchore-admission-controller.labels" . | nindent 8 }}
spec:
serviceAccountName: {{ template "anchore-admission-controller.fullname" . }}
volumes:
- name: serving-cert
secret:
defaultMode: 420
secretName: {{ template "anchore-admission-controller.name" . }}-certs
- name: controller-config
configMap:
name: {{.Release.Name}}-controller-config
- name: anchore-auth
secret:
secretName: {{ if .Values.existingCredentialsSecret }}{{ .Values.existingCredentialsSecret }}{{ else }}{{ template "anchore-admission-controller.fullname" . }}{{ end }}
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image }}"
imagePullPolicy: {{ .Values.imagePullPolicy }}
command:
- "/anchore-kubernetes-admission-controller"
- "--audit-log-path=-"
- "--tls-cert-file=/var/serving-cert/tls.crt"
- "--tls-private-key-file=/var/serving-cert/tls.key"
- "--v={{ .Values.logVerbosity }}"
- "--secure-port={{ .Values.service.internalPort }}"
ports:
- containerPort: {{ .Values.service.internalPort }}
readinessProbe:
httpGet:
path: /healthz
port: {{ .Values.service.internalPort }}
scheme: HTTPS
volumeMounts:
- mountPath: /var/serving-cert
name: serving-cert
readOnly: true
- mountPath: /config
name: controller-config
- mountPath: /credentials
name: anchore-auth
env:
- name: CONFIG_FILE_PATH
value: /config/config.json
- name: CREDENTIALS_FILE_PATH
value: /credentials/credentials.json
resources: {{- toYaml .Values.resources | nindent 12 }}
{{- if .Values.nodeSelector }}
nodeSelector: {{- toYaml .Values.nodeSelector | nindent 8 }}
{{- end }}
{{- if .Values.tolerations }}
tolerations: {{- toYaml .Values.tolerations | nindent 8 }}
{{- end }}
{{- if .Values.affinity }}
affinity: {{- toYaml .Values.affinity | nindent 8 }}
{{- end }}
apiVersion: batch/v1
kind: Job
metadata:
name: "{{ .Release.Name }}-init-ca"
labels:
app: {{ template "anchore-admission-controller.fullname" . }}
component: admission-server
annotations:
"helm.sh/hook": pre-install
"helm.sh/hook-delete-policy": hook-succeeded
spec:
template:
metadata:
labels:
app: {{ template "anchore-admission-controller.fullname" . }}
component: admission-server
spec:
serviceAccountName: {{ template "anchore-admission-controller.fullname" . }}-init-ca
restartPolicy: OnFailure
volumes:
- name: init-ca-script
configMap:
name: {{.Release.Name}}-init-ca
containers:
- name: create-ca
image: "cfssl/cfssl:latest"
command:
- bash
- -xe
- /scripts/init-ca.sh
volumeMounts:
- name: init-ca-script
mountPath: /scripts
env:
{{- with .Values.initCa.extraEnv }}
{{- toYaml . | nindent 8 }}
{{- end }}
apiVersion: v1
kind: ConfigMap
metadata:
name: {{.Release.Name}}-init-ca
labels:
app: {{ template "anchore-admission-controller.fullname" . }}
annotations:
"helm.sh/hook": pre-install
"helm.sh/hook-weight": "-5"
data:
init-ca.sh: |-
#!/bin/bash -xe
# Adapted from https://github.com/openshift/kubernetes-namespace-reservation/blob/master/hack/install-kube.sh
apt-get update && apt-get install -y jq
curl -LO https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/amd64/kubectl
chmod +x ./kubectl
mv ./kubectl /usr/bin
# creates a client CA, args are sudo, dest-dir, ca-id, purpose
# purpose is dropped in after "key encipherment", you usually want
# '"client auth"'
# '"server auth"'
# '"client auth","server auth"'
function kube::util::create_signing_certkey {
local sudo=$1
local dest_dir=$2
local id=$3
local purpose=$4
# Create client ca
${sudo} /bin/bash -e <<EOF
rm -f "${dest_dir}/${id}-ca.crt" "${dest_dir}/${id}-ca.key"
openssl req -x509 -sha256 -new -nodes -days 365 -newkey rsa:2048 -keyout "${dest_dir}/${id}-ca.key" -out "${dest_dir}/${id}-ca.crt" -subj "/C=xx/ST=x/L=x/O=x/OU=x/CN=ca/emailAddress=x/"
echo '{"signing":{"default":{"expiry":"43800h","usages":["signing","key encipherment",${purpose}]}}}' > "${dest_dir}/${id}-ca-config.json"
EOF
}
# signs a serving certificate: args are sudo, dest-dir, ca, filename (roughly), subject, hosts...
function kube::util::create_serving_certkey {
local sudo=$1
local dest_dir=$2
local ca=$3
local id=$4
local cn=${5:-$4}
local hosts=""
local SEP=""
shift 5
while [ -n "${1:-}" ]; do
hosts+="${SEP}\"$1\""
SEP=","
shift 1
done
${sudo} /bin/bash -e <<EOF
cd ${dest_dir}
echo '{"CN":"${cn}","hosts":[${hosts}],"key":{"algo":"rsa","size":2048}}' | cfssl gencert -ca=${ca}.crt -ca-key=${ca}.key -config=${ca}-config.json - | cfssljson -bare serving-${id}
mv "serving-${id}-key.pem" "serving-${id}.key"
mv "serving-${id}.pem" "serving-${id}.crt"
rm -f "serving-${id}.csr"
EOF
}
which jq &>/dev/null || { echo "Please install jq (https://stedolan.github.io/jq/)."; exit 1; }
which cfssljson &>/dev/null || { echo "Please install cfssljson (https://github.com/cloudflare/cfssl))."; exit 1; }
# create necessary TLS certificates:
# - a local CA key and cert
# - a webhook server key and cert signed by the local CA
rm -rf ./_output/
CERT_DIR=_output/tmp/certs
mkdir -p "${CERT_DIR}"
kube::util::create_signing_certkey "" "${CERT_DIR}" serving '"server auth"'
# create webhook server key and cert
kube::util::create_serving_certkey "" "${CERT_DIR}" "serving-ca" {{ template "anchore-admission-controller.fullname" . }}.{{ .Release.Namespace }}.svc "{{ template "anchore-admission-controller.fullname" . }}.{{ .Release.Namespace }}.svc" "{{ template "anchore-admission-controller.fullname" . }}.{{ .Release.Namespace }}.svc"
cat > secret.yaml <<EOF
apiVersion: v1
kind: Secret
metadata:
name: {{ template "anchore-admission-controller.name" . }}-certs
type: kubernetes.io/tls
data:
tls.crt: TLS_SERVING_CERT
tls.key: TLS_SERVING_KEY
EOF
sed "s/TLS_SERVING_CERT/$(base64 ${CERT_DIR}/serving-{{ template "anchore-admission-controller.fullname" . }}.{{ .Release.Namespace }}.svc.crt | tr -d '\n')/g" secret.yaml |
sed "s/TLS_SERVING_KEY/$(base64 ${CERT_DIR}/serving-{{ template "anchore-admission-controller.fullname" . }}.{{ .Release.Namespace }}.svc.key | tr -d '\n')/g" | kubectl -n {{ .Release.Namespace }} apply -f -
cat > api-service.yaml <<EOF
apiVersion: apiregistration.k8s.io/v1beta1
kind: APIService
metadata:
name: {{ .Values.apiService.version }}.{{ .Values.apiService.group }}
spec:
caBundle: SERVICE_SERVING_CERT_CA
group: {{ .Values.apiService.group }}
groupPriorityMinimum: 1000
versionPriority: 15
service:
name: {{ template "anchore-admission-controller.fullname" . }}
namespace: {{ .Release.Namespace }}
version: {{ .Values.apiService.version }}
EOF
sed "s/SERVICE_SERVING_CERT_CA/$(base64 ${CERT_DIR}/serving-ca.crt | tr -d '\n')/g" api-service.yaml | kubectl -n {{ .Release.Namespace }} apply -f -
apiVersion: v1
kind: ServiceAccount
metadata:
namespace: {{ .Release.Namespace }}
annotations:
"helm.sh/hook": pre-install
"helm.sh/hook-weight": "-6"
name: {{ template "anchore-admission-controller.fullname" . }}-init-ca
---
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
namespace: {{ .Release.Namespace }}
name: {{ template "anchore-admission-controller.fullname" . }}-init-ca
annotations:
"helm.sh/hook": pre-install
"helm.sh/hook-weight": "-6"
rules:
- apiGroups: [""] # "" indicates the core API group
resources: ["secrets", "deployments"]
verbs: ["get", "patch", "create"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
annotations:
"helm.sh/hook": pre-install
"helm.sh/hook-weight": "-6"
namespace: {{ .Release.Namespace }}
name: extension-{{ template "anchore-admission-controller.fullname" . }}-init-ca-admin
roleRef:
kind: Role
apiGroup: rbac.authorization.k8s.io
name: {{ template "anchore-admission-controller.fullname" . }}-init-ca
subjects:
- kind: ServiceAccount
name: {{ template "anchore-admission-controller.fullname" . }}-init-ca
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: {{ template "anchore-admission-controller.fullname" . }}-init-ca-cluster
annotations:
"helm.sh/hook": pre-install
"helm.sh/hook-weight": "-6"
rules:
- apiGroups: ["admissionregistration.k8s.io"]
resources: ["validatingwebhookconfigurations"]
verbs: ["get", "watch", "list", "create", "update", "patch"]
- apiGroups: ["apiregistration.k8s.io"]
resources: ["apiservices"]
verbs: ["get", "watch", "list", "create", "update", "patch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
annotations:
"helm.sh/hook": pre-install
"helm.sh/hook-weight": "-6"
name: extension-{{ template "anchore-admission-controller.fullname" . }}-init-ca-cluster
roleRef:
kind: ClusterRole
apiGroup: rbac.authorization.k8s.io
name: {{ template "anchore-admission-controller.fullname" . }}-init-ca-cluster
subjects:
- kind: ServiceAccount
namespace: {{ .Release.Namespace }}
name: {{ template "anchore-admission-controller.fullname" . }}-init-ca
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
namespace: kube-system
name: extension-{{ template "anchore-admission-controller.fullname" . }}-authentication-reader-default
labels: {{- include "anchore-admission-controller.labels" . | nindent 4 }}
roleRef:
kind: Role
apiGroup: rbac.authorization.k8s.io
name: extension-apiserver-authentication-reader
subjects:
- kind: ServiceAccount
name: {{ template "anchore-admission-controller.fullname" . }}
namespace: {{ .Release.Namespace }}
apiVersion: v1
kind: Secret
metadata:
name: {{ template "anchore-admission-controller.fullname" . }}
labels: {{- include "anchore-admission-controller.labels" . | nindent 4 }}
type: Opaque
data:
credentials.json: |
{{ toJson .Values.credentials | b64enc | indent 4 }}
apiVersion: v1
kind: Service
metadata:
name: {{ template "anchore-admission-controller.fullname" . }}
labels: {{- include "anchore-admission-controller.labels" . | nindent 4 }}
spec:
type: {{ .Values.service.type }}
ports:
- port: {{ .Values.service.externalPort }}
targetPort: {{ .Values.service.internalPort }}
protocol: TCP
name: {{ .Values.service.name }}
selector:
app.kubernetes.io/name: {{ template "anchore-admission-controller.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
# Service Account with which the controller operates
apiVersion: v1
kind: ServiceAccount
metadata:
name: {{ template "anchore-admission-controller.fullname" . }}
labels: {{- include "anchore-admission-controller.labels" . | nindent 4 }}
{{- if .Values.apiService.webhook.enabled }}
apiVersion: admissionregistration.k8s.io/v1beta1
kind: ValidatingWebhookConfiguration
metadata:
name: {{ template "anchore-admission-controller.name" . }}-admission.anchore.io
labels: {{- include "anchore-admission-controller.labels" . | nindent 4 }}
webhooks:
- name: {{ template "anchore-admission-controller.name" . }}-admission.anchore.io
clientConfig:
service:
namespace: default
name: kubernetes
path: /apis/{{ .Values.apiService.group }}/v1beta1/imagechecks
{{- with .Values.apiService.webhook.caBundle }}
caBundle: {{ . | b64enc }}
{{- end }}
rules:
- operations:
- CREATE
apiGroups:
- ""
apiVersions:
- "*"
resources:
- pods
failurePolicy: {{ .Values.apiService.webhook.failurePolicy }}
namespaceSelector: {{- toYaml .Values.apiService.namespaceSelector | nindent 4 }}
{{- end }}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment