Skip to content

Commit

Permalink
Change init behaviour
Browse files Browse the repository at this point in the history
  • Loading branch information
swoehrl-mw committed May 31, 2022
1 parent 2e610a8 commit f9ec5ad
Show file tree
Hide file tree
Showing 15 changed files with 384 additions and 108 deletions.
27 changes: 25 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,30 @@ If you want to use the current master version from git:

1. Install the chart: `helm install --namespace bridgekeeper --create-namespace bridgekeeper ./charts/bridgekeeper`

### Configuration

Bridgekeeper has a number of options to configure behaviour. They can be set via helm values:

```yaml
replicaCount: 1 # Number of instances of bridgekeeper to run, should be >1 for production setups

installCRDs: true # By default the helm chart installs the CRD, set to false if you want to do this in a separate workflow

bridgekeeper:
# namespaces to ignore for validation, you should add the namespace you install bridgekeeper in
ignoreNamespaces:
- kube-system
- kube-public
- kube-node-lease
# If set to true any requests in non-ignored namespaces will fail while bridgekeeper is not available (sets failure policy to "Fail")
strictAdmission: false
audit:
# Set this to true if you want bridgekeeper to run regular audits, if enabled you should have replicaCount: 1
enabled: false
# Audit interval in seconds
interval: 600
```
### Writing constraints
Bridgekeeper uses a custom resource called Constraint to manage policies. A constraint consists of a target that describes what kubernetes resources are to be validated by the constraint and a rule script written in python.
Expand Down Expand Up @@ -122,7 +146,7 @@ This service is written in Rust and uses [kube-rs](https://github.com/clux/kube-
1. Compile binary: `cargo build`
2. Generate certificates and install webhook: `cargo run -- init --local host.k3d.internal:8081`
3. Install CRD: `kubectl apply -f charts/bridgekeeper/crds/constraint.yaml`
4. Launch bridgekeeper: `cargo run -- server --cert-dir .certs`
4. Launch bridgekeeper: `cargo run -- server --cert-dir .certs --local host.k3d.internal:8081`

After you are finished, run `cargo run -- cleanup --local` to delete the webook.

Expand All @@ -140,4 +164,3 @@ If you change the schema of the CRD (via `src/crd.rs`) you need to regenerate th
## Planned features

* Give rules access to existing objects of the same type (to do e.g. uniqueness checks)
* Ability to modify/patch resources
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
{{- if .Values.installCRDs }}
---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
Expand All @@ -6,12 +7,15 @@ metadata:
spec:
group: bridgekeeper.maibornwolff.de
names:
categories: []
kind: Constraint
plural: constraints
shortNames: []
singular: constraint
scope: Cluster
versions:
- name: v1alpha1
- additionalPrinterColumns: []
name: v1alpha1
schema:
openAPIV3Schema:
description: "Auto-generated derived type for ConstraintSpec via `CustomResource`"
Expand Down Expand Up @@ -94,3 +98,4 @@ spec:
storage: true
subresources:
status: {}
{{- end }}
3 changes: 3 additions & 0 deletions charts/bridgekeeper/templates/deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ spec:
- bridgekeeper
args:
- server
{{- if .Values.bridgekeeper.strictAdmission }}
- "--strict-admission"
{{- end }}
{{- if .Values.bridgekeeper.audit.enabled }}
- --audit
- "--audit-interval"
Expand Down
116 changes: 115 additions & 1 deletion charts/bridgekeeper/templates/init.yaml
Original file line number Diff line number Diff line change
@@ -1,10 +1,124 @@
{{- if .Values.bridgekeeper.runInit }}
apiVersion: v1
kind: ServiceAccount
metadata:
name: {{ include "bridgekeeper.serviceAccountName" . }}-init
labels:
{{- include "bridgekeeper.labels" . | nindent 4 }}
annotations:
"helm.sh/hook": pre-install,pre-upgrade
"helm.sh/hook-weight": "1"
"helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
labels:
{{- include "bridgekeeper.labels" . | nindent 4 }}
annotations:
"helm.sh/hook": pre-install,pre-upgrade
"helm.sh/hook-weight": "2"
"helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded
name: bridgekeeper-init-role
rules:
# The init job needs to create/update the webhook configurations
- apiGroups:
- admissionregistration.k8s.io
resources:
- mutatingwebhookconfigurations
- validatingwebhookconfigurations
verbs:
- create
- delete
- get
- list
- patch
- update
- watch
# The init job needs to update namespaces with the ignore label
- apiGroups:
- ""
resources:
- namespaces
verbs:
- patch
- update
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
labels:
{{- include "bridgekeeper.labels" . | nindent 4 }}
annotations:
"helm.sh/hook": pre-install,pre-upgrade
"helm.sh/hook-weight": "3"
"helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded
name: bridgekeeper-init-role
namespace: "{{ .Release.Namespace }}"
rules:
# The init job creates a secret with the TLS certificate, the main program needs to read it
- apiGroups:
- ""
resources:
- secrets
verbs:
- create
- delete
- get
- list
- patch
- update
- watch
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
labels:
{{- include "bridgekeeper.labels" . | nindent 4 }}
annotations:
"helm.sh/hook": pre-install,pre-upgrade
"helm.sh/hook-weight": "4"
"helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded
name: bridgekeeper-init-rolebinding
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: bridgekeeper-init-role
subjects:
- kind: ServiceAccount
name: {{ include "bridgekeeper.serviceAccountName" . }}-init
namespace: "{{ .Release.Namespace }}"
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
labels:
{{- include "bridgekeeper.labels" . | nindent 4 }}
annotations:
"helm.sh/hook": pre-install,pre-upgrade
"helm.sh/hook-weight": "5"
"helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded
name: bridgekeeper-init-rolebinding
namespace: "{{ .Release.Namespace }}"
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: bridgekeeper-init-role
subjects:
- kind: ServiceAccount
name: {{ include "bridgekeeper.serviceAccountName" . }}-init
namespace: "{{ .Release.Namespace }}"
---
apiVersion: batch/v1
kind: Job
metadata:
name: "bridgekeeper-init"
labels:
{{- include "bridgekeeper.labels" . | nindent 4 }}
annotations:
"helm.sh/hook": pre-install,pre-upgrade
"helm.sh/hook-weight": "10"
"helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded
spec:
template:
metadata:
Expand All @@ -17,7 +131,7 @@ spec:
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
serviceAccountName: {{ include "bridgekeeper.serviceAccountName" . }}
serviceAccountName: {{ include "bridgekeeper.serviceAccountName" . }}-init
automountServiceAccountToken: true
containers:
- name: bridgekeeper-init
Expand Down
4 changes: 2 additions & 2 deletions charts/bridgekeeper/values.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
replicaCount: 1

installCRDs: true

image:
repository: ghcr.io/maibornwolff/bridgekeeper
pullPolicy: IfNotPresent
Expand Down Expand Up @@ -52,15 +54,13 @@ affinity: {}

bridgekeeper:
# namespaces to ignore for validation
# Note: Changing this value after initial installation has no effect
ignoreNamespaces:
- kube-system
- kube-public
- kube-node-lease
# Set this to false if you want to take care of creating and installing the server cert and the webhook configuration yourself
runInit: true
# If set to true any requests in non-ignored namespaces will fail while bridgekeeper is not available (sets failure policy to "Fail")
# Note: Changing this value after initial installation has no effect
strictAdmission: false
audit:
# Set this to true if you want bridgekeeper to run regular audits
Expand Down
5 changes: 3 additions & 2 deletions src/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ pub const CERT_FILENAME: &str = "tls.crt";
pub const KEY_FILENAME: &str = "tls.key";
pub const CACERT_FILENAME: &str = "tls.key";
pub const SECRET_NAME: &str = "bridgekeeper-webhook-server-cert";
pub const WEBHOOK_NAME: &str = "bridgekeeper-webhook";
pub const CONSTRAINT_VALIDATION_WEBHOOK_NAME: &str = "bridgekeeper-constraint-validation";
pub const ADMISSION_WEBHOOK_NAME: &str = "bridgekeeper-webhook";
pub const SERVICE_NAME: &str = "bridgekeeper-webhook";
pub const MANAGER_NAME: &str = "bridgekeeper";
pub const CRD_FILEPATH: &str = "charts/bridgekeeper/crds/constraint.yaml";
pub const CRD_FILEPATH: &str = "charts/bridgekeeper/templates/crds.yaml";
2 changes: 1 addition & 1 deletion src/evaluator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,7 @@ pub fn evaluate_constraint_audit(
}

fn extract_result(
name: &String,
name: &str,
request: &ValidationRequest,
result: &PyAny,
) -> (bool, Option<String>, Option<json_patch::Patch>) {
Expand Down
10 changes: 8 additions & 2 deletions src/helper/cleanup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,17 @@ pub async fn run(args: Args) {

// Delete webhook
let webhook_api: kube::Api<MutatingWebhookConfiguration> = kube::Api::all(client.clone());
if let Err(err) = webhook_api.delete(WEBHOOK_NAME, &Default::default()).await {
if let Err(err) = webhook_api
.delete(ADMISSION_WEBHOOK_NAME, &Default::default())
.await
{
println!("Encountered error when deleting admission webhook: {}", err);
}
let webhook_api: kube::Api<ValidatingWebhookConfiguration> = kube::Api::all(client.clone());
if let Err(err) = webhook_api.delete(WEBHOOK_NAME, &Default::default()).await {
if let Err(err) = webhook_api
.delete(CONSTRAINT_VALIDATION_WEBHOOK_NAME, &Default::default())
.await
{
println!(
"Encountered error when deleting constraint validation webhook: {}",
err
Expand Down
13 changes: 12 additions & 1 deletion src/helper/gencrd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,22 @@ pub struct Args {
/// file to write yaml to
#[argh(option, short = 'f')]
pub file: Option<String>,
/// do not wrap CRD yaml in helm template if condition
#[argh(switch)]
pub no_wrapping: bool,
}

pub fn run(args: Args) {
let data = serde_yaml::to_string(&Constraint::crd())
.expect("Could not generate yaml from CRD definition");
let filepath = args.file.unwrap_or_else(|| CRD_FILEPATH.to_string());
fs::write(filepath, data).expect("Unable to write crd yaml");
let wrapped_data = "{{- if .Values.installCRDs }}\n".to_string() + &data + "{{- end }}\n";
fs::write(
filepath,
match args.no_wrapping {
true => data,
false => wrapped_data,
},
)
.expect("Unable to write crd yaml");
}
Loading

0 comments on commit f9ec5ad

Please sign in to comment.