|
|
||
|---|---|---|
| .forgejo | ||
| ci | ||
| src | ||
| .dockerignore | ||
| .gitignore | ||
| Dockerfile | ||
| LICENSE.txt | ||
| README.md | ||
| renovate.json5 | ||
forgejo-pages: a no-fuzz static page webhook server for Forgejo
A lightweight webhook service that pulls static pages from Forgejo/Gitea repositories based on webhook events.
Overview
forgejo-pages acts as a simple webhook receiver that:
- Listens for push events from Forgejo/Gitea
- Pulls repository content when changes occur
- Stores files in a directory for serving by a web server like nginx
Repository Setup
Everything in branch static-pages (configurable with --branch flag) will be stored and available for serving.
Repository URL Handling
The application intelligently handles different repository URL formats:
- If the webhook payload contains an SSH URL (
ssh_url), it will use that directly - If only an HTTP URL (
clone_url) is available, it will convert it to an SSH URL - As a last resort, it constructs an SSH URL using the server hostname and repository path
- If SSH cloning fails, it will fall back to HTTP cloning (useful for public repositories)
This flexibility ensures seamless operation with various Forgejo/Gitea configurations.
Authentication
The application uses a Forgejo access token for HTTP-based authentication.
Setting Up Access Token
To generate a token in Forgejo:
- Go to your user settings (click your profile picture in the top right and select "Settings")
- Navigate to "Applications" in the sidebar
- Under "Generate New Token":
- Enter a token name (e.g., "forgejo-pages")
- Select the "repo" scope to grant read access to repositories
- Click "Generate Token"
- Copy the generated token and provide it to forgejo-pages using the
PAGES_TOKENenvironment variable
This token allows read-only access to repositories. The token is embedded in the clone URL when Git commands are executed.
Container Security
The container is built on a minimal scratch base with:
- Only essential components
- No unnecessary libraries or tools
- Proper permissions for secure operation
Configuration
Configuration is managed through environment variables:
| Environment Variable | Description | Default |
|---|---|---|
PAGES_BIND |
Address and port to bind the webhook server | :8080 |
PAGES_SERVER |
URL of the Forgejo/Gitea server | - |
PAGES_TOKEN |
Forgejo access token for repository authentication | - |
PAGES_BRANCH |
Branch containing static content | static-pages |
PAGES_DIR |
Directory to store repository data | - |
RUN_AT_START |
Run the webhook at startup | true |
DEFAULT_REPO |
Default repository to use when run_at_start is true (format: user/repo) | - |
TRIGGER_ONLY |
Use webhook as trigger only and ignore payload | false |
Security Features
The application implements several security features:
- Input Validation: Repository and user names are validated against a safe pattern to prevent path traversal and command injection.
- Security-Focused Logging: Full IP addresses are logged for security monitoring and auditing purposes.
- Trigger-Only Mode: When
TRIGGER_ONLY=true, the webhook ignores the payload content and uses theDEFAULT_REPOconfiguration, mitigating risks from untrusted webhook sources. - Early Response: The webhook handler returns a response immediately after validation, reducing the risk of denial-of-service attacks.
- Secure Token Management: Tokens are stored and transmitted securely, using Kubernetes secrets for deployment.
Using Trigger-Only Mode
For enhanced security, you can enable trigger-only mode:
env:
TRIGGER_ONLY: "true"
DEFAULT_REPO: "user/repo"
In this mode, the webhook endpoint acts only as a trigger, ignoring the payload content and using the configured DEFAULT_REPO instead. This mitigates risks from untrusted webhook sources, as the repository details come from your trusted configuration instead of the payload.
Logging
The application logs important events to the standard output:
- Webhook events (received, processed)
- Repository events (pulling, cloning, fetching)
- Git operations (clone, fetch, checkout)
- Error conditions with detailed output
This makes it easy to monitor the application's operations and troubleshoot issues.
Kubernetes Deployment
Deploy forgejo-pages with a shared volume for a web server:
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: &app blog
spec:
interval: 1h
timeout: 30s
chartRef:
kind: OCIRepository
name: app-template
namespace: flux-system
install:
remediation:
retries: 3
upgrade:
cleanupOnFail: true
remediation:
strategy: rollback
retries: 3
values:
controllers:
# Webhook container - pulls content from git
blog:
annotations:
reloader.stakater.com/auto: "true"
containers:
app:
image:
repository: code.252.no/pub/forgejo-pages
tag: 0.1.1
args:
- listen
env:
PAGES_SERVER: https://code.252.no
DEFAULT_REPO: /tommy/website
PAGES_BRANCH: static-pages
PAGES_DIR: /data
PAGES_BIND: :8080
RUN_AT_START: true
envFrom:
- secretRef:
name: blog-secret
resources:
requests:
cpu: 15m
memory: 200M
limits:
cpu: 500m
memory: 512M
web:
image:
repository: nginx
tag: alpine
defaultPodOptions:
imagePullSecrets:
- name: forgejo-code-252-no-imagepull
persistence:
data:
type: emptyDir
globalMounts:
- path: /data
- path: /usr/share/nginx/html
service:
# Webhook service
webhook:
controller: blog
ports:
http:
port: 8081
# Web service for nginx
web:
controller: blog
ports:
http:
port: 8080
persistence:
data:
type: emptyDir
globalMounts:
- path: /data
Creating the Token Secret
Create a Kubernetes secret for your Forgejo token:
# Create Kubernetes secret
kubectl create secret generic static-pages-token \
--from-literal=token=YOUR_FORGEJO_TOKEN \
--namespace=YOUR_NAMESPACE
Then reference it in your deployment:
env:
- name: PAGES_TOKEN
valueFrom:
secretKeyRef:
name: static-pages-token
key: token
External Secret Configuration
If using external-secrets operator, you can create the token secret from an external vault:
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: static-pages-token
spec:
secretStoreRef:
kind: ClusterSecretStore
name: onepassword
target:
name: static-pages-token
creationPolicy: Owner
dataFrom:
- extract:
key: static-pages-token
HTTPRoute Configuration
To expose your static pages with Gateway API:
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: static-pages
spec:
parentRefs:
- name: gateway
namespace: gateway-system
hostnames:
- "www.252.no"
rules:
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
- name: static-pages-server
port: 8080
Webhook Setup
In your Forgejo/Gitea repository settings:
- Add a webhook pointing to
http://static-pages-webhook.web.svc.cluster.local:8081/webhook - Select HTTP-method
POST - Select only the "Push" event for the static-pages branch (or the branch you've configured)
- Set the content type to
application/json
Testing the Webhook
The webhook endpoint only accepts POST requests. Test the webhook locally with:
curl -X POST http://localhost:8080/webhook \
-H "Content-Type: application/json" \
-H "X-GitHub-Event: push" \
-d '{
"ref": "refs/heads/static-pages",
"repository": {
"clone_url": "https://code.252.no/tommy/website.git",
"html_url": "https://code.252.no/tommy/website",
"full_name": "tommy/website"
}
}'
The webhook supports multiple event header variations:
X-GitHub-Event: pushX-Forgejo-Event: pushX-Gitea-Event: pushX-Gogs-Event: push
Any of these headers with the value "push" will trigger the webhook processing.
License
MPLv2.
Current source available at https://code.252.no/pub/forgejo-pages
The repo is based on https://github.com/Ronmi/forgejo-pages