modified: README.md
modified: certbot_dns_netbox/dns_netbox.py
This commit is contained in:
1 parent
93248447b2
commit
da48d8ef90
2 files changed
+131
-20
No files matched your search
@@ -20,23 +20,6 @@ and the plugin **never creates or deletes records**:
|
|||||||
3. leaves the records in place after validation so they can be updated again
|
3. leaves the records in place after validation so they can be updated again
|
||||||
on the next renewal.
|
on the next renewal.
|
||||||
|
|
||||||
### Provisioning
|
|
||||||
|
|
||||||
The challenge TXT records must be created beforehand in netbox-dns, in the
|
|
||||||
view configured via `search_view` (`certbot_dns_netbox/dns_netbox.py`,
|
|
||||||
default: `global`). Each challenge token needs its own record:
|
|
||||||
|
|
||||||
| Certificate request | TXT records needed |
|
|
||||||
| ------------------------------------------------------ | ------------------ |
|
|
||||||
| `-d example.com` or `-d '*.example.com'` | 1 |
|
|
||||||
| `-d example.com -d '*.example.com'` (apex + wildcard) | 2 |
|
|
||||||
|
|
||||||
All records for a challenge share the same name (e.g. name
|
|
||||||
`_acme-challenge.unifi` in zone `ewcs.ch`); netbox-dns allows multiple TXT
|
|
||||||
records with different values for the same name. If a certificate request
|
|
||||||
needs more records than exist, certbot aborts with an error. Renewals simply
|
|
||||||
rewrite the values, so the records never need to be touched again.
|
|
||||||
|
|
||||||
Connection settings are taken from the environment, exactly as `nbapi.py`
|
Connection settings are taken from the environment, exactly as `nbapi.py`
|
||||||
expects them:
|
expects them:
|
||||||
|
|
||||||
@@ -51,12 +34,140 @@ One of `NETBOX_BEARER` / `NETBOX_TOKEN` is required. The token's user needs
|
|||||||
NetBox object permissions to view and change netbox-dns records only — no
|
NetBox object permissions to view and change netbox-dns records only — no
|
||||||
zone or view permissions are required.
|
zone or view permissions are required.
|
||||||
|
|
||||||
|
## Tutorial: end-to-end setup
|
||||||
|
|
||||||
|
This walkthrough sets up certificates for `example.com`, including the
|
||||||
|
wildcard, from scratch. Adapt domains, names and paths to your environment.
|
||||||
|
|
||||||
|
### 1. Create the DNS zone
|
||||||
|
|
||||||
|
Create the `example.com` zone in netbox-dns, in the view the plugin searches
|
||||||
|
(`search_view` in `certbot_dns_netbox/dns_netbox.py`, default: `_default_`), and
|
||||||
|
note the numeric **zone ID** of the new zone — it is shown in the web UI URL
|
||||||
|
and can be looked up with:
|
||||||
|
|
||||||
|
```
|
||||||
|
GET /api/plugins/netbox-dns/zones/?name=example.com
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Create a NetBox permission
|
||||||
|
|
||||||
|
In the NetBox admin interface, create a permission named
|
||||||
|
`ACME Client - Zone example.com`:
|
||||||
|
|
||||||
|
* **Object type**: `DNS › record` only
|
||||||
|
* **Actions**: view and change — nothing else. The plugin never creates or
|
||||||
|
deletes records, and it does not need zone or view permissions.
|
||||||
|
* **Constraint**:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{"name": "_acme-challenge", "type": "TXT", "zone_id": 0000}
|
||||||
|
```
|
||||||
|
|
||||||
|
`0000` is the zone ID from step 1. The constraint limits the token to TXT
|
||||||
|
records named `_acme-challenge` in exactly that zone. Note that certificates
|
||||||
|
for sub-domains (`-d sub.example.com`) use a record named
|
||||||
|
`_acme-challenge.sub`; in that case, relax the constraint to
|
||||||
|
`"name__startswith": "_acme-challenge"` instead of the exact `name`.
|
||||||
|
|
||||||
|
### 3. Create a service user
|
||||||
|
|
||||||
|
* Create a user group, e.g. `ACME Clients`, and assign the permission from
|
||||||
|
step 2 to the **group** — any service user added to the group later
|
||||||
|
inherits it.
|
||||||
|
* Create a user, e.g. `certbot`, without a usable login, and make it a member
|
||||||
|
of the group.
|
||||||
|
|
||||||
|
### 4. Pre-provision the TXT records
|
||||||
|
|
||||||
|
The plugin only updates records, so they must exist before the first run.
|
||||||
|
Create `_acme-challenge` TXT records in the `example.com` zone — one per
|
||||||
|
challenge token:
|
||||||
|
|
||||||
|
| Certificate request | TXT records needed |
|
||||||
|
| ----------------------------------------------------- | ------------------ |
|
||||||
|
| `-d example.com` or `-d '*.example.com'` | 1 |
|
||||||
|
| `-d example.com -d '*.example.com'` (apex + wildcard) | 2 |
|
||||||
|
|
||||||
|
All records for a challenge share the same name (`_acme-challenge`, relative
|
||||||
|
to the zone); netbox-dns allows multiple TXT records with different values
|
||||||
|
for the same name. Initial values are placeholders and get rewritten by the
|
||||||
|
plugin. If a certificate request needs more records than exist, certbot
|
||||||
|
aborts with `Not enough TXT records ...` — add another record and rerun.
|
||||||
|
|
||||||
|
### 5. Create an API token and environment file
|
||||||
|
|
||||||
|
On the `certbot` user, create an API token (leave write enabled) and expose
|
||||||
|
it to whatever runs certbot — a cronjob in most cases — via environment
|
||||||
|
variables. Keep them in a root-readable-only file, e.g.
|
||||||
|
`/etc/default/certbot-netbox`:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
NETBOX_URL=https://netbox.example.com
|
||||||
|
NETBOX_TOKEN=<token> # or: NETBOX_BEARER=<token>
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
chmod 600 /etc/default/certbot-netbox
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6. Install certbot and the plugin in a dedicated venv
|
||||||
|
|
||||||
|
Do not install certbot from the system package manager — certbot and the
|
||||||
|
plugin live together in a dedicated venv:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
python3 -m venv /opt/certbot-netbox
|
||||||
|
/opt/certbot-netbox/bin/pip install certbot certbot-dns-netbox
|
||||||
|
```
|
||||||
|
|
||||||
|
### 7. Test
|
||||||
|
|
||||||
|
```sh
|
||||||
|
/opt/certbot-netbox/bin/certbot certonly \
|
||||||
|
--authenticator dns-netbox \
|
||||||
|
-d example.com -d '*.example.com'
|
||||||
|
```
|
||||||
|
|
||||||
|
The first ever certbot run asks for an ACME account e-mail and the terms of
|
||||||
|
service. During the run the plugin writes the challenge tokens into the
|
||||||
|
`_acme-challenge` TXT records; the records remain in place afterwards. Tip:
|
||||||
|
append `--dry-run` for a first try to validate the setup against the staging
|
||||||
|
ACME API without touching rate limits.
|
||||||
|
|
||||||
|
### 8. Automate renewal
|
||||||
|
|
||||||
|
Create a wrapper that loads the environment and runs certbot from the venv,
|
||||||
|
e.g. `/usr/local/bin/certbot-netbox-renew`:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
#!/bin/sh
|
||||||
|
set -a
|
||||||
|
. /etc/default/certbot-netbox
|
||||||
|
set +a
|
||||||
|
exec /opt/certbot-netbox/bin/certbot renew --quiet
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
chmod +x /usr/local/bin/certbot-netbox-renew
|
||||||
|
```
|
||||||
|
|
||||||
|
And a cron entry, e.g. `/etc/cron.d/certbot-netbox`:
|
||||||
|
|
||||||
|
```
|
||||||
|
0 3 * * * root /usr/local/bin/certbot-netbox-renew >> /var/log/certbot-netbox-renew.log 2>&1
|
||||||
|
```
|
||||||
|
|
||||||
|
`certbot renew` reads the renewal configuration written by step 7, so no
|
||||||
|
authenticator flags are needed here. Add a `--deploy-hook` (e.g. restarting
|
||||||
|
the web server) if services need to pick up the renewed certificate.
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
Install into the same Python environment as certbot:
|
Install into the same Python environment as certbot:
|
||||||
|
|
||||||
```
|
```
|
||||||
pip install ./certbot-dns-netbox
|
pip install certbot-dns-netbox
|
||||||
```
|
```
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
@@ -107,6 +218,6 @@ certbot certonly --dry-run \
|
|||||||
-d sub.example.com # end-to-end against the staging ACME API
|
-d sub.example.com # end-to-end against the staging ACME API
|
||||||
```
|
```
|
||||||
|
|
||||||
Watch the `_acme-challenge` TXT records in the `global` view: their values
|
Watch the `_acme-challenge` TXT records in the `_default_` view: their values
|
||||||
change to the challenge tokens during the run and remain in place afterwards.
|
change to the challenge tokens during the run and remain in place afterwards.
|
||||||
The next renewal updates the same records again.
|
The next renewal updates the same records again.
|
||||||
@@ -26,7 +26,7 @@ from certbot.plugins import dns_common
|
|||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
# netbox-dns view that contains the zone with the challenge TXT record.
|
# netbox-dns view that contains the zone with the challenge TXT record.
|
||||||
search_view = "global"
|
search_view = "_default_"
|
||||||
|
|
||||||
|
|
||||||
class Authenticator(dns_common.DNSAuthenticator):
|
class Authenticator(dns_common.DNSAuthenticator):
|
||||||
|
|||||||
Reference in new issue
Block a user