added gitea for PoC

This commit is contained in:
Prox 2026-03-06 17:11:28 +02:00
parent ded80f5a4b
commit 53433dc3de
8 changed files with 235 additions and 65 deletions

View File

@ -5,12 +5,13 @@ testing of the NetBird GitOps reconciler.
## Stack overview
| Component | Purpose |
| ---------- | ------------------------------------------- |
| Caddy | TLS termination, reverse proxy |
| NetBird | Management, Signal, Relay, Dashboard, TURN |
| Reconciler | Declarative config → NetBird API reconciler |
| Gitea | Local Git server (optional, off by default) |
| Component | Purpose |
| --------------- | ------------------------------------------- |
| Caddy | TLS termination, reverse proxy |
| NetBird | Management, Signal, Relay, Dashboard, TURN |
| Reconciler | Declarative config → NetBird API reconciler |
| Gitea | Git server for GitOps source-of-truth |
| Gitea Runner | Executes CI workflows (Actions) |
All services run as Docker containers on a single VPS, connected via a `netbird`
Docker bridge network. Caddy handles ACME certificates automatically.
@ -18,7 +19,9 @@ Docker bridge network. Caddy handles ACME certificates automatically.
## Prerequisites
- SSH access to `46.225.220.61` (root, key-based)
- DNS A record: `vps-a.networkmonitor.cc``46.225.220.61`
- DNS A records:
- `vps-a.networkmonitor.cc``46.225.220.61`
- `gitea.vps-a.networkmonitor.cc``46.225.220.61`
- `rsync` installed locally (used to sync reconciler source)
- Ansible 2.15+ with `community.general` and `ansible.posix` collections
@ -28,22 +31,16 @@ Install collections if needed:
ansible-galaxy collection install community.general ansible.posix
```
## Setup
## Deployment (multi-phase)
### 1. Create vault file
The deployment is intentionally multi-phase because some tokens can only be
obtained after services are running.
### Phase 1: Initial deploy
```bash
cd poc/ansible
cp group_vars/all/vault.yml.example group_vars/all/vault.yml
```
For the first deploy, leave all values as empty strings — the playbook
auto-generates NetBird secrets and a reconciler token.
### 2. Deploy
```bash
cd poc/ansible
ansible-playbook -i inventory.yml playbook.yml
```
@ -51,48 +48,61 @@ The playbook will:
1. Generate secrets (encryption key, TURN password, relay secret, reconciler
token)
2. Install Docker if not present
3. Configure UFW firewall
4. Rsync the reconciler source code to VPS-A
5. Template all config files
6. Build the reconciler Docker image on VPS-A
7. Pull NetBird/Gitea images and start all services
8. Run health checks and print a summary with generated secrets
2. Install Docker, configure UFW
3. Rsync the reconciler source code and build the Docker image
4. Template configs and start all services
5. Skip the Gitea Actions runner (no token yet)
6. Print a summary with generated secrets
**Save the generated secrets** printed at the end into `vault.yml` so subsequent
runs are idempotent.
**Save the generated secrets** into `vault.yml` so subsequent runs are
idempotent.
### 3. Create NetBird admin + API token
### Phase 2: Create NetBird admin + API token
1. Open `https://vps-a.networkmonitor.cc` in a browser
2. Create the first admin account (embedded IdP — no external OAuth)
3. Go to **Settings → Personal Access Tokens → Generate**
4. Copy the token into `vault.yml` as `vault_netbird_api_token`
5. Re-run the playbook:
### Phase 3: Set up Gitea
1. Open `https://gitea.vps-a.networkmonitor.cc` and complete the install wizard
2. Create an admin account (user: `blastpilot`)
3. Create org `BlastPilot` and repo `netbird-gitops`
4. Generate a Gitea API token (**Settings → Applications**) → `vault_gitea_token`
5. Go to **Site Administration → Actions → Runners** → copy runner registration
token → `vault_gitea_admin_password` and `vault_gitea_runner_token`
### Phase 4: Re-deploy with all tokens
```bash
ansible-playbook -i inventory.yml playbook.yml
```
The reconciler will now start successfully with a valid API token.
This run will:
- Start the reconciler with a valid NetBird API token
- Register and start the Gitea Actions runner
- Wire the reconciler to poll Gitea for `netbird.json` changes
### 4. (Optional) Enable Gitea
### Phase 5: Push code and test CI
To enable Gitea-backed GitOps polling:
```bash
cd /path/to/netbird-gitops
git remote add poc git@gitea.vps-a.networkmonitor.cc:BlastPilot/netbird-gitops.git
git push poc main
```
1. Open `http://vps-a.networkmonitor.cc:3000` and complete the install wizard
2. Create an admin account (user: `blastpilot`)
3. Create org `BlastPilot` and repo `netbird-gitops`
4. Push `netbird.json` to the repo
5. Generate a Gitea API token (Settings → Applications)
6. In `vars.yml`, set `gitea_enabled: "true"`
7. In `vault.yml`, fill in `vault_gitea_token` and `vault_gitea_admin_password`
8. Re-run the playbook
Then configure Gitea repo secrets (Settings → Actions → Secrets):
- `RECONCILER_TOKEN` — the reconciler bearer token
- `RECONCILER_URL``https://vps-a.networkmonitor.cc/reconciler`
- `GITEA_TOKEN` — same Gitea API token
Create a branch, modify `netbird.json`, open a PR — the dry-run workflow should
post a plan as a PR comment.
## Testing
All commands below assume you have the reconciler token. Replace `<TOKEN>` with
the value of `vault_reconciler_token`.
Replace `<TOKEN>` with `vault_reconciler_token`.
### Health check
@ -127,14 +137,6 @@ curl -H "Authorization: Bearer <TOKEN>" \
'https://vps-a.networkmonitor.cc/reconciler/export'
```
### Enroll a peer
Use a setup key from the reconcile response (`created_keys` field):
```bash
sudo netbird up --management-url https://vps-a.networkmonitor.cc --setup-key <KEY>
```
## Teardown
Remove all containers and volumes:
@ -143,6 +145,12 @@ Remove all containers and volumes:
ssh root@46.225.220.61 "cd /opt/netbird-poc && docker compose down -v"
```
Stop the runner:
```bash
ssh root@46.225.220.61 "systemctl stop gitea-runner && systemctl disable gitea-runner"
```
## File structure
```
@ -158,7 +166,7 @@ poc/
templates/
docker-compose.yml.j2 # All services (NetBird + Gitea + Reconciler)
management.json.j2 # NetBird management config
Caddyfile.j2 # Caddy reverse proxy with reconciler route
Caddyfile.j2 # Caddy reverse proxy (NetBird + Gitea)
dashboard.env.j2 # NetBird dashboard env
relay.env.j2 # NetBird relay env
turnserver.conf.j2 # TURN server config

View File

@ -12,15 +12,21 @@ coturn_version: "4.8.0-r0"
# --- Reconciler ---
reconciler_port: 8080
# --- Gitea (standalone mode by default) ---
# String "false" because it goes into an env var verbatim.
gitea_enabled: "false"
# --- Gitea ---
gitea_enabled: "true"
gitea_version: "1.23"
gitea_domain: "gitea.vps-a.networkmonitor.cc"
gitea_http_port: 3000
gitea_ssh_port: 2222
gitea_admin_user: "blastpilot"
gitea_org_name: "BlastPilot"
gitea_repo_name: "netbird-gitops"
# --- Gitea Actions Runner ---
gitea_runner_version: "0.2.11"
gitea_runner_dir: "/opt/gitea-runner"
gitea_runner_name: "poc-runner"
gitea_runner_labels: "ubuntu-latest:docker://node:20-bookworm,ubuntu-22.04:docker://ubuntu:22.04"
# --- Paths ---
base_dir: /opt/netbird-poc

View File

@ -18,3 +18,7 @@ vault_netbird_api_token: ""
# Gitea API token (created via Gitea UI after first deploy):
vault_gitea_token: ""
# Gitea Actions runner registration token
# (get from Gitea: Site Administration → Actions → Runners):
vault_gitea_runner_token: ""

View File

@ -166,10 +166,10 @@
port: "3478"
proto: tcp
- name: Allow Gitea HTTP
- name: Allow Gitea SSH
community.general.ufw:
rule: allow
port: "{{ gitea_http_port | string }}"
port: "{{ gitea_ssh_port | string }}"
proto: tcp
- name: Enable UFW (default deny incoming)
@ -327,7 +327,88 @@
changed_when: false
# =========================================================================
# 9. Summary
# 9. Gitea Actions Runner
# =========================================================================
# The runner needs Gitea to be up and a registration token.
# On first deploy, skip this (vault_gitea_runner_token is empty).
# After Gitea is running, get the token from Site Administration →
# Actions → Runners, add it to vault.yml, and re-run.
- name: Create runner directory
ansible.builtin.file:
path: "{{ gitea_runner_dir }}"
state: directory
mode: "0755"
when: vault_gitea_runner_token | default('') | length > 0
- name: Download act_runner binary
ansible.builtin.get_url:
url: "https://gitea.com/gitea/act_runner/releases/download/v{{ gitea_runner_version }}/act_runner-{{ gitea_runner_version }}-linux-amd64"
dest: "{{ gitea_runner_dir }}/act_runner"
mode: "0755"
when: vault_gitea_runner_token | default('') | length > 0
- name: Check if runner is already registered
ansible.builtin.stat:
path: "{{ gitea_runner_dir }}/.runner"
register: _runner_config
when: vault_gitea_runner_token | default('') | length > 0
- name: Register runner with Gitea
ansible.builtin.command:
cmd: >-
{{ gitea_runner_dir }}/act_runner register
--instance https://{{ gitea_domain }}
--token {{ vault_gitea_runner_token }}
--name {{ gitea_runner_name }}
--labels {{ gitea_runner_labels }}
--no-interactive
chdir: "{{ gitea_runner_dir }}"
when:
- vault_gitea_runner_token | default('') | length > 0
- not (_runner_config.stat.exists | default(false))
- name: Create systemd service for runner
ansible.builtin.copy:
dest: /etc/systemd/system/gitea-runner.service
mode: "0644"
content: |
[Unit]
Description=Gitea Actions Runner
After=network.target docker.service
Requires=docker.service
[Service]
Type=simple
User=root
WorkingDirectory={{ gitea_runner_dir }}
ExecStart={{ gitea_runner_dir }}/act_runner daemon
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target
when: vault_gitea_runner_token | default('') | length > 0
- name: Start and enable runner service
ansible.builtin.systemd:
name: gitea-runner
daemon_reload: true
state: started
enabled: true
when: vault_gitea_runner_token | default('') | length > 0
- name: Skip runner (no token provided)
ansible.builtin.debug:
msg: >-
Skipping Gitea Actions runner — vault_gitea_runner_token is empty.
After Gitea is running, get the token from
https://{{ gitea_domain }}/-/admin/actions/runners
and add it to vault.yml.
when: vault_gitea_runner_token | default('') | length == 0
# =========================================================================
# 10. Summary
# =========================================================================
- name: Note about NetBird API token
@ -343,11 +424,12 @@
ansible.builtin.debug:
msg: |
============================================================
NetBird + Reconciler PoC deployed on VPS-A
NetBird + Reconciler + Gitea PoC deployed on VPS-A
============================================================
Dashboard: https://{{ netbird_domain }}
Gitea: http://{{ netbird_domain }}:{{ gitea_http_port }}
Gitea: https://{{ gitea_domain }}
Gitea SSH: ssh://git@{{ gitea_domain }}:{{ gitea_ssh_port }}
Reconciler: https://{{ netbird_domain }}/reconciler/health
Reconciler status: {{ 'healthy' if (_reconciler_check.status | default(0)) == 200 else 'NOT YET READY (see note above)' }}
@ -362,5 +444,8 @@
1. Open the dashboard and create an admin account
2. Go to Settings > API > generate a Personal Access Token
3. Put the token in vault.yml as vault_netbird_api_token
4. Re-run: ansible-playbook -i inventory.yml playbook.yml
4. Open Gitea, complete install wizard, create org + repo
5. Go to Site Administration > Actions > Runners, copy token
6. Put the token in vault.yml as vault_gitea_runner_token
7. Re-run: ansible-playbook -i inventory.yml playbook.yml
============================================================

View File

@ -44,3 +44,11 @@
# NetBird Dashboard (catch-all must be last)
reverse_proxy /* dashboard:80
}
# =============================================================================
# Gitea
# =============================================================================
{{ gitea_domain }} {
import security_headers
reverse_proxy gitea:{{ gitea_http_port }}
}

View File

@ -96,13 +96,14 @@ services:
networks:
- netbird
environment:
- GITEA__server__DOMAIN={{ netbird_domain }}
- GITEA__server__ROOT_URL=http://{{ netbird_domain }}:{{ gitea_http_port }}
- GITEA__server__DOMAIN={{ gitea_domain }}
- GITEA__server__ROOT_URL=https://{{ gitea_domain }}
- GITEA__server__SSH_DOMAIN={{ gitea_domain }}
- GITEA__database__DB_TYPE=sqlite3
- GITEA__actions__ENABLED=true
volumes:
- gitea_data:/data
ports:
- "{{ gitea_http_port }}:3000"
- "{{ gitea_ssh_port }}:22"
logging:
driver: json-file

View File

@ -2,11 +2,9 @@ NETBIRD_API_URL=http://management:80/api
NETBIRD_API_TOKEN={{ vault_netbird_api_token }}
RECONCILER_TOKEN={{ vault_reconciler_token }}
GITEA_ENABLED={{ gitea_enabled }}
{% if gitea_enabled == "true" %}
GITEA_URL=http://gitea:{{ gitea_http_port }}
GITEA_TOKEN={{ vault_gitea_token }}
GITEA_REPO={{ gitea_org_name }}/{{ gitea_repo_name }}
{% endif %}
POLL_INTERVAL_SECONDS=30
PORT={{ reconciler_port }}
DATA_DIR=/data

60
state/test.json Normal file
View File

@ -0,0 +1,60 @@
{
"groups": {
"ground-stations": {
"peers": []
},
"pilots": {
"peers": []
}
},
"setup_keys": {
"GS-TestHawk-1": {
"type": "one-off",
"expires_in": 604800,
"usage_limit": 1,
"auto_groups": [
"ground-stations"
],
"enrolled": false
},
"Pilot-TestHawk-1": {
"type": "one-off",
"expires_in": 604800,
"usage_limit": 1,
"auto_groups": [
"pilots"
],
"enrolled": false
}
},
"policies": {
"pilots-to-gs": {
"description": "",
"enabled": true,
"sources": [
"pilots"
],
"destinations": [
"ground-stations"
],
"bidirectional": true,
"protocol": "all",
"action": "accept",
"source_posture_checks": []
}
},
"posture_checks": {},
"networks": {},
"peers": {},
"users": {
"admin@example.com": {
"name": "admin",
"role": "owner",
"auto_groups": []
}
},
"routes": {},
"dns": {
"nameserver_groups": {}
}
}