79 Commits

Author SHA1 Message Date
datadunia eb525879f1 fix: test build
Beta Release / deploy (push) Successful in 1m2s
2026-05-09 15:19:03 +07:00
datadunia b1c7f10fec fix: test build
Beta Release / deploy (push) Successful in 1m4s
2026-05-09 14:41:26 +07:00
datadunia da09fb2e65 fix: test build
Beta Release / deploy (push) Successful in 1m3s
2026-05-09 14:34:26 +07:00
datadunia fefaf49298 fix: test build
Beta Release / deploy (push) Successful in 1m1s
2026-05-09 13:50:03 +07:00
datadunia fc05ac0725 fix: test build
Beta Release / deploy (push) Failing after 20s
2026-05-09 05:54:28 +07:00
datadunia 5bcac141d1 fix: test build
Beta Release / deploy (push) Successful in 1m4s
2026-05-09 03:48:48 +07:00
datadunia c7ad30d2df fix: test build
Beta Release / deploy (push) Successful in 1m4s
2026-05-09 02:42:52 +07:00
datadunia 93f6aaea76 feat: check and delete existing release before creating a new one
Beta Release / deploy (push) Successful in 1m5s
2026-05-09 01:57:11 +07:00
datadunia 7a448ffa3a fix: test build
Beta Release / deploy (push) Successful in 58s
2026-05-09 01:47:14 +07:00
datadunia 89443f9481 fix: test build
Beta Release / deploy (push) Successful in 49s
2026-05-09 01:27:45 +07:00
datadunia c5d3651a96 fix: test build
Beta Release / deploy (push) Failing after 1m0s
2026-05-09 01:22:33 +07:00
datadunia 548e3949ad feat: test build
Beta Release / deploy (push) Failing after 35s
2026-05-09 01:00:59 +07:00
datadunia 4b8d5008d6 refactor: extract shared CI steps to deploy_call.yaml reusable workflow
Beta Release / deploy (push) Successful in 50s
Release / deploy (push) Successful in 59s
2026-05-08 05:51:48 +07:00
datadunia 25e0cfd15e refactor: extract shared CI steps to deploy_call.yaml reusable workflow
Release / deploy (push) Has been cancelled
Beta Release / deploy (push) Has been cancelled
2026-05-08 05:48:02 +07:00
datadunia 9c771f098c refactor: extract shared CI steps to deploy_call.yaml reusable workflow
Beta Release / deploy (push) Successful in 59s
Release / deploy (push) Failing after 56s
2026-05-08 05:41:06 +07:00
datadunia dfc8d9ed57 fix: restore Create Release step with correct Swagger-compliant JSON and auth header
Release / build-and-release (push) Successful in 1m0s
2026-05-08 05:04:54 +07:00
datadunia 565d7f1a35 fix: restore Create Release step with correct Swagger-compliant JSON and auth header
Release / build-and-release (push) Failing after 54s
2026-05-08 05:00:55 +07:00
datadunia 45c7788d1d fix: restore Create Release step with correct Swagger-compliant JSON and auth header
Release / build-and-release (push) Failing after 52s
2026-05-08 04:55:29 +07:00
datadunia b9fbe1430f fix: use printf for JSON body and Authorization header for Gitea API
Release / build-and-release (push) Failing after 53s
2026-05-08 04:30:35 +07:00
datadunia 5eec7651da fix: use printf for JSON body and Authorization header for Gitea API
Release / build-and-release (push) Failing after 53s
2026-05-08 04:26:27 +07:00
datadunia c8cf771bfa fix: use printf for JSON body and Authorization header for Gitea API
Release / build-and-release (push) Failing after 59s
2026-05-08 04:22:21 +07:00
datadunia ba7b9ac64c fix: use printf for JSON body and Authorization header for Gitea API
Release / build-and-release (push) Failing after 53s
2026-05-08 04:19:51 +07:00
datadunia c42683e968 fix: use printf for JSON body and Authorization header for Gitea API
Release / build-and-release (push) Failing after 56s
2026-05-08 04:14:57 +07:00
datadunia 4a3d099ff9 fix: use printf for JSON body and Authorization header for Gitea API
Release / build-and-release (push) Failing after 56s
2026-05-08 04:03:52 +07:00
datadunia 64e8372fc1 fix: use token query param and form-data upload per Gitea API spec
Release / build-and-release (push) Failing after 58s
2026-05-08 03:17:40 +07:00
datadunia d7b1de1885 fix: use curl -u x-token auth for Gitea API
Release / build-and-release (push) Failing after 53s
2026-05-08 03:10:03 +07:00
datadunia 7d0c0c18d0 fix: use curl for Gitea release API instead of broken action
Release / build-and-release (push) Failing after 55s
2026-05-08 03:02:30 +07:00
datadunia 8d237b293f fix: Action TOKEN
Release / build-and-release (push) Failing after 55s
2026-05-08 02:49:43 +07:00
datadunia 43477a3333 fix: install jq if missing, restore jq for changelog in latest.json
Release / build-and-release (push) Failing after 1m10s
2026-05-08 02:20:55 +07:00
datadunia 42a35302b3 fix: pass token via env GITHUB_TOKEN and GITEA_TOKEN for release action
Release / build-and-release (push) Failing after 54s
2026-05-08 02:18:01 +07:00
datadunia 6d9fa80b54 update app submodule
Release / build-and-release (push) Failing after 55s
2026-05-08 02:05:13 +07:00
datadunia a59ebd4813 fix: Action TOKEN 2026-05-08 02:01:46 +07:00
datadunia 0a7bf72d6c fix: use akkuman/gitea-release-action from GitHub with server_url
Release / build-and-release (push) Failing after 56s
2026-05-08 01:52:48 +07:00
datadunia 861d910363 fix: use gitea-release-action for native Gitea release support
Release / build-and-release (push) Failing after 4s
2026-05-08 01:47:49 +07:00
datadunia d0d529450c fix: use softprops/action-gh-release and add cleanup step
Release / build-and-release (push) Failing after 1m16s
2026-05-08 01:35:05 +07:00
datadunia 0c6bac4c22 fix: use tag refs instead of commit SHA for all actions
Release / build (amd64) (push) Successful in 51s
Release / build (arm64) (push) Successful in 44s
2026-05-08 00:17:23 +07:00
datadunia 7b02527200 fix: downgrade upload-artifact v4 to v3 for Gitea compatibility
Release / build (amd64) (push) Failing after 56s
Release / build (arm64) (push) Failing after 4s
2026-05-08 00:06:16 +07:00
datadunia fa4bf318b6 chore: comment out debug submodule step
Release / build (amd64) (push) Failing after 41s
Release / build (arm64) (push) Failing after 37s
2026-05-07 23:19:56 +07:00
datadunia cdd22860b3 fix: remove generate-latest-json job, update app submodule
Release / build (amd64) (push) Failing after 2m23s
Release / build (arm64) (push) Failing after 1m3s
2026-05-07 22:30:03 +07:00
datadunia bbae401002 fix: Action TOKEN
Release / build (amd64) (push) Failing after 30s
Release / build (arm64) (push) Failing after 23s
Release / generate-latest-json (push) Has been skipped
2026-05-07 14:21:50 +07:00
datadunia fb198f64bb fix: Action TOKEN
Release / build (amd64) (push) Failing after 5s
Release / build (arm64) (push) Failing after 5s
Release / generate-latest-json (push) Has been skipped
2026-05-07 14:18:52 +07:00
datadunia d62936d607 fix: Action TOKEN
Release / build (amd64) (push) Failing after 6s
Release / build (arm64) (push) Failing after 5s
Release / generate-latest-json (push) Has been skipped
2026-05-07 14:16:54 +07:00
datadunia 3b0f478d3a fix: Action TOKEN
Release / build (amd64) (push) Failing after 6s
Release / build (arm64) (push) Failing after 6s
Release / generate-latest-json (push) Has been skipped
2026-05-07 14:14:10 +07:00
datadunia b39d7a4bd2 fix: Action TOKEN
Release / build (amd64) (push) Failing after 6s
Release / build (arm64) (push) Failing after 5s
Release / generate-latest-json (push) Has been skipped
2026-05-07 14:07:54 +07:00
datadunia 81af99dfc4 fix: Action TOKEN
Release / build (amd64) (push) Failing after 6s
Release / build (arm64) (push) Failing after 5s
Release / generate-latest-json (push) Has been skipped
2026-05-07 14:05:04 +07:00
datadunia 139463855d fix: Action TOKEN
Release / build (amd64) (push) Failing after 6s
Release / build (arm64) (push) Failing after 6s
Release / generate-latest-json (push) Has been skipped
2026-05-07 14:02:45 +07:00
datadunia 309a3fea8f fix: Action TOKEN
Release / build (amd64) (push) Failing after 7s
Release / build (arm64) (push) Failing after 6s
Release / generate-latest-json (push) Has been skipped
2026-05-07 13:52:59 +07:00
datadunia 1fdb8135db fix: shallow
Release / build (amd64) (push) Failing after 7s
Release / build (arm64) (push) Failing after 6s
Release / generate-latest-json (push) Has been skipped
2026-05-07 13:45:26 +07:00
datadunia 22b9133431 fix: shallow
Release / build (amd64) (push) Failing after 6s
Release / build (arm64) (push) Failing after 5s
Release / generate-latest-json (push) Has been skipped
2026-05-07 13:43:36 +07:00
datadunia 6e9d389cbf fix: shallow
Release / build (amd64) (push) Failing after 5s
Release / build (arm64) (push) Failing after 5s
Release / generate-latest-json (push) Has been skipped
2026-05-07 12:53:35 +07:00
datadunia 644c6e2dde fix: shallow 2026-05-07 12:51:55 +07:00
datadunia 1716747c09 fix: shallow
Release / build (amd64) (push) Failing after 6s
Release / build (arm64) (push) Failing after 6s
Release / generate-latest-json (push) Has been skipped
2026-05-07 12:48:27 +07:00
datadunia 47abc63658 fix: shallow
Release / build (arm64) (push) Has been cancelled
Release / generate-latest-json (push) Has been cancelled
Release / build (amd64) (push) Has been cancelled
2026-05-07 11:41:42 +07:00
datadunia 44c480a89a fix: shallow
Release / build (arm64) (push) Has been cancelled
Release / generate-latest-json (push) Has been cancelled
Release / build (amd64) (push) Has been cancelled
2026-05-07 11:10:14 +07:00
datadunia 35dcc2f15f fix: shallow 2026-05-07 11:08:53 +07:00
datadunia f71ef8c3e4 fix: shallow
Release / build (arm64) (push) Has been cancelled
Release / generate-latest-json (push) Has been cancelled
Release / build (amd64) (push) Has been cancelled
2026-05-07 06:23:02 +07:00
datadunia 6308017b24 fix: shallow
Release / build (amd64) (push) Failing after 4s
Release / build (arm64) (push) Failing after 4s
Release / generate-latest-json (push) Has been skipped
2026-05-07 06:20:45 +07:00
datadunia 5b4f91e6e0 fix: shallow
Release / build (amd64) (push) Failing after 5s
Release / build (arm64) (push) Failing after 5s
Release / generate-latest-json (push) Has been skipped
2026-05-07 06:16:39 +07:00
datadunia f6af1fb6d8 fix: shallow 2026-05-07 05:16:39 +07:00
datadunia f47b50b857 fix: shallow
Release / build (amd64) (push) Failing after 5s
Release / build (arm64) (push) Failing after 4s
Release / generate-latest-json (push) Has been skipped
2026-05-07 05:10:19 +07:00
datadunia e2c807dc12 fix: shallow
Release / build (amd64) (push) Failing after 13s
Release / build (arm64) (push) Failing after 9s
2026-05-07 04:35:06 +07:00
datadunia 144be82e9b fix: shallow
Release / build (arm64) (push) Has been cancelled
Release / build (amd64) (push) Has been cancelled
2026-05-07 04:32:28 +07:00
datadunia 0feed113ac fix: shallow
Release / build (arm64) (push) Has been cancelled
Release / generate-latest-json (push) Has been cancelled
Release / build (amd64) (push) Has been cancelled
2026-05-07 04:19:49 +07:00
datadunia 20478191a3 fix: shallow
Release / build (amd64) (push) Failing after 8s
Release / build (arm64) (push) Failing after 6s
Release / generate-latest-json (push) Has been skipped
2026-05-07 02:17:39 +07:00
datadunia 32f1e78ee1 fix: shallow
Release / build (amd64) (push) Failing after 5s
Release / build (arm64) (push) Failing after 6s
Release / generate-latest-json (push) Has been skipped
2026-05-07 02:16:24 +07:00
datadunia ac32038ebb fix: shallow clone with timeout to prevent hang
Release / build (amd64) (push) Failing after 8s
Release / build (arm64) (push) Failing after 6s
Release / generate-latest-json (push) Has been skipped
2026-05-07 02:14:26 +07:00
datadunia 417fd99dbf fix: use repo-local http.extraHeader for submodule auth
Release / build (arm64) (push) Has been cancelled
Release / generate-latest-json (push) Has been cancelled
Release / build (amd64) (push) Has been cancelled
2026-05-07 01:52:21 +07:00
datadunia 1633fede7a fix: manual clone with http.extraheader reset for host mode
Release / build (amd64) (push) Failing after 6s
Release / build (arm64) (push) Failing after 4s
Release / generate-latest-json (push) Has been skipped
2026-05-07 01:12:45 +07:00
datadunia 14742b4c27 fix: use submodules: true for checkout
Release / build (amd64) (push) Failing after 33s
Release / build (arm64) (push) Failing after 47s
Release / generate-latest-json (push) Has been skipped
2026-05-06 23:43:51 +07:00
datadunia c49964d4d4 fix: use SSH for submodule auth 2026-05-06 17:52:25 +07:00
datadunia 4c00af7c44 fix: checkout with submodules false + manual auth
Release / build (amd64) (push) Failing after 41s
Release / build (arm64) (push) Failing after 33s
Release / generate-latest-json (push) Has been skipped
2026-05-06 17:33:20 +07:00
datadunia de21d422b6 fix: use git pull + submodule update instead of actions/checkout
Release / build (amd64) (push) Failing after 4s
Release / build (arm64) (push) Failing after 4s
Release / generate-latest-json (push) Has been skipped
2026-05-06 17:20:49 +07:00
datadunia 581350971a fix: use secrets for submodule auth
Release / build (amd64) (push) Failing after 7s
Release / build (arm64) (push) Failing after 6s
Release / generate-latest-json (push) Has been skipped
2026-05-06 16:40:09 +07:00
datadunia 8a8927c8e8 fix: submodule checkout with token auth
Release / build (amd64) (push) Failing after 7s
Release / generate-latest-json (push) Has been skipped
Release / build (arm64) (push) Failing after 6s
2026-05-06 16:21:55 +07:00
datadunia 282b2a3b69 fix: properly initialize submodules in workflow
Release / build (amd64) (push) Failing after 7s
Release / build (arm64) (push) Failing after 7s
Release / generate-latest-json (push) Has been skipped
2026-05-06 16:15:16 +07:00
datadunia fc1e5513aa workflow fix 2026-05-06 16:05:05 +07:00
datadunia 481807769e feat: add Gitea CI/CD workflows and update documentation
Release / build (amd64) (push) Failing after 10s
Release / build (arm64) (push) Failing after 11s
Release / generate-latest-json (push) Has been skipped
- Add .gitea/workflows/release.yaml for multi-arch build (amd64, arm64)
- Generate latest.json for auto-update system
- Update AGENTS.md and README.md for WGRplane integration
- Update app/ submodule reference to latest commit (2db8e5a)
2026-05-06 05:27:58 +07:00
datadunia 52e629ac02 docs: update AGENTS.md and README.md for WGRplane integration
- Add WGRplane component documentation to AGENTS.md
- Update README.md with combined WireGuard Policy + WGRplane docs
- Fix .gitignore (remove .gitea/ from ignore)
2026-05-06 05:23:00 +07:00
datadunia b3676b87cb feat: update WGRplane submodule to latest version
- Update app/ submodule to commit ff80d9c
- Complete WGRplane with Vue.js frontend and Go backend
- Includes Glassmorphism UI, webhooks, i18n, and plugins
2026-05-05 03:35:52 +07:00
7 changed files with 526 additions and 26 deletions
+15
View File
@@ -0,0 +1,15 @@
name: Beta Release
on:
push:
tags:
- 'v*-beta*'
- 'v*-test*'
jobs:
deploy:
if: "contains(github.ref_name, 'beta') || contains(github.ref_name, 'test')"
uses: ./.gitea/workflows/deploy_call.yaml
with:
prerelease: true
secrets: inherit
+136
View File
@@ -0,0 +1,136 @@
name: Deploy
on:
workflow_call:
inputs:
prerelease:
description: 'Mark as prerelease'
required: false
type: boolean
default: false
permissions:
contents: write
jobs:
build-and-release:
runs-on: ubuntu-latest
steps:
- name: Clone repository with submodules
run: |
git config --global --remove-section http || true
git config --global --unset-all core.askPass || true
TOKEN="${{ secrets.BUILD_TOKEN }}"
git clone --recurse-submodules \
-c credential.helper="" \
https://token:$TOKEN@git.datadunia.com/devops/wireguard-vpn.git .
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: '1.22'
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Build Vue frontend
run: |
cd app/frontend
npm install
npm run build
- name: Build Go binaries
run: |
cd app
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o ../wgrplane-linux-amd64 .
CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -o ../wgrplane-linux-arm64 .
- name: Generate latest.json
run: |
VERSION="${{ gitea.ref_name }}"
RELEASE_DATE="$(date -u +'%Y-%m-%dT%H:%M:%SZ')"
REPO="${{ gitea.repository }}"
SERVER="${{ gitea.server_url }}"
cat > latest.json << ENDJSON
{
"version": "$VERSION",
"release_date": "$RELEASE_DATE",
"download_urls": {
"amd64": "${SERVER}/${REPO}/releases/download/${VERSION}/wgrplane-linux-amd64",
"arm64": "${SERVER}/${REPO}/releases/download/${VERSION}/wgrplane-linux-arm64"
}
}
ENDJSON
- name: Create Release and upload assets
env:
TOKEN: ${{ secrets.BUILD_TOKEN }}
run: |
if [ -z "$TOKEN" ]; then
echo "Value: [EMPTY]"
exit 1
else
echo "Length: ${#TOKEN} characters"
fi
REPO="${{ gitea.repository }}"
TAG="${{ gitea.ref_name }}"
API="${{ gitea.server_url }}/api/v1"
# 0. Check & Delete Existing Release
echo "=== 0. Check & Delete Existing Release ==="
EXISTING_RESP=$(curl -s -H "Authorization: token $TOKEN" "$API/repos/$REPO/releases/tags/$TAG")
EXISTING_ID=$(echo "$EXISTING_RESP" | grep -o '"id":[0-9]*' | head -n 1 | cut -d':' -f2 || true)
if [ -n "$EXISTING_ID" ] && [ "$EXISTING_ID" != "null" ]; then
echo "⚠️ Found existing release for tag $TAG with ID: $EXISTING_ID. Deleting..."
DELETE_RESP=$(curl -s -w "\n%{http_code}" -X DELETE -H "Authorization: token $TOKEN" "$API/repos/$REPO/releases/$EXISTING_ID")
echo "✅ Delete response: $DELETE_RESP"
else
echo "No existing release found for $TAG. Proceeding..."
fi
# 1. Create Release
echo "=== 1. Create New Release ==="
JSON_BODY=$(printf '{"tag_name":"%s","name":"%s","body":"Release %s","draft":false,"prerelease":%s}' "$TAG" "$TAG" "$TAG" "${{ inputs.prerelease }}")
RELEASE_RESP=$(curl -s -X POST \
-H "Authorization: token $TOKEN" \
-H "Content-Type: application/json" \
-d "$JSON_BODY" \
"$API/repos/$REPO/releases")
# Ambil ID dengan lebih teliti
# Tambahkan || true agar grep tidak membuat script crash (karena set -e) jika id tidak ditemukan
RELEASE_ID=$(echo "$RELEASE_RESP" | grep -o '"id":[0-9]*' | head -n 1 | cut -d':' -f2 || true)
if [ -z "$RELEASE_ID" ] || [ "$RELEASE_ID" = "null" ]; then
echo "Gagal membuat release. Response: $RELEASE_RESP"
exit 1
fi
echo "Release ID: $RELEASE_ID"
# 2. Upload Assets
for FILE in wgrplane-linux-amd64 wgrplane-linux-arm64 latest.json; do
if [ -f "$FILE" ]; then
echo "Uploading $FILE..."
curl -s -X POST \
-H "Authorization: token $TOKEN" \
-F "attachment=@$FILE" \
"$API/repos/$REPO/releases/$RELEASE_ID/assets?name=$FILE"
else
echo "Skip $FILE (tidak ditemukan)"
fi
done
- name: Cleanup build artifacts
if: always()
run: |
git config --global --remove-section http || true
git config --global --unset-all core.askPass || true
rm -f wgrplane-linux-amd64 wgrplane-linux-arm64 latest.json
rm -rf app/frontend/node_modules app/frontend/dist
echo "Cleanup done"
+14
View File
@@ -0,0 +1,14 @@
name: Release
on:
push:
tags:
- 'v[0-9]*.[0-9]*.[0-9]'
jobs:
deploy:
if: "!contains(github.ref_name, 'beta') && !contains(github.ref_name, 'test')"
uses: ./.gitea/workflows/deploy_call.yaml
with:
prerelease: false
secrets: inherit
+1
View File
@@ -1 +1,2 @@
.test/
.sisyphus/
+111 -22
View File
@@ -1,27 +1,116 @@
# WireGuard Policy Firewall (`03.wireguard-policy`)
# AGENTS.md -- WireGuard Policy Firewall + WGRplane
## Architecture & Configuration Flow
- **Goal:** Dynamic iptables/ipset rules based on WireGuard configuration (`wg0.conf`).
- **Data Flow:** `wg0.conf` -> `wg-sync-policy.sh` -> `policy.json` -> `wg-policy-engine.sh` -> `iptables`/`ipset`
- **File Watcher:** `wg-sync-watch.sh` monitors `wg0.conf` via `inotifywait` and debounces changes to re-run the sync and engine.
**Generated:** 2026-05-08
**Project:** 03.wireguard-policy
## Critical Parsing Rules & Design Constraints
- **Target IPs Parsing (`#Access`):** The firewall script uses the custom `#Access` comment in `wg0.conf` to define egress/firewall whitelists for clients.
- **Why `#Access` is mandatory:** WireGuard's native `AllowedIPs` on a Server dictates *routing* towards the client. If we put target destinations in the Server's `AllowedIPs`, the Server would wrongly route traffic destined for those IPs *into* the client tunnel. Therefore, a custom `#Access` comment is the only correct way to define firewall whitelist destinations without breaking WireGuard's Cryptokey Routing.
- **Do not remove `#Access`:** Future agents MUST NOT attempt to refactor the script to parse targets from `AllowedIPs`. It is architecturally incorrect for this use case.
## OVERVIEW
Hybrid project: (1) WireGuard Policy Firewall -- shell script iptables/ipset engine driven by `wg0.conf` `#Access` comments. (2) WGRplane -- Go binary (port 10087) serving REST API + Vue 3 SPA for WireGuard control plane management.
## Testing & Verifying
- `wg-policy-ctl status`: Check the overall health, including interface status, JSON validity, lock files, and iptables rules counts.
- `wg-policy-ctl validate`: Validates `policy.json` without applying.
- `wg-policy-ctl rules`: View the applied iptables rules in the active chain (`WG_POLICY`).
- `wg-policy-ctl reload`: Forces a re-sync from `wg0.conf` and re-applies iptables.
## STRUCTURE
```
03.wireguard-policy/
├── app/ # WGRplane: Go backend + Vue 3 frontend (see app/AGENTS.md)
│ ├── *.go # 13 Go files, flat package (no subdirs)
│ ├── frontend/ # Vue 3 SPA (see app/frontend/src/AGENTS.md)
│ └── docs/ # Swagger auto-generated (DO NOT EDIT)
├── wg-sync-policy.sh # Parses wg0.conf → policy.json (atomic write + flock)
├── wg-policy-engine.sh # Reads policy.json → iptables/ipset WG_POLICY chain
├── wg-sync-watch.sh # inotifywait daemon, debounces wg0.conf changes
├── wg-policy-lib.sh # Shared shell library (source only, never execute directly)
├── wg-policy-ctl # CLI wrapper for operator use
├── wg-policy-cleanup.sh # PostDown cleanup (run by WireGuard)
├── *.service / *.timer # systemd units for daemon + health check
├── install.sh # Unified installer (embeds all scripts, built by build.sh)
├── build.sh / build.bat # Rebuilds install.sh from source scripts
├── Dockerfile # Multi-stage: Go build + frontend build
└── README.md # Full user documentation
```
## Script Constraints & Gotchas
- **Atomic Operations:** Always use atomic writes (`mv -f tmp target`) for `policy.json` to prevent the policy engine from reading partial files.
- **Locking:** `wg-sync-policy.sh` uses file-based locking (`flock`) to prevent race conditions during updates.
- **Rollback:** `wg-policy-engine.sh` creates a backup chain (`WG_POLICY_BAK`) and uses a trap on `ERR` to rollback if applying rules fails halfway.
- **Dependencies:** Requires `jq` and `inotify-tools`.
## WHERE TO LOOK
| Task | Location |
|------|----------|
| Policy firewall logic | `wg-sync-policy.sh`, `wg-policy-engine.sh`, `wg-policy-lib.sh` |
| Firewall rule chain | `wg-policy-engine.sh` -- WG_POLICY iptables chain |
| Policy JSON schema | `wg-sync-policy.sh` output / `wg-policy-engine.sh` input |
| WGRplane API handlers | `app/handlers.go` |
| WGRplane DB models | `app/models.go` |
| Auth middleware | `app/auth.go` (CAUTION: see known issues) |
| nftables rules (forward mode) | `app/nftables.go` |
| Webhook delivery | `app/webhook.go` |
| Vue frontend | `app/frontend/src/` |
| i18n translations | `app/frontend/src/i18n/locales/` (en, id, zh) |
| Backend i18n | `app/active.{en,id,zh}.json` |
| Systemd service config | `wg-policy.service`, `wgrplane.service` |
## Development Commands
- Restart the watcher service: `systemctl restart wg-policy.service`
- Check service logs: `journalctl -u wg-policy.service -f`
## CRITICAL DESIGN RULES
### Policy Firewall -- `#Access` MUST NOT be changed
- WireGuard `AllowedIPs` on the server side = Cryptokey Routing, not firewall whitelist
- Putting destination IPs in server's `AllowedIPs` breaks routing (WG tunnels those packets INTO client)
- `#Access` comment is the ONLY correct way to declare firewall destinations per-peer
- **NEVER refactor to parse targets from `AllowedIPs`** -- architecturally incorrect
### Atomic Writes
- Always `mv -f tmp target` for `policy.json` -- never write directly
- `wg-sync-policy.sh` uses `flock` -- never bypass locking
### Rollback
- `wg-policy-engine.sh` creates `WG_POLICY_BAK` chain; traps `ERR` for rollback
### No SaveConfig
- WireGuard `SaveConfig = true` strips ALL comments including `#Access` -- NEVER enable
## KNOWN ISSUES / GOTCHAS
- **AuthMiddleware NOT applied**: `auth.go` defines `AuthMiddleware` but it is NOT wired to any routes in `main.go`. All API endpoints currently unprotected (auth header still checked inside handlers via manual if-check, but middleware chain is absent).
- **TOTP secrets in-memory**: `totpSecrets` map in `auth.go` is not persisted; lost on restart.
- **WebSocket stats are MOCK**: `stats.go` broadcasts randomly generated numbers, not real WireGuard traffic.
- **Plugin system is stub**: `plugins.go` TelegramNotifier/SlackNotifier just print to stdout.
- **Binary + DB in app/**: `wgrplane` binary and `wgrplane.db` live in `app/` (non-standard, intentional).
- **Duplicate i18n**: `app/active.*.json` (backend i18n) and `app/frontend/src/i18n/locales/` (frontend i18n) are separate systems.
## ANTI-PATTERNS
- Never parse firewall targets from `AllowedIPs` -- use `#Access` only
- Never write `policy.json` without atomic mv + flock
- Never run `wg-policy-lib.sh` directly (source-only library)
- Never enable `SaveConfig = true` in wg0.conf
- Do NOT edit `app/docs/docs.go` -- auto-generated by swaggo
- Do NOT put business logic in `app/main.go` -- it's bootstrap only
## COMMANDS
### Policy Firewall
```bash
wg-policy-ctl status # Health: interface, JSON validity, rule counts
wg-policy-ctl validate # Validate policy.json without applying
wg-policy-ctl rules # Show active WG_POLICY iptables rules
wg-policy-ctl reload # Force re-sync from wg0.conf + re-apply
wg-policy-ctl policy # Show raw policy.json
wg-policy-ctl log # Show dropped packet logs (rate-limited)
systemctl restart wg-policy.service
journalctl -u wg-policy.service -f
```
### WGRplane (Go App)
```bash
cd app && go build -o ../wgrplane . # Build Go binary
cd app/frontend && npm install && npm run build # Build Vue frontend
./wgrplane # Run (serves :10087)
systemctl restart wgrplane.service
journalctl -u wgrplane.service -f
curl -H "wg-rplane-datadunia: test-api-key" http://localhost:10087/api/servers
```
### Installer
```bash
./build.sh # Rebuild install.sh from source scripts (Linux)
./build.bat # Rebuild install.sh from source scripts (Windows)
sudo ./install.sh install # Full install (deps + scripts + systemd + service)
sudo ./install.sh uninstall # Remove all
```
## DEPENDENCIES
- Shell: `jq`, `inotify-tools` (required); `ipset` (optional, O(1) lookup)
- Go: 1.25.1, SQLite (glebarez/sqlite), Gorilla Mux, GORM, JWT, TOTP, WebSocket
- Frontend: Vue 3, TypeScript, Vite, TailwindCSS 4, vue-i18n 9
- Auth headers: `wg-rplane-datadunia: <KEY>` (API key) | `Authorization: Bearer <JWT>` | `X-TOTP: <CODE>`
- Env vars: `WG_API_KEY` (default: test-api-key), `JWT_SECRET`, `WG_RPLANE_MODE` (forward|standalone), `APP_FRONTEND_DIR`
+248 -3
View File
@@ -1,8 +1,29 @@
# WireGuard Dynamic Policy Firewall
# WireGuard Policy Firewall + WGRplane Control Plane
A lightweight, robust, and highly dynamic iptables/ipset policy firewall engine designed to restrict and control WireGuard peer traffic (egress traffic mapping) straight from `wg0.conf`.
A complete WireGuard management solution combining two powerful components:
Rather than allowing all VPN clients to reach any part of your internal network, this tool isolates clients from each other by default and reads a custom `#Access` comment inside `wg0.conf` to automatically generate strict `iptables` rules and `ipset` whitelists per-client on the fly.
1. **WireGuard Dynamic Policy Firewall** - A lightweight, robust iptables/ipset policy engine that restricts and controls WireGuard peer traffic directly from `wg0.conf` using custom `#Access` comments.
2. **WGRplane** - A Go-native control plane application with Vue 3 frontend, providing a modern web dashboard for WireGuard management with real-time monitoring, peer CRUD, and hybrid firewall enforcement.
---
## 📑 Table of Contents
### Policy Firewall (Shell Scripts)
- [Architecture & Data Flow](#-architecture--data-flow)
- [Installation](#-installation)
- [Prerequisites](#-prerequisites)
- [wg0.conf Integration](#-integrasi-ke-wg0conf)
- [CLI Usage](#-wg-policy-ctl-cli-usage)
- [Systemd Integration](#-systemd-integration-watcher-daemon)
### WGRplane (Go App)
- [WGRplane Overview](#-wgrplane-overview)
- [WGRplane Architecture](#-wgrplane-architecture)
- [WGRplane Features](#-wgrplane-features)
- [WGRplane Tech Stack](#-wgrplane-tech-stack)
- [WGRplane API Endpoints](#-wgrplane-api-endpoints)
- [WGRplane Installation](#-wgrplane-installation)
---
@@ -171,3 +192,227 @@ Check the watcher logs:
```bash
journalctl -u wg-policy.service -f
```
---
## 🚀 WGRplane Overview
**WGRplane** is a Go-native WireGuard control plane application with a Vue 3 frontend, providing a modern web dashboard for WireGuard management. It features a single Go binary backend, SPA frontend, dynamic policy firewall integration, and glassmorphism UI design.
The application lives in the `/app` directory. For full documentation, see [`app/README.md`](app/README.md).
---
## 🏗 WGRplane Architecture
```
wg0.conf (with/without #Access)
┌─────────────────────────────────────────────┐
│ WGRplane (Go Binary :10087) │
│ ┌───────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Gorilla │ │ GORM │ │ nftables │ │
│ │ Mux Router│ │ SQLite │ │ Engine │ │
│ └───────────┘ └──────────┘ └──────────┘ │
│ ┌───────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Webhook │ │ Scheduler│ │ WebSocket│ │
│ │ Engine │ │ Cron │ │ Hub │ │
│ └───────────┘ └──────────┘ └──────────┘ │
│ ┌───────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Auth │ │ SMTP │ │ Plugins │ │
│ │ JWT/TOTP │ │ Email │ │ TG/Slack │ │
│ └───────────┘ └──────────┘ └──────────┘ │
└─────────────────────────────────────────────┘
↓ HTTP/WebSocket
┌─────────────────────────────────────────────┐
│ Frontend (Vue 3 + TypeScript + Tailwind) │
│ Glassmorphism UI, i18n, Dark/Light mode │
└─────────────────────────────────────────────┘
```
### Hybrid Mode
WGRplane supports two server modes:
- **`forward`** - Directly applies nftables rules on the local machine. Peer policies are enforced via `nft` commands.
- **`standalone`** - Acts as a control plane that triggers webhooks to remote WireGuard servers. Policy enforcement happens on the remote side.
### 2-Column Policy
Each peer has two independent policy columns:
| Column | Function |
|--------|----------|
| **AllowAccess** | List of CIDRs the peer can access (internal targets) |
| **AllowInternet** | Boolean flag. If `true`, peer gets unlimited internet access (MASQUERADE) |
Peers without any rules are isolated from other peers and the internet by default.
---
## ✨ WGRplane Features
- **Go-Native Architecture**: Single Go binary handles all API, database, webhooks, scheduler, and nftables. No Python/Flask needed.
- **Complete Peer CRUD**: Add, edit, delete peers. Generate QR codes for mobile client import. Export `.conf` configuration files.
- **Hybrid Mode**: `forward` mode (local nftables) or `standalone` mode (webhook to remote servers).
- **2-Column Policy UI**: "Allow Access" column (firewall whitelist CIDR) and "Allow Internet" toggle per peer.
- **Real-time Monitoring**: WebSocket broadcasts peer statistics and traffic every 5 seconds.
- **Automated Scheduling**: Daily cron jobs to delete expired peers, restrict over-limit peers, and reset monthly data usage.
- **Security**: API Key authentication (`wg-rplane-datadunia`), JWT Bearer tokens, and TOTP (2FA).
- **Webhook Engine**: Integration with remote servers (Mikrotik, etc.). Retry with exponential backoff, custom headers, Go templates.
- **Plugin System**: Telegram, Slack, and Traffic Logger notifications.
- **i18n & Themes**: Multi-language (English, Indonesian, Chinese). Dark/Light/Auto mode.
- **Glassmorphism UI**: Futuristic design with frosted glass cards, buttons, and inputs.
---
## 🛠 WGRplane Tech Stack
| Component | Technology |
|-----------|------------|
| **Backend** | Go, Gorilla Mux, GORM (SQLite via glebarez/sqlite) |
| **Frontend** | Vue 3, TypeScript, Vite, TailwindCSS 4, vue-i18n 9 |
| **Auth** | JWT (golang-jwt/v5), TOTP (pquerna/otp), API Key |
| **WebSockets** | gorilla/websocket |
| **Webhooks** | Go net/http with retry + exponential backoff |
| **Scheduling** | robfig/cron v3 |
| **QR Code** | skip2/go-qrcode |
| **Email** | jordan-wright/email (SMTP) |
| **Firewall** | Bash, iptables, ipset, nftables, inotify-tools, jq |
| **Container** | Docker (multi-stage build), docker-compose |
---
## 🌐 WGRplane API Endpoints Summary
All endpoints are served on port **10087**. For complete API documentation with request/response details, see [`app/README.md`](app/README.md) or visit `/swagger/` on your running instance.
### Authentication
| Method | Header | Notes |
|--------|--------|-------|
| API Key | `wg-rplane-datadunia: <KEY>` | Set via env var `WG_API_KEY`. Default: `test-api-key` |
| JWT | `Authorization: Bearer <TOKEN>` | Expires in 15 minutes. Secret via env var `JWT_SECRET` |
| TOTP | `X-TOTP: <CODE>` | Required if user enables TOTP |
### Main Endpoints
| Endpoint | Method | Description |
|----------|--------|-------------|
| `/api/servers` | `GET/POST` | List all servers / Create new server |
| `/api/servers/{id}` | `GET/PUT/DELETE` | Get/Update/Delete server |
| `/api/servers/{id}/peers` | `GET/POST` | List peers / Create new peer |
| `/api/peers/{id}` | `PUT/DELETE` | Update/Delete peer |
| `/api/peers/{id}/config` | `GET` | Download WireGuard `.conf` file |
| `/api/peers/{id}/qrcode` | `GET` | Generate QR code PNG for mobile import |
| `/api/servers/{id}/webhooks` | `GET/POST` | List/Create webhooks |
| `/api/stats` | `GET` | Global statistics |
| `/ws/stats` | WebSocket | Real-time stats broadcast (5s interval) |
| `/swagger/` | - | Interactive Swagger UI documentation |
---
## 📦 WGRplane Installation
### Option 1: Docker Compose (Recommended)
```bash
# Clone repository
git clone https://git.datadunia.com/hainzero/WGRplane.git
cd 03.wireguard-policy
# Start WGRplane and WireGuard
docker compose up -d
# Access dashboard at http://localhost:10087
```
### Option 2: Install Script
```bash
# Run automated installer (Ubuntu/Debian/CentOS)
sudo ./install.sh install
# Uninstall
sudo ./install.sh uninstall
```
### Option 3: Manual Build
```bash
# Build Go binary
cd app
go build -o ../wgrplane .
cd ..
# Build frontend
cd app/frontend
npm install && npm run build
cd ../..
# Run
./wgrplane
# Server starts on :10087
```
### Option 4: Native CLI / Systemd Service
The WGRplane binary includes a built-in CLI to manage its own systemd service.
```bash
# Check system dependencies first
./wgrplane doctor
# Install as systemd service (auto-creates unit, enables, and starts)
sudo ./wgrplane install
# View logs
journalctl -u wgrplane -f
# Other available commands:
sudo ./wgrplane stop
sudo ./wgrplane restart
sudo ./wgrplane uninstall
```
### Manual Serve & Config
To run the server manually in the foreground with custom ports:
```bash
./wgrplane serve --port 8080 --host 127.0.0.1
```
On first run, it generates a default configuration file at `~/.config/wgrplane/config.json`.
---
## 📁 Project Structure
```
03.wireguard-policy/
├── app/ # Go backend + Vue frontend
│ ├── main.go # Bootstrap server, routing, init DB
│ ├── handlers.go # REST API route handlers
│ ├── models.go # GORM models (Server, Peer, Webhook, SMTP)
│ ├── auth.go # JWT, TOTP, API key auth middleware
│ ├── nftables.go # nftables rule management (mode forward)
│ ├── webhook.go # Webhook engine with retry/backoff
│ ├── scheduler.go # Cron jobs (expiry, data limit, reset)
│ ├── stats.go # WebSocket Hub for real-time stats
│ ├── frontend/ # Vue 3 SPA (TypeScript, TailwindCSS)
│ └── docs/ # Swagger documentation
├── wg-sync-policy.sh # Parse wg0.conf → policy.json
├── wg-policy-engine.sh # Apply policy.json → iptables/ipset
├── wg-sync-watch.sh # inotifywait watcher daemon
├── wg-policy-ctl # CLI wrapper for policy management
├── wg-policy-cleanup.sh # Cleanup script for PostDown
├── wg-policy.service # Systemd unit for watcher daemon
├── wgrplane.service # Systemd unit for Go backend
├── install.sh # Automated installer (Docker + services)
├── Dockerfile # Multi-stage Docker build
├── docker-compose.yml # Docker Compose stack
└── README.md # This file
```
For detailed WGRplane documentation including webhooks, plugins, scheduler, and frontend details, refer to [`app/README.md`](app/README.md).
+1 -1
Submodule app updated: d4462053a5...b43866d42c