Files
aur-shield/install-client.sh
T

368 lines
12 KiB
Bash

#!/usr/bin/env bash
# AUR-Shield Client Installer
# Configures pacman to use the AUR-Shield repo
set -euo pipefail
SHIELD_HOST="${AUR_SHIELD_HOST:-10.90.9.102}"
SHIELD_PORT="${AUR_SHIELD_PORT:-8443}"
# Load config from /etc/safe-yay.conf if it exists (overrides defaults)
if [ -f /etc/safe-yay.conf ]; then
source /etc/safe-yay.conf
fi
echo "╔══════════════════════════════════════════════╗"
echo "║ AUR-Shield Client Installer ║"
echo "╚══════════════════════════════════════════════╝"
echo ""
if ! command -v pacman &>/dev/null; then
echo "✗ This script requires Arch Linux (pacman)."
exit 1
fi
echo "Server: http://$SHIELD_HOST:$SHIELD_PORT"
echo ""
# Write config file
echo "Writing /etc/safe-yay.conf..."
sudo tee /etc/safe-yay.conf > /dev/null << EOFCONF
# AUR-Shield client configuration
SHIELD_HOST="$SHIELD_HOST"
SHIELD_PORT="$SHIELD_PORT"
EOFCONF
echo "✓ Config written to /etc/safe-yay.conf"
# Add pacman repo
PACMAN_CONF="/etc/pacman.conf"
REPO_ENTRY="[aur-shield]
Server = http://$SHIELD_HOST:$SHIELD_PORT/repo
SigLevel = Never"
if grep -q "^\[aur-shield\]" "$PACMAN_CONF"; then
echo "⚠ [aur-shield] already exists — updating server URL..."
sudo sed -i "/^\[aur-shield\]/,/^\[/ s|Server = .*|Server = http://$SHIELD_HOST:$SHIELD_PORT/repo|" "$PACMAN_CONF"
echo "✓ Updated repo server URL"
else
echo "Adding [aur-shield] to $PACMAN_CONF..."
sudo tee -a "$PACMAN_CONF" > /dev/null << EOF
# AUR-Shield — AI-scanned AUR packages
$REPO_ENTRY
EOF
echo "✓ Added repo entry"
fi
# Install safe-yay wrapper
WRAPPER_PATH="/usr/local/bin/safe-yay"
echo "Installing safe-yay wrapper to $WRAPPER_PATH..."
sudo tee "$WRAPPER_PATH" > /dev/null << 'WRAPPER'
#!/usr/bin/env bash
# safe-yay — wrapper that routes AUR packages through AUR-Shield
set -euo pipefail
SHIELD_HOST="${AUR_SHIELD_HOST:-10.90.9.102}"
SHIELD_PORT="${AUR_SHIELD_PORT:-8443}"
# Load config from /etc/safe-yay.conf if it exists (overrides defaults)
if [ -f /etc/safe-yay.conf ]; then
source /etc/safe-yay.conf
fi
# Exit codes (archcanary-compatible)
# 0 = clean, 1 = warning, 2 = malicious/blocked
if [ $# -eq 0 ]; then
echo "Usage: safe-yay <package> [package2 ...]"
echo " safe-yay -Syu # System-Update mit AUR-Scan"
echo " safe-yay --scan-only <pkg> # Scan without building"
echo " safe-yay --check-installed # Installierte AUR-Pakete pruefen"
echo " safe-yay --doctor # Check AUR-Shield setup"
exit 0
fi
# --doctor health check
if [ "$1" = "--doctor" ]; then
echo "AUR-Shield Doctor"
echo "================"
echo ""
STATUS=$(curl -sf "http://$SHIELD_HOST:$SHIELD_PORT/api/status" 2>/dev/null)
if [ $? -eq 0 ]; then
MODEL=$(echo "$STATUS" | python3 -c "import sys,json; print(json.loads(sys.stdin.read()).get('model','?'))" 2>/dev/null)
CACHE=$(echo "$STATUS" | python3 -c "import sys,json; print(json.loads(sys.stdin.read()).get('cached_scans',0))" 2>/dev/null)
echo " ✓ Server: http://$SHIELD_HOST:$SHIELD_PORT"
echo " Model: $MODEL"
echo " Cached scans: $CACHE"
else
echo " ✗ Server: http://$SHIELD_HOST:$SHIELD_PORT NOT REACHABLE"
exit 1
fi
OLLAMA=$(curl -sf "http://$SHIELD_HOST:11434/api/tags" 2>/dev/null)
if [ $? -eq 0 ]; then
echo " ✓ Ollama: running"
else
echo " ⚠ Ollama: not reachable on $SHIELD_HOST"
fi
if grep -q "^\[aur-shield\]" /etc/pacman.conf 2>/dev/null; then
echo " ✓ pacman repo: [aur-shield] configured"
else
echo " ⚠ pacman repo: [aur-shield] NOT in /etc/pacman.conf"
fi
echo ""
echo "All checks passed."
exit 0
fi
# --check-installed: pruefe bereits installierte AUR-Pakete gegen IOC-Listen
if [ "$1" = "--check-installed" ]; then
echo "AUR-Shield: Installierte AUR-Pakete pruefen"
echo "=========================================="
echo ""
# Foreign packages = AUR + manuell installierte
FOREIGN=$(pacman -Qmq 2>/dev/null)
if [ -z "$FOREIGN" ]; then
echo "Keine AUR-Pakete installiert."
exit 0
fi
COUNT=$(echo "$FOREIGN" | wc -l)
echo "Pruefe $COUNT installierte AUR-Pakete gegen IOC-Listen..."
echo ""
MALICIOUS=0
SUSPICIOUS=0
CLEAN=0
for pkg in $FOREIGN; do
RESPONSE=$(curl -sf "http://$SHIELD_HOST:$SHIELD_PORT/api/scan/$pkg" 2>/dev/null)
if [ $? -ne 0 ]; then
continue
fi
VERDICT=$(echo "$RESPONSE" | python3 -c "import sys,json; print(json.loads(sys.stdin.read()).get('verdict','error'))" 2>/dev/null)
case "$VERDICT" in
malicious)
echo " ✗ MALICIOUS: $pkg"
echo "$RESPONSE" | python3 -c "
import sys,json
d = json.loads(sys.stdin.read())
for ioc in d.get('ioc_matches',[]):
print(f\" IOC: {ioc['source']} — {ioc['description']}\")
" 2>/dev/null
MALICIOUS=$((MALICIOUS + 1))
;;
suspicious)
echo " ⚠ SUSPICIOUS: $pkg"
SUSPICIOUS=$((SUSPICIOUS + 1))
;;
clean)
CLEAN=$((CLEAN + 1))
;;
esac
done
echo ""
echo "=========================================="
echo " Ergebnis:"
echo " Clean: $CLEAN"
echo " Suspicious: $SUSPICIOUS"
echo " Malicious: $MALICIOUS"
echo "=========================================="
if [ $MALICIOUS -gt 0 ]; then
exit 2
elif [ $SUSPICIOUS -gt 0 ]; then
exit 1
fi
exit 0
fi
# -Syu: System-Update — scanne AUR-Updates bevor sie installiert werden
if [ "$1" = "-Syu" ] || [ "$1" = "-Sy" ]; then
echo "AUR-Shield: System-Update mit Pre-Scan"
echo "======================================="
echo ""
# Erst normales Repo-Update (ohne AUR)
echo "→ Repo-Updates..."
sudo pacman -Sy 2>&1
# AUR-Updates finden mit yay
if command -v yay &>/dev/null; then
echo ""
echo "→ Pruefe AUR-Updates..."
AUR_UPDATES=$(yay -Qua 2>/dev/null || true)
if [ -z "$AUR_UPDATES" ]; then
echo " Keine AUR-Updates."
else
echo " AUR-Updates gefunden:"
echo "$AUR_UPDATES"
echo ""
echo "→ Scanne AUR-Updates durch AUR-Shield..."
BLOCKED=0
SAFE_UPDATES=""
while IFS= read -r line; do
pkg=$(echo "$line" | awk '{print $1}')
pkg=$(echo "$pkg" | sed 's/[+]$//') # strip + suffix from yay output
RESPONSE=$(curl -sf "http://$SHIELD_HOST:$SHIELD_PORT/api/scan/$pkg" 2>/dev/null)
if [ $? -ne 0 ]; then
echo " ⚠ $pkg: Scan fehlgeschlagen — ueberspringe"
SAFE_UPDATES="$SAFE_UPDATES $pkg"
continue
fi
VERDICT=$(echo "$RESPONSE" | python3 -c "import sys,json; print(json.loads(sys.stdin.read()).get('verdict','error'))" 2>/dev/null)
case "$VERDICT" in
clean)
echo " ✓ $pkg: clean"
SAFE_UPDATES="$SAFE_UPDATES $pkg"
;;
suspicious)
echo " ⚠ $pkg: SUSPICIOUS"
echo "$RESPONSE" | python3 -c "
import sys,json
d = json.loads(sys.stdin.read())
for f in d.get('findings',[])[:3]:
print(f\" {f}\")
" 2>/dev/null
read -rp " Update $pkg trotzdem? [y/N] " FORCE
if [[ "${FORCE,,}" == "y" ]]; then
SAFE_UPDATES="$SAFE_UPDATES $pkg"
fi
;;
malicious)
echo " ✗ $pkg: MALICIOUS — BLOCKIERT!"
echo "$RESPONSE" | python3 -c "
import sys,json
d = json.loads(sys.stdin.read())
for ioc in d.get('ioc_matches',[]):
print(f\" IOC: {ioc['source']} — {ioc['description']}\")
" 2>/dev/null
BLOCKED=$((BLOCKED + 1))
;;
esac
done <<< "$AUR_UPDATES"
echo ""
if [ $BLOCKED -gt 0 ]; then
echo "⚠ $BLOCKED Paket(e) blockiert!"
fi
# Safe updates installieren
if [ -n "$SAFE_UPDATES" ]; then
echo "→ Installiere sichere AUR-Updates: $SAFE_UPDATES"
yay -S $SAFE_UPDATES
else
echo "→ Keine sicheren AUR-Updates."
fi
fi
else
echo " ⚠ yay nicht installiert — AUR-Updates manuell pruefen"
fi
# Normale Repo-Updates installieren
echo ""
echo "→ Repo-Updates installieren..."
sudo pacman -Su 2>&1
echo ""
echo "System-Update abgeschlossen."
exit 0
fi
# --scan-only mode
SCAN_ONLY=false
if [ "$1" = "--scan-only" ]; then
SCAN_ONLY=true
shift
fi
EXIT_CODE=0
for pkg in "$@"; do
echo "→ Processing $pkg..."
if [ "$SCAN_ONLY" = true ]; then
RESPONSE=$(curl -sf "http://$SHIELD_HOST:$SHIELD_PORT/api/scan/$pkg" 2>&1) || {
echo " ✗ Failed to scan $pkg"
echo " $RESPONSE"
EXIT_CODE=1
continue
}
else
RESPONSE=$(curl -sf "http://$SHIELD_HOST:$SHIELD_PORT/api/build/$pkg" 2>&1) || {
echo " ✗ Failed to process $pkg"
echo " $RESPONSE"
EXIT_CODE=1
continue
}
fi
VERDICT=$(echo "$RESPONSE" | python3 -c "import sys,json; print(json.loads(sys.stdin.read()).get('verdict','error'))" 2>/dev/null)
case "$VERDICT" in
clean)
echo " ✓ Clean — package verified"
if [ "$SCAN_ONLY" = false ]; then
echo " Installing via pacman..."
sudo pacman -Sy "aur-shield/$pkg" || {
echo " ⚠ Package not in repo yet — may need to wait for build"
EXIT_CODE=1
}
fi
;;
suspicious)
echo " ⚠ Suspicious — review recommended"
echo " Report: http://$SHIELD_HOST:$SHIELD_PORT/api/report/$pkg"
EXIT_CODE=1
if [ "$SCAN_ONLY" = false ]; then
read -rp " Install anyway? [y/N] " FORCE
if [[ "${FORCE,,}" == "y" ]]; then
sudo pacman -Sy "aur-shield/$pkg"
fi
fi
;;
malicious)
echo " ✗ MALICIOUS — package blocked!"
echo " Report: http://$SHIELD_HOST:$SHIELD_PORT/api/report/$pkg"
echo "$RESPONSE" | python3 -c "
import sys,json
d = json.loads(sys.stdin.read())
for ioc in d.get('ioc_matches',[]):
print(f\" IOC: {ioc['source']} — {ioc['description']}\")
" 2>/dev/null
EXIT_CODE=2
;;
*)
echo " ? Error: $RESPONSE"
EXIT_CODE=1
;;
esac
done
exit $EXIT_CODE
WRAPPER
sudo chmod +x "$WRAPPER_PATH"
echo "✓ Installed safe-yay"
echo ""
echo "╔══════════════════════════════════════════════╗"
echo "║ Client setup complete! ║"
echo "╠══════════════════════════════════════════════╣"
echo "║ ║"
echo "║ Usage: ║"
echo "║ safe-yay <package> ║"
echo "║ sudo pacman -S aur-shield/<package> ║"
echo "║ ║"
echo "║ Set server via env: ║"
echo "║ export AUR_SHIELD_HOST=10.90.9.102 ║"
echo "║ export AUR_SHIELD_PORT=8443 ║"
echo "║ ║"
echo "╚══════════════════════════════════════════════╝"