Complete hardening workflow now covers all key technical concepts: - AppArmor/SELinux status, systemd sandboxing audit - Semgrep SAST, YARA malware patterns, Nuclei network scan - Cosign artifact verification, SHA256 package verification - Falco/eBPF runtime monitoring, Volatility 3 memory forensics - OIDC/GAT token best practices - Ansible integration per .clinerules/workstation.md Co-authored-by: Junie <junie@jetbrains.com>
495 lines
14 KiB
Markdown
495 lines
14 KiB
Markdown
---
|
|
description: >-
|
|
Arch Linux workstation hardening workflow with daily, weekly, and monthly
|
|
security checks based on March 2026 supply chain threat landscape.
|
|
tags:
|
|
- arch-linux
|
|
- hardening
|
|
- security
|
|
- supply-chain
|
|
- workflow
|
|
last_updated: '2026-03-24'
|
|
priority: 8
|
|
---
|
|
|
|
# Workstation Hardening Workflow (Arch Linux)
|
|
|
|
**Source**: `docs/tech/supply-chain-security-2026-03.md`
|
|
|
|
## Schedule Overview
|
|
|
|
| Cadence | Duration | Focus |
|
|
|---------|----------|-------|
|
|
| Daily | 5 min | Service health, network, recent changes |
|
|
| Weekly | 15 min | Package audit, SBOM scan, journal review |
|
|
| Monthly | 30 min | IOC sweep, persistence audit, credential rotation |
|
|
|
|
---
|
|
|
|
## Daily Checks (5 min)
|
|
|
|
### 1. Systemd Service Health
|
|
|
|
```bash
|
|
# Failed services
|
|
systemctl --failed
|
|
|
|
# Unexpected user services (CanisterWorm pgmon detection)
|
|
systemctl --user list-units --type=service --all
|
|
```
|
|
|
|
**Alert if**: Unknown services present, especially short names like `pgmon`.
|
|
|
|
### 2. Network Connections
|
|
|
|
```bash
|
|
# Established outbound connections
|
|
ss -ntu state established
|
|
|
|
# TeamPCP C2 check
|
|
ss -ntu | grep -E "45.148.10.212"
|
|
|
|
# ICP C2 check (unexpected Dfinity connections)
|
|
ss -ntu | grep -E "icp0\.io|dfinity"
|
|
```
|
|
|
|
**Alert if**: Connections to unknown external IPs, especially C2 indicators.
|
|
|
|
### 3. Boot Performance & Service Timing
|
|
|
|
```bash
|
|
# Boot time
|
|
systemd-analyze
|
|
|
|
# Slowest services (flag if >1s unexpected)
|
|
systemd-analyze blame | head -10
|
|
|
|
# Critical chain
|
|
systemd-analyze critical-chain
|
|
```
|
|
|
|
**Alert if**: New services in critical chain, unexpected boot time increase.
|
|
|
|
### 4. Recent Package Changes
|
|
|
|
```bash
|
|
# Packages changed in last 24h
|
|
pacman -Q --explicit | while read pkg ver; do
|
|
stat -c '%Y %n' /var/lib/pacman/local/$pkg-* 2>/dev/null
|
|
done | awk '$1 > '$(date -d 'yesterday' +%s)' {print}'
|
|
```
|
|
|
|
**Alert if**: Unexpected package installations or updates.
|
|
|
|
---
|
|
|
|
## Weekly Audit (15 min)
|
|
|
|
### 1. Package Orphan Cleanup
|
|
|
|
```bash
|
|
# List orphans (no longer required)
|
|
pacman -Qdt
|
|
|
|
# Remove orphans (review first)
|
|
pacman -Rns $(pacman -Qdtq)
|
|
```
|
|
|
|
### 2. npm/pnpm Audit
|
|
|
|
```bash
|
|
# Verify scripts disabled
|
|
npm config get ignore-scripts # expect: true
|
|
|
|
# Audit all active projects
|
|
fd -t f -e json -p 'package-lock' ~/internal ~/external 2>/dev/null | while read lock; do
|
|
dir=$(dirname "$lock")
|
|
echo "=== $dir ==="
|
|
(cd "$dir" && pnpm audit 2>&1 | head -20)
|
|
done
|
|
```
|
|
|
|
**Alert if**: `ignore-scripts` is not `true`, high/critical vulnerabilities.
|
|
|
|
### 3. SBOM Vulnerability Scan
|
|
|
|
```bash
|
|
# Generate and scan SBOM for active projects
|
|
fd -t d -p 'node_modules' ~/internal ~/external 2>/dev/null | while read dir; do
|
|
project=$(dirname "$dir")
|
|
echo "=== $project ==="
|
|
syft packages dir:"$project" -o cyclonedx-json 2>/dev/null | grype sbom:/dev/stdin --fail-on high 2>&1 | tail -5
|
|
done
|
|
```
|
|
|
|
**Alert if**: High/critical CVEs in dependencies.
|
|
|
|
### 4. Semgrep SAST Scan
|
|
|
|
```bash
|
|
# Scan all repos for supply chain rules
|
|
fd -t d -p '.git' ~/internal ~/external 2>/dev/null | while read gitdir; do
|
|
repo=$(dirname "$gitdir")
|
|
echo "=== $repo ==="
|
|
(cd "$repo" && semgrep --config p/supply-chain --config p/javascript --error 2>&1 | tail -5)
|
|
done
|
|
```
|
|
|
|
**Alert if**: Findings in `eval`, `child_process`, `Buffer.from`, prototype pollution.
|
|
|
|
### 5. SHA256 Package Verification
|
|
|
|
```bash
|
|
# Verify pacman package integrity against known-good hashes
|
|
pacman -Qk 2>&1 | grep -v "0 modified files"
|
|
|
|
# Check for package downgrade attacks
|
|
pacman -Qi linux | grep -E "Install Date|Version"
|
|
```
|
|
|
|
**Alert if**: Modified files in package, unexpected version downgrade.
|
|
|
|
### 6. Journal Suspicious Activity
|
|
|
|
```bash
|
|
# Last 7 days: failed auth, suspicious exec, ptrace
|
|
journalctl --since "7 days ago" -p err --no-pager | grep -iE "denied|ptrace|segfault|oom"
|
|
|
|
# Suspicious process starts from non-standard paths
|
|
journalctl --since "7 days ago" --no-pager | grep -E "ExecStart.*\.local" | grep -v "systemd"
|
|
```
|
|
|
|
**Alert if**: Repeated auth failures, ptrace from unexpected processes.
|
|
|
|
---
|
|
|
|
## Monthly Deep Scan (30 min)
|
|
|
|
### 1. Unicode Injection Scan (GlassWorm)
|
|
|
|
```bash
|
|
# Scan all code repos for invisible Unicode
|
|
fd -t f -e js -e ts -e mjs -e py ~/internal ~/external 2>/dev/null | while read f; do
|
|
# Variation Selectors (U+FE00-FE0F)
|
|
grep -Pl "[\x{FE00}-\x{FE0F}]" "$f" 2>/dev/null && echo "VS_RANGE: $f"
|
|
# Ideographic Variation Selectors (U+E0100-E01EF)
|
|
grep -Pl "[\x{E0100}-\x{E01EF}]" "$f" 2>/dev/null && echo "IVS_RANGE: $f"
|
|
# Decoder pattern
|
|
grep -Pl "codePointAt.*0xFE00|codePointAt.*0xE0100|eval\(Buffer\.from" "$f" 2>/dev/null && echo "DECODER: $f"
|
|
done
|
|
```
|
|
|
|
**Alert if**: Any matches = immediate investigation, likely compromise.
|
|
|
|
### 2. Systemd Persistence Audit (CanisterWorm)
|
|
|
|
```bash
|
|
# List all user service files
|
|
ls -la ~/.config/systemd/user/
|
|
|
|
# Check for suspicious ExecStart paths (not /usr/bin or /usr/local)
|
|
grep -r "ExecStart" ~/.config/systemd/user/ 2>/dev/null | grep -v "/usr/bin\|/usr/local/bin"
|
|
|
|
# Check for worm artifacts
|
|
find ~ -maxdepth 3 -name "pgmon*" -o -name "uv-proxy" -o -name "pglog" 2>/dev/null
|
|
```
|
|
|
|
**Alert if**: Unknown services, ExecStart pointing to dotfiles or temp dirs.
|
|
|
|
### 3. IOC Sweep
|
|
|
|
```bash
|
|
# TeamPCP exfil repos
|
|
gh repo list --limit 100 --json name 2>/dev/null | jq -r '.[].name' | grep -i "tpcp-docs"
|
|
|
|
# DNS C2 resolution check
|
|
getent hosts scan.aquasecurtiy.org 2>/dev/null
|
|
|
|
# Known malicious extension check
|
|
code --list-extensions 2>/dev/null | grep -i "quartz.quartz"
|
|
```
|
|
|
|
**Alert if**: Any IOC matches = incident response protocol.
|
|
|
|
### 4. Credential Rotation Check
|
|
|
|
```bash
|
|
# npm tokens older than 90 days
|
|
npm token list 2>/dev/null
|
|
|
|
# GitHub tokens - check for long-lived PATs
|
|
gh auth status 2>/dev/null
|
|
|
|
# SSH keys - check for keys without passphrase
|
|
ssh-add -l 2>/dev/null
|
|
```
|
|
|
|
**Action**: Rotate any token older than 90 days. Enforce GAT scoping.
|
|
|
|
### OIDC & Token Best Practices
|
|
|
|
| Practice | Implementation |
|
|
|----------|---------------|
|
|
| **OIDC over PAT** | Use `gh auth login` with browser flow, not personal access tokens |
|
|
| **GAT scoping** | `gh auth refresh -s repo,read:packages` (minimum scopes) |
|
|
| **npm OIDC** | Configure `npm-provenance` in CI, avoid `NPM_TOKEN` env vars |
|
|
| **Short-lived tokens** | CI tokens max 1 hour TTL, no long-lived secrets in repos |
|
|
| **Secret scanning** | Enable GitHub secret scanning + push protection |
|
|
|
|
### 5. YARA Malware Pattern Scan
|
|
|
|
```bash
|
|
# Scan home directories with YARA rules (see source doc for rule files)
|
|
yara -r /path/to/rules/malware.yar ~/internal ~/external 2>/dev/null
|
|
|
|
# Quick inline check for known worm patterns
|
|
grep -rl "pgmon\|uv-proxy\|pglog" ~/internal ~/external 2>/dev/null
|
|
grep -rl "codePointAt.*0xFE00\|0xE0100" ~/internal ~/external 2>/dev/null
|
|
```
|
|
|
|
**Alert if**: Any YARA rule match = incident response protocol.
|
|
|
|
### 6. Cosign Artifact Verification
|
|
|
|
```bash
|
|
# Verify container images before use (example)
|
|
cosign verify --key cosign.pub ghcr.io/org/image:tag 2>/dev/null
|
|
|
|
# Verify with OIDC keyless
|
|
cosign verify --certificate-identity=*.satware.ai --certificate-oidc-issuer=https://token.actions.githubusercontent.com ghcr.io/org/image:tag 2>/dev/null
|
|
```
|
|
|
|
**Alert if**: Signature verification fails, do not use the artifact.
|
|
|
|
### 7. Nuclei Network Template Scan
|
|
|
|
```bash
|
|
# Scan local services for known vulnerabilities
|
|
nuclei -u http://localhost -t cves/ -severity high,critical -silent 2>/dev/null
|
|
|
|
# Scan specific ports
|
|
nuclei -u http://localhost:3000 -t exposures/ -silent 2>/dev/null
|
|
```
|
|
|
|
**Alert if**: High/critical template matches on local services.
|
|
|
|
### 8. Full SBOM Regenerate
|
|
|
|
```bash
|
|
# Regenerate SBOMs for all active projects
|
|
fd -t f -e json -p 'package-lock' ~/internal ~/external 2>/dev/null | while read lock; do
|
|
dir=$(dirname "$lock")
|
|
syft packages dir:"$dir" -o cyclonedx-json > "$dir/sbom.cyclonedx.json" 2>/dev/null
|
|
grype sbom:"$dir/sbom.cyclonedx.json" --fail-on high > "$dir/grype-report.txt" 2>&1
|
|
echo "$dir: $(grep -c 'High\|Critical' "$dir/grype-report.txt" 2>/dev/null || echo 0) findings"
|
|
done
|
|
```
|
|
|
|
---
|
|
|
|
## Kernel Hardening Check (Monthly)
|
|
|
|
```bash
|
|
# Verify kernel parameters
|
|
sysctl kernel.kptr_restrict # expect: 1 (hide kernel pointers)
|
|
sysctl kernel.dmesg_restrict # expect: 1 (restrict dmesg)
|
|
sysctl kernel.unprivileged_bpf_disabled # expect: 1 (restrict eBPF)
|
|
sysctl vm.mmap_min_addr # expect: 65536 (prevent null mapping)
|
|
|
|
# Verify ASLR
|
|
cat /proc/sys/kernel/randomize_va_space # expect: 2 (full randomization)
|
|
|
|
# Check loaded kernel modules for suspicious entries
|
|
lsmod | awk '{print $1}' | sort
|
|
|
|
# Verify IOMMU enabled (hardware memory protection)
|
|
dmesg | grep -i "DMAR\|IOMMU" | head -3
|
|
```
|
|
|
|
**Alert if**: Any parameter is 0/unset, unknown kernel modules loaded.
|
|
|
|
## AppArmor/SELinux Status Check (Monthly)
|
|
|
|
```bash
|
|
# AppArmor (Arch default)
|
|
sudo aa-status 2>/dev/null | head -20
|
|
|
|
# Verify profiles are in enforce mode (not complain)
|
|
sudo aa-status 2>/dev/null | grep -c "enforce" # should be > 0
|
|
sudo aa-status 2>/dev/null | grep "complain" # should be empty
|
|
|
|
# If using SELinux (alternative)
|
|
getenforce 2>/dev/null # expect: Enforcing
|
|
sestatus 2>/dev/null
|
|
```
|
|
|
|
**Alert if**: No profiles loaded, profiles in complain mode, SELinux permissive.
|
|
|
|
## Systemd Sandboxing Audit (Monthly)
|
|
|
|
```bash
|
|
# Check which services lack sandboxing directives
|
|
for svc in $(systemctl list-units --type=service --state=running -q --no-pager | awk '{print $1}'); do
|
|
has_sandbox=$(systemctl show "$svc" --property=ProtectSystem,ProtectHome,PrivateTmp,NoNewPrivileges,ReadOnlyPaths 2>/dev/null | grep -v "^$" | wc -l)
|
|
if [ "$has_sandbox" -lt 3 ]; then
|
|
echo "WEAK_SANDBOX: $svc ($has_sandbox directives)"
|
|
fi
|
|
done
|
|
|
|
# Audit user services for sandboxing
|
|
systemctl --user show '*.service' --property=ProtectSystem,ProtectHome,PrivateTmp 2>/dev/null | grep -B1 "^$"
|
|
```
|
|
|
|
**Alert if**: Critical services (docker, sshd, network) lack sandboxing directives. See source doc for per-service hardening.
|
|
|
|
## Runtime Monitoring (Falco/eBPF) (Ongoing)
|
|
|
|
```bash
|
|
# Check Falco daemon status (if deployed)
|
|
systemctl status falco 2>/dev/null | head -5
|
|
|
|
# Review recent Falco alerts
|
|
journalctl -u falco --since "7 days ago" --no-pager 2>/dev/null | grep -E "Warning|Critical"
|
|
|
|
# Alternative: check Tetragon if deployed
|
|
kubectl get events --field-selector type=Warning 2>/dev/null | tail -10
|
|
```
|
|
|
|
**Alert if**: Falco not running, high-severity alerts in logs. See source doc for Falco rules and Tetragon TracingPolicy YAML.
|
|
|
|
## Memory Forensics (Incident Response Only)
|
|
|
|
```bash
|
|
# Create memory dump (requires elevated privileges)
|
|
sudo dd if=/dev/mem of=/tmp/memdump.raw bs=1M count=1024 2>/dev/null
|
|
|
|
# Analyze with Volatility 3
|
|
uv tool install volatility3
|
|
vol -f /tmp/memdump.raw linux.pslist 2>/dev/null
|
|
vol -f /tmp/memdump.raw linux.bash 2>/dev/null
|
|
|
|
# TeamPCP memory theft detection
|
|
vol -f /tmp/memdump.raw linux.check_syscall 2>/dev/null
|
|
|
|
# Cleanup
|
|
rm -f /tmp/memdump.raw
|
|
```
|
|
|
|
**Use only**: During active incident investigation. Requires prior setup.
|
|
|
|
## Firewall Status Check (Weekly)
|
|
|
|
```bash
|
|
# Check nftables ruleset
|
|
sudo nft list ruleset 2>/dev/null | head -30
|
|
|
|
# If using ufw instead
|
|
sudo ufw status verbose 2>/dev/null
|
|
|
|
# Verify default policies (should be DROP for input)
|
|
# nftables: look for "type filter hook input priority 0; policy drop"
|
|
# ufw: look for "Default: deny (incoming)"
|
|
|
|
# Check for unexpected open ports
|
|
ss -tlnp | grep -E "LISTEN"
|
|
```
|
|
|
|
**Alert if**: Default input policy is ACCEPT, unexpected listening ports, no firewall active.
|
|
|
|
## Ansible Integration
|
|
|
|
Persistent hardening changes go through `ansible/` per `.clinerules/workstation.md`.
|
|
|
|
```bash
|
|
# Verify current hardening state
|
|
ansible-playbook ansible/workstation.yml --check
|
|
|
|
# Apply hardening changes
|
|
ansible-playbook ansible/workstation.yml
|
|
```
|
|
|
|
**Required Ansible tasks** (add to `ansible/roles/common/tasks/main.yml`):
|
|
|
|
- Kernel sysctl parameters (kptr_restrict, dmesg_restrict, ASLR)
|
|
- Firewall default policies (nftables DROP input)
|
|
- AppArmor profile installation
|
|
- Systemd sandboxing directives for critical services
|
|
- `ignore-scripts: true` for npm config
|
|
|
|
**Rule**: Never apply sysctl or firewall changes manually. Always via Ansible.
|
|
|
|
## Tool Installation (One-Time)
|
|
|
|
```bash
|
|
# SBOM, vulnerability scanning, artifact verification, malware patterns
|
|
pacman -S syft grype cosign yara
|
|
|
|
# AppArmor user-space tools
|
|
sudo pacman -S apparmor apparmor-profiles
|
|
|
|
# SAST with supply chain rules
|
|
uv tool install semgrep
|
|
|
|
# Network template scanning
|
|
go install -v github.com/projectdiscovery/nuclei/v3/cmd/nuclei@latest
|
|
|
|
# Memory forensics (incident response)
|
|
uv tool install volatility3
|
|
|
|
# npm package risk scoring
|
|
npm install -g socket-cli
|
|
```
|
|
|
|
## Pre-commit Hook Setup (One-Time)
|
|
|
|
Add to `.pre-commit-config.yaml` in each repo:
|
|
|
|
```yaml
|
|
repos:
|
|
- repo: local
|
|
hooks:
|
|
- id: no-invisible-unicode
|
|
name: Block invisible Unicode (GlassWorm defense)
|
|
entry: python3 scripts/unicode-scanner.py
|
|
language: script
|
|
types: [text]
|
|
```
|
|
|
|
Place the Unicode scanner at `scripts/unicode-scanner.py` (see source doc for full script).
|
|
|
|
## IDE Hardening (One-Time)
|
|
|
|
VS Code `settings.json`:
|
|
|
|
```json
|
|
{
|
|
"editor.renderWhitespace": "all",
|
|
"editor.unicodeHighlight.ambiguousCharacters": true,
|
|
"editor.unicodeHighlight.invisibleCharacters": true
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Incident Response Quick Reference
|
|
|
|
| Indicator | Action |
|
|
|-----------|--------|
|
|
| Unknown systemd service | Stop + investigate + remove |
|
|
| C2 IP in `ss` output | Kill connection, firewall block, full IOC sweep |
|
|
| Unicode IOC in code | Isolate repo, check git history, revert commit |
|
|
| Malicious extension | Uninstall immediately, clear extension cache |
|
|
| npm token compromise | Revoke token, rotate all tokens, audit publish log |
|
|
|
|
## Threat Context (March 2026)
|
|
|
|
| Threat | Vector | Detection |
|
|
|--------|--------|-----------|
|
|
| **TeamPCP** | Tag poisoning, `/proc/pid/mem` theft | SHA pinning, Falco eBPF |
|
|
| **CanisterWorm** | npm worm, systemd persistence | Service audit, npm script block |
|
|
| **GlassWorm** | Unicode steganography, invisible chars | grep VS ranges, pre-commit hook |
|
|
|
|
## Full Reference
|
|
|
|
Detailed technical background, Falco rules, Tetragon policies, Semgrep rules, YARA rules, and Python scanner: `docs/tech/supply-chain-security-2026-03.md`
|