462 lines
10 KiB
HCL
462 lines
10 KiB
HCL
job "traefik" {
|
|
|
|
|
|
datacenters = ["dc1"]
|
|
region = "global"
|
|
|
|
|
|
group "traefik" {
|
|
|
|
|
|
count = 2
|
|
shutdown_delay = "6s"
|
|
|
|
|
|
constraint {
|
|
operator = "distinct_hosts"
|
|
value = "true"
|
|
}
|
|
|
|
|
|
ephemeral_disk {
|
|
# Use minimal ephemeral disk
|
|
size = 101
|
|
}
|
|
|
|
|
|
network {
|
|
mode = "bridge"
|
|
port "http" {
|
|
static = 80
|
|
to = 5080
|
|
}
|
|
port "https" {
|
|
static = 443
|
|
to = 5443
|
|
}
|
|
}
|
|
|
|
service {
|
|
name = "traefik-sidecar"
|
|
port = "https"
|
|
|
|
connect {
|
|
sidecar_service {
|
|
}
|
|
sidecar_task {
|
|
config {
|
|
args = [
|
|
"-c",
|
|
"${NOMAD_SECRETS_DIR}/envoy_bootstrap.json",
|
|
"-l",
|
|
"${meta.connect.log_level}",
|
|
"--concurrency",
|
|
"${meta.connect.proxy_concurrency}",
|
|
"--disable-hot-restart"
|
|
]
|
|
}
|
|
|
|
resources {
|
|
cpu = 50
|
|
memory = 64
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
service {
|
|
name = "traefik"
|
|
port = "https"
|
|
task = "traefik"
|
|
|
|
meta {
|
|
alloc = "${NOMAD_ALLOC_INDEX}"
|
|
datacenter = "${NOMAD_DC}"
|
|
group = "${NOMAD_GROUP_NAME}"
|
|
job = "${NOMAD_JOB_NAME}"
|
|
namespace = "${NOMAD_NAMESPACE}"
|
|
node = "${node.unique.name}"
|
|
region = "${NOMAD_REGION}"
|
|
}
|
|
|
|
|
|
# Traefik supports native Consul service mesh
|
|
connect {
|
|
native = true
|
|
}
|
|
|
|
tags = [
|
|
"traefik.http.routers.traefik-api.rule=(Host(`traefik.example.org`) || HostRegexp(`(.+\\.)?traefik.service.consul`)) && (PathPrefix(`/api`) || PathPrefix(`/dashboard`))",
|
|
"traefik.http.routers.traefik-api.service=api@internal",
|
|
|
|
"traefik.enable=true",
|
|
"traefik.http.routers.traefik-api.entrypoints=https",
|
|
"traefik.http.middlewares.csp-traefik-api.headers.contentsecuritypolicy=default-src 'self';font-src 'self' data:;img-src 'self' data:;script-src 'self' 'unsafe-inline' 'unsafe-eval';style-src 'self' 'unsafe-inline';",
|
|
"traefik.http.middlewares.traefik-api-99-path.replacepathregex.regex=^/dashboard/(.*)",
|
|
"traefik.http.middlewares.traefik-api-99-path.replacepathregex.replacement=/dashboard/$${1}",
|
|
"traefik.http.routers.traefik-api.middlewares=security-headers@file,rate-limit-std@file,forward-proto@file,inflight-std@file,hsts@file,compression@file,traefik-api-99-path,csp-traefik-api",
|
|
|
|
"traefik.http.routers.traefik-ping.rule=(Host(`traefik.example.org`) || HostRegexp(`(.+\\.)?traefik.service.consul`)) && Path(`/ping`) && Method(`GET`)",
|
|
"traefik.http.routers.traefik-ping.service=ping@internal",
|
|
|
|
"traefik.enable=true",
|
|
"traefik.http.routers.traefik-ping.entrypoints=http,https",
|
|
"traefik.http.routers.traefik-ping.priority=2000",
|
|
"traefik.http.middlewares.csp-traefik-ping.headers.contentsecuritypolicy=default-src 'self';font-src 'self' data:;img-src 'self' data:;script-src 'self' 'unsafe-inline' 'unsafe-eval';style-src 'self' 'unsafe-inline';",
|
|
"traefik.http.routers.traefik-ping.middlewares=security-headers@file,rate-limit-std@file,forward-proto@file,inflight-std@file,hsts@file,compression@file,csp-traefik-ping",
|
|
|
|
|
|
"traefik-${NOMAD_ALLOC_INDEX}"
|
|
]
|
|
}
|
|
|
|
|
|
|
|
task "traefik" {
|
|
driver = "docker"
|
|
user = 5443
|
|
|
|
vault {
|
|
policies = ["traefik"]
|
|
}
|
|
|
|
config {
|
|
image = "danielberteaud/traefik:3.0.0-rc5-1"
|
|
command = "traefik"
|
|
args = [
|
|
"--configfile=/secrets/traefik.yml"
|
|
]
|
|
}
|
|
|
|
# Main traefik configuration
|
|
template {
|
|
data = <<_EOF
|
|
log:
|
|
level: INFO
|
|
|
|
accessLog:
|
|
bufferingSize: 100
|
|
|
|
entryPoints:
|
|
http:
|
|
address: ":{{ env "NOMAD_PORT_http" }}"
|
|
http:
|
|
redirections:
|
|
entryPoint:
|
|
priority: 1000
|
|
to: :{{ env "NOMAD_HOST_PORT_https" }}
|
|
|
|
transport:
|
|
lifeCycle:
|
|
requestAcceptGraceTimeout: 4
|
|
https:
|
|
address: ":{{ env "NOMAD_PORT_https" }}"
|
|
http:
|
|
tls: {}
|
|
|
|
transport:
|
|
lifeCycle:
|
|
requestAcceptGraceTimeout: 4
|
|
|
|
api:
|
|
dashboard: True
|
|
|
|
providers:
|
|
consulCatalog:
|
|
prefix: traefik
|
|
endpoint:
|
|
address: {{ sockaddr "GetInterfaceIP \"nomad\"" }}:8500
|
|
scheme: http
|
|
token: {{ with secret "consul/creds/traefik" }}{{ .Data.token }}{{ end }}
|
|
exposedByDefault: False
|
|
connectAware: True
|
|
connectByDefault: True
|
|
serviceName: traefik
|
|
refreshInterval: 5s
|
|
watch: True
|
|
file:
|
|
directory: /secrets/config
|
|
watch: True
|
|
|
|
ping:
|
|
manualRouting: True
|
|
|
|
_EOF
|
|
destination = "secrets/traefik.yml"
|
|
perms = "0400"
|
|
uid = 105443
|
|
gid = 100000
|
|
}
|
|
|
|
# Dynamic file configuration
|
|
|
|
template {
|
|
data = <<_EOF
|
|
---
|
|
|
|
{{ if gt (len (secrets "kv/service/traefik/basicauth/")) 0 }}
|
|
http:
|
|
middlewares:
|
|
{{- range secrets "kv/service/traefik/basicauth/" }}
|
|
basicauth-{{ . }}:
|
|
basicAuth:
|
|
realm: {{ . }}
|
|
removeheader: true
|
|
users:
|
|
{{- with secret (printf "kv/data/service/traefik/basicauth/%s" .) }}
|
|
{{- range $k, $v := .Data.data }}
|
|
- {{ $k }}:{{ if $v | regexMatch "^\\$2y\\$" }}{{ $v }}{{ else }}{{ sprig_bcrypt $v }}{{ end }}
|
|
{{- end }}
|
|
{{- end }}
|
|
{{- end }}
|
|
{{- end }}
|
|
|
|
_EOF
|
|
destination = "secrets/config/basicauth.yml"
|
|
change_mode = "noop"
|
|
perms = "0400"
|
|
uid = 105443
|
|
gid = 100000
|
|
}
|
|
|
|
|
|
|
|
template {
|
|
data = <<_EOF
|
|
---
|
|
|
|
|
|
_EOF
|
|
destination = "secrets/config/lemonldap.yml"
|
|
change_mode = "noop"
|
|
perms = "0400"
|
|
uid = 105443
|
|
gid = 100000
|
|
}
|
|
|
|
|
|
|
|
template {
|
|
data = <<_EOF
|
|
---
|
|
|
|
{{- if ne 0 (len (secrets "kv/service/traefik/certs/")) }}
|
|
tls:
|
|
certificates:
|
|
{{- range secrets "kv/service/traefik/certs/" }}
|
|
{{- $cn := . }}
|
|
{{- with secret (printf "kv/service/traefik/certs/%s" $cn) }}
|
|
# {{ $cn }}
|
|
- certFile: |-
|
|
{{ .Data.data.cert | replaceAll "\n\n" "\n" | indent 8 }}
|
|
keyFile: |-
|
|
{{ .Data.data.key | indent 8 }}
|
|
{{- end }}
|
|
{{- end }}
|
|
{{- end }}
|
|
|
|
_EOF
|
|
destination = "secrets/config/certificates.yml"
|
|
change_mode = "noop"
|
|
perms = "0400"
|
|
uid = 105443
|
|
gid = 100000
|
|
}
|
|
|
|
|
|
|
|
template {
|
|
data = <<_EOF
|
|
---
|
|
|
|
{{ if ne 0 (len (ls "common/ip")) }}
|
|
http:
|
|
middlewares:
|
|
{{ range ls "common/ip" }}
|
|
ip-{{ .Key }}:
|
|
ipAllowList:
|
|
sourceRange:{{ range .Value | parseYAML }}
|
|
{{- if . | regexMatch "^include:.*" }}
|
|
# Include IP from {{ . | replaceAll "include:" "" }}
|
|
{{- range key (printf "common/ip/%s" . | replaceAll "include:" "") | parseYAML }}
|
|
- {{ . }}{{ end }}
|
|
{{- else }}
|
|
- {{ . }}{{ end }}{{ end }}
|
|
{{ end }}
|
|
|
|
tcp:
|
|
middlewares:
|
|
{{ range ls "common/ip" }}
|
|
ip-{{ .Key }}:
|
|
ipAllowList:
|
|
sourceRange:{{ range .Value | parseYAML }}
|
|
{{- if . | regexMatch "^include:.*" }}
|
|
# Include IP from {{ . | replaceAll "include:" "" }}
|
|
{{- range key (printf "common/ip/%s" . | replaceAll "include:" "") | parseYAML }}
|
|
- {{ . }}{{ end }}
|
|
{{- else }}
|
|
- {{ . }}{{ end }}{{ end }}
|
|
{{ end }}
|
|
{{ end }}
|
|
|
|
_EOF
|
|
destination = "secrets/config/ip.yml"
|
|
change_mode = "noop"
|
|
perms = "0400"
|
|
uid = 105443
|
|
gid = 100000
|
|
}
|
|
|
|
|
|
|
|
template {
|
|
data = <<_EOF
|
|
---
|
|
|
|
http:
|
|
middlewares:
|
|
autodetect:
|
|
contentType: {}
|
|
compression:
|
|
compress:
|
|
excludedContentTypes:
|
|
- image/png
|
|
- image/jpeg
|
|
- image/jpg
|
|
- image/pjpeg
|
|
- image/avif
|
|
- image/webp
|
|
- image/x-icon
|
|
- font/woff2
|
|
- font/woff
|
|
- video/webm
|
|
- video/ogg
|
|
- video/mpeg
|
|
- video/mp4
|
|
- video/3gpp
|
|
- video/3gpp2
|
|
- video/ogg
|
|
- video/x-flv
|
|
- video/h261
|
|
- video/h263
|
|
- video/h264
|
|
- video/jpm
|
|
- video/jpeg
|
|
- video/quicktime
|
|
- audio/x-aac
|
|
- audio/x-aiff
|
|
- audio/mpeg
|
|
- audio/mp4
|
|
- audio/ogg
|
|
- audio/webm
|
|
- audio/opus
|
|
- application/zip
|
|
- application/gzip
|
|
- application/x-7z-compressed
|
|
- application/x-ace-compressed
|
|
- application/x-debian-package
|
|
- application/vnd.android.package-archive
|
|
|
|
_EOF
|
|
destination = "secrets/config/performance.yml"
|
|
change_mode = "noop"
|
|
perms = "0400"
|
|
uid = 105443
|
|
gid = 100000
|
|
}
|
|
|
|
|
|
|
|
template {
|
|
data = <<_EOF
|
|
---
|
|
|
|
http:
|
|
middlewares:
|
|
rate-limit-std:
|
|
rateLimit:
|
|
average: 30
|
|
burst: 50
|
|
|
|
rate-limit-high:
|
|
rateLimit:
|
|
average: 100
|
|
burst: 200
|
|
|
|
inflight-std:
|
|
inFlightReq:
|
|
amount: 100
|
|
|
|
inflight-high:
|
|
inFlightReq:
|
|
amount: 300
|
|
|
|
security-headers:
|
|
headers:
|
|
contentTypeNosniff: True
|
|
browserXssFilter: True
|
|
# customFrameOptionsValue: sameorigin
|
|
customResponseHeaders:
|
|
Server: ""
|
|
X-Powered-By: ""
|
|
X-Envoy-Upstream-Service-Time: ""
|
|
|
|
hsts:
|
|
headers:
|
|
forceSTSHeader: True
|
|
stsIncludeSubdomains: True
|
|
stsSeconds: 63072000
|
|
stsPreload: True
|
|
|
|
csp-strict:
|
|
headers:
|
|
contentSecurityPolicy: "default-src 'self'; img-src 'self' data:; style-src 'self' 'unsafe-inline'"
|
|
|
|
csp-relaxed:
|
|
headers:
|
|
contentSecurityPolicy: "default-src 'self'; img-src 'self' data:; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; font-src 'self' data:"
|
|
|
|
_EOF
|
|
destination = "secrets/config/security.yml"
|
|
change_mode = "noop"
|
|
perms = "0400"
|
|
uid = 105443
|
|
gid = 100000
|
|
}
|
|
|
|
|
|
|
|
template {
|
|
data = <<_EOF
|
|
---
|
|
|
|
http:
|
|
middlewares:
|
|
forward-proto:
|
|
headers:
|
|
customRequestHeaders:
|
|
X-Forwarded-Proto: https
|
|
|
|
_EOF
|
|
destination = "secrets/config/proxy.yml"
|
|
change_mode = "noop"
|
|
perms = "0400"
|
|
uid = 105443
|
|
gid = 100000
|
|
}
|
|
|
|
|
|
resources {
|
|
cpu = 500
|
|
memory = 256
|
|
memory_max = 300
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
# vim: syntax=hcl
|