Add Coturn TURN/STUN server and improve infrastructure

Major changes:
- Add Coturn role for Nextcloud Talk WebRTC support
  - Automatic SSL/TLS via Let's Encrypt
  - DPI-resistant configuration with static auth
  - Firewall rules for TURN (3478) and relay ports (49152-49252)
- Optimize nginx WebSocket support with conditional Connection header
- Change Forgejo domain from git.okhsunrog.dev to fgj.okhsunrog.dev
- Fix certbot role to properly handle new domains on existing infrastructure
- Update all Ansible variables to use ansible_facts syntax (Ansible 2.24 compatibility)
- Add network role support for ports with both TCP and UDP
- Remove unused snake/serpentina service configuration
- Add comprehensive documentation in docs/ directory

Bug fixes:
- Certbot now checks each domain individually instead of skipping all if any exist
- Create ssl-cert group before adding turnserver user to it
This commit is contained in:
okhsunrog 2025-12-12 16:17:58 +03:00
parent 689fec46e2
commit e38a231159
20 changed files with 670 additions and 186 deletions

45
docs/README.md Normal file
View file

@ -0,0 +1,45 @@
# Cloud Forge Documentation
Documentation for the cloud-forge infrastructure automation project.
## Available Guides
- **[Coturn Setup Guide](coturn-setup.md)** - Complete guide for deploying and configuring Coturn (TURN/STUN) server for Nextcloud Talk
## Quick Links
### Project Structure
- Main playbook: `site.yml`
- Inventory: `inventory.yml`
- Variables: `group_vars/all/vars.yml`
- Encrypted secrets: `group_vars/all/vault.yml`
- Ansible configuration: `ansible.cfg`
### Common Commands
Deploy full stack:
```bash
ansible-playbook site.yml
```
Update VPN users only:
```bash
ansible-playbook update_vpn_users.yml
```
Edit vault secrets:
```bash
ansible-vault edit group_vars/all/vault.yml
```
## Infrastructure Components
- **VPN Services**: OpenConnect (ocserv), WireGuard, AmneziaWG
- **Web Proxies**: HAProxy, Nginx with SSL termination
- **Communication**: Coturn (TURN/STUN for Nextcloud Talk)
- **Security**: Fail2ban, iptables firewall
- **Certificates**: Let's Encrypt via certbot
## Adding New Documentation
When adding new components or features, create a new markdown file in this directory and link it in this README.

315
docs/coturn-setup.md Normal file
View file

@ -0,0 +1,315 @@
# Coturn (TURN/STUN) Server Setup Guide
This guide explains how to configure and deploy the Coturn server for Nextcloud Talk.
## Overview
Coturn provides TURN/STUN services required for Nextcloud Talk to work properly, especially for:
- Peer-to-peer connections behind NAT/firewalls
- WebRTC media streaming
- Voice and video calls
## Architecture
The Coturn setup integrates with your existing infrastructure:
- **Domain**: `turn.okhsunrog.dev`
- **Listening Port**: `3478` (TCP/UDP) - STUN/TURN
- **Relay Ports**: `49152-49252` (UDP) - 100 concurrent sessions
- **SSL/TLS**: Let's Encrypt certificates (auto-managed)
- **Authentication**: Static auth secret (secure method for Nextcloud)
## Prerequisites
Before deploying, ensure:
1. ✅ DNS A record pointing `turn.okhsunrog.dev` to your VPS IP
2. ✅ Ports `3478` (TCP/UDP) and `49152-49252` (UDP) allowed through any upstream firewalls
3. ✅ Ansible vault password configured at `~/.vault_pass`
## Configuration Steps
### 1. Add Static Auth Secret to Vault
The Coturn server uses a static auth secret for authentication. You need to add this to your encrypted vault.
#### Generate a secure random secret:
```bash
openssl rand -hex 32
```
#### Edit the vault:
```bash
ansible-vault edit group_vars/all/vault.yml
```
#### Add the variable:
```yaml
coturn_static_secret: "your-generated-secret-here"
```
Save and exit (`:wq` in vim).
### 2. Verify DNS Configuration
Ensure your DNS is properly configured:
```bash
dig turn.okhsunrog.dev +short
```
This should return your VPS public IP address.
### 3. Deploy Coturn
Run the full Ansible playbook to deploy Coturn:
```bash
ansible-playbook site.yml
```
Or deploy only the coturn role (if already configured):
```bash
ansible-playbook site.yml --tags coturn
```
The playbook will:
- Install coturn package
- Generate configuration from template
- Request Let's Encrypt certificate for `turn.okhsunrog.dev`
- Configure firewall rules (iptables)
- Start and enable the coturn service
### 4. Verify Coturn is Running
SSH into your VPS and check the service status:
```bash
systemctl status coturn
```
Check the logs:
```bash
tail -f /var/log/turnserver/turnserver.log
```
### 5. Test TURN/STUN Server
You can test your TURN server using online tools:
- https://webrtc.github.io/samples/src/content/peerconnection/trickle-ice/
- Add STUN server: `stun:turn.okhsunrog.dev:3478`
- Add TURN server: `turn:turn.okhsunrog.dev:3478`
- Use your static secret for authentication
## Nextcloud Talk Configuration
### Access Nextcloud Admin Settings
1. Log into Nextcloud as admin
2. Go to **Settings****Administration** → **Talk**
### Configure TURN/STUN Servers
Add the following configuration:
#### STUN Server:
```
turn.okhsunrog.dev:3478
```
#### TURN Server:
```
turn.okhsunrog.dev:3478
```
#### TURN Secret:
```
your-coturn-static-secret
```
(Use the same secret from your vault)
#### Protocols:
- ✅ UDP
- ✅ TCP
### Save Configuration
Click **Save** and test with a call in Nextcloud Talk.
## Configuration Files Reference
### Main Configuration Variables
File: `group_vars/all/vars.yml`
```yaml
ports:
external:
coturn:
port: 3478
type: both # TCP and UDP
coturn_relay_min:
port: 49152
type: udp
coturn_relay_max:
port: 49252
type: udp
domains:
coturn: "turn.okhsunrog.dev"
```
### Coturn Configuration Template
File: `roles/coturn/templates/turnserver.conf.j2`
Key settings:
- **Listening port**: 3478
- **Realm**: turn.okhsunrog.dev
- **SSL/TLS**: Let's Encrypt certificates
- **Auth method**: Static auth secret
- **Relay ports**: 49152-49252
- **User quota**: 100 concurrent sessions
- **Total quota**: 1000 concurrent sessions
## Troubleshooting
### Check if Coturn is listening
```bash
ss -tulnp | grep 3478
```
Expected output:
```
udp UNCONN 0 0 0.0.0.0:3478 0.0.0.0:* users:(("turnserver",pid=...))
tcp LISTEN 0 5 0.0.0.0:3478 0.0.0.0:* users:(("turnserver",pid=...))
```
### Check firewall rules
```bash
iptables -L INPUT -n -v | grep 3478
```
### Certificate issues
If coturn can't access certificates:
```bash
# Check turnserver user is in ssl-cert group
groups turnserver
# Check certificate permissions
ls -la /etc/letsencrypt/live/turn.okhsunrog.dev/
```
### Logs
View detailed logs:
```bash
tail -f /var/log/turnserver/turnserver.log
```
Enable verbose logging (already enabled by default in the configuration).
### Common Issues
**Issue**: Coturn fails to start with certificate errors
**Solution**: Ensure the certificate for `turn.okhsunrog.dev` exists:
```bash
certbot certificates | grep turn.okhsunrog.dev
```
If missing, manually request:
```bash
certbot certonly --standalone -d turn.okhsunrog.dev
```
---
**Issue**: Calls still fail in Nextcloud Talk
**Solution**:
1. Verify TURN server in Nextcloud admin settings
2. Check browser console for WebRTC errors
3. Test with the online ICE tester mentioned above
4. Ensure both UDP and TCP are enabled in Nextcloud
---
**Issue**: Port 3478 already in use
**Solution**: Check what's using the port:
```bash
lsof -i :3478
```
## Security Considerations
1. **Static Secret**: Keep your `coturn_static_secret` secure in the vault
2. **Firewall**: Only required ports are exposed (managed by iptables)
3. **SSL/TLS**: All connections use Let's Encrypt certificates
4. **Quotas**: User and total quotas prevent abuse
5. **No CLI**: CLI interface disabled for security
## Maintenance
### Certificate Renewal
Certificates are automatically renewed via certbot. After renewal, coturn needs restart:
```bash
systemctl restart coturn
```
This is handled automatically by the `certbot_renewal_config` role.
### Updating Configuration
1. Edit variables in `group_vars/all/vars.yml` or vault
2. Modify template in `roles/coturn/templates/turnserver.conf.j2`
3. Redeploy:
```bash
ansible-playbook site.yml --tags coturn
```
### Monitoring
Monitor coturn resource usage:
```bash
# Check process
ps aux | grep turnserver
# Check memory usage
systemctl status coturn
# Check active connections
ss -tulnp | grep turnserver
```
## Additional Resources
- [Coturn Documentation](https://github.com/coturn/coturn/wiki)
- [Nextcloud Talk Documentation](https://nextcloud-talk.readthedocs.io/)
- [WebRTC Troubleshooting](https://webrtc.github.io/samples/)
## Summary
Your Coturn server is now integrated into your infrastructure with:
- ✅ Automated deployment via Ansible
- ✅ Automatic SSL/TLS certificate management
- ✅ Firewall rules configured
- ✅ Ready for Nextcloud Talk integration
- ✅ Secure static auth secret authentication
- ✅ Production-ready configuration with quotas and security settings

View file

@ -26,6 +26,15 @@ ports:
ocserv_personal:
port: 443
type: udp
coturn:
port: 3478
type: both # UDP and TCP
coturn_relay_min:
port: 49152
type: udp
coturn_relay_max:
port: 49252
type: udp
internal: # localhost, tcp ports
ocserv_personal: 447
@ -39,14 +48,13 @@ reverse_proxy: # used by nginx
nextcloud: 2345
photoprism: 2342
forgejo: 3000
snake: 8081
snake_webrtc: 8082
email: "me@okhsunrog.dev" # for certbot
domains:
ocserv_personal: "open.okhsunrog.dev"
ocserv_friends: "kafe.okhsunrog.dev"
coturn: "turn.okhsunrog.dev"
nginx:
blog:
- "okhsunrog.ru"
@ -61,10 +69,7 @@ domains:
- "photoprism.okhsunrog.ru"
- "photoprism.okhsunrog.dev"
git:
- "git.okhsunrog.dev"
# snake:
# - "serpentina.okhsunrog.ru"
# - "snake.okhsunrog.dev"
- "fgj.okhsunrog.dev"
ocserv_instances:
- instance_id: "personal"

View file

@ -1,82 +1,86 @@
$ANSIBLE_VAULT;1.1;AES256
37636630316634613663386563393166316166373331656362383630336165326534393031366531
6238623530356634346461336435343934356264646661310a336661313962383165313633383137
63353237333966376237343633363831376362383061343432353637663361646137613162353830
6464356134323831390a363438653430333361623165623734326236306165343764653739663435
37613133663732353237666366306365643064623031616236386239386230386163313432326465
36313535636639343238613264343831366235363262316138333662333562313531323536393263
34663066323262656132623861346266363038626339323834313338666435373866613166346361
36336665383165373236316437663036373663323162323064316531346462333732323638666663
38306337306130393431353662653561353731353265393032623135343563326562626462346133
31613531623965303732643162613732393561373666376534633935623266323835646666666137
37383931633133613634376538316234623437343134313434386433323633623666313332353565
34646437353138323165613035306230316331656331643761386437363637666630353964343166
30626235616261653833346365316630353430386566303536323937623534386164363539396532
33663831396531623430396230373865376461663063623731623131303866613436366265316537
63336633623364633165313239643531643461653466653237633564323131616639626339393734
64306135613436373333373534393039656636633864656461353565366361383331656464643035
39366433623236643535663339643437346332393262653938636135616139623932383235333732
62326265303937613537363033373930336663366162643635383464656636376166353764343337
30363931386665656364303232366236376364353930643165333263636235323634346636393461
31386564356133346531363637666461356639366462336430633838386538316630643336343139
37316134383135373739666534633162393563366262633664666331633638316162343463656539
61373236336139363836323865343133353862393261366330666564343133323065386637613534
63313131643839356237356135663930356334646230323966356565383864636164303763643865
34343239646365303439643932383765323131636430666130316530303137636661353038356132
65346232363934653362376366366634633362363863313362386364353861633261383861383963
62323235363137333066636431636330653134373034366566326433353462663232313861623935
61386262653765353736666364623862663533393934653334663137333464366561343138623035
62626363386366646139623530313837313032346164383065646537663661616437393661306264
32343032336661623561323166363132383663373436323434663163323264633737303865636462
66353531613834646461636161386135316637353961313738316233393037363165633063396566
37376633396264306330393165346337323937303265343163306564616266363932316437323130
64303231386233316264333730633738373130336538616166326463653430636539316464616364
33623938393436366164353230363030623137373632323437343266626263396438643437656633
36663831336361346334346133653437376439346536633338646533653830313430333034623661
30303263376133383836333264623733353336653338613263303164376264653030353966346162
30646566313165393663383733313732353330393565636266616366613963383339363337653533
64343036386566396434393264666663333565356133653133646661306331313934623038393430
64623133313232306537643134666465313234663631303564316537303331373566386434316238
63656461353339306331366665353965303437626132316332666137333234616162376565323164
37396562653334386235643139666261346130656538633633626166383662353563353766633932
34343161343463663037623935343961303831313464373936383431626239356331353866336538
61626330396537353863373263336464356639353030343931326532306338376339653935666363
63363430313435343431343564396532626537663031396135633365323864323166316361336535
32633564386166313332616434633539303764326530656631366361356639353236383336613662
61623633366433396631636136653433636531363833343234336533313366373763333638393131
30313338666536336434613635636663333566393266346262393236303965356138333039663831
35333031326264373535393631356633336135643264336633333739316332343236393430353063
64366436656230353961343862616632316136633035623830306136653864623166663936393435
32303063653463653665333139323165633061663630613630323437393839646466626563303631
37383439623664626332393137616339636137373330336335646338336335363663346361626134
37353332333736323733643736353930363366663163663733666336373935326333373734623362
30623938303764386638326533373939393364656161303661643030643165343130646431346132
38336566306663353137643465616230303839633430326634356161383361363932386266643738
36376639623964316135633638633361656439343165316631666536653439363036396535396130
32666131383633383334333332393266613634306134383264333665373933623535353630353266
34623663333135386463356638373766643963353962623436373836656662303232393939326239
34616133363665373333643531316334396634626336353035303730313166343634363437353830
64393064333930333935663462663530636638643833343930376431326162313036643564666133
35333965643564613230643839666639623030313265303930383664626130343830323061313338
61346335636266313166396336616465653836663537653762633331336565643765316262336332
30323761346538643561363634363666626435316239386535626466346239333736343332343338
30646564323230383731623532646632326165623034663665643837353138333430643365376336
62643830356331626331306635666464386162643366353332616338343662386663383233653632
35383831346133653864353839643731643639633561623033366130646166623231303366353031
32343566393632323266393464336339303434663066663036383034656433303135643363306331
62393433313730316636383531343632386464656163323863383765346237646165633438616135
36633434653039396333373731656563333062633638356565393164326334646362376463346636
30326633666634373361663837336535633162633462623132303666346133313237626164646631
34376538663864343336326662633766646331356466366662633563386265333465646138643633
35306535366266333635643338363635316366363730343461396438353666313763643065316431
64313537623135663032313763383132373430653833356630663866313965323963333661393163
65646232316636323062373332626430656562663338343864666138393431366365643234666435
36393733353864393735353165653739353833343362343333326663633039636466666333663433
37336432623136326663623663353938326337613331363433353431316664313030313932386235
62666331393435616536633261313366346438366536306431323734356333306564623939636539
37313066356162663439623039373935623130393563366338636562316237616139393639313136
39396161343063383432343035353035653136306339393863393262343862323065626562643930
62313862663733363732336236366634643935396265313438306662363437306161383138313734
30663131656231346635643433643362363961396337313038663434366630363364343830353935
37613033396137393963373866356633363166633464346665303737333836323962333964353139
3431
64386439353733366437613030616131643630626636326437336266633036393238383837316130
3863383230643334666130616432653738306264303539320a633461653933626262616139303636
65613661353566643733323633346331613738313263616166343739306534353765336430353237
3064653034373730660a643263376532636639313134393432333833373566333337386564653263
32633261643231353262383630346232353133643762363538313633666635663561356564653364
38663630666232326230333731313064626463623939363933336132616536356236613630323331
38383032333532333263643736653137646261316133393537333365623064363134393630326364
31386536396362666237643263313933336431396662393832323764313435316532633563303133
38343463626232663066333334613733633736353432393937373037643732343430653538326630
37623635353166643761626465346537323034346165626436323764663536643461636338373830
62346637343739313964313462646533616666616331623131656132333133343134656437636463
37633839393831386136373933313432316666613135393839346431386666363239336331646632
62313265343933316231373331306436656239303838666338653532316565303139623333353134
39366362326362383833386562323538353864636265623237663438343764353537336432633339
33356538373530646465343563376237326664363732653438613562323936653364663631333534
37306538623566633063376562333037643930316465313432646333626364386565663566613463
33343162646462346431643930383065306266643534616531343031656239376434613335383661
37363232373633396466386433333834333334323462313736323265363438646435626137633734
38353533323932663263623665313130623333613839303938306333343538313435636165373561
35313164383030633231653030333935393338393364633136353761303665376531656232613163
63373265303935306563613432613237343533623930323462306361613335386437303537613830
36383637343961383730363039633037346639613935616537303636373830383364326633313932
34353839646437663266326132316236613361626463363961616465333139333632356134323763
39376639316234333962643865333531356435346136663736623038346664363361373865663863
31316333323366633666373064373439613661653936356536396164346466393230333430626261
66346136353632613632656533663032366230396533616530366465613139656136626136313663
39336663623536393132356634336631326537386261613537343331356265623463383136336463
38363461613731383165313933653230313737633731613262356437633866393433373633333233
65306432653635646663643163653837373163376234316466646562386336666362633135396662
61376633663465656365396466303330616435666436626131626665343764346139383230643662
61376139373733323831666439373565386164623137653234643632623064646261303266633733
34333031396266636232666334616162646136613737326432313964303530393536336566333439
38663135306562376636373766373936333836363837623064303965376162656433653134356336
62613230393537306136373635383234356638303362633739323236613333376532613136363737
65313662653562393665333363353839656365346539633864313333386264393966613431343535
38396633346532306664323466613139343639663235316339333132343965393365653030633231
63366439613335363465613938383666626239323863613065643864633035616333623866343865
64393038366136643839663364373930616535663761333632376166613739353337376166636136
63633636396634366261303563643131373630636263336264306466373366653964376161383535
62303236316162616430613238353465643837366463323132636332653131656539623761343236
35633665643038396336346334326334326162363336366636326365333339343430306432633638
37653537636432343066393965343164633438623431386632376538636135666533623438346265
32626234343863336631336639376139633361313231373732646432303033313166343631653431
33383137343866653665303263616138376630636530636235336463633938653538383935363963
37303932353137636235666364623531393563363634313032373861366266396633326661323539
65666161363064363537326330636339336236353436393761393330376162646638313437396635
65656530626161326230353531393136356531346465663331356462316566323265313535666230
63343263623533373464343564373062613832313861613166626632613366613162323832663232
32666364333937343730303666643132363732626637633562633763343631343037633732323066
62646132643261353965643365343638393061643936373864363330623332353035326364623663
36666238663962646239343763366132643361646535646138653638353237346163656465386265
39366433386434623335393931336331636163653161643839613566303131666265616566653630
62643431636361613464373432666465623966333932396636363637383833343133353462363839
35623865646538643464336262386135336539386637623466326665633137333162633765626334
34346165373830656438313132366261383332626430356131316165396233306365343735663335
31623832316130313837666431623930636366646631663334626637626365316130316231653134
37346335326163306335366133346234386634376538386134363134623532663462663566616432
65353630333337353464343536633161333635626363613136333064313731306431636133346163
30643835656262353837356164616238616265363135353763616436373265653731306637663131
62666666343730333134393731313636363238353962633131393533326563343234643938663431
62363134333765316663633435353233616631626532363632336237653163323739646561313663
33646432323030636165613130303036353038306135323231613639313635313931336532333836
31633037313065643131336161376530656435643035613533363631323335626632386331336439
33623337663765356633623165333135636331656230633831383462656665653936656462313961
62386463376661343366653032316163626637316665366131643834356435623930616233383562
33623761333466343233353666623631376334653834616130366230646164653465326331323030
34366462383139323736313661633330306166373564333332653832363535386332383632383933
65303762303631646634663661383365636130336163376236663764633265633633353836336431
61653564396562656263623233306663616665626336333333376536613462633830313836336638
30303964653966613832373539383539363038326563343736353139656536633761383230376263
30666539633730386361366533396637333764306366353134323936623664653239393664313162
64313233613065643232393232363032303335386534363864313733336638373231383362653662
66336262356634666231643832623466626634616236363534663764333530326137653861363662
38633837353630623661373338646633653133363438303666363830363830353238623662353337
33393431366365616132643637613137643833623961363831366131616236383830393934303837
37316562653132613261383336363434626465613132356535336463393238643866336566383837
33666432636230313763663061343736353432343334623263353466353864356230616664396165
30356230643466333062336665643930393631346630656237626561333963653932623332316331
66343965333535376635653161316130313234666434383965623263346538386261393132356366
34316431633034356166376164363039333533613234353166653038623863313930663663663430
31386138353933643262613031336562306239313432666261306130613630346566636433633962
36626139363631636635653339383534623463303266643335356338333562323061343939646633
66393436323463636532343735363861633937366530613965623731653731323931333265663838
38353261643636343866346562366136623563616437333863616362646139616637633664666633
66316437343234313833623239343834313564636231643337343662663435346236

View file

@ -10,7 +10,7 @@
Components: main restricted universe multiverse
Signed-By: /usr/share/keyrings/ubuntu-archive-keyring.gpg
create: yes
when: ansible_distribution == 'Ubuntu' and ansible_distribution_version == '24.04'
when: ansible_facts['distribution'] == 'Ubuntu' and ansible_facts['distribution_version'] == '24.04'
- name: Install prerequisites for AmneziaWG
apt:
@ -18,7 +18,7 @@
- software-properties-common
- python3-launchpadlib
- gnupg2
- linux-headers-{{ ansible_kernel }}
- linux-headers-{{ ansible_facts['kernel'] }}
- build-essential
- dkms
state: present
@ -32,12 +32,12 @@
- name: Add AmneziaWG PPA repository
apt_repository:
repo: "deb https://ppa.launchpadcontent.net/amnezia/ppa/ubuntu {{ 'noble' if ansible_distribution_version == '24.04' else 'focal' }} main"
repo: "deb https://ppa.launchpadcontent.net/amnezia/ppa/ubuntu {{ 'noble' if ansible_facts['distribution_version'] == '24.04' else 'focal' }} main"
state: present
- name: Add AmneziaWG PPA source repository
- name: Add AmneziaWG PPA source repository
apt_repository:
repo: "deb-src https://ppa.launchpadcontent.net/amnezia/ppa/ubuntu {{ 'noble' if ansible_distribution_version == '24.04' else 'focal' }} main"
repo: "deb-src https://ppa.launchpadcontent.net/amnezia/ppa/ubuntu {{ 'noble' if ansible_facts['distribution_version'] == '24.04' else 'focal' }} main"
state: present
- name: Update apt cache and install AmneziaWG

View file

@ -6,7 +6,7 @@ MTU = 1420
[Peer]
PublicKey = {{ amneziawg_public_key.content | b64decode | trim }}
Endpoint = {{ ansible_default_ipv4.address }}:{{ amneziawg_port }}
Endpoint = {{ ansible_facts['default_ipv4']['address'] }}:{{ amneziawg_port }}
AllowedIPs = 0.0.0.0/0
# AmneziaWG obfuscation parameters for DPI bypass

View file

@ -21,7 +21,7 @@
deb http://mirrors.digitalocean.com/ubuntu/ jammy-backports main restricted universe multiverse
deb http://security.ubuntu.com/ubuntu/ jammy-security main restricted universe multiverse
dest: /etc/apt/sources.list
when: ansible_distribution_version == "22.04"
when: ansible_facts['distribution_version'] == "22.04"
- name: Configure Ubuntu 24.04 repositories
copy:
@ -31,7 +31,7 @@
deb http://mirrors.digitalocean.com/ubuntu/ noble-backports main restricted universe multiverse
deb http://security.ubuntu.com/ubuntu/ noble-security main restricted universe multiverse
dest: /etc/apt/sources.list
when: ansible_distribution_version == "24.04"
when: ansible_facts['distribution_version'] == "24.04"
- name: Update apt cache
apt:

View file

@ -16,13 +16,22 @@
dest: /usr/bin/certbot
state: link
- name: Check if certificates exist and are up to date
- name: Convert domains to proper list
set_fact:
domains_list: "{{ all_domains | from_yaml | flatten | unique | list }}"
- name: Check which domains already have certificates
shell: |
certbot certificates 2>/dev/null | grep "VALID:" | grep -v "EXPIRING SOON"
register: cert_status
certbot certificates -d {{ item }} 2>/dev/null | grep -q "Certificate Name: {{ item }}" && echo "exists" || echo "missing"
loop: "{{ domains_list }}"
register: cert_check
changed_when: false
failed_when: false
- name: Build list of domains needing certificates
set_fact:
domains_needing_certs: "{{ cert_check.results | selectattr('stdout', 'search', 'missing') | map(attribute='item') | list }}"
- name: Check if nginx is running
service_facts:
register: services_state
@ -31,29 +40,25 @@
set_fact:
nginx_running: "{{ 'nginx.service' in services_state.ansible_facts.services and services_state.ansible_facts.services['nginx.service'].state == 'running' }}"
- name: Stop nginx if running
- name: Stop nginx if running and certificates are needed
systemd:
name: nginx
state: stopped
when:
- cert_status.rc != 0
when:
- domains_needing_certs | length > 0
- nginx_running
- name: Convert domains to proper list
set_fact:
domains_list: "{{ all_domains | from_yaml | flatten | unique | list }}"
- name: Obtain certificates for each domain
- name: Obtain certificates for domains that need them
shell: >
certbot certonly --standalone --non-interactive --agree-tos -m {{ email }}
certbot certonly --standalone --non-interactive --agree-tos -m {{ email }}
-d {{ item }}
loop: "{{ domains_list }}"
when: cert_status.rc != 0
loop: "{{ domains_needing_certs }}"
when: domains_needing_certs | length > 0
- name: Start nginx if it was running
systemd:
name: nginx
state: started
when:
- cert_status.rc != 0
when:
- domains_needing_certs | length > 0
- nginx_running

View file

@ -0,0 +1,6 @@
coturn_listening_port: "{{ ports.external.coturn.port }}"
coturn_relay_min_port: "{{ ports.external.coturn_relay_min.port }}"
coturn_relay_max_port: "{{ ports.external.coturn_relay_max.port }}"
coturn_realm: "{{ domains.coturn }}"
coturn_config_path: "/etc/turnserver.conf"
coturn_log_path: "/var/log/turnserver"

View file

@ -0,0 +1,5 @@
---
- name: restart coturn
systemd:
name: coturn
state: restarted

View file

@ -0,0 +1,61 @@
---
- name: Install coturn
apt:
name: coturn
state: present
update_cache: yes
- name: Create coturn log directory
file:
path: "{{ coturn_log_path }}"
state: directory
owner: turnserver
group: turnserver
mode: '0755'
- name: Generate coturn configuration
template:
src: turnserver.conf.j2
dest: "{{ coturn_config_path }}"
owner: turnserver
group: turnserver
mode: '0600'
notify: restart coturn
- name: Enable coturn in /etc/default/coturn
lineinfile:
path: /etc/default/coturn
regexp: '^#?TURNSERVER_ENABLED='
line: 'TURNSERVER_ENABLED=1'
create: yes
notify: restart coturn
- name: Create ssl-cert group if it doesn't exist
group:
name: ssl-cert
state: present
- name: Add turnserver user to ssl-cert group for certificate access
user:
name: turnserver
groups: ssl-cert
append: yes
notify: restart coturn
- name: Set permissions on Let's Encrypt live directory
file:
path: /etc/letsencrypt/live
mode: '0755'
state: directory
- name: Set permissions on Let's Encrypt archive directory
file:
path: /etc/letsencrypt/archive
mode: '0755'
state: directory
- name: Enable and start coturn service
systemd:
name: coturn
enabled: yes
state: started

View file

@ -0,0 +1,78 @@
# Coturn TURN/STUN server configuration for Nextcloud Talk
# {{ ansible_managed }}
# Listening port for STUN/TURN
listening-port={{ coturn_listening_port }}
# Listening IP (0.0.0.0 for all interfaces)
listening-ip=0.0.0.0
# External IP address (automatically detected if not set)
# external-ip=YOUR_PUBLIC_IP
# Relay ports range
min-port={{ coturn_relay_min_port }}
max-port={{ coturn_relay_max_port }}
# Enable verbose logging
verbose
# Log file path
log-file={{ coturn_log_path }}/turnserver.log
# Realm for TURN server
realm={{ coturn_realm }}
# Use static auth secret (recommended for Nextcloud)
{% if coturn_static_secret is defined %}
use-auth-secret
static-auth-secret={{ coturn_static_secret }}
{% endif %}
# SSL/TLS certificates (Let's Encrypt)
cert=/etc/letsencrypt/live/{{ coturn_realm }}/fullchain.pem
pkey=/etc/letsencrypt/live/{{ coturn_realm }}/privkey.pem
# Cipher list for TLS
cipher-list="ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384"
# Disable UDP relay endpoints (optional, use only if needed)
# no-udp-relay
# Disable TCP relay endpoints (optional, use only if needed)
# no-tcp-relay
# Enable TLS
# tls-listening-port=5349
# Disable TLS 1.0 and 1.1
no-tlsv1
no-tlsv1_1
# Mobility support
mobility
# Fingerprint in TURN messages
fingerprint
# Long-term credentials mechanism
lt-cred-mech
# No multicast peers
no-multicast-peers
# Deny peer access to private IP ranges
no-loopback-peers
# User quota (max 100 concurrent sessions per user)
user-quota=100
# Total quota (max 1000 concurrent sessions)
total-quota=1000
# Disable CLI
no-cli
# Run as user/group
proc-user=turnserver
proc-group=turnserver

View file

@ -18,7 +18,7 @@ defaults
timeout server 50000ms
frontend sni_front
bind {{ ansible_default_ipv4.address }}:443
bind {{ ansible_facts['default_ipv4']['address'] }}:443
mode tcp
tcp-request inspect-delay 5s
tcp-request content accept if { req_ssl_hello_type 1 }

View file

@ -58,7 +58,26 @@
protocol: "{{ item.value.type }}"
destination_port: "{{ item.value.port }}"
loop: "{{ ports.external | dict2items }}"
when: item.value.type != 'icmp'
when: item.value.type != 'icmp' and item.value.type != 'both'
notify: save iptables
- name: Configure firewall rules for ports that need both TCP and UDP
iptables:
chain: INPUT
jump: ACCEPT
protocol: "{{ item[1] }}"
destination_port: "{{ item[0].value.port }}"
loop: "{{ ports.external | dict2items | product(['tcp', 'udp']) | list }}"
when: item[0].value.type == 'both'
notify: save iptables
- name: Configure firewall rules for UDP port ranges
iptables:
chain: INPUT
jump: ACCEPT
protocol: udp
destination_port: "{{ ports.external.coturn_relay_min.port }}:{{ ports.external.coturn_relay_max.port }}"
when: ports.external.coturn_relay_min is defined
notify: save iptables
- name: Allow ICMP (Ping)

View file

@ -11,6 +11,12 @@ events {
}
http {
# WebSocket connection upgrade map
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
# Basic Settings
sendfile on;
charset utf-8;

View file

@ -36,7 +36,7 @@ server {
# WebSocket support
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Connection $connection_upgrade;
proxy_buffering off;
}

View file

@ -1,66 +0,0 @@
{% set serpentina_ip = reverse_proxy.nas_ip %}
{% set serpentina_port = reverse_proxy.ports.snake %}
{% set serpentina_port_webrtc = reverse_proxy.ports.snake_webrtc %}
{% for domain in domains.nginx.snake %}
server {
listen {{ ports.internal.nginx_https }} ssl http2 proxy_protocol;
server_name {{ domain }};
set_real_ip_from 127.0.0.1;
real_ip_header proxy_protocol;
client_max_body_size 10M;
# Security headers
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
ssl_certificate /etc/letsencrypt/live/{{ domain }}/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/{{ domain }}/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
# Main website
location / {
proxy_pass http://{{ serpentina_ip }}:{{ serpentina_port }};
proxy_set_header Host $host;
proxy_set_header X-Real-IP $proxy_protocol_addr;
proxy_set_header X-Forwarded-For $proxy_protocol_addr;
proxy_set_header X-Forwarded-Proto $scheme;
# WebSocket support
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_buffering off;
}
# WebRTC specific settings
location /webrtc {
proxy_pass http://{{ serpentina_ip }}:{{ serpentina_port_webrtc }};
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $proxy_protocol_addr;
proxy_set_header X-Forwarded-For $proxy_protocol_addr;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 86400;
}
}
{% endfor %}
# HTTP redirect server block
server {
listen 80 proxy_protocol;
server_name {% for domain in domains.nginx.snake %}{{ domain }} {% endfor %};
set_real_ip_from 127.0.0.1;
real_ip_header proxy_protocol;
return 301 https://$host$request_uri;
}

View file

@ -14,7 +14,7 @@ ocserv_base_paths:
# Default settings for all instances
ocserv_default_settings:
udp_listen_host: "{{ ansible_default_ipv4.address }}"
udp_listen_host: "{{ ansible_facts['default_ipv4']['address'] }}"
tcp_listen_host: "127.0.0.1"
max_clients: 24
max_same_clients: 5

View file

@ -6,6 +6,6 @@ MTU = 1420
[Peer]
PublicKey = {{ wireguard_public_key.content | b64decode }}
Endpoint = {{ ansible_default_ipv4.address }}:{{ wireguard_port }}
Endpoint = {{ ansible_facts['default_ipv4']['address'] }}:{{ wireguard_port }}
AllowedIPs = 0.0.0.0/0
PersistentKeepalive = 25

View file

@ -7,15 +7,15 @@
pre_tasks:
- name: Check OS version
debug:
msg: "Running on Ubuntu {{ ansible_distribution_version }}"
when: ansible_distribution == "Ubuntu" and ansible_distribution_version in ['22.04', '24.04']
msg: "Running on Ubuntu {{ ansible_facts['distribution_version'] }}"
when: ansible_facts['distribution'] == "Ubuntu" and ansible_facts['distribution_version'] in ['22.04', '24.04']
- name: Verify Ubuntu 22.04 or higher
fail:
msg: "This playbook requires Ubuntu 22.04 (Jammy) or higher"
when: >
ansible_distribution != "Ubuntu" or
ansible_distribution_version not in ['22.04', '24.04']
ansible_facts['distribution'] != "Ubuntu" or
ansible_facts['distribution_version'] not in ['22.04', '24.04']
roles:
- base_system
@ -25,6 +25,7 @@
- haproxy
- nginx
- ocserv
- coturn
- certbot_renewal_config
- network
- fail2ban