A FastAPI reverse proxy for the Norge i Bilder (NiB)
WMS/WMTS services. It transparently authenticates requests against the NiB
Eksport API's token endpoint
(https://backend-api.klienter-prod-k8s2.norgeibilder.no/swagger/v1/swagger.json,
POST /token/tilecache) using a shared GeoID credential, reuses a single
shared token (bound to this proxy's own request IP via client=requestip,
auto-detected by the upstream), and automatically refreshes the token on
expiry (default validity: 1 hour).
- Set
NIB_USERNAME/NIB_PASSWORD(a GeoID user) in.env. - Configure the proxied services in
services.yaml— no code changes needed to add/remove upstream services. Each entry maps apath_prefixon the proxy to anupstreambase URL. - Incoming requests are matched against
services.yaml, a single shared NiB token is fetched/cached (bound to this proxy's own request IP, since it's this proxy -- not the original caller -- that makes the actual upstream request), and the request is forwarded upstream with?token=<token>appended to the query string. On a401/403from upstream, the token is refreshed once and the request is retried. - CORS is enabled and configurable via
CORS_ALLOW_ORIGINS/CORS_ALLOW_METHODS/CORS_ALLOW_HEADERS/CORS_ALLOW_CREDENTIALS(defaults allow any origin). - Restrict proxy access with comma-separated
ALLOWED_REFERRERSand/orALLOWED_IPS. When both are configured, a request is allowed if either itsRefererURL or direct client IP matches. Referrer entries match the exact scheme and host plus an optional path prefix; IP entries accept addresses or CIDR ranges. Leave both unset to allow all requests. The IP check uses the direct TCP peer, notX-Forwarded-For; configure the reverse proxy to pass the actual client address when deploying behind one. - Set
BASE_PATH(e.g./nib) to mount the proxy under a path prefix, so it can be exposed ashttps://host/nib/...without needing a reverse proxy that strips the prefix./healthzremains available both prefixed and unprefixed for infra liveness/readiness probes. GET /services(orGET <BASE_PATH>/services) lists the currently configured upstream services (name, effective path prefix, upstream URL) for introspection/debugging.- Set
PUBLIC_BASE_URL(e.g.https://example.org) to rewrite occurrences of a service's upstream URL found in textual response bodies (WMS/WMTS Capabilities documents) to point back at this proxy instead (any scheme/port variant of the configured upstream URL is matched), so clients keep going through it (and its authentication) for subsequent requests instead of bypassing it.
Install uv: https://docs.astral.sh/uv/getting-started/installation/
git init
uv sync --dev
git add .
git commit -m "Initial commit"
uv run prek install # optional
cp .env.example .env # then fill in NIB_USERNAME / NIB_PASSWORDTo execute your software you have two options:
Option 1: Direct execution
uv run main.pyOption 2: Run as installed package
uvx --from . nib_proxyJust run uv run main.py and you are good to go!
To update your project with the latest changes from the template, run:
uvx --with copier-template-extensions copier update --trustYou can keep your previous answers by using:
uvx --with copier-template-extensions copier update --trust --defaultsprek is a fast, Rust-based tool for managing git hooks (100% compatible with pre-commit). It helps ensure code quality by running checks every time you make a commit.
First, install prek:
uv tool install prekIf you have installed the git hooks with pre-commit (template version 0.2.6 and older), remove them before installing the ones provided by prek:
pre-commit uninstall
Then install git hooks:
prek installTo run prek on all files:
prek run --all-filesRun uv add <package-name> to install a package. For example:
uv add requestsIf you are using visual studio code install the recommended extensions
A basic docker image is already provided, run:
docker compose up --build watch- uv
- prek (optional)
Environment variables are variables that are not populated in your code but rather in the environment that you are running your code. This is extremely useful mainly for two reasons:
- security, you can share your code without sharing your passwords/credentials
- portability, you can avoid using hard-coded values like file-system paths or folder names
you can place your environment variables in a file called .env, the main.py will read from it. Remember to:
- NEVER commit your
.env - Keep a
.env.examplefile updated with the variables that the software expects