add dehydrated-powerdns-hook
This commit is contained in:
parent
d6c3872aaa
commit
0a405edbc4
1 changed files with 103 additions and 0 deletions
103
dehydrated-powerdns-hook.py
Normal file
103
dehydrated-powerdns-hook.py
Normal file
|
@ -0,0 +1,103 @@
|
|||
#!/usr/bin/env python3
|
||||
import requests
|
||||
import sys
|
||||
from configparser import ConfigParser
|
||||
|
||||
CONFIG_FILE = "/etc/dehydrated/hook.ini"
|
||||
# contents should look something like this:
|
||||
# [powerdns]
|
||||
# endpoint = https://dns.example.lol/api/v1/servers/localhost
|
||||
# api_key = lololololol
|
||||
|
||||
config = ConfigParser()
|
||||
config.read("/etc/dehydrated/hook.ini")
|
||||
|
||||
def deploy_challenge(domain, token_filename, token_value):
|
||||
endpoint = config["powerdns"]["endpoint"]
|
||||
headers = {"X-Api-Key": config["powerdns"]["api_key"], "Content-Type": "application/json"}
|
||||
|
||||
# list all zones, try to find one that matches
|
||||
zones = requests.get(f"{endpoint}/zones", headers=headers).json()
|
||||
|
||||
zone = None
|
||||
for z in zones:
|
||||
if domain.endswith(z['name'][:-1]):
|
||||
zone = z['name']
|
||||
break
|
||||
|
||||
if zone is None:
|
||||
raise Exception(f"unable to find zone for {domain}")
|
||||
|
||||
records = [{"content": f"\"{token_value}\"", "disabled": False}]
|
||||
|
||||
name = f"_acme-challenge.{domain}."
|
||||
|
||||
existing_zone = requests.get(f"{endpoint}/zones/{zone}", headers=headers).json()
|
||||
for rrset in existing_zone['rrsets']:
|
||||
if rrset["name"] == name and rrset["type"] == "TXT":
|
||||
records += rrset["records"]
|
||||
|
||||
patchdata = {
|
||||
"rrsets": [
|
||||
{
|
||||
"name": name,
|
||||
"type": "TXT",
|
||||
"ttl": 30,
|
||||
"changetype": "REPLACE",
|
||||
"records": records
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
resp = requests.patch(f"{endpoint}/zones/{zone}", headers=headers, json=patchdata)
|
||||
if not resp.ok:
|
||||
print(resp.content.decode())
|
||||
resp.raise_for_status()
|
||||
|
||||
def clean_challenge(domain, token_filename, token_value):
|
||||
endpoint = config["powerdns"]["endpoint"]
|
||||
headers = {"X-Api-Key": config["powerdns"]["api_key"], "Content-Type": "application/json"}
|
||||
|
||||
# list all zones, try to find one that matches
|
||||
zones = requests.get(f"{endpoint}/zones", headers=headers).json()
|
||||
|
||||
zone = None
|
||||
for z in zones:
|
||||
if domain.endswith(z['name'][:-1]):
|
||||
zone = z['name']
|
||||
break
|
||||
|
||||
if zone is None:
|
||||
raise Exception(f"unable to find zone for {domain}")
|
||||
|
||||
records = []
|
||||
name = f"_acme-challenge.{domain}."
|
||||
|
||||
existing_zone = requests.get(f"{endpoint}/zones/{zone}", headers=headers).json()
|
||||
for rrset in existing_zone['rrsets']:
|
||||
if rrset["name"] == name and rrset["type"] == "TXT":
|
||||
# preserve all records that don't match the one we are supposed to be cleaning
|
||||
records += [x for x in rrset["records"] if x["content"] != f"\"{token_value}\""]
|
||||
|
||||
patchdata = {
|
||||
"rrsets": [
|
||||
{
|
||||
"name": name,
|
||||
"type": "TXT",
|
||||
"ttl": 30,
|
||||
"changetype": "REPLACE",
|
||||
"records": records
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
resp = requests.patch(f"{endpoint}/zones/{zone}", headers=headers, json=patchdata)
|
||||
if not resp.ok:
|
||||
print(resp.content.decode())
|
||||
resp.raise_for_status()
|
||||
|
||||
if __name__ == "__main__":
|
||||
if sys.argv[1] == "deploy_challenge":
|
||||
deploy_challenge(sys.argv[2], sys.argv[3], sys.argv[4])
|
||||
elif sys.argv[1] == "clean_challenge":
|
||||
clean_challenge(sys.argv[2], sys.argv[3], sys.argv[4])
|
Loading…
Reference in a new issue