feat: comprehensive security hardening
Traefik: - Enable access logs → /var/log/traefik/access.log (needed for CrowdSec) - Add global security headers middleware: HSTS, X-Frame-Options, CSP, nosniff, XSS filter, referrer policy, permissions policy - Add rate limiting: default 100/s, API 30/s, admin 10/s (strict) - Add Authelia ForwardAuth middleware for SSO integration CrowdSec (new service): - Analyzes Traefik access logs + auth.log in real time - Community IP reputation blocklist (crowdsecurity/traefik + http-cve) - Firewall bouncer: bans malicious IPs at kernel level (iptables) Authelia (new service, auth.csrx.ru): - 2FA/SSO portal with TOTP (Google Authenticator) - Protects: traefik.csrx.ru, sync.csrx.ru, /god-mode/ in Plane - Session: 12h expiry, 30m inactivity, Redis backend - argon2id password hashing Container security: - Add security_opt: no-new-privileges to traefik, vaultwarden, forgejo, grafana, authelia CI/CD security: - Remove hardcoded server IP 87.249.49.32 from workflow - Use SSH_KNOWN_HOSTS secret instead of ssh-keyscan (prevents MITM) - Added SSH_KNOWN_HOSTS secret to Forgejo Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
a42ff4afc7
commit
aa9706bbc4
14 changed files with 428 additions and 54 deletions
|
|
@ -38,9 +38,10 @@ jobs:
|
|||
- name: Configure SSH
|
||||
run: |
|
||||
mkdir -p ~/.ssh
|
||||
echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_ed25519
|
||||
printf '%s' "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_ed25519
|
||||
chmod 600 ~/.ssh/id_ed25519
|
||||
ssh-keyscan -p 22 87.249.49.32 >> ~/.ssh/known_hosts
|
||||
printf '%s' "${{ secrets.SSH_KNOWN_HOSTS }}" > ~/.ssh/known_hosts
|
||||
chmod 600 ~/.ssh/known_hosts
|
||||
|
||||
- name: Write vault password
|
||||
run: |
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ domain_plane: "plane.{{ domain_base }}"
|
|||
domain_sync: "sync.{{ domain_base }}"
|
||||
domain_traefik: "traefik.{{ domain_base }}"
|
||||
domain_dashboard: "dashboard.{{ domain_base }}"
|
||||
domain_auth: "auth.{{ domain_base }}"
|
||||
|
||||
# Service paths
|
||||
services_root: /opt/services
|
||||
|
|
@ -28,6 +29,11 @@ forgejo_runner_token: "{{ vault_forgejo_runner_token }}"
|
|||
grafana_admin_password: "{{ vault_grafana_admin_password }}"
|
||||
alertmanager_telegram_token: "{{ vault_alertmanager_telegram_token }}"
|
||||
alertmanager_telegram_chat_id: "{{ vault_alertmanager_telegram_chat_id }}"
|
||||
authelia_jwt_secret: "{{ vault_authelia_jwt_secret }}"
|
||||
authelia_session_secret: "{{ vault_authelia_session_secret }}"
|
||||
authelia_storage_key: "{{ vault_authelia_storage_key }}"
|
||||
authelia_admin_password_hash: "{{ vault_authelia_admin_password_hash }}"
|
||||
crowdsec_bouncer_key: "{{ vault_crowdsec_bouncer_key }}"
|
||||
|
||||
# CI/CD deploy key (public key — not a secret)
|
||||
ci_deploy_pubkey: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHdr9mRSSUqt7Ym4wA5RpVyz76wEXSOtVfh2/yCSMIbg ci-deploy@forgejo-runner"
|
||||
|
|
|
|||
|
|
@ -1,50 +1,76 @@
|
|||
$ANSIBLE_VAULT;1.1;AES256
|
||||
63336635333534363962303037316537376162313965663565623562613761653239313330303333
|
||||
6163643864313062373336313037383661636238313230650a383062386234313130633231663832
|
||||
64313337663263313362303964326231386236353339663638656235393633643265653338306139
|
||||
6463356531653538620a333037376239343430343335336635636361386335303963373364636162
|
||||
36333531323565616132643431633166633937616630313834623934646134386337393166393834
|
||||
64326237306432376638623833666638316564353134613365323162336136373335396534623838
|
||||
32306435383062393933666430386338376531663236323430366162613363666361353066383138
|
||||
39303864636333343235623136633132663230333433356638653237643635383061366537383462
|
||||
33616235613164323164643662363862656630393361633764313731323638396232353235313838
|
||||
62636231326533616538323734303039613238633533336363376639643630306439353838623966
|
||||
64343135386361663633333162653064306130633263653335313431616365666336613737326237
|
||||
38343731313234663263633431623561653932333161633731346632326262353565356637666230
|
||||
61666134613038313665333162653534313034643562353134356131353938393834633265336561
|
||||
62633063646237393966373334353437636136343938323132356432653664353062363630363633
|
||||
62613066383232353630373130353433356430356435396136373261653761386435636331643636
|
||||
37333964393461626461613662616235653036366434613432613864616633323436643331333866
|
||||
37393235343361363438326466633632323131313830663835663431393137336261616138646666
|
||||
65383166306638353838643933353065386661663833633666626263653135336631383332643939
|
||||
61656561666564643335383566363861303939346538333730636364343234306539343263383336
|
||||
32653439653937313434396466306265336231356437393965363366306134633062356637313464
|
||||
31386538353039316631613265386261303239653965303163646561316366336234333866653663
|
||||
30633666383338623661666232643166613638373763333666653963656165366432643665643838
|
||||
36313135396462303339323638326235666538333530323638336330343263383634643532623164
|
||||
63633633656630363665636664343863343836636134396634653661613633613165623039626466
|
||||
33333932386435333335346135663661313831306636623061396565643665396432343732383463
|
||||
34336333313261643531376432353036336639343834366631393234663064303865396438366432
|
||||
38313630623936393765393233633437386234316233393934316334663533656538383534623563
|
||||
66646466613233313234356166363539653239366433623864663239623964643062393535393038
|
||||
32303132626332303130643064633530313731653831656432323163643733383962343239633436
|
||||
37386561386637363331666336366539313538353838626364626635333432383936613664326338
|
||||
66656438656134323632366265383062336566393030386333646632653132623931663863323966
|
||||
34373232373361326466353066323436653738336431303663383835356436613331313064393835
|
||||
38636365643061626238356539313133383564393365373131383664326234326332633035373433
|
||||
36666533636461653638356637363830633464316139646336623636346563663439356630623534
|
||||
62396436306265336362303735383933356464363437633236363637343665333035633532356637
|
||||
65353634316365346235313138636461663830636232623534363164393965323265623262643934
|
||||
62306339653433353839643830653733343062396164383934393262303135666130643233366639
|
||||
30636565663235323333333933386432333430323433633335383638383632393832323839626437
|
||||
34636638303261616333653632313266336662366663303432616138636330323232353033326630
|
||||
34633366613265373864613238663533306334303164386566656133643535303431643938663135
|
||||
36373838316435613766633261613836353135373832656363643664323533623333336433383761
|
||||
62363532356563353535643531316338363231333331383236613838386266666139346334353434
|
||||
30323266616337643034393363333437393630636337303632303530653866376465343364313637
|
||||
38393863666366613130326165316236396136383132323238653962343734353666333662306163
|
||||
37643465336432623266623932356330326537316261646239623232396233303637353863633337
|
||||
38333665306665656431656262643861643435616538313530376438383738373530316565373835
|
||||
36346263656635363464636533663630363362313461363766373231326434346435386432343866
|
||||
62376262313065386130363533326133623165666633353264666463633062626434303362636434
|
||||
32623165363835623931386434663961343037373835346262663738373461633366
|
||||
61316166393964386231353533353731353730326134323862666166373430346531383435396264
|
||||
3339363034336365363263643165656264333030323036640a313937396562326539633430643931
|
||||
61626330343235646637653065666237626564376130376662366238336135373836613362643963
|
||||
6335393437333362390a393930373132366161333762643535373232613136306664346662366231
|
||||
33643832306638306130653937323863643237346231363432623462313534386162373866663362
|
||||
34313665323632393766626535656239333231396438383833663835623963323530323663323539
|
||||
38373639623235396133373632613337353538666666303538333637333537363162323238376366
|
||||
65393233366162643835316439613262326531373961646336626232626334643331643438663834
|
||||
66386332316561333435313535666161323661663038343464383130663131363130303238626632
|
||||
62383934643539623434333566376463653930353833333433633364383764393732633734633636
|
||||
37646139656634666666613161396631353164363831353366393365643336376330613565353966
|
||||
32363431666534316165366636376164353165333738326230326232386137666366353636633865
|
||||
66623264623730653866663030646431386238633662336162623665356536613832346131316230
|
||||
34353664373930636361383961646334633838366636343335313438663836623761666235383431
|
||||
33373534653930623666323433326636633133336538633166333362353663356264323936623763
|
||||
37396338623962626638346538363565316262646232336266393936323839613533666465663439
|
||||
63343635346333636539373335323831366630356536336262353534643035323765653366656363
|
||||
63393534396135393061653234646362303066386133333736643739373164623034396361363539
|
||||
37326532643064656666363735333535643765643433633131356334393434333939623239343761
|
||||
32383966646435363936346464616233313865303264333331613437396635373336383664326665
|
||||
30366436303264633762336234356431666238353535396234383133353362366465363834643666
|
||||
33653732646264343636646266653138313634346239623764656136303462316364656234623833
|
||||
35313561323464346435656565633036383264373436313164363262643164616436663564643032
|
||||
30646637653036663533663430386134663237333030336430373936323738653030353564313464
|
||||
39393562383735313237366433646431356364363039353539366133333237303532653965666364
|
||||
35393830336533373133366666653765366562336539646131636633326434393164343530633737
|
||||
34626263636163626333373438376137636139643263646336643735316462313361663834613031
|
||||
37396233633831393536313838313964343762383363356238393761646230393334303836633735
|
||||
65666366393932346636396237333166323936613732333036323333343637393931393534323166
|
||||
61626334613035353137303365373365373837616336343838643365616538623538653238363664
|
||||
30356539663763633337393162623764376265646435663064303930643364396439626661666532
|
||||
38306364356463643866336530303430633766336236616135326462353163343637336438373534
|
||||
37616536386131366634633663633566313238366133376131353666663464306463313232626436
|
||||
61306236663332373764303566643332343530366362376134653437356630613937346663323131
|
||||
65376563666434653132383032643830386465363965653530323036623034313764306136366239
|
||||
34666538623232646266666537353033386661333861386564316662386233636265366536386135
|
||||
35653735383231616664366338623264326663353730623461613766613432396233383061383464
|
||||
35376461323233633938646532373663396233396463323565633539353630653934616231636166
|
||||
39633037333963653061386362316662613235646236326666633164336661373963386339633932
|
||||
64616436333637373930373062333463336562303439623937643136323735616231303762373161
|
||||
62666438613038363833626664316435316331373030343738356438323563313565613039326639
|
||||
65386533306132663964386330396566623063633433653439383235373761363037633138376461
|
||||
65323465663137363135336662643432616437323466656666313437333366626234623765643033
|
||||
30633132636235383561373566366465393664663464643965363634323466303433623361613061
|
||||
66623861336537333339636161636564356239636562363166326635646166653933376634626234
|
||||
61643738346263646664356134313138373331343731366532383264613931353030313061636135
|
||||
30343430383630633966393933396238366463373934653130656433633437323137326666633964
|
||||
32626639396165323334393263393961663666623137643834373065383966353835613335636362
|
||||
31646635356233323730393039366162613331393465633139616432353462363165333530373364
|
||||
32343935643933326136383835633232356263343264373437383630313537343138383135613832
|
||||
32383738353435323437336137626231343535633364666663633133353662383139383364373837
|
||||
37386133383135326662383661346639393134313931383637613631343836646663663834336632
|
||||
64383734373362316666343031393764393161613035373863323839383237643863326664656465
|
||||
34306637316466366332666237313064366534323961373166663339333439303365633137386236
|
||||
64313338383037613439626462323737393034303732303537636565353033386365653239326131
|
||||
32326162663766626264653965323134366664386238393564386163613165383661303832633565
|
||||
31306335393439323635653731363931663364613438373130623437376638386364333266643838
|
||||
35303436383839653434316632616163623264326531616439643437663538376333366432666165
|
||||
38363635653864383662346235353561316233656332383031643938613735396635363436313735
|
||||
66396535383030353437626165626432646634613434643830303434643530636566333063323366
|
||||
30663738326562343732376662366566636330346435373838363165643666393764343832656638
|
||||
65666134616265633138376133386438666465666661323631373539666330616638306439636533
|
||||
34346365333462623438333930376133383233373064366336343937616638376163303435313163
|
||||
34303537306532396230383236393731663230393135386133316638343735373666306337376235
|
||||
34616639326432386266373361306537343637356335613136346261316433613464323263646134
|
||||
31326232323738313830353535313363663363393037653631353932613834346133616535666361
|
||||
62353539646331656665323763396662313137366261336139356231646663646564646536373433
|
||||
62646234393737666635626536656636316535343661626364376536633461383530633135396137
|
||||
32343163373265623138333162653231636336373661316530633331346463393365353462336136
|
||||
66336236313765616436646532336164363261656262646135653734376331646665353139613037
|
||||
65323338316139353837623661353134656164613362313632656163643737353435366432666564
|
||||
36303631643331373965616239353762663862636232326234643663383664613666303538316465
|
||||
36323232663263653238393066663839653539343536316461333964316132353531333936663461
|
||||
37326337653930306637333163343431626663633139303263646639313862313365326665376264
|
||||
373039623038653731373939343537376634
|
||||
|
|
|
|||
|
|
@ -27,3 +27,8 @@ grafana_image: "grafana/grafana:11.6.1" # https://hub
|
|||
alertmanager_image: "prom/alertmanager:v0.28.1" # https://hub.docker.com/r/prom/alertmanager/tags
|
||||
loki_image: "grafana/loki:3.4.3" # https://hub.docker.com/r/grafana/loki/tags
|
||||
promtail_image: "grafana/promtail:3.4.3" # https://hub.docker.com/r/grafana/promtail/tags
|
||||
crowdsec_image: "crowdsecurity/crowdsec:v1.6.8" # https://hub.docker.com/r/crowdsecurity/crowdsec/tags
|
||||
crowdsec_bouncer_image: "crowdsecurity/cs-firewall-bouncer:v0.0.31" # https://hub.docker.com/r/crowdsecurity/cs-firewall-bouncer/tags
|
||||
authelia_image: "authelia/authelia:4.38" # https://hub.docker.com/r/authelia/authelia/tags
|
||||
redis_image: "redis:7-alpine" # shared with plane-redis
|
||||
authelia_admin_user: "admin"
|
||||
|
|
|
|||
|
|
@ -134,6 +134,33 @@
|
|||
mode: "0644"
|
||||
notify: Restart stack
|
||||
|
||||
- name: Deploy CrowdSec acquisition config
|
||||
ansible.builtin.template:
|
||||
src: crowdsec/acquis.yaml.j2
|
||||
dest: "{{ services_root }}/crowdsec/acquis.yaml"
|
||||
owner: "{{ deploy_user }}"
|
||||
group: "{{ deploy_group }}"
|
||||
mode: "0644"
|
||||
notify: Restart stack
|
||||
|
||||
- name: Deploy Authelia configuration
|
||||
ansible.builtin.template:
|
||||
src: authelia/configuration.yml.j2
|
||||
dest: "{{ services_root }}/authelia/configuration.yml"
|
||||
owner: "{{ deploy_user }}"
|
||||
group: "{{ deploy_group }}"
|
||||
mode: "0600"
|
||||
notify: Restart stack
|
||||
|
||||
- name: Deploy Authelia users database
|
||||
ansible.builtin.template:
|
||||
src: authelia/users.yml.j2
|
||||
dest: "{{ services_root }}/authelia/users.yml"
|
||||
owner: "{{ deploy_user }}"
|
||||
group: "{{ deploy_group }}"
|
||||
mode: "0600"
|
||||
notify: Restart stack
|
||||
|
||||
- name: Create acme.json for Let's Encrypt certificates
|
||||
ansible.builtin.file:
|
||||
path: "{{ services_root }}/traefik/acme.json"
|
||||
|
|
|
|||
|
|
@ -31,3 +31,6 @@
|
|||
- grafana/provisioning/dashboards/json
|
||||
- prometheus/rules
|
||||
- loki
|
||||
- traefik/logs
|
||||
- crowdsec
|
||||
- authelia
|
||||
|
|
|
|||
|
|
@ -25,6 +25,9 @@
|
|||
- "{{ alertmanager_image }}"
|
||||
- "{{ loki_image }}"
|
||||
- "{{ promtail_image }}"
|
||||
- "{{ crowdsec_image }}"
|
||||
- "{{ crowdsec_bouncer_image }}"
|
||||
- "{{ authelia_image }}"
|
||||
register: pull_result
|
||||
changed_when: "'Status: Downloaded newer image' in pull_result.stdout"
|
||||
retries: 5
|
||||
|
|
|
|||
77
roles/services/templates/authelia/configuration.yml.j2
Normal file
77
roles/services/templates/authelia/configuration.yml.j2
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
# Generated by Ansible — do not edit manually
|
||||
# Authelia v4 configuration
|
||||
|
||||
theme: dark
|
||||
|
||||
server:
|
||||
host: 0.0.0.0
|
||||
port: 9091
|
||||
|
||||
log:
|
||||
level: warn
|
||||
|
||||
jwt_secret: "{{ authelia_jwt_secret }}"
|
||||
|
||||
default_redirection_url: "https://{{ domain_auth }}"
|
||||
|
||||
session:
|
||||
name: authelia_session
|
||||
secret: "{{ authelia_session_secret }}"
|
||||
expiration: 12h
|
||||
inactivity: 30m
|
||||
domain: "{{ domain_base }}"
|
||||
redis:
|
||||
host: authelia-redis
|
||||
port: 6379
|
||||
|
||||
regulation:
|
||||
max_retries: 3
|
||||
find_time: 2m
|
||||
ban_time: 10m
|
||||
|
||||
storage:
|
||||
encryption_key: "{{ authelia_storage_key }}"
|
||||
local:
|
||||
path: /config/db.sqlite3
|
||||
|
||||
notifier:
|
||||
disable_startup_check: true
|
||||
filesystem:
|
||||
filename: /config/notifications.txt
|
||||
|
||||
authentication_backend:
|
||||
password_reset:
|
||||
disable: false
|
||||
file:
|
||||
path: /config/users.yml
|
||||
password:
|
||||
algorithm: argon2id
|
||||
iterations: 3
|
||||
memory: 65536
|
||||
parallelism: 4
|
||||
key_length: 32
|
||||
salt_length: 16
|
||||
|
||||
access_control:
|
||||
default_policy: deny
|
||||
rules:
|
||||
# Authelia portal itself — всегда доступен
|
||||
- domain: "{{ domain_auth }}"
|
||||
policy: bypass
|
||||
|
||||
# Traefik dashboard — только admin, требует 2FA
|
||||
- domain: "{{ domain_traefik }}"
|
||||
policy: two_factor
|
||||
subject: "group:admins"
|
||||
|
||||
# Syncthing — только admin, требует 2FA
|
||||
- domain: "{{ domain_sync }}"
|
||||
policy: two_factor
|
||||
subject: "group:admins"
|
||||
|
||||
# Plane god-mode — только admin, требует 2FA
|
||||
- domain: "{{ domain_plane }}"
|
||||
resources:
|
||||
- "^/god-mode/.*$"
|
||||
policy: two_factor
|
||||
subject: "group:admins"
|
||||
12
roles/services/templates/authelia/users.yml.j2
Normal file
12
roles/services/templates/authelia/users.yml.j2
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
# Generated by Ansible — do not edit manually
|
||||
# Authelia users database
|
||||
# To update password hash: docker exec authelia authelia crypto hash generate argon2 --password 'yourpassword'
|
||||
# To set up TOTP: visit https://{{ domain_auth }} and login — QR code will appear on first use
|
||||
|
||||
users:
|
||||
{{ authelia_admin_user }}:
|
||||
displayname: "Admin"
|
||||
password: "{{ authelia_admin_password_hash }}"
|
||||
email: "{{ acme_email }}"
|
||||
groups:
|
||||
- admins
|
||||
18
roles/services/templates/crowdsec/acquis.yaml.j2
Normal file
18
roles/services/templates/crowdsec/acquis.yaml.j2
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
# Generated by Ansible — do not edit manually
|
||||
# CrowdSec acquisition config: what log sources to watch
|
||||
|
||||
---
|
||||
filenames:
|
||||
- /var/log/traefik/access.log
|
||||
labels:
|
||||
type: traefik
|
||||
---
|
||||
filenames:
|
||||
- /var/log/auth.log
|
||||
labels:
|
||||
type: syslog
|
||||
---
|
||||
filenames:
|
||||
- /var/log/syslog
|
||||
labels:
|
||||
type: syslog
|
||||
|
|
@ -26,6 +26,9 @@ networks:
|
|||
monitoring:
|
||||
driver: bridge
|
||||
internal: true
|
||||
authelia-internal:
|
||||
driver: bridge
|
||||
internal: true
|
||||
|
||||
volumes:
|
||||
vaultwarden_data:
|
||||
|
|
@ -41,6 +44,8 @@ volumes:
|
|||
prometheus_data:
|
||||
grafana_data:
|
||||
loki_data:
|
||||
crowdsec_data:
|
||||
authelia_data:
|
||||
|
||||
services:
|
||||
|
||||
|
|
@ -50,6 +55,8 @@ services:
|
|||
image: {{ traefik_image }}
|
||||
container_name: traefik
|
||||
restart: unless-stopped
|
||||
security_opt:
|
||||
- no-new-privileges:true
|
||||
ports:
|
||||
- "80:80"
|
||||
- "443:443"
|
||||
|
|
@ -60,6 +67,7 @@ services:
|
|||
- {{ services_root }}/traefik/traefik.yml:/etc/traefik/traefik.yml:ro
|
||||
- {{ services_root }}/traefik/dynamic:/etc/traefik/dynamic:ro
|
||||
- {{ services_root }}/traefik/acme.json:/acme/acme.json
|
||||
- {{ services_root }}/traefik/logs:/var/log/traefik
|
||||
healthcheck:
|
||||
test: ["CMD", "traefik", "healthcheck", "--ping"]
|
||||
interval: 30s
|
||||
|
|
@ -71,6 +79,8 @@ services:
|
|||
image: {{ vaultwarden_image }}
|
||||
container_name: vaultwarden
|
||||
restart: unless-stopped
|
||||
security_opt:
|
||||
- no-new-privileges:true
|
||||
networks:
|
||||
- backend
|
||||
volumes:
|
||||
|
|
@ -94,6 +104,8 @@ services:
|
|||
image: {{ forgejo_image }}
|
||||
container_name: forgejo
|
||||
restart: unless-stopped
|
||||
security_opt:
|
||||
- no-new-privileges:true
|
||||
depends_on:
|
||||
forgejo-db:
|
||||
condition: service_healthy
|
||||
|
|
@ -484,6 +496,8 @@ services:
|
|||
image: {{ grafana_image }}
|
||||
container_name: grafana
|
||||
restart: unless-stopped
|
||||
security_opt:
|
||||
- no-new-privileges:true
|
||||
depends_on:
|
||||
- prometheus
|
||||
networks:
|
||||
|
|
@ -532,5 +546,75 @@ services:
|
|||
- /var/log:/var/log:ro
|
||||
- /var/lib/docker/containers:/var/lib/docker/containers:ro
|
||||
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||
- {{ services_root }}/traefik/logs:/var/log/traefik:ro
|
||||
- {{ services_root }}/loki/promtail.yml:/etc/promtail/config.yml:ro
|
||||
command: -config.file=/etc/promtail/config.yml
|
||||
|
||||
# ── Security Stack ─────────────────────────────────────────────────────────
|
||||
# CrowdSec: анализирует логи Traefik, банит злоумышленников по IP
|
||||
# Использует community-репутацию + локальный анализ поведения
|
||||
crowdsec:
|
||||
image: {{ crowdsec_image }}
|
||||
container_name: crowdsec
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- monitoring
|
||||
environment:
|
||||
- COLLECTIONS=crowdsecurity/traefik crowdsecurity/http-cve crowdsecurity/linux
|
||||
- GID=1000
|
||||
volumes:
|
||||
- crowdsec_data:/var/lib/crowdsec/data
|
||||
- {{ services_root }}/crowdsec/acquis.yaml:/etc/crowdsec/acquis.yaml:ro
|
||||
- {{ services_root }}/traefik/logs:/var/log/traefik:ro
|
||||
- /var/log/auth.log:/var/log/auth.log:ro
|
||||
- /var/log/syslog:/var/log/syslog:ro
|
||||
|
||||
# Bouncer: получает решения от CrowdSec и блокирует IP через firewall
|
||||
crowdsec-bouncer:
|
||||
image: {{ crowdsec_bouncer_image }}
|
||||
container_name: crowdsec-bouncer
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- monitoring
|
||||
environment:
|
||||
- CROWDSEC_BOUNCER_API_KEY=${CROWDSEC_BOUNCER_KEY}
|
||||
- CROWDSEC_AGENT_HOST=crowdsec:8080
|
||||
- GID=0
|
||||
cap_add:
|
||||
- NET_ADMIN
|
||||
- NET_RAW
|
||||
|
||||
# ── Authelia: 2FA SSO portal ───────────────────────────────────────────────
|
||||
# Защищает: Traefik dashboard, Syncthing, Plane /god-mode/
|
||||
# Вход: логин + пароль + TOTP (Google Authenticator)
|
||||
authelia:
|
||||
image: {{ authelia_image }}
|
||||
container_name: authelia
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- authelia-redis
|
||||
networks:
|
||||
- backend
|
||||
- authelia-internal
|
||||
volumes:
|
||||
- authelia_data:/config
|
||||
- {{ services_root }}/authelia/configuration.yml:/config/configuration.yml:ro
|
||||
- {{ services_root }}/authelia/users.yml:/config/users.yml:ro
|
||||
environment:
|
||||
- AUTHELIA_JWT_SECRET=${AUTHELIA_JWT_SECRET}
|
||||
- AUTHELIA_SESSION_SECRET=${AUTHELIA_SESSION_SECRET}
|
||||
- AUTHELIA_STORAGE_ENCRYPTION_KEY=${AUTHELIA_STORAGE_KEY}
|
||||
- TZ=UTC
|
||||
healthcheck:
|
||||
test: ["CMD", "wget", "-qO-", "http://localhost:9091/api/health"]
|
||||
interval: 30s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
|
||||
authelia-redis:
|
||||
image: {{ redis_image }}
|
||||
container_name: authelia-redis
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- authelia-internal
|
||||
command: redis-server --appendonly yes --maxmemory 64mb --maxmemory-policy allkeys-lru
|
||||
|
|
|
|||
|
|
@ -12,3 +12,7 @@ DOMAIN_SYNC={{ domain_sync }}
|
|||
DOMAIN_TRAEFIK={{ domain_traefik }}
|
||||
FORGEJO_RUNNER_TOKEN={{ forgejo_runner_token }}
|
||||
GRAFANA_ADMIN_PASSWORD={{ grafana_admin_password }}
|
||||
AUTHELIA_JWT_SECRET={{ authelia_jwt_secret }}
|
||||
AUTHELIA_SESSION_SECRET={{ authelia_session_secret }}
|
||||
AUTHELIA_STORAGE_KEY={{ authelia_storage_key }}
|
||||
CROWDSEC_BOUNCER_KEY={{ crowdsec_bouncer_key }}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ http:
|
|||
tls:
|
||||
certresolver: letsencrypt
|
||||
service: api@internal
|
||||
middlewares: [traefik-auth]
|
||||
middlewares: [authelia@docker, rate-limit-strict]
|
||||
|
||||
vaultwarden:
|
||||
rule: "Host(`{{ domain_vault }}`)"
|
||||
|
|
@ -17,6 +17,7 @@ http:
|
|||
tls:
|
||||
certresolver: letsencrypt
|
||||
service: vaultwarden
|
||||
middlewares: [rate-limit-default]
|
||||
|
||||
forgejo:
|
||||
rule: "Host(`{{ domain_git }}`)"
|
||||
|
|
@ -24,6 +25,7 @@ http:
|
|||
tls:
|
||||
certresolver: letsencrypt
|
||||
service: forgejo
|
||||
middlewares: [rate-limit-default]
|
||||
|
||||
plane-api:
|
||||
rule: "Host(`{{ domain_plane }}`) && (PathPrefix(`/api/`) || PathPrefix(`/auth/`))"
|
||||
|
|
@ -31,6 +33,7 @@ http:
|
|||
tls:
|
||||
certresolver: letsencrypt
|
||||
service: plane-api
|
||||
middlewares: [rate-limit-api]
|
||||
|
||||
plane:
|
||||
rule: "Host(`{{ domain_plane }}`)"
|
||||
|
|
@ -38,6 +41,25 @@ http:
|
|||
tls:
|
||||
certresolver: letsencrypt
|
||||
service: plane-web
|
||||
middlewares: [rate-limit-default]
|
||||
|
||||
plane-godmode:
|
||||
rule: "Host(`{{ domain_plane }}`) && PathPrefix(`/god-mode/`)"
|
||||
entrypoints: [websecure]
|
||||
tls:
|
||||
certresolver: letsencrypt
|
||||
service: plane-admin
|
||||
middlewares: [authelia@docker, rate-limit-strict]
|
||||
priority: 10
|
||||
|
||||
plane-spaces:
|
||||
rule: "Host(`{{ domain_plane }}`) && PathPrefix(`/spaces/`)"
|
||||
entrypoints: [websecure]
|
||||
tls:
|
||||
certresolver: letsencrypt
|
||||
service: plane-space
|
||||
middlewares: [rate-limit-default]
|
||||
priority: 10
|
||||
|
||||
syncthing:
|
||||
rule: "Host(`{{ domain_sync }}`)"
|
||||
|
|
@ -45,7 +67,7 @@ http:
|
|||
tls:
|
||||
certresolver: letsencrypt
|
||||
service: syncthing
|
||||
middlewares: [syncthing-auth]
|
||||
middlewares: [authelia@docker, rate-limit-strict]
|
||||
|
||||
grafana:
|
||||
rule: "Host(`{{ domain_dashboard }}`)"
|
||||
|
|
@ -53,6 +75,15 @@ http:
|
|||
tls:
|
||||
certresolver: letsencrypt
|
||||
service: grafana
|
||||
middlewares: [rate-limit-default]
|
||||
|
||||
authelia:
|
||||
rule: "Host(`{{ domain_auth }}`)"
|
||||
entrypoints: [websecure]
|
||||
tls:
|
||||
certresolver: letsencrypt
|
||||
service: authelia
|
||||
middlewares: [rate-limit-strict]
|
||||
|
||||
services:
|
||||
vaultwarden:
|
||||
|
|
@ -75,6 +106,16 @@ http:
|
|||
servers:
|
||||
- url: "http://plane-web:3000"
|
||||
|
||||
plane-admin:
|
||||
loadBalancer:
|
||||
servers:
|
||||
- url: "http://plane-admin:80"
|
||||
|
||||
plane-space:
|
||||
loadBalancer:
|
||||
servers:
|
||||
- url: "http://plane-space:3000"
|
||||
|
||||
syncthing:
|
||||
loadBalancer:
|
||||
servers:
|
||||
|
|
@ -85,7 +126,51 @@ http:
|
|||
servers:
|
||||
- url: "http://grafana:3000"
|
||||
|
||||
authelia:
|
||||
loadBalancer:
|
||||
servers:
|
||||
- url: "http://authelia:9091"
|
||||
|
||||
middlewares:
|
||||
# ── Security Headers (applied globally via entrypoint) ─────────────────
|
||||
security-headers:
|
||||
headers:
|
||||
stsSeconds: 31536000
|
||||
stsIncludeSubdomains: true
|
||||
stsPreload: true
|
||||
forceSTSHeader: true
|
||||
frameDeny: true
|
||||
contentTypeNosniff: true
|
||||
browserXssFilter: true
|
||||
referrerPolicy: "strict-origin-when-cross-origin"
|
||||
permissionsPolicy: "camera=(), microphone=(), geolocation=(), payment=()"
|
||||
customResponseHeaders:
|
||||
X-Robots-Tag: "noindex, nofollow"
|
||||
Server: ""
|
||||
|
||||
# ── Rate Limiting ──────────────────────────────────────────────────────
|
||||
# Default: 100 req/s burst 50 — general web traffic
|
||||
rate-limit-default:
|
||||
rateLimit:
|
||||
average: 100
|
||||
burst: 50
|
||||
period: 1s
|
||||
|
||||
# API: 30 req/s burst 20 — API endpoints
|
||||
rate-limit-api:
|
||||
rateLimit:
|
||||
average: 30
|
||||
burst: 20
|
||||
period: 1s
|
||||
|
||||
# Strict: 10 req/s burst 5 — admin/login interfaces
|
||||
rate-limit-strict:
|
||||
rateLimit:
|
||||
average: 10
|
||||
burst: 5
|
||||
period: 1s
|
||||
|
||||
# ── Auth (legacy basic auth kept for direct fallback) ─────────────────
|
||||
traefik-auth:
|
||||
basicAuth:
|
||||
users:
|
||||
|
|
@ -95,3 +180,14 @@ http:
|
|||
basicAuth:
|
||||
users:
|
||||
- "{{ syncthing_basic_auth_htpasswd }}"
|
||||
|
||||
# ── Authelia ForwardAuth ───────────────────────────────────────────────
|
||||
authelia:
|
||||
forwardAuth:
|
||||
address: "http://authelia:9091/api/verify?rd=https://{{ domain_auth }}"
|
||||
trustForwardHeader: true
|
||||
authResponseHeaders:
|
||||
- Remote-User
|
||||
- Remote-Groups
|
||||
- Remote-Email
|
||||
- Remote-Name
|
||||
|
|
|
|||
|
|
@ -8,7 +8,16 @@ global:
|
|||
log:
|
||||
level: INFO
|
||||
|
||||
accessLog: {}
|
||||
accessLog:
|
||||
filePath: /var/log/traefik/access.log
|
||||
bufferingSize: 100
|
||||
fields:
|
||||
defaultMode: keep
|
||||
headers:
|
||||
defaultMode: drop
|
||||
names:
|
||||
User-Agent: keep
|
||||
Referer: drop
|
||||
|
||||
api:
|
||||
dashboard: true
|
||||
|
|
@ -26,6 +35,9 @@ entryPoints:
|
|||
scheme: https
|
||||
websecure:
|
||||
address: ":443"
|
||||
http:
|
||||
middlewares:
|
||||
- security-headers@file
|
||||
|
||||
certificatesResolvers:
|
||||
letsencrypt:
|
||||
|
|
|
|||
Loading…
Reference in a new issue