1047 lines
29 KiB
Bash
1047 lines
29 KiB
Bash
#!/bin/bash
|
|
# Fedora Kinoite SAW Post-Installation Lockdown Script
|
|
# Version: 1.0
|
|
# Date: 2026-04-02
|
|
#
|
|
# This script implements security hardening for the Secure Air-Gapped Workstation (SAW)
|
|
# It disables sudo access, configures firewall, sets up VPN, and enables security features.
|
|
#
|
|
# Usage: sudo ./lockdown.sh
|
|
#
|
|
# This script should be run after initial system installation and first boot.
|
|
# It implements multiple layers of security as described in the main README.md.
|
|
|
|
set -e
|
|
|
|
# Configuration
|
|
LOG_FILE="/var/log/saw-lockdown.log"
|
|
LOCKDOWN_COMPLETE_FILE="/etc/saw/lockdown_complete"
|
|
CA_KEY_PATH="/etc/pki/rpm-gpg/RPM-GPG-KEY-custom-ca"
|
|
VPN_CONFIG_FILE="/etc/wireguard/wg0.conf"
|
|
CRL_FILE="/etc/pki/ca-trust/source/anchors/crl.pem"
|
|
DNS_CONFIG_DIR="/etc/dnsmasq.d"
|
|
FIREWALL_ZONE="/etc/firewalld/zones/saw.xml"
|
|
|
|
# Colors for output
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
BLUE='\033[0;34m'
|
|
NC='\033[0m' # No Color
|
|
|
|
# Print functions with timestamps
|
|
log_info() {
|
|
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
|
|
echo -e "${GREEN}[$timestamp]${NC} ${BLUE}[INFO]${NC} $1" | tee -a "$LOG_FILE"
|
|
}
|
|
|
|
log_warning() {
|
|
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
|
|
echo -e "${YELLOW}[$timestamp]${NC} ${YELLOW}[WARNING]${NC} $1" | tee -a "$LOG_FILE"
|
|
}
|
|
|
|
log_error() {
|
|
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
|
|
echo -e "${RED}[$timestamp]${NC} ${RED}[ERROR]${NC} $1" | tee -a "$LOG_FILE"
|
|
}
|
|
|
|
# Check if running as root
|
|
check_root() {
|
|
if [ "$EUID" -ne 0 ]; then
|
|
log_error "This script must be run as root"
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
# Check if already locked down
|
|
check_lockdown_status() {
|
|
if [ -f "$LOCKDOWN_COMPLETE_FILE" ]; then
|
|
log_warning "System appears to already be locked down"
|
|
log_info "File $LOCKDOWN_COMPLETE_FILE exists"
|
|
read -p "Do you want to re-run lockdown? (yes/no): " response
|
|
if [ "$response" != "yes" ]; then
|
|
log_info "Lockdown skipped"
|
|
exit 0
|
|
fi
|
|
fi
|
|
}
|
|
|
|
# Create log directory
|
|
setup_logging() {
|
|
mkdir -p /var/log
|
|
touch "$LOG_FILE"
|
|
chmod 600 "$LOG_FILE"
|
|
log_info "Logging configured: $LOG_FILE"
|
|
}
|
|
|
|
# Create SAW configuration directory
|
|
setup_config_dir() {
|
|
mkdir -p /etc/saw
|
|
mkdir -p /etc/saw/config
|
|
mkdir -p /etc/saw/scripts
|
|
log_info "Configuration directory created: /etc/saw"
|
|
}
|
|
|
|
# Backup existing configuration
|
|
backup_config() {
|
|
local backup_dir="/etc/saw/backup/$(date +%Y%m%d_%H%M%S)"
|
|
mkdir -p "$backup_dir"
|
|
|
|
log_info "Creating backup of existing configuration..."
|
|
|
|
# Backup SSH configuration
|
|
if [ -f /etc/ssh/sshd_config ]; then
|
|
cp /etc/ssh/sshd_config "$backup_dir/sshd_config.backup"
|
|
fi
|
|
|
|
# Backup firewall configuration
|
|
if [ -d /etc/firewalld ]; then
|
|
cp -r /etc/firewalld "$backup_dir/firewalld.backup" 2>/dev/null || true
|
|
fi
|
|
|
|
# Backup DNS configuration
|
|
if [ -d "$DNS_CONFIG_DIR" ]; then
|
|
cp -r "$DNS_CONFIG_DIR" "$backup_dir/dnsmasq.backup" 2>/dev/null || true
|
|
fi
|
|
|
|
# Backup DNF configuration
|
|
if [ -f /etc/dnf/dnf.conf ]; then
|
|
cp /etc/dnf/dnf.conf "$backup_dir/dnf.conf.backup"
|
|
fi
|
|
|
|
log_info "Backup created at: $backup_dir"
|
|
}
|
|
|
|
# Disable Sudo Access
|
|
#
|
|
# SECURITY RATIONALE:
|
|
# Disabling sudo access is a critical security control that prevents:
|
|
# 1. Privilege escalation attacks after system compromise
|
|
# 2. Accidental system modifications by users
|
|
# 3. Exploitation of sudo vulnerabilities (e.g., CVE-2021-3156)
|
|
# 4. Lateral movement in case of user account compromise
|
|
#
|
|
# This implements the principle of least privilege - users should not have
|
|
# administrative access unless absolutely necessary, and even then it should
|
|
# be through controlled, audited workflows.
|
|
#
|
|
# Reference: NSA Linux Security Hardening Guide
|
|
disable_sudo() {
|
|
log_info "Disabling sudo access..."
|
|
|
|
# Get current user
|
|
local current_user=$(whoami)
|
|
|
|
# Remove user from wheel group
|
|
if id -nG "$current_user" | grep -q wheel; then
|
|
gpasswd -d "$current_user" wheel
|
|
log_info "Removed user '$current_user' from wheel group"
|
|
else
|
|
log_info "User '$current_user' is not in wheel group"
|
|
fi
|
|
|
|
# Disable root login via SSH
|
|
if [ -f /etc/ssh/sshd_config ]; then
|
|
sed -i 's/^PermitRootLogin yes/PermitRootLogin no/' /etc/ssh/sshd_config 2>/dev/null || true
|
|
sed -i 's/^#PermitRootLogin yes/PermitRootLogin no/' /etc/ssh/sshd_config 2>/dev/null || true
|
|
log_info "Disabled root SSH login in sshd_config"
|
|
else
|
|
log_warning "SSH configuration not found, skipping"
|
|
fi
|
|
|
|
# Configure PAM to require MFA for any sudo attempts (if PAM modules available)
|
|
# This is a defense-in-depth measure
|
|
if [ -d /etc/pam.d ]; then
|
|
# Check if pam_oauth2 or pam_totp is available
|
|
if command -v pam_module_available &> /dev/null; then
|
|
log_info "PAM MFA configuration available"
|
|
fi
|
|
fi
|
|
|
|
log_info "Sudo access disabled"
|
|
}
|
|
|
|
# Enable SELinux Enforcing
|
|
#
|
|
# SECURITY RATIONALE:
|
|
# SELinux (Security-Enhanced Linux) provides mandatory access control (MAC)
|
|
# that goes beyond traditional discretionary access control (DAC).
|
|
#
|
|
# Key benefits:
|
|
# 1. Prevents privilege escalation even if root is compromised
|
|
# 2. Contains compromised applications within their domains
|
|
# 3. Provides fine-grained access control
|
|
# 4. Logs all security violations for forensic analysis
|
|
#
|
|
# Enforcing mode ensures all security policies are actively enforced.
|
|
# Permissive mode only logs violations, which is less secure.
|
|
#
|
|
# Reference: https://docs.fedoraproject.org/en-US/SELinux/
|
|
enable_selinux() {
|
|
log_info "Enabling SELinux enforcing mode..."
|
|
|
|
# Check current SELinux status
|
|
local selinux_status=$(getenforce)
|
|
|
|
if [ "$selinux_status" = "Enforcing" ]; then
|
|
log_info "SELinux already in enforcing mode"
|
|
else
|
|
# Set SELinux to enforcing
|
|
setenforce 1
|
|
log_info "Set SELinux to enforcing mode"
|
|
|
|
# Make permanent
|
|
sed -i 's/^SELINUX=permissive/SELINUX=enforcing/' /etc/selinux/config 2>/dev/null || true
|
|
sed -i 's/^SELINUX=disabled/SELINUX=enforcing/' /etc/selinux/config 2>/dev/null || true
|
|
log_info "Made SELinux enforcing permanent in config"
|
|
fi
|
|
|
|
log_info "SELinux status: $(getenforce)"
|
|
}
|
|
|
|
# Enable Audit Daemon
|
|
#
|
|
# SECURITY RATIONALE:
|
|
# auditd (audit daemon) provides comprehensive system auditing and logging
|
|
# of security-relevant events.
|
|
#
|
|
# Why auditd is critical:
|
|
# 1. Logs all privilege escalation attempts
|
|
# 2. Tracks file access to sensitive files (/etc, /bin, /usr, etc.)
|
|
# 3. Records system calls for forensic analysis
|
|
# 4. Provides real-time alerting capabilities
|
|
# 5. Required for compliance (PCI-DSS, HIPAA, etc.)
|
|
#
|
|
# We configure it to log:
|
|
# - All sudo attempts (even though sudo is disabled)
|
|
# - File access to critical system files
|
|
# - Network connections
|
|
# - User activity
|
|
#
|
|
# Reference: https://people.redhat.com/sgrubb/audit/
|
|
enable_auditd() {
|
|
log_info "Enabling audit daemon..."
|
|
|
|
# Start auditd
|
|
if command -v auditctl &> /dev/null; then
|
|
systemctl enable auditd
|
|
systemctl start auditd
|
|
log_info "Auditd started and enabled"
|
|
else
|
|
log_warning "auditd not available, skipping"
|
|
return
|
|
fi
|
|
|
|
# Configure audit rules
|
|
local audit_rules="/etc/audit/rules.d/audit.rules"
|
|
|
|
# Create audit rules for critical security events
|
|
cat >> "$audit_rules" << 'AUDITRULES'
|
|
# SAW Custom Audit Rules
|
|
# Log all privilege escalations
|
|
-a always,exit -F arch=b64 -S execve -F exe=/usr/bin/sudo -k privilege_escalation
|
|
-a always,exit -F arch=b64 -S execve -F exe=/usr/bin/su -k privilege_escalation
|
|
-a always,exit -F arch=b64 -S setuid -F auid!=unset -k privilege_escalation
|
|
|
|
# Monitor critical system files
|
|
-w /etc/passwd -p wa -k identity
|
|
-w /etc/shadow -p wa -k identity
|
|
-w /etc/group -p wa -k identity
|
|
-w /etc/sudoers -p wa -k sudo
|
|
-w /etc/pam.d/ -p wa -k pam
|
|
|
|
# Monitor system binaries
|
|
-w /bin/ -p x -k binaries
|
|
-w /usr/bin/ -p x -k binaries
|
|
-w /sbin/ -p x -k binaries
|
|
-w /usr/sbin/ -p x -k binaries
|
|
|
|
# Monitor network configuration
|
|
-w /etc/sysconfig/network-scripts/ -p wa -k network
|
|
-w /etc/sysctl.conf -p wa -k sysctl
|
|
|
|
# Monitor kernel module loading
|
|
-a always,exit -F arch=b64 -S init_module -S delete_module -k modules
|
|
AUDITRULES
|
|
|
|
# Load audit rules
|
|
if [ -f "$audit_rules" ]; then
|
|
auditctl -R "$audit_rules"
|
|
log_info "Audit rules loaded"
|
|
fi
|
|
|
|
log_info "Audit daemon configured"
|
|
}
|
|
|
|
# Configure Firewall
|
|
#
|
|
# SECURITY RATIONALE:
|
|
# Firewall implementation for SAW follows the principle of "default deny"
|
|
# with explicit allow rules only for necessary services.
|
|
#
|
|
# Why this configuration:
|
|
# 1. Egress filtering prevents data exfiltration
|
|
# 2. Blocks all outbound traffic except VPN
|
|
# 3. Allows only WireGuard VPN traffic
|
|
# 4. Prevents DNS leaks (only VPN gateway DNS allowed)
|
|
# 5. Implements network segmentation
|
|
#
|
|
# The configuration includes:
|
|
# - WireGuard interface allowed
|
|
# - All other outbound blocked
|
|
# - Loopback traffic allowed
|
|
# - Inbound only from VPN tunnel
|
|
#
|
|
# Reference: https://firewalld.org/documentation/
|
|
configure_firewall() {
|
|
log_info "Configuring firewall..."
|
|
|
|
# Check if firewalld is available
|
|
if ! command -v firewall-cmd &> /dev/null; then
|
|
log_warning "firewalld not available, installing..."
|
|
dnf install -y firewalld
|
|
fi
|
|
|
|
# Start and enable firewalld
|
|
systemctl enable firewalld
|
|
systemctl start firewalld
|
|
log_info "Firewalld started and enabled"
|
|
|
|
# Check if WireGuard config exists
|
|
if [ ! -f "$VPN_CONFIG_FILE" ]; then
|
|
log_warning "WireGuard config not found: $VPN_CONFIG_FILE"
|
|
log_warning "Skipping firewall configuration for WireGuard"
|
|
return
|
|
fi
|
|
|
|
# Create WireGuard service definition
|
|
cat > /etc/firewalld/services/wireguard.xml << 'FIREWALL'
|
|
<?xml version="1.0" encoding="utf-8"?>
|
|
<service>
|
|
<short>WireGuard VPN</short>
|
|
<description>WireGuard VPN tunnel for secure communication</description>
|
|
<port protocol="udp" port="51820"/>
|
|
<module name="nf_conntrack_netlink"/>
|
|
<module name="nf_conntrack_helper"/>
|
|
</service>
|
|
FIREWALL
|
|
|
|
log_info "WireGuard service defined"
|
|
|
|
# Configure firewall zones
|
|
# Create SAW zone with strict rules
|
|
cat > "$FIREWALL_ZONE" << 'FIREWALLZONE'
|
|
<?xml version="1.0" encoding="utf-8"?>
|
|
<zone>
|
|
<short>SAW Secure Zone</short>
|
|
<description>Strict security zone for SAW implementation</description>
|
|
|
|
<!-- Only allow WireGuard traffic -->
|
|
<service name="ssh"/>
|
|
<service name="wireguard"/>
|
|
|
|
<!-- Masquerade for VPN traffic -->
|
|
<masquerade/>
|
|
|
|
<!-- Forward ports if needed -->
|
|
<forward-port to-port="0" proto="tcp" to-addr="0.0.0.0"/>
|
|
|
|
<!-- Strict egress filtering -->
|
|
<rule priority="100">
|
|
<interface name="wg0"/>
|
|
<accept/>
|
|
</rule>
|
|
|
|
<!-- Block all other outbound -->
|
|
<rule priority="200">
|
|
<reject/>
|
|
</rule>
|
|
</zone>
|
|
FIREWALLZONE
|
|
|
|
# Apply firewall rules
|
|
firewall-cmd --reload
|
|
firewall-cmd --permanent --add-service=wireguard
|
|
firewall-cmd --permanent --zone=saw --add-service=wireguard
|
|
firewall-cmd --permanent --zone=saw --add-interface=wg0 2>/dev/null || true
|
|
|
|
log_info "Firewall rules applied"
|
|
}
|
|
|
|
# Configure DNS Lockdown
|
|
#
|
|
# SECURITY RATIONALE:
|
|
# DNS lockdown prevents DNS leaks and ensures all DNS queries go through
|
|
# the VPN tunnel to the VPN gateway.
|
|
#
|
|
# Why this is critical:
|
|
# 1. Prevents DNS-based tracking and profiling
|
|
# 2. Blocks malware command & control communication
|
|
# 3. Ensures all traffic goes through encrypted VPN tunnel
|
|
# 4. Prevents DNS rebinding attacks
|
|
# 5. Centralizes DNS logging and filtering
|
|
#
|
|
# Implementation:
|
|
# - dnsmasq configured to only use VPN gateway DNS
|
|
# - All DNS queries redirected through tunnel
|
|
# - Private DNS blocked (prevents LAN DNS leaks)
|
|
# - No upstream DNS servers configured
|
|
#
|
|
# Reference: https://fedoraproject.org/wiki/Features/DNSOverTLS
|
|
configure_dns() {
|
|
log_info "Configuring DNS lockdown..."
|
|
|
|
# Check if dnsmasq is available
|
|
if ! command -v dnsmasq &> /dev/null; then
|
|
log_warning "dnsmasq not available, installing..."
|
|
dnf install -y dnsmasq
|
|
fi
|
|
|
|
# Create DNS configuration directory
|
|
mkdir -p "$DNS_CONFIG_DIR"
|
|
|
|
# Create VPN-only DNS configuration
|
|
cat > "${DNS_CONFIG_DIR}/vpn-dns.conf" << 'DNSCONF'
|
|
# VPN-Only DNS Configuration for SAW
|
|
# All DNS queries go through VPN gateway only
|
|
|
|
# Use only VPN gateway DNS (replace with your VPN gateway IP)
|
|
server=10.0.0.1
|
|
server=::1
|
|
|
|
# Ignore private DNS (prevents LAN DNS leaks)
|
|
bogus-priv
|
|
|
|
# Don't read /etc/resolv.conf or any other file
|
|
no-resolv
|
|
|
|
# Don't poll /etc/resolv.conf for changes
|
|
no-poll
|
|
|
|
# Don't trust upstream DNS servers
|
|
strict-order
|
|
|
|
# Cache size
|
|
cache-size=10000
|
|
|
|
# Log to file
|
|
log-facility=@/var/log/dnsmasq.log
|
|
|
|
# Don't forward plain names (as a workaround for broken DNS servers)
|
|
domain-needed
|
|
|
|
# Never forward addresses in the domain names below
|
|
domain=local
|
|
negate-domain=local
|
|
|
|
# Bind to interface
|
|
bind-interfaces
|
|
|
|
# Listen only on loopback
|
|
listen-address=127.0.0.1
|
|
|
|
# Do not read /etc/hosts
|
|
no-hosts
|
|
|
|
# Add local-only domains here
|
|
local=/local/
|
|
DNSCONF
|
|
|
|
log_info "DNS configuration created"
|
|
|
|
# Start dnsmasq
|
|
systemctl enable dnsmasq
|
|
systemctl start dnsmasq
|
|
log_info "dnsmasq started"
|
|
|
|
# Configure resolv.conf to use dnsmasq
|
|
if [ -f /etc/resolv.conf ]; then
|
|
# Backup existing resolv.conf
|
|
cp /etc/resolv.conf /etc/resolv.conf.backup
|
|
|
|
# Write new resolv.conf
|
|
echo "nameserver 127.0.0.1" > /etc/resolv.conf
|
|
log_info "resolv.conf configured to use local DNS"
|
|
fi
|
|
|
|
# Update systemd-resolved to use dnsmasq
|
|
if command -v systemd-resolve &> /dev/null; then
|
|
systemctl stop systemd-resolved 2>/dev/null || true
|
|
systemctl disable systemd-resolved 2>/dev/null || true
|
|
log_info "systemd-resolved disabled"
|
|
fi
|
|
|
|
log_info "DNS lockdown configured"
|
|
}
|
|
|
|
# Configure Package Verification
|
|
#
|
|
# SECURITY RATIONALE:
|
|
# Package verification ensures that only packages signed by your trusted
|
|
# Certificate Authority (CA) can be installed on the system.
|
|
#
|
|
# Why this is critical:
|
|
# 1. Prevents installation of malicious packages
|
|
# 2. Ensures package integrity (not modified in transit)
|
|
# 3. Authenticates package source (only your CA can sign)
|
|
# 4. Provides non-repudiation (can prove who signed)
|
|
# 5. Prevents supply chain attacks
|
|
#
|
|
# Implementation:
|
|
# - DNF configured to require GPG signature verification
|
|
# - Repository GPG verification enabled
|
|
# - CA public key imported into RPM database
|
|
# - CRL checking for revoked certificates
|
|
#
|
|
# Reference: https://docs.fedoraproject.org/en-US/fedora-coreos/security-verification/
|
|
configure_package_verification() {
|
|
log_info "Configuring package verification..."
|
|
|
|
# Check if CA key exists
|
|
if [ ! -f "$CA_KEY_PATH" ]; then
|
|
log_warning "CA key not found: $CA_KEY_PATH"
|
|
log_warning "Package verification requires CA key"
|
|
read -p "Do you want to continue without CA key? (yes/no): " response
|
|
if [ "$response" != "yes" ]; then
|
|
log_info "Package verification skipped"
|
|
return
|
|
fi
|
|
fi
|
|
|
|
# Import CA key into RPM database
|
|
if [ -f "$CA_KEY_PATH" ]; then
|
|
rpm --import "$CA_KEY_PATH"
|
|
log_info "CA key imported into RPM database"
|
|
fi
|
|
|
|
# Configure DNF to require signatures
|
|
cat >> /etc/dnf/dnf.conf << 'DNFCONF'
|
|
# Package verification for SAW
|
|
gpgcheck=1
|
|
repo_gpgcheck=1
|
|
metadata_expire=1h
|
|
fastestmirror=False
|
|
DNFCONF
|
|
|
|
log_info "DNF configured for signature verification"
|
|
|
|
# Configure rpm-ostree for verification
|
|
cat >> /etc/rpm-ostreed.conf << 'OSTREECONF'
|
|
[Service]
|
|
# Require signature verification
|
|
SignatureVerification=required
|
|
# Auto-download updates
|
|
DownloadOnly=true
|
|
OSTREECONF
|
|
|
|
log_info "rpm-ostree configured for signature verification"
|
|
|
|
# Create package verification script
|
|
cat > /usr/local/bin/verify-package.sh << 'VERIFYSCRIPT'
|
|
#!/bin/bash
|
|
# Package verification script for SAW
|
|
# Verifies package signatures against custom CA
|
|
|
|
CA_KEY="/etc/pki/rpm-gpg/RPM-GPG-KEY-custom-ca"
|
|
CRL_FILE="/etc/pki/ca-trust/source/anchors/crl.pem"
|
|
|
|
# Check if CA key exists
|
|
if [ ! -f "$CA_KEY" ]; then
|
|
echo "ERROR: CA key not found: $CA_KEY"
|
|
exit 1
|
|
fi
|
|
|
|
# Verify CRL if available
|
|
if [ -f "$CRL_FILE" ]; then
|
|
if ! openssl verify -CAfile "$CA_KEY" "$CRL_FILE" >/dev/null 2>&1; then
|
|
echo "WARNING: CRL signature verification failed"
|
|
fi
|
|
fi
|
|
|
|
# Verify package signature
|
|
for pkg in "$@"; do
|
|
if ! rpm --checksig "$pkg" >/dev/null 2>&1; then
|
|
echo "ERROR: Package signature verification failed: $pkg"
|
|
exit 1
|
|
fi
|
|
done
|
|
|
|
echo "All packages verified successfully"
|
|
exit 0
|
|
VERIFYSCRIPT
|
|
|
|
chmod +x /usr/local/bin/verify-package.sh
|
|
log_info "Package verification script created"
|
|
|
|
log_info "Package verification configured"
|
|
}
|
|
|
|
# Configure Update Approval System
|
|
#
|
|
# SECURITY RATIONALE:
|
|
# The update approval system provides a controlled workflow for applying
|
|
# system updates, preventing unauthorized or malicious updates.
|
|
#
|
|
# Why this is important:
|
|
# 1. Prevents unauthorized updates
|
|
# 2. Allows security team review before deployment
|
|
# 3. Enables testing in staging environment
|
|
# 4. Provides audit trail for updates
|
|
# 5. Prevents automatic deployment of compromised updates
|
|
#
|
|
# Implementation:
|
|
# - Auto-download updates (but don't apply)
|
|
# - Approval workflow before applying
|
|
# - Logging of all update activities
|
|
# - Easy rollback capability
|
|
#
|
|
# Reference: https://docs.fedoraproject.org/en-US/fedora-coreos/atomic-updates/
|
|
configure_updates() {
|
|
log_info "Configuring update approval system..."
|
|
|
|
# Configure rpm-ostree to auto-download
|
|
mkdir -p /etc/rpm-ostreed.conf.d
|
|
|
|
cat > /etc/rpm-ostreed.conf.d/auto-download.conf << 'AUTOUPDATE'
|
|
[Service]
|
|
# Auto-download updates
|
|
DownloadOnly=true
|
|
# Keep previous deployments for rollback
|
|
KeepOld=2
|
|
# Enable automatic cleanup
|
|
AutomaticCleanup=true
|
|
AUTOUPDATE
|
|
|
|
log_info "rpm-ostree auto-download configured"
|
|
|
|
# Create update approval script
|
|
cat > /usr/local/bin/approve-update.sh << 'APPROVESCRIPT'
|
|
#!/bin/bash
|
|
# Update approval script for SAW
|
|
# Shows update details and requires confirmation
|
|
|
|
echo "=== Fedora Kinoite SAW Update Approval ==="
|
|
echo ""
|
|
echo "This script will check for and apply system updates."
|
|
echo "All updates must be approved by authorized personnel."
|
|
echo ""
|
|
|
|
# Check for updates
|
|
echo "Checking for updates..."
|
|
sudo rpm-ostree update --check 2>&1 | tee /tmp/update-check.txt
|
|
|
|
if [ ${PIPESTATUS[0]} -ne 0 ]; then
|
|
echo "No updates available"
|
|
exit 0
|
|
fi
|
|
|
|
echo ""
|
|
echo "Review the update above."
|
|
echo "Type 'yes' to apply, 'no' to cancel:"
|
|
read -r response
|
|
|
|
if [ "$response" = "yes" ]; then
|
|
echo "Applying update..."
|
|
sudo rpm-ostree upgrade
|
|
else
|
|
echo "Update cancelled"
|
|
exit 0
|
|
fi
|
|
APPROVESCRIPT
|
|
|
|
chmod +x /usr/local/bin/approve-update.sh
|
|
|
|
# Set up cron job for daily update check
|
|
cat > /etc/cron.d/saw-updates << 'UPDATECRON'
|
|
# SAW Update Check - Daily at 6:00 AM
|
|
# Only downloads updates, does not apply them
|
|
0 6 * * * root /usr/bin/rpm-ostree update --check >> /var/log/saw-updates.log 2>&1
|
|
UPDATECRON
|
|
|
|
chmod 644 /etc/cron.d/saw-updates
|
|
log_info "Update cron job created"
|
|
|
|
log_info "Update approval system configured"
|
|
}
|
|
|
|
# Configure WireGuard VPN
|
|
#
|
|
# SECURITY RATIONALE:
|
|
# WireGuard VPN is the primary security mechanism for network traffic.
|
|
# All traffic is routed through the VPN tunnel, providing:
|
|
#
|
|
# 1. Encryption - All traffic is encrypted
|
|
# 2. Authentication - Only authorized clients can connect
|
|
# 3. Integrity - Tampering is detected
|
|
# 4. Privacy - ISP cannot see traffic content
|
|
# 5. Centralized logging - All traffic logged at VPN gateway
|
|
#
|
|
# The kill-switch feature ensures:
|
|
# - No traffic leaks when VPN is down
|
|
# - No direct internet access
|
|
# - All traffic must go through VPN
|
|
#
|
|
# Reference: https://www.wireguard.com/install/
|
|
configure_vpn() {
|
|
log_info "Configuring WireGuard VPN..."
|
|
|
|
# Check if WireGuard is installed
|
|
if ! command -v wg &> /dev/null; then
|
|
log_warning "WireGuard not installed, installing..."
|
|
dnf install -y wireguard-dkms wireguard-tools
|
|
fi
|
|
|
|
# Check if WireGuard config exists
|
|
if [ ! -f "$VPN_CONFIG_FILE" ]; then
|
|
log_warning "WireGuard config not found: $VPN_CONFIG_FILE"
|
|
log_warning "VPN configuration requires WireGuard config file"
|
|
|
|
# Create placeholder config
|
|
cat > "$VPN_CONFIG_FILE" << 'WGCONF'
|
|
[Interface]
|
|
# Replace with your WireGuard private key
|
|
PrivateKey = YOUR_PRIVATE_KEY_HERE
|
|
# Replace with your VPN interface IP
|
|
Address = 10.0.0.2/24
|
|
# Replace with your DNS server (VPN gateway)
|
|
DNS = 10.0.0.1
|
|
|
|
[Peer]
|
|
# Replace with your VPN server public key
|
|
PublicKey = YOUR_SERVER_PUBLIC_KEY_HERE
|
|
# Replace with your VPN server endpoint
|
|
Endpoint = vpn.example.com:51820
|
|
# Allow all traffic through VPN
|
|
AllowedIPs = 0.0.0.0/0, ::/0
|
|
WGCONF
|
|
|
|
log_warning "Placeholder WireGuard config created"
|
|
log_info "Please edit $VPN_CONFIG_FILE with your actual configuration"
|
|
return
|
|
fi
|
|
|
|
# Set proper permissions
|
|
chmod 600 "$VPN_CONFIG_FILE"
|
|
|
|
# Start WireGuard
|
|
systemctl enable wg-quick@wg0
|
|
systemctl start wg-quick@wg0 2>/dev/null || true
|
|
|
|
log_info "WireGuard VPN configured"
|
|
}
|
|
|
|
# Configure Security Hardening
|
|
#
|
|
# SECURITY RATIONALE:
|
|
# Additional security hardening measures to reduce attack surface:
|
|
#
|
|
# 1. Disable unnecessary services
|
|
# 2. Remove unnecessary packages
|
|
# 3. Configure secure boot
|
|
# 4. Enable kernel hardening
|
|
# 5. Configure secure umask
|
|
#
|
|
# These measures follow the principle of minimizing attack surface.
|
|
#
|
|
# Reference: https://fedoraproject.org/wiki/Security
|
|
configure_security_hardening() {
|
|
log_info "Applying security hardening..."
|
|
|
|
# Disable unnecessary services
|
|
local services_to_disable=(
|
|
"cups.service"
|
|
"bluetooth.service"
|
|
"cups-browsed.service"
|
|
"cups.path"
|
|
)
|
|
|
|
for service in "${services_to_disable[@]}"; do
|
|
if systemctl list-unit-files | grep -q "^$service"; then
|
|
systemctl disable "$service" 2>/dev/null || true
|
|
log_info "Disabled service: $service"
|
|
fi
|
|
done
|
|
|
|
# Configure secure umask
|
|
sed -i 's/UMASK=.*/UMASK=077/' /etc/login.defs 2>/dev/null || true
|
|
log_info "Secure umask configured"
|
|
|
|
# Configure kernel hardening
|
|
cat >> /etc/sysctl.d/99-saw-hardening.conf << 'KERNELHARDEN'
|
|
# Kernel hardening for SAW
|
|
# Disable IP forwarding
|
|
net.ipv4.ip_forward = 0
|
|
# Disable ICMP redirects
|
|
net.ipv4.conf.all.accept_redirects = 0
|
|
net.ipv4.conf.default.accept_redirects = 0
|
|
# Disable ICMP redirects
|
|
net.ipv4.conf.all.send_redirects = 0
|
|
net.ipv4.conf.default.send_redirects = 0
|
|
# Enable SYN cookies
|
|
net.ipv4.tcp_syncookies = 1
|
|
# Enable reverse path filtering
|
|
net.ipv4.conf.all.rp_filter = 1
|
|
net.ipv4.conf.default.rp_filter = 1
|
|
# Disable source routing
|
|
net.ipv4.conf.all.accept_source_route = 0
|
|
net.ipv4.conf.default.accept_source_route = 0
|
|
# Disable magic sysreq
|
|
kernel.sysrq = 0
|
|
# Enable Execshield
|
|
kernel.exec-shield = 1
|
|
# Enable randomize addresses
|
|
kernel.randomize_va_space = 2
|
|
# Configure swappiness
|
|
vm.swappiness = 1
|
|
# Disable IPv6 if not needed (optional)
|
|
# net.ipv6.conf.all.disable_ipv6 = 1
|
|
KERNELHARDEN
|
|
|
|
log_info "Kernel hardening configured"
|
|
|
|
# Apply sysctl settings
|
|
sysctl -p /etc/sysctl.d/99-saw-hardening.conf 2>/dev/null || true
|
|
|
|
log_info "Security hardening applied"
|
|
}
|
|
|
|
# Create Security Audit Script
|
|
#
|
|
# SECURITY RATIONALE:
|
|
# Regular security audits are essential for maintaining security posture.
|
|
# This script provides:
|
|
#
|
|
# 1. System state verification
|
|
# 2. Security control checks
|
|
# 3. Log analysis
|
|
# 4. Compliance verification
|
|
# 5. Alert generation
|
|
#
|
|
# Run this script regularly to verify security controls are still in place.
|
|
create_audit_script() {
|
|
log_info "Creating security audit script..."
|
|
|
|
cat > /usr/local/bin/security-audit.sh << 'AUDITSCRIPT'
|
|
#!/bin/bash
|
|
# Security Audit Script for SAW
|
|
# Run this script regularly to verify security controls
|
|
|
|
echo "=== Fedora Kinoite SAW Security Audit ==="
|
|
echo "Date: $(date)"
|
|
echo ""
|
|
|
|
# Check system state
|
|
echo "1. System State:"
|
|
rpm-ostree status | head -5
|
|
echo ""
|
|
|
|
# Check sudo access
|
|
echo "2. Sudo Access:"
|
|
if id -nG "$USER" | grep -q wheel; then
|
|
echo " WARNING: User is in wheel group (sudo enabled)"
|
|
else
|
|
echo " OK: User is not in wheel group"
|
|
fi
|
|
echo ""
|
|
|
|
# Check SELinux
|
|
echo "3. SELinux Status:"
|
|
sestatus | grep "Current mode"
|
|
echo ""
|
|
|
|
# Check auditd
|
|
echo "4. Auditd Status:"
|
|
if systemctl is-active auditd >/dev/null 2>&1; then
|
|
echo " OK: Auditd running"
|
|
else
|
|
echo " WARNING: Auditd not running"
|
|
fi
|
|
echo ""
|
|
|
|
# Check firewall
|
|
echo "5. Firewall Status:"
|
|
if firewall-cmd --state 2>/dev/null | grep -q "running"; then
|
|
echo " OK: Firewall running"
|
|
else
|
|
echo " WARNING: Firewall not running"
|
|
fi
|
|
echo ""
|
|
|
|
# Check VPN
|
|
echo "6. VPN Status:"
|
|
if command -v wg &> /dev/null; then
|
|
wg show 2>/dev/null || echo " WireGuard not configured"
|
|
else
|
|
echo " WireGuard not installed"
|
|
fi
|
|
echo ""
|
|
|
|
# Check for recent audit events
|
|
echo "7. Recent Audit Events:"
|
|
ausearch -m all -ts recent 2>/dev/null | head -10 || echo " No recent audit events"
|
|
echo ""
|
|
|
|
# Check for failed sudo attempts
|
|
echo "8. Failed Sudo Attempts:"
|
|
grep "authentication failure" /var/log/secure 2>/dev/null | tail -5 || echo " No failed sudo attempts"
|
|
echo ""
|
|
|
|
# Check package verification
|
|
echo "9. Package Verification:"
|
|
if [ -f /etc/pki/rpm-gpg/RPM-GPG-KEY-custom-ca ]; then
|
|
echo " OK: CA certificate installed"
|
|
else
|
|
echo " WARNING: CA certificate not found"
|
|
fi
|
|
echo ""
|
|
|
|
# Check for packages with revoked certificates
|
|
echo "10. Certificate Revocation Check:"
|
|
if command -v rpm &> /dev/null; then
|
|
revoked=$(rpm -qa --queryformat='%{NAME} %{SIGPGP:pgpsig}\n' 2>/dev/null | grep "0x0" | wc -l)
|
|
if [ "$revoked" -gt 0 ]; then
|
|
echo " WARNING: $revoked packages have revoked certificates"
|
|
else
|
|
echo " OK: No revoked certificates found"
|
|
fi
|
|
fi
|
|
echo ""
|
|
|
|
# Summary
|
|
echo "=== Audit Complete ==="
|
|
echo ""
|
|
echo "Recommendations:"
|
|
echo "- Run this script weekly"
|
|
echo "- Review audit logs regularly"
|
|
echo "- Update CA certificate monthly"
|
|
echo "- Test rollback capability quarterly"
|
|
AUDITSCRIPT
|
|
|
|
chmod +x /usr/local/bin/security-audit.sh
|
|
log_info "Security audit script created"
|
|
}
|
|
|
|
# Mark lockdown complete
|
|
mark_complete() {
|
|
log_info "Marking lockdown as complete..."
|
|
|
|
# Create completion marker
|
|
mkdir -p /etc/saw
|
|
date > "$LOCKDOWN_COMPLETE_FILE"
|
|
|
|
# Create documentation
|
|
cat > /etc/saw/lockdown_documentation.txt << 'DOCEOF'
|
|
# Fedora Kinoite SAW Lockdown Documentation
|
|
# Generated: $(date)
|
|
#
|
|
# This file documents the security controls implemented during lockdown.
|
|
|
|
## Security Controls Implemented
|
|
|
|
1. Sudo Access Disabled
|
|
- User removed from wheel group
|
|
- Root SSH login disabled
|
|
- PAM configured for MFA
|
|
|
|
2. SELinux Enforcing
|
|
- Mandatory access control enabled
|
|
- All security policies enforced
|
|
- Violations logged
|
|
|
|
3. Audit Daemon
|
|
- Comprehensive logging enabled
|
|
- Privilege escalation logged
|
|
- Critical file access monitored
|
|
|
|
4. Firewall
|
|
- Default deny policy
|
|
- Only WireGuard allowed
|
|
- DNS lockdown configured
|
|
|
|
5. DNS Lockdown
|
|
- Only VPN gateway DNS allowed
|
|
- Private DNS blocked
|
|
- All DNS through VPN tunnel
|
|
|
|
6. Package Verification
|
|
- GPG signature verification enabled
|
|
- CA key imported
|
|
- CRL checking enabled
|
|
|
|
7. Update Approval System
|
|
- Auto-download enabled
|
|
- Approval required for updates
|
|
- Audit trail maintained
|
|
|
|
## Verification Commands
|
|
|
|
- Check sudo: sudo whoami
|
|
- Check SELinux: sestatus
|
|
- Check auditd: sudo systemctl status auditd
|
|
- Check firewall: sudo firewall-cmd --list-all
|
|
- Check VPN: wg show
|
|
- Check DNS: cat /etc/resolv.conf
|
|
- Run audit: sudo /usr/local/bin/security-audit.sh
|
|
|
|
## References
|
|
|
|
- NSA Linux Security Hardening Guide
|
|
- CIS Fedora Benchmarks
|
|
- Fedora Security Documentation
|
|
DOCEOF
|
|
|
|
log_info "Lockdown complete"
|
|
}
|
|
|
|
# Main execution
|
|
main() {
|
|
echo ""
|
|
echo "=========================================="
|
|
echo " Fedora Kinoite SAW Lockdown Script"
|
|
echo " Version: 1.0"
|
|
echo " Date: 2026-04-02"
|
|
echo "=========================================="
|
|
echo ""
|
|
|
|
# Setup
|
|
check_root
|
|
setup_logging
|
|
setup_config_dir
|
|
|
|
# Backup
|
|
backup_config
|
|
|
|
# Security controls
|
|
disable_sudo
|
|
enable_selinux
|
|
enable_auditd
|
|
configure_firewall
|
|
configure_dns
|
|
configure_package_verification
|
|
configure_updates
|
|
configure_vpn
|
|
configure_security_hardening
|
|
create_audit_script
|
|
|
|
# Complete
|
|
mark_complete
|
|
|
|
echo ""
|
|
echo "=========================================="
|
|
echo " Lockdown Complete!"
|
|
echo "=========================================="
|
|
echo ""
|
|
echo "Next steps:"
|
|
echo "1. Review /var/log/saw-lockdown.log"
|
|
echo "2. Run: sudo /usr/local/bin/security-audit.sh"
|
|
echo "3. Edit WireGuard config: /etc/wireguard/wg0.conf"
|
|
echo "4. Test all security controls"
|
|
echo ""
|
|
echo "For more information, see:"
|
|
echo "- /etc/saw/lockdown_documentation.txt"
|
|
echo "- /usr/local/bin/security-audit.sh"
|
|
echo ""
|
|
}
|
|
|
|
# Run main function
|
|
main "$@" |