max / makenotwork
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 "$@" |