diff options
Diffstat (limited to 'overlays')
| -rw-r--r-- | overlays/inwx-cdnskey/default.nix | 28 | ||||
| -rw-r--r-- | overlays/inwx-cdnskey/inwx-cdnskey.py | 95 | ||||
| -rw-r--r-- | overlays/inwx-cdnskey/python-packages.nix | 98 |
3 files changed, 221 insertions, 0 deletions
diff --git a/overlays/inwx-cdnskey/default.nix b/overlays/inwx-cdnskey/default.nix new file mode 100644 index 00000000..c19c2df5 --- /dev/null +++ b/overlays/inwx-cdnskey/default.nix | |||
| @@ -0,0 +1,28 @@ | |||
| 1 | final: prev: | ||
| 2 | let | ||
| 3 | packageOverrides = final.callPackage ./python-packages.nix {}; | ||
| 4 | inpPython = final.python39.override { inherit packageOverrides; }; | ||
| 5 | in { | ||
| 6 | inwx-cdnskey = prev.stdenv.mkDerivation rec { | ||
| 7 | name = "inwx-cdnskey"; | ||
| 8 | src = ./inwx-cdnskey.py; | ||
| 9 | |||
| 10 | phases = [ "buildPhase" "checkPhase" "installPhase" ]; | ||
| 11 | |||
| 12 | python = inpPython.withPackages (ps: with ps; [pyxdg inwx-domrobot configparser dnspython]); | ||
| 13 | |||
| 14 | buildPhase = '' | ||
| 15 | substituteAll $src inwx-cdnskey | ||
| 16 | ''; | ||
| 17 | |||
| 18 | doCheck = true; | ||
| 19 | checkPhase = '' | ||
| 20 | ${python}/bin/python -m py_compile inwx-cdnskey | ||
| 21 | ''; | ||
| 22 | |||
| 23 | installPhase = '' | ||
| 24 | install -m 0755 -D -t $out/bin \ | ||
| 25 | inwx-cdnskey | ||
| 26 | ''; | ||
| 27 | }; | ||
| 28 | } | ||
diff --git a/overlays/inwx-cdnskey/inwx-cdnskey.py b/overlays/inwx-cdnskey/inwx-cdnskey.py new file mode 100644 index 00000000..0add24a9 --- /dev/null +++ b/overlays/inwx-cdnskey/inwx-cdnskey.py | |||
| @@ -0,0 +1,95 @@ | |||
| 1 | #!@python@/bin/python | ||
| 2 | |||
| 3 | from INWX.Domrobot import ApiClient, ApiType | ||
| 4 | from xdg import BaseDirectory | ||
| 5 | import configparser | ||
| 6 | import sys | ||
| 7 | import dns.message | ||
| 8 | import dns.rdataclass | ||
| 9 | import dns.rdatatype | ||
| 10 | import dns.query | ||
| 11 | import dns.zone | ||
| 12 | import dns.rdtypes.ANY.DNSKEY | ||
| 13 | import argparse | ||
| 14 | import ipaddress | ||
| 15 | import re | ||
| 16 | import base64 | ||
| 17 | |||
| 18 | |||
| 19 | class ApiConnection: | ||
| 20 | username = '' | ||
| 21 | password = '' | ||
| 22 | shared_secret = None | ||
| 23 | api_client = None | ||
| 24 | |||
| 25 | def __init__(self, **kwargs): | ||
| 26 | config = configparser.ConfigParser() | ||
| 27 | config.read(BaseDirectory.load_config_paths('inwx-cdnskey.ini')) | ||
| 28 | self.username = config.get('INWX', 'username') | ||
| 29 | self.password = config.get('INWX', 'password') | ||
| 30 | self.shared_secret = config.get('INWX', 'shared_secret', fallback=None) | ||
| 31 | self.api_client = ApiClient(api_url=ApiClient.API_LIVE_URL, api_type=ApiType.JSON_RPC, **kwargs) | ||
| 32 | |||
| 33 | def __enter__(self): | ||
| 34 | self.api_client.login(self.username, self.password, shared_secret = self.shared_secret) | ||
| 35 | return self | ||
| 36 | |||
| 37 | def __exit__(self, type, value, traceback): | ||
| 38 | self.logout() | ||
| 39 | |||
| 40 | def login(self, *args, **kwargs): | ||
| 41 | return ApiConnection.check_api_result(self.api_client.login(*args, **kwargs), msg='API login error') | ||
| 42 | |||
| 43 | def logout(self, *args, **kwargs): | ||
| 44 | return ApiConnection.check_api_result(self.api_client.logout(*args, **kwargs), msg='API logout error', success_code=1500) | ||
| 45 | |||
| 46 | def call_api(self, *args, **kwargs): | ||
| 47 | return ApiConnection.check_api_result(self.api_client.call_api(*args, **kwargs), msg='API error') | ||
| 48 | |||
| 49 | @staticmethod | ||
| 50 | def check_api_result(result, msg='API error', success_code=1000): | ||
| 51 | if result['code'] != success_code: | ||
| 52 | raise ApiConnection.format_exception(msg, result) | ||
| 53 | return result | ||
| 54 | |||
| 55 | @staticmethod | ||
| 56 | def format_exception(msg, result): | ||
| 57 | return Exception(f"{msg}. Code: {result['code']} Message: {result['msg']}") | ||
| 58 | |||
| 59 | |||
| 60 | |||
| 61 | def main(): | ||
| 62 | parser = argparse.ArgumentParser(prog = "inwx-cdnskey") | ||
| 63 | parser.add_argument('--resolver', metavar='ADDRESS', type=ipaddress.ip_address, default='127.0.0.1') | ||
| 64 | parser.add_argument('domains', metavar='DOMAIN', nargs='+') | ||
| 65 | args = parser.parse_args() | ||
| 66 | |||
| 67 | with ApiConnection(debug_mode=False) as api_conn: | ||
| 68 | for domain in args.domains: | ||
| 69 | active_keys = set() | ||
| 70 | deleted_keys = set() | ||
| 71 | api_data = api_conn.call_api('dnssec.listkeys', {'domainName': domain})['resData'] | ||
| 72 | for dat in api_data: | ||
| 73 | if dat.get('publicKey') is None: | ||
| 74 | continue | ||
| 75 | |||
| 76 | dnskey_rdat = dns.rdtypes.ANY.DNSKEY.DNSKEY(dns.rdataclass.IN, dns.rdatatype.DNSKEY, int(dat['flagId']), 3, int(dat['algorithmId']), base64.b64decode(dat['publicKey'])) | ||
| 77 | if dat['status'] == 'DELETED': | ||
| 78 | deleted_keys.add(dnskey_rdat) | ||
| 79 | else: | ||
| 80 | active_keys.add(dnskey_rdat) | ||
| 81 | |||
| 82 | dns_keys = set() | ||
| 83 | qname = dns.name.from_text(domain) | ||
| 84 | q = dns.message.make_query(qname, dns.rdatatype.CDNSKEY) | ||
| 85 | r, _ = dns.query.udp_with_fallback(q, str(args.resolver)) | ||
| 86 | for rdat in r.find_rrset(dns.message.ANSWER, qname, dns.rdataclass.IN, dns.rdatatype.CDNSKEY): | ||
| 87 | dnskey_rdat = dns.rdtypes.ANY.DNSKEY.DNSKEY(dns.rdataclass.IN, dns.rdatatype.DNSKEY, rdat.flags, rdat.protocol, rdat.algorithm, rdat.key) | ||
| 88 | dns_keys.add(dnskey_rdat) | ||
| 89 | |||
| 90 | print('delete: ', active_keys - dns_keys) | ||
| 91 | print('add: ', dns_keys - active_keys) | ||
| 92 | |||
| 93 | |||
| 94 | if __name__ == '__main__': | ||
| 95 | sys.exit(main()) | ||
diff --git a/overlays/inwx-cdnskey/python-packages.nix b/overlays/inwx-cdnskey/python-packages.nix new file mode 100644 index 00000000..e22f223e --- /dev/null +++ b/overlays/inwx-cdnskey/python-packages.nix | |||
| @@ -0,0 +1,98 @@ | |||
| 1 | # Generated by pip2nix 0.8.0.dev1 | ||
| 2 | # See https://github.com/nix-community/pip2nix | ||
| 3 | |||
| 4 | { pkgs, fetchurl, fetchgit, fetchhg }: | ||
| 5 | |||
| 6 | self: super: { | ||
| 7 | "certifi" = super.buildPythonPackage rec { | ||
| 8 | pname = "certifi"; | ||
| 9 | version = "2021.10.8"; | ||
| 10 | src = fetchurl { | ||
| 11 | url = "https://files.pythonhosted.org/packages/37/45/946c02767aabb873146011e665728b680884cd8fe70dde973c640e45b775/certifi-2021.10.8-py2.py3-none-any.whl"; | ||
| 12 | sha256 = "0scm6gbbk4gfwb1flivxihnim9wragvvvcia0jn488scxdih2ann"; | ||
| 13 | }; | ||
| 14 | format = "wheel"; | ||
| 15 | doCheck = false; | ||
| 16 | buildInputs = []; | ||
| 17 | checkInputs = []; | ||
| 18 | nativeBuildInputs = []; | ||
| 19 | propagatedBuildInputs = []; | ||
| 20 | }; | ||
| 21 | "charset-normalizer" = super.buildPythonPackage rec { | ||
| 22 | pname = "charset-normalizer"; | ||
| 23 | version = "2.0.12"; | ||
| 24 | src = fetchurl { | ||
| 25 | url = "https://files.pythonhosted.org/packages/06/b3/24afc8868eba069a7f03650ac750a778862dc34941a4bebeb58706715726/charset_normalizer-2.0.12-py3-none-any.whl"; | ||
| 26 | sha256 = "1pxim0sfz7gq157k9kv88kxxzvgnid1ip0maxas3jyxipnzfv0b8"; | ||
| 27 | }; | ||
| 28 | format = "wheel"; | ||
| 29 | doCheck = false; | ||
| 30 | buildInputs = []; | ||
| 31 | checkInputs = []; | ||
| 32 | nativeBuildInputs = []; | ||
| 33 | propagatedBuildInputs = []; | ||
| 34 | }; | ||
| 35 | "idna" = super.buildPythonPackage rec { | ||
| 36 | pname = "idna"; | ||
| 37 | version = "3.3"; | ||
| 38 | src = fetchurl { | ||
| 39 | url = "https://files.pythonhosted.org/packages/04/a2/d918dcd22354d8958fe113e1a3630137e0fc8b44859ade3063982eacd2a4/idna-3.3-py3-none-any.whl"; | ||
| 40 | sha256 = "1zrm4xnjas13byafi11ma2q8h5rr1fmjwvi41xp5k07sgw2dvnc4"; | ||
| 41 | }; | ||
| 42 | format = "wheel"; | ||
| 43 | doCheck = false; | ||
| 44 | buildInputs = []; | ||
| 45 | checkInputs = []; | ||
| 46 | nativeBuildInputs = []; | ||
| 47 | propagatedBuildInputs = []; | ||
| 48 | }; | ||
| 49 | "inwx-domrobot" = super.buildPythonPackage rec { | ||
| 50 | pname = "inwx-domrobot"; | ||
| 51 | version = "3.1.0"; | ||
| 52 | src = fetchurl { | ||
| 53 | url = "https://files.pythonhosted.org/packages/ad/8f/ceda033f32e0c50285cbee1ef202c2a8c48d05126f773bb75ffa845d8905/inwx_domrobot-3.1.0-py3-none-any.whl"; | ||
| 54 | sha256 = "19z7yb2qgzkarwmilclag5wld5gw4cijmgnhrlnhc44fwgxndmrg"; | ||
| 55 | }; | ||
| 56 | format = "wheel"; | ||
| 57 | doCheck = false; | ||
| 58 | buildInputs = []; | ||
| 59 | checkInputs = []; | ||
| 60 | nativeBuildInputs = []; | ||
| 61 | propagatedBuildInputs = [ | ||
| 62 | self."requests" | ||
| 63 | ]; | ||
| 64 | }; | ||
| 65 | "requests" = super.buildPythonPackage rec { | ||
| 66 | pname = "requests"; | ||
| 67 | version = "2.27.1"; | ||
| 68 | src = fetchurl { | ||
| 69 | url = "https://files.pythonhosted.org/packages/2d/61/08076519c80041bc0ffa1a8af0cbd3bf3e2b62af10435d269a9d0f40564d/requests-2.27.1-py2.py3-none-any.whl"; | ||
| 70 | sha256 = "0bg7c13nfm400gx7wn5kjbjfjyz1b6bwf6p4wqbgvpf9akjs2bzj"; | ||
| 71 | }; | ||
| 72 | format = "wheel"; | ||
| 73 | doCheck = false; | ||
| 74 | buildInputs = []; | ||
| 75 | checkInputs = []; | ||
| 76 | nativeBuildInputs = []; | ||
| 77 | propagatedBuildInputs = [ | ||
| 78 | self."certifi" | ||
| 79 | self."charset-normalizer" | ||
| 80 | self."idna" | ||
| 81 | self."urllib3" | ||
| 82 | ]; | ||
| 83 | }; | ||
| 84 | "urllib3" = super.buildPythonPackage rec { | ||
| 85 | pname = "urllib3"; | ||
| 86 | version = "1.26.8"; | ||
| 87 | src = fetchurl { | ||
| 88 | url = "https://files.pythonhosted.org/packages/4e/b8/f5a25b22e803f0578e668daa33ba3701bb37858ec80e08a150bd7d2cf1b1/urllib3-1.26.8-py2.py3-none-any.whl"; | ||
| 89 | sha256 = "1v976ijsh5zxyjziwqhqvyzj2mrhhpp26w3c3hjw4cx2f7saf300"; | ||
| 90 | }; | ||
| 91 | format = "wheel"; | ||
| 92 | doCheck = false; | ||
| 93 | buildInputs = []; | ||
| 94 | checkInputs = []; | ||
| 95 | nativeBuildInputs = []; | ||
| 96 | propagatedBuildInputs = []; | ||
| 97 | }; | ||
| 98 | } | ||
