feat(yeti): add it

This commit is contained in:
Tommy 2024-05-13 17:15:39 +02:00
parent 6dd85fc5b0
commit 5ad5613ea0
No known key found for this signature in database
13 changed files with 671 additions and 0 deletions

20
charts/yeti/Chart.yaml Normal file
View file

@ -0,0 +1,20 @@
apiVersion: v2
name: yeti
version: 1.0.2
description: A Helm chart for Yeti Kubernetes deployments.
keywords:
- yeti
- dfir
- security
home: "https://yeti-platform.io/"
maintainers:
- name: Tommy Skaug
email: tommy@skaug.me
url: https://github.com/tommy-skaug/charts
sources:
- https://github.com/yeti-platform/yeti
- https://github.com/google/osdfir-infrastructure
appVersion: "latest"
annotations:
category: Security
licenses: Apache-2.0

1
charts/yeti/README.org Normal file
View file

@ -0,0 +1 @@
* TODO figure out what the pvc was used for and if we can do without it

View file

@ -0,0 +1,71 @@
{{/*
Init Container for when a Timesketch pod starts. To prevent duplicate code,
this file has been created which then applies to both the Timesketch Web and
Worker pod upon startup.
*/}}
{{- define "yeti.envs" -}}
- name: YETI_REDIS_HOST
value: {{ .Values.redis.host }}
- name: YETI_REDIS_PORT
value: "{{ .Values.redis.port }}"
- name: YETI_REDIS_DATABASE
value: "0"
- name: YETI_ARANGODB_HOST
value: {{ .Values.arangodb.host }}
- name: YETI_ARANGODB_PORT
value: {{ .Values.arangodb.port | quote }}
- name: YETI_ARANGODB_DATABASE
value: {{ .Values.arangodb.database | quote }}
- name: YETI_ARANGODB_USERNAME
valueFrom:
secretKeyRef:
name: {{ .Values.existingSecret }}
key: YETI_ARANGODB_USERNAME
- name: YETI_ARANGODB_PASSWORD
valueFrom:
secretKeyRef:
name: {{ .Values.existingSecret }}
key: YETI_ARANGODB_PASSWORD
- name: YETI_AUTH_SECRET_KEY
valueFrom:
secretKeyRef:
name: {{ .Values.existingSecret }}
key: YETI_AUTH_SECRET_KEY
- name: YETI_AUTH_ALGORITHM
value: HS256
- name: YETI_AUTH_ACCESS_TOKEN_EXPIRE_MINUTES
value: "30"
- name: YETI_AUTH_ENABLED
value: "True"
- name: YETI_SYSTEM_PLUGINS_PATH
value: "./plugins"
- name: YETI_USER_USERNAME
valueFrom:
secretKeyRef:
name: {{ .Values.existingSecret }}
key: YETI_USER
- name: YETI_USER_PASSWORD
valueFrom:
secretKeyRef:
name: {{ .Values.existingSecret }}
key: YETI_USER_PASSWORD
- name: YETI_API_KEY
valueFrom:
secretKeyRef:
name: {{ .Values.existingSecret }}
key: YETI_API_KEY
{{- if .Values.timesketch.enabled }}
- name: YETI_TIMESKETCH_ENDPOINT
value: {{ .Values.timesketch.endpoint }}
- name: YETI_TIMESKETCH_USERNAME
valueFrom:
secretKeyRef:
name: {{ .Values.existingSecret }}
key: YETI_TIMESKETCH_USERNAME
- name: YETI_TIMESKETCH_PASSWORD
valueFrom:
secretKeyRef:
name: {{ .Values.existingSecret }}
key: YETI_TIMESKETCH_PASSWORD
{{- end }}
{{- end }}

View file

@ -0,0 +1,45 @@
{{/*
Expand the name of the chart.
*/}}
{{- define "yeti.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Create a default fully qualified app name.
*/}}
{{- define "yeti.fullname" -}}
{{- if contains .Chart.Name .Release.Name }}
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- printf "%s-%s" .Release.Name "yeti" | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- end }}
{{/*
Create chart name and version as used by the chart label.
*/}}
{{- define "yeti.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Common labels
*/}}
{{- define "yeti.labels" -}}
helm.sh/chart: {{ include "yeti.chart" . }}
{{ include "yeti.selectorLabels" . }}
{{- if .Chart.AppVersion }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
date: "{{ now | htmlDate }}"
{{- end }}
{{/*
Selector labels
*/}}
{{- define "yeti.selectorLabels" -}}
app.kubernetes.io/name: {{ include "yeti.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}

View file

@ -0,0 +1,181 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ include "timesketch.fullname" . }}-init-configmap
namespace: {{ .Release.Namespace | quote }}
labels:
{{- include "timesketch.labels" . | nindent 4 }}
data:
yeti.conf: |
[system]
##
## Basic system settings
##
# if export_path is not set, then the default value is /tmp
export_path = /opt/yeti/exports
logging = /var/log/yeti_user_activity.log
plugins_path = ./plugins
audit_logfile = /var/log/yeti_audit.log
# Public scheme + hostname + port for Yeti. Use it if you want to specify an
# OIDC callback
# for testing use also a port number, e.g. http://localhost:8000
# webroot =
[auth]
##
## Use these settings to configure Yeti authentication.
##
# oidc, local
module = oidc
# to get a stronger value run:
# openssl rand -hex 32
# SECRET_KEY = SECRET
# ALGORITHM = HS256
# ACCESS_TOKEN_EXPIRE_MINUTES = 30
enabled = True
# OIDC
#
# Google can be used as an OIDC provider:
# See Instructions here: https://developers.google.com/identity/protocols/oauth2
#
# OIDC_CLIENT_ID = LONGRANDOMSTRING.apps.googleusercontent.com
# OIDC_CLIENT_SECRET = BLABLA-BLABLABLA
# OIDC_DISCOVERY_URL = https://accounts.google.com/.well-known/openid-configuration
[tag]
##
## Use these settings to configure Yeti tags.
## If you specify default_tag_expiration = 7776000, then the tag will expire for 90 days.
## Value must be in seconds (7776000 seconds is 90 days).
##
# default_tag_expiration = 7776000
[arangodb]
##
## Use these settings to configure how to connect to your ArangoDB database.
## All settings are optional, with default values being the one in the comment.
## If you do not specify a username and password, there will be no authentication.
##
# host = arangodb
# port = 8529
# username = root
# password =
# database = yeti_dev
[redis]
##
## Use these settings to configure how to connect to your redis server.
## All settings are optional, with default values being the one in the comment.
##
# host = redis
# port = 6379
# database = 0
# tls = ok
[misp]
##
## Use this setting in order to specify a comma-separated list of MISP instances
## that should be taken into account by the MISP feed.
##
# instances = misp_1
[misp_1]
##
## For each instance in the 'misp.instances' setting, you should specify a
## configuration block with this format, in order to specify at least the URL
## and the auth key.
##
# name = MISP_1
# url = MISP_URL
# key = MISP_AUTH_KEY
# galaxy_filter = filtering_galaxy_to_drop
# days = days_history_to_change_by_default_60_days
# verifycert = true_or_false
[proxy]
# Format proxies like socks5://user:pass@host:port
http =
https =
[github]
# Generate token: https://github.com/settings/tokens
# Select repo only
# no token - limit 60 r/h
# w/ token - limit 5k r/h
# token =
[otx]
key = YourOTXKey
days = 1
[abuseIPDB]
key = YourKey
[phishtank]
key=
[vt]
key=
[passivedns]
login=
password=
url=
[circl_passivessl]
username=
password=
[circl_pdns]
username=
password=
[dnsdb]
api_key=
[macaddressio]
api_key=
[malshare]
api_key=
[timesketch]
endpoint =
username =
password =
[censys]
api_key =
secret =
[shodan]
# Set result_limit to -1 for unlimited results, default is 100
api_key =
result_limit =
[dfiq]
# Comma-separated list of additional directories to load DFIQ objects from.
extra_dirs = /dfiq

View file

@ -0,0 +1,25 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ include "yeti.fullname" . }}-frontend-nginx
namespace: {{ .Release.Namespace | quote }}
labels:
{{- include "yeti.labels" . | nindent 4 }}
data:
default.conf: |
server {
root /www;
location /api/v2 {
proxy_pass http://{{ include "yeti.fullname" . }}-api.{{ .Release.Namespace }}.svc.cluster.local:8080;
}
location ~(^/docs|^/openapi.json) {
proxy_pass http://{{ include "yeti.fullname" . }}-api.{{ .Release.Namespace }}.svc.cluster.local:8080;
}
location / {
try_files $uri $uri/ /index.html;
}
}

View file

@ -0,0 +1,62 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "yeti.fullname" . }}-api
namespace: {{ .Release.Namespace | quote }}
labels:
app.kubernetes.io/component: api
{{- include "yeti.labels" . | nindent 4 }}
spec:
replicas: 1
selector:
matchLabels:
app.kubernetes.io/component: api
{{- include "yeti.selectorLabels" . | nindent 6 }}
template:
metadata:
annotations:
# Have Deployment restart after each upgrade
roll: {{ randAlphaNum 5 | quote }}
{{- if .Values.metrics.enabled }}
prometheus.io/port: {{ .Values.metrics.port | quote }}
prometheus.io/scrape: "true"
{{- end }}
labels:
app.kubernetes.io/component: api
{{- include "yeti.selectorLabels" . | nindent 8 }}
spec:
serviceAccountName: {{ .Values.serviceAccount.name }}
securityContext:
{{- toYaml .Values.backend.podSecurityContext | nindent 8 }}
containers:
- name: api
securityContext:
{{- toYaml .Values.backend.containerSecurityContext | nindent 12 }}
image: "{{ .Values.backend.image.repository }}:{{ .Values.backend.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.backend.image.pullPolicy }}
command: ["sh", "-c", "/docker-entrypoint.sh webserver"]
lifecycle:
postStart:
exec:
command: ["sh", "-c", "poetry run python yetictl/cli.py create-user $YETI_USER_USERNAME $YETI_USER_PASSWORD --api_key $YETI_API_KEY --admin || true"]
env:
{{- include "yeti.envs" . | nindent 12 }}
ports:
{{- if .Values.metrics.enabled }}
- containerPort: {{ .Values.metrics.port }}
{{- end }}
- containerPort: 8000
resources:
{{- toYaml .Values.backend.api.resources | nindent 12 }}
{{- with .Values.backend.api.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.backend.api.affinity }}
affinity:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.backend.api.tolerations }}
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}

View file

@ -0,0 +1,64 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "yeti.fullname" . }}
namespace: {{ .Release.Namespace | quote }}
labels:
app.kubernetes.io/component: frontend
{{- include "yeti.labels" . | nindent 4 }}
spec:
replicas: 1
selector:
matchLabels:
app.kubernetes.io/component: frontend
{{- include "yeti.selectorLabels" . | nindent 6 }}
template:
metadata:
annotations:
# Have Deployment restart after each upgrade
roll: {{ randAlphaNum 5 | quote }}
{{- if .Values.metrics.enabled }}
prometheus.io/port: {{ .Values.metrics.port | quote }}
prometheus.io/scrape: "true"
{{- end }}
labels:
app.kubernetes.io/component: frontend
{{- include "yeti.selectorLabels" . | nindent 8 }}
spec:
serviceAccountName: {{ .Values.serviceAccount.name }}
securityContext:
{{- toYaml .Values.frontend.podSecurityContext | nindent 8 }}
containers:
- name: frontend
securityContext:
{{- toYaml .Values.frontend.containerSecurityContext | nindent 12 }}
image: "{{ .Values.frontend.image.repository }}:{{ .Values.frontend.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.frontend.image.pullPolicy }}
ports:
{{- if .Values.metrics.enabled }}
- containerPort: {{ .Values.metrics.port }}
{{- end }}
- containerPort: 80
resources:
{{- toYaml .Values.frontend.resources | nindent 12 }}
volumeMounts:
- mountPath: /etc/nginx/conf.d/default.conf
subPath: default.conf
name: nginx-config
readOnly: true
volumes:
- name: nginx-config
configMap:
name: {{ include "yeti.fullname" . }}-frontend-nginx
{{- with .Values.frontend.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.frontend.affinity }}
affinity:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.frontend.tolerations }}
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}

View file

@ -0,0 +1,57 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "yeti.fullname" . }}-tasks
namespace: {{ .Release.Namespace | quote }}
labels:
app.kubernetes.io/component: tasks
{{- include "yeti.labels" . | nindent 4 }}
spec:
replicas: 1
selector:
matchLabels:
app.kubernetes.io/component: tasks
{{- include "yeti.selectorLabels" . | nindent 6 }}
template:
metadata:
annotations:
# Have Deployment restart after each upgrade
roll: {{ randAlphaNum 5 | quote }}
{{- if .Values.metrics.enabled }}
prometheus.io/port: {{ .Values.metrics.port | quote }}
prometheus.io/scrape: "true"
{{- end }}
labels:
app.kubernetes.io/component: tasks
{{- include "yeti.selectorLabels" . | nindent 8 }}
spec:
serviceAccountName: {{ .Values.serviceAccount.name }}
securityContext:
{{- toYaml .Values.backend.podSecurityContext | nindent 8 }}
containers:
- name: tasks
securityContext:
{{- toYaml .Values.backend.tasks.containerSecurityContext | nindent 12 }}
image: "{{ .Values.backend.image.repository }}:{{ .Values.backend.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.backend.image.pullPolicy }}
command: ["sh", "-c", "echo sleeping && sleep 75 && /docker-entrypoint.sh tasks"]
env:
{{- include "yeti.envs" . | nindent 12 }}
ports:
{{- if .Values.metrics.enabled }}
- containerPort: {{ .Values.metrics.port }}
{{- end }}
resources:
{{- toYaml .Values.backend.tasks.resources | nindent 12 }}
{{- with .Values.backend.tasks.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.backend.tasks.affinity }}
affinity:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.backend.tasks.tolerations }}
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}

View file

@ -0,0 +1,33 @@
apiVersion: v1
kind: Service
metadata:
name: {{ include "yeti.fullname" . }}-frontend
namespace: {{ .Release.Namespace | quote }}
labels:
{{- include "yeti.labels" . | nindent 4 }}
spec:
type: ClusterIP
ports:
- port: 8080
protocol: TCP
targetPort: 80
selector:
app.kubernetes.io/component: frontend
{{- include "yeti.selectorLabels" . | nindent 4 }}
---
apiVersion: v1
kind: Service
metadata:
name: {{ include "yeti.fullname" . }}-api
namespace: {{ .Release.Namespace | quote }}
labels:
{{- include "yeti.labels" . | nindent 4 }}
spec:
type: ClusterIP
ports:
- port: 8080
protocol: TCP
targetPort: 8000
selector:
app.kubernetes.io/component: api
{{- include "yeti.selectorLabels" . | nindent 4 }}

View file

@ -0,0 +1,10 @@
apiVersion: v1
kind: ServiceAccount
metadata:
name: {{ .Values.serviceAccount.name }}
labels:
{{- include "yeti.labels" . | nindent 4 }}
{{- with .Values.serviceAccount.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}

View file

@ -0,0 +1,31 @@
config:
externalUrl: https://cache.example.com/
persistence:
existingClaim: attic
initContainers:
dbInit:
image:
repository: ghcr.io/onedr0p/postgres-init
tag: "16"
envFrom:
- secretRef:
name: attic-secret
envFromSecret: attic-secret
image:
repository: ghcr.io/zhaofengli/attic
tag: 4dbdbee45728d8ce5788db6461aaaa89d98081f0
postgres:
secretName: attic-secret
resources:
limits:
memory: "3Gi"
cpu: "1000m"
# requests:
# cpu: 100m
# memory: 250Mi

71
charts/yeti/values.yaml Normal file
View file

@ -0,0 +1,71 @@
existingSecret: yeti-secret
frontend:
image:
repository: yetiplatform/yeti-frontend
tag: 2.1.6
pullPolicy: IfNotPresent
podSecurityContext: {}
containerSecurityContext: {}
nodeSelector: {}
affinity: {}
tolerations: {}
resources:
requests:
memory: "100Mi"
cpu: "50m"
limits:
memory: "2Gi"
cpu: "1000m"
backend:
image:
repository: yetiplatform/yeti
tag: 2.1.6
pullPolicy: IfNotPresent
podSecurityContext: {}
api:
containerSecurityContext: {}
nodeSelector: {}
affinity: {}
tolerations: {}
resources:
requests:
memory: "100Mi"
cpu: "50m"
limits:
memory: "2Gi"
cpu: "1000m"
tasks:
containerSecurityContext: {}
nodeSelector: {}
affinity: {}
tolerations: {}
resources:
requests:
memory: "100Mi"
cpu: "50m"
limits:
memory: "2Gi"
cpu: "1000m"
redis:
host: dragonfly.databases.svc.cluster.local
port: 6397
arangodb:
database: yeti
host: arango-dfir-cluster-ea.databases.svc.cluster.local
port: 8529
timesketch:
enabled: false
endpoint: ""
serviceAccount:
name: "yeti"
annotations: {}
metrics:
enabled: true
port: 9001