From 331c298d3086c8ff05fb5f38910464dbfb6a1f69 Mon Sep 17 00:00:00 2001 From: Trent V Date: Tue, 16 Jan 2024 17:45:54 +0000 Subject: [PATCH] Added multiple features to helm chart example as well as docs (#290) * added multiple features to helm chart example as well as docs Signed-off-by: Trent V * fixed spelling mistaeks Signed-off-by: Trent V * added additional templating to consolidate configuration Signed-off-by: Trent V --------- Signed-off-by: Trent V --- examples/helm/Chart.yaml | 4 +- examples/helm/README.md | 78 +++++++++++++++++++++ examples/helm/templates/cmbh.yaml | 32 +++++++++ examples/helm/templates/deploybh.yaml | 15 +++- examples/helm/templates/deploygraphdb.yaml | 4 +- examples/helm/templates/ingressbh.yaml | 5 +- examples/helm/templates/ingressgraphdb.yaml | 27 +++++++ examples/helm/templates/nsbloodhound.yaml | 2 +- examples/helm/templates/svcappdb.yaml | 2 +- examples/helm/templates/svcgraphdb.yaml | 2 +- examples/helm/values.yaml | 30 ++++++-- 11 files changed, 184 insertions(+), 17 deletions(-) create mode 100644 examples/helm/README.md create mode 100644 examples/helm/templates/cmbh.yaml create mode 100644 examples/helm/templates/ingressgraphdb.yaml diff --git a/examples/helm/Chart.yaml b/examples/helm/Chart.yaml index cefd43aad2..9c152c8368 100644 --- a/examples/helm/Chart.yaml +++ b/examples/helm/Chart.yaml @@ -15,8 +15,8 @@ # SPDX-License-Identifier: Apache-2.0 apiVersion: v2 -name: BloodhoundAD -description: A Helm Chart for deploying BloodHoundAD on Kubernetes +name: Bloodhound +description: A Helm Chart for deploying BloodHound on Kubernetes type: application version: 0.1.0 appVersion: "5.3.1" diff --git a/examples/helm/README.md b/examples/helm/README.md new file mode 100644 index 0000000000..dc8286b5b0 --- /dev/null +++ b/examples/helm/README.md @@ -0,0 +1,78 @@ +# BloodHound Community Edition Helm Chart Example + +BloodHound Community Edition is composed of three distinct parts: + +- A PostgreSQL database used for application state storage +- A Neo4J graph database used for storing all the graph data +- A single binary containing the BloodHound API server and the UI assets + +This Helm chart templates these three services and facilitates communication between them inside a Kubernetes cluster. This chart uses the Prebuilt docker images to run inside of Kubernetes. Helm will allow you to template important information about your deployment. Please use the included `values.yaml` to customize deployment configuration. + +## Prerequisites + +In order to run this chart you will need the following: +- A Kubernetes Cluster in which to deploy (any supported version should work with this chart). +- Admin access or the ability to create Helm charts and the associated Objects in this chart (validate RBAC Policies and Policy Engine configuration) +- Helm (v3+) installed on a local workstation or CI Pipeline. + +## Quick start + +To install this chart inside of a Kubernetes cluster **make sure helm is properly configured to point at the correct cluster within the kubeconfig**. Once You have validated your helm install, clone this repo and run the following command from your workstation or pipeline: + +``` +$ helm install bhce $BH_ROOT/examples/helm/. +``` + +## Accessing +By default, the ingress is enabled. This means the application will be available on the ingress endpoint with the host set to `bloodhound.example.com` (Each of these values can be configured in the values.yaml). As long as you have a properly configured ingress controller and valid DNS configuration the application will be available at `https://bloodhound.example.com`, else without DNS you can test with curl by passing the 'Host' Header: `curl -H 'Host: bloodhound.example.com' https://$endpointIP`. + + If you have a TLS cert you can enable `bloodhound.tls.customCert` in the values.yaml and provide the ingress secret in `bloodhound.tls.certSecret`. Else, the provided cert will be self signed from the ingress controller. + + ## Configuring BloodHound Community Edition + +To configure the Helm Chart deployment of BloodHound Community Edition you can use the two files specified below: + +- `values.yaml` - A general Helm Chart configuration file - you can use this to configure aspects of the deployment of BloodHound and set Environment Variables. This file generally will be the single source of truth for application and deployment configuration. +- `templates/cmbh.yaml` - This is the Kubernetes configmap used for the BloodHound Application - you can add your own custom configuration in the `bloodhound-config.json` section of this file. Some of it is populated with the `values.yaml` as well for portibility + +If using a custom Certificate for TLS please terminate it on the ingress and not the application directly for easier Kubernetes native management. + +Changing the database credentials is not a requirement for development/testing but is **Highly** Recommended for production environments. + + ## Ports +The default ports are as follows: + +- 8080 - BloodHound Web Port. This is the backend port of the service used by BloodHound. +- 7474 - Neo4J Web Interface. Useful for when you need to run queries directly against the Neo4J database. (Note: this is not exposed externally by default) +- 7687 - Neo4J Database Port. This is provided in case you want to access the Neo4J database from some other application on your machine. (Note: this is not exposed externally by default) + + +## Choosing a BloodHound Version + +BloodHound docker images are tagged for each release: + +- `latest` will give you the most recent stable release +- `X` (e.g. `5`) will give you the latest stable release for that major version +- `X.X` (e.g. `5.0`) will give you the latest stable release for that minor version +- `X.X.X` (e.g. `5.0.0`) will give you the release for that specific patch version +- `X.X.X-rcX` (e.g. `5.0.0-rc1`) will give you a specific release candidate for an upcoming release +- `edge` will give you the most recent main commit (not guaranteed to be stable) + +By Default the `latest` tag is set. You can change this by setting the `bloodhound.container.version` in the values.yaml + +## Troubleshooting + +Please assure any local host-based firewall frontends such as `firewalld` or `ufw` on the node is disabled. Please use Kubernetes NetworkPolicies and admission controllers instead. + +Validate all 3 of the Deployments are healthy, Services are active and the ingress is properly configured with `kubectl get -n bloodhoundad deployments,svc,ingress` + +Assure the cluster DNS is resolving the dns for the 'app-db' service and the 'graph-db' service. An easy way to validate this is to spin up a busybox pod in the bloodhoundad namespace and use nslookup to check for resolution issues. + +``` +$ kubectl run -it --rm dns-test -n bloodhoundad --image busybox:latest +# for name in app-db graph-db; do nslookup $name; done +``` +If you are having trouble with cluster DNS please refer to the upstream Kubernetes documentation: +https://kubernetes.io/docs/tasks/administer-cluster/dns-debugging-resolution/ + +Else, please refer to the upstream Kubernetes Documentation for General cluster troubleshooting: https://kubernetes.io/docs/tasks/debug/debug-cluster/. \ No newline at end of file diff --git a/examples/helm/templates/cmbh.yaml b/examples/helm/templates/cmbh.yaml new file mode 100644 index 0000000000..4599e17538 --- /dev/null +++ b/examples/helm/templates/cmbh.yaml @@ -0,0 +1,32 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ .Values.bloodhound.configMap.name }} + namespace: {{ .Values.namespace }} +data: + bloodhound.config.json: | + { + "version": 1, + "bind_addr": "0.0.0.0:{{ .Values.bloodhound.service.port }}", + "metrics_port": ":{{ (index .Values.bloodhound.container.ports 1).containerPort }}", + "root_url": "http://{{ .Values.bloodhound.ingress.host }}", + "work_dir": "/opt/bloodhound/work", + "log_level": "INFO", + "log_path": "bloodhound.log", + "features": { + "enable_auth": true + }, + "database": { + "connection": "user={{ .Values.appdb.container.env.POSTGRES_USER }} password={{ .Values.appdb.container.env.POSTGRES_PASSWORD }} dbname={{ .Values.appdb.container.env.POSTGRES_DB }} host={{ .Values.appdb.service.name }}" + }, + "neo4j": { + "connection": "neo4j://{{ .Values.graphdb.container.env.NEO4J_AUTH_USER }}:{{ .Values.graphdb.container.env.NEO4J_AUTH_PASS }}@{{ .Values.graphdb.service.name }}:{{ (index .Values.graphdb.service.ports 0).port }}/" + }, + "collectors_base_path": "/etc/bloodhound/collectors", + "default_admin": { + "principal_name": "admin", + "first_name": "Bloodhound", + "last_name": "Admin", + "email_address": "spam@example.com" + } + } diff --git a/examples/helm/templates/deploybh.yaml b/examples/helm/templates/deploybh.yaml index 434f7291b9..29177921d7 100644 --- a/examples/helm/templates/deploybh.yaml +++ b/examples/helm/templates/deploybh.yaml @@ -29,9 +29,19 @@ spec: labels: app: {{ .Values.bloodhound.appName }} spec: + initContainers: + - name: check-db-availability + image: busybox + command: ['/bin/sh', '-c'] + args: + - > + until nc -z {{ .Values.graphdb.service.name }} {{ (index .Values.graphdb.service.ports 0).port }} && nc -z {{ .Values.appdb.service.name }} {{ .Values.appdb.service.port }}; do + echo "Waiting for databases to be ready"; + sleep 5; + done; containers: - name: {{ .Values.bloodhound.container.name }} - image: {{ .Values.bloodhound.container.image }} + image: {{ .Values.bloodhound.container.image }}:{{ .Values.bloodhound.container.version }} env: - name: bhe_disable_cypher_qc value: "{{ .Values.bloodhound.container.env.bhe_disable_cypher_qc }}" @@ -40,7 +50,8 @@ spec: {{- if .Values.bloodhound.configMap.enabled }} volumeMounts: - name: bloodhound-config - mountPath: /path/to/config # Update with actual mount path + mountPath: /bloodhound.config.json + subPath: bloodhound.config.json {{- end }} {{- if .Values.bloodhound.configMap.enabled }} volumes: diff --git a/examples/helm/templates/deploygraphdb.yaml b/examples/helm/templates/deploygraphdb.yaml index 0beb7daab2..b88fa067b1 100644 --- a/examples/helm/templates/deploygraphdb.yaml +++ b/examples/helm/templates/deploygraphdb.yaml @@ -34,7 +34,7 @@ spec: image: {{ .Values.graphdb.container.image }} env: - name: NEO4J_AUTH - value: "{{ .Values.graphdb.container.env.NEO4J_AUTH }}" + value: "{{ .Values.graphdb.container.env.NEO4J_AUTH_USER }}/{{ .Values.graphdb.container.env.NEO4J_AUTH_PASS }}" - name: NEO4J_dbms_allow__upgrade value: "{{ .Values.graphdb.container.env.NEO4J_dbms_allow__upgrade }}" ports: @@ -47,7 +47,7 @@ spec: - -O - /dev/null - -q - - http://localhost:7474 + - http://127.0.0.1:{{ (index .Values.graphdb.container.ports 1).containerPort }} initialDelaySeconds: {{ .Values.graphdb.livenessProbe.initialDelaySeconds }} periodSeconds: {{ .Values.graphdb.livenessProbe.periodSeconds }} timeoutSeconds: {{ .Values.graphdb.livenessProbe.timeoutSeconds }} diff --git a/examples/helm/templates/ingressbh.yaml b/examples/helm/templates/ingressbh.yaml index 381e8e4224..7acec481fa 100644 --- a/examples/helm/templates/ingressbh.yaml +++ b/examples/helm/templates/ingressbh.yaml @@ -32,9 +32,12 @@ spec: name: {{ .Values.bloodhound.name }}-service port: number: {{ .Values.bloodhound.service.port }} - {{- if .Values.bloodhound.ingress.tls }} + {{- if .Values.bloodhound.tls.enabled }} tls: - hosts: - {{ .Values.bloodhound.ingress.host }} + {{- if .Values.bloodhound.tls.customCert }} + secretName: {{ .Values.bloodhound.tls.certSecret }} + {{- end }} {{- end }} {{- end }} diff --git a/examples/helm/templates/ingressgraphdb.yaml b/examples/helm/templates/ingressgraphdb.yaml new file mode 100644 index 0000000000..75fa0809c1 --- /dev/null +++ b/examples/helm/templates/ingressgraphdb.yaml @@ -0,0 +1,27 @@ +{{- if .Values.graphdb.ingress.enabled }} +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: {{ .Values.graphdb.name }}-ingress + namespace: {{ .Values.namespace }} +spec: + rules: + - host: {{ .Values.graphdb.ingress.host }} + http: + paths: + - path: {{ .Values.graphdb.ingress.path }} + pathType: Prefix + backend: + service: + name: graph-db + port: + number: {{ (index .Values.graphdb.service.ports 0).http }} + {{- if .Values.graphdb.tls.enabled }} + tls: + - hosts: + - {{ .Values.graphdb.ingress.host }} + {{- if .Values.graphdb.tls.customCert }} + secretName: {{ .Values.graphdb.tls.certSecret }} + {{- end }} + {{- end }} +{{- end }} diff --git a/examples/helm/templates/nsbloodhound.yaml b/examples/helm/templates/nsbloodhound.yaml index e2b8d34a33..ec5df8c7ca 100644 --- a/examples/helm/templates/nsbloodhound.yaml +++ b/examples/helm/templates/nsbloodhound.yaml @@ -17,7 +17,7 @@ apiVersion: v1 kind: Namespace metadata: - name: bloodhoundad + name: bloodhound annotations: "helm.sh/hook": pre-install,pre-upgrade "helm.sh/hook-weight": "-5" # ensures this is executed first diff --git a/examples/helm/templates/svcappdb.yaml b/examples/helm/templates/svcappdb.yaml index a2f6007a55..022ae11779 100644 --- a/examples/helm/templates/svcappdb.yaml +++ b/examples/helm/templates/svcappdb.yaml @@ -18,7 +18,7 @@ apiVersion: v1 kind: Service metadata: - name: app-db + name: {{ .Values.appdb.service.name }} namespace: {{ .Values.namespace }} spec: type: {{ .Values.appdb.service.type }} diff --git a/examples/helm/templates/svcgraphdb.yaml b/examples/helm/templates/svcgraphdb.yaml index 8fc700d7bc..d2ae069414 100644 --- a/examples/helm/templates/svcgraphdb.yaml +++ b/examples/helm/templates/svcgraphdb.yaml @@ -18,7 +18,7 @@ apiVersion: v1 kind: Service metadata: - name: graph-db + name: {{ .Values.graphdb.service.name }} namespace: {{ .Values.namespace }} spec: selector: diff --git a/examples/helm/values.yaml b/examples/helm/values.yaml index 945ae73a53..7ea76aff74 100644 --- a/examples/helm/values.yaml +++ b/examples/helm/values.yaml @@ -14,12 +14,12 @@ # # SPDX-License-Identifier: Apache-2.0 -namespace: bloodhoundad +namespace: bloodhound appdb: - name: bloodhound-postgres + name: postgres replicas: 1 - appName: bloodhound-postgres + appName: postgres container: name: postgres image: docker.io/library/postgres:13.2 @@ -39,6 +39,7 @@ appdb: failureThreshold: 5 service: enabled: true + name: app-db type: ClusterIP port: 5432 targetPort: 5432 @@ -56,13 +57,15 @@ graphdb: name: neo4j image: docker.io/library/neo4j:4.4 env: - NEO4J_AUTH: "neo4j/bloodhoundcommunityedition" + NEO4J_AUTH_USER: "neo4j" + NEO4J_AUTH_PASS: "bloodhoundcommunityedition" NEO4J_dbms_allow__upgrade: "true" ports: - containerPort: 7687 - containerPort: 7474 service: enabled: true + name: graph-db type: ClusterIP ports: - name: graphdb @@ -83,6 +86,14 @@ graphdb: persistentVolumeClaim: claimName: graphdb-neo4j-data-pvc size: "1Gi" # Configurable storage size + ingress: + enabled: false + host: graphdb.example.com + path: / + tls: + enabled: true + customCert: false + certSecret: graphdb-tls bloodhound: name: bloodhound @@ -90,11 +101,13 @@ bloodhound: appName: bloodhound container: name: bloodhound - image: docker.io/specterops/bloodhound:latest + image: docker.io/specterops/bloodhound + version: latest env: bhe_disable_cypher_qc: "false" ports: - containerPort: 8080 + - containerPort: 2112 service: enabled: true type: ClusterIP @@ -104,7 +117,10 @@ bloodhound: enabled: true host: bloodhound.example.com path: / - tls: false + tls: + enabled: true + customCert: false + certSecret: bloodhound-tls configMap: - enabled: false + enabled: true name: bloodhound-config