diff --git a/Pulumi.mew.yaml b/Pulumi.mew.yaml new file mode 100644 index 00000000..d61fcb9b --- /dev/null +++ b/Pulumi.mew.yaml @@ -0,0 +1,6 @@ +config: + cloudflare:apiToken: + secure: AAABACPPwldgPSVHPYdh4lQnYR+baaGsnQRwGw/qwNke8wuFnCTdZIqj6yXu0CCGaYBu0+66LTALNNl9mo9HXs/PMLmoOqLo + tailscale:apiKey: + secure: AAABAGc7s7XJ+voSUNcMmRuVwrUdx3kojn0fdEl6qpUy0WmhgHbk6cEz2/kGSEGhuLGwo3mzOGVTI+NVu6/Xz4PmE9FME++VfE8cz5DFjDrMJ4JdX0DR + tailscale:tailnet: inskip.me diff --git a/__main__.py b/__main__.py index 5b0a49ab..25f2c100 100644 --- a/__main__.py +++ b/__main__.py @@ -3,20 +3,181 @@ import pulumi from pulumi import Output import pulumi_tailscale as tailscale -#import pulumi_cloudflare as cloudflare +import pulumi_cloudflare as cloudflare +from typing import Optional +import collections +import json +import jsonpickle tailnet = tailscale.get_devices() -domain_names = [ - "inskip.me", - "gensokyo.zone", - "kittywit.ch", - "dork.dev" -] +zones_ = {"inskip": "inskip.me"} -#domains = {zone: cloudflare.Zone( -# jump_start = False, -# resource_name = zone, -# zone = zone, -# plan = "free" -#) for zone in domain_names} +record_dict = collections.OrderedDict( + { + "inskip": { + "gmail": [ + { + "recordType": "caa", + "flags": 0, + "tag": "iodef", + "value": "mailto:acme@inskip.me", + }, + {"recordType": "caa", "flags": 0, "tag": "issuewild", "value": ";"}, + { + "recordType": "caa", + "flags": 0, + "tag": "issue", + "value": "letsencrypt.org", + }, + {"recordType": "mx", "priority": 1, "value": "aspmx.l.google.com"}, + {"recordType": "mx", "priority": 5, "value": "alt1.aspmx.l.google.com"}, + {"recordType": "mx", "priority": 5, "value": "alt2.aspmx.l.google.com"}, + { + "recordType": "mx", + "priority": 10, + "value": "alt3.aspmx.l.google.com", + }, + { + "recordType": "mx", + "priority": 10, + "value": "alt4.aspmx.l.google.com", + }, + { + "recordType": "mx", + "priority": 15, + "value": "6uyykkzhqi4zgogxiicbuamoqrxajwo5werga4byh77b2iyx3wma.mx-verification.google.com", + }, + { + "recordType": "txt", + "domain": "@", + "value": "v=spf1 include:_spf.google.com ~all", + }, + { + "recordType": "txt", + "domain": "google._domainkey", + "value": "v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAkxag/EmXQ89XQmLrBDPpPtZ7EtEJT0hgvWf/+AFiOfBOm902tq9NbTTvRJ2dLeBLPaV+hNvq2Alc7UfkKUDlLTWQjeuiC6aOnRKQQg3LZ2W25U3AlIj0jd2IPiUhg9JGV4c66XiqQ5ylTBniShfUUyeAXxbPhYFBCkBg62LZcO/tFpFsdKWtZzLjgac5vTJID+M4F8duHpkA/ZCNNUEmtt7RNQB/LLI1Gr5yR4GdQl9z7NmwtOTo9pghbZuvljr8phYjdDrwZeFTMKQnvR1l2Eh/dZ8I0C4nP5Bk4QEfmLq666P1HzOxwT6iCU6Tc+P/pkWbrx0HJh39E1aKGyLJMQIDAQAB", + }, + { + "recordType": "txt", + "domain": "_dmarc", + "value": "v=DMARC1; p=none; rua=mailto:dmarc-reports@inskip.me", + }, + ], + }, + } +) + +class DnsRecord: + def __init__(self, **data): + self.recordType = data["recordType"].upper() if "recordType" in data else None + self.zone = zones[data["zone"]] if "zone" in data else zones["inskip"] + self.recorded = None + self.priority = data["priority"] if "priority" in data else None + self.flags = data["flags"] if "flags" in data else None + self.value = data["value"] + self.domain = data["domain"] if "domain" in data else "@" + self.ttl = data["ttl"] if "ttl" in data else 3600 + self.tag = data["tag"] if "tag" in data else None + self.data = data + + def record(self): + self.name = f'{self.recordType}-{self.data["zone"] if "zone" in self.data else "inskip"}-{self.domain if self.domain != None else "@"}-{self.priority if self.priority != None else "na"}-{hash(self.value)}' + if self.recordType == "CAA": + self.recorded = cloudflare.Record( + self.name, + zone_id=self.zone.id, + type=self.recordType, + ttl=self.ttl, + name=self.domain, + data={ + "flags": self.flags, + "tag": self.tag, + "value": self.value, + }, + ) + else: + self.recorded = cloudflare.Record( + self.name, + zone_id=self.zone.id, + type=self.recordType, + priority=self.priority, + value=self.value, + ttl=self.ttl, + name=self.domain, + ) + + +class ARecord(DnsRecord): + def __init__(self, **data): + super().__init__(**data) + self.recordType = "A" + self.priority = None + self.record() + + +class AAAARecord(DnsRecord): + def __init__(self, **data): + super().__init__(**data) + self.recordType = "AAAA" + self.priority = None + self.record() + + +class MXRecord(DnsRecord): + def __init__(self, **data): + super().__init__(**data) + self.recordType = "MX" + self.record() + + +class TXTRecord(DnsRecord): + def __init__(self, **data): + super().__init__(**data) + self.recordType = "TXT" + self.priority = None + self.record() + + +class CAARecord(DnsRecord): + def __init__(self, **data): + super().__init__(**data) + self.recordType = "CAA" + self.record() + + +def ConstructorToType(type): + return { + "a": ARecord, + "aaaa": AAAARecord, + "mx": MXRecord, + "txt": TXTRecord, + "caa": CAARecord, + }[type.lower()] + + +zones = { + alias: cloudflare.Zone(alias, jump_start=False, zone=value, plan="free") + for alias, value in zones_.items() +} + +tailscale_devices_ = tailscale.get_devices() + +tailscale_devices = { + device.name: device.addresses for device in tailscale_devices_.devices +} + +records = {**{ + i: ConstructorToType(content["recordType"])(**content) + for i, content in enumerate(record_dict["inskip"]["gmail"]) +}, **{ + f"tailscale-{name.split('.')[0]}": { + recordType: ConstructorToType(recordType)( + value=addresses[0], domain=name.split(".")[0], zone="inskip" + ) + for recordType in ["A", "AAAA"] + } + for name, addresses in tailscale_devices.items() +}} + +pulumi.info(jsonpickle.encode(records, indent=2)) diff --git a/requirements.txt b/requirements.txt index d15f5895..c470dd08 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,4 @@ pulumi>=3.0.0,<4.0.0 pulumi-tailscale pulumi-cloudflare +jsonpickle diff --git a/systems/sumireko.nix b/systems/sumireko.nix index 918c9d07..df768929 100644 --- a/systems/sumireko.nix +++ b/systems/sumireko.nix @@ -77,6 +77,7 @@ _: let "boop" "obsidian" "contexts" + "rectangle" ]; taps = [ "pulumi/tap"