feat: full mail server — send + receive for @csrx.ru
Some checks failed
CI/CD / syntax-check (push) Successful in 1m25s
CI/CD / deploy (push) Has been cancelled

Upgrade docker-mailserver from SMTP_ONLY to full Postfix + Dovecot:
- Remove SMTP_ONLY, enable Dovecot (IMAP) and Rspamd (spam filter)
- Expose ports 25 (SMTP), 587 (submission), 993 (IMAPS), 465 (SMTPS)
- SSL_TYPE=letsencrypt — certbot obtains cert for mail.csrx.ru via
  Cloudflare DNS-01 challenge (uses existing cloudflare_dns_api_token)
- UFW: open ports 25, 587, 993, 465
- Accounts: admin@csrx.ru, jack@csrx.ru, noreply@csrx.ru

Mail client settings after deploy:
  IMAP: mail.csrx.ru:993 (SSL)
  SMTP: mail.csrx.ru:587 (STARTTLS)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
jack 2026-03-22 17:16:09 +07:00
parent cb798b67e9
commit 26c0df851e
4 changed files with 201 additions and 136 deletions

View file

@ -48,6 +48,8 @@ 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 }}"
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"
ip_tools: "85.193.83.9"

View file

@ -1,110 +1,116 @@
$ANSIBLE_VAULT;1.1;AES256
64646131626662663937643431386162343036353133623631303531353735653862353835303438
6637363563353433326235383231633363326533653634660a316436323933373162316463343063
32656364646331363232376632353737303330356633336261383561333165623664666130636335
3734366139333039360a353034636336313362383734623264616439303734323763343535616661
34336136663561336334666430376566353865363837613834326336383563373131666562663238
37636638613966373162633565366361613965636232393266373039313931323361303130356463
35653132336232393934626631366137346366373635636633346530633134336264663761316564
31626230363562343035323334373364316333306162353565356461373938653235303562383134
35623561313165366364613036643264303738613038316163636164663833333635356561646438
37336265363835326363313964333033636537366232313336633932386261373031663338343334
35336664313836336434666237663539386337656461396264613562313536653463393231306463
33633937356564643235383562346461373563656435626139643530326236313030396638323365
32393531363064343030333461343739633363616665376262663831303630653034633563313834
31373938313962386634383937393031633263383536613964373562393438643332656635306632
37613634666533646462626661646463326164323361306337316463653536323938373866633834
32336338333465306131396261383933313538323163636332323962316162306234323263336239
35663965626662633134363066323831623630326365376432616464326532636435313965613561
37343031373764326639646365663663633765393230323466343539303034323939336330383836
30343135313466396136326461333131336232656530656264383563363166323364636338653165
65663963373365646661616661393766653964663135643431646535333335353162623866326663
37356534316437653130316531323065636335613931366563626639393439623734653865326636
66356565303637613639626634313464333033373231656432343765363332666232656162386336
66666563356465363263373936366238643535633364663263383337396565313337353537623061
63346536663765623535363037323536633730306135636566663866663763613137653033666435
32653661333865613632323339633631643637663438333065336666666562663535633135316639
31333563663130396164386535373335613364643966623164353639393536343866666437613137
65636564343164306466333333333930316337623161363163656365653465313135393233363562
39396437363239643233653938343030386539666332666130303764373138633236326534636638
62336630666331646365643039386665343964346336343837336639653864653331323438323436
38376361613161376133323066633930333165343939376135376531363939303139326161333030
39666366613665346330303863666634393366396439656133653038623466383838343833626365
35346666316265393561303563313333636337323334316534303637303133303639613838346633
31336437633733646237646432653462323462363165393665363564636566666161333436363163
33323439636138343064613530633132616264626338336132356234313863393934646133313933
36626133613862616130666464386361393265333064373538353435376231346134343066633939
61346561633933623336653462623764383938616466643138346164303863303734626162383431
37623036613032353431646332623735393265656533373536313065393665663433376235663361
66373934646133623264363433643437316261343739316164363065653263373539383462623439
64663733336535613736653731623137613137653962363262303836306162616339626430353662
65653739336136623934333837663966663838393164353933333039306339323131613535636333
39383836373061353164323337643135383164383434333465643331346230326633643965366531
34316635636464616162666234616164393636616165326535336435373633323036313131653964
62666363313836633765393862396235633434353835373961393164333538313965623266333839
64303363373862613266383262643966613738376434343263346538623937346235616534373534
64316235386231373736663934636335343236316563626161323437323831653236383139313936
35666664616632643030643536323336316535646130633762333166353632613139363964386462
62323764333930653534613834313964323832336161333538626531383631363238316634336131
39616130626338343861613337383564636265383032313833366264333231393633666432373234
66646137303336643937313938326236643765636330613064336632343631623631613831336332
38333364396338613065643939346161336431313261353761616461393161643534623364393235
36306337396363336231343230323536623933346634363037333135353530643665663034633134
35393839343865373561623561353739396261343833373137616663386164326465323234393938
63306535623331366462383961373639366631353738313534656230376365323436353165346663
33663939353463373866366230366463633733623266613135343765393431663436666466363831
38323561663565363862633037303237613231333435646564383730623462653333326633316334
65643661363036613837303366323261633563313338323663303064623937653837623861376636
61323437656139363461386535396436386564316662396361396133663834353064376634326565
37393433656532653638303433376332643536666635383334666161393664616265613430313337
31373936646430383132613535343562643638363666323162386638643263613436643562333631
64363034323232303161396536396136373163663035393238346462616539366637356233386363
63343062373062666363393663396633643331626561643133303866313864353831323162623962
61356237363235653038663132623263333565333065363361363362303431623462343361326638
34316139313635366435393365643136303264626264643961643938633238346161616339653535
37656238623762363564626365316361303730303936626637323066343537656231326136666134
38326538363230393533383439666165353037636162626538326536616635393831313031613133
38333137393361383931336437363434303232633733623832343534343039643038373932656361
35316464663936306632326632323462376435633234386261383338663034336132336338353836
64646433383337333662623861303632653636333332396466613439643565326331376535333466
30333661366661303531623437393265353736646464653535336162383737333832383966306366
38363136663732383335303966636161653833313338393137373463303465326239666161303939
62363761646539383462313464353332356634333762383836366334386236363062323131316436
65313465373038653237633963316138353339366666343133316366396236343831313035653665
62383135383763346336393534396236303832336361616264373838666331363636323434363961
63363539313164393035333361373765626532383735643535316364646134656632353165646138
33386638366661396538366465666236656166613031653338396232336239393631353435373564
35333637313166353035626465653235396133303762623630333963313063613963363236663964
62623437326535643138346536333637643839343964306364313532383932373834653632383839
65303736353362303839623830653939316139376232373531313165346266306130633566393732
32636662343931383235646165626632636330663634643238653562373864346537333639656235
34653864393338383964303861393866333333666164376137386664346638356566386430353663
61363139353861633665636565393966663535333731396264636531303233663865366538656362
66333234613465663332663765386461376239396161383230363763616236303236636565616664
36306539323433303132333733653039666666636463646535613737653934383161656436636664
65346634633430666165316631636334333537393565623131636338623333313033336665346132
39313065653932643132333536353737326663616133326130623834396530663635616339363162
64316163376266653463333637373939376463656232666461306163376230353336633732653965
64386139356336353265343233643761313261666463363738353631366233636463303931396162
34336439346139363832323333646365356630613930366431653766653633343634363066363639
61656535333662363865633130663431386633363336663730363934306435383838343933633231
30303138303237666236336461353164396336346236626666383065386365636236306435343233
63396436656364396662326364633636616366363531336363306333333831383339346232323261
33616164356464636165376235353835613966333730663833636530646430383134333437333333
39336664643937303462356330323066343633306164373064666634303862326261666238653938
65346266303665636464663564653064663839663261666665336635613934333933326666306262
34333861626135386239656333323037663463326531663966313266616637636435383862396237
37316231666662343765326638313335353032636236633263646539646636636432343661326463
62393336646638646531313837393935356465613136373330363131663130323632626261646636
35656166306437643631663261656166313232373339666265393033656631366532346163333038
38306431323264666461646661646537363235386434326362346239356466663137363135383537
61613363646235376633303232316564373731666537373032633937393832393935313039656464
66623236666564313665343461343337326233343131623331636635363131396339383266633461
63323063363138353266643433616439333433353032313861363932336336656536343839626536
32316434376536316232666430653561313661656264383665386663356562303132393666333064
36306236363137383762626431373937313933653735326131383038636263396431393037373235
33376234366265343966356462306134663734363837323464313137653863643436393437653065
61643935633862303330653666376261326537386564396330636561656230633630393466666364
31303134636262616461393465616635663061306330316139623462306266346464303034323066
37333830323335303065626436343837636462616263663434303563333430386663393465303862
3261
33303531373535383332343237343432613333353066346231653031623364353166376631613330
3938633033303861613365656532663262363861333030300a643763383932393436646236663538
62623961383132326235623261626366313966636264373465656362633634326139616435346536
6532613233366333620a626637316236316532643336316435643465353133303833303132636162
33396238626539333163343033666438633735316338373665343163363766393630613235383666
31373563366136633334353161396466386630656236646234326331326634313837353564333064
65653934636431626335353534383731353432613266356437323834643239366238643739643064
33376661613362316537376438303464393033303365356564363537323130343432666231353562
38363133623335323162336139663536373834333535323730333433373562343238663237316663
35393661656536356336313436393536663865336136623564383437636638386639343731623565
62393763323635633263373863633238313838396365363639623835323538376362306435663561
65613639393761303663646433353461646366356236303036333062613566363263363436336364
30363538393335613037346466313030393835623638363064313033393763393734333430643564
61613166633465333162396532633164656330613934333162663263396338636130336132643562
33353235363861343062666264316435663563303263653463336465333939626233613633353961
36646434366330343666656539376632626233363936623965613538323137613739383266663030
37623763356432616638336634383262653234313834633636363434373137393030363637303337
65616463666365663466383930626632343936323333633630363336303236653836666136363332
64336336326237643839363964626635623035616633313961623237646233333339653734346264
39396635396561343039326434616562636464383633656138356638383733373736336363306364
33343162613931313763386538363638326563643961316637623036373938303765353466613237
38346635383862386631343637333066633634326134633539633961313362343534316363616464
63633633313633366630383638356130323166373631656330626336376562333233643538353935
36303833326166323239653936626538336363323530666438633763313030356562343338323561
64396238343865663463626538346561643866643363613862383136636534616364313434323964
36303532306131376335323736396130343630643766666665636161393731333261303861346637
66316337663731356632313137643633643365363038386463373831636134343831326432333861
30666639666435343139306262636235323333393966626537313933376234623763333831643339
62633934353766396537313766633834353366353661343539383437393365353564326665306637
64356335303732313663613331393034366632306431383432643962346361323831333430313232
38653036333232366532616236336237653061376433333137656537333531656566373830396230
35326539396138393337653861393366373439616137356338313661313061323532633066376235
32646333353864333837343837386235653162326563636537373766386136333136633738363831
33613265633031633866623062633833336364333731636437643530643238396235643633323235
35303939353165356464333435383537343833333230303862353036343634643132363763326433
61643935373564386537306237636137386433326639336435343436353635383561393465666365
64663231396263626563636630613861383065303365343135386531363634666631323632376434
62393237373838643764346465616365653561386466353963643434653833663564323233326138
37386534366233656263646263333931316362343838616163366366386331666234643463663933
30643133393038383331376263343762643534346266363730356539373533383665393062653633
61366332353531383931616437626439383833313964333536366465376563663063356134643536
66376436356335646135613138343330323139303835383636643031343638323334333635323736
61633932623566366464323563353462323633363033356230313062313731326663616632663034
33383263343561656231313533633965643163396564656130646165613531663337643732363066
65666635316235326332316536316535303166643939353032373338353866383238353530636230
38616164636336626165366261326335313039353430336634616633333638666262656462303932
36316661333533616438383831323164373336333832356362633136323166336635643364353332
35303135643664373861666630363361386335366133613139626335323262643530373536336234
66376433396166633762393763346362616462643831663230616531623639396631313234366264
62663663626133646632343833613565613261313562303562386237346133346132613966323035
64666262366434656566376462306133656435323333326434353266343562356636356438303836
62373235393337616638343663316533633234646237383932346464373261343637313930353761
62653037393434313765633331396330336563663362636639313134663461383966363333393064
36656532613639333963353937343363316563353463346331656136386534336439393764376333
64393238313363386561363935353166386430643566393939623937626265316239346662396365
62343835393766343963383664373230626536343061363065326365343239366362393363383730
37316330393162396631633062313634353830353331396462613832343737353135623735343439
64323032623766353166323138623730616338336663646266366438653661373966383533313539
30613262323030313766393233306331616265663732363437663738653066346463336136653335
37343264316262383365376532323634636339343764366562363230303462303934316638343535
33336266313065623130383562613562363131633561616364346262646364353930653532343637
30633738333135623662666164633739383531313362336535353639626465336461626435316538
38323966383934346661643834363366613262393536323433623038396536323939356565626537
63323637643539656434333864313331356262636662653530336265626264396634363161376562
65616432613133666261613464343263646535373662653364383934613437633338376635376563
62313832646232626436626366356262326561633736356238383165353337633538646165373765
65353630386531356335643937383534383965396464353830313034623761666635336439623233
37363765333961353362633931663937666361613038326138333265343332626432626565306165
39613838666330333865663530623138376534386133313838383531326637363164373139636362
39623535346666326561656666316331633631616163366133623732313165303362613066616564
37653966396561353563383138346636373061316436326663353233353965323661363934616365
31653862353731346436393438396361376538383962646535616536653861623632333762666431
61356436613836333263656163396531356630636631313036363135626531656435336533633738
33626661396239396134633262363861303466613535326463666431623064643464356163376164
31656134353861303866646438373839383531366535346466383165343337303037366664636238
66333033646363333161386336373534623637303339376636653633666334646361336362666439
34643835636537343966346337366439326638356630636539326535313063373935313263623464
33633732643736373937393936373264313265353236373734386534613264623837613635343835
66616630653232623636396430383864623231646165653038633737366366376462333333646562
30623230613233346338306331303866663531643166356432333563343532626366353165303538
38336666356239643936373637663537643635326532353630353164396361666338303935613230
64303363323063313265646265663437393532646132623566656636363535343666656661386539
37373638653835656162333737336266383638363665356335313866613639316234393334316336
31633731343434303133303137646264356430386330326538353864663261353462326364376635
35613037623635323837613838356435356239616135316236373065633731313938366433393933
39323631393763386439623963386334393330366263363938386337356334353439633035646463
31386333636265336333303132656364613739366363303265386435653633623665653662356333
30373633643932313239313262363538656330643237633434303034323265636638326163646231
32373363383661333137623835343764383964396262323330333833313938633438356636653563
63316435386161643165643663303566383732323539633232383332373162396432383737306334
38323833313462643537366231353738346535313765343534366634373366306461636234356239
37383231613431323561393531386438656366613738353439366666373566663834633461386436
31366533333865306334383636666162353637663933343438383733333635623339656161336332
33633936666132313539363136656231653165376139626366373836383835303737646231396231
32666230373139656431656463306334343639306662376331323564366637353961383834316635
38313864323139383835373466393231666535343662633031373336656433323333396137616564
32306430633535303731393833396264353364343763623632623332353233656266356538393136
31633931353436343731313633313262313561646535373134353430623031666566616236643666
63646430356562373035656331613439613136396264333962313333326362396531626232386263
36313162626461363163663661343661623039383137616438383763633863383166633965316262
61396636346464633061353630393561616338633438356165336465336633343333636332373134
62613065646433396564393364613437316565393261613731366537623262376665636261373936
35343864383331623633346530613032396637323261353030303765633537313761353037653235
65363461356433373632623037646333383732306436613631623236396564363235626239333639
61623135613538356539386535653665636635303966653230353465386234663038333933626665
38646534363962353963616265653030363738373339633633316261653962626636663662346331
37303936623566633931343538623166323865656136616335336363326431336533373934613737
31626466376231366566373532393137326236626232646562626166353665316630346164363830
64313564376433326635393438643530306638396461646632313465323364396466346161336335
65323165613434633934373762353030323535316165353532643365633037636335656332386139
32343462363637323133363030363962303661623738363062646161383534666131353633616365
39353634666166396564386136343163613161363461396135623831636539393061343738653032
65626539353161626436366566636334363333346631626638303965633434323936303966393461
35303839663564313937303836636335326164643935333666633736366561326130326538316464
663865383166653334373338386332323765

View file

@ -20,6 +20,66 @@
- mail-logs
- config
# ── 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 mail.{{ 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 mail.{{ 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"
# ── 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:
src: docker-compose.yml.j2
@ -53,35 +113,32 @@
state: present
pull: missing
# ── 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: 12
retries: 18
delay: 10
until: postfix_status.rc == 0
# Check the host-side mounted config file directly (setup email list fails if file doesn't exist yet)
- name: Check if noreply mail account exists
- name: Check postfix-accounts.cf exists
ansible.builtin.stat:
path: "{{ tools_root }}/mailserver/config/postfix-accounts.cf"
register: postfix_accounts_file
- name: Check noreply account in postfix-accounts.cf
- name: Create mail accounts
ansible.builtin.command: >
grep -q "noreply@{{ domain_base }}" {{ tools_root }}/mailserver/config/postfix-accounts.cf
register: mail_account_check
changed_when: false
failed_when: false
when: postfix_accounts_file.stat.exists
- name: Create noreply mail account
ansible.builtin.command: >
docker exec mailserver setup email add noreply@{{ domain_base }} {{ mailserver_noreply_password }}
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 }}" }
when: >
not postfix_accounts_file.stat.exists or
(mail_account_check.rc is defined and mail_account_check.rc != 0)
item.address not in (lookup('file', tools_root + '/mailserver/config/postfix-accounts.cf', errors='ignore') | default(''))
# ── DKIM ─────────────────────────────────────────────────────────────────────
- name: Check if DKIM key exists
ansible.builtin.stat:
path: "{{ tools_root }}/mailserver/config/opendkim/keys/{{ domain_base }}/mail.private"
@ -93,7 +150,7 @@
when: not dkim_key.stat.exists
register: dkim_generated
- name: Show DKIM DNS record
- name: Read DKIM DNS record
ansible.builtin.command: >
cat {{ tools_root }}/mailserver/config/opendkim/keys/{{ domain_base }}/mail.txt
when: dkim_generated is changed
@ -105,11 +162,5 @@
══════════════════════════════════════════════════════════════════
DKIM key generated! Add this TXT record to Cloudflare DNS:
{{ dkim_record.stdout }}
Also add SPF record:
Type: TXT Name: @ Value: v=spf1 ip4:{{ ip_tools }} ~all
And DMARC record:
Type: TXT Name: _dmarc Value: v=DMARC1; p=none; rua=mailto:admin@{{ domain_base }}
══════════════════════════════════════════════════════════════════
when: dkim_generated is changed

View file

@ -131,7 +131,7 @@ services:
max-size: "10m"
max-file: "3"
# ── Mail server (outbound SMTP for Outline magic links) ──────────────────────
# ── Mail server (Postfix + Dovecot — send & receive for @csrx.ru) ───────────
mailserver:
image: {{ mailserver_image }}
container_name: mailserver
@ -139,14 +139,19 @@ services:
domainname: {{ domain_base }}
restart: unless-stopped
networks:
- mail-internal # Outline → mailserver (internal)
- front # mailserver → internet (outbound delivery)
- mail-internal # Outline → mailserver (internal, port 25, no 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)
- "{{ ip_tools }}:993:993" # IMAPS (mail clients)
- "{{ ip_tools }}:465:465" # SMTPS (mail clients, alternative)
environment:
- SMTP_ONLY=1 # no IMAP/POP3, sending only
- ENABLE_RSPAMD=0 # lightweight: skip spam filter
- ENABLE_CLAMAV=0 # no antivirus needed for outbound-only
- ENABLE_RSPAMD=1 # spam filter for inbound mail
- ENABLE_CLAMAV=0 # no antivirus (saves RAM)
- ENABLE_FAIL2BAN=0 # host fail2ban already handles this
- POSTFIX_INET_PROTOCOLS=ipv4
- SSL_TYPE=letsencrypt # TLS via certbot cert at /etc/letsencrypt
- LOG_LEVEL=warn
- OVERRIDE_HOSTNAME=mail.{{ domain_base }}
- POSTMASTER_ADDRESS=admin@{{ domain_base }}
@ -155,6 +160,7 @@ services:
- {{ 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