Skip to content
This repository was archived by the owner on Mar 14, 2026. It is now read-only.

Commit 3ee6d4d

Browse files
authored
Merge pull request #5 from UNHCSC/1-pfsenseopnsense-script
pfSense scripts
2 parents 5ed03d6 + c7d5333 commit 3ee6d4d

4 files changed

Lines changed: 633 additions & 0 deletions

File tree

bsd/pfBackup.sh

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
#!/bin/sh
2+
# pfSense Backup Collector
3+
# Creates a timestamped archive containing critical configuration,
4+
# service state metadata, logs, and security-relevant system files.
5+
#
6+
# Usage:
7+
# sh pfBackup.sh [output_directory]
8+
#
9+
# Example:
10+
# sh pfBackup.sh /root/pfBackups
11+
12+
set -u
13+
14+
SCRIPT_NAME="$(basename "$0")"
15+
HOSTNAME_SHORT="$(hostname -s 2>/dev/null || hostname 2>/dev/null || echo unknown)"
16+
TIMESTAMP="$(date '+%Y%m%d_%H%M%S')"
17+
DEST_BASE="${1:-/root/pfBackups}"
18+
STAGE_DIR="${DEST_BASE}/pfbackup_${HOSTNAME_SHORT}_${TIMESTAMP}"
19+
ARCHIVE_NAME="$(basename "$STAGE_DIR").tar.gz"
20+
ARCHIVE_PATH="${DEST_BASE}/${ARCHIVE_NAME}"
21+
22+
log() {
23+
printf '[%s] %s\n' "$(date '+%F %T')" "$*" >&2
24+
}
25+
26+
require_root() {
27+
if [ "$(id -u)" -ne 0 ]; then
28+
log "This script must run as root on pfSense."
29+
exit 1
30+
fi
31+
}
32+
33+
run_cmd() {
34+
# Usage: run_cmd "description" "command"
35+
_desc="$1"
36+
_cmd="$2"
37+
38+
{
39+
printf '===== %s =====\n' "$_desc"
40+
printf 'CMD: %s\n' "$_cmd"
41+
printf 'TIME: %s\n\n' "$(date '+%F %T')"
42+
sh -c "$_cmd" 2>&1
43+
printf '\n'
44+
} >>"$STAGE_DIR/metadata/system_state.txt"
45+
}
46+
47+
stage_path_if_exists() {
48+
# Usage: stage_path_if_exists "relative/path/from/root"
49+
_rel="$1"
50+
51+
if [ -e "/$_rel" ]; then
52+
log "Staging /$_rel"
53+
mkdir -p "$STAGE_DIR/files"
54+
55+
# Use tar pipe to preserve modes/ownership/symlinks and avoid shell glob issues.
56+
if tar -C / -cpf - "$_rel" 2>/dev/null | tar -C "$STAGE_DIR/files" -xpf - 2>/dev/null; then
57+
printf '%s\n' "/$_rel" >>"$STAGE_DIR/metadata/staged_paths.txt"
58+
else
59+
printf '%s\n' "/$_rel" >>"$STAGE_DIR/metadata/failed_paths.txt"
60+
fi
61+
else
62+
printf '%s\n' "/$_rel" >>"$STAGE_DIR/metadata/missing_paths.txt"
63+
fi
64+
}
65+
66+
require_root
67+
umask 077
68+
mkdir -p "$STAGE_DIR/metadata" "$STAGE_DIR/files"
69+
70+
log "Starting pfSense backup stage in: $STAGE_DIR"
71+
72+
# Basic metadata for quick triage and provenance.
73+
{
74+
printf 'pfSense Backup Collector\n'
75+
printf 'Script: %s\n' "$SCRIPT_NAME"
76+
printf 'Collected: %s\n' "$(date '+%F %T %z')"
77+
printf 'Host: %s\n' "$(hostname 2>/dev/null || echo unknown)"
78+
printf 'Stage Dir: %s\n' "$STAGE_DIR"
79+
printf 'Archive: %s\n' "$ARCHIVE_PATH"
80+
printf '\n'
81+
} >"$STAGE_DIR/README.txt"
82+
83+
# Capture current system/service/firewall state.
84+
run_cmd "System - OS/Kernel" "uname -a"
85+
run_cmd "System - Versions" "cat /etc/version /etc/version.patch /etc/platform 2>/dev/null"
86+
run_cmd "System - Uptime/Boot" "uptime && sysctl kern.boottime"
87+
run_cmd "Network - Interfaces" "ifconfig -a"
88+
run_cmd "Network - Routing table" "netstat -rn"
89+
run_cmd "Network - Listening sockets" "sockstat -4 -6 -l"
90+
run_cmd "Firewall - pf summary" "pfctl -sa 2>/dev/null"
91+
run_cmd "Processes - ps auxww" "ps auxww"
92+
run_cmd "Packages - installed" "pkg info 2>/dev/null"
93+
run_cmd "Cron - root" "crontab -l -u root 2>/dev/null || crontab -l 2>/dev/null"
94+
95+
# Core pfSense + FreeBSD config, secrets, service definitions, and logs.
96+
# Paths are relative to root (/).
97+
IMPORTANT_PATHS="
98+
cf/conf
99+
etc
100+
usr/local/etc
101+
usr/local/www
102+
var/etc
103+
var/db
104+
var/dhcpd
105+
var/unbound
106+
var/cron/tabs
107+
var/log
108+
root
109+
home
110+
boot
111+
"
112+
113+
printf '' >"$STAGE_DIR/metadata/staged_paths.txt"
114+
printf '' >"$STAGE_DIR/metadata/missing_paths.txt"
115+
printf '' >"$STAGE_DIR/metadata/failed_paths.txt"
116+
117+
for rel_path in $IMPORTANT_PATHS; do
118+
stage_path_if_exists "$rel_path"
119+
done
120+
121+
# Hash everything in the staged backup so integrity can be verified later.
122+
(
123+
cd "$STAGE_DIR" || exit 1
124+
find . -type f -print0 | xargs -0 sha256 >SHA256SUMS.txt 2>/dev/null || true
125+
)
126+
127+
# Create compressed archive.
128+
log "Creating archive: $ARCHIVE_PATH"
129+
if tar -C "$DEST_BASE" -czpf "$ARCHIVE_PATH" "$(basename "$STAGE_DIR")"; then
130+
:
131+
else
132+
log "Failed to create archive."
133+
exit 1
134+
fi
135+
136+
{
137+
printf 'Completed: %s\n' "$(date '+%F %T')"
138+
printf 'Archive path: %s\n' "$ARCHIVE_PATH"
139+
printf 'Staged files dir: %s\n' "$STAGE_DIR/files"
140+
printf 'Metadata report: %s\n' "$STAGE_DIR/metadata/system_state.txt"
141+
} >>"$STAGE_DIR/README.txt"
142+
143+
log "Backup complete."
144+
log "Archive: $ARCHIVE_PATH"
145+
log "Stage directory retained at: $STAGE_DIR"

bsd/pfSenseCollectInfo.sh

Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
#!/bin/sh
2+
# pfSense Inventory Collector
3+
# Collects broad host/network/security context for incident response and
4+
# competition triage. Output is written to a timestamped directory.
5+
6+
SCRIPT_NAME="$(basename "$0")"
7+
HOSTNAME_SHORT="$(hostname -s 2>/dev/null || hostname 2>/dev/null || echo unknown)"
8+
TIMESTAMP="$(date '+%Y%m%d_%H%M%S')"
9+
DEFAULT_OUTDIR="/tmp/pfsense_inventory_${HOSTNAME_SHORT}_${TIMESTAMP}"
10+
OUTDIR="${1:-$DEFAULT_OUTDIR}"
11+
12+
umask 077
13+
mkdir -p "$OUTDIR"
14+
15+
log() {
16+
printf '[%s] %s\n' "$(date '+%F %T')" "$*" >&2
17+
}
18+
19+
run_cmd() {
20+
# Usage: run_cmd "description" "command"
21+
_desc="$1"
22+
_cmd="$2"
23+
24+
{
25+
printf '===== %s =====\n' "$_desc"
26+
printf 'CMD: %s\n' "$_cmd"
27+
printf 'TIME: %s\n\n' "$(date '+%F %T')"
28+
sh -c "$_cmd" 2>&1
29+
printf '\n'
30+
} >>"$OUTDIR/inventory.txt"
31+
}
32+
33+
copy_file_if_exists() {
34+
# Usage: copy_file_if_exists "/path/to/src" "relative/dest/name"
35+
_src="$1"
36+
_dst="$2"
37+
_dst_dir="$(dirname "$OUTDIR/$_dst")"
38+
39+
if [ -f "$_src" ]; then
40+
mkdir -p "$_dst_dir"
41+
cp "$_src" "$OUTDIR/$_dst"
42+
chmod 600 "$OUTDIR/$_dst" 2>/dev/null || true
43+
fi
44+
}
45+
46+
capture_tail_if_exists() {
47+
# Usage: capture_tail_if_exists "/path/to/log" "relative/dest/name" "lines"
48+
_src="$1"
49+
_dst="$2"
50+
_lines="$3"
51+
_dst_dir="$(dirname "$OUTDIR/$_dst")"
52+
53+
if [ -f "$_src" ]; then
54+
mkdir -p "$_dst_dir"
55+
tail -n "$_lines" "$_src" >"$OUTDIR/$_dst" 2>/dev/null || true
56+
chmod 600 "$OUTDIR/$_dst" 2>/dev/null || true
57+
fi
58+
}
59+
60+
log "Starting collection to: $OUTDIR"
61+
log "This can take a minute depending on system size and log volume."
62+
63+
# Header summary
64+
{
65+
printf 'pfSense Inventory Collector\n'
66+
printf 'Script: %s\n' "$SCRIPT_NAME"
67+
printf 'Collected: %s\n' "$(date '+%F %T %z')"
68+
printf 'Host: %s\n' "$(hostname 2>/dev/null || echo unknown)"
69+
printf 'Output Dir: %s\n' "$OUTDIR"
70+
printf '\n'
71+
} >"$OUTDIR/README.txt"
72+
73+
# Core system context
74+
run_cmd "System - OS/Kernel" "uname -a"
75+
run_cmd "System - pfSense Version Files" "cat /etc/version /etc/version.patch /etc/platform 2>/dev/null"
76+
run_cmd "System - Uptime/Boot" "uptime && sysctl kern.boottime"
77+
run_cmd "System - Date" "date"
78+
run_cmd "System - CPU" "sysctl hw.model hw.ncpu hw.physmem 2>/dev/null"
79+
run_cmd "System - Full Sysctl (verbose)" "sysctl -a"
80+
run_cmd "System - Dmesg" "dmesg -a"
81+
run_cmd "System - Last Logins" "last -n 40 2>/dev/null"
82+
83+
# Filesystems and storage
84+
run_cmd "Storage - Disk Usage" "df -h"
85+
run_cmd "Storage - Mounts" "mount"
86+
run_cmd "Storage - GEOM Disks" "geom disk list 2>/dev/null"
87+
run_cmd "Storage - GEOM Partitions" "geom part list 2>/dev/null"
88+
run_cmd "Storage - gpart show" "gpart show 2>/dev/null"
89+
run_cmd "Storage - swapinfo" "swapinfo -h 2>/dev/null"
90+
run_cmd "Storage - ZFS status" "zpool status 2>/dev/null"
91+
run_cmd "Storage - ZFS datasets" "zfs list 2>/dev/null"
92+
93+
# Network and interface context
94+
run_cmd "Network - Interfaces" "ifconfig -a"
95+
run_cmd "Network - Routing table" "netstat -rn"
96+
run_cmd "Network - Listening sockets" "sockstat -4 -6 -l"
97+
run_cmd "Network - Active sockets" "sockstat -4 -6"
98+
run_cmd "Network - ARP cache" "arp -an"
99+
run_cmd "Network - Interface stats" "netstat -i -W"
100+
run_cmd "Network - Established/All connections" "netstat -an"
101+
run_cmd "Network - IPv4 stats" "netstat -s -p ip 2>/dev/null"
102+
run_cmd "Network - TCP stats" "netstat -s -p tcp 2>/dev/null"
103+
run_cmd "Network - UDP stats" "netstat -s -p udp 2>/dev/null"
104+
105+
# Firewall and pf state
106+
run_cmd "Firewall - pf info" "pfctl -si 2>/dev/null"
107+
run_cmd "Firewall - pf rules" "pfctl -sr 2>/dev/null"
108+
run_cmd "Firewall - pf NAT rules" "pfctl -sn 2>/dev/null"
109+
run_cmd "Firewall - pf tables" "pfctl -sTables 2>/dev/null"
110+
run_cmd "Firewall - pf states" "pfctl -ss 2>/dev/null"
111+
run_cmd "Firewall - pf all status (verbose)" "pfctl -sa 2>/dev/null"
112+
113+
# Service and process context
114+
run_cmd "Processes - ps auxww" "ps auxww"
115+
run_cmd "Services - rc status" "service -e 2>/dev/null"
116+
run_cmd "Services - pkg inventory" "pkg info 2>/dev/null"
117+
run_cmd "Services - package locks" "pkg lock -l 2>/dev/null"
118+
run_cmd "Services - cron jobs (root)" "crontab -l -u root 2>/dev/null || crontab -l 2>/dev/null"
119+
run_cmd "Services - periodic conf" "cat /etc/periodic.conf 2>/dev/null"
120+
121+
# User and auth context
122+
run_cmd "Users - passwd" "cat /etc/passwd"
123+
run_cmd "Users - group" "cat /etc/group"
124+
run_cmd "Users - login classes" "cat /etc/login.conf 2>/dev/null"
125+
run_cmd "Users - sudoers" "cat /usr/local/etc/sudoers 2>/dev/null"
126+
run_cmd "Users - authorized_keys files" "find /root /home -maxdepth 4 -type f -name authorized_keys 2>/dev/null | while IFS= read -r f; do echo --- \"\$f\"; cat \"\$f\"; done"
127+
run_cmd "Users - SSH daemon config" "cat /etc/ssh/sshd_config 2>/dev/null"
128+
129+
# pfSense-specific configuration references
130+
run_cmd "pfSense - Config history/listing" "ls -lah /cf/conf 2>/dev/null && ls -lah /cf/conf/backup 2>/dev/null"
131+
run_cmd "pfSense - OpenVPN files" "ls -lah /var/etc/openvpn 2>/dev/null"
132+
run_cmd "pfSense - IPsec files" "ls -lah /var/etc/ipsec 2>/dev/null"
133+
run_cmd "pfSense - Unbound DNS files" "ls -lah /var/unbound 2>/dev/null"
134+
run_cmd "pfSense - DHCP files" "ls -lah /var/dhcpd 2>/dev/null"
135+
run_cmd "pfSense - Resolver/Services local configs" "find /var/etc -maxdepth 2 -type f 2>/dev/null | head -n 300"
136+
137+
# Copy key configuration files (sensitive)
138+
mkdir -p "$OUTDIR/config" "$OUTDIR/log_tails"
139+
copy_file_if_exists "/cf/conf/config.xml" "config/config.xml"
140+
copy_file_if_exists "/etc/ssh/sshd_config" "config/sshd_config"
141+
copy_file_if_exists "/etc/rc.conf" "config/rc.conf"
142+
copy_file_if_exists "/etc/resolv.conf" "config/resolv.conf"
143+
copy_file_if_exists "/etc/hosts" "config/hosts"
144+
copy_file_if_exists "/usr/local/etc/sudoers" "config/sudoers"
145+
copy_file_if_exists "/etc/crontab" "config/crontab"
146+
copy_file_if_exists "/var/cron/tabs/root" "config/root_crontab"
147+
148+
# Log tails for quick triage
149+
capture_tail_if_exists "/var/log/system.log" "log_tails/system.log.tail" "400"
150+
capture_tail_if_exists "/var/log/filter.log" "log_tails/filter.log.tail" "400"
151+
capture_tail_if_exists "/var/log/auth.log" "log_tails/auth.log.tail" "400"
152+
capture_tail_if_exists "/var/log/vpn.log" "log_tails/vpn.log.tail" "400"
153+
capture_tail_if_exists "/var/log/dhcpd.log" "log_tails/dhcpd.log.tail" "400"
154+
capture_tail_if_exists "/var/log/resolver.log" "log_tails/resolver.log.tail" "400"
155+
capture_tail_if_exists "/var/log/nginx/error.log" "log_tails/nginx_error.log.tail" "400"
156+
157+
# Permissions and integrity quick checks
158+
run_cmd "Security - SUID/SGID files" "find / -xdev \\( -perm -4000 -o -perm -2000 \\) -type f 2>/dev/null"
159+
run_cmd "Security - Writable by others (top level paths)" "find /bin /sbin /usr/bin /usr/sbin /usr/local/bin /usr/local/sbin -type f -perm -0002 2>/dev/null"
160+
run_cmd "Security - rc scripts" "ls -lah /etc/rc* /usr/local/etc/rc.d 2>/dev/null"
161+
run_cmd "Security - SSH host keys fingerprints" "ssh-keygen -lf /etc/ssh/ssh_host_rsa_key.pub 2>/dev/null; ssh-keygen -lf /etc/ssh/ssh_host_ecdsa_key.pub 2>/dev/null; ssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pub 2>/dev/null"
162+
163+
# Build a simple file manifest
164+
(
165+
cd "$OUTDIR" || exit 1
166+
find . -type f -print0 | xargs -0 sha256 >SHA256SUMS.txt 2>/dev/null || true
167+
)
168+
169+
{
170+
printf 'Collection complete: %s\n' "$(date '+%F %T')"
171+
printf 'Primary report: %s/inventory.txt\n' "$OUTDIR"
172+
printf 'Sensitive config copies may exist in: %s/config\n' "$OUTDIR"
173+
} >>"$OUTDIR/README.txt"
174+
175+
log "Collection complete."
176+
log "Report: $OUTDIR/inventory.txt"
177+
log "Bundle this directory for offline analysis if needed."

0 commit comments

Comments
 (0)