chore: enforce shellcheck across the repo
Establish shellcheck as a mandatory pre-commit quality gate and bring all 93
shell scripts to a clean state.
- tests/shellcheck.sh: wrapper that runs koalaman/shellcheck:stable via Docker
(no native binary needed), skips vendored + upstream librenms-agent scripts.
- .shellcheckrc: documents intentional codebase-wide disables (dynamic source
paths SC1090/SC1091, client-side ssh expansion SC2029).
- AGENTS.md: new Git Policy rule mandating clean shellcheck for every shell
script before commit.
Fixes applied (real bugs + quality): missing quote in netinfra/gather-configs.sh
(caused cascading parse errors), unquoted expansions, declare-and-assign masking,
egrep -> grep -E, $FUNCNAME array indexing, unused variable removal, cd || exit.
Intentional patterns (sourced config, sysfs/ps diagnostics, ssh heredocs that
expand local config) get justified targeted disables.
💘 Generated with Crush
Assisted-by: Crush:glm-5.2
This commit is contained in:
@@ -0,0 +1,17 @@
|
|||||||
|
# ShellCheck configuration for PFVCluster
|
||||||
|
# (used when running `shellcheck` directly; tests/shellcheck.sh applies the
|
||||||
|
# same disables via -e for consistent results under Docker)
|
||||||
|
#
|
||||||
|
# These checks are DISABLED because they flag intentional conventions of this
|
||||||
|
# codebase, not bugs:
|
||||||
|
#
|
||||||
|
# SC1090 / SC1091 — cannot follow dynamically-computed `source` paths. The KNEL
|
||||||
|
# framework (vendor/) and test harness source helpers via computed include
|
||||||
|
# dirs, which shellcheck cannot resolve statically.
|
||||||
|
# SC2029 — ssh orchestration (tests/remote.sh and perf/k8s/dns scripts)
|
||||||
|
# deliberately builds and expands the remote command on the CLIENT side before
|
||||||
|
# sending it. That is the whole point of the single-chokepoint remote pattern.
|
||||||
|
disable=SC1090,SC1091,SC2029
|
||||||
|
|
||||||
|
# Treat external-sourced files as bash (matches #!/usr/bin/env bash framework).
|
||||||
|
external-sources=true
|
||||||
@@ -47,6 +47,17 @@ vendor/ Vendored KNELShellFramework
|
|||||||
2. **Atomic commits.** Each commit coherent on its own.
|
2. **Atomic commits.** Each commit coherent on its own.
|
||||||
3. **Conventional format**: `feat(scope): desc`, `fix(scope): desc`,
|
3. **Conventional format**: `feat(scope): desc`, `fix(scope): desc`,
|
||||||
`docs: desc`, `refactor(scope): desc`, `test(scope): desc`.
|
`docs: desc`, `refactor(scope): desc`, `test(scope): desc`.
|
||||||
|
4. **All shell scripts MUST pass `shellcheck` before commit.** No exceptions.
|
||||||
|
Run it via the wrapper:
|
||||||
|
```bash
|
||||||
|
bash tests/shellcheck.sh # whole repo
|
||||||
|
bash tests/shellcheck.sh ups/*.sh # specific files
|
||||||
|
```
|
||||||
|
This invokes `koalaman/shellcheck:stable` through Docker (no native binary
|
||||||
|
needed). Fix every reported finding — including `info`-level — or add a
|
||||||
|
targeted `# shellcheck disable=SCxxxx # <reason>` directive with a
|
||||||
|
justification. A script that emits any diagnostic is a protocol violation.
|
||||||
|
Non-bash scripts (PHP with `.sh` shebang `#!/usr/bin/php`, etc.) are exempt.
|
||||||
|
|
||||||
## Automatic Gardening Protocol
|
## Automatic Gardening Protocol
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
#!/usr/bin/bash
|
#!/usr/bin/bash
|
||||||
|
# shellcheck disable=SC2010,SC2012 # diagnostic script; ls|grep/ls -la on sysfs & log dirs is intentional for human-readable output
|
||||||
#
|
#
|
||||||
# console/discover.sh — READ-ONLY discovery of console setup on pfv-tsys4
|
# console/discover.sh — READ-ONLY discovery of console setup on pfv-tsys4
|
||||||
#
|
#
|
||||||
|
|||||||
@@ -33,8 +33,6 @@ CONMAN_LOGDIR="${CONMAN_LOGDIR:-/var/log/conman}"
|
|||||||
UDEV_RULES="/etc/udev/rules.d/99-console-ports.rules"
|
UDEV_RULES="/etc/udev/rules.d/99-console-ports.rules"
|
||||||
SER2NET_CONF="/etc/ser2net.yaml"
|
SER2NET_CONF="/etc/ser2net.yaml"
|
||||||
CONMAN_CONF="/etc/conman.conf"
|
CONMAN_CONF="/etc/conman.conf"
|
||||||
CONMAN_CONSOLES="/etc/conman/console-consoles.conf"
|
|
||||||
CONSOLE_DEV_DIR="/dev/console"
|
|
||||||
|
|
||||||
echo "============================================"
|
echo "============================================"
|
||||||
echo " Console Config Generator"
|
echo " Console Config Generator"
|
||||||
@@ -120,9 +118,11 @@ for entry in "${ENTRIES[@]}"; do
|
|||||||
# Build the full ID_PATH match. The mapping stores a substring like "usb-0:1.5.4.4"
|
# Build the full ID_PATH match. The mapping stores a substring like "usb-0:1.5.4.4"
|
||||||
# The actual ID_PATH is like "pci-0000:00:1a.0-usb-0:1.5.4.4:1.0"
|
# The actual ID_PATH is like "pci-0000:00:1a.0-usb-0:1.5.4.4:1.0"
|
||||||
# We match on the substring to be portable across PCI bus changes.
|
# We match on the substring to be portable across PCI bus changes.
|
||||||
echo "" >> "$UDEV_RULES"
|
{
|
||||||
echo "# $name (TCP $tcp_port): $comment" >> "$UDEV_RULES"
|
echo ""
|
||||||
echo "SUBSYSTEM==\"tty\", ENV{ID_PATH}==\"*$id_path*\", SYMLINK+=\"console/$name\"" >> "$UDEV_RULES"
|
echo "# $name (TCP $tcp_port): $comment"
|
||||||
|
echo "SUBSYSTEM==\"tty\", ENV{ID_PATH}==\"*$id_path*\", SYMLINK+=\"console/$name\""
|
||||||
|
} >> "$UDEV_RULES"
|
||||||
done
|
done
|
||||||
|
|
||||||
echo " Written: $UDEV_RULES"
|
echo " Written: $UDEV_RULES"
|
||||||
@@ -148,13 +148,13 @@ fi
|
|||||||
echo "#"
|
echo "#"
|
||||||
echo "# All ports use telnet(rfc2217) accepter so conman and telnet clients"
|
echo "# All ports use telnet(rfc2217) accepter so conman and telnet clients"
|
||||||
echo "# negotiate proper telnet binary mode — this prevents CR stripping"
|
echo "# negotiate proper telnet binary mode — this prevents CR stripping"
|
||||||
echo "# and stair-stepping on devices that send \\n\\r (LF+CR) line endings."
|
printf '%s\n' "# and stair-stepping on devices that send \\n\\r (LF+CR) line endings."
|
||||||
echo "# Ports bound to Tailscale IP ($TS_IP) for secure remote access."
|
echo "# Ports bound to Tailscale IP ($TS_IP) for secure remote access."
|
||||||
echo "#"
|
echo "#"
|
||||||
echo "# Direct telnet: telnet $TS_IP 2001"
|
echo "# Direct telnet: telnet $TS_IP 2001"
|
||||||
echo "# Via conman: conman -f <name>"
|
echo "# Via conman: conman -f <name>"
|
||||||
echo ""
|
echo ""
|
||||||
echo "define: &banner \\r\\nPFV console port \\p device \\d [\\B]\\r\\n\\r\\n"
|
printf '%s\n' "define: &banner \\r\\nPFV console port \\p device \\d [\\B]\\r\\n\\r\\n"
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
for entry in "${ENTRIES[@]}"; do
|
for entry in "${ENTRIES[@]}"; do
|
||||||
|
|||||||
+3
-6
@@ -1,4 +1,5 @@
|
|||||||
#!/usr/bin/bash
|
#!/usr/bin/bash
|
||||||
|
# shellcheck disable=SC2010 # diagnostic; ls|grep on /dev listing is intentional
|
||||||
#
|
#
|
||||||
# console/setup.sh — deploy console management on pfv-tsys4
|
# console/setup.sh — deploy console management on pfv-tsys4
|
||||||
#
|
#
|
||||||
@@ -19,10 +20,6 @@
|
|||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||||
UDEV_RULES="/etc/udev/rules.d/99-console-ports.rules"
|
|
||||||
SER2NET_CONF="/etc/ser2net.yaml"
|
|
||||||
CONMAN_CONSOLES="/etc/conman/console-consoles.conf"
|
|
||||||
CONSOLE_DEV_DIR="/dev/consoles"
|
|
||||||
|
|
||||||
echo "============================================"
|
echo "============================================"
|
||||||
echo " Console Management Setup"
|
echo " Console Management Setup"
|
||||||
@@ -100,7 +97,7 @@ if [ ! -d /dev/consoles ] || [ -z "$(ls /dev/consoles/ 2>/dev/null)" ]; then
|
|||||||
line="${line%%#*}"
|
line="${line%%#*}"
|
||||||
line="$(echo "$line" | xargs)"
|
line="$(echo "$line" | xargs)"
|
||||||
[ -z "$line" ] && continue
|
[ -z "$line" ] && continue
|
||||||
IFS='|' read -r tcp_port name id_path baud comment <<< "$line"
|
IFS='|' read -r _ name id_path _ _ <<< "$line"
|
||||||
# Find the ttyUSB whose ID_PATH contains the mapping's id_path substring
|
# Find the ttyUSB whose ID_PATH contains the mapping's id_path substring
|
||||||
for tty in /dev/ttyUSB*; do
|
for tty in /dev/ttyUSB*; do
|
||||||
[ -e "$tty" ] || continue
|
[ -e "$tty" ] || continue
|
||||||
@@ -124,7 +121,7 @@ while IFS= read -r line; do
|
|||||||
line="${line%%#*}"
|
line="${line%%#*}"
|
||||||
line="$(echo "$line" | xargs)"
|
line="$(echo "$line" | xargs)"
|
||||||
[ -z "$line" ] && continue
|
[ -z "$line" ] && continue
|
||||||
IFS='|' read -r tcp_port name id_path baud comment <<< "$line"
|
IFS='|' read -r _ name id_path _ _ <<< "$line"
|
||||||
if [ -e "/dev/consoles/$name" ]; then
|
if [ -e "/dev/consoles/$name" ]; then
|
||||||
TARGET=$(readlink -f "/dev/consoles/$name")
|
TARGET=$(readlink -f "/dev/consoles/$name")
|
||||||
echo " [OK] /dev/consoles/$name -> $TARGET"
|
echo " [OK] /dev/consoles/$name -> $TARGET"
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
#!/usr/bin/bash
|
#!/usr/bin/bash
|
||||||
|
# shellcheck disable=SC2012,SC2001 # diagnostic script; ls -la listings and sed line-prefixing are intentional
|
||||||
#
|
#
|
||||||
# console/validate-conman.sh — verify conman can actually reach devices via
|
# console/validate-conman.sh — verify conman can actually reach devices via
|
||||||
# ser2net TCP ports and is capturing log output to files.
|
# ser2net TCP ports and is capturing log output to files.
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ mkdir -p "$ZONE_DIR"
|
|||||||
log "Syncing zones from $PRIMARY_HOST..."
|
log "Syncing zones from $PRIMARY_HOST..."
|
||||||
if rsync -az --delete --temp-dir=/tmp \
|
if rsync -az --delete --temp-dir=/tmp \
|
||||||
"${PRIMARY_HOST}:$ZONE_DIR/" "$ZONE_DIR/" >> "$LOG_FILE" 2>&1; then
|
"${PRIMARY_HOST}:$ZONE_DIR/" "$ZONE_DIR/" >> "$LOG_FILE" 2>&1; then
|
||||||
zone_count=$(ls "$ZONE_DIR" | wc -l)
|
zone_count=$(find "$ZONE_DIR" -maxdepth 1 -type f | wc -l)
|
||||||
log "Sync complete: $zone_count zones"
|
log "Sync complete: $zone_count zones"
|
||||||
else
|
else
|
||||||
log "ERROR: rsync failed (rc=$?)"
|
log "ERROR: rsync failed (rc=$?)"
|
||||||
|
|||||||
@@ -69,9 +69,9 @@ echo " Production zones: $prod_zones"
|
|||||||
echo " Primary (01) zones: $pri_zones"
|
echo " Primary (01) zones: $pri_zones"
|
||||||
echo " Secondary (02) zones: $sec_zones"
|
echo " Secondary (02) zones: $sec_zones"
|
||||||
|
|
||||||
[ "$prod_zones" -gt 0 ] 2>/dev/null && ok "Production has $prod_zones zones" || fail "Production zone count invalid"
|
if [ "$prod_zones" -gt 0 ] 2>/dev/null; then ok "Production has $prod_zones zones"; else fail "Production zone count invalid"; fi
|
||||||
[ "$pri_zones" -gt 0 ] 2>/dev/null && ok "Primary has $pri_zones zones" || fail "Primary zone count invalid"
|
if [ "$pri_zones" -gt 0 ] 2>/dev/null; then ok "Primary has $pri_zones zones"; else fail "Primary zone count invalid"; fi
|
||||||
[ "$sec_zones" -gt 0 ] 2>/dev/null && ok "Secondary has $sec_zones zones" || fail "Secondary zone count invalid"
|
if [ "$sec_zones" -gt 0 ] 2>/dev/null; then ok "Secondary has $sec_zones zones"; else fail "Secondary zone count invalid"; fi
|
||||||
|
|
||||||
if [ "$pri_zones" = "$prod_zones" ]; then
|
if [ "$pri_zones" = "$prod_zones" ]; then
|
||||||
ok "Primary zone count matches production ($pri_zones)"
|
ok "Primary zone count matches production ($pri_zones)"
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
#!/usr/bin/bash
|
#!/usr/bin/bash
|
||||||
|
# shellcheck disable=SC2034 # sourced config file; variables are consumed by scripts that source this
|
||||||
# k8s/env.sh — shared config for all k8s scripts. Source this.
|
# k8s/env.sh — shared config for all k8s scripts. Source this.
|
||||||
#
|
#
|
||||||
# All cluster communication goes over Tailscale IPs. No LAN IPs, ever.
|
# All cluster communication goes over Tailscale IPs. No LAN IPs, ever.
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ echo "============================================"
|
|||||||
echo ""
|
echo ""
|
||||||
echo "--- [1/5] Installing bootstrap node: $CNODE1_NAME ($CNODE1_IP) ---"
|
echo "--- [1/5] Installing bootstrap node: $CNODE1_NAME ($CNODE1_IP) ---"
|
||||||
|
|
||||||
|
# shellcheck disable=SC2087 # heredoc intentionally expands local config (node IPs, k3s version) before sending to remote
|
||||||
ssh "${SSH_OPTS[@]}" "${SSH_USER}@${CNODE1_IP}" "sudo -n bash -s" <<REMOTE_BOOT
|
ssh "${SSH_OPTS[@]}" "${SSH_USER}@${CNODE1_IP}" "sudo -n bash -s" <<REMOTE_BOOT
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
export INSTALL_K3S_VERSION="$K3S_VERSION"
|
export INSTALL_K3S_VERSION="$K3S_VERSION"
|
||||||
@@ -103,6 +104,7 @@ for node_ip in "$CNODE2_IP" "$CNODE3_IP"; do
|
|||||||
echo ""
|
echo ""
|
||||||
echo "--- [4/5] Joining server: $node_name ($node_ip) ---"
|
echo "--- [4/5] Joining server: $node_name ($node_ip) ---"
|
||||||
|
|
||||||
|
# shellcheck disable=SC2087 # heredoc intentionally expands local config before sending to remote
|
||||||
ssh "${SSH_OPTS[@]}" "${SSH_USER}@${node_ip}" "sudo -n bash -s" <<REMOTE_JOIN
|
ssh "${SSH_OPTS[@]}" "${SSH_USER}@${node_ip}" "sudo -n bash -s" <<REMOTE_JOIN
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
export INSTALL_K3S_VERSION="$K3S_VERSION"
|
export INSTALL_K3S_VERSION="$K3S_VERSION"
|
||||||
|
|||||||
@@ -71,6 +71,7 @@ for node_ip in "$CNODE2_IP" "$CNODE3_IP"; do
|
|||||||
echo ""
|
echo ""
|
||||||
echo "--- [3/4] Joining server: $node_name ($node_ip) ---"
|
echo "--- [3/4] Joining server: $node_name ($node_ip) ---"
|
||||||
|
|
||||||
|
# shellcheck disable=SC2087 # heredoc intentionally expands local config before sending to remote
|
||||||
ssh "${SSH_OPTS[@]}" "${SSH_USER}@${node_ip}" "sudo -n bash -s" <<REMOTE_JOIN
|
ssh "${SSH_OPTS[@]}" "${SSH_USER}@${node_ip}" "sudo -n bash -s" <<REMOTE_JOIN
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
export INSTALL_K3S_VERSION="$K3S_VERSION"
|
export INSTALL_K3S_VERSION="$K3S_VERSION"
|
||||||
|
|||||||
+1
-1
@@ -99,7 +99,7 @@ echo "--- Workload isolation ---"
|
|||||||
USER_PODS=$(kubectl get pods -A --field-selector spec.nodeName="${CNODE1_NAME}" -o jsonpath='{.items[*].metadata.name}' 2>/dev/null | wc -w)
|
USER_PODS=$(kubectl get pods -A --field-selector spec.nodeName="${CNODE1_NAME}" -o jsonpath='{.items[*].metadata.name}' 2>/dev/null | wc -w)
|
||||||
# Subtract system pods
|
# Subtract system pods
|
||||||
SYSTEM_PODS=$(kubectl get pods -A --field-selector spec.nodeName="${CNODE1_NAME}" -l k8s-app --no-headers 2>/dev/null | wc -l)
|
SYSTEM_PODS=$(kubectl get pods -A --field-selector spec.nodeName="${CNODE1_NAME}" -l k8s-app --no-headers 2>/dev/null | wc -l)
|
||||||
if [ "$USER_PODS" -le 10 ]; then ok "Only system pods on cnodes (expected)"; else fail "Unexpected pods on $CNODE1_NAME"; fi
|
if [ "$((USER_PODS - SYSTEM_PODS))" -le 0 ]; then ok "Only system pods on cnodes (expected)"; else fail "Unexpected pods on $CNODE1_NAME"; fi
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "============================================"
|
echo "============================================"
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
# shellcheck disable=SC2012 # diagnostic baseline script; ls -la listings are intentional
|
||||||
# baseline.sh — quick read-only baseline of a target node.
|
# baseline.sh — quick read-only baseline of a target node.
|
||||||
set -u
|
set -u
|
||||||
hdr() { printf '\n=== %s ===\n' "$1"; }
|
hdr() { printf '\n=== %s ===\n' "$1"; }
|
||||||
|
|||||||
@@ -37,9 +37,11 @@ for f in setupVars.conf pihole-FTL.conf adlists.list custom.list local.list rege
|
|||||||
echo "--- /etc/pihole/$f ---"; $DG exec pihole cat "/etc/pihole/$f" 2>&1 || true
|
echo "--- /etc/pihole/$f ---"; $DG exec pihole cat "/etc/pihole/$f" 2>&1 || true
|
||||||
done
|
done
|
||||||
echo "-- /etc/dnsmasq.d/* --"
|
echo "-- /etc/dnsmasq.d/* --"
|
||||||
|
# shellcheck disable=SC2016 # $f/$t expand inside the container's sh, not locally — single quotes are intentional
|
||||||
$DG exec pihole sh -c 'for f in /etc/dnsmasq.d/*; do echo "--- $f ---"; cat "$f"; done' 2>&1 || true
|
$DG exec pihole sh -c 'for f in /etc/dnsmasq.d/*; do echo "--- $f ---"; cat "$f"; done' 2>&1 || true
|
||||||
echo "-- pihole version --"; $DG exec pihole pihole -v 2>&1 || true
|
echo "-- pihole version --"; $DG exec pihole pihole -v 2>&1 || true
|
||||||
echo "-- gravity row counts --"
|
echo "-- gravity row counts --"
|
||||||
|
# shellcheck disable=SC2016 # $t expands inside the container's sh, not locally
|
||||||
$DG exec pihole sh -c 'for t in adlist domainlist client "group" info; do printf "%s=" "$t"; sqlite3 /etc/pihole/gravity.db "SELECT COUNT(*) FROM $t;" 2>/dev/null; done' 2>&1 || true
|
$DG exec pihole sh -c 'for t in adlist domainlist client "group" info; do printf "%s=" "$t"; sqlite3 /etc/pihole/gravity.db "SELECT COUNT(*) FROM $t;" 2>/dev/null; done' 2>&1 || true
|
||||||
echo "-- adlist addresses --"
|
echo "-- adlist addresses --"
|
||||||
$DG exec pihole sqlite3 /etc/pihole/gravity.db "SELECT address,enabled,comment FROM adlist;" 2>&1 || true
|
$DG exec pihole sqlite3 /etc/pihole/gravity.db "SELECT address,enabled,comment FROM adlist;" 2>&1 || true
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ if command -v sqlite3 >/dev/null 2>&1; then
|
|||||||
echo "-- adlist count --"; sqlite3 -readonly "$G" "SELECT COUNT(*) FROM adlist;" 2>&1
|
echo "-- adlist count --"; sqlite3 -readonly "$G" "SELECT COUNT(*) FROM adlist;" 2>&1
|
||||||
echo "-- domainlist count by type --"; sqlite3 -readonly "$G" "SELECT type,COUNT(*) FROM domainlist GROUP BY type;" 2>&1
|
echo "-- domainlist count by type --"; sqlite3 -readonly "$G" "SELECT type,COUNT(*) FROM domainlist GROUP BY type;" 2>&1
|
||||||
echo "-- domainlist (allow=0/allow_exact, deny=1/deny_exact, etc.) first 80 --"; sqlite3 -readonly "$G" "SELECT type,domain,enabled,comment FROM domainlist LIMIT 80;" 2>&1
|
echo "-- domainlist (allow=0/allow_exact, deny=1/deny_exact, etc.) first 80 --"; sqlite3 -readonly "$G" "SELECT type,domain,enabled,comment FROM domainlist LIMIT 80;" 2>&1
|
||||||
echo -- client --"; sqlite3 -readonly "$G" "SELECT ip,comment FROM client;" 2>&1
|
echo "-- client --"; sqlite3 -readonly "$G" "SELECT ip,comment FROM client;" 2>&1
|
||||||
echo "-- group --"; sqlite3 -readonly "$G" "SELECT id,name,enabled,comment FROM 'group';" 2>&1
|
echo "-- group --"; sqlite3 -readonly "$G" "SELECT id,name,enabled,comment FROM 'group';" 2>&1
|
||||||
echo "-- info --"; sqlite3 -readonly "$G" "SELECT * FROM info;" 2>&1
|
echo "-- info --"; sqlite3 -readonly "$G" "SELECT * FROM info;" 2>&1
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -34,7 +34,6 @@ set -euo pipefail
|
|||||||
|
|
||||||
NETBOOT="localuser@pfv-netboot"
|
NETBOOT="localuser@pfv-netboot"
|
||||||
SVC_ROOT="/home/localuser/services"
|
SVC_ROOT="/home/localuser/services"
|
||||||
PIHOLE_PW='REDACTED_PASSWORD' # replicated verbatim from netboot compose
|
|
||||||
|
|
||||||
log() { printf '\n\033[1;36m[%s]\033[0m %s\n' "$(date +%H:%M:%S)" "$*" >&2; }
|
log() { printf '\n\033[1;36m[%s]\033[0m %s\n' "$(date +%H:%M:%S)" "$*" >&2; }
|
||||||
warn() { printf '\n\033[1;33m[WARN %s]\033[0m %s\n' "$(date +%H:%M:%S)" "$*" >&2; }
|
warn() { printf '\n\033[1;33m[WARN %s]\033[0m %s\n' "$(date +%H:%M:%S)" "$*" >&2; }
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ trap 'rm -rf "$TMP"' EXIT
|
|||||||
# plus all error-like counters. Falls back gracefully if a counter doesn't
|
# plus all error-like counters. Falls back gracefully if a counter doesn't
|
||||||
# exist (different NIC drivers expose different names).
|
# exist (different NIC drivers expose different names).
|
||||||
nic_snapshot() {
|
nic_snapshot() {
|
||||||
|
# shellcheck disable=SC2013 # interface names contain no spaces; word-splitting is safe here
|
||||||
for s in $(awk '/^Slave Interface:/{print $3}' "/proc/net/bonding/$BOND"); do
|
for s in $(awk '/^Slave Interface:/{print $3}' "/proc/net/bonding/$BOND"); do
|
||||||
[ -n "$s" ] || continue
|
[ -n "$s" ] || continue
|
||||||
echo "[$s]"
|
echo "[$s]"
|
||||||
@@ -144,9 +145,10 @@ echo "Bond: $BOND"
|
|||||||
echo ""
|
echo ""
|
||||||
echo "--- bond0 hash + driver ---"
|
echo "--- bond0 hash + driver ---"
|
||||||
grep -E "Bonding Mode|Transmit Hash|Number of ports|Partner Mac" /proc/net/bonding/$BOND
|
grep -E "Bonding Mode|Transmit Hash|Number of ports|Partner Mac" /proc/net/bonding/$BOND
|
||||||
|
# shellcheck disable=SC2013 # interface names contain no spaces; word-splitting is safe here
|
||||||
for s in $(awk '/^Slave Interface:/{print $3}' /proc/net/bonding/$BOND); do
|
for s in $(awk '/^Slave Interface:/{print $3}' /proc/net/bonding/$BOND); do
|
||||||
drv=$(ethtool -i "$s" 2>/dev/null | awk -F: '/^driver:/{print $2}' | sed 's/^ *//')
|
drv=$(ethtool -i "$s" 2>/dev/null | awk -F: '/^driver:/{print $2}' | sed 's/^ *//')
|
||||||
speed=$(cat /sys/class/net/$s/speed 2>/dev/null)
|
speed=$(cat "/sys/class/net/$s/speed" 2>/dev/null)
|
||||||
ring=$(ethtool -g "$s" 2>/dev/null | awk '/RX:/{print $2; exit}')
|
ring=$(ethtool -g "$s" 2>/dev/null | awk '/RX:/{print $2; exit}')
|
||||||
printf " %-8s driver=%-20s speed=%-6s current RX ring=%s\n" "$s" "$drv" "$speed" "$ring"
|
printf " %-8s driver=%-20s speed=%-6s current RX ring=%s\n" "$s" "$drv" "$speed" "$ring"
|
||||||
done
|
done
|
||||||
|
|||||||
@@ -68,6 +68,7 @@ echo "[$(ts)] switch=$SWITCH device=$DEVICE baud=$BAUD host=$HOST"
|
|||||||
|
|
||||||
# 1. De-conflict: any local ssh to pfv-tsys4 in flight?
|
# 1. De-conflict: any local ssh to pfv-tsys4 in flight?
|
||||||
echo "[$(ts)] checking for in-flight ssh to pfv-tsys4..."
|
echo "[$(ts)] checking for in-flight ssh to pfv-tsys4..."
|
||||||
|
# shellcheck disable=SC2009 # intentional: need full ps columns filtered by process args
|
||||||
if ps -eo pid,etime,args | grep -E 'ssh.*pfv-tsys4|scp.*pfv-tsys4' | grep -v grep >/tmp/.swcap.ps 2>&1; then
|
if ps -eo pid,etime,args | grep -E 'ssh.*pfv-tsys4|scp.*pfv-tsys4' | grep -v grep >/tmp/.swcap.ps 2>&1; then
|
||||||
cat /tmp/.swcap.ps
|
cat /tmp/.swcap.ps
|
||||||
echo "[$(ts)] ABORT: another ssh/scp to pfv-tsys4 is running (other agent?)." >&2
|
echo "[$(ts)] ABORT: another ssh/scp to pfv-tsys4 is running (other agent?)." >&2
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ set -u
|
|||||||
|
|
||||||
# De-conflict: any ssh to pfv-tsys4 right now?
|
# De-conflict: any ssh to pfv-tsys4 right now?
|
||||||
echo "===== LOCAL ssh activity ====="
|
echo "===== LOCAL ssh activity ====="
|
||||||
|
# shellcheck disable=SC2009 # intentional: need full ps columns filtered by process args
|
||||||
ps -eo pid,etime,args | grep -E 'ssh.*pfv-tsys' | grep -v grep || echo "(none to pfv-tsys4)"
|
ps -eo pid,etime,args | grep -E 'ssh.*pfv-tsys' | grep -v grep || echo "(none to pfv-tsys4)"
|
||||||
|
|
||||||
echo
|
echo
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
set -u
|
set -u
|
||||||
|
|
||||||
echo "===== LOCAL ssh/scp activity (other-agent de-confliction) ====="
|
echo "===== LOCAL ssh/scp activity (other-agent de-confliction) ====="
|
||||||
|
# shellcheck disable=SC2009 # intentional: need full ps columns (etime,args) filtered by process args
|
||||||
ps -eo pid,ppid,etime,user,args | grep -E 'ssh|scp' | grep -v grep || echo "(none)"
|
ps -eo pid,ppid,etime,user,args | grep -E 'ssh|scp' | grep -v grep || echo "(none)"
|
||||||
|
|
||||||
echo
|
echo
|
||||||
|
|||||||
+3
-3
@@ -1,5 +1,5 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# shellcheck.sh - permanent wrapper to lint every shell script in this project.
|
# Lint wrapper - permanent wrapper to lint every shell script in this project.
|
||||||
#
|
#
|
||||||
# Uses the koalaman/shellcheck:stable docker image so nothing is installed
|
# Uses the koalaman/shellcheck:stable docker image so nothing is installed
|
||||||
# on the host. Run from anywhere; lints scripts/ and any .sh under switches/.
|
# on the host. Run from anywhere; lints scripts/ and any .sh under switches/.
|
||||||
@@ -45,7 +45,7 @@ echo
|
|||||||
# Make paths relative to ROOT so docker volume maps cleanly
|
# Make paths relative to ROOT so docker volume maps cleanly
|
||||||
REL_TARGETS=()
|
REL_TARGETS=()
|
||||||
for t in "${TARGETS[@]}"; do
|
for t in "${TARGETS[@]}"; do
|
||||||
rel="${t#$ROOT/}"
|
rel="${t#"$ROOT"/}"
|
||||||
[ "$rel" = "$t" ] && rel="$t"
|
[ "$rel" = "$t" ] && rel="$t"
|
||||||
REL_TARGETS+=("$rel")
|
REL_TARGETS+=("$rel")
|
||||||
done
|
done
|
||||||
@@ -61,5 +61,5 @@ if [ "$STRICT" -eq 1 ]; then
|
|||||||
exit $RC
|
exit $RC
|
||||||
fi
|
fi
|
||||||
# Non-strict: only fail on parse errors / errors, not style notes.
|
# Non-strict: only fail on parse errors / errors, not style notes.
|
||||||
# shellcheck exit code 1 means "findings"; re-run with severity to distinguish.
|
# (a shellcheck exit code of 1 means "findings"; re-run with severity to distinguish.)
|
||||||
exit 0
|
exit 0
|
||||||
|
|||||||
@@ -75,8 +75,7 @@ for HOST in "$@"; do
|
|||||||
for vmid in $VM_LIST; do
|
for vmid in $VM_LIST; do
|
||||||
name=$(ssh "${SSH_OPTS[@]}" "root@$HOST" "qm config $vmid 2>/dev/null | awk -F: '/^name:/{gsub(/^ /,\"\");print \$2}'")
|
name=$(ssh "${SSH_OPTS[@]}" "root@$HOST" "qm config $vmid 2>/dev/null | awk -F: '/^name:/{gsub(/^ /,\"\");print \$2}'")
|
||||||
echo -n " [$vmid $name] starting... "
|
echo -n " [$vmid $name] starting... "
|
||||||
start_output=$(ssh "${SSH_OPTS[@]}" "root@$HOST" "qm start $vmid" 2>&1)
|
if start_output=$(ssh "${SSH_OPTS[@]}" "root@$HOST" "qm start $vmid" 2>&1); then
|
||||||
if [ $? -eq 0 ]; then
|
|
||||||
echo "OK"
|
echo "OK"
|
||||||
else
|
else
|
||||||
echo "FAILED: $start_output"
|
echo "FAILED: $start_output"
|
||||||
@@ -115,8 +114,7 @@ for HOST in "$@"; do
|
|||||||
if [ "$agent" = "1" ]; then
|
if [ "$agent" = "1" ]; then
|
||||||
name=$(ssh "${SSH_OPTS[@]}" "root@$HOST" "qm config $vmid 2>/dev/null | awk -F: '/^name:/{gsub(/^ /,\"\");print \$2}'")
|
name=$(ssh "${SSH_OPTS[@]}" "root@$HOST" "qm config $vmid 2>/dev/null | awk -F: '/^name:/{gsub(/^ /,\"\");print \$2}'")
|
||||||
echo -n " [$vmid $name] agent ping... "
|
echo -n " [$vmid $name] agent ping... "
|
||||||
result=$(ssh "${SSH_OPTS[@]}" "root@$HOST" "timeout 10 qm agent $vmid ping 2>&1")
|
if ssh "${SSH_OPTS[@]}" "root@$HOST" "timeout 10 qm agent $vmid ping" >/dev/null 2>&1; then
|
||||||
if [ $? -eq 0 ]; then
|
|
||||||
echo "OK"
|
echo "OK"
|
||||||
else
|
else
|
||||||
echo "no response (VM may still be booting)"
|
echo "no response (VM may still be booting)"
|
||||||
|
|||||||
+1
-1
@@ -82,7 +82,7 @@ udevadm trigger --subsystem-match=tty 2>/dev/null || true
|
|||||||
sleep 1
|
sleep 1
|
||||||
|
|
||||||
if [ -e "/dev/${PDU_DEV_NAME}" ]; then
|
if [ -e "/dev/${PDU_DEV_NAME}" ]; then
|
||||||
echo " Device symlink active: /dev/${PDU_DEV_NAME} -> $(readlink -f /dev/${PDU_DEV_NAME})"
|
echo " Device symlink active: /dev/${PDU_DEV_NAME} -> $(readlink -f "/dev/${PDU_DEV_NAME}")"
|
||||||
else
|
else
|
||||||
echo " WARNING: /dev/${PDU_DEV_NAME} not found yet. Adapter may be unplugged."
|
echo " WARNING: /dev/${PDU_DEV_NAME} not found yet. Adapter may be unplugged."
|
||||||
echo " Falling back to /dev/ttyUSB* discovery..."
|
echo " Falling back to /dev/ttyUSB* discovery..."
|
||||||
|
|||||||
@@ -5,17 +5,18 @@
|
|||||||
#magic to detect main int
|
#magic to detect main int
|
||||||
echo "Determining management interface..."
|
echo "Determining management interface..."
|
||||||
#export MAIN_INT=$(brctl show $(netstat -rn|grep 0.0.0.0|head -n1|awk '{print $NF}') | awk '{print $NF}'|tail -1|awk -F '.' '{print $1}')
|
#export MAIN_INT=$(brctl show $(netstat -rn|grep 0.0.0.0|head -n1|awk '{print $NF}') | awk '{print $NF}'|tail -1|awk -F '.' '{print $1}')
|
||||||
export MAIN_INT=$(brctl show|grep vmbr0|awk '{print $NF}'|awk -F '.' '{print $1}')
|
MAIN_INT=$(brctl show|grep vmbr0|awk '{print $NF}'|awk -F '.' '{print $1}')
|
||||||
|
export MAIN_INT
|
||||||
|
|
||||||
echo "Management interface is: $MAIN_INT"
|
echo "Management interface is: $MAIN_INT"
|
||||||
|
|
||||||
#fix the issue
|
#fix the issue
|
||||||
echo "Fixing management interface..."
|
echo "Fixing management interface..."
|
||||||
ethtool -K $MAIN_INT tso off
|
ethtool -K "$MAIN_INT" tso off
|
||||||
ethtool -K $MAIN_INT gro off
|
ethtool -K "$MAIN_INT" gro off
|
||||||
ethtool -K $MAIN_INT gso off
|
ethtool -K "$MAIN_INT" gso off
|
||||||
ethtool -K $MAIN_INT tx off
|
ethtool -K "$MAIN_INT" tx off
|
||||||
ethtool -K $MAIN_INT rx off
|
ethtool -K "$MAIN_INT" rx off
|
||||||
|
|
||||||
#https://forum.proxmox.com/threads/e1000-driver-hang.58284/
|
#https://forum.proxmox.com/threads/e1000-driver-hang.58284/
|
||||||
#https://serverfault.com/questions/616485/e1000e-reset-adapter-unexpectedly-detected-hardware-unit-hang
|
#https://serverfault.com/questions/616485/e1000e-reset-adapter-unexpectedly-detected-hardware-unit-hang
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# auth-cloudron-ldap.sh — placeholder module (Cloudron LDAP auth integration).
|
||||||
|
# Intentionally empty; populated when the auth stack is deployed.
|
||||||
|
true
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
# shellcheck disable=SC2103 # legacy R&D build script; cd/cd- sequence is intentional
|
||||||
|
|
||||||
#Made from instructions at https://www.tunetheweb.com/performance/http2/
|
#Made from instructions at https://www.tunetheweb.com/performance/http2/
|
||||||
|
|
||||||
@@ -24,17 +25,17 @@ CURL_FILE="curl-7.60.0.tar.gz"
|
|||||||
#Download and install latest version of openssl
|
#Download and install latest version of openssl
|
||||||
wget $OPENSSL_URL_BASE/$OPENSSL_FILE
|
wget $OPENSSL_URL_BASE/$OPENSSL_FILE
|
||||||
tar xzf $OPENSSL_FILE
|
tar xzf $OPENSSL_FILE
|
||||||
cd openssl-1.1.0h
|
cd openssl-1.1.0h || exit
|
||||||
./config enable-weak-ssl-ciphers shared zlib-dynamic -DOPENSSL_TLS_SECURITY_LEVEL=0 --prefix=/usr/local/custom-ssl/openssl-1.1.0h ; make ; make install
|
./config enable-weak-ssl-ciphers shared zlib-dynamic -DOPENSSL_TLS_SECURITY_LEVEL=0 --prefix=/usr/local/custom-ssl/openssl-1.1.0h ; make ; make install
|
||||||
ln -s /usr/local/custom-ssl/openssl-1.1.0h /usr/local/openssl
|
ln -s /usr/local/custom-ssl/openssl-1.1.0h /usr/local/openssl
|
||||||
cd -
|
cd - || exit
|
||||||
|
|
||||||
#Download and install nghttp2 (needed for mod_http2).
|
#Download and install nghttp2 (needed for mod_http2).
|
||||||
wget $NGHTTP_URL_BASE/$NGHTTP_FILE
|
wget $NGHTTP_URL_BASE/$NGHTTP_FILE
|
||||||
tar xzf $NGHTTP_FILE
|
tar xzf $NGHTTP_FILE
|
||||||
cd nghttp2-1.31.0
|
cd nghttp2-1.31.0 || exit
|
||||||
./configure --prefix=/usr/local/custom-ssl/nghttp ; make ; make install
|
./configure --prefix=/usr/local/custom-ssl/nghttp ; make ; make install
|
||||||
cd -
|
cd - || exit
|
||||||
|
|
||||||
#Updated ldconfig so curl build
|
#Updated ldconfig so curl build
|
||||||
|
|
||||||
@@ -48,34 +49,34 @@ ldconfig
|
|||||||
#Download and install curl
|
#Download and install curl
|
||||||
wget $CURL_URL_BASE/$CURL_FILE
|
wget $CURL_URL_BASE/$CURL_FILE
|
||||||
tar xzf curl-7.60.0.tar.gz
|
tar xzf curl-7.60.0.tar.gz
|
||||||
cd curl-7.60.0
|
cd curl-7.60.0 || exit
|
||||||
./configure --prefix=/usr/local/custom-ssl/curl --with-nghttp2=/usr/local/custom-ssl/nghttp/ --with-ssl=/usr/local/custom-ssl/openssl-1.1.0h/ ; make ; make install
|
./configure --prefix=/usr/local/custom-ssl/curl --with-nghttp2=/usr/local/custom-ssl/nghttp/ --with-ssl=/usr/local/custom-ssl/openssl-1.1.0h/ ; make ; make install
|
||||||
cd -
|
cd - || exit
|
||||||
|
|
||||||
|
|
||||||
#Download and install latest apr
|
#Download and install latest apr
|
||||||
wget $APR_URL_BASE/$APR_FILE
|
wget $APR_URL_BASE/$APR_FILE
|
||||||
tar xzf $APR_FILE
|
tar xzf $APR_FILE
|
||||||
cd apr-1.6.3
|
cd apr-1.6.3 || exit
|
||||||
./configure --prefix=/usr/local/custom-ssl/apr ; make ; make install
|
./configure --prefix=/usr/local/custom-ssl/apr ; make ; make install
|
||||||
cd -
|
cd - || exit
|
||||||
|
|
||||||
#Download and install latest apr-util
|
#Download and install latest apr-util
|
||||||
wget $APR_UTIL_URL_BASE/$APR_UTIL_FILE
|
wget $APR_UTIL_URL_BASE/$APR_UTIL_FILE
|
||||||
tar xzf apr-util-1.6.1.tar.gz
|
tar xzf apr-util-1.6.1.tar.gz
|
||||||
cd apr-util-1.6.1
|
cd apr-util-1.6.1 || exit
|
||||||
./configure --prefix=/usr/local/custom-ssl/apr-util --with-apr=/usr/local/custom-ssl/apr ; make; make install
|
./configure --prefix=/usr/local/custom-ssl/apr-util --with-apr=/usr/local/custom-ssl/apr ; make; make install
|
||||||
cd -
|
cd - || exit
|
||||||
|
|
||||||
#Download and install apache
|
#Download and install apache
|
||||||
wget $APACHE_URL_BASE/$APACHE_FILE
|
wget $APACHE_URL_BASE/$APACHE_FILE
|
||||||
tar xzf httpd-2.4.33.tar.gz
|
tar xzf httpd-2.4.33.tar.gz
|
||||||
cd httpd-2.4.33
|
cd httpd-2.4.33 || exit
|
||||||
cp -r ../apr-1.6.3 srclib/apr
|
cp -r ../apr-1.6.3 srclib/apr
|
||||||
cp -r ../apr-util-1.6.1 srclib/apr-util
|
cp -r ../apr-util-1.6.1 srclib/apr-util
|
||||||
./configure --prefix=/usr/local/custom-ssl/apache --with-ssl=/usr/local/custom-ssl/openssl-1.1.0h/ --with-pcre=/usr/bin/pcre-config --enable-unique-id --enable-ssl --enable-so --with-included-apr --enable-http2 --with-nghttp2=/usr/local/custom-ssl/nghttp/
|
./configure --prefix=/usr/local/custom-ssl/apache --with-ssl=/usr/local/custom-ssl/openssl-1.1.0h/ --with-pcre=/usr/bin/pcre-config --enable-unique-id --enable-ssl --enable-so --with-included-apr --enable-http2 --with-nghttp2=/usr/local/custom-ssl/nghttp/
|
||||||
make
|
make
|
||||||
make install
|
make install
|
||||||
ln -s /usr/local/custom-ssl/apache /usr/local/apache
|
ln -s /usr/local/custom-ssl/apache /usr/local/apache
|
||||||
cd -
|
cd - || exit
|
||||||
|
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ if [ "$SUBODEV_CHECK" = 1 ]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
export DEV_WORKSTATION_CHECK
|
export DEV_WORKSTATION_CHECK
|
||||||
DEV_WORKSTATION_CHECK="$(hostname | egrep -c 'subopi-dev|CharlesDevServer' || true)"
|
DEV_WORKSTATION_CHECK="$(hostname | grep -Ec 'subopi-dev|CharlesDevServer' || true)"
|
||||||
|
|
||||||
if [ "$DEV_WORKSTATION_CHECK" -eq 0 ]; then
|
if [ "$DEV_WORKSTATION_CHECK" -eq 0 ]; then
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ export user_check
|
|||||||
user_check="$(echo "$curr_user" | grep -c root)"
|
user_check="$(echo "$curr_user" | grep -c root)"
|
||||||
|
|
||||||
|
|
||||||
if [ $user_check -ne 1 ]; then
|
if [ "$user_check" -ne 1 ]; then
|
||||||
print_error "Must run as root."
|
print_error "Must run as root."
|
||||||
error_out
|
error_out
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
|
# shellcheck shell=bash disable=SC2148 # sourced function file (no shebang by design)
|
||||||
function pi-detect()
|
function pi-detect()
|
||||||
{
|
{
|
||||||
print_info Now running "$FUNCNAME"....
|
print_info Now running "${FUNCNAME[0]}"....
|
||||||
if [ -f /sys/firmware/devicetree/base/model ] ; then
|
if [ -f /sys/firmware/devicetree/base/model ] ; then
|
||||||
export IS_RASPI="1"
|
export IS_RASPI="1"
|
||||||
fi
|
fi
|
||||||
@@ -9,5 +9,5 @@ fi
|
|||||||
if [ ! -f /sys/firmware/devicetree/base/model ] ; then
|
if [ ! -f /sys/firmware/devicetree/base/model ] ; then
|
||||||
export IS_RASPI="0"
|
export IS_RASPI="0"
|
||||||
fi
|
fi
|
||||||
print_info Completed running "$FUNCNAME"
|
print_info Completed running "${FUNCNAME[0]}"
|
||||||
}
|
}
|
||||||
@@ -60,18 +60,18 @@ LOCALUSER_CHECK="$(getent passwd | grep -c localuser || true)"
|
|||||||
#######################
|
#######################
|
||||||
|
|
||||||
function global-oam() {
|
function global-oam() {
|
||||||
print_info "Now running $FUNCNAME...."
|
print_info "Now running ${FUNCNAME[0]}...."
|
||||||
|
|
||||||
cat "$SCRIPTS_PATH/up2date.sh" >/usr/local/bin/up2date.sh && chmod +x /usr/local/bin/up2date.sh
|
cat "$SCRIPTS_PATH/up2date.sh" >/usr/local/bin/up2date.sh && chmod +x /usr/local/bin/up2date.sh
|
||||||
|
|
||||||
bash "$MODULES_PATH/OAM/oam-librenms.sh"
|
bash "$MODULES_PATH/OAM/oam-librenms.sh"
|
||||||
|
|
||||||
print_info "Completed running $FUNCNAME"
|
print_info "Completed running ${FUNCNAME[0]}"
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function global-systemServiceConfigurationFiles() {
|
function global-systemServiceConfigurationFiles() {
|
||||||
print_info "Now running $FUNCNAME...."
|
print_info "Now running ${FUNCNAME[0]}...."
|
||||||
|
|
||||||
cat "$CONFIGFILES_PATH/ZSH/tsys-zshrc" >/etc/zshrc
|
cat "$CONFIGFILES_PATH/ZSH/tsys-zshrc" >/etc/zshrc
|
||||||
cat "$CONFIGFILES_PATH/SMTP/aliases" >/etc/aliases
|
cat "$CONFIGFILES_PATH/SMTP/aliases" >/etc/aliases
|
||||||
@@ -79,11 +79,11 @@ function global-systemServiceConfigurationFiles() {
|
|||||||
|
|
||||||
newaliases
|
newaliases
|
||||||
|
|
||||||
print_info "Completed running $FUNCNAME"
|
print_info "Completed running ${FUNCNAME[0]}"
|
||||||
}
|
}
|
||||||
|
|
||||||
function global-installPackages() {
|
function global-installPackages() {
|
||||||
print_info "Now running $FUNCNAME...."
|
print_info "Now running ${FUNCNAME[0]}...."
|
||||||
|
|
||||||
# Setup webmin repo, used for RBAC/2fa PAM
|
# Setup webmin repo, used for RBAC/2fa PAM
|
||||||
|
|
||||||
@@ -195,7 +195,7 @@ function global-installPackages() {
|
|||||||
VIRT_TYPE="$(virt-what)"
|
VIRT_TYPE="$(virt-what)"
|
||||||
|
|
||||||
export IS_VIRT_GUEST
|
export IS_VIRT_GUEST
|
||||||
IS_VIRT_GUEST="$(echo "$VIRT_TYPE" | egrep -c 'hyperv|kvm' || true)"
|
IS_VIRT_GUEST="$(echo "$VIRT_TYPE" | grep -Ec 'hyperv|kvm' || true)"
|
||||||
|
|
||||||
export IS_KVM_GUEST
|
export IS_KVM_GUEST
|
||||||
IS_KVM_GUEST="$(echo "$VIRT_TYPE" | grep -c 'kvm' || true)"
|
IS_KVM_GUEST="$(echo "$VIRT_TYPE" | grep -c 'kvm' || true)"
|
||||||
@@ -227,12 +227,12 @@ function global-installPackages() {
|
|||||||
|
|
||||||
# vault cli
|
# vault cli
|
||||||
|
|
||||||
print_info "Completed running $FUNCNAME"
|
print_info "Completed running ${FUNCNAME[0]}"
|
||||||
}
|
}
|
||||||
|
|
||||||
function global-postPackageConfiguration() {
|
function global-postPackageConfiguration() {
|
||||||
|
|
||||||
print_info "Now running $FUNCNAME"
|
print_info "Now running ${FUNCNAME[0]}"
|
||||||
|
|
||||||
systemctl --now enable auditd
|
systemctl --now enable auditd
|
||||||
|
|
||||||
@@ -253,7 +253,7 @@ function global-postPackageConfiguration() {
|
|||||||
#This is under test/dev and may fail
|
#This is under test/dev and may fail
|
||||||
echo "hi from root to root" | mail -s "hi directly to root from $(hostname)" root
|
echo "hi from root to root" | mail -s "hi directly to root from $(hostname)" root
|
||||||
|
|
||||||
chsh -s $(which zsh) root
|
chsh -s "$(which zsh)" root
|
||||||
|
|
||||||
if [ "$LOCALUSER_CHECK" -gt 0 ]; then
|
if [ "$LOCALUSER_CHECK" -gt 0 ]; then
|
||||||
chsh -s "$(which zsh)" localuser
|
chsh -s "$(which zsh)" localuser
|
||||||
@@ -312,7 +312,7 @@ function global-postPackageConfiguration() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
export NTP_SERVER_CHECK
|
export NTP_SERVER_CHECK
|
||||||
NTP_SERVER_CHECK="$(hostname | egrep -c 'pfv-netboot|pfvsvrpi|pfv-netinfra' || true)"
|
NTP_SERVER_CHECK="$(hostname | grep -Ec 'pfv-netboot|pfvsvrpi|pfv-netinfra' || true)"
|
||||||
|
|
||||||
if [ "$NTP_SERVER_CHECK" -eq 0 ]; then
|
if [ "$NTP_SERVER_CHECK" -eq 0 ]; then
|
||||||
|
|
||||||
@@ -341,7 +341,7 @@ function global-postPackageConfiguration() {
|
|||||||
tuned-adm profile virtual-guest
|
tuned-adm profile virtual-guest
|
||||||
fi
|
fi
|
||||||
|
|
||||||
print_info "Completed running $FUNCNAME"
|
print_info "Completed running ${FUNCNAME[0]}"
|
||||||
}
|
}
|
||||||
|
|
||||||
####################################################################################################
|
####################################################################################################
|
||||||
@@ -355,41 +355,41 @@ function global-postPackageConfiguration() {
|
|||||||
# SSH
|
# SSH
|
||||||
|
|
||||||
function secharden-ssh() {
|
function secharden-ssh() {
|
||||||
print_info "Now running $FUNCNAME"
|
print_info "Now running ${FUNCNAME[0]}"
|
||||||
|
|
||||||
bash "$MODULES_PATH/Security/secharden-ssh.sh"
|
bash "$MODULES_PATH/Security/secharden-ssh.sh"
|
||||||
|
|
||||||
print_info "Completed running $FUNCNAME"
|
print_info "Completed running ${FUNCNAME[0]}"
|
||||||
}
|
}
|
||||||
|
|
||||||
function secharden-wazuh() {
|
function secharden-wazuh() {
|
||||||
print_info "Now running $FUNCNAME"
|
print_info "Now running ${FUNCNAME[0]}"
|
||||||
bash "$MODULES_PATH/Security/secharden-wazuh.sh"
|
bash "$MODULES_PATH/Security/secharden-wazuh.sh"
|
||||||
print_info "Completed running $FUNCNAME"
|
print_info "Completed running ${FUNCNAME[0]}"
|
||||||
}
|
}
|
||||||
|
|
||||||
function secharden-2fa() {
|
function secharden-2fa() {
|
||||||
print_info "Now running $FUNCNAME"
|
print_info "Now running ${FUNCNAME[0]}"
|
||||||
bash "$MODULES_PATH/Security/secharden-2fa.sh"
|
bash "$MODULES_PATH/Security/secharden-2fa.sh"
|
||||||
print_info "Completed running $FUNCNAME"
|
print_info "Completed running ${FUNCNAME[0]}"
|
||||||
}
|
}
|
||||||
|
|
||||||
function secharden-scap-stig() {
|
function secharden-scap-stig() {
|
||||||
print_info "Now running $FUNCNAME"
|
print_info "Now running ${FUNCNAME[0]}"
|
||||||
bash "$MODULES_PATH/Security/secharden-scap-stig.sh"
|
bash "$MODULES_PATH/Security/secharden-scap-stig.sh"
|
||||||
print_info "Completed running $FUNCNAME"
|
print_info "Completed running ${FUNCNAME[0]}"
|
||||||
}
|
}
|
||||||
|
|
||||||
function secharden-agents() {
|
function secharden-agents() {
|
||||||
print_info "Now running $FUNCNAME"
|
print_info "Now running ${FUNCNAME[0]}"
|
||||||
bash "$MODULES_PATH/Security/secharden-audit-agents.sh"
|
bash "$MODULES_PATH/Security/secharden-audit-agents.sh"
|
||||||
print_info "Completed running $FUNCNAME"
|
print_info "Completed running ${FUNCNAME[0]}"
|
||||||
}
|
}
|
||||||
|
|
||||||
function secharden-auto-upgrades() {
|
function secharden-auto-upgrades() {
|
||||||
print_info "Now running $FUNCNAME"
|
print_info "Now running ${FUNCNAME[0]}"
|
||||||
#curl --silent ${DL_ROOT}/Modules/Security/secharden-ssh.sh|$(which bash)
|
#curl --silent ${DL_ROOT}/Modules/Security/secharden-ssh.sh|$(which bash)
|
||||||
print_info "Completed running $FUNCNAME"
|
print_info "Completed running ${FUNCNAME[0]}"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -400,16 +400,16 @@ function secharden-auto-upgrades() {
|
|||||||
####################################################################################################
|
####################################################################################################
|
||||||
|
|
||||||
function auth-cloudron-ldap() {
|
function auth-cloudron-ldap() {
|
||||||
print_info "Now running "$FUNCNAME""
|
print_info "Now running ${FUNCNAME[0]}"
|
||||||
#curl --silent ${DL_ROOT}/Modules/Auth/auth-cloudron-ldap.sh|$(which bash)
|
#curl --silent ${DL_ROOT}/Modules/Auth/auth-cloudron-ldap.sh|$(which bash)
|
||||||
print_info "Completed running "$FUNCNAME""
|
print_info "Completed running ${FUNCNAME[0]}"
|
||||||
}
|
}
|
||||||
|
|
||||||
####################################################################################################
|
####################################################################################################
|
||||||
# RUn the various functions in the correct order
|
# RUn the various functions in the correct order
|
||||||
####################################################################################################
|
####################################################################################################
|
||||||
|
|
||||||
echo >$LOGFILENAME
|
echo >"$LOGFILENAME"
|
||||||
|
|
||||||
print_info "Execution starting at $CURRENT_TIMESTAMP..."
|
print_info "Execution starting at $CURRENT_TIMESTAMP..."
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
# shellcheck shell=bash disable=SC2148 # sourced .bashrc profile fragment
|
||||||
if command -v tmux &> /dev/null && [ -n "$PS1" ] && [[ ! "$TERM" =~ screen ]] && [[ ! "$TERM" =~ tmux ]] && [ -z "$TMUX" ]; then
|
if command -v tmux &> /dev/null && [ -n "$PS1" ] && [[ ! "$TERM" =~ screen ]] && [[ ! "$TERM" =~ tmux ]] && [ -z "$TMUX" ]; then
|
||||||
tmux a -t default || exec tmux new -s default && exit;
|
tmux a -t default || exec tmux new -s default && exit;
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -1 +1,2 @@
|
|||||||
|
# shellcheck shell=bash disable=SC2148 # sourced .bashrc profile fragment
|
||||||
export HISTTIMEFORMAT="%m/%d/%Y %T "
|
export HISTTIMEFORMAT="%m/%d/%Y %T "
|
||||||
+2
-1
@@ -52,7 +52,8 @@ function run_test_suite() {
|
|||||||
|
|
||||||
function run_single_test() {
|
function run_single_test() {
|
||||||
local test_file="$1"
|
local test_file="$1"
|
||||||
local test_name="$(basename "$test_file" .sh)"
|
local test_name
|
||||||
|
test_name="$(basename "$test_file" .sh)"
|
||||||
|
|
||||||
print_info "Running test: $test_name"
|
print_info "Running test: $test_name"
|
||||||
|
|
||||||
|
|||||||
@@ -5,8 +5,6 @@
|
|||||||
|
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
PROJECT_ROOT="$(dirname "$(realpath "${BASH_SOURCE[0]}")")/../.."
|
|
||||||
|
|
||||||
function test_2fa_packages() {
|
function test_2fa_packages() {
|
||||||
echo "🔍 Testing 2FA package installation..."
|
echo "🔍 Testing 2FA package installation..."
|
||||||
|
|
||||||
@@ -235,7 +233,8 @@ function test_backup_existence() {
|
|||||||
|
|
||||||
if [[ -d "$backup_dir" ]]; then
|
if [[ -d "$backup_dir" ]]; then
|
||||||
# Look for recent 2FA backups
|
# Look for recent 2FA backups
|
||||||
local recent_backups=$(find "$backup_dir" -name "2fa-*" -type d -newer /etc/ssh/sshd_config 2>/dev/null | wc -l)
|
local recent_backups
|
||||||
|
recent_backups=$(find "$backup_dir" -name "2fa-*" -type d -newer /etc/ssh/sshd_config 2>/dev/null | wc -l)
|
||||||
|
|
||||||
if [[ $recent_backups -gt 0 ]]; then
|
if [[ $recent_backups -gt 0 ]]; then
|
||||||
echo "✅ Recent 2FA backup found in $backup_dir"
|
echo "✅ Recent 2FA backup found in $backup_dir"
|
||||||
|
|||||||
@@ -0,0 +1,92 @@
|
|||||||
|
#!/usr/bin/bash
|
||||||
|
#
|
||||||
|
# tests/shellcheck.sh — enforce shellcheck (via Docker) on all repo shell scripts
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# bash tests/shellcheck.sh # lint every .sh in the repo
|
||||||
|
# bash tests/shellcheck.sh path/a.sh path/b.sh # lint specific files
|
||||||
|
#
|
||||||
|
# Uses the koalaman/shellcheck:stable image (no native binary required).
|
||||||
|
# Exits non-zero if ANY script emits a diagnostic. Skips non-bash scripts
|
||||||
|
# (e.g. PHP files with a .sh extension) and the vendored/ trees.
|
||||||
|
#
|
||||||
|
set -uo pipefail
|
||||||
|
|
||||||
|
IMAGE="koalaman/shellcheck:stable"
|
||||||
|
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
|
|
||||||
|
# --- Gather target files -----------------------------------------------------
|
||||||
|
mapfile -t FILES < <(
|
||||||
|
if [ "$#" -gt 0 ]; then
|
||||||
|
# Explicit args: resolve to repo-root-relative paths, keep only .sh
|
||||||
|
for f in "$@"; do
|
||||||
|
f_abs="$(cd "$(dirname "$f")" && pwd)/$(basename "$f")"
|
||||||
|
case "$f_abs" in
|
||||||
|
"$ROOT"/vendor/*) ;; # skip vendored
|
||||||
|
"$ROOT"/provisioning/Agents/librenms/*) ;; # upstream librenms-agent scripts
|
||||||
|
*) case "$f_abs" in *.sh) echo "$f_abs";; esac ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
else
|
||||||
|
# Default: every .sh under the repo, minus vendor/
|
||||||
|
while IFS= read -r f_abs; do
|
||||||
|
case "$f_abs" in
|
||||||
|
"$ROOT"/vendor/*) ;; # skip vendored
|
||||||
|
"$ROOT"/provisioning/Agents/librenms/*) ;; # upstream librenms-agent scripts
|
||||||
|
*) echo "$f_abs" ;;
|
||||||
|
esac
|
||||||
|
done < <(find "$ROOT" -type f -name '*.sh' \
|
||||||
|
-not -path '*/.git/*' -not -path "$ROOT/vendor/*")
|
||||||
|
fi
|
||||||
|
)
|
||||||
|
|
||||||
|
if [ "${#FILES[@]}" -eq 0 ]; then
|
||||||
|
echo "shellcheck.sh: no .sh files to check." >&2
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- Filter out non-bash scripts (e.g. PHP with a .sh extension) --------------
|
||||||
|
BASH_FILES=()
|
||||||
|
for f in "${FILES[@]}"; do
|
||||||
|
shebang=$(head -c 64 "$f" 2>/dev/null | head -n1)
|
||||||
|
case "$shebang" in
|
||||||
|
\#!/usr/bin/php*|\#!/usr/bin/env\ php*) ;; # PHP, skip
|
||||||
|
\#!*) BASH_FILES+=("$f") ;; # any other shebang → check
|
||||||
|
*) BASH_FILES+=("$f") ;; # no shebang → check anyway
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ "${#BASH_FILES[@]}" -eq 0 ]; then
|
||||||
|
echo "shellcheck.sh: no bash scripts among targets." >&2
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- Run shellcheck in Docker (mount repo root, pass root-relative paths) -----
|
||||||
|
# shellcheck disable=SC2012 # basename loop is intentional
|
||||||
|
REL=()
|
||||||
|
for f in "${BASH_FILES[@]}"; do REL+=("${f#"$ROOT"/}"); done
|
||||||
|
|
||||||
|
# Disable checks that are intentional conventions of this codebase (not bugs):
|
||||||
|
# SC1090/SC1091 — cannot follow dynamically-computed `source` paths (KNEL framework)
|
||||||
|
# SC2029 — ssh orchestration deliberately expands the command client-side
|
||||||
|
DISABLES=(
|
||||||
|
-e SC1090
|
||||||
|
-e SC1091
|
||||||
|
-e SC2029
|
||||||
|
)
|
||||||
|
|
||||||
|
echo "Checking ${#BASH_FILES[@]} script(s) with $IMAGE:"
|
||||||
|
printf ' %s\n' "${REL[@]}"
|
||||||
|
|
||||||
|
docker run --rm \
|
||||||
|
-v "$ROOT:/mnt:ro" \
|
||||||
|
-w /mnt \
|
||||||
|
"$IMAGE" -x "${DISABLES[@]}" "${REL[@]}"
|
||||||
|
rc=$?
|
||||||
|
|
||||||
|
if [ "$rc" -eq 0 ]; then
|
||||||
|
echo "shellcheck: PASS (${#BASH_FILES[@]} scripts clean)"
|
||||||
|
else
|
||||||
|
echo "shellcheck: FAIL (fix the findings above or add targeted disable directives)" >&2
|
||||||
|
fi
|
||||||
|
exit "$rc"
|
||||||
@@ -6,8 +6,6 @@
|
|||||||
|
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
|
||||||
|
|
||||||
# The authoritative pair (pfv-netinfra-01 / pfv-netinfra-02).
|
# The authoritative pair (pfv-netinfra-01 / pfv-netinfra-02).
|
||||||
DNS_PRIMARY="192.168.3.252"
|
DNS_PRIMARY="192.168.3.252"
|
||||||
DNS_SECONDARY="192.168.3.253"
|
DNS_SECONDARY="192.168.3.253"
|
||||||
@@ -20,7 +18,6 @@ NTP_CONF="/etc/ntpsec/ntp.conf"
|
|||||||
# A name every recursive resolver must be able to resolve.
|
# A name every recursive resolver must be able to resolve.
|
||||||
DNS_PROBE_NAME="github.com"
|
DNS_PROBE_NAME="github.com"
|
||||||
|
|
||||||
failed=0
|
|
||||||
have() { command -v "$1" >/dev/null 2>&1; }
|
have() { command -v "$1" >/dev/null 2>&1; }
|
||||||
|
|
||||||
# --- Configuration assertions -------------------------------------------------
|
# --- Configuration assertions -------------------------------------------------
|
||||||
|
|||||||
@@ -12,7 +12,8 @@ REQUIRED_COMMANDS=("curl" "wget" "git" "systemctl" "apt-get")
|
|||||||
|
|
||||||
# Test functions
|
# Test functions
|
||||||
function test_memory_requirements() {
|
function test_memory_requirements() {
|
||||||
local total_mem_kb=$(grep MemTotal /proc/meminfo | awk '{print $2}')
|
local total_mem_kb
|
||||||
|
total_mem_kb=$(grep MemTotal /proc/meminfo | awk '{print $2}')
|
||||||
local total_mem_gb=$((total_mem_kb / 1024 / 1024))
|
local total_mem_gb=$((total_mem_kb / 1024 / 1024))
|
||||||
|
|
||||||
if [[ $total_mem_gb -ge $MIN_RAM_GB ]]; then
|
if [[ $total_mem_gb -ge $MIN_RAM_GB ]]; then
|
||||||
@@ -25,7 +26,8 @@ function test_memory_requirements() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function test_disk_space() {
|
function test_disk_space() {
|
||||||
local available_gb=$(df / | tail -1 | awk '{print int($4/1024/1024)}')
|
local available_gb
|
||||||
|
available_gb=$(df / | tail -1 | awk '{print int($4/1024/1024)}')
|
||||||
|
|
||||||
if [[ $available_gb -ge $MIN_DISK_GB ]]; then
|
if [[ $available_gb -ge $MIN_DISK_GB ]]; then
|
||||||
echo "✅ Disk space requirement met: ${available_gb}GB >= ${MIN_DISK_GB}GB"
|
echo "✅ Disk space requirement met: ${available_gb}GB >= ${MIN_DISK_GB}GB"
|
||||||
@@ -53,8 +55,10 @@ function test_required_commands() {
|
|||||||
|
|
||||||
function test_os_compatibility() {
|
function test_os_compatibility() {
|
||||||
if [[ -f /etc/os-release ]]; then
|
if [[ -f /etc/os-release ]]; then
|
||||||
local os_id=$(grep "^ID=" /etc/os-release | cut -d'=' -f2 | tr -d '"')
|
local os_id
|
||||||
local os_version=$(grep "^VERSION_ID=" /etc/os-release | cut -d'=' -f2 | tr -d '"')
|
local os_version
|
||||||
|
os_id=$(grep "^ID=" /etc/os-release | cut -d'=' -f2 | tr -d '"')
|
||||||
|
os_version=$(grep "^VERSION_ID=" /etc/os-release | cut -d'=' -f2 | tr -d '"')
|
||||||
|
|
||||||
case "$os_id" in
|
case "$os_id" in
|
||||||
ubuntu|debian)
|
ubuntu|debian)
|
||||||
|
|||||||
Reference in New Issue
Block a user