Compare commits

..

8 Commits

Author SHA1 Message Date
pdmarf
f362bd3721 Revert package/ restructure, restore scripts to repo root
Moves all automated scripts back to the repo root where setup.sh
expects them. standalone/ remains for manual-run tools.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 14:28:00 +01:00
pdmarf
7585a12b6d Restructure repo into package/ and standalone/ directories
Moves automated scan scripts and setup.sh into package/.
bind-ssh-tailscale.sh remains in standalone/ as a manual-run tool.
Updates README.md setup instructions to reflect new paths.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 14:25:11 +01:00
pdmarf
50aa38712e Add bind-ssh-tailscale.sh as standalone manual-run script
Places the script in standalone/ so it is excluded from setup.sh automation.
Documents manual curl-and-run usage in README.md.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 14:22:56 +01:00
pdmarf
c5037c0ac0 Fix branch name in README-scanner.md curl command (main -> master)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 11:01:13 +01:00
pdmarf
f257fcfcb9 Set git pull.rebase false in setup.sh to prevent divergent branch errors
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-18 16:59:15 +01:00
pdmarf
d9b4592c50 Fix setup.sh to print only current run of npm sudo config audit
Use tee -a instead of redirect + cat, so only the current run's output
is shown rather than the entire accumulated daily log.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-18 16:37:32 +01:00
pdmarf
72a8f37290 Add check-npm-sudo-config docs and print audit log on setup
- README: add Scripts section explaining what check-npm-sudo-config.sh
  does, what it checks, and that it is audit-only
- setup.sh: print check-npm-sudo-config log to terminal after initial scan

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-18 16:32:34 +01:00
pdmarf
4eee88a004 Add file logging to check-npm-sudo-config.sh v1.0 2026-04-18 10:07:29 +01:00
5 changed files with 160 additions and 9 deletions

View File

@@ -5,7 +5,7 @@ Quick scanner for CVE-2025-66478 / CVE-2025-55182 (CVSS 10.0)
## Usage ## Usage
```bash ```bash
curl -o check-nextjs-rce.sh http://100.120.125.113:3000/pdm/security-tools/raw/branch/main/check-nextjs-rce.sh curl -o check-nextjs-rce.sh http://100.120.125.113:3000/pdm/security-tools/raw/branch/master/check-nextjs-rce.sh
chmod +x check-nextjs-rce.sh chmod +x check-nextjs-rce.sh
sudo ./check-nextjs-rce.sh sudo ./check-nextjs-rce.sh
``` ```

View File

@@ -2,6 +2,71 @@
A collection of security scripts versioned in this repository. A collection of security scripts versioned in this repository.
## Scripts
### check-npm-sudo-config.sh
Audits npm configuration on a Linux VM to detect cases where npm is — or has
been — configured to install packages into system-owned directories, which
requires `sudo` and creates security risks.
Running `sudo npm install -g` can deposit files owned by root inside your npm
prefix or cache directory. This causes permission errors for non-root users,
encourages further `sudo npm` use to work around them, and means malicious
packages run with root privileges during installation.
**This script is audit-only — it makes no changes.** It reports issues and
prints recommended commands, but you must run those commands yourself.
The script checks:
1. **npm prefix** — flags if it points to `/usr` or `/usr/local` (system-wide, requires sudo)
2. **~/.npmrc** — checks whether the prefix is explicitly pinned to a user directory
3. **PATH** — confirms the npm prefix bin directory is in PATH
4. **Root-owned files in the prefix** — evidence of past `sudo npm` usage
5. **Shell history** — scans `.bash_history` / `.zsh_history` for `sudo npm` commands
6. **npm cache ownership** — root-owned cache files cause EACCES errors
7. **Node version manager** — detects nvm, fnm, or n; flags if n is present without N_PREFIX set
If issues are found, it sends a Telegram alert and logs results to `logs/`.
The correct fix is to configure npm to install global packages into a
user-owned directory (e.g. `~/.npm-global`) so that `sudo` is never needed:
```bash
npm config set prefix ~/.npm-global
export PATH="$HOME/.npm-global/bin:$PATH"
```
## Standalone Scripts
These scripts live in `standalone/` and are **not run by `setup.sh`**. They are
single-use tools intended to be copied to a target machine and run manually.
### standalone/bind-ssh-tailscale.sh
Binds SSH to the Tailscale interface only and disables password authentication.
- Requires root (`sudo bash bind-ssh-tailscale.sh`)
- Tailscale must be installed and connected before running
- Uses a drop-in config at `/etc/ssh/sshd_config.d/99-tailscale-only.conf` if
that directory exists; otherwise edits `/etc/ssh/sshd_config` directly with
an automatic backup
- Validates the config with `sshd -t` before restarting the SSH service
- Prints revert instructions on completion
**To use on a target machine:**
```bash
curl -O https://gitea.pdmarf.co.uk/pdm/security-tools/raw/branch/master/standalone/bind-ssh-tailscale.sh
# or via Tailscale:
curl -O http://100.120.125.113:3000/pdm/security-tools/raw/branch/master/standalone/bind-ssh-tailscale.sh
sudo bash bind-ssh-tailscale.sh
```
---
## Claude Code Context ## Claude Code Context
This project is maintained with Claude Code. The working directory on macOS is: This project is maintained with Claude Code. The working directory on macOS is:

View File

@@ -1,5 +1,5 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# check-npm-sudo-config.sh # check-npm-sudo-config.sh v1.0
# Audits npm configuration on this VM for sudo-related issues and recommends fixes. # Audits npm configuration on this VM for sudo-related issues and recommends fixes.
set -euo pipefail set -euo pipefail
@@ -17,6 +17,8 @@ send_telegram() {
HOSTNAME=$(hostname) HOSTNAME=$(hostname)
DATE=$(date) DATE=$(date)
LOGFILE="$SCRIPT_DIR/logs/npm-sudo-config-$(date +%Y%m%d).log"
mkdir -p "$SCRIPT_DIR/logs"
RED='\033[0;31m' RED='\033[0;31m'
YELLOW='\033[1;33m' YELLOW='\033[1;33m'
@@ -27,12 +29,12 @@ RESET='\033[0m'
ISSUES=0 ISSUES=0
WARNINGS=0 WARNINGS=0
log() { echo "$*"; } log() { echo "$*" | tee -a "$LOGFILE"; }
ok() { printf "${GREEN}${RESET} %s\n" "$*"; } ok() { log "$(printf "${GREEN}${RESET} %s" "$*")"; }
warn() { printf "${YELLOW}${RESET} %s\n" "$*"; (( WARNINGS++ )) || true; } warn() { log "$(printf "${YELLOW}${RESET} %s" "$*")"; (( WARNINGS++ )) || true; }
fail() { printf "${RED}${RESET} %s\n" "$*"; (( ISSUES++ )) || true; } fail() { log "$(printf "${RED}${RESET} %s" "$*")"; (( ISSUES++ )) || true; }
rec() { printf " ${YELLOW}${RESET} %s\n" "$*"; } rec() { log "$(printf " ${YELLOW}${RESET} %s" "$*")"; }
header() { echo ""; echo "=========================================="; echo "$*"; echo "=========================================="; } header() { log ""; log "=========================================="; log "$*"; log "=========================================="; }
log "==========================================" log "=========================================="
log " npm sudo config audit" log " npm sudo config audit"
@@ -179,6 +181,7 @@ fi
# ── Summary ─────────────────────────────────────────────────────────────────── # ── Summary ───────────────────────────────────────────────────────────────────
header "SUMMARY" header "SUMMARY"
log "Scan completed at: $(date)" log "Scan completed at: $(date)"
log "Log saved to : $LOGFILE"
log "" log ""
if [[ $ISSUES -gt 0 ]]; then if [[ $ISSUES -gt 0 ]]; then

View File

@@ -9,6 +9,10 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
echo "=== Security Tools Setup ===" echo "=== Security Tools Setup ==="
echo "" echo ""
# ── Git config ────────────────────────────────────────────────────────────────
git -C "$SCRIPT_DIR" config pull.rebase false
echo "Git pull strategy set to merge."
# ── Telegram credentials ─────────────────────────────────────────────────────── # ── Telegram credentials ───────────────────────────────────────────────────────
if [[ -f "$SCRIPT_DIR/config.sh" ]]; then if [[ -f "$SCRIPT_DIR/config.sh" ]]; then
echo "config.sh already exists — skipping credential setup." echo "config.sh already exists — skipping credential setup."
@@ -99,6 +103,10 @@ echo ""
echo "Running initial security scan..." echo "Running initial security scan..."
bash "$SCRIPT_DIR/npm-security-check.sh" >> "$SCRIPT_DIR/logs/npm-security-check-$(date +%Y%m%d).log" 2>&1 && echo "npm-security-check: done." || echo "npm-security-check: issues found — check Telegram." bash "$SCRIPT_DIR/npm-security-check.sh" >> "$SCRIPT_DIR/logs/npm-security-check-$(date +%Y%m%d).log" 2>&1 && echo "npm-security-check: done." || echo "npm-security-check: issues found — check Telegram."
bash "$SCRIPT_DIR/check-nextjs-rce.sh" >> "$SCRIPT_DIR/logs/check-nextjs-rce-$(date +%Y%m%d).log" 2>&1 && echo "check-nextjs-rce: done." || echo "check-nextjs-rce: issues found — check Telegram." bash "$SCRIPT_DIR/check-nextjs-rce.sh" >> "$SCRIPT_DIR/logs/check-nextjs-rce-$(date +%Y%m%d).log" 2>&1 && echo "check-nextjs-rce: done." || echo "check-nextjs-rce: issues found — check Telegram."
bash "$SCRIPT_DIR/check-npm-sudo-config.sh" >> "$SCRIPT_DIR/logs/check-npm-sudo-config-$(date +%Y%m%d).log" 2>&1 && echo "check-npm-sudo-config: done." || echo "check-npm-sudo-config: issues found — check Telegram." NPM_SUDO_LOG="$SCRIPT_DIR/logs/check-npm-sudo-config-$(date +%Y%m%d).log"
echo ""
echo "--- npm sudo config audit results ---"
bash "$SCRIPT_DIR/check-npm-sudo-config.sh" 2>&1 | tee -a "$NPM_SUDO_LOG"
echo "-------------------------------------"
echo "" echo ""
echo "Initial scan complete. Check Telegram for any alerts." echo "Initial scan complete. Check Telegram for any alerts."

View File

@@ -0,0 +1,75 @@
#!/usr/bin/env bash
set -euo pipefail
log() { echo "[$(date +'%Y-%m-%d %H:%M:%S')] $1"; }
if [ "$EUID" -ne 0 ]; then
log "ERROR: Please run as root"
exit 1
fi
# Check Tailscale is installed and connected
if ! command -v tailscale &>/dev/null; then
log "ERROR: Tailscale is not installed"
exit 1
fi
TAILSCALE_IP=$(tailscale ip -4 2>/dev/null)
if [ -z "$TAILSCALE_IP" ]; then
log "ERROR: Could not get Tailscale IP — is Tailscale connected?"
exit 1
fi
log "Tailscale IP: ${TAILSCALE_IP}"
# Detect SSH service name
if systemctl is-active --quiet ssh 2>/dev/null; then
SSH_SERVICE="ssh"
elif systemctl is-active --quiet sshd 2>/dev/null; then
SSH_SERVICE="sshd"
else
log "ERROR: No running SSH service found (tried ssh, sshd)"
exit 1
fi
# Use a drop-in file if sshd_config.d exists, otherwise edit sshd_config directly
if [ -d /etc/ssh/sshd_config.d ]; then
DROPIN="/etc/ssh/sshd_config.d/99-tailscale-only.conf"
[ -f "$DROPIN" ] && log "WARNING: ${DROPIN} already exists — overwriting"
cat > "$DROPIN" << EOF
# Bind SSH to Tailscale interface only
# To revert: rm ${DROPIN} && systemctl restart ${SSH_SERVICE}
ListenAddress ${TAILSCALE_IP}
PasswordAuthentication no
PermitEmptyPasswords no
EOF
log "Written: ${DROPIN}"
else
BACKUP="/etc/ssh/sshd_config.bak.$(date +%Y%m%d_%H%M%S)"
cp /etc/ssh/sshd_config "$BACKUP"
log "Backed up sshd_config to ${BACKUP}"
# Remove any existing ListenAddress lines and add ours
sed -i '/^[#]*ListenAddress/d' /etc/ssh/sshd_config
echo "ListenAddress ${TAILSCALE_IP}" >> /etc/ssh/sshd_config
log "Updated /etc/ssh/sshd_config"
fi
# Validate config before restarting
if ! sshd -t; then
log "ERROR: SSH config validation failed — aborting without restart"
[ -n "${DROPIN:-}" ] && rm -f "$DROPIN"
exit 1
fi
log "SSH config validated OK"
systemctl restart "$SSH_SERVICE"
log "SSH service restarted"
log ""
log "Done. SSH is now bound to Tailscale only (${TAILSCALE_IP})"
log "Connect with: ssh root@${TAILSCALE_IP}"
if [ -d /etc/ssh/sshd_config.d ]; then
log "To revert: rm ${DROPIN} && systemctl restart ${SSH_SERVICE}"
else
log "To revert: cp ${BACKUP} /etc/ssh/sshd_config && systemctl restart ${SSH_SERVICE}"
fi