Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 59 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
# Deploy Thunderstorm as a Container

[THOR Thunderstorm](https://www.nextron-systems.com/thor-thunderstorm/) is a web service that lets you scan files with our compromise assessment tool THOR through a Web-API. This guide provides a base [container image](https://github.com/NextronSystems/thunderstorm-deployment/pkgs/container/thunderstorm-deployment) and a [Docker Compose template](https://raw.githubusercontent.com/NextronSystems/thunderstorm-deployment/master/docker-compose.yml) so you can run Thunderstorm as a container with just providing your contract token.
[THOR Thunderstorm](https://www.nextron-systems.com/thor-thunderstorm/) is a web service that lets you scan files with our compromise assessment tool THOR through a Web-API. This guide provides a base [container image](https://github.com/NextronSystems/thunderstorm-deployment/pkgs/container/thunderstorm-deployment), a [Docker Compose template](https://raw.githubusercontent.com/NextronSystems/thunderstorm-deployment/master/docker-compose.yml), and a [Helm chart](charts/thunderstorm) so you can run Thunderstorm as a container with just providing your contract token.


## Quick-Start
## Quick-Start (Docker Compose)

1. Download the [Docker Compose](https://raw.githubusercontent.com/NextronSystems/thunderstorm-deployment/master/docker-compose.yml) file

Expand All @@ -21,6 +21,63 @@ CONTRACT_TOKEN=<CONTRACT_TOKEN> docker compose up -d

Thunderstorm is exposed on port **8080** by default.

## Quick-Start (Kubernetes / Helm)

A Helm chart is provided in [charts/thunderstorm](charts/thunderstorm). Requires `kubectl` connected to a cluster and `helm` 3.x.

1. Get a contract token (see [Contract-Token](#contract-token))

2. Install the chart

```
helm install thunderstorm ./charts/thunderstorm \
--namespace thunderstorm --create-namespace \
--set contractToken.value=<CONTRACT_TOKEN>
```

To avoid putting the token on the command line, create the Secret yourself and reference it via `--set contractToken.existingSecret=<secret-name>`.

3. Reach the API (default service type is ClusterIP)

```
kubectl -n thunderstorm port-forward svc/thunderstorm 8080:8080
curl http://localhost:8080/api/status
```

### Common overrides

All settings live in [charts/thunderstorm/values.yaml](charts/thunderstorm/values.yaml). Frequently-tuned knobs:

- `env.TECHPREVIEW=true` — opt into the THOR 11 techpreview channel
- `env.SIGNATURE_UPDATE_INTERVAL=24` — recurring signature refresh (hours)
- `persistence.size`, `persistence.storageClass` — PVC for THOR binaries and signatures
- `resources.requests` / `resources.limits` — set generous memory limits; THOR can be memory-hungry
- `service.type=LoadBalancer` or `ingress.enabled=true` — expose externally

### Expose via Cilium Gateway

If your cluster uses Cilium as the Gateway API implementation and you already have a shared `Gateway` resource, attach an `HTTPRoute` by enabling the chart's `gateway` block. The chart only creates the `HTTPRoute`; the `Gateway` is expected to already exist.

```
helm upgrade thunderstorm ./charts/thunderstorm -n thunderstorm --reuse-values \
--set gateway.enabled=true \
--set gateway.parentRef.name=cilium-gateway \
--set gateway.parentRef.namespace=tooling \
--set gateway.parentRef.sectionName=https-standard \
--set gateway.hostnames[0]=thunderstorm.example.com
```

### Upgrade and uninstall

```
helm upgrade thunderstorm ./charts/thunderstorm -n thunderstorm -f my-values.yaml
helm uninstall thunderstorm -n thunderstorm
# The THOR binaries PVC is retained by default; delete it manually to wipe state:
# kubectl -n thunderstorm delete pvc -l app.kubernetes.io/name=thunderstorm
```

When upgrading after the chart introduces new value defaults, prefer `--reset-then-reuse-values` (Helm 3.14+) over `--reuse-values` — the latter silently drops any new defaults shipped by the chart.

## Contract-Token

Deploying Thunderstorm as a container requires a **non-host-based** Thunderstorm contract with at least one issued license.
Expand Down
10 changes: 10 additions & 0 deletions charts/thunderstorm/.helmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
.DS_Store
.git/
.gitignore
.idea/
.vscode/
*.swp
*.bak
*.tmp
*.orig
*~
17 changes: 17 additions & 0 deletions charts/thunderstorm/Chart.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
apiVersion: v1
name: thunderstorm
description: THOR Thunderstorm - Nextron's web service for scanning files via the compromise-assessment API
version: 0.1.0
appVersion: "latest"
home: https://github.com/NextronSystems/thunderstorm-deployment
sources:
- https://github.com/NextronSystems/thunderstorm-deployment
maintainers:
- name: Marius Benthin
email: marius.benthin@nextron-systems.com
keywords:
- thor
- thunderstorm
- nextron
- security
- scanner
34 changes: 34 additions & 0 deletions charts/thunderstorm/templates/NOTES.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
Thunderstorm has been deployed as release {{ .Release.Name }} in namespace {{ .Release.Namespace }}.

{{- if not (or .Values.contractToken.value .Values.contractToken.existingSecret) }}

WARNING: No CONTRACT_TOKEN configured. On initial install set either:
--set contractToken.value=<token>
--set contractToken.existingSecret=<existing-secret-name>
{{- end }}

To reach the Thunderstorm API:
{{- if .Values.gateway.enabled }}
{{- $section := .Values.gateway.parentRef.sectionName | default "" }}
{{- $scheme := ternary "https" "http" (contains "https" $section) }}
{{- range .Values.gateway.hostnames }}
{{ $scheme }}://{{ . }}
{{- end }}

Attached to Gateway {{ .Values.gateway.parentRef.namespace | default .Release.Namespace }}/{{ .Values.gateway.parentRef.name }}{{ with .Values.gateway.parentRef.sectionName }} (listener {{ . }}){{ end }}.
Check route status:
kubectl --namespace {{ .Release.Namespace }} get httproute {{ include "thunderstorm.fullname" . }} -o yaml
{{- else if .Values.ingress.enabled }}
{{- range .Values.ingress.hosts }}
http{{ if $.Values.ingress.tls }}s{{ end }}://{{ .host }}
{{- end }}
{{- else if eq .Values.service.type "ClusterIP" }}
kubectl --namespace {{ .Release.Namespace }} port-forward svc/{{ include "thunderstorm.fullname" . }} 8080:{{ .Values.service.port }}
curl http://localhost:8080/api/status
{{- else if eq .Values.service.type "LoadBalancer" }}
kubectl --namespace {{ .Release.Namespace }} get svc {{ include "thunderstorm.fullname" . }}
{{- else if eq .Values.service.type "NodePort" }}
export NODE_PORT=$(kubectl --namespace {{ .Release.Namespace }} get svc {{ include "thunderstorm.fullname" . }} -o jsonpath='{.spec.ports[0].nodePort}')
export NODE_IP=$(kubectl --namespace {{ .Release.Namespace }} get nodes -o jsonpath='{.items[0].status.addresses[0].address}')
curl http://$NODE_IP:$NODE_PORT/api/status
{{- end }}
82 changes: 82 additions & 0 deletions charts/thunderstorm/templates/_helpers.tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
{{/*
Expand the name of the chart.
*/}}
{{- define "thunderstorm.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
{{- end }}

{{/*
Create a default fully qualified app name.
*/}}
{{- define "thunderstorm.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 }}

{{/*
Chart label.
*/}}
{{- define "thunderstorm.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
{{- end }}

{{/*
Common labels.
*/}}
{{- define "thunderstorm.labels" -}}
helm.sh/chart: {{ include "thunderstorm.chart" . }}
{{ include "thunderstorm.selectorLabels" . }}
{{- if .Chart.AppVersion }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end }}

{{/*
Selector labels.
*/}}
{{- define "thunderstorm.selectorLabels" -}}
app.kubernetes.io/name: {{ include "thunderstorm.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}

{{/*
Service account name.
*/}}
{{- define "thunderstorm.serviceAccountName" -}}
{{- if .Values.serviceAccount.create }}
{{- default (include "thunderstorm.fullname" .) .Values.serviceAccount.name }}
{{- else }}
{{- default "default" .Values.serviceAccount.name }}
{{- end }}
{{- end }}

{{/*
Contract token secret name.
*/}}
{{- define "thunderstorm.contractTokenSecretName" -}}
{{- if .Values.contractToken.existingSecret }}
{{- .Values.contractToken.existingSecret }}
{{- else }}
{{- printf "%s-contract-token" (include "thunderstorm.fullname" .) }}
{{- end }}
{{- end }}

{{/*
TLS secret name.
*/}}
{{- define "thunderstorm.tlsSecretName" -}}
{{- if .Values.tls.existingSecret }}
{{- .Values.tls.existingSecret }}
{{- else }}
{{- printf "%s-tls" (include "thunderstorm.fullname" .) }}
{{- end }}
{{- end }}
123 changes: 123 additions & 0 deletions charts/thunderstorm/templates/deployment.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "thunderstorm.fullname" . }}
labels:
{{- include "thunderstorm.labels" . | nindent 4 }}
spec:
replicas: 1
strategy:
type: Recreate
selector:
matchLabels:
{{- include "thunderstorm.selectorLabels" . | nindent 6 }}
template:
metadata:
{{- with .Values.podAnnotations }}
annotations:
{{- toYaml . | nindent 8 }}
{{- end }}
labels:
{{- include "thunderstorm.selectorLabels" . | nindent 8 }}
{{- with .Values.podLabels }}
{{- toYaml . | nindent 8 }}
{{- end }}
spec:
serviceAccountName: {{ include "thunderstorm.serviceAccountName" . }}
{{- with .Values.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.podSecurityContext }}
securityContext:
{{- toYaml . | nindent 8 }}
{{- end }}
containers:
- name: thunderstorm
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
{{- with .Values.securityContext }}
securityContext:
{{- toYaml . | nindent 12 }}
{{- end }}
ports:
- name: http
containerPort: 8080
protocol: TCP
env:
- name: CONTRACT_TOKEN
valueFrom:
secretKeyRef:
name: {{ include "thunderstorm.contractTokenSecretName" . }}
key: {{ .Values.contractToken.existingSecretKey | default "CONTRACT_TOKEN" }}
optional: true
{{- if .Values.tls.enabled }}
- name: TLS_CERT
value: /run/secrets/tls/tls.crt
- name: TLS_KEY
value: /run/secrets/tls/tls.key
{{- end }}
{{- range $key, $value := .Values.env }}
{{- if $value }}
- name: {{ $key }}
value: {{ $value | quote }}
{{- end }}
{{- end }}
volumeMounts:
- name: data
mountPath: /opt/nextron/thunderstorm
{{- if .Values.tls.enabled }}
- name: tls
mountPath: /run/secrets/tls
readOnly: true
{{- end }}
{{- if .Values.livenessProbe.enabled }}
livenessProbe:
exec:
command:
- /bin/sh
- -c
- '[ -n "$TECHPREVIEW" ] || [ "${SIGNATURE_UPDATE_INTERVAL:-0}" = "0" ] || [ $(($(date +%s) - $(stat -c %Y /proc/1))) -lt $(( ${SIGNATURE_UPDATE_INTERVAL} * 3600 )) ]'
initialDelaySeconds: {{ .Values.livenessProbe.initialDelaySeconds }}
periodSeconds: {{ .Values.livenessProbe.periodSeconds }}
timeoutSeconds: {{ .Values.livenessProbe.timeoutSeconds }}
failureThreshold: {{ .Values.livenessProbe.failureThreshold }}
{{- end }}
{{- if .Values.readinessProbe.enabled }}
readinessProbe:
tcpSocket:
port: http
initialDelaySeconds: {{ .Values.readinessProbe.initialDelaySeconds }}
periodSeconds: {{ .Values.readinessProbe.periodSeconds }}
timeoutSeconds: {{ .Values.readinessProbe.timeoutSeconds }}
failureThreshold: {{ .Values.readinessProbe.failureThreshold }}
{{- end }}
{{- with .Values.resources }}
resources:
{{- toYaml . | nindent 12 }}
{{- end }}
volumes:
- name: data
{{- if .Values.persistence.enabled }}
persistentVolumeClaim:
claimName: {{ .Values.persistence.existingClaim | default (include "thunderstorm.fullname" .) }}
{{- else }}
emptyDir: {}
{{- end }}
{{- if .Values.tls.enabled }}
- name: tls
secret:
secretName: {{ include "thunderstorm.tlsSecretName" . }}
{{- end }}
{{- with .Values.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.affinity }}
affinity:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.tolerations }}
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}
38 changes: 38 additions & 0 deletions charts/thunderstorm/templates/httproute.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{{- if .Values.gateway.enabled }}
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: {{ include "thunderstorm.fullname" . }}
labels:
{{- include "thunderstorm.labels" . | nindent 4 }}
{{- with .Values.gateway.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
parentRefs:
- group: gateway.networking.k8s.io
kind: Gateway
name: {{ required "gateway.parentRef.name is required when gateway.enabled is true" .Values.gateway.parentRef.name }}
{{- with .Values.gateway.parentRef.namespace }}
namespace: {{ . }}
{{- end }}
{{- with .Values.gateway.parentRef.sectionName }}
sectionName: {{ . }}
{{- end }}
{{- with .Values.gateway.hostnames }}
hostnames:
{{- toYaml . | nindent 4 }}
{{- end }}
rules:
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
- group: ""
kind: Service
name: {{ include "thunderstorm.fullname" . }}
port: {{ .Values.service.port }}
weight: 1
{{- end }}
Loading