Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions deploy/config/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
certs/*
!certs/.keep
80 changes: 80 additions & 0 deletions deploy/config/Caddyfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
{
email {$MW_ADMIN_EMAIL}
# For testing against LE staging, uncomment:
# acme_ca https://acme-staging-v02.api.letsencrypt.org/directory
}

# --------- Reusable snippets ---------

# WDQS headers + preflight + upstream headers
(wdqs_headers) {
@preflight method OPTIONS
handle @preflight {
header {
Access-Control-Allow-Origin "*"
Access-Control-Allow-Methods "GET,OPTIONS,POST"
Access-Control-Allow-Headers "*"
}
respond 204
}

header {
Vary "Accept"
Access-Control-Allow-Origin "*"
Access-Control-Allow-Methods "GET,OPTIONS,POST"
Access-Control-Allow-Headers "*"
}

reverse_proxy wdqs:9999 {
header_up X-BIGDATA-READ-ONLY "yes"
header_up X-BIGDATA-MAX-QUERY-MILLIS "300000"
}
}

# /sparql -> /bigdata/namespace/wdq/sparql{...}, else -> frontend
(wdqs_route) {
@sparql path /sparql*
handle @sparql {
uri replace /sparql /bigdata/namespace/wdq/sparql
import wdqs_headers
}

# everything else -> WDQS frontend
handle {
reverse_proxy wdqs-frontend:80
}
}

# QuickStatements under /tools/quickstatements
(quickstatements_route) {
# redirect noslash to trailing slash (parity with Traefik redirectRegex)
@qs_noslash path /tools/quickstatements
redir @qs_noslash /tools/quickstatements/ 308

@qs path /tools/quickstatements*
handle @qs {
uri strip_prefix /tools/quickstatements
reverse_proxy quickstatements:80
}
}

# --------- TLS indirection (toggle via /etc/caddy/tls.caddy mount) ---------
# This file defines a snippet called (tls_for), which we import per host below.
import tls.caddy

# ---------------- Sites ----------------

{$WIKIBASE_PUBLIC_HOST} {
import tls_for {$WIKIBASE_PUBLIC_HOST}
import quickstatements_route

# Everything else (MediaWiki/Wikibase)
handle {
reverse_proxy wikibase:80
}
}

{$WDQS_PUBLIC_HOST} {
import tls_for {$WDQS_PUBLIC_HOST}
import wdqs_route
}
3 changes: 3 additions & 0 deletions deploy/config/Caddyfile.files.tls
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
(tls_for) {
tls /certs/{args.0}.crt /certs/{args.0}.key
}
3 changes: 3 additions & 0 deletions deploy/config/Caddyfile.letsencrypt.tls
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
(tls_for) {
# Nothing needed — auto HTTPS
}
Empty file added deploy/config/certs/.keep
Empty file.
79 changes: 79 additions & 0 deletions deploy/create-local-certs.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
#!/usr/bin/env bash
set -euo pipefail

# ----------------------------------------------------------------------
# create-local-certs.sh
#
# Generate local TLS certs with mkcert for the hosts in .env:
# WIKIBASE_PUBLIC_HOST
# WDQS_PUBLIC_HOST
# Output files: config/certs/<domain>.crt and .key
#
# Requires:
# - mkcert (https://github.com/FiloSottile/mkcert)
# - Completed WBS Deploy configuration (.env present with above vars)
# ----------------------------------------------------------------------

# --- Check mkcert is installed ---
if ! command -v mkcert >/dev/null 2>&1; then
echo "Error: mkcert is not installed."
echo "Please install mkcert first: https://github.com/FiloSottile/mkcert#installation"
exit 1
fi

# --- Ensure .env exists ---
if [[ ! -f .env ]]; then
echo "⚠️ Error: .env file not found. Please complete WBS Deploy configuration first."
exit 1
fi

# --- Extract domains from .env ---
WIKIBASE_PUBLIC_HOST=$(grep -E '^WIKIBASE_PUBLIC_HOST=' .env | cut -d '=' -f2- | tr -d '"')
WDQS_PUBLIC_HOST=$(grep -E '^WDQS_PUBLIC_HOST=' .env | cut -d '=' -f2- | tr -d '"')

# --- Validate values ---
if [[ -z "$WIKIBASE_PUBLIC_HOST" || -z "$WDQS_PUBLIC_HOST" ]]; then
echo "⚠️ Error: WIKIBASE_PUBLIC_HOST or WDQS_PUBLIC_HOST is missing in .env."
echo "Please set these values in .env before running this script."
exit 1
fi

# --- Ensure both domains end in .test ---
for host in "$WIKIBASE_PUBLIC_HOST" "$WDQS_PUBLIC_HOST"; do
if [[ ! "$host" =~ \.test$ ]]; then
echo
echo "⚠️ Error: '$host' is not a .test domain."
echo
echo "Only .test domains are supported for localhost configuration."
echo "Reason: '.test' is reserved for local development and avoids conflicts with real domains or OS-level protocols."
echo "See: https://datatracker.ietf.org/doc/html/rfc2606"
echo
exit 1
fi
done

DOMAINS=("$WIKIBASE_PUBLIC_HOST" "$WDQS_PUBLIC_HOST")

# --- Target directory for certs ---
CERT_DIR="./config/certs"
mkdir -p "$CERT_DIR"

# --- Ensure mkcert CA is installed (idempotent) ---
mkcert -install

# --- Generate certs for each domain ---
for domain in "${DOMAINS[@]}"; do
cert="$CERT_DIR/${domain}.crt"
key="$CERT_DIR/${domain}.key"
mkcert -cert-file "$cert" -key-file "$key" "$domain"
done

# --- Append CADDY_TLS_FILE_PATH to .env only if last entry for CADDY_TLS_FILE_PATH isn't already correct ---
if ! awk 'BEGIN {found=0} /^CADDY_TLS_FILE_PATH=\.\/config\/Caddyfile\.files\.tls$/ {found=1} END {exit !found}' <(tail -r .env 2>/dev/null || tail -n 9999 .env | awk '{a[NR]=$0} END{for (i=NR;i>0;i--) print a[i]}'); then
{
echo ""
echo "# Configures Caddy to use custom TLS/SSL certificates configuration"
echo "CADDY_TLS_FILE_PATH=./config/Caddyfile.files.tls"
} >> .env
echo "Added CADDY_TLS_FILE_PATH to .env"
fi
23 changes: 23 additions & 0 deletions deploy/docker-compose.dev.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
services:
wikibase:
image: wikibase/wikibase
pull_policy: never
wikibase-jobrunner:
image: wikibase/wikibase
pull_policy: never
elasticsearch:
image: wikibase/elasticsearch
pull_policy: never
wdqs:
image: wikibase/wdqs
pull_policy: never
wdqs-updater:
image: wikibase/wdqs
pull_policy: never
wdqs-frontend:
image: wikibase/wdqs-frontend
pull_policy: never
quickstatements:
image: wikibase/quickstatements
pull_policy: never

29 changes: 29 additions & 0 deletions deploy/docker-compose.override.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
services:
caddy:
image: caddy:2
restart: unless-stopped
ports:
- "80:80"
- "443:443"
environment:
WIKIBASE_PUBLIC_HOST: ${WIKIBASE_PUBLIC_HOST}
WDQS_PUBLIC_HOST: ${WDQS_PUBLIC_HOST}
MW_ADMIN_EMAIL: ${MW_ADMIN_EMAIL}
volumes:
# Set CADDY_TLS_FILE_PATH to ./config/Caddyfile.files.tls if you're
# providing your own TLS/SSL certs. This is particularly useful when
# needing valid certificates in a localhost scenario (see mkcert).
- ${CADDY_TLS_FILE_PATH:-./config/Caddyfile.letsencrypt.tls}:/etc/caddy/tls.caddy:ro
- ./config/certs:/certs:ro
- ./config/Caddyfile:/etc/caddy/Caddyfile:ro
- caddy-data:/data
- caddy-config:/config
networks:
default:
aliases:
- ${WIKIBASE_PUBLIC_HOST}
- ${WDQS_PUBLIC_HOST}

volumes:
caddy-data:
caddy-config:
51 changes: 51 additions & 0 deletions deploy/docker-compose.traefik.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
services:
# --------------------------------------------------
# C. REVERSE PROXY AND SSL SERVICES
# --------------------------------------------------

# This is the reverse proxy and SSL service
traefik:
image: traefik:3
command:
# traefik static configuration via command line
# enable accesslog
- "--accesslog.format=common"
# http endpoint
- "--entrypoints.web.address=:80"
# https endpoint
- "--entrypoints.websecure.address=:443"
- "--entrypoints.websecure.asdefault"
- "--entrypoints.websecure.http.tls.certresolver=letsencrypt"
# http to https redirect
- "--entrypoints.web.http.redirections.entryPoint.to=websecure"
- "--entrypoints.web.http.redirections.entryPoint.scheme=https"
- "--entrypoints.web.http.redirections.entrypoint.permanent=true"
# ACME SSL certificate generation
- "--certificatesresolvers.letsencrypt.acme.httpchallenge=true"
- "--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web"
- "--certificatesresolvers.letsencrypt.acme.email=${MW_ADMIN_EMAIL}"
- "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json"
# additionial traefik dynamic configuration via config file
- "--providers.file.filename=/etc/traefik/dynamic.yml"
# Uncomment this line to only test ssl generation first, makes sure you don't run into letsencrypt rate limits
# - "--certificatesresolvers.letsencrypt.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory"
# Uncomment the following line for debugging, also expose port 8080 below
# - "--api.dashboard=true"
# - "--api.insecure=true"
# - "--log.level=DEBUG"
restart: unless-stopped
ports:
- 80:80
- 443:443
# traefik dashboard
# - 8080:8080
volumes:
- ./config/traefik-dynamic.yml:/etc/traefik/dynamic.yml:ro
- traefik-letsencrypt-data:/letsencrypt
environment:
WIKIBASE_PUBLIC_HOST: ${WIKIBASE_PUBLIC_HOST}
WDQS_PUBLIC_HOST: ${WDQS_PUBLIC_HOST}

volumes:
# C. REVERSE PROXY AND SSL SERVICES DATA
traefik-letsencrypt-data:
49 changes: 0 additions & 49 deletions deploy/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -151,53 +151,6 @@ services:
interval: 10s
start_period: 2m

# --------------------------------------------------
# C. REVERSE PROXY AND SSL SERVICES
# --------------------------------------------------

# This is the reverse proxy and SSL service
traefik:
image: traefik:3
command:
# traefik static configuration via command line
# enable accesslog
- "--accesslog.format=common"
# http endpoint
- "--entrypoints.web.address=:80"
# https endpoint
- "--entrypoints.websecure.address=:443"
- "--entrypoints.websecure.asdefault"
- "--entrypoints.websecure.http.tls.certresolver=letsencrypt"
# http to https redirect
- "--entrypoints.web.http.redirections.entryPoint.to=websecure"
- "--entrypoints.web.http.redirections.entryPoint.scheme=https"
- "--entrypoints.web.http.redirections.entrypoint.permanent=true"
# ACME SSL certificate generation
- "--certificatesresolvers.letsencrypt.acme.httpchallenge=true"
- "--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web"
- "--certificatesresolvers.letsencrypt.acme.email=${MW_ADMIN_EMAIL}"
- "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json"
# additionial traefik dynamic configuration via config file
- "--providers.file.filename=/etc/traefik/dynamic.yml"
# Uncomment this line to only test ssl generation first, makes sure you don't run into letsencrypt rate limits
# - "--certificatesresolvers.letsencrypt.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory"
# Uncomment the following line for debugging, also expose port 8080 below
# - "--api.dashboard=true"
# - "--api.insecure=true"
# - "--log.level=DEBUG"
restart: unless-stopped
ports:
- 80:80
- 443:443
# traefik dashboard
# - 8080:8080
volumes:
- ./config/traefik-dynamic.yml:/etc/traefik/dynamic.yml:ro
- traefik-letsencrypt-data:/letsencrypt
environment:
WIKIBASE_PUBLIC_HOST: ${WIKIBASE_PUBLIC_HOST}
WDQS_PUBLIC_HOST: ${WDQS_PUBLIC_HOST}

volumes:
# A. CORE WIKIBASE SUITE SERVICES DATA
wikibase-image-data:
Expand All @@ -206,5 +159,3 @@ volumes:
wdqs-data:
elasticsearch-data:
quickstatements-data:
# C. REVERSE PROXY AND SSL SERVICES DATA
traefik-letsencrypt-data:
1 change: 1 addition & 0 deletions test/setup/make-test-settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ export const defaultSettings: Partial<TestSettings> = {
],
composeFiles: [
'../deploy/docker-compose.yml',
'../deploy/docker-compose.override.yml',
'suites/docker-compose.override.yml'
]
};
Loading