Compare commits
No commits in common. "702cb85df8798954737c93a4bd978d9a711ed343" and "812eaa893dfdceaa813bd3ac20a1cb925e8312fc" have entirely different histories.
702cb85df8
...
812eaa893d
|
@ -1,3 +1 @@
|
|||
*.secret
|
||||
**/__pycache__
|
||||
certbot
|
41
README.md
41
README.md
|
@ -1,41 +0,0 @@
|
|||
# hatecomputers.club dns updater & certbot plugin
|
||||
|
||||
this is a simple wrapper over hatecomputers.club's dns api
|
||||
|
||||
## dns creation steps
|
||||
|
||||
1. obtain an api key at [hatecomputers.club](https://hatecomputers.club)
|
||||
2. put it in `apikey.secret`
|
||||
3. modify `records.json` to your liking
|
||||
4. `./main.py --create --records-file=records.json`
|
||||
|
||||
## certbot plugin
|
||||
|
||||
follow the above to generate an api key.
|
||||
|
||||
if you use the split-zone dns provided by hatecomputers.club and run your own certificate
|
||||
authority, you can try something like:
|
||||
```bash
|
||||
REQUESTS_CA_BUNDLE=~/armin/roots.pem certbot certonly \
|
||||
--manual --manual-auth-hook ./plugin.sh \
|
||||
--preferred-challenges dns \
|
||||
-d *.internal.simponic.xyz \
|
||||
--config-dir ./certbot \
|
||||
--work-dir ./certbot \
|
||||
--logs-dir ./certbot \
|
||||
--server https://ca.internal.simponic.xyz/acme/ACME/directory \
|
||||
--email simponic@hatecomputers.club \
|
||||
--agree-tos \
|
||||
--no-eff-email
|
||||
```
|
||||
|
||||
otherwise:
|
||||
```bash
|
||||
sudo certbot certonly \
|
||||
--manual --manual-auth-hook ./plugin.sh \
|
||||
--preferred-challenges dns \
|
||||
-d *.simponic.hatecomputers.club \
|
||||
--email simponic@hatecomputers.club \
|
||||
--agree-tos \
|
||||
--no-eff-email
|
||||
```
|
59
args.py
59
args.py
|
@ -1,59 +0,0 @@
|
|||
import argparse
|
||||
|
||||
|
||||
def get_args():
|
||||
parser = argparse.ArgumentParser()
|
||||
|
||||
parser.add_argument(
|
||||
"--endpoint", default="https://hatecomputers.club", help="API endpoint"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--api-key-file",
|
||||
default="apikey.secret",
|
||||
help="path to file containing the API key",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--log-level",
|
||||
default="INFO",
|
||||
choices=["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"],
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--public-suffixes",
|
||||
default="hatecomputers.club",
|
||||
help="comma separated list of public suffixes",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--dns-propogate-time",
|
||||
default=20,
|
||||
type=int,
|
||||
help="time to sleep to allow DNS to propogate",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--certbot", action="store_true", default=False, help="enable certbot mode"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--certbot-domain", required=False, help="splat/domain to validate with certbot"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--certbot-validation", required=False, help="validation token for certbot"
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--create",
|
||||
action="store_true",
|
||||
default=False,
|
||||
help="upload records file to API to sync",
|
||||
)
|
||||
parser.add_argument("--records-file", default="records.json", help="records file")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if (args.certbot) and (not args.certbot_domain):
|
||||
parser.error("--certbot-domain is required when --certbot is used")
|
||||
if (args.certbot) and (not args.certbot_validation):
|
||||
parser.error("--certbot-validation is required when --certbot is used")
|
||||
if args.certbot:
|
||||
args.public_suffixes = args.public_suffixes.split(",")
|
||||
|
||||
return args
|
57
main.py
57
main.py
|
@ -1,57 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
import os
|
||||
import json
|
||||
import logging
|
||||
import time
|
||||
|
||||
from updater.adapter import HatecomputersDNSAdapter
|
||||
from updater.utils import record_transformer
|
||||
from args import get_args
|
||||
|
||||
|
||||
def certbot_mode(args, dns_api_adapter, record_transformer):
|
||||
domain = args.certbot_domain
|
||||
if domain.startswith("*."):
|
||||
domain = domain[2:]
|
||||
logging.info(f"processing domain {domain}")
|
||||
|
||||
record = {
|
||||
"ttl": 60,
|
||||
"name": "_acme-challenge." + domain,
|
||||
"type": "TXT",
|
||||
"content": args.certbot_validation,
|
||||
}
|
||||
record = record_transformer(record)
|
||||
logging.info(f"creating record {record}")
|
||||
dns_api_adapter.post_record(record)
|
||||
|
||||
logging.info(
|
||||
f"eeping out for {args.dns_propogate_time}s, to allow DNS propogation. look at this cute little guy 🐢 until then!!"
|
||||
)
|
||||
time.sleep(args.dns_propogate_time)
|
||||
|
||||
logging.info(f"updating record for {domain} with {args.certbot_validation}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
args = get_args()
|
||||
logging.basicConfig()
|
||||
logging.root.setLevel(args.log_level)
|
||||
|
||||
api_key = open(args.api_key_file, "r").read().strip()
|
||||
dns_api_adapter = HatecomputersDNSAdapter(args.endpoint, api_key)
|
||||
|
||||
if args.create:
|
||||
records_file = open(args.records_file, "r")
|
||||
dns_records = json.load(records_file)
|
||||
dns_api_adapter.post_records(dns_records)
|
||||
|
||||
if args.certbot:
|
||||
certbot_mode(
|
||||
args,
|
||||
dns_api_adapter,
|
||||
record_transformer(args.public_suffixes),
|
||||
)
|
||||
|
||||
logging.info("done")
|
14
plugin.sh
14
plugin.sh
|
@ -1,14 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
unset REQUESTS_CA_BUNDLE
|
||||
|
||||
API_KEY_FILE=/Users/lizzy/git/simponic/dns-updater/apikey.secret
|
||||
ENDPOINT=https://hatecomputers.club
|
||||
PUBLIC_SUFFIXES=.hatecomputers.club
|
||||
|
||||
./main.py --certbot \
|
||||
--public-suffixes=$PUBLIC_SUFFIXES \
|
||||
--certbot-domain=$CERTBOT_DOMAIN \
|
||||
--certbot-validation=$CERTBOT_VALIDATION \
|
||||
--endpoint=$ENDPOINT \
|
||||
--api-key-file=$API_KEY_FILE
|
38
records.json
38
records.json
|
@ -16,7 +16,7 @@
|
|||
{
|
||||
"type": "CNAME",
|
||||
"name": "vaultwarden.internal.simponic.xyz.",
|
||||
"content": "johan.internal.simponic.xyz.",
|
||||
"content": "johan.internal.simponic.xyz",
|
||||
"ttl": "43200",
|
||||
"internal": "on"
|
||||
},
|
||||
|
@ -75,41 +75,5 @@
|
|||
"content": "europa.internal.simponic.xyz.",
|
||||
"ttl": "43200",
|
||||
"internal": "on"
|
||||
},
|
||||
{
|
||||
"type": "CNAME",
|
||||
"name": "simponic.endpoints",
|
||||
"content": "levi.simponic.xyz.",
|
||||
"ttl": "43200",
|
||||
"internal": "off"
|
||||
},
|
||||
{
|
||||
"type": "CNAME",
|
||||
"name": "simponic",
|
||||
"content": "simponic.xyz.",
|
||||
"ttl": "43200",
|
||||
"internal": "off"
|
||||
},
|
||||
|
||||
{
|
||||
"type": "A",
|
||||
"name": "armin.internal.simponic.xyz",
|
||||
"content": "100.64.0.6",
|
||||
"ttl": "43200",
|
||||
"internal": "on"
|
||||
},
|
||||
{
|
||||
"type": "CNAME",
|
||||
"name": "dev.armin.internal.simponic.xyz",
|
||||
"content": "armin.internal.simponic.xyz",
|
||||
"ttl": "43200",
|
||||
"internal": "on"
|
||||
},
|
||||
{
|
||||
"type": "CNAME",
|
||||
"name": "traefik.armin.internal.simponic.xyz.",
|
||||
"content": "armin.internal.simponic.xyz.",
|
||||
"ttl": "43200",
|
||||
"internal": "on"
|
||||
}
|
||||
]
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
requests==2.31.0
|
|
@ -0,0 +1,38 @@
|
|||
import json
|
||||
import requests
|
||||
import time
|
||||
import logging
|
||||
|
||||
RECORDS_FILE = "records.json"
|
||||
ENDPOINT = "https://hatecomputers.club"
|
||||
API_KEY = open('apikey.secret', 'r').read().strip()
|
||||
|
||||
class HatecomputersDNSAdapter:
|
||||
def __init__(self, endpoint, api_key):
|
||||
self.endpoint = endpoint
|
||||
self.session = requests.Session()
|
||||
self.headers = {'Authorization': 'Bearer ' + api_key}
|
||||
self.session = requests.Session()
|
||||
|
||||
def post_record(self, record):
|
||||
endpoint = self.endpoint + "/dns"
|
||||
logging.info("adding", record, "at", endpoint)
|
||||
|
||||
self.session.post(endpoint, headers=self.headers, data=record)
|
||||
|
||||
def post_records(self, dns_entries, sleep_time=300):
|
||||
for record in dns_entries:
|
||||
self.post_record(record)
|
||||
|
||||
logging.info("sleeping", sleep_time)
|
||||
time.sleep(sleep_time)
|
||||
|
||||
if __name__ == "__main__":
|
||||
logging.basicConfig()
|
||||
logging.root.setLevel(logging.NOTSET)
|
||||
|
||||
records_file = open(RECORDS_FILE, 'r')
|
||||
dns_records = json.load(records_file)
|
||||
|
||||
adapter = HatecomputersDNSAdapter(ENDPOINT, API_KEY)
|
||||
adapter.post_records(dns_records)
|
|
@ -1,24 +0,0 @@
|
|||
import requests
|
||||
import time
|
||||
import logging
|
||||
|
||||
|
||||
class HatecomputersDNSAdapter:
|
||||
def __init__(self, endpoint, api_key, logger=None):
|
||||
self.endpoint = endpoint
|
||||
self.session = requests.Session()
|
||||
self.headers = {"Authorization": "Bearer " + api_key}
|
||||
self.logger = logger or logging.getLogger(__name__)
|
||||
|
||||
def post_record(self, record):
|
||||
endpoint = self.endpoint + "/dns"
|
||||
self.logger.info(f"adding {record} to {endpoint}")
|
||||
|
||||
self.session.post(endpoint, headers=self.headers, data=record)
|
||||
|
||||
def post_records(self, dns_entries, eepy_time=0.25):
|
||||
for record in dns_entries:
|
||||
self.post_record(record)
|
||||
|
||||
self.logger.info(f"eeping out for {eepy_time}s")
|
||||
time.sleep(eepy_time)
|
|
@ -1,21 +0,0 @@
|
|||
import logging
|
||||
|
||||
|
||||
def record_transformer(public_suffixes):
|
||||
def transform(record):
|
||||
name = record["name"]
|
||||
suffixes = [suffix for suffix in public_suffixes if name.endswith(suffix)]
|
||||
suffix = suffixes[0] if suffixes else None
|
||||
|
||||
if suffix:
|
||||
logging.debug(f"stripping {suffix} from {name} as it is a public suffix")
|
||||
|
||||
record["name"] = name[: -len(suffix)]
|
||||
record["internal"] = "off"
|
||||
return record
|
||||
|
||||
logging.debug(f"keeping {name} as it is not a public suffix")
|
||||
record["internal"] = "on"
|
||||
return record
|
||||
|
||||
return transform
|
Loading…
Reference in New Issue