feat(vaultwarden): add SMTP configuration options and enhance signup settings

- Introduced SMTP settings for Vaultwarden including host, port, security, and authentication details.
- Added variables for signup verification, 2FA settings, password hints, and logging options.
- Updated Vaultwarden deployment to utilize new SMTP configurations.
- Enhanced Grafana module to support dynamic dashboard and datasource provisioning.
- Added LLM proxy configuration for Open Web UI with necessary environment variables.
This commit is contained in:
2026-05-30 23:24:44 +02:00
parent 350650ecc2
commit ec740f458f
89 changed files with 3154 additions and 856 deletions
+27 -8
View File
@@ -3,7 +3,7 @@ module "open-webui" {
persistent_folder = local.persistent_folder
fqdn = local.open_webui_fqdn
ollama_proxy = local.ollama_proxy
llm_proxy = local.llm_proxy
secret_key = var.webui_secret_key
tag = "v0.9.2-slim"
log_level = local.open_webui_log_level
@@ -43,13 +43,13 @@ module "nextcloud" {
issuer = local.internal_issuer
}
module "comfyui_proxy" {
source = "../../modules/utils/wol_proxy"
# module "comfyui_proxy" {
# source = "../../modules/utils/wol_proxy"
upstream = local.comfyui_proxy
proxy_port = local.comfyui_proxy.port
namespace = "comfyui"
}
# upstream = local.comfyui_proxy
# proxy_port = local.comfyui_proxy.port
# namespace = "comfyui"
# }
module "prometheus" {
source = "../../modules/utils/prometheus"
@@ -73,6 +73,15 @@ module "grafana" {
persistent_folder = "${local.persistent_folder}/monitoring"
fqdn = local.grafana_fqdn
dashboard_folder = {
System = "${path.module}/resources/grafana/system"
Kubernetes = "${path.module}/resources/grafana/kubernetes"
Security = "${path.module}/resources/grafana/security"
GPU = "${path.module}/resources/grafana/gpu"
LLM = "${path.module}/resources/grafana/llm"
Windows = "${path.module}/resources/grafana/windows"
}
datasource_folder = "${path.module}/resources/grafana"
}
module "mstream" {
@@ -99,6 +108,16 @@ module "vaultwarden" {
persistent_folder = local.vaultwarden_folder
fqdn = local.vaultwarden_fqdn
issuer = local.vaultwarden_issuer
tag = "1.33.4"
tag = local.vaultwarden_version
admin_token = var.vaultwarden_admin_token
smtp_host = local.vaultwarden_smtp_host
smtp_port = local.vaultwarden_smtp_port
smtp_security = local.vaultwarden_smtp_security
smtp_from = local.vaultwarden_smtp_from
smtp_from_name = local.vaultwarden_smtp_from_name
smtp_username = local.vaultwarden_smtp_username
# We temporarily enable signup to create the first admin user, then we will disable it and rotate the admin token
# signup_enabled = true
# signup_verify = false
}
+44 -36
View File
@@ -132,22 +132,22 @@ locals {
]
proxy_services = {
"comfyui" = {
http_port = 8080
https_port = 8443
tls = true
fqdn = ["comfyui.lab.alexpires.me"]
upstream = "http://upstream.comfyui.svc.cluster.local:8188"
resolver = "kube-dns.kube-system.svc.cluster.local"
# "comfyui" = {
# http_port = 8080
# https_port = 8443
# tls = true
# fqdn = ["comfyui.lab.alexpires.me"]
# upstream = "http://upstream.comfyui.svc.cluster.local:8188"
# resolver = "kube-dns.kube-system.svc.cluster.local"
locations = [
{
path = "/"
"Upgrade" = "$http_upgrade"
"Connection" = "upgrade"
},
]
}
# locations = [
# {
# path = "/"
# "Upgrade" = "$http_upgrade"
# "Connection" = "upgrade"
# },
# ]
# }
}
streams = {
@@ -171,20 +171,20 @@ locals {
}
}
ollama_proxy = {
llm_proxy = {
enabled = true
mac = "04:7c:16:d6:63:66"
ip = "10.19.4.106"
port = "11435"
port = 11434
scheme = "http"
}
comfyui_proxy = {
mac = "04:7c:16:d6:63:66"
ip = "10.19.4.106"
port = "8188"
scheme = "http"
}
# comfyui_proxy = {
# mac = "04:7c:16:d6:63:66"
# ip = "10.19.4.106"
# port = "8188"
# scheme = "http"
# }
scaleway_smtp_host = "smtp.tem.scw.cloud"
@@ -235,16 +235,16 @@ locals {
grafana_fqdn = "grafana.${local.lab_domain}"
devolo_devices = {
devolo_620 = {
ip = "192.168.0.199"
model = "620"
}
# devolo_326 = {
# ip = "192.168.0.200"
# model = "326"
# }
}
# devolo_devices = {
# devolo_620 = {
# ip = "192.168.0.199"
# model = "620"
# }
# # devolo_326 = {
# # ip = "192.168.0.200"
# # model = "326"
# # }
# }
# mstream
mstream_music_folder = "/mnt/storage/Music"
@@ -256,9 +256,17 @@ locals {
searxng_issuer = "letsencrypt-prod"
# vaultwarden
vaultwarden_folder = "/mnt/md0/vaultwarden"
vaultwarden_fqdn = "vaultwarden.${local.lab_domain}"
vaultwarden_issuer = "internal-ca"
vaultwarden_folder = "/mnt/md0/vaultwarden"
vaultwarden_fqdn = "vaultwarden.${local.lab_domain}"
vaultwarden_issuer = "letsencrypt-prod"
vaultwarden_version = "1.36.0"
vaultwarden_smtp_host = local.scaleway_smtp_host
vaultwarden_smtp_port = 465
vaultwarden_smtp_security = "force_tls"
vaultwarden_smtp_from = "vaultwarden@${local.lab_domain}"
vaultwarden_smtp_from_name = "Vaultwarden"
vaultwarden_smtp_username = var.scaleway_project_id
vaultwarden_smtp_password = local.mail_app_api_secret_key
}
data "terraform_remote_state" "scaleway" {
@@ -0,0 +1,35 @@
apiVersion: 1
datasources:
- name: prometheus-dev-01
type: prometheus
url: http://prometheus.prometheus.svc.cluster.local:9090
isDefault: true
editable: false
jsonData:
httpMethod: POST
timeInterval: 15s
- name: prometheus-ci-01
type: prometheus
url: http://ci-01.lab.alexpires.me:9090
isDefault: false
editable: false
jsonData:
httpMethod: POST
timeInterval: 15s
- name: prometheus-gpu-01
type: prometheus
url: http://gpu-01.lab.alexpires.me:9091
isDefault: false
editable: false
jsonData:
httpMethod: POST
timeInterval: 15s
- name: prometheus-prod-01
type: prometheus
url: http://10.5.5.2:9090
isDefault: false
editable: false
jsonData:
httpMethod: POST
timeInterval: 15s
@@ -0,0 +1,500 @@
{
"annotations": {
"list": [
{
"builtIn": 1,
"datasource": {
"type": "grafana",
"uid": "-- Grafana --"
},
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
"name": "Annotations & Alerts",
"type": "dashboard"
}
]
},
"editable": true,
"fiscalYearStartMonth": 0,
"graphTooltip": 0,
"id": null,
"links": [],
"liveNow": true,
"panels": [
{
"datasource": "$datasource",
"fieldConfig": {
"defaults": {
"color": {
"mode": "thresholds"
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "red",
"value": null
},
{
"color": "green",
"value": 1
}
]
},
"unit": "short"
},
"overrides": []
},
"gridPos": {
"h": 6,
"w": 24,
"x": 0,
"y": 0
},
"id": 1,
"options": {
"colorMode": "background",
"graphMode": "none",
"justifyMode": "auto",
"orientation": "auto",
"reduceOptions": {
"calcs": [
"lastNotNull"
],
"fields": "",
"values": false
},
"textMode": "auto"
},
"pluginVersion": "11.0.0",
"targets": [
{
"editorMode": "code",
"expr": "up{job=~\"prometheus|nvidia-gpu|amdgpu_exporter\"}",
"instant": true,
"legendFormat": "{{job}}",
"range": false,
"refId": "A"
}
],
"title": "GPU Exporter Health",
"type": "stat"
},
{
"datasource": "$datasource",
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"unit": "percent"
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 6
},
"id": 2,
"options": {
"legend": {
"calcs": [],
"displayMode": "table",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"pluginVersion": "11.0.0",
"targets": [
{
"editorMode": "code",
"expr": "{__name__=~\"gpu_.*_gpu_util_percent|gpu_util_percent\"}",
"legendFormat": "{{__name__}} {{gpu}}",
"range": true,
"refId": "A"
}
],
"title": "GPU Utilization",
"type": "timeseries"
},
{
"datasource": "$datasource",
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"unit": "celsius"
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 6
},
"id": 3,
"options": {
"legend": {
"calcs": [],
"displayMode": "table",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"pluginVersion": "11.0.0",
"targets": [
{
"editorMode": "code",
"expr": "{__name__=~\"gpu_.*_temperature_celsius|temperature_celsius\"}",
"legendFormat": "{{__name__}} {{gpu}}",
"range": true,
"refId": "A"
}
],
"title": "GPU Temperature",
"type": "timeseries"
},
{
"datasource": "$datasource",
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"unit": "watt"
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 14
},
"id": 4,
"options": {
"legend": {
"calcs": [],
"displayMode": "table",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"pluginVersion": "11.0.0",
"targets": [
{
"editorMode": "code",
"expr": "{__name__=~\"gpu_.*_power_watts|power_watts\"}",
"legendFormat": "{{__name__}} {{gpu}}",
"range": true,
"refId": "A"
}
],
"title": "GPU Power Draw",
"type": "timeseries"
},
{
"datasource": "$datasource",
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"unit": "percent"
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 14
},
"id": 5,
"options": {
"legend": {
"calcs": [],
"displayMode": "table",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"pluginVersion": "11.0.0",
"targets": [
{
"editorMode": "code",
"expr": "{__name__=~\"gpu_.*_memory_used_percent\"}",
"legendFormat": "nvidia",
"range": true,
"refId": "A"
},
{
"editorMode": "code",
"expr": "100 * mem_used_bytes / clamp_min(mem_used_bytes + mem_avail_bytes, 1)",
"legendFormat": "amd {{gpu}}",
"range": true,
"refId": "B"
}
],
"title": "GPU Memory Usage",
"type": "timeseries"
},
{
"datasource": "$datasource",
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"unit": "s"
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 22
},
"id": 6,
"options": {
"legend": {
"calcs": [],
"displayMode": "table",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"pluginVersion": "11.0.0",
"targets": [
{
"editorMode": "code",
"expr": "scrape_duration_seconds{job=~\"nvidia-gpu|amdgpu_exporter|prometheus\"}",
"legendFormat": "{{job}}",
"range": true,
"refId": "A"
}
],
"title": "GPU Scrape Duration",
"type": "timeseries"
},
{
"datasource": "$datasource",
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"unit": "short"
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 22
},
"id": 7,
"options": {
"legend": {
"calcs": [],
"displayMode": "table",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"pluginVersion": "11.0.0",
"targets": [
{
"editorMode": "code",
"expr": "{__name__=~\"gpu_.*_pstate\"}",
"legendFormat": "{{__name__}}",
"range": true,
"refId": "A"
}
],
"title": "NVIDIA P-State",
"type": "timeseries"
},
{
"datasource": "$datasource",
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"unit": "short"
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 30
},
"id": 8,
"options": {
"legend": {
"calcs": [],
"displayMode": "table",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"pluginVersion": "11.0.0",
"targets": [
{
"editorMode": "code",
"expr": "{__name__=~\"gpu_.*_pcie_link_gen_current\"}",
"legendFormat": "pcie gen",
"range": true,
"refId": "A"
},
{
"editorMode": "code",
"expr": "{__name__=~\"gpu_.*_pcie_link_width_current\"}",
"legendFormat": "pcie width",
"range": true,
"refId": "B"
}
],
"title": "NVIDIA PCIe Link",
"type": "timeseries"
},
{
"datasource": "$datasource",
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"max": 1,
"min": 0,
"unit": "short"
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 30
},
"id": 9,
"options": {
"legend": {
"calcs": [],
"displayMode": "table",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"pluginVersion": "11.0.0",
"targets": [
{
"editorMode": "code",
"expr": "{__name__=~\"gpu_.*_throttle_sw_power_cap\"}",
"legendFormat": "sw power cap",
"range": true,
"refId": "A"
},
{
"editorMode": "code",
"expr": "{__name__=~\"gpu_.*_throttle_hw_thermal_slowdown\"}",
"legendFormat": "hw thermal slowdown",
"range": true,
"refId": "B"
},
{
"editorMode": "code",
"expr": "{__name__=~\"gpu_.*_throttle_hw_power_brake_slowdown\"}",
"legendFormat": "hw power brake slowdown",
"range": true,
"refId": "C"
}
],
"title": "NVIDIA Throttle Flags",
"type": "timeseries"
}
],
"refresh": "5s",
"schemaVersion": 39,
"style": "dark",
"tags": [
"gpu",
"monitoring",
"realtime"
],
"templating": {
"list": [
{
"current": {
"selected": true,
"text": "Prometheus",
"value": "Prometheus"
},
"hide": 0,
"includeAll": false,
"label": "Datasource",
"multi": false,
"name": "datasource",
"options": [],
"query": "prometheus",
"refresh": 1,
"regex": "",
"skipUrlSync": false,
"type": "datasource"
}
]
},
"time": {
"from": "now-30m",
"to": "now"
},
"timepicker": {},
"timezone": "",
"title": "GPU Monitoring Realtime",
"uid": "gpu-monitoring-realtime",
"version": 2,
"weekStart": ""
}
@@ -0,0 +1,436 @@
{
"annotations": {
"list": [
{
"builtIn": 1,
"datasource": {
"type": "grafana",
"uid": "-- Grafana --"
},
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
"name": "Annotations & Alerts",
"type": "dashboard"
}
]
},
"editable": true,
"fiscalYearStartMonth": 0,
"graphTooltip": 0,
"id": null,
"links": [],
"liveNow": true,
"panels": [
{
"datasource": "$datasource",
"fieldConfig": {
"defaults": {
"color": {
"mode": "thresholds"
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "red",
"value": null
},
{
"color": "green",
"value": 1
}
]
},
"unit": "short"
},
"overrides": []
},
"gridPos": {
"h": 6,
"w": 24,
"x": 0,
"y": 0
},
"id": 1,
"options": {
"colorMode": "background",
"graphMode": "none",
"justifyMode": "auto",
"orientation": "auto",
"reduceOptions": {
"calcs": [
"lastNotNull"
],
"fields": "",
"values": false
},
"textMode": "auto"
},
"pluginVersion": "11.0.0",
"targets": [
{
"editorMode": "code",
"expr": "up{job=~\"prometheus|llama_exporter\"}",
"instant": true,
"legendFormat": "{{job}}",
"range": false,
"refId": "A"
}
],
"title": "LLM Exporter Health",
"type": "stat"
},
{
"datasource": "$datasource",
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"unit": "short"
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 6
},
"id": 2,
"options": {
"legend": {
"calcs": [],
"displayMode": "table",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"pluginVersion": "11.0.0",
"targets": [
{
"editorMode": "code",
"expr": "llama_server_health{model=~\"$model\"}",
"legendFormat": "health {{server}}",
"range": true,
"refId": "A"
},
{
"editorMode": "code",
"expr": "llama_models_loaded{model=~\"$model\"}",
"legendFormat": "models {{server}} {{model}}",
"range": true,
"refId": "B"
}
],
"title": "Llama Server Metrics",
"type": "timeseries"
},
{
"datasource": "$datasource",
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"unit": "reqps"
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 6
},
"id": 3,
"options": {
"legend": {
"calcs": [],
"displayMode": "table",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"pluginVersion": "11.0.0",
"targets": [
{
"editorMode": "code",
"expr": "rate(llamacpp:tokens_predicted_total{model=~\"$model\"}[5m])",
"legendFormat": "decode {{model}}",
"range": true,
"refId": "A"
},
{
"editorMode": "code",
"expr": "rate(llamacpp:prompt_tokens_total{model=~\"$model\"}[5m])",
"legendFormat": "prompt {{model}}",
"range": true,
"refId": "B"
}
],
"title": "Token Throughput",
"type": "timeseries"
},
{
"datasource": "$datasource",
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"unit": "s"
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 14
},
"id": 4,
"options": {
"legend": {
"calcs": [],
"displayMode": "table",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"pluginVersion": "11.0.0",
"targets": [
{
"editorMode": "code",
"expr": "rate(llamacpp:tokens_predicted_seconds_total{model=~\"$model\"}[5m])",
"legendFormat": "decode time {{model}}",
"range": true,
"refId": "A"
},
{
"editorMode": "code",
"expr": "rate(llamacpp:prompt_seconds_total{model=~\"$model\"}[5m])",
"legendFormat": "prompt time {{model}}",
"range": true,
"refId": "B"
}
],
"title": "Processing Time",
"type": "timeseries"
},
{
"datasource": "$datasource",
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"unit": "short"
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 14
},
"id": 5,
"options": {
"legend": {
"calcs": [],
"displayMode": "table",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"pluginVersion": "11.0.0",
"targets": [
{
"editorMode": "code",
"expr": "llamacpp:requests_processing{model=~\"$model\"}",
"legendFormat": "processing {{model}}",
"range": true,
"refId": "A"
},
{
"editorMode": "code",
"expr": "llamacpp:requests_deferred{model=~\"$model\"}",
"legendFormat": "deferred {{model}}",
"range": true,
"refId": "B"
}
],
"title": "Request Queue",
"type": "timeseries"
},
{
"datasource": "$datasource",
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"unit": "short"
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 22
},
"id": 6,
"options": {
"legend": {
"calcs": [],
"displayMode": "table",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"pluginVersion": "11.0.0",
"targets": [
{
"editorMode": "code",
"expr": "llamacpp:n_busy_slots_per_decode{model=~\"$model\"}",
"legendFormat": "busy slots {{model}}",
"range": true,
"refId": "A"
}
],
"title": "Busy Slots Per Decode",
"type": "timeseries"
},
{
"datasource": "$datasource",
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"unit": "short"
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 22
},
"id": 7,
"options": {
"legend": {
"calcs": [],
"displayMode": "table",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"pluginVersion": "11.0.0",
"targets": [
{
"editorMode": "code",
"expr": "llamacpp:n_tokens_max{model=~\"$model\"}",
"legendFormat": "max tokens {{model}}",
"range": true,
"refId": "A"
}
],
"title": "Max Tokens (Context Window)",
"type": "timeseries"
}
],
"refresh": "5s",
"schemaVersion": 39,
"style": "dark",
"tags": [
"llm",
"monitoring",
"realtime"
],
"templating": {
"list": [
{
"current": {
"selected": true,
"text": "Prometheus",
"value": "Prometheus"
},
"hide": 0,
"includeAll": false,
"label": "Datasource",
"multi": false,
"name": "datasource",
"options": [],
"query": "prometheus",
"refresh": 1,
"regex": "",
"skipUrlSync": false,
"type": "datasource"
},
{
"current": {
"selected": false,
"text": "All",
"value": "$__all"
},
"datasource": {
"type": "prometheus",
"uid": "$datasource"
},
"definition": "label_values(llamacpp:tokens_predicted_total, model)",
"hide": 0,
"includeAll": true,
"label": "Model",
"multi": false,
"name": "model",
"options": [],
"query": "label_values(llamacpp:tokens_predicted_total, model)",
"refresh": 2,
"regex": "",
"skipUrlSync": false,
"sort": 1,
"type": "query"
}
]
},
"time": {
"from": "now-30m",
"to": "now"
},
"timepicker": {},
"timezone": "",
"title": "LLM Monitoring Realtime",
"uid": "llm-monitoring-realtime",
"version": 1,
"weekStart": ""
}
+1
View File
@@ -39,3 +39,4 @@ variable "vaultwarden_admin_token" {
type = string
sensitive = true
}
+16 -5
View File
@@ -161,9 +161,20 @@ module "auth_exporter" {
module "vaultwarden" {
source = "../../modules/apps/vaultwarden"
persistent_folder = local.vaultwarden_folder
fqdn = local.vaultwarden_fqdn
issuer = local.internal_issuer
tag = "1.33.4"
admin_token = var.vaultwarden_admin_token
persistent_folder = local.vaultwarden_folder
fqdn = local.vaultwarden_fqdn
issuer = local.vaultwarden_issuer
tag = local.vaultwarden_version
admin_token = var.vaultwarden_admin_token
smtp_host = local.vaultwarden_smtp_host
smtp_port = local.vaultwarden_smtp_port
smtp_security = local.vaultwarden_smtp_security
smtp_from = local.vaultwarden_smtp_from
smtp_from_name = local.vaultwarden_smtp_from_name
smtp_username = local.vaultwarden_smtp_username
smtp_password = var.vaultwarden_smtp_password
smtp_timeout = local.vaultwarden_smtp_timeout
smtp_auth_mechanism = local.vaultwarden_smtp_auth_mechanism
smtp_helo_name = local.vaultwarden_smtp_helo_name
smtp_embed_images = local.vaultwarden_smtp_embed_images
}
+14 -3
View File
@@ -211,9 +211,20 @@ locals {
telegram_message = file("${path.module}/resources/telegram.tpl")
# vaultwarden
vaultwarden_folder = "${local.persistent_folder}/vaultwarden"
vaultwarden_fqdn = "vaultwarden.${local.primary_domain}"
internal_issuer = "internal-ca"
vaultwarden_folder = "${local.persistent_folder}/vaultwarden"
vaultwarden_fqdn = "vaultwarden.${local.primary_domain}"
vaultwarden_issuer = "internal-ca"
vaultwarden_version = "1.36.0"
vaultwarden_smtp_host = ""
vaultwarden_smtp_port = 587
vaultwarden_smtp_security = "starttls"
vaultwarden_smtp_from = ""
vaultwarden_smtp_from_name = "Vaultwarden"
vaultwarden_smtp_username = ""
vaultwarden_smtp_timeout = 15
vaultwarden_smtp_auth_mechanism = ""
vaultwarden_smtp_helo_name = ""
vaultwarden_smtp_embed_images = true
# get secrets from remote state
backup_bucket_name = data.terraform_remote_state.scaleway.outputs.backup_bucket_name
+7
View File
@@ -140,3 +140,10 @@ variable "vaultwarden_admin_token" {
type = string
sensitive = true
}
variable "vaultwarden_smtp_password" {
description = "SMTP password for VaultWarden email"
type = string
sensitive = true
default = ""
}
+1
View File
@@ -34,3 +34,4 @@ TF_VAR_wireguard_1_pre_shared_key=$WIREGUARD_1_PRE_SHARED_KEY
TF_VAR_nginx_token_bearer=$NGINX_TOKEN_BEARER
TF_VAR_telegram_bot_token=$TELEGRAM_BOT_TOKEN
TF_VAR_vaultwarden_admin_token=$VAULTWARDEN_ADMIN_TOKEN
TF_VAR_vaultwarden_smtp_password=$VAULTWARDEN_SMTP_PASSWORD
+43 -4
View File
@@ -2,8 +2,47 @@ locals {
app_version = var.tag
monitoring_folder = "${var.persistent_folder}/grafana"
grafana_datasource_config = file("${path.module}/resources/default.yml")
grafana_dashboard_provider = file("${path.module}/resources/dashboard-provider.yml")
grafana_trivy_dashboard = file("${path.module}/resources/trivy-vulnerabilities-dashboard.json")
grafana_gpu_realtime_dashboard = file("${path.module}/resources/gpu-realtime-dashboard.json")
dashboard_resource_names = {
for folder_name in keys(var.dashboard_folder) :
folder_name => "${substr(replace(lower(folder_name), "/[^a-z0-9-]/", "-"), 0, 40)}-${substr(md5(folder_name), 0, 8)}"
}
dashboard_mount_paths = {
for folder_name in keys(var.dashboard_folder) :
folder_name => "/var/lib/grafana/dashboards/${local.dashboard_resource_names[folder_name]}"
}
# Load datasource YAMLs from app root if datasource_folder is provided
datasource_files = {
for f in fileset(var.datasource_folder, "*.{yml,yaml}") : f => file("${var.datasource_folder}/${f}")
}
# Load dashboard JSONs from app root folders grouped by Grafana category name
dashboard_files_by_category = {
for folder_name, folder_path in var.dashboard_folder :
folder_name => {
for f in fileset(folder_path, "*.json") : f => file("${folder_path}/${f}")
}
}
grafana_dashboard_provider = yamlencode({
apiVersion = 1
providers = [
for folder_name in keys(var.dashboard_folder) : {
name = "local-${local.dashboard_resource_names[folder_name]}"
orgId = 1
folder = folder_name
type = "file"
disableDeletion = false
updateIntervalSeconds = 30
allowUiUpdates = true
options = {
path = local.dashboard_mount_paths[folder_name]
}
}
]
})
checksum_datasources = md5(join("", values(local.datasource_files)))
checksum_dashboards = md5("${local.grafana_dashboard_provider}${jsonencode(local.dashboard_files_by_category)}")
}
+22 -17
View File
@@ -4,9 +4,7 @@ resource "kubernetes_config_map" "grafana_datasources" {
namespace = kubernetes_namespace.grafana.metadata[0].name
}
data = {
"default.yml" = replace(local.grafana_datasource_config, "__PROMETHEUS_URL__", var.prometheus_url)
}
data = local.datasource_files
}
resource "kubernetes_config_map" "grafana_dashboard_provider" {
@@ -21,15 +19,14 @@ resource "kubernetes_config_map" "grafana_dashboard_provider" {
}
resource "kubernetes_config_map" "grafana_dashboards" {
for_each = local.dashboard_files_by_category
metadata {
name = "grafana-dashboards"
name = "grafana-dashboards-${local.dashboard_resource_names[each.key]}"
namespace = kubernetes_namespace.grafana.metadata[0].name
}
data = {
"trivy-vulnerabilities-dashboard.json" = local.grafana_trivy_dashboard
"gpu-realtime-dashboard.json" = local.grafana_gpu_realtime_dashboard
}
data = each.value
}
resource "kubernetes_deployment" "grafana" {
@@ -50,7 +47,9 @@ resource "kubernetes_deployment" "grafana" {
template {
metadata {
labels = {
app = "grafana"
app = "grafana"
checksum_datasources = local.checksum_datasources
checksum_dashboards = local.checksum_dashboards
}
}
spec {
@@ -111,10 +110,13 @@ resource "kubernetes_deployment" "grafana" {
mount_path = "/etc/grafana/provisioning/dashboards"
read_only = true
}
volume_mount {
name = "grafana-dashboards"
mount_path = "/var/lib/grafana/dashboards"
read_only = true
dynamic "volume_mount" {
for_each = local.dashboard_mount_paths
content {
name = "grafana-dashboards-${local.dashboard_resource_names[volume_mount.key]}"
mount_path = volume_mount.value
read_only = true
}
}
}
volume {
@@ -135,10 +137,13 @@ resource "kubernetes_deployment" "grafana" {
name = kubernetes_config_map.grafana_dashboard_provider.metadata[0].name
}
}
volume {
name = "grafana-dashboards"
config_map {
name = kubernetes_config_map.grafana_dashboards.metadata[0].name
dynamic "volume" {
for_each = local.dashboard_mount_paths
content {
name = "grafana-dashboards-${local.dashboard_resource_names[volume.key]}"
config_map {
name = kubernetes_config_map.grafana_dashboards[volume.key].metadata[0].name
}
}
}
}
@@ -1,9 +1,9 @@
apiVersion: 1
providers:
- name: trivy-vulnerabilities
- name: local
orgId: 1
folder: Security
folder: Default
type: file
disableDeletion: false
updateIntervalSeconds: 30
@@ -1,9 +0,0 @@
apiVersion: 1
datasources:
- name: Prometheus
type: prometheus
access: proxy
url: __PROMETHEUS_URL__
isDefault: true
editable: true
@@ -1,550 +0,0 @@
{
"annotations": {
"list": [
{
"builtIn": 1,
"datasource": {
"type": "grafana",
"uid": "-- Grafana --"
},
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
"name": "Annotations & Alerts",
"type": "dashboard"
}
]
},
"editable": true,
"fiscalYearStartMonth": 0,
"graphTooltip": 0,
"id": null,
"links": [],
"liveNow": true,
"panels": [
{
"datasource": "$datasource",
"fieldConfig": {
"defaults": {
"color": {
"mode": "thresholds"
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "red",
"value": null
},
{
"color": "green",
"value": 1
}
]
},
"unit": "short"
},
"overrides": []
},
"gridPos": {
"h": 6,
"w": 24,
"x": 0,
"y": 0
},
"id": 1,
"options": {
"colorMode": "background",
"graphMode": "none",
"justifyMode": "auto",
"orientation": "auto",
"reduceOptions": {
"calcs": [
"lastNotNull"
],
"fields": "",
"values": false
},
"textMode": "auto"
},
"pluginVersion": "11.0.0",
"targets": [
{
"editorMode": "code",
"expr": "up{job=~\"prometheus|nvidia-gpu|amdgpu_exporter|llama_exporter\"}",
"instant": true,
"legendFormat": "{{job}}",
"range": false,
"refId": "A"
}
],
"title": "Exporter Health",
"type": "stat"
},
{
"datasource": "$datasource",
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"unit": "percent"
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 6
},
"id": 2,
"options": {
"legend": {
"calcs": [],
"displayMode": "table",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"pluginVersion": "11.0.0",
"targets": [
{
"editorMode": "code",
"expr": "{__name__=~\"gpu_.*_gpu_util_percent|gpu_util_percent\"}",
"legendFormat": "{{__name__}} {{gpu}}",
"range": true,
"refId": "A"
}
],
"title": "GPU Utilization",
"type": "timeseries"
},
{
"datasource": "$datasource",
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"unit": "celsius"
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 6
},
"id": 3,
"options": {
"legend": {
"calcs": [],
"displayMode": "table",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"pluginVersion": "11.0.0",
"targets": [
{
"editorMode": "code",
"expr": "{__name__=~\"gpu_.*_temperature_celsius|temperature_celsius\"}",
"legendFormat": "{{__name__}} {{gpu}}",
"range": true,
"refId": "A"
}
],
"title": "GPU Temperature",
"type": "timeseries"
},
{
"datasource": "$datasource",
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"unit": "watt"
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 14
},
"id": 4,
"options": {
"legend": {
"calcs": [],
"displayMode": "table",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"pluginVersion": "11.0.0",
"targets": [
{
"editorMode": "code",
"expr": "{__name__=~\"gpu_.*_power_watts|power_watts\"}",
"legendFormat": "{{__name__}} {{gpu}}",
"range": true,
"refId": "A"
}
],
"title": "GPU Power Draw",
"type": "timeseries"
},
{
"datasource": "$datasource",
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"unit": "percent"
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 14
},
"id": 5,
"options": {
"legend": {
"calcs": [],
"displayMode": "table",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"pluginVersion": "11.0.0",
"targets": [
{
"editorMode": "code",
"expr": "{__name__=~\"gpu_.*_memory_used_percent\"}",
"legendFormat": "nvidia",
"range": true,
"refId": "A"
},
{
"editorMode": "code",
"expr": "100 * mem_used_bytes / clamp_min(mem_used_bytes + mem_avail_bytes, 1)",
"legendFormat": "amd {{gpu}}",
"range": true,
"refId": "B"
}
],
"title": "GPU Memory Usage",
"type": "timeseries"
},
{
"datasource": "$datasource",
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"unit": "s"
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 22
},
"id": 6,
"options": {
"legend": {
"calcs": [],
"displayMode": "table",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"pluginVersion": "11.0.0",
"targets": [
{
"editorMode": "code",
"expr": "scrape_duration_seconds{job=~\"nvidia-gpu|amdgpu_exporter|llama_exporter|prometheus\"}",
"legendFormat": "{{job}}",
"range": true,
"refId": "A"
}
],
"title": "Scrape Duration",
"type": "timeseries"
},
{
"datasource": "$datasource",
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"unit": "short"
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 22
},
"id": 7,
"options": {
"legend": {
"calcs": [],
"displayMode": "table",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"pluginVersion": "11.0.0",
"targets": [
{
"editorMode": "code",
"expr": "llama_server_health",
"legendFormat": "health {{server}}",
"range": true,
"refId": "A"
},
{
"editorMode": "code",
"expr": "llama_models_loaded",
"legendFormat": "models {{server}} {{model}}",
"range": true,
"refId": "B"
}
],
"title": "Llama Server Metrics",
"type": "timeseries"
},
{
"datasource": "$datasource",
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"unit": "short"
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 30
},
"id": 8,
"options": {
"legend": {
"calcs": [],
"displayMode": "table",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"pluginVersion": "11.0.0",
"targets": [
{
"editorMode": "code",
"expr": "{__name__=~\"gpu_.*_pstate\"}",
"legendFormat": "{{__name__}}",
"range": true,
"refId": "A"
}
],
"title": "NVIDIA P-State",
"type": "timeseries"
},
{
"datasource": "$datasource",
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"unit": "short"
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 30
},
"id": 9,
"options": {
"legend": {
"calcs": [],
"displayMode": "table",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"pluginVersion": "11.0.0",
"targets": [
{
"editorMode": "code",
"expr": "{__name__=~\"gpu_.*_pcie_link_gen_current\"}",
"legendFormat": "pcie gen",
"range": true,
"refId": "A"
},
{
"editorMode": "code",
"expr": "{__name__=~\"gpu_.*_pcie_link_width_current\"}",
"legendFormat": "pcie width",
"range": true,
"refId": "B"
}
],
"title": "NVIDIA PCIe Link",
"type": "timeseries"
},
{
"datasource": "$datasource",
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"max": 1,
"min": 0,
"unit": "short"
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 24,
"x": 0,
"y": 38
},
"id": 10,
"options": {
"legend": {
"calcs": [],
"displayMode": "table",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"pluginVersion": "11.0.0",
"targets": [
{
"editorMode": "code",
"expr": "{__name__=~\"gpu_.*_throttle_sw_power_cap\"}",
"legendFormat": "sw power cap",
"range": true,
"refId": "A"
},
{
"editorMode": "code",
"expr": "{__name__=~\"gpu_.*_throttle_hw_thermal_slowdown\"}",
"legendFormat": "hw thermal slowdown",
"range": true,
"refId": "B"
},
{
"editorMode": "code",
"expr": "{__name__=~\"gpu_.*_throttle_hw_power_brake_slowdown\"}",
"legendFormat": "hw power brake slowdown",
"range": true,
"refId": "C"
}
],
"title": "NVIDIA Throttle Flags",
"type": "timeseries"
}
],
"refresh": "5s",
"schemaVersion": 39,
"style": "dark",
"tags": [
"gpu",
"monitoring",
"realtime"
],
"templating": {
"list": [
{
"current": {
"selected": true,
"text": "Prometheus",
"value": "Prometheus"
},
"hide": 0,
"includeAll": false,
"label": "Datasource",
"multi": false,
"name": "datasource",
"options": [],
"query": "prometheus",
"refresh": 1,
"regex": "",
"skipUrlSync": false,
"type": "datasource"
}
]
},
"time": {
"from": "now-30m",
"to": "now"
},
"timepicker": {},
"timezone": "",
"title": "GPU Monitoring Realtime",
"uid": "gpu-monitoring-realtime",
"version": 1,
"weekStart": ""
}
+9 -4
View File
@@ -21,8 +21,13 @@ variable "persistent_folder" {
type = string
}
variable "prometheus_url" {
description = "Prometheus service URL used by Grafana datasource provisioning"
type = string
default = "http://prometheus.prometheus.svc.cluster.local:9090"
variable "dashboard_folder" {
description = "Map of Grafana folder name => local folder path containing dashboard JSON files. Each entry becomes a Grafana provider/category and loads all .json files from its path."
type = map(string)
default = {}
}
variable "datasource_folder" {
description = "Path to a folder containing Grafana datasource provisioning YAML files. When set, all .yml/.yaml files in this folder will be loaded and have __PROMETHEUS_URL__ replaced with the actual prometheus_url. If not set, falls back to the default single-prometheus datasource."
type = string
}
@@ -1,22 +1,22 @@
resource "kubernetes_service_account" "ollama_proxy" {
resource "kubernetes_service_account" "llm_proxy" {
count = var.ollama_proxy.enabled ? 1 : 0
count = var.llm_proxy.enabled ? 1 : 0
metadata {
name = "ollama-proxy-sa"
name = "llm-proxy-sa"
namespace = kubernetes_namespace.openwebui.metadata[0].name
}
automount_service_account_token = false
}
resource "kubernetes_deployment" "ollama_proxy" {
resource "kubernetes_deployment" "llm_proxy" {
count = var.ollama_proxy.enabled ? 1 : 0
count = var.llm_proxy.enabled ? 1 : 0
metadata {
name = "ollama-proxy"
name = "llm-proxy"
namespace = kubernetes_namespace.openwebui.metadata[0].name
labels = {
app = "ollama-proxy"
app = "llm-proxy"
}
}
@@ -24,7 +24,7 @@ resource "kubernetes_deployment" "ollama_proxy" {
replicas = 1
selector {
match_labels = {
app = "ollama-proxy"
app = "llm-proxy"
}
}
@@ -35,7 +35,7 @@ resource "kubernetes_deployment" "ollama_proxy" {
template {
metadata {
labels = {
app = "ollama-proxy"
app = "llm-proxy"
}
}
@@ -57,37 +57,37 @@ resource "kubernetes_deployment" "ollama_proxy" {
port {
name = "http"
container_port = var.ollama_proxy.port
container_port = var.llm_proxy.port
}
env {
name = "UPSTREAM_MAC"
value = var.ollama_proxy.mac
value = var.llm_proxy.mac
}
env {
name = "UPSTREAM_IP"
value = var.ollama_proxy.ip
value = var.llm_proxy.ip
}
env {
name = "UPSTREAM_PORT"
value = var.ollama_proxy.port
value = try(var.llm_proxy.upstream_port, var.llm_proxy.port)
}
env {
name = "UPSTREAM_SCHEME"
value = var.ollama_proxy.scheme
value = var.llm_proxy.scheme
}
env {
name = "PROXY_PORT"
value = var.ollama_proxy.port
value = var.llm_proxy.port
}
readiness_probe {
tcp_socket {
port = 11434
port = var.llm_proxy.port
}
initial_delay_seconds = 5
period_seconds = 10
+14 -7
View File
@@ -20,14 +20,15 @@ variable "issuer" {
default = "internal-ca"
}
variable "ollama_proxy" {
description = "Configuration for the Ollama proxy"
variable "llm_proxy" {
description = "Configuration for the llm proxy"
type = object({
enabled = bool
mac = optional(string, "")
ip = optional(string, "")
port = optional(string, "11434")
scheme = optional(string, "http")
enabled = bool
mac = optional(string, "")
ip = optional(string, "")
port = optional(string, "11434")
scheme = optional(string, "http")
upstream_port = optional(string, "11434")
})
default = {
enabled = false
@@ -54,3 +55,9 @@ variable "log_level" {
error_message = "log_level must be one of: DEBUG, INFO, WARNING, ERROR, CRITICAL"
}
}
variable "aiohttp_client_timeout" {
description = "The timeout for aiohttp client in seconds (e.g., 300, 600 etc.) (By default the timeout will be set to None, effectively disabling the timeout and allowing the client to wait indefinitely.)"
type = string
default = ""
}
+4 -1
View File
@@ -77,7 +77,6 @@ resource "kubernetes_deployment" "openwebui" {
}
}
container {
name = "app"
image = "ghcr.io/open-webui/open-webui:${var.tag}"
@@ -115,6 +114,10 @@ resource "kubernetes_deployment" "openwebui" {
value = var.log_level
}
env {
name = "AIOHTTP_CLIENT_TIMEOUT"
value = var.aiohttp_client_timeout
}
readiness_probe {
tcp_socket {
+21 -4
View File
@@ -1,6 +1,23 @@
locals {
app_folder = var.persistent_folder
app_version = var.tag
app_fqdn = var.fqdn
db_path = var.db_folder != "" ? var.db_folder : local.app_folder
app_folder = var.persistent_folder
app_version = var.tag
app_fqdn = var.fqdn
db_path = var.db_folder != "" ? var.db_folder : local.app_folder
signup = var.signup_enabled ? "true" : "false"
signup_verify = var.signup_verify ? "true" : "false"
disable_2fa_remember = var.disable_2fa_remember ? "true" : "false"
show_password_hint = var.show_password_hint ? "true" : "false"
password_hints_allowed = var.password_hints_allowed ? "true" : "false"
disable_icon_download = var.disable_icon_download ? "true" : "false"
icon_service = var.icon_service
http_request_block_non_global_ips = var.http_request_block_non_global_ips ? "true" : "false"
extended_logging = var.extended_logging ? "true" : "false"
websocket_enabled = var.websocket_enabled ? "true" : "false"
sends_allowed = var.sends_allowed ? "true" : "false"
trash_auto_delete_days = var.trash_auto_delete_days
events_days_retain = var.events_days_retain
admin_session_lifetime = tostring(var.admin_session_lifetime)
# SMTP locals — only enable mail if host or from is configured
smtp_enabled = var.smtp_host != "" || var.smtp_from != ""
}
+49 -10
View File
@@ -19,11 +19,38 @@ resource "kubernetes_config_map" "vaultwarden_config" {
namespace = kubernetes_namespace.vaultwarden.metadata[0].name
}
data = {
DOMAIN = "https://${local.app_fqdn}/"
SIGNUP = var.signup_enabled ? "true" : "false"
DATABASE_URL = "file:///data/db.sqlite3"
}
data = merge({
DOMAIN = "https://${local.app_fqdn}/"
SIGNUPS_ALLOWED = local.signup
SIGNUPS_VERIFY = local.signup_verify
DATABASE_URL = "/data/db.sqlite3"
ENABLE_WEBSOCKET = local.websocket_enabled
DISABLE_2FA_REMEMBER = local.disable_2fa_remember
SHOW_PASSWORD_HINT = local.show_password_hint
PASSWORD_HINTS_ALLOWED = local.password_hints_allowed
DISABLE_ICON_DOWNLOAD = local.disable_icon_download
ICON_SERVICE = local.icon_service
HTTP_REQUEST_BLOCK_NON_GLOBAL_IPS = local.http_request_block_non_global_ips
EXTENDED_LOGGING = local.extended_logging
SENDS_ALLOWED = local.sends_allowed
TRASH_AUTO_DELETE_DAYS = local.trash_auto_delete_days
EVENTS_DAYS_RETAIN = local.events_days_retain
ADMIN_SESSION_LIFETIME = local.admin_session_lifetime
USE_SENDMAIL = "false"
},
local.smtp_enabled ? {
SMTP_HOST = var.smtp_host
SMTP_PORT = tostring(var.smtp_port)
SMTP_SECURITY = var.smtp_security
SMTP_FROM = var.smtp_from
SMTP_FROM_NAME = var.smtp_from_name
SMTP_TIMEOUT = tostring(var.smtp_timeout)
SMTP_EMBED_IMAGES = var.smtp_embed_images ? "true" : "false"
SMTP_DEBUG = var.smtp_debug ? "true" : "false"
SMTP_ACCEPT_INVALID_CERTS = var.smtp_accept_invalid_certs ? "true" : "false"
SMTP_ACCEPT_INVALID_HOSTNAMES = var.smtp_accept_invalid_hostnames ? "true" : "false"
} : {}
)
}
resource "kubernetes_secret" "vaultwarden_secrets" {
@@ -32,9 +59,14 @@ resource "kubernetes_secret" "vaultwarden_secrets" {
namespace = kubernetes_namespace.vaultwarden.metadata[0].name
}
data = {
data = merge({
ADMIN_TOKEN = var.admin_token
}
},
local.smtp_enabled ? {
SMTP_USERNAME = var.smtp_username
SMTP_PASSWORD = var.smtp_password
} : {}
)
}
resource "kubernetes_deployment" "vaultwarden" {
@@ -62,7 +94,8 @@ resource "kubernetes_deployment" "vaultwarden" {
template {
metadata {
labels = {
app = "vaultwarden"
app = "vaultwarden"
config_checksum = md5(join("", values(kubernetes_config_map.vaultwarden_config.data)))
}
}
@@ -101,6 +134,11 @@ resource "kubernetes_deployment" "vaultwarden" {
}
}
env {
name = "ROCKET_PORT"
value = "8080"
}
security_context {
allow_privilege_escalation = false
privileged = false
@@ -114,7 +152,7 @@ resource "kubernetes_deployment" "vaultwarden" {
port {
name = "http"
container_port = 80
container_port = 8080
protocol = "TCP"
}
@@ -151,6 +189,7 @@ resource "kubernetes_deployment" "vaultwarden" {
name = "vaultwarden-data"
host_path {
path = local.db_path
type = "DirectoryOrCreate"
}
}
@@ -177,7 +216,7 @@ resource "kubernetes_service" "vaultwarden" {
port {
name = "http"
port = 80
target_port = 80
target_port = 8080
}
type = "ClusterIP"
@@ -32,6 +32,84 @@ variable "signup_enabled" {
default = false
}
variable "signup_verify" {
description = "Require email verification on signup (defense-in-depth)"
type = bool
default = true
}
variable "disable_2fa_remember" {
description = "Force 2FA on every login, no 'remember this browser'"
type = bool
default = true
}
variable "show_password_hint" {
description = "Show password hints on web login (no SMTP configured)"
type = bool
default = false
}
variable "password_hints_allowed" {
description = "Allow users to set password hints"
type = bool
default = false
}
variable "disable_icon_download" {
description = "Disable external icon downloads to prevent network leaks"
type = bool
default = true
}
variable "icon_service" {
description = "Icon service to use (internal, bitwarden, duckduckgo, google)"
type = string
default = "internal"
}
variable "http_request_block_non_global_ips" {
description = "Block requests to private/reserved IP addresses"
type = bool
default = true
}
variable "extended_logging" {
description = "Enable extended logging with timestamps for audit"
type = bool
default = true
}
variable "websocket_enabled" {
description = "Enable WebSocket notifications for real-time sync"
type = bool
default = true
}
variable "sends_allowed" {
description = "Allow users to create Bitwarden Sends"
type = bool
default = true
}
variable "trash_auto_delete_days" {
description = "Days before auto-deleting trashed items (empty = never)"
type = string
default = "30"
}
variable "events_days_retain" {
description = "Days to retain event logs (empty = indefinitely)"
type = string
default = "90"
}
variable "admin_session_lifetime" {
description = "Admin session lifetime in minutes"
type = number
default = 20
}
variable "admin_token" {
description = "Admin token for VaultWarden"
type = string
@@ -43,3 +121,90 @@ variable "db_folder" {
type = string
default = ""
}
# --- SMTP / Email settings ---
variable "smtp_host" {
description = "SMTP host for sending emails"
type = string
default = ""
}
variable "smtp_port" {
description = "SMTP port (default: 587 for starttls, 465 for force_tls, 25 for off)"
type = number
default = 587
}
variable "smtp_security" {
description = "SMTP security mode: starttls, force_tls, or off"
type = string
default = "starttls"
}
variable "smtp_from" {
description = "Email address to send from"
type = string
default = ""
}
variable "smtp_from_name" {
description = "Sender name for outgoing emails"
type = string
default = "Vaultwarden"
}
variable "smtp_username" {
description = "SMTP username for authentication"
type = string
default = ""
}
variable "smtp_password" {
description = "SMTP password for authentication"
type = string
sensitive = true
default = ""
}
variable "smtp_timeout" {
description = "SMTP connection timeout in seconds"
type = number
default = 15
}
variable "smtp_auth_mechanism" {
description = "SMTP authentication mechanisms (e.g., Plain, Login, Xoauth2, comma-separated)"
type = string
default = ""
}
variable "smtp_helo_name" {
description = "Server name sent during SMTP HELO"
type = string
default = ""
}
variable "smtp_embed_images" {
description = "Embed images as email attachments"
type = bool
default = true
}
variable "smtp_debug" {
description = "Enable detailed SMTP debug output (warning: may contain sensitive data)"
type = bool
default = false
}
variable "smtp_accept_invalid_certs" {
description = "Accept invalid SMTP server certificates (dangerous, not recommended)"
type = bool
default = false
}
variable "smtp_accept_invalid_hostnames" {
description = "Accept invalid SMTP server hostnames (dangerous, not recommended)"
type = bool
default = false
}