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
|
- name: Configure SSH
|
||||||
run: |
|
run: |
|
||||||
mkdir -p ~/.ssh
|
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
|
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
|
- name: Write vault password
|
||||||
run: |
|
run: |
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ domain_plane: "plane.{{ domain_base }}"
|
||||||
domain_sync: "sync.{{ domain_base }}"
|
domain_sync: "sync.{{ domain_base }}"
|
||||||
domain_traefik: "traefik.{{ domain_base }}"
|
domain_traefik: "traefik.{{ domain_base }}"
|
||||||
domain_dashboard: "dashboard.{{ domain_base }}"
|
domain_dashboard: "dashboard.{{ domain_base }}"
|
||||||
|
domain_auth: "auth.{{ domain_base }}"
|
||||||
|
|
||||||
# Service paths
|
# Service paths
|
||||||
services_root: /opt/services
|
services_root: /opt/services
|
||||||
|
|
@ -28,6 +29,11 @@ forgejo_runner_token: "{{ vault_forgejo_runner_token }}"
|
||||||
grafana_admin_password: "{{ vault_grafana_admin_password }}"
|
grafana_admin_password: "{{ vault_grafana_admin_password }}"
|
||||||
alertmanager_telegram_token: "{{ vault_alertmanager_telegram_token }}"
|
alertmanager_telegram_token: "{{ vault_alertmanager_telegram_token }}"
|
||||||
alertmanager_telegram_chat_id: "{{ vault_alertmanager_telegram_chat_id }}"
|
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/CD deploy key (public key — not a secret)
|
||||||
ci_deploy_pubkey: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHdr9mRSSUqt7Ym4wA5RpVyz76wEXSOtVfh2/yCSMIbg ci-deploy@forgejo-runner"
|
ci_deploy_pubkey: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHdr9mRSSUqt7Ym4wA5RpVyz76wEXSOtVfh2/yCSMIbg ci-deploy@forgejo-runner"
|
||||||
|
|
|
||||||
|
|
@ -1,50 +1,76 @@
|
||||||
$ANSIBLE_VAULT;1.1;AES256
|
$ANSIBLE_VAULT;1.1;AES256
|
||||||
63336635333534363962303037316537376162313965663565623562613761653239313330303333
|
61316166393964386231353533353731353730326134323862666166373430346531383435396264
|
||||||
6163643864313062373336313037383661636238313230650a383062386234313130633231663832
|
3339363034336365363263643165656264333030323036640a313937396562326539633430643931
|
||||||
64313337663263313362303964326231386236353339663638656235393633643265653338306139
|
61626330343235646637653065666237626564376130376662366238336135373836613362643963
|
||||||
6463356531653538620a333037376239343430343335336635636361386335303963373364636162
|
6335393437333362390a393930373132366161333762643535373232613136306664346662366231
|
||||||
36333531323565616132643431633166633937616630313834623934646134386337393166393834
|
33643832306638306130653937323863643237346231363432623462313534386162373866663362
|
||||||
64326237306432376638623833666638316564353134613365323162336136373335396534623838
|
34313665323632393766626535656239333231396438383833663835623963323530323663323539
|
||||||
32306435383062393933666430386338376531663236323430366162613363666361353066383138
|
38373639623235396133373632613337353538666666303538333637333537363162323238376366
|
||||||
39303864636333343235623136633132663230333433356638653237643635383061366537383462
|
65393233366162643835316439613262326531373961646336626232626334643331643438663834
|
||||||
33616235613164323164643662363862656630393361633764313731323638396232353235313838
|
66386332316561333435313535666161323661663038343464383130663131363130303238626632
|
||||||
62636231326533616538323734303039613238633533336363376639643630306439353838623966
|
62383934643539623434333566376463653930353833333433633364383764393732633734633636
|
||||||
64343135386361663633333162653064306130633263653335313431616365666336613737326237
|
37646139656634666666613161396631353164363831353366393365643336376330613565353966
|
||||||
38343731313234663263633431623561653932333161633731346632326262353565356637666230
|
32363431666534316165366636376164353165333738326230326232386137666366353636633865
|
||||||
61666134613038313665333162653534313034643562353134356131353938393834633265336561
|
66623264623730653866663030646431386238633662336162623665356536613832346131316230
|
||||||
62633063646237393966373334353437636136343938323132356432653664353062363630363633
|
34353664373930636361383961646334633838366636343335313438663836623761666235383431
|
||||||
62613066383232353630373130353433356430356435396136373261653761386435636331643636
|
33373534653930623666323433326636633133336538633166333362353663356264323936623763
|
||||||
37333964393461626461613662616235653036366434613432613864616633323436643331333866
|
37396338623962626638346538363565316262646232336266393936323839613533666465663439
|
||||||
37393235343361363438326466633632323131313830663835663431393137336261616138646666
|
63343635346333636539373335323831366630356536336262353534643035323765653366656363
|
||||||
65383166306638353838643933353065386661663833633666626263653135336631383332643939
|
63393534396135393061653234646362303066386133333736643739373164623034396361363539
|
||||||
61656561666564643335383566363861303939346538333730636364343234306539343263383336
|
37326532643064656666363735333535643765643433633131356334393434333939623239343761
|
||||||
32653439653937313434396466306265336231356437393965363366306134633062356637313464
|
32383966646435363936346464616233313865303264333331613437396635373336383664326665
|
||||||
31386538353039316631613265386261303239653965303163646561316366336234333866653663
|
30366436303264633762336234356431666238353535396234383133353362366465363834643666
|
||||||
30633666383338623661666232643166613638373763333666653963656165366432643665643838
|
33653732646264343636646266653138313634346239623764656136303462316364656234623833
|
||||||
36313135396462303339323638326235666538333530323638336330343263383634643532623164
|
35313561323464346435656565633036383264373436313164363262643164616436663564643032
|
||||||
63633633656630363665636664343863343836636134396634653661613633613165623039626466
|
30646637653036663533663430386134663237333030336430373936323738653030353564313464
|
||||||
33333932386435333335346135663661313831306636623061396565643665396432343732383463
|
39393562383735313237366433646431356364363039353539366133333237303532653965666364
|
||||||
34336333313261643531376432353036336639343834366631393234663064303865396438366432
|
35393830336533373133366666653765366562336539646131636633326434393164343530633737
|
||||||
38313630623936393765393233633437386234316233393934316334663533656538383534623563
|
34626263636163626333373438376137636139643263646336643735316462313361663834613031
|
||||||
66646466613233313234356166363539653239366433623864663239623964643062393535393038
|
37396233633831393536313838313964343762383363356238393761646230393334303836633735
|
||||||
32303132626332303130643064633530313731653831656432323163643733383962343239633436
|
65666366393932346636396237333166323936613732333036323333343637393931393534323166
|
||||||
37386561386637363331666336366539313538353838626364626635333432383936613664326338
|
61626334613035353137303365373365373837616336343838643365616538623538653238363664
|
||||||
66656438656134323632366265383062336566393030386333646632653132623931663863323966
|
30356539663763633337393162623764376265646435663064303930643364396439626661666532
|
||||||
34373232373361326466353066323436653738336431303663383835356436613331313064393835
|
38306364356463643866336530303430633766336236616135326462353163343637336438373534
|
||||||
38636365643061626238356539313133383564393365373131383664326234326332633035373433
|
37616536386131366634633663633566313238366133376131353666663464306463313232626436
|
||||||
36666533636461653638356637363830633464316139646336623636346563663439356630623534
|
61306236663332373764303566643332343530366362376134653437356630613937346663323131
|
||||||
62396436306265336362303735383933356464363437633236363637343665333035633532356637
|
65376563666434653132383032643830386465363965653530323036623034313764306136366239
|
||||||
65353634316365346235313138636461663830636232623534363164393965323265623262643934
|
34666538623232646266666537353033386661333861386564316662386233636265366536386135
|
||||||
62306339653433353839643830653733343062396164383934393262303135666130643233366639
|
35653735383231616664366338623264326663353730623461613766613432396233383061383464
|
||||||
30636565663235323333333933386432333430323433633335383638383632393832323839626437
|
35376461323233633938646532373663396233396463323565633539353630653934616231636166
|
||||||
34636638303261616333653632313266336662366663303432616138636330323232353033326630
|
39633037333963653061386362316662613235646236326666633164336661373963386339633932
|
||||||
34633366613265373864613238663533306334303164386566656133643535303431643938663135
|
64616436333637373930373062333463336562303439623937643136323735616231303762373161
|
||||||
36373838316435613766633261613836353135373832656363643664323533623333336433383761
|
62666438613038363833626664316435316331373030343738356438323563313565613039326639
|
||||||
62363532356563353535643531316338363231333331383236613838386266666139346334353434
|
65386533306132663964386330396566623063633433653439383235373761363037633138376461
|
||||||
30323266616337643034393363333437393630636337303632303530653866376465343364313637
|
65323465663137363135336662643432616437323466656666313437333366626234623765643033
|
||||||
38393863666366613130326165316236396136383132323238653962343734353666333662306163
|
30633132636235383561373566366465393664663464643965363634323466303433623361613061
|
||||||
37643465336432623266623932356330326537316261646239623232396233303637353863633337
|
66623861336537333339636161636564356239636562363166326635646166653933376634626234
|
||||||
38333665306665656431656262643861643435616538313530376438383738373530316565373835
|
61643738346263646664356134313138373331343731366532383264613931353030313061636135
|
||||||
36346263656635363464636533663630363362313461363766373231326434346435386432343866
|
30343430383630633966393933396238366463373934653130656433633437323137326666633964
|
||||||
62376262313065386130363533326133623165666633353264666463633062626434303362636434
|
32626639396165323334393263393961663666623137643834373065383966353835613335636362
|
||||||
32623165363835623931386434663961343037373835346262663738373461633366
|
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
|
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
|
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
|
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"
|
mode: "0644"
|
||||||
notify: Restart stack
|
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
|
- name: Create acme.json for Let's Encrypt certificates
|
||||||
ansible.builtin.file:
|
ansible.builtin.file:
|
||||||
path: "{{ services_root }}/traefik/acme.json"
|
path: "{{ services_root }}/traefik/acme.json"
|
||||||
|
|
|
||||||
|
|
@ -31,3 +31,6 @@
|
||||||
- grafana/provisioning/dashboards/json
|
- grafana/provisioning/dashboards/json
|
||||||
- prometheus/rules
|
- prometheus/rules
|
||||||
- loki
|
- loki
|
||||||
|
- traefik/logs
|
||||||
|
- crowdsec
|
||||||
|
- authelia
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,9 @@
|
||||||
- "{{ alertmanager_image }}"
|
- "{{ alertmanager_image }}"
|
||||||
- "{{ loki_image }}"
|
- "{{ loki_image }}"
|
||||||
- "{{ promtail_image }}"
|
- "{{ promtail_image }}"
|
||||||
|
- "{{ crowdsec_image }}"
|
||||||
|
- "{{ crowdsec_bouncer_image }}"
|
||||||
|
- "{{ authelia_image }}"
|
||||||
register: pull_result
|
register: pull_result
|
||||||
changed_when: "'Status: Downloaded newer image' in pull_result.stdout"
|
changed_when: "'Status: Downloaded newer image' in pull_result.stdout"
|
||||||
retries: 5
|
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:
|
monitoring:
|
||||||
driver: bridge
|
driver: bridge
|
||||||
internal: true
|
internal: true
|
||||||
|
authelia-internal:
|
||||||
|
driver: bridge
|
||||||
|
internal: true
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
vaultwarden_data:
|
vaultwarden_data:
|
||||||
|
|
@ -41,6 +44,8 @@ volumes:
|
||||||
prometheus_data:
|
prometheus_data:
|
||||||
grafana_data:
|
grafana_data:
|
||||||
loki_data:
|
loki_data:
|
||||||
|
crowdsec_data:
|
||||||
|
authelia_data:
|
||||||
|
|
||||||
services:
|
services:
|
||||||
|
|
||||||
|
|
@ -50,6 +55,8 @@ services:
|
||||||
image: {{ traefik_image }}
|
image: {{ traefik_image }}
|
||||||
container_name: traefik
|
container_name: traefik
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
security_opt:
|
||||||
|
- no-new-privileges:true
|
||||||
ports:
|
ports:
|
||||||
- "80:80"
|
- "80:80"
|
||||||
- "443:443"
|
- "443:443"
|
||||||
|
|
@ -60,6 +67,7 @@ services:
|
||||||
- {{ services_root }}/traefik/traefik.yml:/etc/traefik/traefik.yml:ro
|
- {{ services_root }}/traefik/traefik.yml:/etc/traefik/traefik.yml:ro
|
||||||
- {{ services_root }}/traefik/dynamic:/etc/traefik/dynamic:ro
|
- {{ services_root }}/traefik/dynamic:/etc/traefik/dynamic:ro
|
||||||
- {{ services_root }}/traefik/acme.json:/acme/acme.json
|
- {{ services_root }}/traefik/acme.json:/acme/acme.json
|
||||||
|
- {{ services_root }}/traefik/logs:/var/log/traefik
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: ["CMD", "traefik", "healthcheck", "--ping"]
|
test: ["CMD", "traefik", "healthcheck", "--ping"]
|
||||||
interval: 30s
|
interval: 30s
|
||||||
|
|
@ -71,6 +79,8 @@ services:
|
||||||
image: {{ vaultwarden_image }}
|
image: {{ vaultwarden_image }}
|
||||||
container_name: vaultwarden
|
container_name: vaultwarden
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
security_opt:
|
||||||
|
- no-new-privileges:true
|
||||||
networks:
|
networks:
|
||||||
- backend
|
- backend
|
||||||
volumes:
|
volumes:
|
||||||
|
|
@ -94,6 +104,8 @@ services:
|
||||||
image: {{ forgejo_image }}
|
image: {{ forgejo_image }}
|
||||||
container_name: forgejo
|
container_name: forgejo
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
security_opt:
|
||||||
|
- no-new-privileges:true
|
||||||
depends_on:
|
depends_on:
|
||||||
forgejo-db:
|
forgejo-db:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
|
|
@ -484,6 +496,8 @@ services:
|
||||||
image: {{ grafana_image }}
|
image: {{ grafana_image }}
|
||||||
container_name: grafana
|
container_name: grafana
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
security_opt:
|
||||||
|
- no-new-privileges:true
|
||||||
depends_on:
|
depends_on:
|
||||||
- prometheus
|
- prometheus
|
||||||
networks:
|
networks:
|
||||||
|
|
@ -532,5 +546,75 @@ services:
|
||||||
- /var/log:/var/log:ro
|
- /var/log:/var/log:ro
|
||||||
- /var/lib/docker/containers:/var/lib/docker/containers:ro
|
- /var/lib/docker/containers:/var/lib/docker/containers:ro
|
||||||
- /var/run/docker.sock:/var/run/docker.sock: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
|
- {{ services_root }}/loki/promtail.yml:/etc/promtail/config.yml:ro
|
||||||
command: -config.file=/etc/promtail/config.yml
|
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 }}
|
DOMAIN_TRAEFIK={{ domain_traefik }}
|
||||||
FORGEJO_RUNNER_TOKEN={{ forgejo_runner_token }}
|
FORGEJO_RUNNER_TOKEN={{ forgejo_runner_token }}
|
||||||
GRAFANA_ADMIN_PASSWORD={{ grafana_admin_password }}
|
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:
|
tls:
|
||||||
certresolver: letsencrypt
|
certresolver: letsencrypt
|
||||||
service: api@internal
|
service: api@internal
|
||||||
middlewares: [traefik-auth]
|
middlewares: [authelia@docker, rate-limit-strict]
|
||||||
|
|
||||||
vaultwarden:
|
vaultwarden:
|
||||||
rule: "Host(`{{ domain_vault }}`)"
|
rule: "Host(`{{ domain_vault }}`)"
|
||||||
|
|
@ -17,6 +17,7 @@ http:
|
||||||
tls:
|
tls:
|
||||||
certresolver: letsencrypt
|
certresolver: letsencrypt
|
||||||
service: vaultwarden
|
service: vaultwarden
|
||||||
|
middlewares: [rate-limit-default]
|
||||||
|
|
||||||
forgejo:
|
forgejo:
|
||||||
rule: "Host(`{{ domain_git }}`)"
|
rule: "Host(`{{ domain_git }}`)"
|
||||||
|
|
@ -24,6 +25,7 @@ http:
|
||||||
tls:
|
tls:
|
||||||
certresolver: letsencrypt
|
certresolver: letsencrypt
|
||||||
service: forgejo
|
service: forgejo
|
||||||
|
middlewares: [rate-limit-default]
|
||||||
|
|
||||||
plane-api:
|
plane-api:
|
||||||
rule: "Host(`{{ domain_plane }}`) && (PathPrefix(`/api/`) || PathPrefix(`/auth/`))"
|
rule: "Host(`{{ domain_plane }}`) && (PathPrefix(`/api/`) || PathPrefix(`/auth/`))"
|
||||||
|
|
@ -31,6 +33,7 @@ http:
|
||||||
tls:
|
tls:
|
||||||
certresolver: letsencrypt
|
certresolver: letsencrypt
|
||||||
service: plane-api
|
service: plane-api
|
||||||
|
middlewares: [rate-limit-api]
|
||||||
|
|
||||||
plane:
|
plane:
|
||||||
rule: "Host(`{{ domain_plane }}`)"
|
rule: "Host(`{{ domain_plane }}`)"
|
||||||
|
|
@ -38,6 +41,25 @@ http:
|
||||||
tls:
|
tls:
|
||||||
certresolver: letsencrypt
|
certresolver: letsencrypt
|
||||||
service: plane-web
|
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:
|
syncthing:
|
||||||
rule: "Host(`{{ domain_sync }}`)"
|
rule: "Host(`{{ domain_sync }}`)"
|
||||||
|
|
@ -45,7 +67,7 @@ http:
|
||||||
tls:
|
tls:
|
||||||
certresolver: letsencrypt
|
certresolver: letsencrypt
|
||||||
service: syncthing
|
service: syncthing
|
||||||
middlewares: [syncthing-auth]
|
middlewares: [authelia@docker, rate-limit-strict]
|
||||||
|
|
||||||
grafana:
|
grafana:
|
||||||
rule: "Host(`{{ domain_dashboard }}`)"
|
rule: "Host(`{{ domain_dashboard }}`)"
|
||||||
|
|
@ -53,6 +75,15 @@ http:
|
||||||
tls:
|
tls:
|
||||||
certresolver: letsencrypt
|
certresolver: letsencrypt
|
||||||
service: grafana
|
service: grafana
|
||||||
|
middlewares: [rate-limit-default]
|
||||||
|
|
||||||
|
authelia:
|
||||||
|
rule: "Host(`{{ domain_auth }}`)"
|
||||||
|
entrypoints: [websecure]
|
||||||
|
tls:
|
||||||
|
certresolver: letsencrypt
|
||||||
|
service: authelia
|
||||||
|
middlewares: [rate-limit-strict]
|
||||||
|
|
||||||
services:
|
services:
|
||||||
vaultwarden:
|
vaultwarden:
|
||||||
|
|
@ -75,6 +106,16 @@ http:
|
||||||
servers:
|
servers:
|
||||||
- url: "http://plane-web:3000"
|
- url: "http://plane-web:3000"
|
||||||
|
|
||||||
|
plane-admin:
|
||||||
|
loadBalancer:
|
||||||
|
servers:
|
||||||
|
- url: "http://plane-admin:80"
|
||||||
|
|
||||||
|
plane-space:
|
||||||
|
loadBalancer:
|
||||||
|
servers:
|
||||||
|
- url: "http://plane-space:3000"
|
||||||
|
|
||||||
syncthing:
|
syncthing:
|
||||||
loadBalancer:
|
loadBalancer:
|
||||||
servers:
|
servers:
|
||||||
|
|
@ -85,7 +126,51 @@ http:
|
||||||
servers:
|
servers:
|
||||||
- url: "http://grafana:3000"
|
- url: "http://grafana:3000"
|
||||||
|
|
||||||
|
authelia:
|
||||||
|
loadBalancer:
|
||||||
|
servers:
|
||||||
|
- url: "http://authelia:9091"
|
||||||
|
|
||||||
middlewares:
|
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:
|
traefik-auth:
|
||||||
basicAuth:
|
basicAuth:
|
||||||
users:
|
users:
|
||||||
|
|
@ -95,3 +180,14 @@ http:
|
||||||
basicAuth:
|
basicAuth:
|
||||||
users:
|
users:
|
||||||
- "{{ syncthing_basic_auth_htpasswd }}"
|
- "{{ 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:
|
log:
|
||||||
level: INFO
|
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:
|
api:
|
||||||
dashboard: true
|
dashboard: true
|
||||||
|
|
@ -26,6 +35,9 @@ entryPoints:
|
||||||
scheme: https
|
scheme: https
|
||||||
websecure:
|
websecure:
|
||||||
address: ":443"
|
address: ":443"
|
||||||
|
http:
|
||||||
|
middlewares:
|
||||||
|
- security-headers@file
|
||||||
|
|
||||||
certificatesResolvers:
|
certificatesResolvers:
|
||||||
letsencrypt:
|
letsencrypt:
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue