139 lines
4.4 KiB
Markdown
139 lines
4.4 KiB
Markdown
# nas-webhook
|
|
|
|
Lightweight webhook container for NAS auto-deploy. Alpine-based with `docker-cli` and `docker-cli-compose` included so hooks can run `docker compose pull && docker compose up -d`.
|
|
|
|
## Image
|
|
|
|
```
|
|
registry.gitlab.com/bgrolleman/nas-webhook:latest
|
|
```
|
|
|
|
Built automatically by GitLab CI on every push to `main`. Image is **public** — no registry login needed on the NAS to pull it.
|
|
|
|
## How it works
|
|
|
|
1. A GitLab CI pipeline builds and pushes a new image (e.g. `hugo_basg.nl:latest`)
|
|
2. The CI pipeline sends a POST to `http://webhook.nas.basg.nl/hooks/<hook-id>` with a secret token in the `X-Webhook-Token` header
|
|
3. The webhook container receives the request, validates the token, and runs `deploy.sh <compose-dir>`
|
|
4. `deploy.sh` does `docker compose pull && docker compose up -d` in the project directory
|
|
|
|
## Deployment on NAS
|
|
|
|
Place the following files in a directory on the NAS (e.g. `/volume1/docker/webhook/`):
|
|
|
|
```
|
|
webhook/
|
|
├── docker-compose.yml
|
|
├── hooks.json
|
|
└── scripts/
|
|
└── deploy.sh
|
|
```
|
|
|
|
Then bring it up via the UGREEN web interface or `docker compose up -d`.
|
|
|
|
The Docker socket (`/var/run/docker.sock`) must be mounted so the container can run Docker commands. The NAS docker directory (`/volume1/docker`) must also be mounted so `deploy.sh` can reach the project compose files.
|
|
|
|
### docker-compose.yml
|
|
|
|
```yaml
|
|
networks:
|
|
external:
|
|
external: true
|
|
|
|
services:
|
|
webhook:
|
|
image: registry.gitlab.com/bgrolleman/nas-webhook:latest
|
|
restart: always
|
|
command: ["-hooks", "/config/hooks.json", "-verbose", "-hotreload"]
|
|
volumes:
|
|
- ./hooks.json:/config/hooks.json:ro
|
|
- ./scripts:/scripts:ro
|
|
- /var/run/docker.sock:/var/run/docker.sock
|
|
- /volume1/docker:/volume1/docker
|
|
expose:
|
|
- 9000
|
|
environment:
|
|
- VIRTUAL_HOST=webhook.nas.basg.nl
|
|
- VIRTUAL_PORT=9000
|
|
networks:
|
|
- external
|
|
```
|
|
|
|
Note: No `LETSENCRYPT_HOST` — running HTTP only. The ACME challenge (Let's Encrypt) doesn't work for this container because it only exposes port 9000, not 80. Since it's only called by GitLab CI (not a browser), HTTP is fine.
|
|
|
|
## Adding a new project
|
|
|
|
### 1. Add a hook to `hooks.json`
|
|
|
|
```json
|
|
{
|
|
"id": "my-project",
|
|
"execute-command": "/scripts/deploy.sh",
|
|
"command-working-directory": "/scripts",
|
|
"pass-arguments-to-command": [
|
|
{
|
|
"source": "string",
|
|
"name": "/volume1/docker/my-project"
|
|
}
|
|
],
|
|
"trigger-rule": {
|
|
"match": {
|
|
"type": "value",
|
|
"value": "YOUR_SECRET_TOKEN_HERE",
|
|
"parameter": {
|
|
"source": "header",
|
|
"name": "X-Webhook-Token"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
Generate a token with: `openssl rand -hex 32`
|
|
|
|
The webhook container uses `-hotreload` so it picks up `hooks.json` changes without a restart.
|
|
|
|
### 2. Add deploy stage to the project's `.gitlab-ci.yml`
|
|
|
|
Store the token as a masked CI variable (e.g. `DEPLOY_WEBHOOK_TOKEN`) in the GitLab project settings.
|
|
|
|
```yaml
|
|
deploy:
|
|
stage: deploy
|
|
image: alpine:latest
|
|
before_script:
|
|
- apk add --no-cache curl
|
|
script:
|
|
- |
|
|
curl -f -X POST \
|
|
-H "X-Webhook-Token: $DEPLOY_WEBHOOK_TOKEN" \
|
|
http://webhook.nas.basg.nl/hooks/my-project
|
|
rules:
|
|
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
|
|
needs: [build]
|
|
```
|
|
|
|
### 3. Make sure the project image is public
|
|
|
|
The NAS pulls images without authentication. Either make the GitLab registry public (project → Settings → General → Visibility → Public) or set up a deploy token and run `sudo docker login registry.gitlab.com` on the NAS.
|
|
|
|
## Testing a hook manually
|
|
|
|
```bash
|
|
curl -X POST \
|
|
-H "X-Webhook-Token: YOUR_TOKEN" \
|
|
http://webhook.nas.basg.nl/hooks/your-hook-id
|
|
```
|
|
|
|
## Current hooks
|
|
|
|
| Hook ID | Project | Compose dir |
|
|
|---------|---------|-------------|
|
|
| `hugo-basg` | bgrolleman/hugo_basg.nl | `/volume1/docker/basg-hugo` |
|
|
|
|
## Known issues / pitfalls
|
|
|
|
- **No HTTPS**: Let's Encrypt won't issue a cert because the container doesn't serve HTTP on port 80. Since only GitLab CI calls this endpoint, HTTP is acceptable. If HTTPS is needed in the future, put a separate nginx container in front that handles port 80/443 and proxies to port 9000.
|
|
- **Docker socket permissions**: The webhook runs as root inside the container (required to use the socket). This is standard for Docker-in-Docker patterns on a home NAS.
|
|
- **Image must be public or NAS must be logged in**: The deploy script runs `docker compose pull` which needs registry access. Easiest solution is a public GitLab project.
|