Switch to terraform
This commit is contained in:
292
ansible/netbird/README.md
Normal file
292
ansible/netbird/README.md
Normal 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"
|
||||
```
|
||||
75
ansible/netbird/cleanup-full.yml
Normal file
75
ansible/netbird/cleanup-full.yml
Normal 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
|
||||
============================================
|
||||
65
ansible/netbird/cleanup-soft.yml
Normal file
65
ansible/netbird/cleanup-soft.yml
Normal 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
|
||||
============================================
|
||||
9
ansible/netbird/dev-inventory.yml
Normal file
9
ansible/netbird/dev-inventory.yml
Normal 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
|
||||
9
ansible/netbird/ext-inventory.yml
Normal file
9
ansible/netbird/ext-inventory.yml
Normal 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
|
||||
71
ansible/netbird/generate-vault.sh
Executable file
71
ansible/netbird/generate-vault.sh
Executable 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"
|
||||
73
ansible/netbird/group_vars/netbird_servers.yml
Normal file
73
ansible/netbird/group_vars/netbird_servers.yml
Normal 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 }}"
|
||||
58
ansible/netbird/group_vars/vault.yml
Normal file
58
ansible/netbird/group_vars/vault.yml
Normal 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
|
||||
42
ansible/netbird/group_vars/vault.yml.example
Normal file
42
ansible/netbird/group_vars/vault.yml.example
Normal 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: ""
|
||||
9
ansible/netbird/inventory.yml
Normal file
9
ansible/netbird/inventory.yml
Normal 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
|
||||
244
ansible/netbird/playbook-no-ssl.yml
Normal file
244
ansible/netbird/playbook-no-ssl.yml
Normal 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"
|
||||
============================================
|
||||
258
ansible/netbird/playbook-ssl-ip.yml
Normal file
258
ansible/netbird/playbook-ssl-ip.yml
Normal 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"
|
||||
============================================
|
||||
242
ansible/netbird/playbook-ssl.yml
Normal file
242
ansible/netbird/playbook-ssl.yml
Normal 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"
|
||||
============================================
|
||||
8
ansible/netbird/poc-inventory.yml
Normal file
8
ansible/netbird/poc-inventory.yml
Normal file
@@ -0,0 +1,8 @@
|
||||
---
|
||||
all:
|
||||
children:
|
||||
netbird_servers:
|
||||
hosts:
|
||||
netbird-poc:
|
||||
ansible_host: observability-poc.networkmonitor.cc
|
||||
ansible_user: root
|
||||
9
ansible/netbird/prod-inventory.yml
Normal file
9
ansible/netbird/prod-inventory.yml
Normal 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
|
||||
197
ansible/netbird/setup-bootstrap.yml
Normal file
197
ansible/netbird/setup-bootstrap.yml
Normal 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)
|
||||
367
ansible/netbird/setup-groups.yml
Normal file
367
ansible/netbird/setup-groups.yml
Normal 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>
|
||||
|
||||
============================================
|
||||
281
ansible/netbird/setup-users.yml
Normal file
281
ansible/netbird/setup-users.yml
Normal 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
|
||||
35
ansible/netbird/templates/Caddyfile-no-ssl.j2
Normal file
35
ansible/netbird/templates/Caddyfile-no-ssl.j2
Normal 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
|
||||
}
|
||||
}
|
||||
61
ansible/netbird/templates/Caddyfile-ssl-ip.j2
Normal file
61
ansible/netbird/templates/Caddyfile-ssl-ip.j2
Normal 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
|
||||
}
|
||||
}
|
||||
45
ansible/netbird/templates/Caddyfile-ssl.j2
Normal file
45
ansible/netbird/templates/Caddyfile-ssl.j2
Normal 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
|
||||
}
|
||||
}
|
||||
22
ansible/netbird/templates/dashboard.env.j2
Normal file
22
ansible/netbird/templates/dashboard.env.j2
Normal 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
|
||||
97
ansible/netbird/templates/docker-compose.yml.j2
Normal file
97
ansible/netbird/templates/docker-compose.yml.j2
Normal 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:
|
||||
49
ansible/netbird/templates/management.json.j2
Normal file
49
ansible/netbird/templates/management.json.j2
Normal 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"
|
||||
]
|
||||
}
|
||||
}
|
||||
8
ansible/netbird/templates/relay.env.j2
Normal file
8
ansible/netbird/templates/relay.env.j2
Normal 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 }}
|
||||
15
ansible/netbird/templates/turnserver.conf.j2
Normal file
15
ansible/netbird/templates/turnserver.conf.j2
Normal 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
|
||||
Reference in New Issue
Block a user