Compare commits
3 commits
3cc463999f
...
7ca2b07971
Author | SHA1 | Date | |
---|---|---|---|
![]() |
7ca2b07971 | ||
![]() |
b0269879ee | ||
![]() |
91bd418021 |
31 changed files with 152 additions and 711 deletions
|
@ -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())
|
|
|
@ -12,10 +12,7 @@ defaults = {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
'sudoers': {
|
'sudoers': {
|
||||||
'backup-receiver': {
|
'backup-receiver': ['ALL'],
|
||||||
'/usr/bin/rsync',
|
|
||||||
'/sbin/zfs',
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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")"
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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',
|
|
||||||
],
|
|
||||||
}
|
|
|
@ -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()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
|
@ -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
|
|
|
@ -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',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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',
|
||||||
|
|
|
@ -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',
|
||||||
# ],
|
],
|
||||||
# },
|
},
|
||||||
# }
|
}
|
||||||
|
|
|
@ -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': {
|
||||||
# },
|
},
|
||||||
# },
|
},
|
||||||
# },
|
},
|
||||||
# },
|
},
|
||||||
# }
|
}
|
||||||
|
|
|
@ -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',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
|
@ -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
|
||||||
|
|
|
@ -1,6 +0,0 @@
|
||||||
Host *
|
|
||||||
SendEnv LANG LC_*
|
|
||||||
HashKnownHosts yes
|
|
||||||
GSSAPIAuthentication yes
|
|
||||||
StrictHostKeyChecking yes
|
|
||||||
GlobalKnownHostsFile /etc/ssh/ssh_known_hosts
|
|
|
@ -1,36 +1,7 @@
|
||||||
# on debian bullseye raspberry images, starting the systemd ssh
|
if not node.metadata.get('FIXME_dont_touch_sshd', False):
|
||||||
# daemon seems to collide with an existing sysv daemon
|
# on debian bullseye raspberry images, starting the systemd ssh
|
||||||
dont_touch_sshd = node.metadata.get('FIXME_dont_touch_sshd', False)
|
# daemon seems to collide with an existing sysv daemon
|
||||||
|
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,
|
}
|
||||||
}
|
|
||||||
|
|
|
@ -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,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
% for command in sorted(commands):
|
|
||||||
${user} ALL=(ALL) NOPASSWD: ${command}
|
|
||||||
% endfor
|
|
|
@ -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',
|
||||||
}
|
}
|
||||||
|
|
|
@ -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',
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
|
|
@ -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
|
|
|
@ -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
|
|
|
@ -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
|
|
|
@ -6,7 +6,7 @@
|
||||||
'Service': {
|
'Service': {
|
||||||
'ExecStart': [
|
'ExecStart': [
|
||||||
'',
|
'',
|
||||||
'-/sbin/agetty --autologin root --noclear %I $TERM',
|
'-/usr/sbin/agetty --autologin root --noclear %I $TERM',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -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'))
|
||||||
|
|
26
libs/ssh.py
26
libs/ssh.py
|
@ -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))
|
|
||||||
|
|
|
@ -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': '',
|
|
||||||
}
|
|
||||||
|
|
13
libs/wol.py
13
libs/wol.py
|
@ -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'))
|
|
|
@ -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': {
|
||||||
|
|
Loading…
Reference in a new issue