commit 35a1d528401b626bca8258e06e9e934498efb350 Author: Franziska Kunsmann Date: Fri Nov 26 19:45:23 2021 +0100 initial commit diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..f0619ed --- /dev/null +++ b/.editorconfig @@ -0,0 +1,24 @@ +root = true + +[*] +indent_style = space +indent_size = 4 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.yaml] +indent_size = 2 + +[*.toml] +indent_size = 2 + +# possibly sql dumps +[*.sql] +indent_size = unset + +# bundlewrap encrypted files +[*.vault] +end_of_line = unset +insert_final_newline = unset diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bbb5845 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.secrets.cfg* +__pycache__ +*.swp diff --git a/bundles/apt b/bundles/apt new file mode 120000 index 0000000..38e6d19 --- /dev/null +++ b/bundles/apt @@ -0,0 +1 @@ +../../bundlewrap/bundles/apt \ No newline at end of file diff --git a/bundles/basic b/bundles/basic new file mode 120000 index 0000000..e8e3320 --- /dev/null +++ b/bundles/basic @@ -0,0 +1 @@ +../../bundlewrap/bundles/basic \ No newline at end of file diff --git a/bundles/nftables b/bundles/nftables new file mode 120000 index 0000000..11d6a01 --- /dev/null +++ b/bundles/nftables @@ -0,0 +1 @@ +../../bundlewrap/bundles/nftables \ No newline at end of file diff --git a/bundles/openssh b/bundles/openssh new file mode 120000 index 0000000..13fb4a8 --- /dev/null +++ b/bundles/openssh @@ -0,0 +1 @@ +../../bundlewrap/bundles/openssh \ No newline at end of file diff --git a/bundles/sudo b/bundles/sudo new file mode 120000 index 0000000..39fbe3a --- /dev/null +++ b/bundles/sudo @@ -0,0 +1 @@ +../../bundlewrap/bundles/sudo \ No newline at end of file diff --git a/bundles/sysctl b/bundles/sysctl new file mode 120000 index 0000000..1860f8f --- /dev/null +++ b/bundles/sysctl @@ -0,0 +1 @@ +../../bundlewrap/bundles/sysctl \ No newline at end of file diff --git a/bundles/systemd b/bundles/systemd new file mode 120000 index 0000000..6ffab24 --- /dev/null +++ b/bundles/systemd @@ -0,0 +1 @@ +../../bundlewrap/bundles/systemd \ No newline at end of file diff --git a/bundles/systemd-networkd b/bundles/systemd-networkd new file mode 120000 index 0000000..45c4a8e --- /dev/null +++ b/bundles/systemd-networkd @@ -0,0 +1 @@ +../../bundlewrap/bundles/systemd-networkd \ No newline at end of file diff --git a/bundles/users b/bundles/users new file mode 120000 index 0000000..3931b4f --- /dev/null +++ b/bundles/users @@ -0,0 +1 @@ +../../bundlewrap/bundles/users \ No newline at end of file diff --git a/groups.py b/groups.py new file mode 100644 index 0000000..268c341 --- /dev/null +++ b/groups.py @@ -0,0 +1,7 @@ +from os.path import join +from pathlib import Path + +groups = {} +for group in Path(join(repo_path, "groups")).rglob("*.py"): + with open(group, 'r') as f: + exec(f.read()) diff --git a/groups/all.py b/groups/all.py new file mode 100644 index 0000000..f695a4b --- /dev/null +++ b/groups/all.py @@ -0,0 +1,8 @@ +groups['all'] = { + 'member_patterns': { + r".*", + }, + 'subgroups': { + 'linux', + }, +} diff --git a/groups/os.py b/groups/os.py new file mode 100644 index 0000000..53adcca --- /dev/null +++ b/groups/os.py @@ -0,0 +1,42 @@ +groups['linux'] = { + 'subgroups': { + 'debian', + }, + 'bundles': { + 'basic', + #'cron', + 'nftables', + 'openssh', + #'postfix', + #'sshmon', + 'sudo', + 'sysctl', + 'systemd', + 'systemd-networkd', + #'telegraf', + 'users', + }, + 'metadata': { + 'apt': { + 'unattended-upgrades': { + 'mail': libs.defaults.hostmaster_email, + }, + }, + }, + 'pip_command': 'pip3', +} + +groups['debian'] = { + 'subgroups': { + 'debian-bullseye', + }, + 'bundles': { + 'apt', + #'backup-client', + }, + 'os': 'debian', +} + +groups['debian-bullseye'] = { + 'os_version': (11,) +} diff --git a/libs/defaults.py b/libs/defaults.py new file mode 100644 index 0000000..cfa7221 --- /dev/null +++ b/libs/defaults.py @@ -0,0 +1,9 @@ +hostmaster_email = 'hostmaster@kunbox.net' + +influxdb_bucket = 'encrypt$gAAAAABgg9iMnq0nKpODMiMN4NtUw231iqpbyDXV-O8epOAGDSL4jcf3CaSa2bLZzH2fJFaKWjW-dpVd384x6KqSQU19XpfsWA==' +influxdb_org = 'encrypt$gAAAAABgg9hyjz4XtvG8NBw9uYxiumS3v7YKIrtc9tTTABg1f9R22gzn55q8ULP9X3wlsPMUQs_DH7CgGv9neYmvVAriRoyd8g==' +influxdb_token = 'encrypt$gAAAAABgg9Ag632Xyuc6SWXaR1uH2tLOChmVKAoBIikhjntSSD2qJFL_eouVQGXCLH2HEuSbSdEXcTPn2qmhOiA9jmFdoDSbVbQUsp0EID1wLsWYG_Um2KOxZSF-tn9eDZlgShQYySjzO3nQRmdlJpVLUnGHsiwv_sHD2FstXGpfzTPZq5_egUqEc0K2X-aN2J6BTYc2fZAN' +influxdb_url = 'https://influxdb.kunsmann.eu/' + +security_email = f'mailto:{hostmaster_email}' +security_lang = {'en', 'de'} diff --git a/libs/tools.py b/libs/tools.py new file mode 100644 index 0000000..d96feec --- /dev/null +++ b/libs/tools.py @@ -0,0 +1,88 @@ +from ipaddress import ip_address, ip_network, IPv4Address, IPv4Network + +from bundlewrap.exceptions import NoSuchGroup, NoSuchNode, BundleError +from bundlewrap.utils.text import bold, red +from bundlewrap.utils.ui import io + +def resolve_identifier(repo, identifier): + """ + Try to resolve an identifier (group or node). Return a set of ip + addresses valid for this identifier. + """ + try: + nodes = {repo.get_node(identifier)} + except NoSuchNode: + try: + nodes = repo.nodes_in_group(identifier) + except NoSuchGroup: + try: + ip = ip_network(identifier) + + if isinstance(ip, IPv4Network): + return {'ipv4': {ip}, 'ipv6': set()} + else: + return {'ipv4': set(), 'ipv6': {ip}} + except Exception as e: + io.stderr('{x} {t} Exception while resolving "{i}": {e}'.format( + x=red('✘'), + t=bold('libs.tools.resolve_identifier'), + i=identifier, + e=str(e), + )) + raise + + found_ips = set() + for node in nodes: + for interface, config in node.metadata.get('interfaces', {}).items(): + for ip in config.get('ips', set()): + if '/' in ip: + found_ips.add(ip_address(ip.split('/')[0])) + else: + found_ips.add(ip_address(ip)) + + if node.metadata.get('external_ipv4', None): + found_ips.add(ip_address(node.metadata.get('external_ipv4'))) + + ip_dict = { + 'ipv4': set(), + 'ipv6': set(), + } + + for ip in found_ips: + if isinstance(ip, IPv4Address): + ip_dict['ipv4'].add(ip) + else: + ip_dict['ipv6'].add(ip) + + return ip_dict + + +def remove_more_specific_subnets(input_subnets) -> list: + final_subnets = [] + + for subnet in sorted(input_subnets): + source = ip_network(subnet) + + if not source in final_subnets: + subnet_found = False + + for dest_subnet in final_subnets: + if source.subnet_of(dest_subnet): + subnet_found = True + + if not subnet_found: + final_subnets.append(source) + + out = [] + for net in final_subnets: + out.append(str(net)) + + return out + + +def require_bundle(node, bundle, hint=''): + # It's considered bad style to use assert statements outside of tests. + # That's why this little helper function exists, so we have an easy + # way of defining bundle requirements in other bundles. + if not node.has_bundle(bundle): + raise BundleError(f'{node.name} requires bundle {bundle}, but wasn\'t found! {hint}') diff --git a/nodes.py b/nodes.py new file mode 100644 index 0000000..92f2ed2 --- /dev/null +++ b/nodes.py @@ -0,0 +1,17 @@ +from json import dumps as json_dumps +from os.path import join +from pathlib import Path + +import bwpass +from bundlewrap.metadata import atomic + +for node in Path(join(repo_path, "nodes")).rglob("*.py"): + with open(node, 'r') as f: + exec(f.read()) + +for name, data in nodes.items(): + if 'hostname' not in data.keys(): + data['hostname'] = '.'.join(reversed(name.split('.'))) + '.kunbox.net' + + if 'hostname' not in data['metadata'].keys(): + data['metadata']['hostname'] = '.'.join(reversed(name.split('.'))) + '.kunbox.net' diff --git a/nodes/qzwi.toml b/nodes/qzwi.toml new file mode 100644 index 0000000..9c8be77 --- /dev/null +++ b/nodes/qzwi.toml @@ -0,0 +1,11 @@ +hostname = "2a00:f820:528::4" +bundles = [] +groups = [ + "debian-bullseye", +] + +[metadata.interfaces.enp1s0] +ips = [ + "2a00:f820:528::4", +] +gateway6 = "2a00:f820:528::1" diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..f13eacb --- /dev/null +++ b/requirements.txt @@ -0,0 +1 @@ +bundlewrap>=4.12.0 diff --git a/users.json b/users.json new file mode 100644 index 0000000..674f0b4 --- /dev/null +++ b/users.json @@ -0,0 +1,19 @@ +{ + "autojenkins": { + "ssh_pubkey": [ + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHZnYhsdtGUYJiFcvfqTLljGkInnFTOoDF/WZniLtPjH" + ] + }, + "kunsi": { + "ssh_pubkey": [ + "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQC+ja1z5VRQzaKCCePsUM14qMr9QR94qlWc7Je5Poki9UmC1t/TyxRVzcCBL1ZdIfBGx6QKtfkEbvhgb3nxVt3PvXjoJrc6wwGLmNrVsU6B88y35g7nzupQiPKYJwkNzJ9j6Dmkgj1F5Q+aY2SitDaX6vqICLJ4Al/ZFw2IQxVJfC7JXRJ9jRMG5o9gWoE3gWDYEAmw+HU2mNzyeuaD12qJw9DHUimAlgkOWzll3gh9WclsYnnXGrCCn5fyHFUCJl+XXAIy519z7YTpKih02rsIOw5dnaGClBZD/YQu2ZKVFZiwIVH7aBiqHOmtgRyWTQgjbh/fMpIN0ar2f/iZsWYUjd6et48TOmXZYIPCQ5FivXNvxt9oo1XZfq76UHBwlmypLJIWROMbz375n2M6hr3hECuxuPjKEUXAv05KiC1aJ4xc6pFoVhqwAR99hvHw5U4o7/ko2NVjNpTu6Jr5DT5VaQLIdDDjC/93kUjMpdD/8P72bEn7454+WexU6OE6uvNiHj1fetrptr2UAuzVfnCoaV8pBqY7X95gk+lnSENdpr8ltJYMg8s0Z7Pzz0OxsZtzzDY5VmWfC9TCdJkN5lT8IbnaixsYlWdjQl1lMmZGElmelfU3K7YQLAbZiHmHKe4hTl9ZoCcWdTQ3d4y2t1DBos+N2HZNdtFCyOS8esDdMw== cardno:000609506971" + ], + "is_admin": true + }, + "rullmann": { + "ssh_pubkey": [ + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIP/0k9XHd05jVY2/obSXFSFbahX8uRnKon5ki5uD/W6Y Rico Ullmann // private // ed25519" + ], + "is_admin": true + } +}