mirror of
https://gitlab.com/keys.openpgp.org/hagrid.git
synced 2025-10-05 16:12:44 +02:00
wkd: update a bit, and add to flake
This commit is contained in:
@@ -8,9 +8,13 @@
|
||||
pkgs = nixpkgs.legacyPackages."${system}";
|
||||
in rec {
|
||||
packages.hagrid = pkgs.callPackage ./. { };
|
||||
packages.wkdDomainChecker = pkgs.callPackage ./wkd-domain-checker/. { };
|
||||
|
||||
packages.default = packages.hagrid;
|
||||
}) // {
|
||||
overlays.hagrid = (final: prev: { hagrid = self.packages."${final.system}".hagrid; });
|
||||
overlays.wkdDomainChecker = (final: prev: { wkdDomainChecker = self.packages."${final.system}".wkdDomainChecker; });
|
||||
|
||||
overlays.default = self.overlays.hagrid;
|
||||
};
|
||||
}
|
||||
|
22
wkd-domain-checker/default.nix
Normal file
22
wkd-domain-checker/default.nix
Normal file
@@ -0,0 +1,22 @@
|
||||
{ lib, python3Packages }:
|
||||
|
||||
python3Packages.buildPythonApplication {
|
||||
pname = "wkd-domain-checker";
|
||||
version = "1.0";
|
||||
|
||||
propagatedBuildInputs = with python3Packages; [
|
||||
flask
|
||||
publicsuffix2
|
||||
requests
|
||||
];
|
||||
|
||||
src = ./.;
|
||||
|
||||
meta = with lib; {
|
||||
description = "WKD domain checker for hagrid wkd gateway";
|
||||
homepage = "https://gitlab.com/keys.openpgp.org/hagrid";
|
||||
license = with licenses; [ gpl3 ];
|
||||
maintainers = with maintainers; [ valodim ];
|
||||
platforms = platforms.all;
|
||||
};
|
||||
}
|
@@ -1,13 +1,14 @@
|
||||
certifi==2019.11.28
|
||||
chardet==3.0.4
|
||||
Click==7.0
|
||||
Flask==1.1.1
|
||||
gunicorn==20.0.4
|
||||
idna==2.8
|
||||
itsdangerous==1.1.0
|
||||
Jinja2==2.11.0
|
||||
MarkupSafe==1.1.1
|
||||
# just for reference, this is canonically built using default.nix
|
||||
blinker==1.9.0
|
||||
certifi==2025.1.31
|
||||
charset-normalizer==3.4.1
|
||||
click==8.1.8
|
||||
Flask==3.1.0
|
||||
idna==3.10
|
||||
itsdangerous==2.2.0
|
||||
Jinja2==3.1.5
|
||||
MarkupSafe==3.0.2
|
||||
publicsuffix2==2.20191221
|
||||
requests==2.22.0
|
||||
urllib3==1.25.8
|
||||
Werkzeug==0.16.1
|
||||
requests==2.32.3
|
||||
urllib3==2.3.0
|
||||
Werkzeug==3.1.3
|
||||
|
12
wkd-domain-checker/setup.py
Normal file
12
wkd-domain-checker/setup.py
Normal file
@@ -0,0 +1,12 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
from setuptools import setup, find_packages
|
||||
|
||||
setup(
|
||||
name='wkd-domain-checker',
|
||||
version='1.0',
|
||||
# Modules to import from other scripts:
|
||||
packages=find_packages(),
|
||||
# Executables
|
||||
scripts=["wkd-domain-checker.py"],
|
||||
)
|
@@ -1,3 +1,5 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# Simple flask server that checks whether a domain is allowed as a WKD target.
|
||||
# Most importantly, this determines whether we attempt to request a certificate
|
||||
# from letsencrypt for it.
|
||||
@@ -10,25 +12,35 @@
|
||||
# - it must be a CNAME that points to wkd.keys.openpgp.org. We do a simple DoH
|
||||
# request to cloudflare to make sure it looks correct from someone else's
|
||||
# perspective.
|
||||
#
|
||||
# Configuration via environment variables:
|
||||
#
|
||||
# - FLASK_GATEWAY_DOMAIN must be set to gateway domain (e.g. wkd.keys.openpgp.org)
|
||||
# - FLASK_ALLOWLIST_FILE may point to a file that contains an explicit allowlist of domains, one per line
|
||||
|
||||
import requests
|
||||
from publicsuffix2 import get_sld
|
||||
from flask import Flask, request, abort, escape
|
||||
app = Flask(__name__)
|
||||
import publicsuffix2
|
||||
import flask
|
||||
import markupsafe
|
||||
import sys
|
||||
|
||||
GATEWAY_DOMAIN = 'wkd.keys.openpgp.org'
|
||||
app = flask.Flask('wkd-domain-checker')
|
||||
app.config.from_prefixed_env()
|
||||
if 'GATEWAY_DOMAIN' not in app.config:
|
||||
app.logger.error('missing config: FLASK_GATEWAY_DOMAIN')
|
||||
sys.exit(1)
|
||||
|
||||
# a manual whitelist of domains. we don't allow arbitrary subdomains for abuse
|
||||
# reasons, but other entries are generally possible. just ask.
|
||||
WHITELIST = [
|
||||
'openpgpkey.keys.openpgp.org',
|
||||
'openpgpkey.my.amazin.horse'
|
||||
]
|
||||
app.config.from_envvar('ALLOWLIST_FILE', silent=True)
|
||||
if 'ALLOWLIST_FILE' in app.config:
|
||||
with open(app.config['ALLOWLIST_FILE'], 'r') as f:
|
||||
app.config['EXPLICIT_ALLOWED_DOMAINS'] = f.read().splitlines()
|
||||
else:
|
||||
app.config['EXPLICIT_ALLOWED_DOMAINS'] = []
|
||||
|
||||
@app.route('/status/')
|
||||
@app.route('/')
|
||||
def check():
|
||||
domain = request.args.get('domain')
|
||||
domain = flask.request.args.get('domain')
|
||||
if not domain:
|
||||
return 'missing parameter: domain\n', 400
|
||||
|
||||
@@ -37,13 +49,15 @@ def check():
|
||||
return result
|
||||
|
||||
def check_domain(domain):
|
||||
if domain in WHITELIST:
|
||||
return 'ok: domain is whitelisted\n'
|
||||
# check allowlist of domains. we don't allow arbitrary subdomains for abuse
|
||||
# reasons, but other entries are generally possible. just ask.
|
||||
if domain in app.config['EXPLICIT_ALLOWED_DOMAINS']:
|
||||
return 'ok: domain is allowlisted\n'
|
||||
|
||||
if not domain.startswith('openpgpkey.'):
|
||||
return 'domain must have "openpgpkey" prefix\n', 400
|
||||
|
||||
if domain != ("openpgpkey." + get_sld(domain)):
|
||||
if domain != ("openpgpkey." + publicsuffix2.get_sld(domain)):
|
||||
return 'subdomains can only be used upon request. send an email to <tt>support at keys dot openpgp dot org</tt>\n', 400
|
||||
|
||||
req = requests.get(
|
||||
@@ -60,7 +74,7 @@ def check_domain(domain):
|
||||
|
||||
if req.status_code != 200:
|
||||
app.logger.debug(f'dns error: {req.status_code} {req.text})')
|
||||
abort(400, f'CNAME lookup failed (http {req.status_code})')
|
||||
flask.abort(400, f'CNAME lookup failed (http {req.status_code})')
|
||||
response = req.json()
|
||||
app.logger.debug(f'response json: {response}')
|
||||
|
||||
@@ -76,10 +90,10 @@ def check_domain(domain):
|
||||
if answer['type'] != 5:
|
||||
return 'CNAME lookup failed: unexpected response (record type)\n', 400
|
||||
if answer['name'] != domain and answer['name'] != f'{domain}.':
|
||||
return f'CNAME lookup failed: unexpected response (domain response was for {escape(domain)})\n', 400
|
||||
if not answer['data'].startswith(GATEWAY_DOMAIN):
|
||||
return f'CNAME lookup failed: {escape(domain)} resolves to {escape(answer["data"])} (expected {GATEWAY_DOMAIN})\n', 400
|
||||
return f'CNAME lookup ok: {escape(domain)} resolves to {GATEWAY_DOMAIN}\n'
|
||||
return f'CNAME lookup failed: unexpected response (domain response was for {markupsafe.escape(domain)})\n', 400
|
||||
if not answer['data'].startswith(app.config['GATEWAY_DOMAIN']):
|
||||
return f'CNAME lookup failed: {markupsafe.escape(domain)} resolves to {markupsafe.escape(answer["data"])} (expected {GATEWAY_DOMAIN})\n', 400
|
||||
return f'CNAME lookup ok: {markupsafe.escape(domain)} resolves to {GATEWAY_DOMAIN}\n'
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.run()
|
||||
|
Reference in New Issue
Block a user