Skip to main content

max / makenotwork

server: add ClamAV + YARA prod setup scripts One-time setup scripts that ship via deploy.sh and run manually on the Hetzner prod host to turn on the two scan layers that currently show "down" on the admin dashboard. setup-clamav.sh: installs clamav-daemon + clamav-freshclam, verifies the LocalSocket directive matches /var/run/clamav/clamd.ctl, waits up to 5min for the initial signature DB pull, enables systemd services, socat-pings the socket before declaring success. Prints the env-var line to add to /opt/makenotwork/.env. setup-yara-rules.sh: shallow git clones Florian Roth's signature-base ruleset (CC-BY-NC 4.0), flattens .yar files into /opt/makenotwork/yara-rules, stamps RULESET_VERSION with the active commit SHA for audit-trail correlation, installs a weekly cron at 04:30 UTC Mondays to refresh upstream. Idempotent. Notes that a manual systemctl restart picks up newly-compiled rules after a ruleset bump. deploy.sh: scp both scripts to /opt/makenotwork/deploy/ and chmod +x on the config-upload path. Scripts are NOT auto-run on every deploy. Operator procedure documented in scan-pipeline-audit.md Appendix B. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Author: Max J. <87768334+MaxJMath@users.noreply.github.com> · 2026-05-24 20:56 UTC
Commit: 2b6537be4520884725808d2ec53434be2a85d74e
Parent: 34a68b7
3 files changed, +270 insertions, -1 deletion
@@ -46,7 +46,10 @@ upload_config() {
46 46 ssh $SSH_OPTS $SERVER "mkdir -p $REMOTE_DIR/deploy"
47 47 scp $SCP_OPTS $DEPLOY_DIR/sshd-git.conf $DEPLOY_DIR/fail2ban-sshd.conf $DEPLOY_DIR/setup-firewall.sh $SERVER:$REMOTE_DIR/deploy/
48 48 scp $SCP_OPTS $DEPLOY_DIR/setup-git-ssh.sh $DEPLOY_DIR/setup-ssh-keys.sh $SERVER:$REMOTE_DIR/deploy/ 2>/dev/null || true
49 - ssh $SSH_OPTS $SERVER "chmod +x $REMOTE_DIR/deploy/setup-firewall.sh $REMOTE_DIR/deploy/setup-git-ssh.sh $REMOTE_DIR/deploy/setup-ssh-keys.sh 2>/dev/null || true"
49 + # Scan-pipeline setup scripts (one-time, manually run on prod). See
50 + # docs/scan-pipeline-audit.md § 8 for the rollout procedure.
51 + scp $SCP_OPTS $DEPLOY_DIR/setup-clamav.sh $DEPLOY_DIR/setup-yara-rules.sh $SERVER:$REMOTE_DIR/deploy/
52 + ssh $SSH_OPTS $SERVER "chmod +x $REMOTE_DIR/deploy/setup-firewall.sh $REMOTE_DIR/deploy/setup-git-ssh.sh $REMOTE_DIR/deploy/setup-ssh-keys.sh $REMOTE_DIR/deploy/setup-clamav.sh $REMOTE_DIR/deploy/setup-yara-rules.sh 2>/dev/null || true"
50 53
51 54 # Minify CSS for production (restore source on exit)
52 55 echo "[config] Minifying CSS..."
@@ -0,0 +1,132 @@
1 + #!/bin/bash
2 + # Set up ClamAV daemon on the Makenotwork production host (Ubuntu 24.04).
3 + #
4 + # Idempotent: safe to re-run. Installs `clamav-daemon` and `clamav-freshclam`,
5 + # configures clamd to listen on a Unix socket the makenotwork service can
6 + # reach, enables both as systemd services, and waits for the first signature
7 + # update to complete.
8 + #
9 + # After this script finishes successfully, add the following to
10 + # /opt/makenotwork/.env (and restart makenotwork):
11 + #
12 + # CLAMAV_SOCKET=/var/run/clamav/clamd.ctl
13 + #
14 + # That env var trips the `clamav` layer from Skip to live. The dashboard's
15 + # Pipeline Health card for `clamav` will flip from "down — last clean: never"
16 + # to "ok" after the first upload completes through the worker.
17 + #
18 + # References:
19 + # - Ubuntu clamav package: https://packages.ubuntu.com/noble/clamav-daemon
20 + # - INSTREAM protocol docs: clamdscan(1), clamd.conf(5)
21 +
22 + set -euo pipefail
23 +
24 + SOCKET_PATH="/var/run/clamav/clamd.ctl"
25 + CLAMD_CONF="/etc/clamav/clamd.conf"
26 +
27 + require_root() {
28 + if [ "$(id -u)" -ne 0 ]; then
29 + echo "[clamav] error: must run as root" >&2
30 + exit 1
31 + fi
32 + }
33 +
34 + install_packages() {
35 + if dpkg -l | grep -q "^ii clamav-daemon "; then
36 + echo "[clamav] clamav-daemon already installed"
37 + return
38 + fi
39 + echo "[clamav] installing clamav-daemon + clamav-freshclam..."
40 + DEBIAN_FRONTEND=noninteractive apt-get update -qq
41 + DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
42 + clamav-daemon clamav-freshclam
43 + }
44 +
45 + configure_socket() {
46 + # Confirm the default LocalSocket directive matches our expectation. If
47 + # the operator (or a future package update) changes it, surface that
48 + # rather than silently writing a path that disagrees with the env var.
49 + local existing
50 + existing=$(grep -E "^LocalSocket\s" "$CLAMD_CONF" | awk '{print $2}' || true)
51 + if [ -n "$existing" ] && [ "$existing" != "$SOCKET_PATH" ]; then
52 + echo "[clamav] error: $CLAMD_CONF has LocalSocket=$existing, expected $SOCKET_PATH" >&2
53 + echo "[clamav] edit clamd.conf and re-run, or update SOCKET_PATH in this script." >&2
54 + exit 1
55 + fi
56 + if [ -z "$existing" ]; then
57 + echo "[clamav] adding LocalSocket directive to $CLAMD_CONF..."
58 + echo "" >> "$CLAMD_CONF"
59 + echo "LocalSocket $SOCKET_PATH" >> "$CLAMD_CONF"
60 + fi
61 + }
62 +
63 + wait_for_initial_signatures() {
64 + # freshclam pulls the initial signature DB on first boot. Until that's
65 + # done, clamd will refuse to start. Poll up to 5 minutes.
66 + echo "[clamav] waiting for freshclam to fetch initial signatures..."
67 + local deadline=$(( $(date +%s) + 300 ))
68 + while [ $(date +%s) -lt $deadline ]; do
69 + if [ -f /var/lib/clamav/daily.cvd ] || [ -f /var/lib/clamav/daily.cld ]; then
70 + echo "[clamav] signatures present"
71 + return
72 + fi
73 + sleep 5
74 + done
75 + echo "[clamav] warn: signatures not present after 5 min; check 'journalctl -u clamav-freshclam'" >&2
76 + }
77 +
78 + enable_services() {
79 + systemctl enable --now clamav-freshclam
80 + # Reload clamd config (idempotent) and start the daemon.
81 + systemctl restart clamav-daemon
82 + systemctl enable clamav-daemon
83 + }
84 +
85 + verify_socket() {
86 + echo "[clamav] verifying socket reachability..."
87 + local deadline=$(( $(date +%s) + 60 ))
88 + while [ $(date +%s) -lt $deadline ]; do
89 + if [ -S "$SOCKET_PATH" ]; then
90 + if echo "PING" | timeout 5 socat - "UNIX-CONNECT:$SOCKET_PATH" 2>/dev/null | grep -q PONG; then
91 + echo "[clamav] OK: clamd responds at $SOCKET_PATH"
92 + return
93 + fi
94 + fi
95 + sleep 2
96 + done
97 + echo "[clamav] warn: clamd not responding at $SOCKET_PATH within 60s" >&2
98 + echo "[clamav] check 'journalctl -u clamav-daemon' and 'systemctl status clamav-daemon'" >&2
99 + exit 1
100 + }
101 +
102 + ensure_socat() {
103 + if ! command -v socat >/dev/null 2>&1; then
104 + DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends socat
105 + fi
106 + }
107 +
108 + main() {
109 + require_root
110 + install_packages
111 + configure_socket
112 + wait_for_initial_signatures
113 + enable_services
114 + ensure_socat
115 + verify_socket
116 +
117 + cat <<EOF
118 +
119 + [clamav] setup complete.
120 +
121 + Next step — set the env var so makenotwork uses this clamd:
122 +
123 + echo 'CLAMAV_SOCKET=$SOCKET_PATH' >> /opt/makenotwork/.env
124 + systemctl restart makenotwork
125 +
126 + The Pipeline Health card for 'clamav' will flip from down to ok after the
127 + first upload passes through the scan worker.
128 +
129 + EOF
130 + }
131 +
132 + main "$@"
@@ -0,0 +1,134 @@
1 + #!/bin/bash
2 + # Set up Florian Roth's `signature-base` YARA ruleset on the Makenotwork
3 + # production host. Idempotent: safe to re-run; pulls upstream changes if
4 + # the repo's already cloned.
5 + #
6 + # After this script finishes, add the following to /opt/makenotwork/.env
7 + # (and restart makenotwork):
8 + #
9 + # YARA_RULES_DIR=/opt/makenotwork/yara-rules
10 + #
11 + # The `yara` layer compiles every `.yar` file in that directory at server
12 + # startup. The Pipeline Health card flips from "down — No YARA rules loaded"
13 + # to "ok" after the first upload completes.
14 + #
15 + # Periodic refresh: install the cron entry at the bottom of this script so
16 + # upstream rule updates land automatically. Re-running the cron does NOT
17 + # restart makenotwork — new rules take effect on next startup. Run a manual
18 + # `systemctl restart makenotwork` after a significant ruleset change.
19 + #
20 + # References:
21 + # - https://github.com/Neo23x0/signature-base
22 + # - https://github.com/Neo23x0/signature-base/blob/master/LICENSE (CC-BY-NC 4.0)
23 + #
24 + # License note: signature-base is CC-BY-NC 4.0 (non-commercial). We use it
25 + # server-side to protect creators on the platform; this is the same usage
26 + # profile the upstream contributors document for SIEM/IR deployments.
27 +
28 + set -euo pipefail
29 +
30 + RULES_REPO="https://github.com/Neo23x0/signature-base.git"
31 + WORK_DIR="/opt/makenotwork/yara-rules-src"
32 + LINK_DIR="/opt/makenotwork/yara-rules"
33 + CRON_FILE="/etc/cron.d/mnw-yara-rules"
34 +
35 + require_root() {
36 + if [ "$(id -u)" -ne 0 ]; then
37 + echo "[yara] error: must run as root" >&2
38 + exit 1
39 + fi
40 + }
41 +
42 + ensure_git() {
43 + if ! command -v git >/dev/null 2>&1; then
44 + DEBIAN_FRONTEND=noninteractive apt-get update -qq
45 + DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends git
46 + fi
47 + }
48 +
49 + pull_or_clone() {
50 + if [ -d "$WORK_DIR/.git" ]; then
51 + echo "[yara] updating $WORK_DIR..."
52 + git -C "$WORK_DIR" fetch --depth=1 origin master
53 + git -C "$WORK_DIR" reset --hard origin/master
54 + else
55 + echo "[yara] cloning $RULES_REPO -> $WORK_DIR..."
56 + git clone --depth=1 "$RULES_REPO" "$WORK_DIR"
57 + fi
58 + }
59 +
60 + assemble_link_dir() {
61 + # The repo lays rules out under yara/, but also ships a few support
62 + # files we want to skip (LICENSE, .md, etc.). Assemble a flat dir of
63 + # .yar files so the yara-x compiler walks it cleanly.
64 + echo "[yara] assembling $LINK_DIR..."
65 + rm -rf "$LINK_DIR"
66 + mkdir -p "$LINK_DIR"
67 + # Copy every .yar / .yara, preserving filenames. find -print0 + cp is
68 + # the safe pattern (handles spaces, though signature-base avoids them).
69 + find "$WORK_DIR/yara" -type f \( -name '*.yar' -o -name '*.yara' \) -print0 \
70 + | xargs -0 -I {} cp {} "$LINK_DIR/"
71 + local count
72 + count=$(ls -1 "$LINK_DIR" | wc -l)
73 + echo "[yara] $count rule files staged"
74 + }
75 +
76 + write_ruleset_version() {
77 + # Stamp the commit SHA so we can correlate scan_layers details back to
78 + # a specific rule revision when a creator asks "why did this flag?"
79 + local sha
80 + sha=$(git -C "$WORK_DIR" rev-parse --short HEAD)
81 + local date
82 + date=$(git -C "$WORK_DIR" log -1 --format=%cI)
83 + cat > "$LINK_DIR/RULESET_VERSION" <<EOF
84 + repo=$RULES_REPO
85 + commit=$sha
86 + date=$date
87 + synced_at=$(date -u +%Y-%m-%dT%H:%M:%SZ)
88 + EOF
89 + echo "[yara] ruleset commit $sha ($date)"
90 + }
91 +
92 + install_cron() {
93 + cat > "$CRON_FILE" <<EOF
94 + # Pull upstream YARA ruleset updates weekly. Does not restart makenotwork —
95 + # the compiled rules in memory keep working until the next service restart.
96 + SHELL=/bin/bash
97 + PATH=/usr/sbin:/usr/bin:/sbin:/bin
98 + 30 4 * * 1 root /opt/makenotwork/deploy/setup-yara-rules.sh >> /var/log/mnw-yara-update.log 2>&1
99 + EOF
100 + chmod 644 "$CRON_FILE"
101 + echo "[yara] cron installed at $CRON_FILE (weekly, Mondays 04:30 UTC)"
102 + }
103 +
104 + main() {
105 + require_root
106 + ensure_git
107 + pull_or_clone
108 + assemble_link_dir
109 + write_ruleset_version
110 +
111 + # Only install the cron on the first setup run (re-running idempotently
112 + # rewrites it, which is also fine but noisy).
113 + if [ ! -f "$CRON_FILE" ]; then
114 + install_cron
115 + fi
116 +
117 + cat <<EOF
118 +
119 + [yara] setup complete. Ruleset at $LINK_DIR.
120 +
121 + Next step — set the env var so makenotwork uses these rules:
122 +
123 + echo 'YARA_RULES_DIR=$LINK_DIR' >> /opt/makenotwork/.env
124 + systemctl restart makenotwork
125 +
126 + The Pipeline Health card for 'yara' will flip from down to ok after the
127 + first upload passes through the scan worker. Subsequent ruleset bumps land
128 + via the weekly cron; a manual 'systemctl restart makenotwork' picks up the
129 + newly-compiled rules.
130 +
131 + EOF
132 + }
133 +
134 + main "$@"