v3.0.0: authenticate with a yk_ App key, not email/app_password
The email+app_password -> /api/v1/auth/login bearer mint was retired with personal app passwords (dns commit 834c90e). Switch to sending a yeil App key (yk_<keyId>_<secret>) directly as the Bearer token, which the DNS API's principal auth accepts. Single credential 'dns_yeil_api_key'; removed the login round-trip. BREAKING: existing credential files must replace email/app_password with an api_key (an App with DNS record-write permission, minted in team Apps). README + version bumped. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
33
README.md
33
README.md
@@ -2,10 +2,11 @@
|
|||||||
|
|
||||||
yeil DNS Authenticator plugin for [Certbot](https://certbot.eff.org/).
|
yeil DNS Authenticator plugin for [Certbot](https://certbot.eff.org/).
|
||||||
|
|
||||||
Authenticates against `dns.yeil.app`'s public API with an email and an
|
Authenticates to `dns.yeil.app`'s public API with a yeil **App key**
|
||||||
app password, then adds/removes TXT records to satisfy ACME DNS-01
|
(`yk_...`) sent as a Bearer token, then adds/removes TXT records to
|
||||||
challenges. Works for any yeil user with an owned DNS zone; the
|
satisfy ACME DNS-01 challenges. Works for any yeil team with an App that
|
||||||
certbot host just needs HTTPS reachability to `dns.yeil.app`.
|
has DNS record-write permission; the certbot host just needs HTTPS
|
||||||
|
reachability to `dns.yeil.app`.
|
||||||
|
|
||||||
Wildcard certs require DNS-01, so this plugin (or another DNS
|
Wildcard certs require DNS-01, so this plugin (or another DNS
|
||||||
authenticator) is needed for `*.example.com`.
|
authenticator) is needed for `*.example.com`.
|
||||||
@@ -18,16 +19,20 @@ pip install git+https://git.eskimo.dev/Yeil/certbot-dns-yeil.git
|
|||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
Create an app password at `https://account.yeil.app/security` and
|
In your yeil team settings, open **Apps**, create an App, grant it DNS
|
||||||
drop it into a credentials INI:
|
**record-write** permission on the zone(s) you'll issue certs for, and
|
||||||
|
mint a key. Drop the key (`yk_...`) into a credentials INI:
|
||||||
|
|
||||||
```ini
|
```ini
|
||||||
dns_yeil_email = you@yourdomain.com
|
dns_yeil_api_key = yk_xxxxxxxx_yyyyyyyyyyyyyyyyyyyyyyyy
|
||||||
dns_yeil_app_password = abcd-efgh-ijkl-mnop
|
|
||||||
```
|
```
|
||||||
|
|
||||||
`chmod 600` it.
|
`chmod 600` it.
|
||||||
|
|
||||||
|
> Migrating from 2.x: the old `dns_yeil_email` / `dns_yeil_app_password`
|
||||||
|
> login was retired with personal app passwords. Replace those two lines
|
||||||
|
> with a single `dns_yeil_api_key`.
|
||||||
|
|
||||||
Optional override if you're testing against a non-production host:
|
Optional override if you're testing against a non-production host:
|
||||||
|
|
||||||
```ini
|
```ini
|
||||||
@@ -55,13 +60,13 @@ certbot certonly \
|
|||||||
|
|
||||||
## How it works
|
## How it works
|
||||||
|
|
||||||
The plugin logs in once per run (`POST /api/v1/auth/login`) and caches
|
The plugin sends the App key as a Bearer token on every request. For
|
||||||
the returned Bearer token. For each requested name it asks the API
|
each requested name it asks the API which of the App's zones covers the
|
||||||
which zone the account owns that covers the FQDN
|
FQDN (`GET /api/v1/zones?suffix_of=<fqdn>`), creates a TXT at
|
||||||
(`GET /api/v1/zones?suffix_of=<fqdn>`), creates a TXT at
|
|
||||||
`_acme-challenge.<rel>` (`POST /api/v1/zones/{id}/records`), waits for
|
`_acme-challenge.<rel>` (`POST /api/v1/zones/{id}/records`), waits for
|
||||||
propagation, and on cleanup deletes the record by id
|
propagation, and on cleanup deletes the record by id
|
||||||
(`DELETE /api/v1/zones/{id}/records/{recordId}`).
|
(`DELETE /api/v1/zones/{id}/records/{recordId}`).
|
||||||
|
|
||||||
The token is a real yeil session; revoking the app password (or
|
Revoking the App key (or disabling the App) in your team settings cuts
|
||||||
hitting `/logout`) invalidates it cleanly.
|
off access cleanly. The key only carries the DNS permissions you granted
|
||||||
|
the App, so scope it to record-write on just the zones you need.
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
"""DNS-01 authenticator plugin for Certbot using the yeil public API.
|
"""DNS-01 authenticator plugin for Certbot using the yeil public API.
|
||||||
|
|
||||||
Authenticates against dns.yeil.app/api/v1/auth/login with an
|
Authenticates to dns.yeil.app with a yeil App key (yk_...) sent as a
|
||||||
email + app password, caches the Bearer token for the run, then
|
Bearer token, then adds/removes TXT records via the public records API.
|
||||||
adds/removes TXT records via the public records API. Any yeil user
|
Create an App with DNS record-write permission on your zone(s) in your
|
||||||
with an app password and an owned DNS zone can use it.
|
yeil team settings (the Apps tab) and put its key in the credentials file.
|
||||||
|
|
||||||
The certbot host only needs HTTPS reachability to dns.yeil.app; no
|
The certbot host only needs HTTPS reachability to dns.yeil.app; no
|
||||||
NetBird or shared admin key.
|
NetBird or shared admin key.
|
||||||
@@ -34,10 +34,6 @@ class Authenticator(dns_common.DNSAuthenticator):
|
|||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
self.credentials = None
|
self.credentials = None
|
||||||
# Bearer token cached for the lifetime of this plugin instance.
|
|
||||||
# The login route mints a 30-day session; we only need it for
|
|
||||||
# the duration of one certbot run.
|
|
||||||
self._token = None
|
|
||||||
# (domain, validation_name, validation) -> (zone_id, record_id)
|
# (domain, validation_name, validation) -> (zone_id, record_id)
|
||||||
self._records = {}
|
self._records = {}
|
||||||
|
|
||||||
@@ -59,8 +55,10 @@ class Authenticator(dns_common.DNSAuthenticator):
|
|||||||
"credentials",
|
"credentials",
|
||||||
"yeil API credentials INI file",
|
"yeil API credentials INI file",
|
||||||
{
|
{
|
||||||
"email": "yeil account email (e.g. you@yourdomain.com)",
|
"api_key": (
|
||||||
"app_password": "yeil app password (create one in account.yeil.app/security)",
|
"yeil App key (yk_...) with DNS record-write permission on "
|
||||||
|
"your zone(s); create an App under Apps in your team settings"
|
||||||
|
),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -85,9 +83,7 @@ class Authenticator(dns_common.DNSAuthenticator):
|
|||||||
req.add_header("Content-Type", "application/json")
|
req.add_header("Content-Type", "application/json")
|
||||||
req.add_header("Content-Length", str(len(data)))
|
req.add_header("Content-Length", str(len(data)))
|
||||||
if auth:
|
if auth:
|
||||||
if not self._token:
|
req.add_header("Authorization", f"Bearer {self._api_key()}")
|
||||||
self._login()
|
|
||||||
req.add_header("Authorization", f"Bearer {self._token}")
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
with urlopen(req, timeout=HTTP_TIMEOUT) as resp:
|
with urlopen(req, timeout=HTTP_TIMEOUT) as resp:
|
||||||
@@ -114,20 +110,14 @@ class Authenticator(dns_common.DNSAuthenticator):
|
|||||||
f"yeil dns API error ({method} {path}, {e.code}): {msg}"
|
f"yeil dns API error ({method} {path}, {e.code}): {msg}"
|
||||||
)
|
)
|
||||||
|
|
||||||
def _login(self):
|
def _api_key(self):
|
||||||
email = self.credentials.conf("email")
|
key = self.credentials.conf("api_key")
|
||||||
password = self.credentials.conf("app_password")
|
if not key:
|
||||||
result = self._request(
|
|
||||||
"POST",
|
|
||||||
"/api/v1/auth/login",
|
|
||||||
body={"email": email, "password": password},
|
|
||||||
auth=False,
|
|
||||||
)
|
|
||||||
if not isinstance(result, dict) or "token" not in result:
|
|
||||||
raise errors.PluginError(
|
raise errors.PluginError(
|
||||||
"yeil dns API login returned no token"
|
"yeil credentials file is missing 'api_key' "
|
||||||
|
"(a yk_... App key with DNS record-write permission)"
|
||||||
)
|
)
|
||||||
self._token = result["token"]
|
return key
|
||||||
|
|
||||||
# ── Zone resolution ────────────────────────────────────────────────
|
# ── Zone resolution ────────────────────────────────────────────────
|
||||||
|
|
||||||
|
|||||||
2
setup.py
2
setup.py
@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
|
|||||||
|
|
||||||
setup(
|
setup(
|
||||||
name="certbot-dns-yeil",
|
name="certbot-dns-yeil",
|
||||||
version="2.0.0",
|
version="3.0.0",
|
||||||
description="yeil DNS Authenticator plugin for Certbot",
|
description="yeil DNS Authenticator plugin for Certbot",
|
||||||
url="https://git.eskimo.dev/Yeil/certbot-dns-yeil",
|
url="https://git.eskimo.dev/Yeil/certbot-dns-yeil",
|
||||||
author="yeil",
|
author="yeil",
|
||||||
|
|||||||
Reference in New Issue
Block a user