Compare commits

..

3 commits

Author SHA1 Message Date
mwiegand
7ca2b07971 wip 2022-03-06 23:46:50 +01:00
mwiegand
b0269879ee wip 2022-03-06 23:30:32 +01:00
mwiegand
91bd418021 wip 2022-03-06 22:21:32 +01:00
31 changed files with 152 additions and 711 deletions

View file

@ -1,63 +0,0 @@
#!/usr/bin/env python3
from bundlewrap.repo import Repository
from os.path import realpath, dirname
from ipaddress import ip_interface
repo = Repository(dirname(dirname(realpath(__file__))))
nodes = [
node
for node in repo.nodes_in_group('debian')
if not node.dummy
]
print('updating nodes:', sorted(node.name for node in nodes))
# UPDATE
for node in nodes:
print('--------------------------------------')
print('updating', node.name)
print('--------------------------------------')
repo.libs.wol.wake(node)
print(node.run('DEBIAN_FRONTEND=noninteractive apt update').stdout.decode())
print(node.run('DEBIAN_FRONTEND=noninteractive apt -y dist-upgrade').stdout.decode())
# REBOOT IN ORDER
wireguard_servers = [
node
for node in nodes
if node.has_bundle('wireguard')
and (
ip_interface(node.metadata.get('wireguard/my_ip')).network.prefixlen <
ip_interface(node.metadata.get('wireguard/my_ip')).network.max_prefixlen
)
]
wireguard_s2s = [
node
for node in nodes
if node.has_bundle('wireguard')
and (
ip_interface(node.metadata.get('wireguard/my_ip')).network.prefixlen ==
ip_interface(node.metadata.get('wireguard/my_ip')).network.max_prefixlen
)
]
everything_else = [
node
for node in nodes
if not node.has_bundle('wireguard')
]
print('======================================')
print(len(everything_else), len(wireguard_s2s), len(wireguard_servers))
for node in [
*everything_else,
*wireguard_s2s,
*wireguard_servers,
]:
print('rebooting', node.name)
print(node.run('systemctl reboot').stdout.decode())

View file

@ -12,10 +12,7 @@ defaults = {
}, },
}, },
'sudoers': { 'sudoers': {
'backup-receiver': { 'backup-receiver': ['ALL'],
'/usr/bin/rsync',
'/sbin/zfs',
},
} }
} }

View file

@ -7,7 +7,7 @@ then
/opt/backup/backup_path_via_zfs "$path" /opt/backup/backup_path_via_zfs "$path"
elif test -d "$path" elif test -d "$path"
then then
/opt/backup/backup_path_via_rsync "$path" /opt/backuo/backup_path_via_rsync "$path"
else else
echo "UNKNOWN PATH: $path" echo "UNKNOWN PATH: $path"
exit 1 exit 1

View file

@ -5,7 +5,7 @@ set -exu
path=$1 path=$1
uuid=$(jq -r .client_uuid < /etc/backup/config.json) uuid=$(jq -r .client_uuid < /etc/backup/config.json)
server=$(jq -r .server_hostname < /etc/backup/config.json) server=$(jq -r .server_hostname < /etc/backup/config.json)
ssh="ssh -o ConnectTimeout=5 backup-receiver@$server" ssh="ssh -o StrictHostKeyChecking=no -o ConnectTimeout=5 backup-receiver@$server"
rsync -av --rsync-path="sudo rsync" "$path/" "backup-receiver@$server:/mnt/backups/$uuid$path/" rsync -av --rsync-path="sudo rsync" "$path/" "backup-receiver@$server:/mnt/backups/$uuid$path/"
$ssh sudo zfs snap "tank/$uuid/fs@auto-backup_$(date +"%Y-%m-%d_%H:%M:%S")" $ssh sudo zfs snap "tank/$uuid/fs@auto-backup_$(date +"%Y-%m-%d_%H:%M:%S")"

View file

@ -5,7 +5,7 @@ set -exu
path=$1 path=$1
uuid=$(jq -r .client_uuid < /etc/backup/config.json) uuid=$(jq -r .client_uuid < /etc/backup/config.json)
server=$(jq -r .server_hostname < /etc/backup/config.json) server=$(jq -r .server_hostname < /etc/backup/config.json)
ssh="ssh -o ConnectTimeout=5 backup-receiver@$server" ssh="ssh -o StrictHostKeyChecking=no -o ConnectTimeout=5 backup-receiver@$server"
source_dataset=$(zfs list -H -o mountpoint,name | grep -P "^$path\t" | cut -d $'\t' -f 2) source_dataset=$(zfs list -H -o mountpoint,name | grep -P "^$path\t" | cut -d $'\t' -f 2)
target_dataset="tank/$uuid/$source_dataset" target_dataset="tank/$uuid/$source_dataset"

View file

@ -1,9 +0,0 @@
for project, options in node.metadata.get('build-ci').items():
directories[options['path']] = {
'owner': 'build-ci',
'group': options['group'],
'mode': '770',
'needs': [
'user:build-ci',
],
}

View file

@ -1,25 +0,0 @@
from shlex import quote
@metadata_reactor.provides(
'users/build-ci/authorized_users',
'sudoers/build-ci',
)
def ssh_keys(metadata):
return {
'users': {
'build-ci': {
'authorized_users': {
f'build-server@{other_node.name}'
for other_node in repo.nodes
if other_node.has_bundle('build-server')
},
},
},
'sudoers': {
'build-ci': {
f"/usr/bin/chown -R build-ci\:{quote(ci['group'])} {quote(ci['path'])}"
for ci in metadata.get('build-ci').values()
}
},
}

View file

@ -1,31 +0,0 @@
#!/bin/bash
set -xu
CONFIG_PATH=${config_path}
JSON="$1"
REPO_NAME=$(jq -r .repository.name <<< $JSON)
CLONE_URL=$(jq -r .repository.clone_url <<< $JSON)
REPO_BRANCH=$(jq -r .ref <<< $JSON | cut -d'/' -f3)
SSH_OPTIONS='-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null'
for INTEGRATION in "$(cat $CONFIG_PATH | jq -r '.ci | values[]')"
do
[[ $(jq -r '.repo' <<< $INTEGRATION) = "$REPO_NAME" ]] || continue
[[ $(jq -r '.branch' <<< $INTEGRATION) = "$REPO_BRANCH" ]] || continue
HOSTNAME=$(jq -r '.hostname' <<< $INTEGRATION)
DEST_PATH=$(jq -r '.path' <<< $INTEGRATION)
DEST_GROUP=$(jq -r '.group' <<< $INTEGRATION)
[[ -z "$HOSTNAME" ]] || [[ -z "$DEST_PATH" ]] || [[ -z "$DEST_GROUP" ]] && exit 5
cd ~
rm -rf "$REPO_NAME"
git clone "$CLONE_URL" "$REPO_NAME"
ssh $SSH_OPTIONS "build-ci@$HOSTNAME" "find \"$DEST_PATH\" -mindepth 1 -delete"
scp -r $SSH_OPTIONS "$REPO_NAME"/* "build-ci@$HOSTNAME:$DEST_PATH"
ssh $SSH_OPTIONS "build-ci@$HOSTNAME" "sudo chown -R build-ci:$DEST_GROUP $(printf "%q" "$DEST_PATH")"
done

View file

@ -10,7 +10,7 @@ directories = {
files = { files = {
'/etc/build-server.json': { '/etc/build-server.json': {
'owner': 'build-server', 'owner': 'build-server',
'content': json.dumps(node.metadata.get('build-server'), indent=4, sort_keys=True, cls=MetadataJSONEncoder) 'content': json.dumps(node.metadata.get('build-server'), indent=4, cls=MetadataJSONEncoder)
}, },
'/opt/build-server/strategies/crystal': { '/opt/build-server/strategies/crystal': {
'content_type': 'mako', 'content_type': 'mako',
@ -21,12 +21,4 @@ files = {
'download_server': node.metadata.get('build-server/download_server_ip'), 'download_server': node.metadata.get('build-server/download_server_ip'),
}, },
}, },
'/opt/build-server/strategies/ci': {
'content_type': 'mako',
'owner': 'build-server',
'mode': '0777', # FIXME
'context': {
'config_path': '/etc/build-server.json',
},
},
} }

View file

@ -40,24 +40,6 @@ def agent_conf(metadata):
}, },
} }
@metadata_reactor.provides(
'build-server',
)
def ci(metadata):
return {
'build-server': {
'ci': {
f'{repo}@{other_node.name}': {
'hostname': other_node.metadata.get('hostname'),
'repo': repo,
**options,
}
for other_node in repo.nodes
if other_node.has_bundle('build-ci')
for repo, options in other_node.metadata.get('build-ci').items()
},
},
}
@metadata_reactor.provides( @metadata_reactor.provides(
'nginx/vhosts', 'nginx/vhosts',

View file

@ -1,72 +1,72 @@
# Git-Hash for Icinga1: b63bb0ef52bf213715e567c81e3ed097024e61af # Git-Hash for Icinga1: b63bb0ef52bf213715e567c81e3ed097024e61af
#
# directories = { directories = {
# '/etc/icinga2': { '/etc/icinga2': {
# 'purge': True, 'purge': True,
# 'owner': 'nagios', 'owner': 'nagios',
# }, },
# '/etc/icinga2/conf.d': { '/etc/icinga2/conf.d': {
# 'purge': True, 'purge': True,
# 'owner': 'nagios', 'owner': 'nagios',
# }, },
# '/etc/icinga2/hosts.d': { '/etc/icinga2/hosts.d': {
# 'purge': True, 'purge': True,
# 'owner': 'nagios', 'owner': 'nagios',
# }, },
# '/etc/icinga2/features.d': { '/etc/icinga2/features.d': {
# 'purge': True, 'purge': True,
# 'owner': 'nagios', 'owner': 'nagios',
# }, },
# } }
#
# files = { files = {
# '/etc/icinga2/icinga2.conf': { '/etc/icinga2/icinga2.conf': {
# 'owner': 'nagios', 'owner': 'nagios',
# }, },
# '/etc/icinga2/constants.conf': { '/etc/icinga2/constants.conf': {
# 'owner': 'nagios', 'owner': 'nagios',
# 'context': { 'context': {
# 'hostname': node.metadata.get('icinga2/hostname') 'hostname': node.metadata.get('icinga2/hostname')
# }, },
# }, },
# '/etc/icinga2/conf.d/templates.conf': { '/etc/icinga2/conf.d/templates.conf': {
# 'source': 'conf.d/templates.conf', 'source': 'conf.d/templates.conf',
# 'owner': 'nagios', 'owner': 'nagios',
# }, },
# '/etc/icinga2/features/ido-pgsql.conf': { '/etc/icinga2/features/ido-pgsql.conf': {
# 'source': 'features/ido-pgsql.conf', 'source': 'features/ido-pgsql.conf',
# 'content_type': 'mako', 'content_type': 'mako',
# 'owner': 'nagios', 'owner': 'nagios',
# 'context': { 'context': {
# 'db_password': node.metadata.get('postgresql/roles/icinga2/password') 'db_password': node.metadata.get('postgresql/roles/icinga2/password')
# }, },
# 'needs': [ 'needs': [
# 'pkg_apt:icinga2-ido-pgsql', 'pkg_apt:icinga2-ido-pgsql',
# ], ],
# }, },
# '/etc/icingaweb2/setup.token': { '/etc/icingaweb2/setup.token': {
# 'content': node.metadata.get('icingaweb2/setup_token'), 'content': node.metadata.get('icingaweb2/setup_token'),
# 'owner': 'nagios', 'owner': 'nagios',
# }, },
# } }
#
# for other_node in repo.nodes: for other_node in repo.nodes:
# files[f'/etc/icinga2/hosts.d/{other_node.name}.conf'] = { files[f'/etc/icinga2/hosts.d/{other_node.name}.conf'] = {
# 'content_type': 'mako', 'content_type': 'mako',
# 'source': 'hosts.d/host.conf', 'source': 'hosts.d/host.conf',
# 'owner': 'nagios', 'owner': 'nagios',
# 'context': { 'context': {
# 'host_name': other_node.name, 'host_name': other_node.name,
# 'host_settings': {}, 'host_settings': {},
# 'services': other_node.metadata.get('monitoring', {}), 'services': other_node.metadata.get('monitoring', {}),
# }, },
# } }
#
# svc_systemd = { svc_systemd = {
# 'icinga2': { 'icinga2': {
# 'needs': [ 'needs': [
# 'pkg_apt:icinga2-ido-pgsql', 'pkg_apt:icinga2-ido-pgsql',
# 'svc_systemd:postgresql', 'svc_systemd:postgresql',
# ], ],
# }, },
# } }

View file

@ -3,19 +3,19 @@ from hashlib import sha3_256
defaults = { defaults = {
'apt': { 'apt': {
'packages': { 'packages': {
'icingadb': {}, 'icinga2': {},
'icingadb-web': {},
'icingaweb2': {}, 'icingaweb2': {},
'icingadb-redis': {}, 'icinga2-ido-pgsql': {},
'icingacli': {},
'monitoring-plugins': {},
}, },
'sources': { 'sources': {
'deb https://packages.icinga.com/debian icinga-{release} main', 'deb https://packages.icinga.com/debian icinga-{release} main',
'deb https://packages.icinga.com/debian icinga-{release}-testing main',
}, },
}, },
'postgresql': { 'postgresql': {
'databases': { 'databases': {
'icingadb': { 'icinga2': {
'owner': 'icinga2', 'owner': 'icinga2',
}, },
'icingaweb2': { 'icingaweb2': {
@ -23,7 +23,7 @@ defaults = {
}, },
}, },
'roles': { 'roles': {
'icingadb': { 'icinga2': {
'password': repo.vault.password_for(f'psql icinga2 on {node.name}'), 'password': repo.vault.password_for(f'psql icinga2 on {node.name}'),
}, },
'icingaweb2': { 'icingaweb2': {
@ -31,44 +31,44 @@ defaults = {
}, },
}, },
}, },
# 'zfs': { 'zfs': {
# 'datasets': { 'datasets': {
# 'tank/icinga2': { 'tank/icinga2': {
# 'mountpoint': '/var/lib/icingadb', 'mountpoint': '/var/lib/icinga2',
# 'needed_by': { 'needed_by': {
# 'pkg_apt:icingadb', 'pkg_apt:icinga2',
# 'pkg_apt:icingadb-web', 'pkg_apt:icingaweb2',
# 'pkg_apt:icingaweb2', 'pkg_apt:icinga2-ido-pgsql',
# }, },
# }, },
# }, },
# }, },
} }
#
# @metadata_reactor.provides( @metadata_reactor.provides(
# 'icingaweb2/setup_token', 'icingaweb2/setup_token',
# ) )
# def setup_token(metadata): def setup_token(metadata):
# return { return {
# 'icingaweb2': { 'icingaweb2': {
# 'setup_token': sha3_256(metadata.get('id').encode()).hexdigest()[:16], 'setup_token': sha3_256(metadata.get('id').encode()).hexdigest()[:16],
# }, },
# } }
#
#
# @metadata_reactor.provides( @metadata_reactor.provides(
# 'nginx/vhosts', 'nginx/vhosts',
# ) )
# def nginx(metadata): def nginx(metadata):
# return { return {
# 'nginx': { 'nginx': {
# 'vhosts': { 'vhosts': {
# metadata.get('icinga2/hostname'): { metadata.get('icinga2/hostname'): {
# 'content': 'icingaweb2/vhost.conf', 'content': 'icingaweb2/vhost.conf',
# 'context': { 'context': {
# }, },
# }, },
# }, },
# }, },
# } }

View file

@ -1,28 +0,0 @@
if not node.has_bundle('build-ci'):
raise Exception('lownercrew needs bundle build-ci')
defaults = {
'build-ci': {
'lonercrew': {
'path': '/opt/lonercrew',
'group': 'www-data',
'branch': 'master',
},
},
}
@metadata_reactor.provides(
'nginx/vhosts',
)
def nginx(metadata):
return {
'nginx': {
'vhosts': {
'lonercrew.io': {
'content': 'lonercrew/vhost.conf',
},
},
},
}

View file

@ -1,5 +1,4 @@
#!/bin/bash #!/bin/bash
php /opt/nextcloud/occ files:scan --all
php /opt/nextcloud/occ files:scan-app-data php /opt/nextcloud/occ files:scan-app-data
php /opt/nextcloud/occ preview:generate-all php /opt/nextcloud/occ preview:generate-all

View file

@ -1,6 +0,0 @@
Host *
SendEnv LANG LC_*
HashKnownHosts yes
GSSAPIAuthentication yes
StrictHostKeyChecking yes
GlobalKnownHostsFile /etc/ssh/ssh_known_hosts

View file

@ -1,36 +1,7 @@
if not node.metadata.get('FIXME_dont_touch_sshd', False):
# on debian bullseye raspberry images, starting the systemd ssh # on debian bullseye raspberry images, starting the systemd ssh
# daemon seems to collide with an existing sysv daemon # daemon seems to collide with an existing sysv daemon
dont_touch_sshd = node.metadata.get('FIXME_dont_touch_sshd', False) files['/etc/ssh/sshd_config'] = {
directories = {
'/etc/ssh': {
'purge': True,
'mode': '0755',
'skip': dont_touch_sshd,
}
}
files = {
'/etc/ssh/moduli': {
'content_type': 'any',
'skip': dont_touch_sshd,
},
'/etc/ssh/ssh_config': {
'triggers': [
'svc_systemd:ssh:restart'
],
'skip': dont_touch_sshd,
},
'/etc/ssh/ssh_config': {
'content_type': 'mako',
'context': {
},
'triggers': [
'svc_systemd:ssh:restart'
],
'skip': dont_touch_sshd,
},
'/etc/ssh/sshd_config': {
'content_type': 'mako', 'content_type': 'mako',
'context': { 'context': {
'users': sorted(node.metadata.get('ssh/allow_users')), 'users': sorted(node.metadata.get('ssh/allow_users')),
@ -38,35 +9,10 @@ files = {
'triggers': [ 'triggers': [
'svc_systemd:ssh:restart' 'svc_systemd:ssh:restart'
], ],
'skip': dont_touch_sshd,
},
'/etc/ssh/ssh_host_ed25519_key': {
'content': node.metadata.get('ssh/host_key/private') + '\n',
'mode': '0600',
'triggers': [
'svc_systemd:ssh:restart'
],
},
'/etc/ssh/ssh_host_ed25519_key.pub': {
'content': node.metadata.get('ssh/host_key/public') + '\n',
'mode': '0644',
'triggers': [
'svc_systemd:ssh:restart'
],
},
'/etc/ssh/ssh_known_hosts': {
'content': '\n'.join(
repo.libs.ssh.known_hosts_entry_for(other_node)
for other_node in sorted(repo.nodes)
if other_node != node
and other_node.has_bundle('ssh')
) + '\n',
},
} }
svc_systemd['ssh'] = { svc_systemd['ssh'] = {
'needs': [ 'needs': [
'tag:ssh_users', 'tag:ssh_users',
], ],
'skip': dont_touch_sshd,
} }

View file

@ -1,7 +1,3 @@
from ipaddress import ip_interface
from base64 import b64decode
@metadata_reactor.provides( @metadata_reactor.provides(
'ssh/allow_users', 'ssh/allow_users',
) )
@ -15,52 +11,3 @@ def users(metadata):
), ),
}, },
} }
@metadata_reactor.provides(
'ssh/host_key',
)
def host_key(metadata):
private, public = repo.libs.ssh.generate_ed25519_key_pair(
b64decode(str(repo.vault.random_bytes_as_base64_for(f"HostKey {metadata.get('id')}", length=32)))
)
return {
'ssh': {
'host_key': {
'private': private + '\n',
'public': public + f' root@{node.name}',
}
},
}
@metadata_reactor.provides(
'ssh/hostnames',
)
def hostnames(metadata):
ips = set()
for network in node.metadata.get('network').values():
if network.get('ipv4', None):
ips.add(str(ip_interface(network['ipv4']).ip))
if network.get('ipv6', None):
ips.add(str(ip_interface(network['ipv6']).ip))
domains = {
domain
for domain, records in node.metadata.get('dns').items()
for type, values in records.items()
if type in {'A', 'AAAA'}
and set(values) & ips
}
return {
'ssh': {
'hostnames': {
node.hostname,
*ips,
*domains,
}
},
}

View file

@ -1,3 +0,0 @@
% for command in sorted(commands):
${user} ALL=(ALL) NOPASSWD: ${command}
% endfor

View file

@ -6,11 +6,6 @@ directories = {
for user, commands in node.metadata.get('sudoers').items(): for user, commands in node.metadata.get('sudoers').items():
files[f'/etc/sudoers.d/{user}'] = { files[f'/etc/sudoers.d/{user}'] = {
'content_type': 'mako', 'content': f"{user} ALL=(ALL) NOPASSWD: {', '.join(sorted(commands))}",
'source': 'sudoer',
'context': {
'user': user,
'commands': commands,
},
'mode': '500', 'mode': '500',
} }

View file

@ -22,11 +22,11 @@ for name, user_config in node.metadata.get('users').items():
git_deploy = { git_deploy = {
join(user_config['home'], '.zsh/oh-my-zsh'): { join(user_config['home'], '.zsh/oh-my-zsh'): {
'repo': 'https://github.com/ohmyzsh/ohmyzsh.git', 'repo': 'git://github.com/ohmyzsh/ohmyzsh.git',
'rev': 'master', 'rev': 'master',
}, },
join(user_config['home'], '.zsh/oh-my-zsh/custom/plugins/zsh-autosuggestions'): { join(user_config['home'], '.zsh/oh-my-zsh/custom/plugins/zsh-autosuggestions'): {
'repo': 'https://github.com/zsh-users/zsh-autosuggestions.git', 'repo': 'git://github.com/zsh-users/zsh-autosuggestions.git',
'rev': 'master', 'rev': 'master',
}, },
} }

View file

@ -1,11 +0,0 @@
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
ssl_certificate /var/lib/dehydrated/certs/${server_name}/fullchain.pem;
ssl_certificate_key /var/lib/dehydrated/certs/${server_name}/privkey.pem;
server_name ${server_name};
index index.html;
root /opt/lonercrew;
}

View file

@ -1,55 +0,0 @@
[Unit]
Description=TEST
[Service]
Type=oneshot
ExecStart=/opt/test
DynamicUser=yes
UMask=077
ProtectSystem=strict
ProtectHome=yes
PrivateTmp=yes
PrivateDevices=yes # DevicePolicy=closed
PrivateNetwork=yes
IPAddressDeny=any
PrivateUsers=yes
ProtectHostname=yes
ProtectClock=yes
ProtectKernelTunables=yes
ProtectKernelModules=yes
ProtectKernelLogs=yes
ProtectControlGroups=yes
RestrictAddressFamilies=none
RestrictFileSystems=ext4 tmpfs zfs
RestrictNamespaces=yes
LockPersonality=yes
MemoryDenyWriteExecute=yes
RestrictRealtime=yes
RestrictSUIDSGID=yes
RemoveIPC=yes
PrivateMounts=yes
SystemCallFilter=~@swap
SystemCallFilter=~@resources
SystemCallFilter=~@reboot
SystemCallFilter=~@raw-io
SystemCallFilter=~@privileged
SystemCallFilter=~@obsolete
SystemCallFilter=~@mount
SystemCallFilter=~@module
SystemCallFilter=~@debug
SystemCallFilter=~@cpu-emulation
SystemCallFilter=~@clock
CapabilityBoundingSet=
ProtectProc=invisible
ProcSubset=pid
NoNewPrivileges=yes
SystemCallArchitectures=native
ReadOnlyPaths=/
NoExecPaths=/
ExecPaths=/opt/test /bin/bash /lib
[Install]
WantedBy=multi-user.target

View file

@ -1,55 +0,0 @@
[Unit]
Description=TEST
[Service]
Type=oneshot
ExecStart=/opt/test
# user
UMask=077
DynamicUser=yes
PrivateUsers=yes
RestrictSUIDSGID=yes
NoNewPrivileges=yes
LockPersonality=yes
RemoveIPC=yes
# fs
ProtectSystem=strict
ProtectHome=yes
PrivateTmp=yes
PrivateDevices=yes
PrivateNetwork=yes
ProtectProc=invisible
ProcSubset=pid
PrivateMounts=yes
RestrictFileSystems=ext4 tmpfs zfs
NoExecPaths=/
ExecPaths=/opt/test /bin /lib /lib64 /usr
TemporaryFileSystem=/var
TemporaryFileSystem=/var
# network
IPAddressDeny=any
RestrictAddressFamilies=none
# syscall
SystemCallArchitectures=native
SystemCallFilter=~@swap ~@resources ~@reboot ~@raw-io ~@privileged ~@obsolete ~@mount ~@module ~@debug ~@cpu-emulation ~@clock
# else
ProtectHostname=yes
ProtectClock=yes
ProtectKernelTunables=yes
ProtectKernelModules=yes
ProtectKernelLogs=yes
ProtectControlGroups=yes
RestrictNamespaces=yes
MemoryDenyWriteExecute=yes
RestrictRealtime=yes
CapabilityBoundingSet=
[Install]
WantedBy=multi-user.target

View file

@ -1,54 +0,0 @@
[Unit]
Description=TEST
[Service]
Type=oneshot
ExecStart=/opt/test
TemporaryFileSystem=/
BindReadOnlyPaths=/opt/test /bin /lib /lib64 /usr
UMask=077
ProtectHome=yes
PrivateTmp=yes
PrivateDevices=yes
PrivateNetwork=yes
IPAddressDeny=any
ProtectHostname=yes
ProtectClock=yes
ProtectKernelTunables=yes
ProtectKernelModules=yes
ProtectKernelLogs=yes
ProtectControlGroups=yes
RestrictAddressFamilies=none
RestrictFileSystems=ext4 tmpfs zfs
RestrictNamespaces=yes
LockPersonality=yes
MemoryDenyWriteExecute=yes
RestrictRealtime=yes
RestrictSUIDSGID=yes
RemoveIPC=yes
PrivateMounts=yes
SystemCallFilter=~@swap
SystemCallFilter=~@resources
SystemCallFilter=~@reboot
SystemCallFilter=~@raw-io
SystemCallFilter=~@privileged
SystemCallFilter=~@obsolete
SystemCallFilter=~@mount
SystemCallFilter=~@module
SystemCallFilter=~@debug
SystemCallFilter=~@cpu-emulation
SystemCallFilter=~@clock
CapabilityBoundingSet=
ProtectProc=invisible
ProcSubset=pid
NoNewPrivileges=yes
SystemCallArchitectures=native
[Install]
WantedBy=multi-user.target

View file

@ -6,7 +6,7 @@
'Service': { 'Service': {
'ExecStart': [ 'ExecStart': [
'', '',
'-/sbin/agetty --autologin root --noclear %I $TERM', '-/usr/sbin/agetty --autologin root --noclear %I $TERM',
], ],
}, },
}, },

View file

@ -1,2 +1,14 @@
from bundlewrap.operations import run_local
from bundlewrap.utils.ui import io
from bundlewrap.utils.text import yellow, bold
def node_apply_start(repo, node, interactive=False, **kwargs): def node_apply_start(repo, node, interactive=False, **kwargs):
repo.libs.wol.wake(node) if node.has_bundle('wol-sleeper'):
io.stdout('{x} {node} waking up...'.format(
x=yellow('!'),
node=bold(node.name)
))
repo\
.get_node(node.metadata.get('wol-sleeper/waker'))\
.run(node.metadata.get('wol-sleeper/wake_command'))

View file

@ -1,7 +1,6 @@
from base64 import b64decode, b64encode from base64 import b64decode, b64encode
from hashlib import sha3_224, sha1 from hashlib import sha3_224
from functools import cache from functools import cache
import hmac
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
from cryptography.hazmat.primitives.serialization import Encoding, PrivateFormat, PublicFormat, NoEncryption from cryptography.hazmat.primitives.serialization import Encoding, PrivateFormat, PublicFormat, NoEncryption
@ -47,26 +46,3 @@ def generate_ed25519_key_pair(secret):
# RETURN # RETURN
return (deterministic_privatekey, public_key) return (deterministic_privatekey, public_key)
#https://www.fragmentationneeded.net/2017/10/ssh-hashknownhosts-file-format.html
# test this:
# - `ssh-keyscan -H 10.0.0.5`
# - take the salt from the ssh-ed25519 entry (first field after '|1|')
# - `bw debug -c 'repo.libs.ssh.known_hosts_entry_for(repo.get_node(<node with hostname 10.0.0.5>), <salt from ssh-keygen>)'`
@cache
def known_hosts_entry_for(node, test_salt=None):
lines = set()
for hostname in sorted(node.metadata.get('ssh/hostnames')):
if test_salt:
salt = b64decode(test_salt)
else:
salt = sha1((node.metadata.get('id') + hostname).encode()).digest()
hash = hmac.new(salt, hostname.encode(), sha1).digest()
pubkey = node.metadata.get('ssh/host_key/public')
lines.add(f'|1|{b64encode(salt).decode()}|{b64encode(hash).decode()} {" ".join(pubkey.split()[:2])}')
return '\n'.join(sorted(lines))

View file

@ -7,7 +7,7 @@ template = '''
# ${segment.split('#', 2)[1]} # ${segment.split('#', 2)[1]}
% endif % endif
[${segment.split('#')[0]}] [${segment.split('#')[0]}]
% for option, value in sorted(options.items()): % for option, value in options.items():
% if isinstance(value, dict): % if isinstance(value, dict):
% for k, v in value.items(): % for k, v in value.items():
${option}=${k}=${v} ${option}=${k}=${v}
@ -16,7 +16,6 @@ ${option}=${k}=${v}
% for item in sorted(value): % for item in sorted(value):
${option}=${item} ${option}=${item}
% endfor % endfor
% elif isinstance(value, type(None)):
% else: % else:
${option}=${str(value)} ${option}=${str(value)}
% endif % endif
@ -40,53 +39,5 @@ def segment_order(segment):
def generate_unitfile(data): def generate_unitfile(data):
return Template(template).render( return Template(template).render(
data=dict(sorted(data.items(), key=segment_order)), data=dict(sorted(data.items(), key=segment_order)),
order=order
).lstrip() ).lstrip()
# wip
def protection():
return {
# user
'UMask': '077',
'DynamicUser': 'yes',
'PrivateUsers': 'yes',
'RestrictSUIDSGID': 'yes',
'NoNewPrivileges': 'yes',
'LockPersonality': 'yes',
'RemoveIPC': 'yes',
# fs
'ProtectSystem': 'strict',
'ProtectHome': 'yes',
'PrivateTmp': 'yes',
'PrivateDevices': 'yes',
'ProtectProc': 'invisible',
'ProcSubset': 'pid',
'PrivateMounts': 'yes',
'RestrictFileSystems': {'ext4', 'tmpfs', 'zfs'},
'NoExecPaths': {'/'},
'ExecPaths': {'/bin', '/sbin', '/lib', '/lib64', '/usr'},
'TemporaryFileSystem': {'/var'},
# network
'IPAddressDeny': 'any',
'PrivateNetwork': 'yes',
'RestrictAddressFamilies': 'none',
# syscall
'SystemCallArchitectures': 'native',
'SystemCallFilter': '~@swap @resources @reboot @raw-io @privileged @obsolete @mount @module @debug @cpu-emulation @clock',
# else
'ProtectHostname': 'yes',
'ProtectClock': 'yes',
'ProtectKernelTunables': 'yes',
'ProtectKernelModules': 'yes',
'ProtectKernelLogs': 'yes',
'ProtectControlGroups': 'yes',
'RestrictNamespaces': 'yes',
'MemoryDenyWriteExecute': 'yes',
'RestrictRealtime': 'yes',
'CapabilityBoundingSet': '',
}

View file

@ -1,13 +0,0 @@
from bundlewrap.utils.ui import io
from bundlewrap.utils.text import yellow, bold
def wake(node):
if node.has_bundle('wol-sleeper'):
io.stdout('{x} {node} waking up...'.format(
x=yellow('!'),
node=bold(node.name)
))
node\
.repo\
.get_node(node.metadata.get('wol-sleeper/waker'))\
.run(node.metadata.get('wol-sleeper/wake_command'))

View file

@ -14,8 +14,6 @@
'islamicstate.eu', 'islamicstate.eu',
'wireguard', 'wireguard',
'zfs', 'zfs',
'lonercrew',
'build-ci',
], ],
'metadata': { 'metadata': {
'id': 'ea29bdf0-0b47-4bf4-8346-67d60c9dc4ae', 'id': 'ea29bdf0-0b47-4bf4-8346-67d60c9dc4ae',
@ -47,7 +45,6 @@
'islamicstate.eu', 'islamicstate.eu',
'hausamsilberberg.de', 'hausamsilberberg.de',
'wiegand.tel', 'wiegand.tel',
'lonercrew.io',
}, },
}, },
'dns': { 'dns': {