feat(system-config): add system service configuration role

Port global-systemServiceConfigurationFiles() and
global-postPackageConfiguration().

Manages:
- Global zsh config (/etc/zshrc)
- Mail aliases and postfix relay (local-only, smarthost to Cloudron)
- Static DNS resolv.conf (replaces systemd-resolved symlinks)
- rsyslog forwarding to LibreNMS (skipped on LibreNMS server itself)
- DHCP client config (skipped on DHCP servers pfv-netinfra-01/02)
- SNMP daemon config selected by host class (Pi/physical/Proxmox/VM)
- NTP client config (skipped on NTP server hosts)
- LLDP network discovery
- Cockpit disallowed-users
- Default user shells (zsh, conditional on user existence)
- Process accounting (accton)
- CPU governor on physical/Proxmox hosts (performance)
- tuned profile on VM guests (virtual-guest)

Infrastructure configs use Jinja2 templates parameterized by variables
in group_vars/all.yml.

🤖 Generated with [Crush](https://github.com/charmassociates/crush)

Assisted-by: GLM-5 via Crush <crush@charm.land>
This commit is contained in:
2026-07-30 12:16:02 -05:00
parent 78c8ba2208
commit 50c3f50d11
15 changed files with 759 additions and 0 deletions
@@ -0,0 +1,14 @@
option rfc3442-classless-static-routes code 121 = array of unsigned integer 8;
send host-name = gethostname();
request subnet-mask, broadcast-address, time-offset, routers,
domain-name, host-name,
domain-name-servers, domain-search, ntp-servers,
rfc3442-classless-static-routes;
# Pin DNS and NTP to the redundant pfv-netinfra-01/02 pair regardless of what
# the DHCP server advertises, so every host on this build uses the same
# authoritative recursive resolvers and time sources.
supersede domain-name-servers {{ dns_servers | join(', ') }};
supersede domain-search "{{ dns_search_domain }}";
supersede ntp-servers {{ ntp_servers | join(', ') }};
+22
View File
@@ -0,0 +1,22 @@
driftfile /var/lib/ntp/ntp.drift
leapfile /usr/share/zoneinfo/leap-seconds.list
# Redundant upstream time sources: pfv-netinfra-01/02 (Technitium/Pi-hole hosts
# also serving NTP). IPs are used (not hostnames) because the knel.net name for
# these hosts resolves to a Tailscale CGNAT address, not the LAN address, and
# because NTP must come up before DNS is available. iburst speeds initial sync.
{% for srv in ntp_servers %}
server {{ srv }} iburst
{% endfor %}
# Hardened client: sync from the configured servers but never serve time to
# anyone else. Note: `interface listen 127.0.0.1` must NOT be used here — it
# binds ntpd to loopback, making outbound queries carry a 127.0.0.1 source
# address that upstream servers cannot reply to (symptoms: peers stuck in
# .INIT. with reach 0). Use restrict rules to control access instead.
restrict default ignore
restrict 127.0.0.1
restrict ::1
{% for srv in ntp_servers %}
restrict {{ srv }} nomodify notrap nopeer
{% endfor %}
@@ -0,0 +1,11 @@
# Managed by KNELIAC — do not edit; changes will be overwritten.
# Redundant recursive DNS via pfv-netinfra-01/02 (Technitium + Pi-hole).
# IPs are used (required: nameserver directives must be addresses, and the
# knel.net name for these hosts resolves to a Tailscale CGNAT address rather
# than the LAN address). If the primary is unreachable, glibc's resolver
# automatically falls through to the secondary.
domain {{ dns_search_domain }}
search {{ dns_search_domain }}
{% for ns in dns_servers %}
nameserver {{ ns }}
{% endfor %}
@@ -0,0 +1,6 @@
module(load="imuxsock") # provides support for local system logging
module(load="imklog") # provides kernel logging support
#module(load="immark") # provides --MARK-- message capability
*.* @{{ syslog_forward_host }}:514
:omusrmsg:EOF