From 75bed6bb04f04109b0ff565c1ca5854609afdd53 Mon Sep 17 00:00:00 2001 From: jack Date: Thu, 26 Mar 2026 04:06:29 +0700 Subject: [PATCH] feat: remove mail stack and Vaultwarden Removed services: - docker-mailserver (Postfix + Dovecot) - SnappyMail webmail - Vaultwarden password manager Removed infrastructure: - certbot + Cloudflare DNS-01 TLS for mx.csrx.ru - UFW rules for ports 25/587/993/465 - mail-internal and webmail-internal Docker networks - SMTP config from Outline env - vault, mail Traefik routes - All related vault secrets and variables Co-Authored-By: Claude Sonnet 4.6 --- inventory/group_vars/all/main.yml | 8 - inventory/group_vars/all/vault.yml | 226 ++++++------- roles/services/defaults/main.yml | 1 - .../services/templates/docker-compose.yml.j2 | 26 -- .../templates/traefik/dynamic/routes.yml.j2 | 26 -- roles/tools/defaults/main.yml | 2 - roles/tools/tasks/main.yml | 299 ------------------ roles/tools/templates/docker-compose.yml.j2 | 69 ---- roles/tools/templates/env.j2 | 8 - 9 files changed, 105 insertions(+), 560 deletions(-) diff --git a/inventory/group_vars/all/main.yml b/inventory/group_vars/all/main.yml index 8daf598..94eb715 100644 --- a/inventory/group_vars/all/main.yml +++ b/inventory/group_vars/all/main.yml @@ -3,7 +3,6 @@ domain_base: "csrx.ru" # Derived domains -domain_vault: "vault.{{ domain_base }}" domain_git: "git.{{ domain_base }}" domain_plane: "plane.{{ domain_base }}" domain_traefik: "traefik.{{ domain_base }}" @@ -12,8 +11,6 @@ domain_auth: "auth.{{ domain_base }}" domain_status: "status.{{ domain_base }}" domain_wiki: "wiki.{{ domain_base }}" domain_n8n: "n8n.{{ domain_base }}" -domain_mail: "mail.{{ domain_base }}" # SnappyMail webmail (HTTPS via Traefik) -domain_mx: "mx.{{ domain_base }}" # docker-mailserver FQDN (SMTP/IMAP direct) # Service paths services_root: /opt/services @@ -22,7 +19,6 @@ deploy_group: deploy # Secrets (from vault) acme_email: "{{ vault_acme_email }}" -vaultwarden_admin_token: "{{ vault_vaultwarden_admin_token }}" forgejo_db_password: "{{ vault_forgejo_db_password }}" plane_db_password: "{{ vault_plane_db_password }}" plane_secret_key: "{{ vault_plane_secret_key }}" @@ -46,10 +42,6 @@ outline_utils_secret: "{{ vault_outline_utils_secret }}" outline_db_password: "{{ vault_outline_db_password }}" n8n_encryption_key: "{{ vault_n8n_encryption_key }}" n8n_jwt_secret: "{{ vault_n8n_jwt_secret }}" -mailserver_noreply_password: "{{ vault_mailserver_noreply_password }}" -snappymail_admin_password: "{{ vault_snappymail_admin_password }}" -mailserver_admin_password: "{{ vault_mailserver_admin_password }}" -mailserver_jack_password: "{{ vault_mailserver_jack_password }}" # Server IPs (used for cross-server Traefik routing) ip_main: "87.249.49.32" diff --git a/inventory/group_vars/all/vault.yml b/inventory/group_vars/all/vault.yml index 70a2e7a..4b8817a 100644 --- a/inventory/group_vars/all/vault.yml +++ b/inventory/group_vars/all/vault.yml @@ -1,122 +1,106 @@ $ANSIBLE_VAULT;1.1;AES256 -38623734633034386664333334383830336637343861383134323961363536333136346436363838 -3836343032363036633735316362646165623030613161320a663961653037633132376166303635 -30653738383130393738383931323535633631303434343233663463346635376561313662336664 -6434633432643536330a336635616262306632616332616361313738383239316135393735643063 -39336634306164363632373066313866366462653536313537396539363235626339303334353031 -65663866626361383663343834333466376339383463366235343166323361366131643534383665 -64663038316634376161633161366663376232636535356239333733303933396561373235363863 -35366335313066343835663362313434363438313137643766323835393531306536346163313933 -31343930316637623830623666633164343765663434396365366134306439336164333466316133 -33346364613731623633346136313033333461623762313930323565313633346633396330646565 -33386264663561666330356662366430383532316639373536356166383365343035623564623832 -37353565303732316665363862363266343836393839663836666434333237326530613936376231 -33613439633265626166383162646530653339313530353136643338666633393036663665663539 -30333932343464636231323231353235623264316536386233353563623235373932316236356563 -65653533383064646130366236303064396662623664326662353737623437653730363463646139 -32333861663339343361383234383332623737626233323666343730626663363738333932643334 -64353365643334323735623937396664393561333434663139363739613532386265656366316136 -33303232663939353566376163366131393066646233333035396135653861643566633032656264 -65353338373661663466613263343866643664353063656333346363373065303263373863326163 -31386565396364353634636231306166393862326364373463393930373036373531323830346163 -34333262333734356436313032356337356336343138643463343635643234653639346337336138 -30333764333466356230626462353463316636636330383333646530623039316330313336656266 -38366338346562613864663961633439656234313662663661303665303466393863623164346464 -36333136303430313034653365343437343166643263356635636433613361663462396233336163 -64316530633063666465396331393030313666333265626634313635613335383037363366353638 -36366132356661316362643533336366326437326561393464366533656134633366643433653061 -38343839643935376430323563373733623334323562653666356333613130336537643864393737 -33353839313438306164666238313131353166316632623633326261393961613333336331643831 -33356636626161613263366261653262633661613766323835366562333062343462623439663462 -64306238386264626364303963376164626531356436353731383965306365386431356166336238 -61323235373033323433306161663832366330646339303235386133323630363064383930336233 -30636338656439376138363238323965653734336538363761633033626464313535653262363638 -39663763623637636635396430623539666162373439643832303766323061383133343865366466 -61653133343061366334343933623330313764666639313931636662383161613432366239613238 -30373034343864383630316233616434646131643337303132313362323935313333323534343032 -32666633616237353161363265376232346663343937393965343665396338613932363864303366 -33613062383531666437306436636364636535323631626163636437396165313663366432343938 -36306332353739636439343130353464646432313761326231643962616166366536656139643963 -36623134663364376237323639643930393664313866393338316135306666653733663930333266 -33396662396339623839643936626465666665396137623731333265363931343137643237623435 -39623132613435346231373632333135303666336533303363393530306533666566633565326466 -35356362646335323861343634386531633035393730646463623337333435663365616163333863 -37666132356463613064326438316132613564383234363134313739356134323236633636633237 -39343236663039383230336463316663363564383638343736613665343238613736646234663366 -35613364643164626364643034323766666266313561386539336437646337353730343363383434 -61346361613637656336346131363830366539653264376330356661363464316139303963663833 -63613632623934313366333965356630646237376636383138393731363564356131333838336538 -36666362336239633665613734323536393630653433643337666166306230623161666366356336 -32326161313432646465393365396265363634633633343065623762653438353139313839396163 -30633764656636303965616434373235643231633336626133356363386333623339373234646664 -66663066333063303465336338623165666164303531303332663964366461643931303863643061 -30336461643662393036356536663335643536613335323664326234623834383932653263633733 -36303864316361643766353439366230363532616562333062653632623737313931343064366433 -36626634323135333764343339313634323734613765343264386632373733326430363137303030 -66356637353930353539303062386564346166343237633239653037333561306365316635356131 -38613331383131303233646331623035626661313233346561383938323164356536306332623136 -38333765323836366535396464616332313665663166663161653330613764306630643665643739 -31663866653666333130316136393861666236343935396133636538323938376330346339366162 -62663137326339376136343230396462333966396235383331313566386664396137303663353437 -35363139666263666232346661643766656362343339396539346631393330346236633163393633 -36396165646231343364333662633639353437303634343065656461386563373531343234356131 -63653865623039626130633731306439613435353265306262386564373765306539623939666361 -38383131323061613362386438633866653131666261333838363134336138613462303939373062 -34396532316535666331336330306463643662346339646565303532653364383730383239616237 -62333832643038613837316538303931303537326663616564343531326461363536663033303133 -31353238656537396234636133616666333364383730653733396631373038313966663631356238 -33363636616336636166306234393538633133343535303166343766623136356162333163616135 -64386635656438643132623430616435353636313739373634653530386461643136333533396633 -34633561333963653036623632326131323664323938343735666138623534303136333165313763 -34633565383935643738666335633633353534386166306631613536373830663733363063306138 -34373637303438633330393037306665386131633964393137373963623438336232643439333261 -35633831376533376333336235326234633231643036363430646364653330313461333534623065 -31666131656366663962353230643839646661623165653530653533306563633166653839316633 -35356138636134323333323961393961366231353736383463626132643031613165623630346334 -32663464613230343839643166663538303734373263663061383031643538633634636632346531 -39663464373439663264376162613464346666326137386163306132383763343337653062366162 -33333431333133633438636633663133613138633732623833313264313766643131366634303234 -65323831643661396330393439306437333438646130326462303964376632306139363661636336 -36323738303436386431643332303862356634643464326130386662643833613835633335313635 -33643365326138326638343539303437643933643733383930633236663839306636333332316132 -65346531663265663439616332663964363639353061666531616164613139303265323137343062 -36653332326338633661323463303637393338613534343835323638376231656334333530323034 -34663036383261303463353232313431383837313162313061363265343431396337633731343365 -64333661333637343564343432353031303538303165383566656537383863643564333336646535 -34376533383766363131363664646635386133636263613532383137376233373130303264653231 -61326363313365396631346565306166366164653530636434346132666537653637363133313866 -34643130346539353736353936656139616230646637396264333839626339653638623839323361 -39303639373732616338383930663236333464386638646664306430656333366531623466353866 -36306333643336373330333661376333663964363964633364633731363831333536393064313566 -37313439386664623832656230376334643535343336663261363230323662306134663961323131 -37373630343164323839656237343861363633323865363532666438373936386531313532623938 -36326631373635323664353463396438643264613135626463653037303739633762636537653661 -64336236383631353766623536383063336636333566643434333237363635656634643437383838 -34653166646332336135303732373365636366343236393633326430326339646239656561336136 -38626162626236346162373335643964333136386438636234333238306264373663643963613339 -33333831353263393739653964313837636161373231663830383538363163393833623837633832 -35303061336632373630653634393633623533313561326639343934306231363265316163313137 -34633962613165616239333061613332613531326564613963346634356561626639643735623836 -63636333356536373239623034613331666230626638373233663937353036633936313333666164 -66343933373164653937326635356436306165363761393330353334633765333633383639303133 -32646534633762303232383332373932653866663035323666363039386632643062346562373035 -31353737383538313036656333623630363234356136343533333539356533623566336430373961 -65323738393466633930623334386365656163313836316165656138313936303364623763666266 -32383130346139623966303061303765363537303664383433633638643032333031326232633538 -34636636643031366335623362323062356534356163326663656530663835636165366430616564 -37343462663230653830613862373732636636346262353436323037656535343436356133663464 -62316635353064646564626366396565326233376465343264356165323664623834373562323664 -33333636373834346635386331383037623933643036653364383932373637383766393738353762 -36356233383139366431363637376265383639323738643133653764663734666638363537303763 -65356535323365326337626464316138386464373566383866356639613961353130356233306630 -32306633393961336363346561376636346466613161643236353038313665626161383830313839 -35336632646462366235656236303362326262373130623166303833666430653563623764323230 -34616134386339373339386264626435666239363264356631653438323666613765626134353830 -37363930353833336337633861623264653163616565393962323839613063363537653634666439 -32663736333131663166303563376263353333303537346635383361306665653634653631626262 -65663165313036393437343332353937633538363533363165396136383261363234386236393962 -63333664656562623466366134373263366165623739326334386332653861353964343363386163 -33656365363131313535316262366666393936303366636439623032636131343537333438336562 -33336663373331643037306439363533643933313937346432636236633663393166386234333931 -32393166306461323631626561383630363561643830646239656330643837326635663438353665 -66363534356265386664376633363739373231663037643930653766373931333633 +62303439636364323266353466656530343431326238393630386234356163306338393062373837 +6631373736376131373162333431396461656139623566370a653734356261613164383139336335 +36333863613661306239326634373162666361366630633265613266366562323337666537306363 +6465643139336636650a343230653364343262656436393563656466386530616130643764623832 +33346463373263373135356365633063666631316563643132316262383637643833393230313565 +31356431623762336438373839393938663864313262383230316630633566303638616634316136 +36313135663963333139613036366264643130663332393163303664633636623765623536633562 +35323737376661373835626134633035373830353637353964613061323463333739353933316336 +33383934383235376563333239373833343462376133336439653736313561303866636166653439 +31333366326236316266363966623332323535666439633232386634646362653433646563313265 +64376662316537326133326236396137643635656564346466646334393535633464306633346462 +39363931383066303766626165323561363562386363666134623662386465643237343738656439 +36656363396464316235636339653966313563616166376239346431323433383462623462383737 +30353464393365396138613638336464373862353730623836336365393932396165353339636139 +61356365653966616430643261663864356466646136373236303764616531303061336162626435 +36323732383736623239343835353265303165373865643966383234666334653231396437396433 +36326536343135636135643135646538356465663861303564396330306536656162383032346233 +62333037316439626638373564363033313632333330323939353863396539353534663931386166 +66623762623330613530323136336630306538666663336134636439303433313430316137393163 +61613630393637333762373163376663646561313064353932633263316634656361386432663865 +39333131323837383065663038383362393239306463346338393634326663623538353037373866 +65393736653432393463653339353166343233633635383437356233656634323637376632666362 +35303265623538356464383736396165346234666663356334656436356431343162353337343465 +66323230316636303432343664343236376535646564313434306564666434616663383233323839 +34356635633030653865386264383836646165366238393330613231373939653565663265313165 +31653638343631366562643731666262343532376464316164643262386166663132356330336464 +35663064663532653738653065333864363434643764653466373138626630376330643064323334 +37353033383861333230636565383831353332396239353765666630306664313061326135313732 +37363066393763363939613466333839626563363264383430663238346463663235363732366332 +32646639393036616239663237303430356563656563376133326565313761626331323139646430 +33333832376638653334666531623634393331623663376134653437303438396437666134303635 +62653165306436343732336437333837383562373263653764363931313163356439666664616430 +61303733633135623564626230623932366633396538343636323530663636643738393032366663 +35626134376535623934323565356465363835316466353937646163393966653966376137323535 +65396330616333316231626162643462393662343063336639396166613834343030393435313338 +38323332366235363733366637633635336666363831376339303466396234316462346539656639 +39303836343638373365663539353239616638636639323738373930326237613033656162353133 +36316330353437616332336262643935396637653736363038306137303632653634343562313238 +39616137666639633838666262326239323365643033666234633865626136373165363838343761 +36653438333336613236613061316137333665386536663566623139313232383033303633333661 +64633933303236363335383038346531663230396231653563386465323537613064393064353035 +65396262636437336238366331316136386639333637663764613362306137366362363139316265 +66353938353237303737343263336262656234663034653432613137326232303163656431323263 +31613536643062343635623962653363343031666438636637333030323033303637383239353930 +64353633373033646330343531356132636663336231363733356334633030656539616561373130 +66373338386430613235323733643539653364343438343037366133316635663461353537396361 +36663664663631303762616166303432643633356635306563393237663435393565653164613763 +38663839323538623034323333393038633833303730333865643036663331383838373532316162 +30343531666665373231653737386662633537663733386439653534353264616565653562656464 +65383230656137316536653437316435353339633734616131643564383632373265303533616262 +31346530376166356531663234636265306339356231313765373165643566656231366136323030 +66313037336436373035386364613263353635353263383161646139353934326663623466653431 +63343764313334613137393764623731633065663835393930336434333931643139336166383138 +37393233323464316433636336326233313566303332633731363562633139616638383962366366 +34613737636233306162666239396563323537626332643132383938623532323666626437363066 +30666236353639633930616264393166356561346134333034336335636231333736346331316233 +64333262323230613534646562613930643732323834643530353534613439373937313531623562 +33333632623735613436626366373431613132316633363338343335656266636663326133376330 +32626535306661623138623065366363616162373166303565613861366261316533373764376266 +30383833383133373431316264373335323531303763653636306236396639306539663964623032 +30613037633638346339613638353935383763623534306633623037643333306362653365336438 +32396432376266346137323731643865636264613832366137393963396137333532393538303165 +35343037613338353634656464306131616664323637643035336664363031333166313566316266 +35653438313065373666303539396139386332373061373433333035653262613930303834306633 +66666132383939636535353565626333616332346261346332373566346331646565653835643065 +64336265613137366539613130336435343961663430666239343839633565633939393338373632 +34653434646437393862313535643563623730333435333231343630623331343266623832313263 +34353265656236326635353432643932373231653732616561663938653137373262376432323635 +64643731343730623063313664613139613063616132653232336138363761353833306238663430 +37616463363363333231636530653731663263383838383662616534356430356666653731303266 +65346437383138666338613133336266393235373266643237386639323838306661373532366537 +31343535633235376137346638316363346435663833326165613030336138663439316132663935 +35336433366231653963333630643635346439616165396538616637303364663662643735326430 +62306431643833323432646239383064636132363731626365636337333333666333366339356433 +34336363353866353138663634363936363962646137636163663536333732653965306131636234 +31383866623639636566363731343662346364313366613832313032393965613465303433383738 +35343161316237613932643466366334663963623332633033303534323365386631633131656464 +34636435643366353238633039353930326430336439623661636433663431626463396666343534 +39643263353738313238356635303235353162376436313130613733623665613630336461363464 +35613832393437653033306234333961343863643961363430316438383834636530343761656434 +35623936356430363633386630663736356637343539393864393730616230396662613731313363 +63396562623838306435363761323935656237653262623638346163346331343330633262383432 +36316433373462343137356136323231666330333135343762646165643135643165336331386431 +35396664643138383665366331306133646330633232333830353463353663613533306339343233 +66393363363535656232333661346563623832626136353461663065653564343062643538366533 +32383930646130643066373032393133646664626465653939343564643631326164333864386461 +31323839653737396233646161316235666232613935356336653864636464663430313236613130 +38363839626139366235626530616166383231333730356630616336646638666634396366383831 +62353564343665393736383432633538366162303632656261383933656239383836306665623464 +62303637306534636436386334356233663931633236376162363934636130356364326661663534 +66616633646633336661306333343165363662336461393463643162653633353663633731343265 +34613361643830376232616233643462336266643534326139653865346536366561393539636335 +35663063313463353330653639326335336561663935353539366462306431353531626333633366 +39316133323832363231346635623366323939653739393964643862373930613332663963623965 +37616365363464666434326266656237623232633531386366633037343739343337636364383833 +32613031383561313166643062353036373166633230646331366561383134313063623732333534 +63383432393833313961646133656439656234353638336335373761396637613462646131363130 +33613833326433643933316531396363313436366238636133633531626363396461313933386664 +35363862346235633264386530376337313130376330626631353364363938653538363032633238 +37363965353064373764363961343234626166336139303632633964363064373633393937626363 +33643336373636613162373761343437626338343765313437323734626538643034636165383437 +32636639313334306565343239653037643364396538653434373337336435623464613234633931 +31363163323731363834323635643337633161363539626531626562343839323664636265306138 +37313936323836386131376261346461343438356534616238386337313133663062316437326231 +61613832663732653265633139353431356366363237333436393561623262613836 diff --git a/roles/services/defaults/main.yml b/roles/services/defaults/main.yml index bdc8717..f193434 100644 --- a/roles/services/defaults/main.yml +++ b/roles/services/defaults/main.yml @@ -5,7 +5,6 @@ services_root: /opt/services # IMPORTANT: pin each image to a specific version tag. # Check Docker Hub for the latest stable release before updating. traefik_image: "traefik:v3.3" # https://hub.docker.com/_/traefik/tags -vaultwarden_image: "vaultwarden/server:1.32.7" # https://hub.docker.com/r/vaultwarden/server/tags forgejo_image: "codeberg.org/forgejo/forgejo:9" forgejo_db_image: "postgres:16-alpine" plane_frontend_image: "makeplane/plane-frontend:stable" # https://hub.docker.com/r/makeplane/plane-frontend/tags diff --git a/roles/services/templates/docker-compose.yml.j2 b/roles/services/templates/docker-compose.yml.j2 index 42e075f..a1125ac 100644 --- a/roles/services/templates/docker-compose.yml.j2 +++ b/roles/services/templates/docker-compose.yml.j2 @@ -31,7 +31,6 @@ networks: internal: true volumes: - vaultwarden_data: forgejo_data: forgejo_db_data: plane_pgdata: @@ -80,31 +79,6 @@ services: timeout: 5s retries: 3 - # ── Vaultwarden ──────────────────────────────────────────────────────────── - vaultwarden: - image: {{ vaultwarden_image }} - container_name: vaultwarden - restart: unless-stopped - security_opt: - - no-new-privileges:true - networks: - - backend - volumes: - - vaultwarden_data:/data - environment: - - ADMIN_TOKEN=${VAULTWARDEN_ADMIN_TOKEN} - - DOMAIN=https://{{ domain_vault }} - - SIGNUPS_ALLOWED=false - - INVITATIONS_ALLOWED=true - - LOG_LEVEL=warn - - EXTENDED_LOGGING=true - - TZ=UTC - healthcheck: - test: ["CMD", "curl", "-sf", "http://localhost:80/"] - interval: 30s - timeout: 5s - retries: 3 - # ── Forgejo ──────────────────────────────────────────────────────────────── forgejo: image: {{ forgejo_image }} diff --git a/roles/services/templates/traefik/dynamic/routes.yml.j2 b/roles/services/templates/traefik/dynamic/routes.yml.j2 index 8580375..b751003 100644 --- a/roles/services/templates/traefik/dynamic/routes.yml.j2 +++ b/roles/services/templates/traefik/dynamic/routes.yml.j2 @@ -23,14 +23,6 @@ http: service: api@internal middlewares: [authelia@docker, rate-limit-strict] - vaultwarden: - rule: "Host(`{{ domain_vault }}`)" - entrypoints: [websecure] - tls: - certresolver: letsencrypt - service: vaultwarden - middlewares: [rate-limit-default] - forgejo: rule: "Host(`{{ domain_git }}`)" entrypoints: [websecure] @@ -114,20 +106,7 @@ http: service: n8n middlewares: [rate-limit-strict] - mail: - rule: "Host(`{{ domain_mail }}`)" - entrypoints: [websecure] - tls: - certresolver: letsencrypt - service: mail - middlewares: [rate-limit-default] - services: - vaultwarden: - loadBalancer: - servers: - - url: "http://vaultwarden:80" - forgejo: loadBalancer: servers: @@ -179,11 +158,6 @@ http: servers: - url: "http://{{ ip_tools }}:5678" - mail: - loadBalancer: - servers: - - url: "http://{{ ip_tools }}:8888" - middlewares: # ── Security Headers (applied globally via entrypoint) ───────────────── security-headers: diff --git a/roles/tools/defaults/main.yml b/roles/tools/defaults/main.yml index 39ca8dd..63b0bc9 100644 --- a/roles/tools/defaults/main.yml +++ b/roles/tools/defaults/main.yml @@ -4,5 +4,3 @@ outline_image: "outlinewiki/outline:0.80.2" outline_db_image: "postgres:15-alpine" outline_redis_image: "redis:7-alpine" n8n_image: "n8nio/n8n:1.89.2" # https://hub.docker.com/r/n8nio/n8n/tags -mailserver_image: "ghcr.io/docker-mailserver/docker-mailserver:14" # https://github.com/docker-mailserver/docker-mailserver/releases -snappymail_image: "djmaze/snappymail:latest" # https://hub.docker.com/r/djmaze/snappymail/tags diff --git a/roles/tools/tasks/main.yml b/roles/tools/tasks/main.yml index 71dd61f..ee9f3a9 100644 --- a/roles/tools/tasks/main.yml +++ b/roles/tools/tasks/main.yml @@ -7,111 +7,6 @@ group: "{{ deploy_group }}" mode: "0750" -- name: Create mailserver directories - ansible.builtin.file: - path: "{{ tools_root }}/mailserver/{{ item }}" - state: directory - owner: root - group: root - mode: "0755" - loop: - - mail-data - - mail-state - - mail-logs - - config - -- name: Create snappymail data directory - ansible.builtin.file: - path: "{{ tools_root }}/snappymail/data" - state: directory - owner: "82" # www-data uid in Alpine (SnappyMail container user) - group: "82" - mode: "0755" - -# ── TLS certificate for mail.csrx.ru (via certbot + Cloudflare DNS-01) ─────── -- name: Install certbot and Cloudflare DNS plugin - ansible.builtin.apt: - name: - - certbot - - python3-certbot-dns-cloudflare - state: present - update_cache: false - -- name: Deploy Cloudflare credentials for certbot - ansible.builtin.copy: - content: | - dns_cloudflare_api_token = {{ cloudflare_dns_api_token }} - dest: /etc/letsencrypt/cloudflare.ini - mode: "0600" - owner: root - group: root - -- name: Obtain TLS certificate for mx.{{ domain_base }} - ansible.builtin.command: > - certbot certonly - --dns-cloudflare - --dns-cloudflare-credentials /etc/letsencrypt/cloudflare.ini - --email {{ acme_email }} - --agree-tos --no-eff-email - -d mx.{{ domain_base }} - --non-interactive - register: certbot_result - changed_when: "'Certificate not yet due for renewal' not in certbot_result.stdout" - failed_when: - - certbot_result.rc != 0 - - "'Certificate not yet due for renewal' not in certbot_result.stdout" - - "'Certificate not yet due for renewal' not in certbot_result.stderr" - -- name: Deploy certbot renewal deploy-hook (reload mailserver after cert renewal) - ansible.builtin.copy: - dest: /etc/letsencrypt/renewal-hooks/deploy/reload-mailserver.sh - mode: "0750" - owner: root - group: root - content: | - #!/usr/bin/env bash - # Triggered by certbot after successful cert renewal - # Reloads Postfix + Dovecot TLS without full restart - set -euo pipefail - if docker ps --format '{{ '{{' }}.Names{{ '}}' }}' | grep -q '^mailserver$'; then - docker exec mailserver supervisorctl restart postfix dovecot - echo "[$(date)] mailserver TLS reloaded after cert renewal" - fi - -- name: Schedule certbot auto-renewal (twice daily, certbot renews only when needed) - ansible.builtin.cron: - name: "certbot renew" - minute: "15" - hour: "3,15" - job: "certbot renew --quiet --deploy-hooks 2>&1 | logger -t certbot" - user: root - state: present - -# ── Open mail ports in UFW ──────────────────────────────────────────────────── -- name: Allow SMTP inbound (port 25) - community.general.ufw: - rule: allow - port: "25" - proto: tcp - -- name: Allow SMTP submission (port 587) - community.general.ufw: - rule: allow - port: "587" - proto: tcp - -- name: Allow IMAPS (port 993) - community.general.ufw: - rule: allow - port: "993" - proto: tcp - -- name: Allow SMTPS (port 465) - community.general.ufw: - rule: allow - port: "465" - proto: tcp - # ── Deploy configs and start stack ──────────────────────────────────────────── - name: Deploy docker-compose.yml ansible.builtin.template: @@ -138,203 +33,9 @@ - "{{ outline_db_image }}" - "{{ outline_redis_image }}" - "{{ n8n_image }}" - - "{{ mailserver_image }}" - - "{{ snappymail_image }}" - name: Start tools stack community.docker.docker_compose_v2: project_src: "{{ tools_root }}" state: present pull: missing - -# ── SnappyMail admin password — write bcrypt hash directly to application.ini ── -# djmaze/snappymail does not reliably apply SNAPPYMAIL_ADMIN_PASSWORD env var; -# instead we verify and update the hash in the config file on every deploy. -- name: Ensure SnappyMail admin password is set correctly - ansible.builtin.shell: | - python3 << 'PYEOF' - import subprocess, re, sys - - config_path = "{{ tools_root }}/snappymail/data/_data_/_default_/configs/application.ini" - password = "{{ snappymail_admin_password }}" - - try: - with open(config_path) as f: - content = f.read() - except FileNotFoundError: - print("CONFIG_NOT_FOUND") - sys.exit(1) - - # Extract current hash (bcrypt hashes start with $2y$) - m = re.search(r'^admin_password\s*=\s*"?(\$2y\$[^"\n]+)', content, re.M) - current_hash = m.group(1).strip() if m else "" - - if current_hash: - r = subprocess.run( - ["docker", "exec", "snappymail", "php", "-r", - f"echo password_verify('{password}', '{current_hash}') ? 'yes' : 'no';"], - capture_output=True, text=True - ) - if r.stdout.strip() == "yes": - print("ALREADY_SET") - sys.exit(0) - - # Generate a new bcrypt hash using PHP inside the container - r = subprocess.run( - ["docker", "exec", "snappymail", "php", "-r", - f"echo password_hash('{password}', PASSWORD_BCRYPT);"], - capture_output=True, text=True - ) - new_hash = r.stdout.strip() - if not new_hash.startswith("$2y$"): - print(f"HASH_ERROR: {r.stderr}") - sys.exit(1) - - new_content = re.sub( - r'^admin_password\s*=.*$', - f'admin_password = "{new_hash}"', - content, flags=re.M - ) - with open(config_path, "w") as f: - f.write(new_content) - print("UPDATED") - PYEOF - register: snappymail_pw_result - changed_when: "'UPDATED' in snappymail_pw_result.stdout" - failed_when: > - snappymail_pw_result.rc != 0 or - 'CONFIG_NOT_FOUND' in snappymail_pw_result.stdout or - 'HASH_ERROR' in snappymail_pw_result.stdout - -- name: Restart SnappyMail after password update - ansible.builtin.command: docker restart snappymail - when: snappymail_pw_result.changed - changed_when: true - -# ── SnappyMail domain config for csrx.ru ───────────────────────────────────── -# Points IMAP/SMTP to the mailserver container (shared `front` Docker network). -# type: 0=plain, 1=SSL, 2=STARTTLS -- name: Deploy SnappyMail domain config for {{ domain_base }} - ansible.builtin.copy: - content: | - { - "IMAP": { - "host": "mailserver", - "port": 993, - "type": 1, - "timeout": 300, - "shortLogin": false, - "lowerLogin": true, - "sasl": ["PLAIN", "LOGIN"], - "ssl": { - "verify_peer": false, - "verify_peer_name": false, - "allow_self_signed": true, - "SNI_enabled": true, - "disable_compression": true, - "security_level": 0 - }, - "disabled_capabilities": [], - "use_expunge_all_on_delete": false, - "fast_simple_search": true, - "force_select": false, - "message_all_headers": false, - "message_list_limit": 10000, - "search_filter": "" - }, - "SMTP": { - "host": "mailserver", - "port": 587, - "type": 2, - "timeout": 60, - "shortLogin": false, - "lowerLogin": true, - "sasl": ["PLAIN", "LOGIN"], - "ssl": { - "verify_peer": false, - "verify_peer_name": false, - "allow_self_signed": true, - "SNI_enabled": true, - "disable_compression": true, - "security_level": 0 - }, - "useAuth": true, - "setSender": true, - "usePhpMail": false - }, - "Sieve": { - "enabled": false, - "host": "mailserver", - "port": 4190, - "type": 0, - "timeout": 10, - "shortLogin": false, - "lowerLogin": true, - "sasl": ["PLAIN"], - "ssl": { - "verify_peer": false, - "verify_peer_name": false, - "allow_self_signed": true - } - }, - "whiteList": "" - } - dest: "{{ tools_root }}/snappymail/data/_data_/_default_/domains/{{ domain_base }}.json" - owner: "82" - group: "82" - mode: "0640" - register: snappymail_domain_result - -- name: Restart SnappyMail after domain config update - ansible.builtin.command: docker restart snappymail - when: snappymail_domain_result.changed - changed_when: true - -# ── Mail accounts (idempotent: check host-side config file) ────────────────── -- name: Wait for mailserver to be ready - ansible.builtin.command: docker exec mailserver postfix status - register: postfix_status - changed_when: false - retries: 18 - delay: 10 - until: postfix_status.rc == 0 - -- name: Create mail accounts - ansible.builtin.command: > - docker exec mailserver setup email add {{ item.address }} {{ item.password }} - loop: - - { address: "noreply@{{ domain_base }}", password: "{{ mailserver_noreply_password }}" } - - { address: "admin@{{ domain_base }}", password: "{{ mailserver_admin_password }}" } - - { address: "jack@{{ domain_base }}", password: "{{ mailserver_jack_password }}" } - register: mail_account_result - changed_when: mail_account_result.rc == 0 - failed_when: > - mail_account_result.rc != 0 and - 'already exists' not in mail_account_result.stderr - -# ── DKIM ───────────────────────────────────────────────────────────────────── -- name: Check if DKIM key exists - ansible.builtin.stat: - path: "{{ tools_root }}/mailserver/config/opendkim/keys/{{ domain_base }}/mail.private" - register: dkim_key - -- name: Generate DKIM key - ansible.builtin.command: > - docker exec mailserver setup config dkim domain {{ domain_base }} - when: not dkim_key.stat.exists - register: dkim_generated - -- name: Read DKIM DNS record - ansible.builtin.command: > - cat {{ tools_root }}/mailserver/config/opendkim/keys/{{ domain_base }}/mail.txt - when: dkim_generated is changed - register: dkim_record - -- name: Print DKIM DNS instructions - ansible.builtin.debug: - msg: | - ══════════════════════════════════════════════════════════════════ - DKIM key generated! Add this TXT record to Cloudflare DNS: - {{ dkim_record.stdout }} - ══════════════════════════════════════════════════════════════════ - when: dkim_generated is changed diff --git a/roles/tools/templates/docker-compose.yml.j2 b/roles/tools/templates/docker-compose.yml.j2 index 0be2560..6191951 100644 --- a/roles/tools/templates/docker-compose.yml.j2 +++ b/roles/tools/templates/docker-compose.yml.j2 @@ -12,9 +12,6 @@ networks: n8n-internal: driver: bridge internal: true - mail-internal: - driver: bridge - internal: true volumes: outline_db_data: @@ -31,7 +28,6 @@ services: env_file: .env networks: - outline-internal - - mail-internal # send mail via mailserver - front # needed for host port binding ports: # Exposed only to main Traefik (access controlled by UFW) @@ -130,68 +126,3 @@ services: options: max-size: "10m" max-file: "3" - - # ── Mail server (Postfix + Dovecot — send & receive for @csrx.ru) ─────────── - mailserver: - image: {{ mailserver_image }} - container_name: mailserver - hostname: mx - domainname: {{ domain_base }} - restart: unless-stopped - networks: - - mail-internal # Outline → mailserver (internal, port 587 with auth) - - front # inbound/outbound internet SMTP - ports: - - "{{ ip_tools }}:25:25" # SMTP inbound (MX delivery from internet) - - "{{ ip_tools }}:587:587" # SMTP submission (mail clients, STARTTLS) - - "{{ ip_tools }}:993:993" # IMAPS (mail clients, TLS) - - "{{ ip_tools }}:465:465" # SMTPS (mail clients, implicit TLS) - environment: - - ENABLE_RSPAMD=1 # spam filter + DKIM signing - - ENABLE_CLAMAV=0 # no antivirus (saves RAM) - - ENABLE_FAIL2BAN=0 # host fail2ban already handles this - - POSTFIX_INET_PROTOCOLS=ipv4 - - SSL_TYPE=letsencrypt # TLS certs from /etc/letsencrypt/live/mx.csrx.ru/ - - LOG_LEVEL=warn - - OVERRIDE_HOSTNAME=mx.{{ domain_base }} - - POSTMASTER_ADDRESS=admin@{{ domain_base }} - - POSTFIX_MESSAGE_SIZE_LIMIT=26214400 # 25 MB max message size - volumes: - - {{ tools_root }}/mailserver/mail-data:/var/mail - - {{ tools_root }}/mailserver/mail-state:/var/mail-state - - {{ tools_root }}/mailserver/mail-logs:/var/log/mail - - {{ tools_root }}/mailserver/config:/tmp/docker-mailserver - - /etc/letsencrypt:/etc/letsencrypt:ro # TLS certs from certbot - stop_grace_period: 1m - cap_add: - - NET_ADMIN - logging: - driver: json-file - options: - max-size: "10m" - max-file: "3" - - # ── SnappyMail webmail ─────────────────────────────────────────────────────── - snappymail: - image: {{ snappymail_image }} - container_name: snappymail - restart: unless-stopped - networks: - - mail-internal # reach mailserver for IMAP/SMTP - - front # expose port 8888 to host - ports: - - "{{ ip_tools }}:8888:8888" - volumes: - - {{ tools_root }}/snappymail/data:/var/lib/snappymail - environment: - - SNAPPYMAIL_ADMIN_PASSWORD={{ snappymail_admin_password }} - healthcheck: - test: ["CMD", "wget", "-qO-", "http://127.0.0.1:8888"] - interval: 30s - timeout: 5s - retries: 3 - logging: - driver: json-file - options: - max-size: "10m" - max-file: "3" diff --git a/roles/tools/templates/env.j2 b/roles/tools/templates/env.j2 index 75e323a..f6e7f3f 100644 --- a/roles/tools/templates/env.j2 +++ b/roles/tools/templates/env.j2 @@ -27,14 +27,6 @@ FILE_STORAGE=s3 # Auth — local accounts (can add OIDC/Authelia later) AUTH_PROVIDERS=email -# SMTP — local docker-mailserver container (same Docker network, port 587 with auth) -SMTP_HOST=mailserver -SMTP_PORT=587 -SMTP_USERNAME=noreply@{{ domain_base }} -SMTP_PASSWORD={{ mailserver_noreply_password }} -SMTP_FROM_EMAIL=noreply@{{ domain_base }} -SMTP_SECURE=false - # Outline DB password (used in docker-compose) OUTLINE_DB_PASSWORD={{ outline_db_password }}