Compare commits

..

42 commits

Author SHA1 Message Date
3c26b78996
qzwi: enable mariadb, add wordpress nginx vhost 2022-05-18 15:52:14 +02:00
4d9b52adf3
nginx: add wordpress extras file for qzwi node 2022-05-18 15:51:43 +02:00
d6db3ac8ca
defaults.py: adjust email address for nginx security info 2022-05-18 15:51:32 +02:00
300e2b1506
add mariadb bundle 2022-05-18 15:51:11 +02:00
acd35c1f5e
update bundlewrap to 4.13.6 2022-05-18 15:03:07 +02:00
fa88e52ca0 Merge pull request 'nc_checks' (#5) from nc_checks into main
Reviewed-on: #5
2022-03-05 17:35:54 +00:00
66984d1359
letsencrypt: moved to rsa as key also
some apps may not work with ECC certificates, e.g. some iOS apps
2022-03-05 18:24:57 +01:00
110f55291e
nginx: add fpm.conf 2022-03-05 17:08:17 +01:00
9fe8a210be
nginx: adjust nextcloud config to make sure basic checks pass 2022-03-05 16:56:39 +01:00
53c1568f80
php: enable memcached apcu module in php.ini for php cli
this is required for nextclouds occ tool
2022-03-05 16:15:05 +01:00
30dc638ed2
qzwi: fix filters for ldap-frontend 2022-02-25 15:36:51 +01:00
d9da4345b2
bundles/openldap: remove no longer required openldap schema 2022-02-25 15:31:13 +01:00
5f5de3f109
bundles/elasticsearch: add override.conf 2022-02-25 15:10:47 +01:00
908a6420b3
bundles/users: set cascade_skip=False if user has a password set 2022-02-25 15:05:05 +01:00
2e94aabfa6
bundles/users: unset PROMPT_COMMAND 2022-02-25 15:04:49 +01:00
421b27b6a7
bundles/basic: install kitty-terminfo instead of manually deploying a terminfo file 2022-02-25 15:04:15 +01:00
4894dccd79
qzwi: add comment about how to use elasticsearch 2022-01-09 22:15:42 +01:00
2e907264fd
add bundle:elasticsearch 2022-01-09 22:04:11 +01:00
764f70b603 Merge pull request 'monit' (#4) from monit into main
Reviewed-on: #4
2022-01-04 18:25:08 +00:00
5a97e04eec
add backupserver 2022-01-04 19:23:26 +01:00
082b1fa07d
redis: add monit integration 2022-01-04 13:43:34 +01:00
2cbe3e3b53
monit: fix service name definition 2022-01-04 13:43:15 +01:00
d317ef17d0
openssh: add monit integration 2022-01-04 13:37:32 +01:00
d5195f3355
monit: remove sshd monitoring 2022-01-04 13:37:24 +01:00
424e2948f8
openldap: add monit integration 2022-01-04 13:34:04 +01:00
13805532bd
monit: add option to check a port without a specific protocol
monit does not support all the protocol we use, e.g. ldaps. therefore we can
only use a generic tcp check for some ports.
2022-01-04 13:33:50 +01:00
00d9152007
ldap-frontend: add monit integration 2022-01-04 13:19:27 +01:00
201487549e
postfix: add monit integration 2022-01-04 13:15:54 +01:00
917d2b9a2c
monit: remove postfix check; sort service items 2022-01-04 13:15:36 +01:00
2960c1d5d8
nginx: remove telegraf and icinga2 metadata reactors 2022-01-04 12:01:42 +01:00
210eb40aa6
nginx: do not check port 80 explicitely with monit 2022-01-04 12:00:50 +01:00
18b2ebbcc0
nginx: check http/s connections with monit 2022-01-04 12:00:15 +01:00
8afbfeb1e7
monit: add option to check http connections for various services 2022-01-04 11:59:44 +01:00
1cb94a0d32
nginx: check port 80 with monit! 2022-01-04 11:41:21 +01:00
65871f1d13
monit: add option to check ports for a specific service 2022-01-04 11:41:08 +01:00
d8765f63a5
nginx: monitor service with monit 2022-01-04 11:26:52 +01:00
84df834f07
monit: add option to add checks for various services 2022-01-04 11:26:39 +01:00
e4fa31464e
qzwi: add basic monit metadata 2022-01-04 11:17:27 +01:00
edb49b9a09
monit: add bundle with base config 2022-01-04 11:17:00 +01:00
ee7d8b491b Merge pull request 'ldap_scheme_postfix' (#3) from ldap_scheme_postfix into main
Reviewed-on: #3
2022-01-04 09:35:24 +00:00
d67869aaf6
postfix: update set formatting in items.py 2022-01-03 13:47:07 +01:00
b442d54746
postfix: install postfix apt package via metadata default rather than via items.py 2022-01-03 13:44:59 +01:00
33 changed files with 499 additions and 168 deletions

Binary file not shown.

View file

@ -20,7 +20,7 @@ files = {
'content': '', 'content': '',
}, },
'/root/.terminfo/x/xterm-kitty': { '/root/.terminfo/x/xterm-kitty': {
'content_type': 'binary', 'delete': True,
}, },
} }

View file

@ -0,0 +1,18 @@
% if node.metadata.get('elasticsearch/cluster-name', None):
cluster.name: ${node.metadata.get('elasticsearch/cluster-name')}
% endif
node.name: ${node.name}
path.data: /var/lib/elasticsearch
path.logs: /var/log/elasticsearch
bootstrap.memory_lock: true
# By default Elasticsearch is only accessible on localhost. Set a different
# address here to expose this node on the network:
#
#network.host: 192.168.0.1
#
# By default Elasticsearch listens for HTTP traffic on the first free port it
# finds starting at 9200. Set a specific HTTP port here:
#
http.port: 9200

View file

@ -0,0 +1,5 @@
[Service]
ExecStart=
# same as in original unit file, but without --quiet
ExecStart=/usr/share/elasticsearch/bin/systemd-entrypoint -p ${PID_DIR}/elasticsearch.pid

View file

@ -0,0 +1,23 @@
files = {
'/etc/elasticsearch/elasticsearch.yml': {
'content_type': 'mako',
'triggers': {
'svc_systemd:elasticsearch:restart',
}
},
'/etc/systemd/system/elasticsearch.service.d/override.conf': {
'triggers': {
'action:systemd-reload',
'svc_systemd:elasticsearch:restart',
},
},
}
svc_systemd = {
'elasticsearch': {
'needs': {
'file:/etc/elasticsearch/elasticsearch.yml',
'pkg_apt:elasticsearch',
},
},
}

View file

@ -0,0 +1,14 @@
defaults = {
'apt': {
'repos': {
'elasticsearch': {
'items': {
'deb https://artifacts.elastic.co/packages/7.x/apt stable main',
},
},
},
'packages': {
'elasticsearch': {},
},
},
}

View file

@ -17,5 +17,17 @@ defaults = {
}, },
'title': 'Usermanagement QZWI', 'title': 'Usermanagement QZWI',
}, },
'monit': {
'services': {
'ldap-frontend': {
'bin': '/opt/ldap-frontend/venv/bin/python /opt/ldap-frontend/venv/bin/gunicorn',
'ports': {
'23000': {
'protocol': 'http',
},
},
},
},
},
} }

View file

@ -3,3 +3,4 @@ BASEDIR=/var/lib/dehydrated
WELLKNOWN="${BASEDIR}/acme-challenges" WELLKNOWN="${BASEDIR}/acme-challenges"
DOMAINS_TXT="/etc/dehydrated/domains.txt" DOMAINS_TXT="/etc/dehydrated/domains.txt"
HOOK="/etc/dehydrated/hook.sh" HOOK="/etc/dehydrated/hook.sh"
KEY_ALGO=rsa

7
bundles/mariadb/items.py Normal file
View file

@ -0,0 +1,7 @@
svc_systemd = {
'mariadb': {
'needs': [
'pkg_apt:mariadb-server',
],
},
}

View file

@ -0,0 +1,19 @@
defaults = {
'apt': {
'packages': {
'mariadb-server': {},
},
},
'backups': {
'paths': {
'/var/lib/mysql',
},
},
'monit': {
'services': {
'mariadb': {
'bin': '/usr/sbin/mariadbd',
},
},
},
}

View file

@ -0,0 +1,60 @@
set daemon 30
with start delay 30
set log syslog
set mailserver localhost
set mail-format { from: ${monit['from_address']} }
% for alert_address in monit['alert_addresses']:
set alert ${alert_address}
% endfor
set httpd unixsocket /var/run/monit.sock
use address 127.0.0.1
allow 127.0.0.1
check system $HOST
if cpu usage > 95% for 10 cycles then alert
if memory usage > 80% then alert
if swap usage > 25% then alert
check filesystem rootfs with path /
if space usage > 80% for 5 times within 15 cycles then alert
if space usage > 90% then alert
if inode usage > 90% then alert
check process cron matching "/usr/sbin/cron"
start program = "/usr/bin/systemctl start cron.service"
stop program = "/usr/bin/systemctl stop cron.service"
% for systemd_service in ('systemd-timesyncd', 'systemd-networkd', 'systemd-journald'):
check process ${systemd_service} matching "/lib/systemd/${systemd_service}"
start program = "/usr/bin/systemctl start ${systemd_service}.service"
stop program = "/usr/bin/systemctl stop ${systemd_service}.service"
% endfor
% for service,options in sorted(monit.get('services', {}).items()):
check process ${service} matching "${options['bin']}"
start program = "/bin/systemctl start ${options.get('systemd_unit', service)}.service"
stop program = "/bin/systemctl stop ${options.get('systemd_unit', service)}.service"
% for port,port_options in sorted(options.get('ports', {}).items()):
if failed port ${port}
% if port_options.get('protocol', {}):
protocol ${port_options['protocol']}
% endif
for ${port_options.get('cycles', '5')} cycles
then restart
% endfor
% for domain,http_options in sorted(options.get('http', {}).items()):
if failed host ${domain}
% if http_options['scheme'] == 'https':
port 443
protocol https
% else:
port 80
protocol http
% endif
then restart
% endfor
% endfor

38
bundles/monit/items.py Normal file
View file

@ -0,0 +1,38 @@
svc_systemd = {
'monit': {
'needs': [
'pkg_apt:monit',
],
},
}
files = {
'/etc/monit/monitrc': {
'mode': '0400',
'content_type': 'mako',
'needs': [
'pkg_apt:monit',
],
'triggers': [
'svc_systemd:monit:restart',
],
'context': {
'monit': node.metadata['monit'],
},
},
}
directories = {
'/etc/monit/conf-enabled': {
'purge': True,
},
'/etc/monit/conf-available': {
'purge': True,
},
'/etc/monit/conf.d': {
'purge': True,
},
'/etc/monit/templates': {
'purge': True,
},
}

View file

@ -0,0 +1,7 @@
defaults = {
'apt': {
'packages': {
'monit': {},
},
},
}

View file

@ -0,0 +1,18 @@
fastcgi_split_path_info ^(.+\.php)(/.+)$;
if (!-f $document_root$fastcgi_script_name) {
return 404;
}
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_param PATH_TRANSLATED $document_root$fastcgi_script_name;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param HTTP_AUTHORIZATION $http_authorization;
fastcgi_buffers 8 16k;
fastcgi_buffer_size 32k;
client_body_buffer_size 128k;

View file

@ -50,6 +50,11 @@ files = {
'svc_systemd:nginx:restart', 'svc_systemd:nginx:restart',
}, },
}, },
'/etc/nginx/fpm.conf': {
'triggers': {
'svc_systemd:nginx:restart',
},
},
'/etc/nginx/sites/stub_status': { '/etc/nginx/sites/stub_status': {
'triggers': { 'triggers': {
'svc_systemd:nginx:restart', 'svc_systemd:nginx:restart',

View file

@ -18,6 +18,13 @@ defaults = {
'nginx': { 'nginx': {
'worker_connections': 768, 'worker_connections': 768,
}, },
'monit': {
'services': {
'nginx': {
'bin': '/usr/sbin/nginx',
},
},
},
} }
@ -92,10 +99,10 @@ def index_files(metadata):
@metadata_reactor.provides( @metadata_reactor.provides(
'icinga2_api/nginx/services', 'monit/services/nginx/http',
) )
def monitoring(metadata): def monithttp(metadata):
services = {} http = {}
for vname, vconfig in metadata.get('nginx/vhosts', {}).items(): for vname, vconfig in metadata.get('nginx/vhosts', {}).items():
domain = vconfig.get('domain', vname) domain = vconfig.get('domain', vname)
@ -105,33 +112,16 @@ def monitoring(metadata):
else: else:
scheme = 'http' scheme = 'http'
if 'website_check_path' in vconfig and 'website_check_string' in vconfig: http[domain] = {
services['NGINX VHOST {} CONTENT'.format(vname)] = { 'scheme': scheme,
'check_command': 'check_http_wget',
'vars.http_wget_contains': vconfig['website_check_string'],
'vars.http_wget_url': '{}://{}{}'.format(scheme, domain, vconfig['website_check_path']),
'vars.notification.sms': True,
}
if vconfig.get('check_ssl', vconfig['ssl']):
services['NGINX VHOST {} CERTIFICATE'.format(vname)] = {
'check_command': 'check_https_cert_at_url',
'vars.domain': domain,
'vars.notification.mail': True,
}
max_connections = metadata.get('nginx/worker_connections') * metadata.get('nginx/worker_processes')
connections_warn = int(max_connections * 0.8)
connections_crit = int(max_connections * 0.9)
services['NGINX STATUS'] = {
'command_on_monitored_host': '/usr/local/share/icinga/plugins/check_nginx_status --warn={},-1,-1 --critical={},-1,-1 -H 127.0.0.1:22999'.format(connections_warn, connections_crit),
} }
return { return {
'icinga2_api': { 'monit': {
'services': {
'nginx': { 'nginx': {
'services': services, 'http': http,
},
}, },
}, },
} }
@ -150,29 +140,3 @@ def firewall(metadata):
}, },
}, },
} }
@metadata_reactor.provides(
'telegraf/input_plugins/tail',
)
def telegraf_anon_timing(metadata):
result = {}
for vhost in metadata.get('nginx/vhosts', {}):
result[f'nginx-{vhost}'] = {
'files': [f'/var/log/nginx-timing/{vhost}.log'],
'from_beginning': False,
'grok_patterns': ['%{LOGPATTERN}'],
'grok_custom_patterns': 'LOGPATTERN \[%{HTTPDATE:ts:ts-httpd}\] %{NUMBER:request_time:float} (?:%{NUMBER:upstream_response_time:float}|-) "%{WORD:verb:tag} %{NOTSPACE:request} HTTP/%{NUMBER:http_version:float}" %{NUMBER:resp_code:tag}',
'data_format': 'grok',
'name_override': 'nginx_timing',
}
return {
'telegraf': {
'input_plugins': {
'tail': result,
},
},
}

View file

@ -29,6 +29,18 @@ defaults = {
}, },
}, },
}, },
'monit': {
'services': {
'openldap': {
'bin': '/usr/sbin/slapd',
'systemd_unit': 'slapd',
'ports': {
'389': {},
'636': {},
},
},
},
},
'openldap': { 'openldap': {
'rootpw': repo.vault.password_for(f'{node.name} openldap rootpw'), 'rootpw': repo.vault.password_for(f'{node.name} openldap rootpw'),
}, },

View file

@ -1,5 +1,21 @@
from bundlewrap.metadata import atomic from bundlewrap.metadata import atomic
defaults = {
'monit': {
'services': {
'openssh': {
'bin': '/usr/sbin/sshd',
'systemd_unit': 'sshd',
'ports': {
'22': {
'protocol': 'ssh',
},
},
},
},
},
}
@metadata_reactor.provides( @metadata_reactor.provides(
'firewall/port_rules/22', 'firewall/port_rules/22',
) )

View file

@ -12,6 +12,8 @@ ignore_user_abort = Off
zend.enable_gc = On zend.enable_gc = On
expose_php = Off expose_php = Off
apc.enable_cli = 1
max_execution_time = 30 max_execution_time = 30
max_input_time = 60 max_input_time = 60
memory_limit = ${memory_limit}M memory_limit = ${memory_limit}M

View file

@ -1,10 +1,9 @@
pkg_apt = {
'postfix': {},
}
svc_systemd = { svc_systemd = {
'postfix': { 'postfix': {
'needs': ['pkg_apt:postfix', 'file:/etc/postfix/main.cf'], 'needs': [
'pkg_apt:postfix',
'file:/etc/postfix/main.cf',
],
} }
} }
@ -12,7 +11,9 @@ files = {
'/etc/postfix/main.cf': { '/etc/postfix/main.cf': {
'content_type': 'mako', 'content_type': 'mako',
'needs': ['pkg_apt:postfix'], 'needs': ['pkg_apt:postfix'],
'triggers': ['svc_systemd:postfix:restart'], 'triggers': [
'svc_systemd:postfix:restart',
],
}, },
} }
@ -20,6 +21,8 @@ directories = {
'/etc/postfix': { '/etc/postfix': {
'owner': 'root', 'owner': 'root',
'mode': '1755', 'mode': '1755',
'needs': ['pkg_apt:postfix'], 'needs': [
'pkg_apt:postfix',
],
}, },
} }

View file

@ -0,0 +1,19 @@
defaults = {
'apt': {
'packages': {
'postfix': {},
},
},
'monit': {
'services': {
'postfix': {
'bin': '/usr/lib/postfix/sbin/master',
'ports': {
'25': {
'protocol': 'smtp',
},
},
},
},
},
}

View file

@ -9,4 +9,14 @@ defaults = {
'/var/lib/redis', '/var/lib/redis',
}, },
}, },
'monit': {
'services': {
'redis': {
'bin': '/usr/bin/redis-server',
'ports': {
'6379': {},
},
},
},
},
} }

View file

@ -9,17 +9,7 @@ then
else else
export PS1='\[\e[1;34m\][\[\e[1;32m\]'"$__node_name"'\[\e[1;34m\]][\[\e[1;32m\]\u\[\e[1;34m\]@\[\e[1;32m\]\w\[\e[1;34m\]] > \[\e[0m\]' export PS1='\[\e[1;34m\][\[\e[1;32m\]'"$__node_name"'\[\e[1;34m\]][\[\e[1;32m\]\u\[\e[1;34m\]@\[\e[1;32m\]\w\[\e[1;34m\]] > \[\e[0m\]'
fi fi
case $TERM in unset PROMPT_COMMAND
xterm*|rxvt*)
export PROMPT_COMMAND='echo -ne "\a\e]0;'"$__node_name"':${PWD}\a"'
;;
screen*)
export PROMPT_COMMAND='echo -ne "\a\ek'"$__node_name"':${PWD}\e\\"'
;;
*)
unset PROMPT_COMMAND
;;
esac
if [[ -f "/etc/node.description" ]] if [[ -f "/etc/node.description" ]]
then then

View file

@ -32,6 +32,7 @@ for username, attrs in node.metadata['users'].items():
if 'password' in attrs: if 'password' in attrs:
user['password'] = attrs['password'] user['password'] = attrs['password']
user['cascade_skip'] = False
else: else:
user['password_hash'] = 'x' if node.use_shadow_passwords else '*' user['password_hash'] = 'x' if node.use_shadow_passwords else '*'

View file

@ -2,6 +2,11 @@ from json import loads
from os.path import join from os.path import join
defaults = { defaults = {
'apt': {
'packages': {
'kitty-terminfo': {},
},
},
'users': { 'users': {
'root': { 'root': {
'home': '/root', 'home': '/root',

View file

@ -0,0 +1,31 @@
-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: GnuPG v2.0.14 (GNU/Linux)
mQENBFI3HsoBCADXDtbNJnxbPqB1vDNtCsqhe49vFYsZN9IOZsZXgp7aHjh6CJBD
A+bGFOwyhbd7at35jQjWAw1O3cfYsKAmFy+Ar3LHCMkV3oZspJACTIgCrwnkic/9
CUliQe324qvObU2QRtP4Fl0zWcfb/S8UYzWXWIFuJqMvE9MaRY1bwUBvzoqavLGZ
j3SF1SPO+TB5QrHkrQHBsmX+Jda6d4Ylt8/t6CvMwgQNlrlzIO9WT+YN6zS+sqHd
1YK/aY5qhoLNhp9G/HxhcSVCkLq8SStj1ZZ1S9juBPoXV1ZWNbxFNGwOh/NYGldD
2kmBf3YgCqeLzHahsAEpvAm8TBa7Q9W21C8vABEBAAG0RUVsYXN0aWNzZWFyY2gg
KEVsYXN0aWNzZWFyY2ggU2lnbmluZyBLZXkpIDxkZXZfb3BzQGVsYXN0aWNzZWFy
Y2gub3JnPokBOAQTAQIAIgUCUjceygIbAwYLCQgHAwIGFQgCCQoLBBYCAwECHgEC
F4AACgkQ0n1mbNiOQrRzjAgAlTUQ1mgo3nK6BGXbj4XAJvuZDG0HILiUt+pPnz75
nsf0NWhqR4yGFlmpuctgCmTD+HzYtV9fp9qW/bwVuJCNtKXk3sdzYABY+Yl0Cez/
7C2GuGCOlbn0luCNT9BxJnh4mC9h/cKI3y5jvZ7wavwe41teqG14V+EoFSn3NPKm
TxcDTFrV7SmVPxCBcQze00cJhprKxkuZMPPVqpBS+JfDQtzUQD/LSFfhHj9eD+Xe
8d7sw+XvxB2aN4gnTlRzjL1nTRp0h2/IOGkqYfIG9rWmSLNlxhB2t+c0RsjdGM4/
eRlPWylFbVMc5pmDpItrkWSnzBfkmXL3vO2X3WvwmSFiQbkBDQRSNx7KAQgA5JUl
zcMW5/cuyZR8alSacKqhSbvoSqqbzHKcUQZmlzNMKGTABFG1yRx9r+wa/fvqP6OT
RzRDvVS/cycws8YX7Ddum7x8uI95b9ye1/Xy5noPEm8cD+hplnpU+PBQZJ5XJ2I+
1l9Nixx47wPGXeClLqcdn0ayd+v+Rwf3/XUJrvccG2YZUiQ4jWZkoxsA07xx7Bj+
Lt8/FKG7sHRFvePFU0ZS6JFx9GJqjSBbHRRkam+4emW3uWgVfZxuwcUCn1ayNgRt
KiFv9jQrg2TIWEvzYx9tywTCxc+FFMWAlbCzi+m4WD+QUWWfDQ009U/WM0ks0Kww
EwSk/UDuToxGnKU2dQARAQABiQEfBBgBAgAJBQJSNx7KAhsMAAoJENJ9ZmzYjkK0
c3MIAIE9hAR20mqJWLcsxLtrRs6uNF1VrpB+4n/55QU7oxA1iVBO6IFu4qgsF12J
TavnJ5MLaETlggXY+zDef9syTPXoQctpzcaNVDmedwo1SiL03uMoblOvWpMR/Y0j
6rm7IgrMWUDXDPvoPGjMl2q1iTeyHkMZEyUJ8SKsaHh4jV9wp9KmC8C+9CwMukL7
vM5w8cgvJoAwsp3Fn59AxWthN3XJYcnMfStkIuWgR7U2r+a210W6vnUxU4oN0PmM
cursYPyeV0NX/KQeUeNMwGTFB6QHS/anRaGQewijkrYYoTNtfllxIu9XYmiBERQ/
qPDlGRlOgVTd9xUfHFkzB52c70E=
=92oX
-----END PGP PUBLIC KEY BLOCK-----

View file

@ -14,92 +14,98 @@
# always provides the desired behaviour. # always provides the desired behaviour.
index index.php index.html /index.php$request_uri; index index.php index.html /index.php$request_uri;
# Rule borrowed from `.htaccess` to handle Microsoft DAV clients
location = / {
if ( $http_user_agent ~ ^DavClnt ) {
return 302 /remote.php/webdav/$is_args$args;
}
}
location = /robots.txt { location = /robots.txt {
allow all; allow all;
log_not_found off; log_not_found off;
access_log off; access_log off;
} }
# Make a regex exception for `/.well-known` so that clients can still location ~ ^.+\.php {
# access it despite the existence of the regex rule include fpm.conf;
# `location ~ /(\.|autotest|...)` which would otherwise handle requests fastcgi_pass unix:/var/run/php/php${php_version}-fpm.sock;
# for `/.well-known`. add_header Strict-Transport-Security "max-age=31536000; includeSubdomains;";
}
# The following 2 rules are only needed for the user_webfinger app.
# Uncomment it if you're planning to use this app.
#rewrite ^/.well-known/host-meta /public.php?service=host-meta last;
#rewrite ^/.well-known/host-meta.json /public.php?service=host-meta-json
# last;
location ^~ /.well-known { location ^~ /.well-known {
# The rules in this block are an adaptation of the rules # The following 6 rules are borrowed from `.htaccess`
# in `.htaccess` that concern `/.well-known`.
location = /.well-known/carddav { return 301 /remote.php/dav/; } location = /.well-known/carddav { return 301 /remote.php/dav/; }
location = /.well-known/caldav { return 301 /remote.php/dav/; } location = /.well-known/caldav { return 301 /remote.php/dav/; }
# Anything else is dynamically handled by Nextcloud
location ^~ /.well-known { return 301 /index.php$uri; }
location = /.well-known/webfinger { return 301 /index.php/.well-known/webfinger; } try_files $uri $uri/ =404;
location = /.well-known/nodeinfo { return 301 /index.php/.well-known/nodeinfo; }
location /.well-known/pki-validation { try_files $uri $uri/ =404; }
# Let Nextcloud's API for `/.well-known` URIs handle all other
# requests by passing them to the front-end controller.
return 301 /index.php$request_uri;
} }
# Rules borrowed from `.htaccess` to hide certain paths from clients # Some headers to fix possible information leaks
location ~ ^/(?:build|tests|config|lib|3rdparty|templates|data)(?:$|/) { return 404; } add_header X-Content-Type-Options nosniff;
location ~ ^/(?:\.|autotest|occ|issue|indie|db_|console) { return 404; } add_header X-XSS-Protection "1; mode=block";
add_header X-Robots-Tag none;
add_header X-Download-Options noopen;
add_header X-Permitted-Cross-Domain-Policies none;
add_header Referrer-Policy no-referrer;
# Remove X-Powered-By, which is an information leak
fastcgi_hide_header X-Powered-By;
# Ensure this block, which passes PHP files to the PHP process, is above the blocks location ~ ^/(?:build|tests|config|lib|3rdparty|templates|data)/ {
# which handle static assets (as seen below). If this block is not declared first, deny all;
# then Nginx will encounter an infinite rewriting loop when it prepends `/index.php` }
# to the URI, resulting in a HTTP 500 error response. location ~ ^/(?:\.|autotest|occ|issue|indie|db_|console) {
location ~ \.php(?:$|/) { deny all;
# Required for legacy support }
rewrite ^/(?!index|remote|public|cron|core\/ajax\/update|status|ocs\/v[12]|updater\/.+|oc[ms]-provider\/.+|.+\/richdocumentscode\/proxy) /index.php$request_uri;
fastcgi_split_path_info ^(.+?\.php)(/.*)$;
set $path_info $fastcgi_path_info;
try_files $fastcgi_script_name =404;
include fastcgi.conf;
fastcgi_pass unix:/run/php/php${php_version}-fpm.sock;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $path_info;
fastcgi_param HTTPS on;
fastcgi_param modHeadersAvailable true; # Avoid sending the security headers twice
fastcgi_param front_controller_active true; # Enable pretty urls
location ~ ^/(?:index|remote|public|cron|core/ajax/update|status|ocs/v[12]|updater/.+|ocs-provider/.+)\.php(?:$|/) {
fastcgi_split_path_info ^(.+\.php)(/.*)$;
include fastcgi_params;
include fpm.conf;
fastcgi_pass unix:/var/run/php/php${php_version}-fpm.sock;
add_header Strict-Transport-Security "max-age=31536000; includeSubdomains;";
#Avoid sending the security headers twice
fastcgi_param modHeadersAvailable true;
fastcgi_param front_controller_active true;
fastcgi_intercept_errors on; fastcgi_intercept_errors on;
fastcgi_request_buffering off; fastcgi_request_buffering off;
} }
location ~ \.(?:css|js|svg|gif|png|jpg|ico|wasm|tflite)$ { location ~ ^/(?:updater|ocs-provider)(?:$|/) {
try_files $uri /index.php$request_uri; try_files $uri/ =404;
expires 6M; # Cache-Control policy borrowed from `.htaccess` index index.php;
access_log off; # Optional: Don't log access to assets
location ~ \.wasm$ {
default_type application/wasm;
}
} }
location ~ \.woff2?$ { # Adding the cache control header for js and css files
try_files $uri /index.php$request_uri; # Make sure it is BELOW the PHP block
expires 7d; # Cache-Control policy borrowed from `.htaccess` location ~ \.(?:css|js|woff|svg|gif)$ {
access_log off; # Optional: Don't log access to assets try_files $uri /index.php$uri$is_args$args;
add_header Cache-Control "public, max-age=15778463";
# Add headers to serve security related headers (It is intended to
# have those duplicated to the ones above)
# Before enabling Strict-Transport-Security headers please read into
# this topic first.
# add_header Strict-Transport-Security "max-age=15768000; includeSubDomains; preload;";
#
# WARNING: Only add the preload option once you read about
# the consequences in https://hstspreload.org/. This option
# will add the domain to a hardcoded list that is shipped
# in all major browsers and getting removed from this list
# could take several months.
add_header Strict-Transport-Security "max-age=31536000; includeSubdomains;";
add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection "1; mode=block";
add_header X-Robots-Tag none;
add_header X-Download-Options noopen;
add_header X-Permitted-Cross-Domain-Policies none;
# Optional: Don't log access to assets
access_log off;
} }
# Rule borrowed from `.htaccess` location ~ \.(?:png|html|ttf|ico|jpg|jpeg)$ {
location /remote { try_files $uri /index.php$uri$is_args$args;
return 301 /remote.php$request_uri; # Optional: Don't log access to other assets
} access_log off;
location / {
try_files $uri $uri/ /index.php$request_uri;
} }

View file

@ -0,0 +1,35 @@
location / {
# This is cool because no php is touched for static content.
# include the "?$args" part so non-default permalinks doesn't break when using query string
try_files $uri $uri/ /index.php?$args;
}
location ~* \.(js|css|png|jpg|jpeg|gif|ico)$ {
expires max;
log_not_found off;
}
location ~* /xmlrpc.php$ {
allow 127.0.0.1;
deny all;
}
if ($request_method !~ ^(GET|POST)$ ) {
return 444;
}
location ~ /\.(svn|git)/* {
deny all;
access_log off;
log_not_found off;
}
location ~ /\.ht {
deny all;
access_log off;
log_not_found off;
}
location ~ /\.user.ini {
deny all;
access_log off;
log_not_found off;
}

View file

@ -1,21 +0,0 @@
attributetype ( 1.3.6.1.4.1.0.1
NAME 'externalMail'
DESC 'external mail address for communication outside the org'
EQUALITY caseIgnoreIA5Match
SUBSTR caseIgnoreIA5SubstringsMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{128}
SINGLE-VALUE )
objectclass ( 1.3.6.1.4.1.0.2
NAME 'qzwiperson'
DESC 'own object schema to add custom values'
SUP top
AUXILIARY
MUST (cn $ sn $ uid $ externalMail)
MAY
( displayName $ givenName $ homePhone $ homePostalAddress $
mail $ mobile $ o $ photo $ userCertificate $
x500uniqueIdentifier $ preferredLanguage $
userSMIMECertificate $ userPKCS12 $ userPassword $
telephoneNumber $ description ) )

View file

@ -1,4 +1,4 @@
hostmaster_email = 'hostmaster@kunbox.net' hostmaster_email = 'hostmaster@qzwi.de'
security_email = f'mailto:{hostmaster_email}' security_email = f'mailto:{hostmaster_email}'
security_lang = {'en', 'de'} security_lang = {'en', 'de'}

15
nodes/backupserver.toml Normal file
View file

@ -0,0 +1,15 @@
hostname = "2a01:4f9:6b:2d99:0:28:6:1969"
bundles = []
groups = [
"debian-bullseye",
]
[metadata.interfaces.enp1s0]
ips = [
"2a01:4f9:6b:2d99::28:6:1969/64",
]
gateway6 = "2a01:4f9:6b:2d99::2"
[metadata.vm]
cpu = 4
ram = 8

View file

@ -1,12 +1,15 @@
#hostname = "2a00:f820:528::4" #hostname = "2a00:f820:528::4"
hostname = "31.47.232.108" hostname = "31.47.232.108"
bundles = [ bundles = [
"elasticsearch",
"ldap-frontend", "ldap-frontend",
"letsencrypt", "letsencrypt",
"mariadb",
"monit",
"nginx", "nginx",
"nextcloud", "nextcloud",
"openldap", "openldap",
"postfix", #"postfix",
"php", "php",
"postgresql", "postgresql",
"redis", "redis",
@ -32,6 +35,8 @@ gateway6 = "2a00:f820:528::1"
"NextCloud" = "https://cloud.qzwi.de/" "NextCloud" = "https://cloud.qzwi.de/"
[metadata.nextcloud] [metadata.nextcloud]
# for elasticsearch to work, please install 'ingest-attachment' plugin:
# /usr/share/elasticsearch/bin/elasticsearch-plugin install ingest-attachment
domain = "cloud.qzwi.de" domain = "cloud.qzwi.de"
sha1 = "0d496eb0808c292502479e93cd37fe2daf95786a" sha1 = "0d496eb0808c292502479e93cd37fe2daf95786a"
version = "23.0.0" version = "23.0.0"
@ -39,6 +44,12 @@ version = "23.0.0"
[metadata.nginx.vhosts.nextcloud] [metadata.nginx.vhosts.nextcloud]
ssl = "letsencrypt" ssl = "letsencrypt"
[metadata.nginx.vhosts.wordpress]
domain = "jackie.qzwi.de"
ssl = "letsencrypt"
php = true
extras = true
[metadata.nginx.vhosts.openldap] [metadata.nginx.vhosts.openldap]
domain = "ldap.qzwi.de" domain = "ldap.qzwi.de"
ssl = "letsencrypt" ssl = "letsencrypt"
@ -53,14 +64,13 @@ backup = [
] ]
schemas = [ schemas = [
"openssh-lpk_openldap", "openssh-lpk_openldap",
"qzwiperson",
] ]
[metadata.ldap-frontend.template] [metadata.ldap-frontend.template]
"group_admin" = "(&(objectclass=qzwiperson)(uid={})(memberOf=ou=qzwi-admins,ou=Groups,dc=qzwi,dc=de))" "group_admin" = "(&(objectclass=inetOrgPerson)(uid={})(memberOf=ou=qzwi-admins,ou=Groups,dc=qzwi,dc=de))"
"group_members" = "(&(objectclass=qzwiperson)(memberOf=ou={},ou=Groups,dc=qzwi,dc=de))" "group_members" = "(&(objectclass=inetOrgPerson)(memberOf=ou={},ou=Groups,dc=qzwi,dc=de))"
"group_nonmembers" = "(&(objectclass=qzwiperson)(!(memberOf=ou={},ou=Groups,dc=qzwi,dc=de)))" "group_nonmembers" = "(&(objectclass=inetOrgPerson)(!(memberOf=ou={},ou=Groups,dc=qzwi,dc=de)))"
"user_search" = "(&(objectclass=qzwiperson)(uid={}))" "user_search" = "(&(objectclass=inetOrgPerson)(uid={}))"
[metadata.openldap.access."ou=Users,dc=qzwi,dc=de"] [metadata.openldap.access."ou=Users,dc=qzwi,dc=de"]
manage = [ manage = [
@ -75,3 +85,9 @@ manage = [
[metadata.vm] [metadata.vm]
cpu = 4 cpu = 4
ram = 4 ram = 4
[metadata.monit]
from_address = "monit@qzwi.de"
alert_addresses = [
"rico@qzwi.de",
]

View file

@ -1 +1 @@
bundlewrap>=4.12.0 bundlewrap==4.13.6