Switch to terraform

This commit is contained in:
Prox
2026-02-15 18:37:15 +02:00
commit a7062b43ab
70 changed files with 6063 additions and 0 deletions

292
ansible/netbird/README.md Normal file
View File

@@ -0,0 +1,292 @@
# NetBird Deployment
Self-hosted NetBird VPN with embedded IdP (no external SSO required).
## Quick Start
```bash
cd ansible/deployments/netbird
# 1. Generate secrets
./generate-vault.sh
ansible-vault encrypt group_vars/vault.yml
# 2. Deploy
ansible-playbook playbook-ssl.yml -i inventory.yml --ask-vault-pass
# 3. Create admin (manual - see below)
# 4. Create PAT (manual - see below)
# 5. Provision groups and users
ansible-playbook setup-groups.yml -i inventory.yml --ask-vault-pass
ansible-playbook setup-users.yml -i inventory.yml --ask-vault-pass
```
## Prerequisites
- Ubuntu 22.04+ VPS with public IP
- Ports 80, 443, 3478 (TCP/UDP) open
- Ansible 2.14+
## Deployment Modes
| Playbook | Use Case |
| --------------------- | ------------------------------------------------------------------------ |
| `playbook-ssl-ip.yml` | HTTPS with self-signed cert on IP (recommended for isolated deployments) |
| `playbook-ssl.yml` | HTTPS with Let's Encrypt (requires domain) |
| `playbook-no-ssl.yml` | HTTP only (not recommended) |
## Full Workflow
### Step 1: Configure Inventory
Edit `inventory.yml`:
```yaml
all:
children:
netbird_servers:
hosts:
netbird-vps:
ansible_host: YOUR_SERVER_IP
ansible_user: root
ansible_python_interpreter: /usr/bin/python3
```
### Step 2: Generate Secrets
```bash
./generate-vault.sh
ansible-vault encrypt group_vars/vault.yml
```
This creates:
- `vault_turn_password` - TURN server authentication
- `vault_relay_secret` - Relay server secret
- `vault_encryption_key` - Embedded IdP encryption (BACK THIS UP!)
- `vault_admin_password` - Initial admin password
- `vault_netbird_service_pat` - Leave empty for now
### Step 3: Deploy NetBird
```bash
ansible-playbook playbook-ssl-ip.yml -i inventory.yml --ask-vault-pass
```
Wait for deployment to complete. Dashboard will be available at `https://YOUR_IP`.
### Step 4: Create Admin User (Manual)
1. Open `https://YOUR_IP` in browser (accept certificate warning)
2. Create admin account:
- **Email**: `admin@achilles.local` (from `group_vars/netbird_servers.yml`)
- **Password**: Use `vault_admin_password` from vault
```bash
# View password
ansible-vault view group_vars/vault.yml | grep vault_admin_password
```
### Step 5: Create Service User & PAT (Manual)
After logging in as admin:
1. Go to **Team****Service Users**
2. Click **Create Service User**
- Name: `Automation Service`
- Role: `Admin`
3. Click on the created service user
4. Click **Create Token**
- Name: `ansible-automation`
- Expiration: 365 days
5. **Copy the token** (shown only once!)
### Step 6: Store PAT in Vault
```bash
ansible-vault edit group_vars/vault.yml
```
Set:
```yaml
vault_netbird_service_pat: "nbp_xxxxxxxxxxxxxxxxxxxx"
```
### Step 7: Create Groups
```bash
ansible-playbook setup-groups.yml -i inventory.yml --ask-vault-pass
```
This creates:
- Battalion groups: `battalion-1-pilots`, `battalion-1-ground-stations`, etc.
- Dev team group: `dev-team`
- Setup keys for each group
- Access policies (battalion isolation + dev access)
### Step 8: Provision Users
Edit `group_vars/netbird_servers.yml` to define users:
```yaml
netbird_users:
# Dev team (full access)
- email: "vlad.stus@achilles.local"
name: "Vlad Stus"
role: "admin"
auto_groups:
- "dev-team"
# Battalion users
- email: "pilot1.bat1@achilles.local"
name: "Pilot One B1"
role: "user"
battalion: "battalion-1"
type: "pilot"
- email: "gs-operator1.bat1@achilles.local"
name: "GS Operator One B1"
role: "user"
battalion: "battalion-1"
type: "ground-station"
```
Then run:
```bash
ansible-playbook setup-users.yml -i inventory.yml --ask-vault-pass
```
**Dry run** (preview without creating):
```bash
ansible-playbook setup-users.yml -i inventory.yml --ask-vault-pass -e "dry_run=true"
```
Credentials are saved to `files/credentials/users-YYYY-MM-DD.yml`.
## User Roles
| Role | Permissions |
| ------- | ------------------------------------------ |
| `owner` | Full control, can delete account |
| `admin` | Manage users, groups, policies, setup keys |
| `user` | Connect peers, view own peers |
## User Types (for battalion assignment)
| Type | Auto-Group |
| ---------------- | ----------------------------- |
| `pilot` | `{battalion}-pilots` |
| `ground-station` | `{battalion}-ground-stations` |
## Backup & Restore
**Create backup** (downloads to `~/achilles-backups/netbird/`):
```bash
ansible-playbook backup.yml -i inventory.yml
```
**Restore latest backup**:
```bash
ansible-playbook restore.yml -i inventory.yml
```
**Restore specific backup**:
```bash
ansible-playbook restore.yml -i inventory.yml -e "backup_file=netbird-backup-20250116T120000.tar.gz"
```
Backups include:
- Management SQLite database (peers, routes, policies, users)
- Configuration files (docker-compose.yml, Caddyfile, etc.)
## Cleanup
**Soft cleanup** (stop containers, keep data):
```bash
ansible-playbook cleanup-soft.yml -i inventory.yml
```
**Full cleanup** (remove everything including data):
```bash
ansible-playbook cleanup-full.yml -i inventory.yml
```
## Connecting Peers
After provisioning, users connect with:
```bash
# Using setup key (for automated deployments)
netbird up --management-url https://YOUR_IP --setup-key SETUP_KEY
# Using user credentials (interactive)
netbird up --management-url https://YOUR_IP
# Then login with email/password in browser
```
## File Structure
```
ansible/deployments/netbird/
├── backup.yml # Backup management DB and config
├── cleanup-full.yml # Remove everything
├── cleanup-soft.yml # Stop containers only
├── generate-vault.sh # Generate random secrets
├── group_vars/
│ ├── netbird_servers.yml # Main configuration
│ ├── vault.yml # Encrypted secrets
│ └── vault.yml.example # Template for vault
├── inventory.yml # Server inventory
├── playbook-no-ssl.yml # HTTP deployment
├── playbook-ssl-ip.yml # HTTPS with self-signed (IP)
├── playbook-ssl.yml # HTTPS with Let's Encrypt
├── restore.yml # Restore from backup
├── setup-bootstrap.yml # Bootstrap admin (if API available)
├── setup-groups.yml # Create groups, keys, policies
├── setup-users.yml # Provision users
├── templates/ # Jinja2 templates
└── files/
└── credentials/ # Generated user passwords (gitignored)
```
## Troubleshooting
### Certificate Warning
Expected for SSL-IP mode. Accept the self-signed certificate in your browser.
### "Unauthenticated" after login
JWKS race condition bug in NetBird. Wait 30 seconds and try again, or restart the management container:
```bash
ssh root@YOUR_IP "cd /opt/netbird && docker compose restart management"
```
### API returns 404 on /api/instance/setup
The bootstrap API isn't exposed in all NetBird versions. Create admin manually in the dashboard.
### View logs
```bash
ssh root@YOUR_IP "cd /opt/netbird && docker compose logs -f"
```
### Check container status
```bash
ssh root@YOUR_IP "cd /opt/netbird && docker compose ps"
```

View File

@@ -0,0 +1,75 @@
---
# =============================================================================
# Full Cleanup - Remove everything including data
# =============================================================================
# WARNING: This will delete all NetBird data including:
# - Peer configurations
# - User accounts
# - Groups and policies
# - TLS certificates
#
# Run: ansible-playbook -i inventory.yml cleanup-full.yml
- name: Full Cleanup - Remove everything
hosts: netbird_servers
become: true
vars_files:
- group_vars/netbird_servers.yml
tasks:
- name: Check if docker-compose.yml exists
ansible.builtin.stat:
path: "{{ netbird_base_dir }}/docker-compose.yml"
register: compose_file
- name: Stop and remove containers with volumes
ansible.builtin.command:
cmd: docker compose down -v
chdir: "{{ netbird_base_dir }}"
when: compose_file.stat.exists
changed_when: true
- name: Remove any orphaned NetBird volumes
ansible.builtin.command:
cmd: docker volume rm {{ item }}
loop:
- netbird_management
- netbird_caddy_data
ignore_errors: true
changed_when: true
- name: Remove configuration directory
ansible.builtin.file:
path: "{{ netbird_base_dir }}"
state: absent
- name: Prune unused Docker images
ansible.builtin.command:
cmd: docker image prune -af --filter "label=org.opencontainers.image.title=netbird*"
changed_when: true
ignore_errors: true
- name: Display cleanup summary
ansible.builtin.debug:
msg: |
============================================
Full Cleanup Complete!
============================================
Removed:
- All NetBird containers
- All NetBird Docker volumes
- Configuration directory: {{ netbird_base_dir }}
- Unused NetBird Docker images
NetBird has been completely removed.
To redeploy, run the appropriate playbook:
ansible-playbook playbook-ssl-ip.yml -i inventory.yml --ask-vault-pass
or
ansible-playbook playbook-ssl.yml -i inventory.yml --ask-vault-pass
or
ansible-playbook playbook-no-ssl.yml -i inventory.yml --ask-vault-pass
Then bootstrap admin:
ansible-playbook setup-bootstrap.yml -i inventory.yml --ask-vault-pass
============================================

View File

@@ -0,0 +1,65 @@
---
# =============================================================================
# Soft Cleanup - Stop containers, preserve data
# =============================================================================
# Run: ansible-playbook -i inventory.yml cleanup-soft.yml
- name: Soft Cleanup - Stop containers, preserve data
hosts: netbird_servers
become: true
vars_files:
- group_vars/netbird_servers.yml
tasks:
- name: Check if docker-compose.yml exists
ansible.builtin.stat:
path: "{{ netbird_base_dir }}/docker-compose.yml"
register: compose_file
- name: Stop and remove containers (preserve volumes)
ansible.builtin.command:
cmd: docker compose down
chdir: "{{ netbird_base_dir }}"
when: compose_file.stat.exists
changed_when: true
- name: Get preserved Docker volumes
ansible.builtin.command:
cmd: docker volume ls -q --filter name=netbird
register: preserved_volumes
changed_when: false
ignore_errors: true
- name: Get config files
ansible.builtin.find:
paths: "{{ netbird_base_dir }}"
patterns: "*"
register: config_files
- name: Display cleanup summary
ansible.builtin.debug:
msg: |
============================================
Soft Cleanup Complete!
============================================
Stopped and removed:
- All NetBird containers
Preserved (data intact):
- Docker volumes:
{% for vol in preserved_volumes.stdout_lines %}
- {{ vol }}
{% endfor %}
- Configuration directory: {{ netbird_base_dir }}
- Configuration files:
{% for file in config_files.files %}
- {{ file.path | basename }}
{% endfor %}
To restart services:
cd {{ netbird_base_dir }} && docker compose up -d
To perform full cleanup (wipe data):
ansible-playbook cleanup-full.yml -i inventory.yml
============================================

View File

@@ -0,0 +1,9 @@
---
all:
children:
netbird_servers:
hosts:
netbird-vps:
ansible_host: dev.netbird.achilles-rnd.cc
ansible_user: app
ansible_python_interpreter: /usr/bin/python3

View File

@@ -0,0 +1,9 @@
---
all:
children:
netbird_servers:
hosts:
netbird-vps:
ansible_host: ext.netbird.achilles-rnd.cc
ansible_user: app
ansible_python_interpreter: /usr/bin/python3

View File

@@ -0,0 +1,71 @@
#!/bin/bash
# =============================================================================
# Generate vault.yml with random passwords
# =============================================================================
# Usage: ./generate-vault.sh
# Output: group_vars/vault.yml (ready to encrypt)
set -e
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
VAULT_FILE="$SCRIPT_DIR/group_vars/vault.yml"
# Generate alphanumeric passwords (no special chars - safe for connection strings)
generate_password() {
local length=${1:-32}
openssl rand -base64 48 | tr -d '/+=\n' | head -c "$length"
}
# Generate base64 encryption key (for AES-256-GCM)
generate_encryption_key() {
openssl rand -base64 32
}
echo "Generating vault.yml with random passwords..."
cat > "$VAULT_FILE" << EOF
---
# =============================================================================
# NetBird v1.6 Vault Secrets
# =============================================================================
# Generated: $(date -Iseconds)
# Encrypt with: ansible-vault encrypt group_vars/vault.yml
# TURN server password
vault_turn_password: "$(generate_password 32)"
# Relay secret
vault_relay_secret: "$(generate_password 32)"
# Encryption key for embedded IdP (AES-256-GCM)
# CRITICAL: Back this up! Loss prevents recovery of user data.
vault_encryption_key: "$(generate_encryption_key)"
# =============================================================================
# User Provisioning
# =============================================================================
# Initial admin password (for setup-bootstrap.yml)
vault_admin_password: "$(generate_password 20)"
# Service user PAT for API automation
# LEAVE EMPTY - fill after running setup-bootstrap.yml and creating PAT in dashboard
vault_netbird_service_pat: ""
EOF
echo ""
echo "Generated: $VAULT_FILE"
echo ""
echo "Contents:"
echo "----------------------------------------"
cat "$VAULT_FILE"
echo "----------------------------------------"
echo ""
echo "Next steps:"
echo " 1. Review the file above"
echo " 2. Encrypt: ansible-vault encrypt group_vars/vault.yml"
echo " 3. Deploy: ansible-playbook -i inventory.yml playbook-ssl-ip.yml --ask-vault-pass"
echo " 4. Bootstrap: ansible-playbook -i inventory.yml setup-bootstrap.yml --ask-vault-pass"
echo " 5. Create service user PAT in dashboard, add to vault.yml"
echo " 6. Groups: ansible-playbook -i inventory.yml setup-groups.yml --ask-vault-pass"
echo " 7. Users: ansible-playbook -i inventory.yml setup-users.yml --ask-vault-pass"

View File

@@ -0,0 +1,73 @@
---
# =============================================================================
# NetBird GitOps PoC Configuration
# =============================================================================
# Lightweight deployment using NetBird's native user management.
# No external IdP dependency.
# =============================================================================
# Domain Configuration
# =============================================================================
netbird_domain: "netbird-poc.networkmonitor.cc"
netbird_protocol: "https"
# =============================================================================
# Let's Encrypt Configuration
# =============================================================================
letsencrypt_email: "vlad.stus@gmail.com"
# =============================================================================
# Paths
# =============================================================================
netbird_base_dir: "/opt/netbird"
# =============================================================================
# Network Configuration
# =============================================================================
netbird_dns_domain: "netbird.local"
# =============================================================================
# TURN Server Configuration
# =============================================================================
turn_user: "netbird"
turn_password: "{{ vault_turn_password }}"
# =============================================================================
# Relay Configuration
# =============================================================================
relay_secret: "{{ vault_relay_secret }}"
# =============================================================================
# Embedded IdP Encryption Key
# =============================================================================
encryption_key: "{{ vault_encryption_key }}"
# =============================================================================
# Docker Configuration
# =============================================================================
netbird_version: "0.63.0"
dashboard_version: "v2.27.1"
caddy_version: "2.10.2"
coturn_version: "4.8.0-r0"
# =============================================================================
# PoC Groups (for Terraform/Pulumi comparison)
# =============================================================================
# These mirror Achilles network structure for testing IaC tools
poc_groups:
- name: "ground-stations"
display_name: "Ground Stations"
- name: "pilots"
display_name: "Pilots"
- name: "operators"
display_name: "Operators"
- name: "fusion-servers"
display_name: "Fusion Servers"
# =============================================================================
# Admin User Configuration (for setup-bootstrap.yml)
# =============================================================================
netbird_admin_user:
email: "admin@poc.local"
name: "PoC Administrator"
password: "{{ vault_admin_password }}"

View File

@@ -0,0 +1,58 @@
$ANSIBLE_VAULT;1.1;AES256
35363237356164656566323662333037363362353262303931363066386262323061636431333535
3938623466643935666439373239323731633432633166360a393938373433626136323237346338
39623463663566336662343365643338313162656161613963363262383038326366333730323733
6137393662316165330a326562663631313637353837333335643838303663356162376361363732
39393132306330643530393235303136363936343065613361646635666564636436366332366137
30633965336434653938646339343662653932663330353934343837626335343163326637666331
34373261616639323635326266346562383065656463373863383039626365656233386230346265
31393731323530313937323038633135376134663863646137336261643862396561336262636637
38616536613565623631646363613564623934623736633865626162346330313038663636623438
65663565313630356433623735663631333932336435663036393839653237383363316162306436
32383735643434336166383236383464333462346339653638393231316562383331613163303762
32386464353761333238613562386565316437343265323765373833336666303462656639616662
32663732373162653239626537313861356466643835643965633737376138363466303736663233
32313439623163643664643961356337323330316365326231616331666336663562323661313261
63356130313736303165303365646139346131646165323432383930623630303430353361636635
37333263373930613930313533623731613264336236623335346364323734613134666465306564
65313161643831343264363134303066653630343538326165316562666463613633653666613436
31383331613734366538623636356663613432663138356135666531323534333532353731343561
31303062306434343534333564336263646564303266373661393837313561343465623734386265
36336432666163383432353330613862393934303066323463353561393236653963653034363731
31346635666132303436356230383031623330303861613539663139616266313865313932383035
38373531386237306233663963613132353435326234383364616136323636636537633235633364
35613038353730323463346561336231613938656664333030313534396438396538353738336434
34323963663434633133643739336164623337626339363566323965346136346365626336393737
66653165306438616535623532313530653338626131353035623832393961643133363561636562
36303262656231633138336462663332656430306538343461383566623437323830303733333066
36383834393365393566366333396266346364303232363462663632346236353936616534643438
64623564383038323038643135356136646262636263623232383136366533636261343536353763
35666137316262383138646337646133383762346436333137393737613830313064356231643635
38616164646166363064663962373433313431303861353433356462643865343361646161646263
63316565393835633163313763336662383636313061636439643966363834623331363561306138
31633830633531306435633463396332633639316562643334393865396234373831333031643463
33346466653237343838636639626633633930343465346562623934643732393466393765643162
36346166643066343766373135383037363834346331343736623537373033383565343864393038
39396438316437653066303966396261643536373865366463306235326139306365316534393730
61613966303139643631343831383334656561333730663033653461323139653663313033613664
64643464323433373833356661383062356465356535396534323336636662303733313636373433
30613565306165303865363333316631653231636561313737373135383263343532343939333162
33313338343335313436656239316234363231313264303063333337636637643137393536626661
66623164636263663663383535663235336432646363393663626363323939666638616335633566
35303934386630616361343362333361316164356532363964613133633136336435623434343037
62383733636130303335323163663538333430363465353965333064316530346165653031303832
61613164356537633436313338636131646161636631376339383237663536336533653361393666
66363032346431623666326163393633656136303435356430653937323566653261376339623532
31643232336538626138353433616563656666326630356530346131396162666133666366316562
32356635663337396662303931633031363963656665383238356662383063303734313333313931
66613764343836356637396336373833323338623632366630326566623231633138623363366132
34393566626662643635643036393763666331623431393931366136613566396631393937626132
33646361346262333730333830343562393635316363373435306333353033316566356238646235
33376665633937613431303763316564666339626564313737383237393432313365356566313234
30663636363833313261616630393535376163323637346666613130623338623134633737616237
34373565306338383531633932623366343864653563313062613131303564356164653137626634
32333431663365343365346665383032663437636666316163386436633261313839623235373838
61376131393238623834663838333265316536383439353862633334653135386137353864373034
39303037363661613263653665376231386266393061646435353038633935623163333630313336
33343532373565333461373666396335666664663838313037383864643033666538316163336663
3031

View File

@@ -0,0 +1,42 @@
---
# =============================================================================
# NetBird v1.6 Vault Secrets
# =============================================================================
# Copy to vault.yml, edit values, then encrypt:
# cp vault.yml.example vault.yml
# # Edit vault.yml with your values
# ansible-vault encrypt vault.yml
#
# Or use: ./generate-vault.sh to auto-generate all secrets
# =============================================================================
# TURN/Relay Configuration
# =============================================================================
# TURN server password (alphanumeric only)
# Generate: openssl rand -base64 32 | tr -d '/+=\n'
vault_turn_password: "YourTurnPassword2024"
# Relay secret (alphanumeric only)
# Generate: openssl rand -base64 32 | tr -d '/+=\n'
vault_relay_secret: "YourRelaySecret2024"
# =============================================================================
# Embedded IdP Encryption Key
# =============================================================================
# CRITICAL: Back this up! Loss prevents recovery of user data.
# Generate: openssl rand -base64 32
vault_encryption_key: "YourBase64EncryptionKey=="
# =============================================================================
# User Provisioning (for setup-bootstrap.yml and setup-users.yml)
# =============================================================================
# Initial admin password (for setup-bootstrap.yml)
# Generate: openssl rand -base64 16 | tr -d '/+=\n'
vault_admin_password: "YourAdminPassword2024"
# Service user PAT for API automation
# LEAVE EMPTY UNTIL AFTER BOOTSTRAP!
# Create manually in dashboard: Team → Service Users → Create Token
vault_netbird_service_pat: ""

View File

@@ -0,0 +1,9 @@
---
all:
children:
netbird_servers:
hosts:
netbird-vps:
ansible_host: your-server.example.com # CHANGE THIS
ansible_user: root
ansible_python_interpreter: /usr/bin/python3

View File

@@ -0,0 +1,244 @@
---
# =============================================================================
# NetBird v1.6 Deployment - No-SSL Mode (HTTP only)
# =============================================================================
# Lightweight deployment for LAN/air-gapped networks.
# Access dashboard by IP address only.
#
# WARNING: All traffic is unencrypted. Only use on isolated networks.
#
# Prerequisites:
# 1. VPS/server accessible on local network
# 2. Update inventory.yml with your server IP
# 3. Create group_vars/vault.yml from vault.yml.example
#
# Run:
# ansible-playbook -i inventory.yml playbook-no-ssl.yml --ask-vault-pass
# =============================================================================
- name: Deploy NetBird v1.6 (No-SSL Mode)
hosts: netbird_servers
become: true
gather_facts: true
vars_files:
- group_vars/netbird_servers.yml
- group_vars/vault.yml
pre_tasks:
- name: Set no-SSL variables (override group_vars)
ansible.builtin.set_fact:
netbird_domain: "{{ ansible_default_ipv4.address }}"
netbird_protocol: "http"
relay_protocol: "rel"
relay_port: 80
signal_port: 80
single_account_domain: "netbird.local"
tasks:
# =========================================================================
# Prerequisites
# =========================================================================
- name: Update apt cache
ansible.builtin.apt:
update_cache: true
cache_valid_time: 3600
- name: Install prerequisites
ansible.builtin.apt:
name:
- apt-transport-https
- ca-certificates
- curl
- gnupg
- lsb-release
- jq
state: present
# =========================================================================
# Docker Installation
# =========================================================================
- name: Check if Docker is installed
ansible.builtin.command: docker --version
register: docker_installed
changed_when: false
failed_when: false
- name: Create keyrings directory
ansible.builtin.file:
path: /etc/apt/keyrings
state: directory
mode: "0755"
when: docker_installed.rc != 0
- name: Add Docker GPG key
ansible.builtin.shell: |
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg
chmod a+r /etc/apt/keyrings/docker.gpg
args:
creates: /etc/apt/keyrings/docker.gpg
when: docker_installed.rc != 0
- name: Add Docker repository
ansible.builtin.apt_repository:
repo: "deb [arch=amd64 signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu {{ ansible_distribution_release }} stable"
state: present
filename: docker
when: docker_installed.rc != 0
- name: Install Docker packages
ansible.builtin.apt:
name:
- docker-ce
- docker-ce-cli
- containerd.io
- docker-buildx-plugin
- docker-compose-plugin
state: present
update_cache: true
when: docker_installed.rc != 0
- name: Start and enable Docker
ansible.builtin.systemd:
name: docker
state: started
enabled: true
# =========================================================================
# NetBird Directory Structure
# =========================================================================
- name: Create NetBird directory
ansible.builtin.file:
path: "{{ netbird_base_dir }}"
state: directory
mode: "0755"
# =========================================================================
# Deploy Configuration Files
# =========================================================================
- name: Deploy docker-compose.yml
ansible.builtin.template:
src: templates/docker-compose.yml.j2
dest: "{{ netbird_base_dir }}/docker-compose.yml"
mode: "0644"
- name: Deploy Caddyfile (No-SSL mode)
ansible.builtin.template:
src: templates/Caddyfile-no-ssl.j2
dest: "{{ netbird_base_dir }}/Caddyfile"
mode: "0644"
- name: Deploy management.json
ansible.builtin.template:
src: templates/management.json.j2
dest: "{{ netbird_base_dir }}/management.json"
mode: "0644"
- name: Deploy dashboard.env
ansible.builtin.template:
src: templates/dashboard.env.j2
dest: "{{ netbird_base_dir }}/dashboard.env"
mode: "0640"
- name: Deploy relay.env
ansible.builtin.template:
src: templates/relay.env.j2
dest: "{{ netbird_base_dir }}/relay.env"
mode: "0640"
- name: Deploy turnserver.conf
ansible.builtin.template:
src: templates/turnserver.conf.j2
dest: "{{ netbird_base_dir }}/turnserver.conf"
mode: "0644"
# =========================================================================
# Firewall (UFW)
# =========================================================================
- name: Install UFW
ansible.builtin.apt:
name: ufw
state: present
- name: Allow SSH
community.general.ufw:
rule: allow
port: "22"
proto: tcp
- name: Allow HTTP
community.general.ufw:
rule: allow
port: "80"
proto: tcp
- name: Allow TURN UDP
community.general.ufw:
rule: allow
port: "3478"
proto: udp
- name: Allow TURN TCP
community.general.ufw:
rule: allow
port: "3478"
proto: tcp
- name: Enable UFW
community.general.ufw:
state: enabled
policy: deny
# =========================================================================
# Start Services
# =========================================================================
- name: Pull Docker images
ansible.builtin.command:
cmd: docker compose pull
chdir: "{{ netbird_base_dir }}"
changed_when: true
- name: Start NetBird services
ansible.builtin.command:
cmd: docker compose up -d
chdir: "{{ netbird_base_dir }}"
changed_when: true
# =========================================================================
# Wait for Services
# =========================================================================
- name: Wait for Management API to be available
ansible.builtin.uri:
url: "http://{{ netbird_domain }}/api/users"
method: GET
status_code: [200, 401, 403]
register: api_check
until: api_check.status in [200, 401, 403]
retries: 30
delay: 10
# =========================================================================
# Display Summary
# =========================================================================
- name: Display deployment status
ansible.builtin.debug:
msg: |
============================================
NetBird v1.6 Deployed Successfully! (No-SSL)
============================================
WARNING: Running in HTTP mode - traffic is unencrypted!
Only use on isolated/air-gapped networks.
Dashboard: http://{{ netbird_domain }}
Initial Setup:
1. Access the dashboard by IP
2. Create your first user (admin)
3. Generate setup keys for battalions
Connect peers with:
netbird up --management-url http://{{ netbird_domain }} --setup-key <KEY>
View logs:
ssh root@{{ ansible_host }} "cd {{ netbird_base_dir }} && docker compose logs -f"
============================================

View File

@@ -0,0 +1,258 @@
---
# =============================================================================
# NetBird v1.6 Deployment - SSL Mode with Public IP (Self-Signed)
# =============================================================================
# Uses Caddy's internal CA with self-signed certificates for HTTPS on IP.
# Browser will show certificate warning - this is expected.
#
# Note: Let's Encrypt supports IP certificates now, but Caddy's implementation
# is incomplete (GitHub issue #7399). Using self-signed as reliable fallback.
#
# Prerequisites:
# 1. VPS with public IP address
# 2. Port 80 and 443 accessible
# 3. Create group_vars/vault.yml from vault.yml.example
#
# Run:
# ansible-playbook -i inventory.yml playbook-ssl-ip.yml --ask-vault-pass
# =============================================================================
- name: Deploy NetBird v1.6 (SSL Mode - Public IP Self-Signed)
hosts: netbird_servers
become: true
gather_facts: true
vars_files:
- group_vars/netbird_servers.yml
- group_vars/vault.yml
pre_tasks:
- name: Set SSL-IP variables (override group_vars)
ansible.builtin.set_fact:
netbird_domain: "{{ ansible_default_ipv4.address }}"
netbird_protocol: "https"
relay_protocol: "rels"
relay_port: 443
signal_port: 443
single_account_domain: "netbird.local"
tasks:
# =========================================================================
# Prerequisites
# =========================================================================
- name: Update apt cache
ansible.builtin.apt:
update_cache: true
cache_valid_time: 3600
- name: Install prerequisites
ansible.builtin.apt:
name:
- apt-transport-https
- ca-certificates
- curl
- gnupg
- lsb-release
- jq
state: present
# =========================================================================
# Docker Installation
# =========================================================================
- name: Check if Docker is installed
ansible.builtin.command: docker --version
register: docker_installed
changed_when: false
failed_when: false
- name: Create keyrings directory
ansible.builtin.file:
path: /etc/apt/keyrings
state: directory
mode: "0755"
when: docker_installed.rc != 0
- name: Add Docker GPG key
ansible.builtin.shell: |
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg
chmod a+r /etc/apt/keyrings/docker.gpg
args:
creates: /etc/apt/keyrings/docker.gpg
when: docker_installed.rc != 0
- name: Add Docker repository
ansible.builtin.apt_repository:
repo: "deb [arch=amd64 signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu {{ ansible_distribution_release }} stable"
state: present
filename: docker
when: docker_installed.rc != 0
- name: Install Docker packages
ansible.builtin.apt:
name:
- docker-ce
- docker-ce-cli
- containerd.io
- docker-buildx-plugin
- docker-compose-plugin
state: present
update_cache: true
when: docker_installed.rc != 0
- name: Start and enable Docker
ansible.builtin.systemd:
name: docker
state: started
enabled: true
# =========================================================================
# NetBird Directory Structure
# =========================================================================
- name: Create NetBird directory
ansible.builtin.file:
path: "{{ netbird_base_dir }}"
state: directory
mode: "0755"
# =========================================================================
# Deploy Configuration Files
# =========================================================================
- name: Deploy docker-compose.yml
ansible.builtin.template:
src: templates/docker-compose.yml.j2
dest: "{{ netbird_base_dir }}/docker-compose.yml"
mode: "0644"
- name: Deploy Caddyfile (SSL-IP mode)
ansible.builtin.template:
src: templates/Caddyfile-ssl-ip.j2
dest: "{{ netbird_base_dir }}/Caddyfile"
mode: "0644"
- name: Deploy management.json
ansible.builtin.template:
src: templates/management.json.j2
dest: "{{ netbird_base_dir }}/management.json"
mode: "0644"
- name: Deploy dashboard.env
ansible.builtin.template:
src: templates/dashboard.env.j2
dest: "{{ netbird_base_dir }}/dashboard.env"
mode: "0640"
- name: Deploy relay.env
ansible.builtin.template:
src: templates/relay.env.j2
dest: "{{ netbird_base_dir }}/relay.env"
mode: "0640"
- name: Deploy turnserver.conf
ansible.builtin.template:
src: templates/turnserver.conf.j2
dest: "{{ netbird_base_dir }}/turnserver.conf"
mode: "0644"
# =========================================================================
# Firewall (UFW)
# =========================================================================
- name: Install UFW
ansible.builtin.apt:
name: ufw
state: present
- name: Allow SSH
community.general.ufw:
rule: allow
port: "22"
proto: tcp
- name: Allow HTTP (for ACME challenge)
community.general.ufw:
rule: allow
port: "80"
proto: tcp
- name: Allow HTTPS
community.general.ufw:
rule: allow
port: "443"
proto: tcp
- name: Allow HTTPS UDP (HTTP/3)
community.general.ufw:
rule: allow
port: "443"
proto: udp
- name: Allow TURN UDP
community.general.ufw:
rule: allow
port: "3478"
proto: udp
- name: Allow TURN TCP
community.general.ufw:
rule: allow
port: "3478"
proto: tcp
- name: Enable UFW
community.general.ufw:
state: enabled
policy: deny
# =========================================================================
# Start Services
# =========================================================================
- name: Pull Docker images
ansible.builtin.command:
cmd: docker compose pull
chdir: "{{ netbird_base_dir }}"
changed_when: true
- name: Start NetBird services
ansible.builtin.command:
cmd: docker compose up -d
chdir: "{{ netbird_base_dir }}"
changed_when: true
# =========================================================================
# Wait for Services
# =========================================================================
- name: Wait for Management API to be available
ansible.builtin.uri:
url: "https://{{ netbird_domain }}/api/users"
method: GET
status_code: [200, 401, 403]
validate_certs: false
register: api_check
until: api_check.status in [200, 401, 403]
retries: 30
delay: 10
# =========================================================================
# Display Summary
# =========================================================================
- name: Display deployment status
ansible.builtin.debug:
msg: |
============================================
NetBird v1.6 Deployed Successfully! (SSL-IP)
============================================
Dashboard: https://{{ netbird_domain }}
Note: Using self-signed certificate (Caddy internal CA).
Your browser will show a certificate warning - accept it to proceed.
Initial Setup:
1. Access the dashboard (accept certificate warning)
2. Create your first user (admin)
3. Generate setup keys for battalions
Connect peers with:
netbird up --management-url https://{{ netbird_domain }} --setup-key <KEY>
View logs:
ssh root@{{ ansible_host }} "cd {{ netbird_base_dir }} && docker compose logs -f"
============================================

View File

@@ -0,0 +1,242 @@
---
# =============================================================================
# NetBird v1.6 Deployment - SSL Mode (Let's Encrypt)
# =============================================================================
# Lightweight deployment without Authentik SSO.
# Uses NetBird native user management.
#
# Prerequisites:
# 1. Domain with DNS A record pointing to VPS IP
# 2. Port 80 open for ACME challenge
# 3. Update inventory.yml with your VPS IP/domain
# 4. Update group_vars/netbird_servers.yml with your domain
# 5. Create group_vars/vault.yml from vault.yml.example
#
# Run:
# ansible-playbook -i inventory.yml playbook-ssl.yml --ask-vault-pass
# =============================================================================
- name: Deploy NetBird v1.6 (SSL Mode)
hosts: netbird_servers
become: true
vars_files:
- group_vars/netbird_servers.yml
- group_vars/vault.yml
vars:
# SSL-specific settings
netbird_protocol: "https"
relay_protocol: "rels"
relay_port: 443
signal_port: 443
tasks:
# =========================================================================
# Prerequisites
# =========================================================================
- name: Update apt cache
ansible.builtin.apt:
update_cache: true
cache_valid_time: 3600
- name: Install prerequisites
ansible.builtin.apt:
name:
- apt-transport-https
- ca-certificates
- curl
- gnupg
- lsb-release
- jq
state: present
# =========================================================================
# Docker Installation
# =========================================================================
- name: Check if Docker is installed
ansible.builtin.command: docker --version
register: docker_installed
changed_when: false
failed_when: false
- name: Create keyrings directory
ansible.builtin.file:
path: /etc/apt/keyrings
state: directory
mode: "0755"
when: docker_installed.rc != 0
- name: Add Docker GPG key
ansible.builtin.shell: |
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg
chmod a+r /etc/apt/keyrings/docker.gpg
args:
creates: /etc/apt/keyrings/docker.gpg
when: docker_installed.rc != 0
- name: Add Docker repository
ansible.builtin.apt_repository:
repo: "deb [arch=amd64 signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu {{ ansible_distribution_release }} stable"
state: present
filename: docker
when: docker_installed.rc != 0
- name: Install Docker packages
ansible.builtin.apt:
name:
- docker-ce
- docker-ce-cli
- containerd.io
- docker-buildx-plugin
- docker-compose-plugin
state: present
update_cache: true
when: docker_installed.rc != 0
- name: Start and enable Docker
ansible.builtin.systemd:
name: docker
state: started
enabled: true
# =========================================================================
# NetBird Directory Structure
# =========================================================================
- name: Create NetBird directory
ansible.builtin.file:
path: "{{ netbird_base_dir }}"
state: directory
mode: "0755"
# =========================================================================
# Deploy Configuration Files
# =========================================================================
- name: Deploy docker-compose.yml
ansible.builtin.template:
src: templates/docker-compose.yml.j2
dest: "{{ netbird_base_dir }}/docker-compose.yml"
mode: "0644"
# Caddyfile is NOT deployed here - shared Caddy handles reverse proxy
# See ../caddy/playbook.yml
- name: Deploy management.json
ansible.builtin.template:
src: templates/management.json.j2
dest: "{{ netbird_base_dir }}/management.json"
mode: "0644"
- name: Deploy dashboard.env
ansible.builtin.template:
src: templates/dashboard.env.j2
dest: "{{ netbird_base_dir }}/dashboard.env"
mode: "0640"
- name: Deploy relay.env
ansible.builtin.template:
src: templates/relay.env.j2
dest: "{{ netbird_base_dir }}/relay.env"
mode: "0640"
- name: Deploy turnserver.conf
ansible.builtin.template:
src: templates/turnserver.conf.j2
dest: "{{ netbird_base_dir }}/turnserver.conf"
mode: "0644"
# =========================================================================
# Firewall (UFW)
# =========================================================================
- name: Install UFW
ansible.builtin.apt:
name: ufw
state: present
- name: Allow SSH
community.general.ufw:
rule: allow
port: "22"
proto: tcp
- name: Allow HTTP (ACME challenge)
community.general.ufw:
rule: allow
port: "80"
proto: tcp
- name: Allow HTTPS
community.general.ufw:
rule: allow
port: "443"
proto: tcp
- name: Allow TURN UDP
community.general.ufw:
rule: allow
port: "3478"
proto: udp
- name: Allow TURN TCP
community.general.ufw:
rule: allow
port: "3478"
proto: tcp
- name: Enable UFW
community.general.ufw:
state: enabled
policy: deny
# =========================================================================
# Start Services
# =========================================================================
- name: Pull Docker images
ansible.builtin.command:
cmd: docker compose pull
chdir: "{{ netbird_base_dir }}"
changed_when: true
- name: Start NetBird services
ansible.builtin.command:
cmd: docker compose up -d
chdir: "{{ netbird_base_dir }}"
changed_when: true
# =========================================================================
# Wait for Services
# =========================================================================
- name: Wait for management container to be running
ansible.builtin.command:
cmd: docker compose ps management --format json
chdir: "{{ netbird_base_dir }}"
register: management_container
until: "'running' in management_container.stdout"
retries: 12
delay: 5
changed_when: false
# =========================================================================
# Display Summary
# =========================================================================
- name: Display deployment status
ansible.builtin.debug:
msg: |
============================================
NetBird v1.6 Containers Deployed!
============================================
Containers: dashboard, signal, relay, management, coturn
============================================
NEXT STEPS:
============================================
1. Deploy shared Caddy:
cd ../caddy && ansible-playbook -i poc-inventory.yml playbook.yml
2. Then access https://{{ netbird_domain }}
============================================
View logs:
ssh root@{{ ansible_host }} "cd {{ netbird_base_dir }} && docker compose logs -f"
============================================

View File

@@ -0,0 +1,8 @@
---
all:
children:
netbird_servers:
hosts:
netbird-poc:
ansible_host: observability-poc.networkmonitor.cc
ansible_user: root

View File

@@ -0,0 +1,9 @@
---
all:
children:
netbird_servers:
hosts:
netbird-vps:
ansible_host: achilles-rnd.cc
ansible_user: root
ansible_python_interpreter: /usr/bin/python3

View File

@@ -0,0 +1,197 @@
---
# =============================================================================
# NetBird v1.6 - Instance Bootstrap
# =============================================================================
# Run ONCE on fresh deployment to:
# 1. Check if instance needs setup
# 2. Create initial admin user via API
# 3. Display instructions for manual PAT creation
#
# NOTE: The embedded IdP (Dex) does not support password grants,
# so PATs cannot be created programmatically without dashboard access.
# After this playbook, you MUST manually:
# 1. Login to dashboard with admin credentials
# 2. Create a service user
# 3. Generate PAT for service user
# 4. Store PAT in vault.yml as vault_netbird_service_pat
#
# Run:
# ansible-playbook -i inventory.yml setup-bootstrap.yml --ask-vault-pass
# =============================================================================
- name: Bootstrap NetBird Instance
hosts: netbird_servers
become: false
gather_facts: true
vars_files:
- group_vars/netbird_servers.yml
- group_vars/vault.yml
vars:
# For SSL-IP mode, use server IP; for domain mode, use netbird_domain
netbird_api_host: "{{ hostvars[inventory_hostname].ansible_host | default(netbird_domain) }}"
netbird_api_url: "https://{{ netbird_api_host }}/api"
tasks:
# =========================================================================
# Check Instance Status
# =========================================================================
- name: Check instance status
ansible.builtin.uri:
url: "{{ netbird_api_url }}/instance"
method: GET
validate_certs: false
status_code: [200, 404]
register: instance_status
delegate_to: localhost
ignore_errors: true
- name: Debug instance status
ansible.builtin.debug:
msg: "Instance API response: status={{ instance_status.status | default('N/A') }}, json={{ instance_status.json | default('N/A') }}"
- name: Determine setup status
ansible.builtin.set_fact:
setup_required: >-
{{
instance_status.status != 200 or
(instance_status.json.setup_required | default(false))
}}
instance_check_failed: "{{ instance_status.status != 200 }}"
- name: Check PAT status
ansible.builtin.set_fact:
pat_configured: "{{ vault_netbird_service_pat is defined and vault_netbird_service_pat | length > 0 }}"
- name: Display status - already configured
ansible.builtin.debug:
msg: |
============================================
Instance Already Configured
============================================
{% if pat_configured %}
Service PAT: ✓ Configured
Ready to provision users:
ansible-playbook -i inventory.yml setup-groups.yml --ask-vault-pass
ansible-playbook -i inventory.yml setup-users.yml --ask-vault-pass
{% else %}
Service PAT: ✗ Not configured
NEXT STEPS:
1. Login to dashboard: https://{{ netbird_api_host }}
(Accept self-signed certificate warning)
2. Create service user: Team → Service Users → Create
3. Generate PAT: Select user → Create Token
4. Store in vault: ansible-vault edit group_vars/vault.yml
Set: vault_netbird_service_pat: "<your-token>"
5. Then run:
ansible-playbook -i inventory.yml setup-groups.yml --ask-vault-pass
ansible-playbook -i inventory.yml setup-users.yml --ask-vault-pass
{% endif %}
============================================
when: not setup_required
- name: End play - instance already configured
ansible.builtin.meta: end_play
when: not setup_required
# =========================================================================
# Bootstrap Admin User
# =========================================================================
- name: Attempt to create initial admin user
ansible.builtin.uri:
url: "{{ netbird_api_url }}/instance/setup"
method: POST
headers:
Content-Type: "application/json"
body_format: json
body:
email: "{{ netbird_admin_user.email }}"
password: "{{ netbird_admin_user.password }}"
name: "{{ netbird_admin_user.name }}"
validate_certs: false
status_code: [200, 201, 404]
register: bootstrap_result
delegate_to: localhost
- name: Handle 404 - API endpoint not available
ansible.builtin.debug:
msg: |
============================================
Bootstrap API Not Available (404)
============================================
The /api/instance/setup endpoint returned 404.
This means either:
- Admin already exists, OR
- This endpoint isn't exposed in your NetBird version
Create admin manually in the dashboard:
1. Go to: https://{{ netbird_api_host }}
2. Create admin account:
Email: {{ netbird_admin_user.email }}
Password: {{ netbird_admin_user.password }}
Then create service user + PAT:
3. Go to Team → Service Users → Create
4. Generate PAT: Select user → Create Token
5. Store in vault: ansible-vault edit group_vars/vault.yml
Set: vault_netbird_service_pat: "<your-token>"
Then provision users:
ansible-playbook -i inventory.yml setup-groups.yml --ask-vault-pass
ansible-playbook -i inventory.yml setup-users.yml --ask-vault-pass
============================================
when: bootstrap_result.status == 404
- name: End play if already bootstrapped
ansible.builtin.meta: end_play
when: bootstrap_result.status == 404
- name: Display bootstrap result
ansible.builtin.debug:
msg: |
============================================
NetBird Instance Bootstrapped!
============================================
Admin User Created:
Email: {{ netbird_admin_user.email }}
Password: {{ netbird_admin_user.password }}
User ID: {{ bootstrap_result.json.user_id | default('N/A') }}
============================================
MANUAL STEPS REQUIRED:
============================================
1. Login to dashboard:
https://{{ netbird_api_host }}
(Accept self-signed certificate warning)
2. Login with:
Email: {{ netbird_admin_user.email }}
Password: {{ netbird_admin_user.password }}
3. Create Service User:
- Go to Team → Service Users
- Click "Create Service User"
- Name: "Automation Service"
- Role: "Admin"
- Save the user
4. Create PAT for Service User:
- Select the service user
- Click "Create Token"
- Name: "ansible-automation"
- Expiration: 365 days
- COPY THE TOKEN (shown only once!)
5. Store PAT in vault:
ansible-vault edit group_vars/vault.yml
Set: vault_netbird_service_pat: "<your-token>"
6. Run group and user provisioning:
ansible-playbook -i inventory.yml setup-groups.yml --ask-vault-pass
ansible-playbook -i inventory.yml setup-users.yml --ask-vault-pass
============================================
when: setup_required | default(true)

View File

@@ -0,0 +1,367 @@
---
# =============================================================================
# NetBird v1.6 - Battalion Group & Access Control Setup
# =============================================================================
# Creates:
# - Groups for each battalion (pilots + ground stations)
# - Dev team group with full access
# - Setup keys with auto-group assignment
# - Access control policies (battalion isolation + dev access)
#
# Prerequisites:
# 1. NetBird deployed and running (playbook-ssl.yml or playbook-no-ssl.yml)
# 2. Admin user created via dashboard
# 3. PAT (Personal Access Token) generated from dashboard
#
# Run:
# ansible-playbook -i inventory.yml setup-groups.yml --ask-vault-pass
# =============================================================================
- name: Configure NetBird Battalion Access Control
hosts: netbird_servers
become: false
gather_facts: false
vars_files:
- group_vars/netbird_servers.yml
- group_vars/vault.yml
vars:
# For SSL-IP mode, use server IP; for domain mode, use netbird_domain
netbird_api_host: "{{ hostvars[inventory_hostname].ansible_host | default(netbird_domain) }}"
netbird_api_url: "https://{{ netbird_api_host }}/api"
# Use PAT from vault, or allow override via command line
netbird_pat: "{{ vault_netbird_service_pat }}"
pre_tasks:
- name: Validate PAT is provided
ansible.builtin.assert:
that:
- netbird_pat is defined
- netbird_pat | length > 0
fail_msg: |
Service PAT not configured in vault.yml!
1. Create service user + PAT in dashboard
2. Store in vault: ansible-vault edit group_vars/vault.yml
Set: vault_netbird_service_pat: "<your-token>"
tasks:
# =========================================================================
# Get Existing Groups (to avoid duplicates)
# =========================================================================
- name: Get existing groups
ansible.builtin.uri:
url: "{{ netbird_api_url }}/groups"
method: GET
headers:
Authorization: "Token {{ netbird_pat }}"
Accept: "application/json"
validate_certs: false
status_code: [200]
register: existing_groups
delegate_to: localhost
- name: Extract existing group names
ansible.builtin.set_fact:
existing_group_names: "{{ existing_groups.json | map(attribute='name') | list }}"
# =========================================================================
# Create Battalion Groups
# =========================================================================
- name: Create battalion pilot groups
ansible.builtin.uri:
url: "{{ netbird_api_url }}/groups"
method: POST
headers:
Authorization: "Token {{ netbird_pat }}"
Content-Type: "application/json"
body_format: json
body:
name: "{{ item.name }}-pilots"
validate_certs: false
status_code: [200, 201]
loop: "{{ battalions }}"
when: "item.name + '-pilots' not in existing_group_names"
register: pilot_groups_created
delegate_to: localhost
- name: Create battalion ground station groups
ansible.builtin.uri:
url: "{{ netbird_api_url }}/groups"
method: POST
headers:
Authorization: "Token {{ netbird_pat }}"
Content-Type: "application/json"
body_format: json
body:
name: "{{ item.name }}-ground-stations"
validate_certs: false
status_code: [200, 201]
loop: "{{ battalions }}"
when: "item.name + '-ground-stations' not in existing_group_names"
register: gs_groups_created
delegate_to: localhost
- name: Create dev team group
ansible.builtin.uri:
url: "{{ netbird_api_url }}/groups"
method: POST
headers:
Authorization: "Token {{ netbird_pat }}"
Content-Type: "application/json"
body_format: json
body:
name: "{{ dev_team_group }}"
validate_certs: false
status_code: [200, 201]
when: "dev_team_group not in existing_group_names"
delegate_to: localhost
# =========================================================================
# Re-fetch Groups to Get IDs
# =========================================================================
- name: Get all groups with IDs
ansible.builtin.uri:
url: "{{ netbird_api_url }}/groups"
method: GET
headers:
Authorization: "Token {{ netbird_pat }}"
Accept: "application/json"
validate_certs: false
status_code: [200]
register: all_groups
delegate_to: localhost
- name: Build group ID mapping
ansible.builtin.set_fact:
group_id_map: "{{ group_id_map | default({}) | combine({item.name: item.id}) }}"
loop: "{{ all_groups.json }}"
- name: Get All group ID
ansible.builtin.set_fact:
all_group_id: "{{ (all_groups.json | selectattr('name', 'equalto', 'All') | first).id }}"
# =========================================================================
# Create Setup Keys
# =========================================================================
- name: Get existing setup keys
ansible.builtin.uri:
url: "{{ netbird_api_url }}/setup-keys"
method: GET
headers:
Authorization: "Token {{ netbird_pat }}"
Accept: "application/json"
validate_certs: false
status_code: [200]
register: existing_keys
delegate_to: localhost
- name: Extract existing setup key names
ansible.builtin.set_fact:
existing_key_names: "{{ existing_keys.json | map(attribute='name') | list }}"
- name: Create setup keys for battalion pilots
ansible.builtin.uri:
url: "{{ netbird_api_url }}/setup-keys"
method: POST
headers:
Authorization: "Token {{ netbird_pat }}"
Content-Type: "application/json"
body_format: json
body:
name: "{{ item.name }}-pilot-key"
type: "reusable"
expires_in: 31536000 # 1 year in seconds
revoked: false
auto_groups:
- "{{ group_id_map[item.name + '-pilots'] }}"
usage_limit: 0 # unlimited
validate_certs: false
status_code: [200, 201]
loop: "{{ battalions }}"
when: "item.name + '-pilot-key' not in existing_key_names"
register: pilot_keys
delegate_to: localhost
- name: Create setup keys for battalion ground stations
ansible.builtin.uri:
url: "{{ netbird_api_url }}/setup-keys"
method: POST
headers:
Authorization: "Token {{ netbird_pat }}"
Content-Type: "application/json"
body_format: json
body:
name: "{{ item.name }}-gs-key"
type: "reusable"
expires_in: 31536000
revoked: false
auto_groups:
- "{{ group_id_map[item.name + '-ground-stations'] }}"
usage_limit: 0
validate_certs: false
status_code: [200, 201]
loop: "{{ battalions }}"
when: "item.name + '-gs-key' not in existing_key_names"
register: gs_keys
delegate_to: localhost
- name: Create setup key for dev team
ansible.builtin.uri:
url: "{{ netbird_api_url }}/setup-keys"
method: POST
headers:
Authorization: "Token {{ netbird_pat }}"
Content-Type: "application/json"
body_format: json
body:
name: "dev-team-key"
type: "reusable"
expires_in: 31536000
revoked: false
auto_groups:
- "{{ group_id_map[dev_team_group] }}"
usage_limit: 0
validate_certs: false
status_code: [200, 201]
when: "'dev-team-key' not in existing_key_names"
register: dev_key
delegate_to: localhost
# =========================================================================
# Create Access Control Policies
# =========================================================================
- name: Get existing policies
ansible.builtin.uri:
url: "{{ netbird_api_url }}/policies"
method: GET
headers:
Authorization: "Token {{ netbird_pat }}"
Accept: "application/json"
validate_certs: false
status_code: [200]
register: existing_policies
delegate_to: localhost
- name: Extract existing policy names
ansible.builtin.set_fact:
existing_policy_names: "{{ existing_policies.json | map(attribute='name') | list }}"
- name: Create battalion internal access policies
ansible.builtin.uri:
url: "{{ netbird_api_url }}/policies"
method: POST
headers:
Authorization: "Token {{ netbird_pat }}"
Content-Type: "application/json"
body_format: json
body:
name: "{{ item.display_name }} - Internal Access"
description: "Allow {{ item.display_name }} pilots to access their ground stations"
enabled: true
rules:
- name: "{{ item.name }}-pilot-to-gs"
description: "Pilots can access ground stations"
enabled: true
sources:
- "{{ group_id_map[item.name + '-pilots'] }}"
destinations:
- "{{ group_id_map[item.name + '-ground-stations'] }}"
bidirectional: true
protocol: "all"
action: "accept"
validate_certs: false
status_code: [200, 201]
loop: "{{ battalions }}"
when: "item.display_name + ' - Internal Access' not in existing_policy_names"
delegate_to: localhost
- name: Create dev team full access policy
ansible.builtin.uri:
url: "{{ netbird_api_url }}/policies"
method: POST
headers:
Authorization: "Token {{ netbird_pat }}"
Content-Type: "application/json"
body_format: json
body:
name: "Dev Team - Full Access"
description: "Dev team can access all peers for troubleshooting"
enabled: true
rules:
- name: "dev-full-access"
description: "Dev team has access to all peers"
enabled: true
sources:
- "{{ group_id_map[dev_team_group] }}"
destinations:
- "{{ all_group_id }}"
bidirectional: true
protocol: "all"
action: "accept"
validate_certs: false
status_code: [200, 201]
when: "'Dev Team - Full Access' not in existing_policy_names"
delegate_to: localhost
# =========================================================================
# Fetch and Display Setup Keys
# =========================================================================
- name: Get all setup keys
ansible.builtin.uri:
url: "{{ netbird_api_url }}/setup-keys"
method: GET
headers:
Authorization: "Token {{ netbird_pat }}"
Accept: "application/json"
validate_certs: false
status_code: [200]
register: final_keys
delegate_to: localhost
- name: Display configuration summary
ansible.builtin.debug:
msg: |
============================================
Battalion Access Control Configured!
============================================
Groups Created:
{% for bat in battalions %}
- {{ bat.name }}-pilots
- {{ bat.name }}-ground-stations
{% endfor %}
- {{ dev_team_group }}
Access Control Matrix:
{% for bat in battalions %}
[{{ bat.display_name }}]
{{ bat.name }}-pilots <--> {{ bat.name }}-ground-stations
{% endfor %}
[Dev Team]
{{ dev_team_group }} --> All (full access)
Setup Keys (use these to register peers):
{% for key in final_keys.json %}
{% if key.name.endswith('-pilot-key') or key.name.endswith('-gs-key') or key.name == 'dev-team-key' %}
{{ key.name }}: {{ key.key }}
{% endif %}
{% endfor %}
Peer Registration Commands:
{% for bat in battalions %}
# {{ bat.display_name }} Pilot:
netbird up --management-url {{ netbird_protocol }}://{{ netbird_domain }} \
--setup-key <{{ bat.name }}-pilot-key> \
--hostname pilot-{{ bat.name }}-<callsign>
# {{ bat.display_name }} Ground Station:
netbird up --management-url {{ netbird_protocol }}://{{ netbird_domain }} \
--setup-key <{{ bat.name }}-gs-key> \
--hostname gs-{{ bat.name }}-<location>
{% endfor %}
# Dev Team:
netbird up --management-url {{ netbird_protocol }}://{{ netbird_domain }} \
--setup-key <dev-team-key> \
--hostname dev-<name>
============================================

View File

@@ -0,0 +1,281 @@
---
# =============================================================================
# NetBird v1.6 - User Provisioning
# =============================================================================
# Creates users with embedded IdP and stores generated passwords.
# Requires: service user PAT in vault.yml (see setup-bootstrap.yml)
#
# Run:
# ansible-playbook -i inventory.yml setup-users.yml --ask-vault-pass
#
# Optional variables:
# -e "dry_run=true" Preview changes without creating users
# =============================================================================
- name: Provision NetBird Users
hosts: netbird_servers
become: false
gather_facts: true
vars_files:
- group_vars/netbird_servers.yml
- group_vars/vault.yml
vars:
# For SSL-IP mode, use server IP; for domain mode, use netbird_domain
netbird_api_host: "{{ hostvars[inventory_hostname].ansible_host | default(netbird_domain) }}"
netbird_api_url: "https://{{ netbird_api_host }}/api"
dry_run: false
pre_tasks:
# =========================================================================
# Validate Prerequisites
# =========================================================================
- name: Validate service PAT is provided
ansible.builtin.assert:
that:
- vault_netbird_service_pat is defined
- vault_netbird_service_pat | length > 0
fail_msg: |
Service PAT not configured!
Run setup-bootstrap.yml first, then add PAT to vault.yml
- name: Verify API connectivity with PAT
ansible.builtin.uri:
url: "{{ netbird_api_url }}/users"
method: GET
headers:
Authorization: "Token {{ vault_netbird_service_pat }}"
Accept: "application/json"
validate_certs: false
status_code: [200]
register: api_check
delegate_to: localhost
- name: Display connection status
ansible.builtin.debug:
msg: "API connection successful. Found {{ api_check.json | length }} existing users."
tasks:
# =========================================================================
# Fetch Existing State
# =========================================================================
- name: Get existing users
ansible.builtin.uri:
url: "{{ netbird_api_url }}/users"
method: GET
headers:
Authorization: "Token {{ vault_netbird_service_pat }}"
Accept: "application/json"
validate_certs: false
status_code: [200]
register: existing_users_response
delegate_to: localhost
- name: Extract existing user emails
ansible.builtin.set_fact:
existing_user_emails: "{{ existing_users_response.json | map(attribute='email') | list }}"
- name: Get existing groups
ansible.builtin.uri:
url: "{{ netbird_api_url }}/groups"
method: GET
headers:
Authorization: "Token {{ vault_netbird_service_pat }}"
Accept: "application/json"
validate_certs: false
status_code: [200]
register: existing_groups_response
delegate_to: localhost
- name: Build group ID mapping
ansible.builtin.set_fact:
group_id_map: "{{ group_id_map | default({}) | combine({item.name: item.id}) }}"
loop: "{{ existing_groups_response.json }}"
# =========================================================================
# Resolve Auto-Groups for Users
# =========================================================================
- name: Resolve auto-groups for users
ansible.builtin.set_fact:
resolved_users: "{{ resolved_users | default([]) + [user_with_groups] }}"
vars:
battalion_group: >-
{%- if item.battalion is defined and item.battalion -%}
{%- if item.type | default('pilot') == 'pilot' -%}
{{ item.battalion }}-pilots
{%- else -%}
{{ item.battalion }}-ground-stations
{%- endif -%}
{%- endif -%}
final_auto_groups: >-
{{ item.auto_groups | default([]) + ([battalion_group | trim] if battalion_group | trim else []) }}
resolved_group_ids: >-
{{ final_auto_groups | map('extract', group_id_map) | select('defined') | list }}
user_with_groups:
email: "{{ item.email }}"
name: "{{ item.name }}"
role: "{{ item.role | default('user') }}"
auto_groups: "{{ resolved_group_ids }}"
auto_group_names: "{{ final_auto_groups }}"
battalion: "{{ item.battalion | default(none) }}"
skip: "{{ item.email in existing_user_emails }}"
loop: "{{ netbird_users | default([]) }}"
# =========================================================================
# Display Plan
# =========================================================================
- name: Count users to process
ansible.builtin.set_fact:
users_to_create: "{{ resolved_users | default([]) | rejectattr('skip') | list }}"
users_to_skip: "{{ resolved_users | default([]) | selectattr('skip') | list }}"
- name: Display provisioning plan
ansible.builtin.debug:
msg: |
============================================
User Provisioning Plan
============================================
Mode: {{ 'DRY RUN' if dry_run else 'EXECUTE' }}
Users to CREATE ({{ users_to_create | length }}):
{% for user in users_to_create %}
- {{ user.email }}
Name: {{ user.name }}
Role: {{ user.role }}
Groups: {{ user.auto_group_names | join(', ') or 'None' }}
{% endfor %}
{% if users_to_create | length == 0 %}
(none)
{% endif %}
Users to SKIP - already exist ({{ users_to_skip | length }}):
{% for user in users_to_skip %}
- {{ user.email }}
{% endfor %}
{% if users_to_skip | length == 0 %}
(none)
{% endif %}
============================================
- name: End play in dry run mode
ansible.builtin.meta: end_play
when: dry_run | bool
- name: End play if no users to create
ansible.builtin.meta: end_play
when: users_to_create | length == 0
# =========================================================================
# Create Users
# =========================================================================
- name: Create credentials directory
ansible.builtin.file:
path: "{{ playbook_dir }}/files/credentials"
state: directory
mode: "0700"
delegate_to: localhost
- name: Create new users
ansible.builtin.uri:
url: "{{ netbird_api_url }}/users"
method: POST
headers:
Authorization: "Token {{ vault_netbird_service_pat }}"
Content-Type: "application/json"
Accept: "application/json"
body_format: json
body:
email: "{{ item.email }}"
name: "{{ item.name }}"
role: "{{ item.role }}"
auto_groups: "{{ item.auto_groups }}"
is_service_user: false
validate_certs: false
status_code: [200, 201]
loop: "{{ users_to_create }}"
register: created_users
delegate_to: localhost
# =========================================================================
# Store Credentials
# =========================================================================
- name: Build credentials list
ansible.builtin.set_fact:
user_credentials: "{{ user_credentials | default([]) + [credential] }}"
vars:
matching_user: "{{ resolved_users | selectattr('email', 'equalto', item.json.email) | first }}"
credential:
email: "{{ item.json.email }}"
name: "{{ item.json.name }}"
password: "{{ item.json.password | default('N/A') }}"
user_id: "{{ item.json.id }}"
role: "{{ item.json.role }}"
created_at: "{{ ansible_date_time.iso8601 }}"
groups: "{{ matching_user.auto_group_names | default([]) }}"
loop: "{{ created_users.results }}"
when: item.json is defined
- name: Save credentials to file
ansible.builtin.copy:
content: |
---
# =============================================================================
# NetBird User Credentials
# =============================================================================
# Generated: {{ ansible_date_time.iso8601 }}
# Instance: {{ netbird_domain }}
#
# WARNING: Store securely! Passwords cannot be retrieved again.
# =============================================================================
users:
{% for user in user_credentials %}
- email: "{{ user.email }}"
name: "{{ user.name }}"
password: "{{ user.password }}"
user_id: "{{ user.user_id }}"
role: "{{ user.role }}"
groups:
{% for group in user.groups %}
- "{{ group }}"
{% endfor %}
{% if user.groups | length == 0 %}
[]
{% endif %}
{% endfor %}
dest: "{{ playbook_dir }}/files/credentials/users-{{ ansible_date_time.date }}.yml"
mode: "0600"
delegate_to: localhost
when:
- user_credentials is defined
- user_credentials | length > 0
# =========================================================================
# Display Summary
# =========================================================================
- name: Display provisioning summary
ansible.builtin.debug:
msg: |
============================================
User Provisioning Complete!
============================================
Created Users ({{ user_credentials | default([]) | length }}):
{% for user in user_credentials | default([]) %}
{{ user.email }}
Password: {{ user.password }}
Role: {{ user.role }}
Groups: {{ user.groups | join(', ') or 'None' }}
{% endfor %}
Credentials saved to:
{{ playbook_dir }}/files/credentials/users-{{ ansible_date_time.date }}.yml
IMPORTANT:
1. Share passwords securely with users
2. Encrypt or delete credentials file after distribution:
ansible-vault encrypt files/credentials/users-{{ ansible_date_time.date }}.yml
3. Users should change passwords on first login
Login URL: https://{{ netbird_api_host }}
============================================
when: user_credentials is defined

View File

@@ -0,0 +1,35 @@
# =============================================================================
# NetBird v1.6 Caddyfile - No-SSL Mode (HTTP only, LAN access)
# =============================================================================
# WARNING: This configuration transmits data in plaintext.
# Only use on isolated/air-gapped networks.
{
servers :80 {
protocols h1 h2c
}
# Disable automatic HTTPS
auto_https off
}
:80 {
# Embedded IdP OAuth2 endpoints
reverse_proxy /oauth2/* management:80
reverse_proxy /.well-known/openid-configuration management:80
reverse_proxy /.well-known/jwks.json management:80
# NetBird Relay
reverse_proxy /relay* relay:80
# NetBird Signal (gRPC)
reverse_proxy /signalexchange.SignalExchange/* h2c://signal:10000
# NetBird Management API (gRPC)
reverse_proxy /management.ManagementService/* h2c://management:80
# NetBird Management REST API
reverse_proxy /api/* management:80
# NetBird Dashboard (catch-all)
reverse_proxy /* dashboard:80
}
}

View File

@@ -0,0 +1,61 @@
# =============================================================================
# NetBird v1.6 Caddyfile - SSL Mode with Public IP (Self-Signed)
# =============================================================================
# Uses Caddy's internal CA to generate self-signed certificates for IP access.
# Note: Let's Encrypt IP certificates are supported but Caddy's implementation
# is incomplete (issue #7399). Using self-signed as reliable fallback.
{
servers :80,:443 {
protocols h1 h2c h2
}
# Required for IP-based TLS - clients don't send SNI for IP addresses
# Docker networking makes Caddy see internal IPs, so we need default_sni
default_sni {{ netbird_domain }}
}
(security_headers) {
header * {
Strict-Transport-Security "max-age=3600; includeSubDomains; preload"
X-Content-Type-Options "nosniff"
X-Frame-Options "SAMEORIGIN"
X-XSS-Protection "1; mode=block"
-Server
Referrer-Policy strict-origin-when-cross-origin
}
}
:80 {
# Redirect HTTP to HTTPS
redir https://{host}{uri} permanent
}
# Bind to IP address explicitly so Caddy knows what certificate to generate
https://{{ netbird_domain }} {
# Use Caddy's internal CA for self-signed certificate
tls internal {
protocols tls1.2 tls1.3
}
import security_headers
# Embedded IdP OAuth2 endpoints
reverse_proxy /oauth2/* management:80
reverse_proxy /.well-known/openid-configuration management:80
reverse_proxy /.well-known/jwks.json management:80
# NetBird Relay
reverse_proxy /relay* relay:80
# NetBird Signal (gRPC)
reverse_proxy /signalexchange.SignalExchange/* h2c://signal:10000
# NetBird Management API (gRPC)
reverse_proxy /management.ManagementService/* h2c://management:80
# NetBird Management REST API
reverse_proxy /api/* management:80
# NetBird Dashboard (catch-all)
reverse_proxy /* dashboard:80
}
}

View File

@@ -0,0 +1,45 @@
# =============================================================================
# NetBird v1.6 Caddyfile - SSL Mode (Let's Encrypt)
# =============================================================================
{
servers :80,:443 {
protocols h1 h2c h2 h3
}
email {{ letsencrypt_email }}
}
(security_headers) {
header * {
Strict-Transport-Security "max-age=3600; includeSubDomains; preload"
X-Content-Type-Options "nosniff"
X-Frame-Options "SAMEORIGIN"
X-XSS-Protection "1; mode=block"
-Server
Referrer-Policy strict-origin-when-cross-origin
}
}
{{ netbird_domain }} {
import security_headers
# Embedded IdP OAuth2 endpoints
reverse_proxy /oauth2/* management:80
reverse_proxy /.well-known/openid-configuration management:80
reverse_proxy /.well-known/jwks.json management:80
# NetBird Relay
reverse_proxy /relay* relay:80
# NetBird Signal (gRPC)
reverse_proxy /signalexchange.SignalExchange/* h2c://signal:10000
# NetBird Management API (gRPC)
reverse_proxy /management.ManagementService/* h2c://management:80
# NetBird Management REST API
reverse_proxy /api/* management:80
# NetBird Dashboard (catch-all)
reverse_proxy /* dashboard:80
}
}

View File

@@ -0,0 +1,22 @@
# =============================================================================
# NetBird Dashboard Environment (v1.6 - Embedded IdP)
# =============================================================================
# Uses NetBird's embedded IdP - no external auth required
# Endpoints
NETBIRD_MGMT_API_ENDPOINT={{ netbird_protocol }}://{{ netbird_domain }}
NETBIRD_MGMT_GRPC_API_ENDPOINT={{ netbird_protocol }}://{{ netbird_domain }}
# OIDC - using embedded IdP
AUTH_AUDIENCE=netbird-dashboard
AUTH_CLIENT_ID=netbird-dashboard
AUTH_CLIENT_SECRET=
AUTH_AUTHORITY={{ netbird_protocol }}://{{ netbird_domain }}/oauth2
USE_AUTH0=false
AUTH_SUPPORTED_SCOPES=openid profile email groups
AUTH_REDIRECT_URI=/nb-auth
AUTH_SILENT_REDIRECT_URI=/nb-silent-auth
# SSL (handled by Caddy)
NGINX_SSL_PORT=443
LETSENCRYPT_DOMAIN=none

View File

@@ -0,0 +1,97 @@
# =============================================================================
# NetBird v1.6 - Lightweight Deployment (No Authentik, No Caddy)
# =============================================================================
# Services: Dashboard, Signal, Relay, Management, Coturn
# Caddy is deployed separately as shared reverse proxy.
services:
# ---------------------------------------------------------------------------
# NetBird Dashboard
# ---------------------------------------------------------------------------
dashboard:
image: netbirdio/dashboard:{{ dashboard_version }}
restart: unless-stopped
networks: [netbird]
env_file:
- {{ netbird_base_dir }}/dashboard.env
logging:
driver: "json-file"
options:
max-size: "500m"
max-file: "2"
# ---------------------------------------------------------------------------
# NetBird Signal Server
# ---------------------------------------------------------------------------
signal:
image: netbirdio/signal:{{ netbird_version }}
restart: unless-stopped
networks: [netbird]
logging:
driver: "json-file"
options:
max-size: "500m"
max-file: "2"
# ---------------------------------------------------------------------------
# NetBird Relay Server
# ---------------------------------------------------------------------------
relay:
image: netbirdio/relay:{{ netbird_version }}
restart: unless-stopped
networks: [netbird]
env_file:
- {{ netbird_base_dir }}/relay.env
logging:
driver: "json-file"
options:
max-size: "500m"
max-file: "2"
# ---------------------------------------------------------------------------
# NetBird Management Server
# ---------------------------------------------------------------------------
management:
image: netbirdio/management:{{ netbird_version }}
restart: unless-stopped
networks: [netbird]
volumes:
- netbird_management:/var/lib/netbird
- {{ netbird_base_dir }}/management.json:/etc/netbird/management.json
command: [
"--port", "80",
"--log-file", "console",
"--log-level", "info",
"--disable-anonymous-metrics=false",
"--single-account-mode-domain={{ single_account_domain | default(netbird_domain) }}",
"--dns-domain={{ netbird_dns_domain }}"
]
logging:
driver: "json-file"
options:
max-size: "500m"
max-file: "2"
# ---------------------------------------------------------------------------
# Coturn TURN/STUN Server
# ---------------------------------------------------------------------------
coturn:
image: coturn/coturn:{{ coturn_version }}
restart: unless-stopped
volumes:
- {{ netbird_base_dir }}/turnserver.conf:/etc/coturn/turnserver.conf:ro
network_mode: host
command:
- "-c"
- "/etc/coturn/turnserver.conf"
logging:
driver: "json-file"
options:
max-size: "500m"
max-file: "2"
volumes:
netbird_management:
networks:
netbird:

View File

@@ -0,0 +1,49 @@
{
"Stuns": [
{
"Proto": "udp",
"URI": "stun:{{ netbird_domain }}:3478"
}
],
"TURNConfig": {
"Turns": [
{
"Proto": "udp",
"URI": "turn:{{ netbird_domain }}:3478",
"Username": "{{ turn_user }}",
"Password": "{{ turn_password }}"
}
],
"TimeBasedCredentials": false
},
"Relay": {
"Addresses": [
"{{ relay_protocol }}://{{ netbird_domain }}:{{ relay_port }}/relay"
],
"CredentialsTTL": "168h",
"Secret": "{{ relay_secret }}"
},
"Signal": {
"Proto": "{{ netbird_protocol }}",
"URI": "{{ netbird_domain }}:{{ signal_port }}"
},
"Datadir": "/var/lib/netbird",
"DataStoreEncryptionKey": "{{ encryption_key }}",
"StoreConfig": {
"Engine": "sqlite"
},
"HttpConfig": {
"Address": "0.0.0.0:80"
},
"IdpManagerConfig": {
"ManagerType": "none"
},
"EmbeddedIdP": {
"Enabled": true,
"Issuer": "{{ netbird_protocol }}://{{ netbird_domain }}/oauth2",
"DashboardRedirectURIs": [
"{{ netbird_protocol }}://{{ netbird_domain }}/nb-auth",
"{{ netbird_protocol }}://{{ netbird_domain }}/nb-silent-auth"
]
}
}

View File

@@ -0,0 +1,8 @@
# =============================================================================
# NetBird Relay Environment
# =============================================================================
NB_LOG_LEVEL=info
NB_LISTEN_ADDRESS=:80
NB_EXPOSED_ADDRESS={{ relay_protocol }}://{{ netbird_domain }}:{{ relay_port }}/relay
NB_AUTH_SECRET={{ relay_secret }}

View File

@@ -0,0 +1,15 @@
# =============================================================================
# Coturn TURN/STUN Server Configuration
# =============================================================================
listening-port=3478
external-ip={{ ansible_default_ipv4.address }}
relay-ip={{ ansible_default_ipv4.address }}
fingerprint
lt-cred-mech
user={{ turn_user }}:{{ turn_password }}
realm={{ netbird_domain }}
log-file=stdout
no-tls
no-dtls
no-cli