GNY server
Project description
GNY
This is currently ALPHA software — lots of bits and pieces are missing.
GNY is a FastAPI-based server that manages DNS TXT records for ACME dns-01 certificate challenges. It is designed to be used as a hook target for certbot (or any ACME client) when automating TLS certificate issuance and renewal.
Overview
GNY exposes a small REST API (documented in openapi.yaml) that lets enrolled servers add and remove _acme-challenge.* DNS TXT records. Access is controlled by:
- Enrollment — a server registers its IP address and administrator email, receiving a Bearer token.
- Email confirmation — an administrator with
access_level >= 1confirms the enrollment via OIDC login (Google, Azure AD, or any standard OIDC provider), activating the token. - PTR-based authorization — an enrolled server may only manage TXT records for its own hostname (as resolved by reverse-DNS lookup of its IP address).
Requirements
- Python 3.11+
- MariaDB or MySQL database
- An OIDC client (Google Cloud OAuth2, Azure AD/Entra ID app registration, or any standard OIDC provider) for enrollment confirmation
- The enrolling server must have a valid PTR (reverse-DNS) record
Installation
PyPI
pip install gny
Debian package
Pre-built .deb packages are attached to each GitHub Release:
wget https://github.com/svalgaard/gny/releases/latest/download/gny_<version>_all.deb
sudo dpkg -i gny_<version>_all.deb
sudo apt-get install -f # resolve any missing dependencies
Docker
docker pull ghcr.io/svalgaard/gny:latest
Configuration
Create a .env file and set the following variables:
| Variable | Description |
|---|---|
APP_URL |
Public base URL of this server (e.g. https://dns.example.com) |
DB_HOST |
MariaDB/MySQL hostname |
DB_DATABASE |
Database name |
DB_USERNAME |
Database user |
DB_PASSWORD |
Database password |
OIDCProviderMetadataURL |
OIDC Discovery document URL (see below) |
OIDCClientID |
OIDC client ID |
OIDCClientSecret |
OIDC client secret |
OIDCRedirectURI |
OIDC redirect path (default /.well-known/sso) |
MAIL_HOST |
SMTP hostname for outgoing mail (default localhost) |
MAIL_PORT |
SMTP port (default 25) |
MAIL_ENCRYPTION |
SMTP encryption: tls, starttls, or none (default tls) |
MAIL_USER |
SMTP username |
MAIL_PASSWORD |
SMTP password |
APP_MAIL_ADDRESS |
From-address for outgoing mail |
ENROLL_CONFIRM_TIMEOUT_HOURS |
Hours before an unconfirmed enrollment expires (default 32) |
LOG_LEVEL |
Logging level: debug, info, warning, error |
DISPLAY_ERRORS |
Show error details in responses (true / false) |
The redirect URI registered with the OIDC provider must be {APP_URL}{OIDCRedirectURI}, e.g. https://dns.example.com/.well-known/sso.
OIDC provider examples
Google:
OIDCProviderMetadataURL=https://accounts.google.com/.well-known/openid-configuration
Azure AD / Entra ID (replace {tenant_id} with your directory tenant ID):
OIDCProviderMetadataURL=https://login.microsoftonline.com/{tenant_id}/v2.0/.well-known/openid-configuration
Running
Direct
uvicorn gny.main:app --host 0.0.0.0 --port 8000
The database tables are created automatically on startup. Interactive API docs are available at http://localhost:8000/docs.
Docker
docker run --env-file .env -p 8000:8000 ghcr.io/svalgaard/gny:latest
Client Setup
The recommended certbot plugin is certbot-dns-gny, available from PyPI:
pip install certbot-dns-gny
Refer to its documentation for credentials file format and certbot integration.
API
All endpoints are under /api. Full spec: openapi.yaml.
Enrollment workflow
1. Enroll
POST /api/enroll
Content-Type: application/json
{ "mail": "admin@example.com" }
Returns a Bearer token, e.g., gny-ab3df26e48b2467bf705156d4c8914f2. The server's IP must resolve to a PTR record or the request is rejected with 409.
2. Confirm enrollment
Open the following URL in a browser as an administrator (a user with access_level >= 1):
GET /api/enroll/start?token={token}
This redirects to the configured OIDC provider (Google, Azure AD, etc.). After authenticating, the provider redirects back to /.well-known/sso, which upserts the user record and activates the enrollment token.
On first login, every user is created with access_level = 0. An existing administrator must grant access before a new user can confirm enrollments:
UPDATE users SET access_level = 1 WHERE mail = 'admin@example.com';
Alternatively, the /api/enroll/confirm endpoint accepts an OIDC Bearer token directly and confirms the enrollment programmatically (still requires access_level >= 1).
Managing TXT records
All requests below require Authorization: Bearer {enrollment_token}.
Add a record
POST /api/txt?name=_acme-challenge.example.com&text=<validation_token>
Delete a record
DELETE /api/txt?name=_acme-challenge.example.com&text=<validation_token>
Test authorization
GET /api/txt/test?name=_acme-challenge.example.com
Returns {"status": "ok"} if the authenticated server is allowed to manage that name, or 403 otherwise.
Authorization rules
A server enrolled from IP 1.2.3.4 whose PTR record resolves to server.example.com may manage any TXT record whose domain component (after stripping a leading _acme-challenge.) equals or is a subdomain of server.example.com.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file gny-0.0.3.tar.gz.
File metadata
- Download URL: gny-0.0.3.tar.gz
- Upload date:
- Size: 28.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
59de4ff6aa60b239c1a2ecf5e20ff74ff6b4973494667c9cdaf2c695f299e62f
|
|
| MD5 |
feaf58dfd2fd0540444a0b8c76196693
|
|
| BLAKE2b-256 |
b0ef99a5343aadc14532637504eb67c3f36cff6026bc9d9cb8ac951a0ab13844
|
Provenance
The following attestation bundles were made for gny-0.0.3.tar.gz:
Publisher:
release.yml on svalgaard/gny
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
gny-0.0.3.tar.gz -
Subject digest:
59de4ff6aa60b239c1a2ecf5e20ff74ff6b4973494667c9cdaf2c695f299e62f - Sigstore transparency entry: 1287637160
- Sigstore integration time:
-
Permalink:
svalgaard/gny@0b6e5d5dcbb8faa57506f2fc86f5a71eaa85748d -
Branch / Tag:
refs/tags/v0.0.3 - Owner: https://github.com/svalgaard
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@0b6e5d5dcbb8faa57506f2fc86f5a71eaa85748d -
Trigger Event:
push
-
Statement type:
File details
Details for the file gny-0.0.3-py3-none-any.whl.
File metadata
- Download URL: gny-0.0.3-py3-none-any.whl
- Upload date:
- Size: 24.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3f3294f3a31f202cfb12670be40b7497eea96104c3235cb04425582989b8a7dc
|
|
| MD5 |
6c12460edb1b23fa0d0ba9e1dcd05d94
|
|
| BLAKE2b-256 |
1f95c07ea55b5aac7a1afc1b8b7da63562c1192d54efe51c6d6f7cff09c56014
|
Provenance
The following attestation bundles were made for gny-0.0.3-py3-none-any.whl:
Publisher:
release.yml on svalgaard/gny
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
gny-0.0.3-py3-none-any.whl -
Subject digest:
3f3294f3a31f202cfb12670be40b7497eea96104c3235cb04425582989b8a7dc - Sigstore transparency entry: 1287637216
- Sigstore integration time:
-
Permalink:
svalgaard/gny@0b6e5d5dcbb8faa57506f2fc86f5a71eaa85748d -
Branch / Tag:
refs/tags/v0.0.3 - Owner: https://github.com/svalgaard
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@0b6e5d5dcbb8faa57506f2fc86f5a71eaa85748d -
Trigger Event:
push
-
Statement type: