base ground
This commit is contained in:
parent
e279804b1d
commit
89b7074268
665
INSTALLATION_GUIDE.md
Normal file
665
INSTALLATION_GUIDE.md
Normal file
@ -0,0 +1,665 @@
|
||||
# Installation Guide for Fedora Kinoite SAW
|
||||
|
||||
## Overview
|
||||
|
||||
This guide provides step-by-step instructions for building and installing a Secure Air-Gapped Workstation (SAW) using Fedora Kinoite with custom CA trust, WireGuard VPN, and strict security controls.
|
||||
|
||||
**Prerequisites:**
|
||||
- 8GB+ USB drive
|
||||
- Computer to build ISO (can be different from target machine)
|
||||
- VPN gateway access information
|
||||
- CA certificate and signing keys
|
||||
- Custom packages (if any)
|
||||
|
||||
---
|
||||
|
||||
## Part 1: Building the Custom ISO
|
||||
|
||||
### Step 1: Install Build Tools
|
||||
|
||||
On the machine where you'll build the ISO (can be different from target):
|
||||
|
||||
```bash
|
||||
# Install required tools
|
||||
sudo dnf install -y lorax anaconda-tools createrepo_c
|
||||
```
|
||||
|
||||
### Step 2: Prepare Repository Structure
|
||||
|
||||
```bash
|
||||
# Create directory structure
|
||||
mkdir -p ~/saw-build/{iso,packages,config}
|
||||
|
||||
# Download base Fedora Kinoite ISO
|
||||
# Visit: https://kinoite.fedoraproject.org/
|
||||
# Download latest ISO
|
||||
```
|
||||
|
||||
### Step 3: Customize Kickstart File
|
||||
|
||||
Edit `kickstart/kinoite-saw.ks`:
|
||||
|
||||
**Required Customizations:**
|
||||
|
||||
1. **CA Certificate Path:**
|
||||
```bash
|
||||
# Change this line with your CA cert path
|
||||
%include /tmp/kickstart-ca-certificate.ks
|
||||
```
|
||||
|
||||
2. **Package List:**
|
||||
```bash
|
||||
# Add your custom packages
|
||||
# Remove unnecessary packages
|
||||
```
|
||||
|
||||
3. **VPN Gateway:**
|
||||
```bash
|
||||
# Update VPN configuration
|
||||
# Add your WireGuard config
|
||||
```
|
||||
|
||||
4. **User Configuration:**
|
||||
```bash
|
||||
# Set up your user account
|
||||
user --name=saw-user --password=changeme
|
||||
```
|
||||
|
||||
### Step 4: Add CA Certificate
|
||||
|
||||
Place your CA certificate in `package-verification/ca/ca.crt`:
|
||||
|
||||
```bash
|
||||
# Your CA certificate should be in PEM format
|
||||
# This will be installed to /etc/pki/ca-trust/source/anchors/
|
||||
```
|
||||
|
||||
### Step 5: Build ISO
|
||||
|
||||
```bash
|
||||
cd ~/saw-build
|
||||
|
||||
# Copy kickstart
|
||||
cp /path/to/kickstart/kinoite-saw.ks .
|
||||
|
||||
# Build ISO using lorax
|
||||
sudo lorax -s file:///path/to/kinoite-iso -p Fedora-Kinoite-SAW -v "SAW 1.0" \
|
||||
--repo Fedora-Kinoite=file:///path/to/kinoite-iso \
|
||||
--arch x86_64 \
|
||||
--no-compress \
|
||||
--variant Server \
|
||||
kinoite-saw.ks
|
||||
|
||||
# Output will be in ~/saw-build/output/
|
||||
```
|
||||
|
||||
**Alternative: Using Pungi (Fedora Build System)**
|
||||
|
||||
```bash
|
||||
# Install pungi
|
||||
sudo dnf install -y pungi
|
||||
|
||||
# Create compose configuration
|
||||
cat > compose-config.toml << EOF
|
||||
[compose]
|
||||
release = "SAW 1.0"
|
||||
version = "1.0"
|
||||
distro = "Fedora-Kinoite-43"
|
||||
base_arches = ["x86_64"]
|
||||
|
||||
[packages]
|
||||
# Add your custom packages here
|
||||
EOF
|
||||
|
||||
# Build
|
||||
sudo pungi-gather --compose-dir compose
|
||||
sudo pungi-make-iso --compose-dir compose
|
||||
```
|
||||
|
||||
### Step 6: Test ISO
|
||||
|
||||
```bash
|
||||
# Test in VM first
|
||||
qemu-system-x86_64 -m 4096 -cdrom output/Fedora-Kinoite-SAW.iso
|
||||
|
||||
# Or use VirtualBox/Virtual Machine Manager
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Part 2: Installation to Target Machine
|
||||
|
||||
### Step 1: Prepare Installation Media
|
||||
|
||||
```bash
|
||||
# Identify USB device
|
||||
lsblk
|
||||
|
||||
# Write ISO to USB (replace /dev/sdX with your device)
|
||||
sudo dd if=output/Fedora-Kinoite-SAW.iso of=/dev/sdX bs=4M status=progress
|
||||
sync
|
||||
|
||||
# Verify
|
||||
lsblk /dev/sdX
|
||||
```
|
||||
|
||||
### Step 2: Boot Installation Media
|
||||
|
||||
```bash
|
||||
# Insert USB into target machine
|
||||
# Boot and select USB as boot device
|
||||
# Press 'e' to edit boot options if needed
|
||||
# Add 'inst.ks=file:///run/media/user/kickstart.ks' for automated install
|
||||
```
|
||||
|
||||
### Step 3: Installation Wizard
|
||||
|
||||
1. **Select Installation Destination:**
|
||||
- Choose disk to install to
|
||||
- Select "I will configure partitioning"
|
||||
- Create partitions:
|
||||
- `/` - 15GB minimum (20GB recommended)
|
||||
- `swap` - 2-4GB
|
||||
- `/home` - remaining space
|
||||
- `/boot/efi` - 512MB (for UEFI)
|
||||
|
||||
2. **Configure Network:**
|
||||
- Enable network interface
|
||||
- Configure VPN if needed for package installation
|
||||
|
||||
3. **Set Root Password:**
|
||||
- Use strong password
|
||||
- Store securely
|
||||
|
||||
4. **Create User:**
|
||||
- Username: `saw-user` (or your preferred name)
|
||||
- Set strong password
|
||||
- Enable sudo access temporarily for post-install setup
|
||||
|
||||
5. **Begin Installation:**
|
||||
- Wait for installation to complete
|
||||
- Remove USB when prompted
|
||||
|
||||
### Step 4: First Boot
|
||||
|
||||
```bash
|
||||
# Complete initial setup
|
||||
# Configure timezone, language, etc.
|
||||
# Log in with your user account
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Part 3: Post-Installation Lockdown
|
||||
|
||||
### Step 1: Initial System Update
|
||||
|
||||
```bash
|
||||
# Check for updates
|
||||
sudo rpm-ostree status
|
||||
|
||||
# Apply updates
|
||||
sudo rpm-ostree upgrade
|
||||
|
||||
# Reboot if needed
|
||||
sudo reboot
|
||||
```
|
||||
|
||||
### Step 2: Run Lockdown Script
|
||||
|
||||
```bash
|
||||
# Copy lockdown script to system
|
||||
sudo cp post-install/lockdown.sh /usr/local/bin/
|
||||
sudo chmod +x /usr/local/bin/lockdown.sh
|
||||
|
||||
# Run lockdown
|
||||
sudo /usr/local/bin/lockdown.sh
|
||||
```
|
||||
|
||||
**What the lockdown script does:**
|
||||
|
||||
1. **Removes user from wheel group** (no sudo access)
|
||||
2. **Disables root SSH login**
|
||||
3. **Configures PAM for MFA** (if enabled)
|
||||
4. **Enables SELinux enforcing mode**
|
||||
5. **Enables auditd**
|
||||
6. **Configures firewall**
|
||||
7. **Sets up VPN**
|
||||
8. **Configures DNS lockdown**
|
||||
|
||||
### Step 3: Verify Installation
|
||||
|
||||
```bash
|
||||
# Check sudo access (should be denied)
|
||||
sudo -l
|
||||
|
||||
# Check user groups (should not include wheel)
|
||||
id
|
||||
|
||||
# Check firewall
|
||||
sudo firewall-cmd --list-all
|
||||
|
||||
# Check SELinux
|
||||
sestatus
|
||||
|
||||
# Check auditd
|
||||
sudo systemctl status auditd
|
||||
|
||||
# Check VPN
|
||||
wg show
|
||||
```
|
||||
|
||||
### Step 4: Configure VPN
|
||||
|
||||
```bash
|
||||
# Copy VPN configuration
|
||||
sudo cp package-verification/ca/ca.crt /etc/pki/ca-trust/source/anchors/
|
||||
sudo update-ca-trust
|
||||
|
||||
# Copy WireGuard config
|
||||
sudo cp config/wireguard/wg0.conf /etc/wireguard/
|
||||
sudo chmod 600 /etc/wireguard/wg0.conf
|
||||
|
||||
# Start WireGuard
|
||||
sudo wg-quick up wg0
|
||||
|
||||
# Check VPN connection
|
||||
wg show
|
||||
ping -c 3 <vpn-gateway-ip>
|
||||
```
|
||||
|
||||
### Step 5: Test DNS
|
||||
|
||||
```bash
|
||||
# Test DNS resolution (should use VPN gateway)
|
||||
nslookup google.com
|
||||
|
||||
# Check /etc/resolv.conf
|
||||
cat /etc/resolv.conf
|
||||
|
||||
# Test that non-VPN DNS fails
|
||||
# (should timeout or fail)
|
||||
```
|
||||
|
||||
### Step 6: Test Firewall
|
||||
|
||||
```bash
|
||||
# Check firewall status
|
||||
sudo firewall-cmd --list-all
|
||||
|
||||
# Test outbound connection (should go through VPN)
|
||||
curl -v https://check.torproject.org
|
||||
|
||||
# Test direct connection (should fail)
|
||||
curl -v https://8.8.8.8
|
||||
```
|
||||
|
||||
### Step 7: Verify Package Trust
|
||||
|
||||
```bash
|
||||
# Check CA certificate is installed
|
||||
ls /etc/pki/rpm-gpg/
|
||||
|
||||
# Verify DNF configuration
|
||||
cat /etc/dnf/dnf.conf
|
||||
|
||||
# Test package verification
|
||||
sudo dnf makecache
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Part 4: Verify Security Controls
|
||||
|
||||
### Step 1: Verify Sudo is Disabled
|
||||
|
||||
```bash
|
||||
# As regular user, try sudo
|
||||
sudo whoami
|
||||
# Expected: "user is not in the sudoers file."
|
||||
|
||||
# Check user groups
|
||||
id
|
||||
# Expected: Should not show 'wheel' group
|
||||
```
|
||||
|
||||
### Step 2: Verify SELinux
|
||||
|
||||
```bash
|
||||
# Check SELinux status
|
||||
sestatus
|
||||
|
||||
# Expected: "Current mode: enforcing"
|
||||
# Expected: "SELinux enforcement: Enabled"
|
||||
|
||||
# Check for denials
|
||||
sudo ausearch -m avc -ts recent
|
||||
```
|
||||
|
||||
### Step 3: Verify Audit Logging
|
||||
|
||||
```bash
|
||||
# Check auditd status
|
||||
sudo systemctl status auditd
|
||||
|
||||
# Test audit logging
|
||||
sudo auditctl -l
|
||||
```
|
||||
|
||||
### Step 4: Verify Firewall
|
||||
|
||||
```bash
|
||||
# Check firewall rules
|
||||
sudo firewall-cmd --list-all
|
||||
|
||||
# Check active zones
|
||||
sudo firewall-cmd --list-zones
|
||||
|
||||
# Verify WireGuard is allowed
|
||||
sudo firewall-cmd --list-services --zone=wg0
|
||||
```
|
||||
|
||||
### Step 5: Verify DNS
|
||||
|
||||
```bash
|
||||
# Check DNS configuration
|
||||
cat /etc/resolv.conf
|
||||
|
||||
# Test DNS resolution
|
||||
nslookup example.com
|
||||
|
||||
# Verify DNS goes through VPN
|
||||
sudo tcpdump -i any port 53
|
||||
```
|
||||
|
||||
### Step 6: Verify VPN
|
||||
|
||||
```bash
|
||||
# Check WireGuard interface
|
||||
wg show
|
||||
|
||||
# Check routing
|
||||
ip route show
|
||||
|
||||
# Verify all traffic goes through VPN
|
||||
ip route show table 51820
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Part 5: Install Custom Packages
|
||||
|
||||
### Step 1: Prepare Package Repository
|
||||
|
||||
```bash
|
||||
# Create local repository
|
||||
sudo mkdir -p /opt/packages
|
||||
sudo cp /path/to/custom-packages/*.rpm /opt/packages/
|
||||
|
||||
# Create repository metadata
|
||||
sudo createrepo /opt/packages/
|
||||
|
||||
# Create repo file
|
||||
cat > /etc/yum.repos.d/custom.repo << EOF
|
||||
[custom-packages]
|
||||
name=Custom Packages
|
||||
baseurl=file:///opt/packages
|
||||
enabled=1
|
||||
gpgcheck=1
|
||||
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-your-ca
|
||||
repo_gpgcheck=1
|
||||
EOF
|
||||
|
||||
# Import CA key
|
||||
sudo rpm --import /etc/pki/rpm-gpg/RPM-GPG-KEY-your-ca
|
||||
```
|
||||
|
||||
### Step 2: Install Custom Packages
|
||||
|
||||
```bash
|
||||
# Make cache
|
||||
sudo dnf makecache
|
||||
|
||||
# Install custom package
|
||||
sudo dnf install your-custom-package
|
||||
|
||||
# Verify signature
|
||||
rpm --checksig your-custom-package
|
||||
```
|
||||
|
||||
### Step 3: Install Flatpak Applications
|
||||
|
||||
```bash
|
||||
# Add Flathub (if needed)
|
||||
flatpak remote-add --user --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo
|
||||
|
||||
# Install applications
|
||||
flatpak install --user flathub org.gnome.Firefox
|
||||
flatpak install --user flathub org.libreoffice.LibreOffice
|
||||
flatpak install --user flathub org.mozilla.thunderbird
|
||||
|
||||
# Verify installation
|
||||
flatpak list
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Part 6: Configure Update System
|
||||
|
||||
### Step 1: Set Up Auto-Download
|
||||
|
||||
```bash
|
||||
# Configure rpm-ostree to auto-download
|
||||
cat > /etc/rpm-ostreed.conf << EOF
|
||||
[Service]
|
||||
DownloadOnly=true
|
||||
EOF
|
||||
|
||||
# Enable auto-download service
|
||||
sudo systemctl enable rpm-ostreed
|
||||
```
|
||||
|
||||
### Step 2: Configure Approval Workflow
|
||||
|
||||
```bash
|
||||
# Create approval script
|
||||
cat > /usr/local/bin/approve-update.sh << 'EOF'
|
||||
#!/bin/bash
|
||||
# Update approval script
|
||||
# Shows update details and requires confirmation
|
||||
|
||||
sudo rpm-ostree update --check
|
||||
|
||||
echo "Review the update above."
|
||||
echo "Type 'yes' to apply, 'no' to cancel:"
|
||||
read response
|
||||
|
||||
if [ "$response" = "yes" ]; then
|
||||
sudo rpm-ostree upgrade
|
||||
else
|
||||
echo "Update cancelled"
|
||||
fi
|
||||
EOF
|
||||
|
||||
chmod +x /usr/local/bin/approve-update.sh
|
||||
```
|
||||
|
||||
### Step 3: Set Up Daily CRL Update
|
||||
|
||||
```bash
|
||||
# Copy CRL updater script
|
||||
sudo cp scripts/daily-crl-update.sh /usr/local/bin/
|
||||
sudo chmod +x /usr/local/bin/daily-crl-update.sh
|
||||
|
||||
# Set up cron job
|
||||
sudo crontab -l > mycron || echo "" > mycron
|
||||
echo "0 2 * * * /usr/local/bin/daily-crl-update.sh >> /var/log/crl-update.log 2>&1" >> mycron
|
||||
sudo crontab mycron
|
||||
sudo rm mycron
|
||||
|
||||
# Verify cron job
|
||||
sudo crontab -l
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Part 7: Final Verification
|
||||
|
||||
### Step 1: Run Security Audit
|
||||
|
||||
```bash
|
||||
# Copy audit script
|
||||
sudo cp scripts/security-audit.sh /usr/local/bin/
|
||||
sudo chmod +x /usr/local/bin/security-audit.sh
|
||||
|
||||
# Run audit
|
||||
sudo /usr/local/bin/security-audit.sh
|
||||
```
|
||||
|
||||
### Step 2: Test Full System
|
||||
|
||||
```bash
|
||||
# Test VPN connectivity
|
||||
curl -v https://check.torproject.org
|
||||
|
||||
# Test DNS lockdown
|
||||
nslookup google.com
|
||||
|
||||
# Test firewall
|
||||
curl -v https://8.8.8.8
|
||||
|
||||
# Test sudo is disabled
|
||||
sudo whoami
|
||||
|
||||
# Test package verification
|
||||
sudo dnf check-update
|
||||
```
|
||||
|
||||
### Step 3: Document Configuration
|
||||
|
||||
```bash
|
||||
# Save system status
|
||||
sudo rpm-ostree status > /root/system-status.txt
|
||||
|
||||
# Save firewall rules
|
||||
sudo firewall-cmd --list-all > /root/firewall-rules.txt
|
||||
|
||||
# Save VPN config
|
||||
sudo wg show > /root/wireguard-status.txt
|
||||
|
||||
# Save audit logs
|
||||
sudo ausearch -m all -ts recent > /root/audit-log.txt
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting Installation Issues
|
||||
|
||||
### Issue: ISO Build Fails
|
||||
|
||||
**Symptoms:** `lorax` fails with error
|
||||
|
||||
**Solution:**
|
||||
```bash
|
||||
# Check ISO path
|
||||
ls -la /path/to/kinoite-iso
|
||||
|
||||
# Check kickstart syntax
|
||||
ksvalidator kinoite-saw.ks
|
||||
|
||||
# Try with --no-compress flag
|
||||
sudo lorax --no-compress ...
|
||||
```
|
||||
|
||||
### Issue: Installation Hangs
|
||||
|
||||
**Symptoms:** Installation process hangs
|
||||
|
||||
**Solution:**
|
||||
```bash
|
||||
# Boot with debug kernel
|
||||
# Add to boot parameters: inst.debug inst.vnc inst.sshd
|
||||
|
||||
# Check disk space
|
||||
df -h
|
||||
|
||||
# Check memory
|
||||
free -h
|
||||
```
|
||||
|
||||
### Issue: Package Verification Fails
|
||||
|
||||
**Symptoms:** `DNF: signature verification failed`
|
||||
|
||||
**Solution:**
|
||||
```bash
|
||||
# Verify CA certificate
|
||||
openssl x509 -in /etc/pki/rpm-gpg/RPM-GPG-KEY-your-ca -text -noout
|
||||
|
||||
# Re-import key
|
||||
sudo rpm --import /etc/pki/rpm-gpg/RPM-GPG-KEY-your-ca
|
||||
|
||||
# Check package signature
|
||||
rpm --checksig package.rpm
|
||||
```
|
||||
|
||||
### Issue: VPN Not Connecting
|
||||
|
||||
**Symptoms:** `wg-quick up wg0` fails
|
||||
|
||||
**Solution:**
|
||||
```bash
|
||||
# Check config
|
||||
cat /etc/wireguard/wg0.conf
|
||||
|
||||
# Check firewall
|
||||
sudo firewall-cmd --list-all
|
||||
|
||||
# Check routing
|
||||
ip route show
|
||||
|
||||
# Test connectivity
|
||||
ping -c 3 <server-ip>
|
||||
```
|
||||
|
||||
### Issue: DNS Not Working
|
||||
|
||||
**Symptoms:** Cannot resolve domain names
|
||||
|
||||
**Solution:**
|
||||
```bash
|
||||
# Check resolv.conf
|
||||
cat /etc/resolv.conf
|
||||
|
||||
# Check dnsmasq
|
||||
sudo systemctl status dnsmasq
|
||||
|
||||
# Check firewall
|
||||
sudo firewall-cmd --list-services
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Next Steps
|
||||
|
||||
After successful installation:
|
||||
|
||||
1. **Test all applications** - Verify everything works as expected
|
||||
2. **Configure backup** - Set up backup for important data
|
||||
3. **Document procedures** - Write your own operational procedures
|
||||
4. **Set up monitoring** - Configure log monitoring
|
||||
5. **Create recovery plan** - Document recovery procedures
|
||||
|
||||
---
|
||||
|
||||
## References
|
||||
|
||||
- [Fedora Kinoite Installation Guide](https://kinoite.fedoraproject.org/)
|
||||
- [rpm-ostree Documentation](https://docs.fedoraproject.org/en-US/fedora-coreos/atomic-updates/)
|
||||
- [WireGuard Documentation](https://www.wireguard.com/install/)
|
||||
- [DNF Configuration](https://dnf.readthedocs.io/en/latest/conf.html)
|
||||
|
||||
---
|
||||
|
||||
**Previous:** [README.md](README.md)
|
||||
**Next:** [Configuration Details](CONFIGURATION_DETAILS.md)
|
||||
249
config/wireguard/README.md
Normal file
249
config/wireguard/README.md
Normal file
@ -0,0 +1,249 @@
|
||||
# WireGuard VPN Configuration Examples
|
||||
|
||||
## Overview
|
||||
|
||||
This directory contains example WireGuard VPN configurations for the SAW implementation.
|
||||
|
||||
## Files
|
||||
|
||||
```
|
||||
config/wireguard/
|
||||
├── wg0.conf.example # Example WireGuard configuration
|
||||
├── README.md # This file
|
||||
└── setup-vpn.sh # VPN setup script (optional)
|
||||
```
|
||||
|
||||
## Quick Start
|
||||
|
||||
### 1. Generate Keys
|
||||
|
||||
```bash
|
||||
# Generate server keys
|
||||
wg genkey | tee server_private.key | wg pubkey > server_public.key
|
||||
|
||||
# Generate client keys
|
||||
wg genkey | tee client_private.key | wg pubkey > client_public.key
|
||||
```
|
||||
|
||||
### 2. Configure Server
|
||||
|
||||
Create `/etc/wireguard/wg0.conf` on VPN server:
|
||||
|
||||
```ini
|
||||
[Interface]
|
||||
PrivateKey = <server_private_key>
|
||||
Address = 10.0.0.1/24
|
||||
ListenPort = 51820
|
||||
|
||||
[Peer]
|
||||
PublicKey = <client_public_key>
|
||||
AllowedIPs = 10.0.0.2/32
|
||||
```
|
||||
|
||||
### 3. Configure Client (SAW)
|
||||
|
||||
Copy the example config and update with your keys:
|
||||
|
||||
```bash
|
||||
cp config/wireguard/wg0.conf.example /etc/wireguard/wg0.conf
|
||||
|
||||
# Edit the configuration
|
||||
nano /etc/wireguard/wg0.conf
|
||||
```
|
||||
|
||||
### 4. Start VPN
|
||||
|
||||
```bash
|
||||
# Start WireGuard
|
||||
sudo wg-quick up wg0
|
||||
|
||||
# Check status
|
||||
sudo wg show
|
||||
|
||||
# Enable auto-start
|
||||
sudo systemctl enable wg-quick@wg0
|
||||
```
|
||||
|
||||
## Example Configuration
|
||||
|
||||
### wg0.conf.example
|
||||
|
||||
```ini
|
||||
[Interface]
|
||||
# Your WireGuard private key (generated with: wg genkey)
|
||||
PrivateKey = YOUR_PRIVATE_KEY_HERE
|
||||
# VPN interface IP and subnet
|
||||
Address = 10.0.0.2/24
|
||||
# DNS servers (should be VPN gateway)
|
||||
DNS = 10.0.0.1
|
||||
|
||||
[Peer]
|
||||
# VPN server's public key
|
||||
PublicKey = YOUR_SERVER_PUBLIC_KEY_HERE
|
||||
# VPN server's endpoint (IP:port)
|
||||
Endpoint = vpn.example.com:51820
|
||||
# Which IPs to route through VPN (0.0.0.0/0 = all traffic)
|
||||
AllowedIPs = 0.0.0.0/0, ::/0
|
||||
# Keep connection alive (optional)
|
||||
PersistentKeepalive = 25
|
||||
```
|
||||
|
||||
### Configuration Parameters Explained
|
||||
|
||||
#### [Interface] Section
|
||||
|
||||
| Parameter | Description | Example |
|
||||
|-----------|-------------|---------|
|
||||
| `PrivateKey` | Your WireGuard private key | Generated with `wg genkey` |
|
||||
| `Address` | VPN interface IP and subnet | `10.0.0.2/24` |
|
||||
| `ListenPort` | Port to listen on (server only) | `51820` |
|
||||
| `DNS` | DNS servers to use | `10.0.0.1` |
|
||||
|
||||
#### [Peer] Section
|
||||
|
||||
| Parameter | Description | Example |
|
||||
|-----------|-------------|---------|
|
||||
| `PublicKey` | VPN server's public key | Generated on server |
|
||||
| `Endpoint` | VPN server address and port | `vpn.example.com:51820` |
|
||||
| `AllowedIPs` | IPs to route through VPN | `0.0.0.0/0, ::/0` |
|
||||
| `PersistentKeepalive` | Keep connection alive (optional) | `25` |
|
||||
|
||||
## Security Considerations
|
||||
|
||||
### Key Management
|
||||
|
||||
1. **Generate keys securely**
|
||||
- Use `wg genkey` (cryptographically secure)
|
||||
- Never share private keys
|
||||
- Store keys in encrypted storage
|
||||
|
||||
2. **Use strong encryption**
|
||||
- WireGuard uses ChaCha20-Poly1305 (default)
|
||||
- Curve25519 key exchange (default)
|
||||
- No weak algorithms
|
||||
|
||||
3. **Restrict AllowedIPs**
|
||||
- Only allow necessary subnets
|
||||
- Use `/32` for single IPs
|
||||
- Avoid `0.0.0.0/0` if not needed
|
||||
|
||||
### Network Security
|
||||
|
||||
1. **Firewall configuration**
|
||||
- Allow WireGuard port (UDP 51820)
|
||||
- Block other ports
|
||||
- Implement egress filtering
|
||||
|
||||
2. **DNS security**
|
||||
- Use VPN gateway DNS
|
||||
- Block external DNS
|
||||
- Consider DNS-over-TLS
|
||||
|
||||
3. **Keep-alive settings**
|
||||
- Use `PersistentKeepalive` for NAT traversal
|
||||
- Set appropriate interval (25 seconds recommended)
|
||||
- Disable if not needed
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### VPN Not Connecting
|
||||
|
||||
```bash
|
||||
# Check WireGuard status
|
||||
sudo wg show
|
||||
|
||||
# Check configuration
|
||||
sudo wg-quick diff wg0.conf
|
||||
|
||||
# Check logs
|
||||
sudo journalctl -u wg-quick@wg0
|
||||
|
||||
# Test connectivity
|
||||
ping -I wg0 <server-ip>
|
||||
```
|
||||
|
||||
### DNS Not Working
|
||||
|
||||
```bash
|
||||
# Check DNS configuration
|
||||
cat /etc/resolv.conf
|
||||
|
||||
# Test DNS resolution
|
||||
nslookup example.com
|
||||
|
||||
# Check firewall
|
||||
sudo firewall-cmd --list-all
|
||||
```
|
||||
|
||||
### Routing Issues
|
||||
|
||||
```bash
|
||||
# Check routing table
|
||||
ip route show
|
||||
|
||||
# Check WireGuard routes
|
||||
ip route show table 51820
|
||||
|
||||
# Test routing
|
||||
ping -I wg0 <external-ip>
|
||||
```
|
||||
|
||||
## Advanced Configuration
|
||||
|
||||
### Split Tunneling
|
||||
|
||||
Route only specific traffic through VPN:
|
||||
|
||||
```ini
|
||||
[Peer]
|
||||
# Only route corporate network
|
||||
AllowedIPs = 192.168.1.0/24, 10.0.0.0/8
|
||||
```
|
||||
|
||||
### Multi-Hop VPN
|
||||
|
||||
Chain multiple VPN servers:
|
||||
|
||||
```ini
|
||||
[Peer]
|
||||
# First hop
|
||||
PublicKey = <first_hop_public_key>
|
||||
Endpoint = <first_hop_endpoint>
|
||||
AllowedIPs = 10.1.0.0/24
|
||||
|
||||
[Peer]
|
||||
# Second hop (behind first hop)
|
||||
PublicKey = <second_hop_public_key>
|
||||
Endpoint = <second_hop_endpoint>
|
||||
AllowedIPs = 10.2.0.0/24
|
||||
```
|
||||
|
||||
### Load Balancing
|
||||
|
||||
Multiple servers for redundancy:
|
||||
|
||||
```ini
|
||||
[Peer]
|
||||
PublicKey = <server1_public_key>
|
||||
Endpoint = <server1_ip>:51820
|
||||
AllowedIPs = 10.0.0.0/24
|
||||
|
||||
[Peer]
|
||||
PublicKey = <server2_public_key>
|
||||
Endpoint = <server2_ip>:51820
|
||||
AllowedIPs = 10.0.0.0/24
|
||||
```
|
||||
|
||||
## References
|
||||
|
||||
- [WireGuard Documentation](https://www.wireguard.com/)
|
||||
- [WireGuard Quick Start](https://www.wireguard.com/quickstart/)
|
||||
- [WireGuard Android/iOS](https://www.wireguard.com/install/)
|
||||
- [WireGuard Windows](https://www.wireguard.com/install/)
|
||||
|
||||
## Support
|
||||
|
||||
For WireGuard configuration help:
|
||||
- Check logs: `sudo journalctl -u wg-quick@wg0`
|
||||
- Verify config: `sudo wg-quick diff wg0.conf`
|
||||
- Test connectivity: `ping -I wg0 <server-ip>`
|
||||
311
kickstart/build-iso.sh
Normal file
311
kickstart/build-iso.sh
Normal file
@ -0,0 +1,311 @@
|
||||
#!/bin/bash
|
||||
# Build custom Fedora Kinoite ISO script
|
||||
# Version: 1.0
|
||||
# Date: 2026-04-02
|
||||
#
|
||||
# This script builds a custom Fedora Kinoite ISO for the SAW implementation
|
||||
# It requires a base Fedora Kinoite ISO and the custom kickstart file
|
||||
|
||||
set -e
|
||||
|
||||
# Configuration
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
BUILD_DIR="${SCRIPT_DIR}/build"
|
||||
OUTPUT_DIR="${SCRIPT_DIR}/output"
|
||||
KICKSTART_FILE="${SCRIPT_DIR}/kinoite-saw.ks"
|
||||
BASE_ISO=""
|
||||
ISO_NAME="Fedora-Kinoite-SAW.iso"
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Print functions
|
||||
print_info() {
|
||||
echo -e "${GREEN}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
print_warning() {
|
||||
echo -e "${YELLOW}[WARNING]${NC} $1"
|
||||
}
|
||||
|
||||
print_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1"
|
||||
}
|
||||
|
||||
# Usage
|
||||
usage() {
|
||||
echo "Usage: $0 -i <base_iso> [-o <output_name>]"
|
||||
echo ""
|
||||
echo "Options:"
|
||||
echo " -i Path to base Fedora Kinoite ISO (required)"
|
||||
echo " -o Output ISO name (optional, default: $ISO_NAME)"
|
||||
echo " -h Show this help message"
|
||||
echo ""
|
||||
echo "Example:"
|
||||
echo " $0 -i Fedora-Kinoite-43-x86_64-latest.iso"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Parse arguments
|
||||
while getopts "i:o:h" opt; do
|
||||
case $opt in
|
||||
i) BASE_ISO="$OPTARG" ;;
|
||||
o) ISO_NAME="$OPTARG" ;;
|
||||
h) usage ;;
|
||||
\?) usage ;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Check requirements
|
||||
check_requirements() {
|
||||
print_info "Checking requirements..."
|
||||
|
||||
# Check if lorax is installed
|
||||
if ! command -v lorax &> /dev/null; then
|
||||
print_error "lorax is not installed"
|
||||
echo "Install with: sudo dnf install -y lorax anaconda-tools"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check if base ISO is provided
|
||||
if [ -z "$BASE_ISO" ]; then
|
||||
print_error "Base ISO not specified"
|
||||
usage
|
||||
fi
|
||||
|
||||
# Check if base ISO exists
|
||||
if [ ! -f "$BASE_ISO" ]; then
|
||||
print_error "Base ISO not found: $BASE_ISO"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check if kickstart file exists
|
||||
if [ ! -f "$KICKSTART_FILE" ]; then
|
||||
print_error "Kickstart file not found: $KICKSTART_FILE"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
print_info "All requirements met"
|
||||
}
|
||||
|
||||
# Create build directory
|
||||
create_build_dir() {
|
||||
print_info "Creating build directory..."
|
||||
mkdir -p "$BUILD_DIR"
|
||||
mkdir -p "$OUTPUT_DIR"
|
||||
}
|
||||
|
||||
# Mount base ISO
|
||||
mount_iso() {
|
||||
print_info "Mounting base ISO..."
|
||||
|
||||
# Create mount point
|
||||
MOUNT_POINT="${BUILD_DIR}/iso-mount"
|
||||
mkdir -p "$MOUNT_POINT"
|
||||
|
||||
# Mount ISO
|
||||
sudo mount -o loop "$BASE_ISO" "$MOUNT_POINT"
|
||||
|
||||
print_info "ISO mounted at: $MOUNT_POINT"
|
||||
}
|
||||
|
||||
# Copy ISO contents
|
||||
copy_iso_contents() {
|
||||
print_info "Copying ISO contents..."
|
||||
|
||||
# Create working directory
|
||||
WORK_DIR="${BUILD_DIR}/work"
|
||||
mkdir -p "$WORK_DIR"
|
||||
|
||||
# Copy ISO contents
|
||||
cp -r "$MOUNT_POINT"/* "$WORK_DIR/"
|
||||
cp -r "$MOUNT_POINT"/.discinfo "$WORK_DIR/"
|
||||
|
||||
print_info "ISO contents copied to: $WORK_DIR"
|
||||
}
|
||||
|
||||
# Copy kickstart file
|
||||
copy_kickstart() {
|
||||
print_info "Copying kickstart file..."
|
||||
|
||||
# Copy kickstart to ISO
|
||||
cp "$KICKSTART_FILE" "$WORK_DIR/kickstart.ks"
|
||||
|
||||
print_info "Kickstart file copied"
|
||||
}
|
||||
|
||||
# Update isolinux configuration
|
||||
update_isolinux() {
|
||||
print_info "Updating boot configuration..."
|
||||
|
||||
ISOLINUX_DIR="${WORK_DIR}/isolinux"
|
||||
|
||||
# Check if isolinux directory exists
|
||||
if [ ! -d "$ISOLINUX_DIR" ]; then
|
||||
print_warning "isolinux directory not found, using GRUB"
|
||||
return
|
||||
fi
|
||||
|
||||
# Create custom isolinux configuration
|
||||
cat > "${ISOLINUX_DIR}/isolinux.cfg" << 'ISOLINUX'
|
||||
default menu
|
||||
timeout 600
|
||||
menu clear
|
||||
menu background isolinux-bg.png
|
||||
menu title Fedora Kinoite SAW
|
||||
menu vshift 8
|
||||
menu rows 18
|
||||
menu margin 16
|
||||
menu helpmsgrow 15
|
||||
menu tabmsgrow 13
|
||||
|
||||
label install
|
||||
menu label Install Fedora Kinoite SAW
|
||||
menu default
|
||||
kernel vmlinuz
|
||||
append initrd=initrd.img inst.ks=hd:UUID=:kickstart.ks inst.stage2=hd:UUID=: inst.gpt quiet
|
||||
text help
|
||||
Install Fedora Kinoite SAW with automated configuration
|
||||
endtext
|
||||
|
||||
label live
|
||||
menu label Try Fedora Kinoite SAW
|
||||
kernel vmlinuz
|
||||
append initrd=initrd.img inst.stage2=hd:UUID=: quiet
|
||||
|
||||
label memtest
|
||||
menu label Memory Test
|
||||
kernel memtest
|
||||
append initrd=initrd.img inst.stage2=hd:UUID=:
|
||||
|
||||
label local
|
||||
menu label Boot from local drive
|
||||
localboot 0x80
|
||||
|
||||
menu end
|
||||
ISOLINUX
|
||||
|
||||
print_info "isolinux configuration updated"
|
||||
}
|
||||
|
||||
# Update GRUB configuration
|
||||
update_grub() {
|
||||
print_info "Updating GRUB configuration..."
|
||||
|
||||
GRUB_DIR="${WORK_DIR}/EFI/BOOT"
|
||||
|
||||
# Check if GRUB directory exists
|
||||
if [ ! -d "$GRUB_DIR" ]; then
|
||||
print_warning "GRUB directory not found, skipping GRUB update"
|
||||
return
|
||||
fi
|
||||
|
||||
# Create custom GRUB configuration
|
||||
cat > "${GRUB_DIR}/grub.cfg" << 'GRUB'
|
||||
set timeout=10
|
||||
set default=0
|
||||
|
||||
menuentry 'Install Fedora Kinoite SAW' {
|
||||
linux /isolinux/vmlinuz inst.ks=hd:UUID=:kickstart.ks inst.stage2=hd:UUID=: quiet
|
||||
initrd /isolinux/initrd.img
|
||||
}
|
||||
|
||||
menuentry 'Try Fedora Kinoite SAW' {
|
||||
linux /isolinux/vmlinuz inst.stage2=hd:UUID=: quiet
|
||||
initrd /isolinux/initrd.img
|
||||
}
|
||||
|
||||
menuentry 'Boot from local drive' {
|
||||
chainloader (hd0,1)
|
||||
}
|
||||
GRUB
|
||||
|
||||
print_info "GRUB configuration updated"
|
||||
}
|
||||
|
||||
# Update repository configuration
|
||||
update_repos() {
|
||||
print_info "Updating repository configuration..."
|
||||
|
||||
REPOS_DIR="${WORK_DIR}/LiveOS"
|
||||
|
||||
# Create custom repository configuration
|
||||
cat > "${WORK_DIR}/Packages/repodata/repomd.xml" << 'REPO'
|
||||
<repomd>
|
||||
<revision>1</revision>
|
||||
<repo type="binary" arch="x86_64">
|
||||
<url>file:///mnt/source</url>
|
||||
</repo>
|
||||
</repomd>
|
||||
REPO
|
||||
|
||||
print_info "Repository configuration updated"
|
||||
}
|
||||
|
||||
# Generate ISO
|
||||
generate_iso() {
|
||||
print_info "Generating ISO..."
|
||||
|
||||
OUTPUT_ISO="${OUTPUT_DIR}/${ISO_NAME}"
|
||||
|
||||
# Create ISO using xorriso
|
||||
sudo xorriso -as mkisofs \
|
||||
-o "$OUTPUT_ISO" \
|
||||
-J -R -T \
|
||||
-volid "Fedora-Kinoite-SAW" \
|
||||
-b isolinux/isolinux.bin \
|
||||
-c isolinux/boot.cat \
|
||||
-no-emul-boot \
|
||||
-boot-load-size 4 \
|
||||
-boot-info-table \
|
||||
-eltorito-alt-boot \
|
||||
-e EFI/gpt_plpmt.efi \
|
||||
-no-emul-boot \
|
||||
-hfs-plus-wrap-aps \
|
||||
-V "Fedora-Kinoite-SAW" \
|
||||
"$WORK_DIR/"
|
||||
|
||||
print_info "ISO generated: $OUTPUT_ISO"
|
||||
}
|
||||
|
||||
# Unmount ISO
|
||||
umount_iso() {
|
||||
print_info "Unmounting ISO..."
|
||||
sudo umount "$MOUNT_POINT"
|
||||
rmdir "$MOUNT_POINT"
|
||||
}
|
||||
|
||||
# Cleanup
|
||||
cleanup() {
|
||||
print_info "Cleaning up..."
|
||||
rm -rf "$BUILD_DIR"
|
||||
}
|
||||
|
||||
# Main execution
|
||||
main() {
|
||||
print_info "Starting Fedora Kinoite SAW ISO build..."
|
||||
print_info "Base ISO: $BASE_ISO"
|
||||
print_info "Output: $OUTPUT_DIR/$ISO_NAME"
|
||||
|
||||
check_requirements
|
||||
create_build_dir
|
||||
mount_iso
|
||||
copy_iso_contents
|
||||
copy_kickstart
|
||||
update_isolinux
|
||||
update_grub
|
||||
update_repos
|
||||
generate_iso
|
||||
umount_iso
|
||||
cleanup
|
||||
|
||||
print_info "Build completed successfully!"
|
||||
print_info "ISO location: $OUTPUT_DIR/$ISO_NAME"
|
||||
print_info "Size: $(du -h "$OUTPUT_ISO" | cut -f1)"
|
||||
}
|
||||
|
||||
# Run main function
|
||||
main "$@"
|
||||
616
kickstart/kinoite-saw.ks
Normal file
616
kickstart/kinoite-saw.ks
Normal file
@ -0,0 +1,616 @@
|
||||
# Fedora Kinoite SAW Kickstart File
|
||||
|
||||
## Overview
|
||||
|
||||
This kickstart file creates a custom Fedora Kinoite ISO for the Secure Air-Gapped Workstation (SAW) implementation.
|
||||
|
||||
**Key Features:**
|
||||
- Custom CA certificate pre-installed
|
||||
- WireGuard VPN pre-configured
|
||||
- Package verification enabled
|
||||
- Security hardening applied
|
||||
- Minimal package set (only essential packages)
|
||||
|
||||
## Kickstart File
|
||||
|
||||
```bash
|
||||
# Kickstart file for Fedora Kinoite SAW
|
||||
# Version: 1.0
|
||||
# Date: 2026-04-02
|
||||
|
||||
# Use text mode installation
|
||||
text
|
||||
|
||||
# System language
|
||||
lang en_US.UTF-8
|
||||
|
||||
# Keyboard layout
|
||||
keyboard --vckeymap=us --layout=US
|
||||
|
||||
# Network configuration
|
||||
network --bootproto=dhcp --device=eth0 --activate
|
||||
|
||||
# Root password (change this!)
|
||||
rootpw --iscrypted YOUR_ENCRYPTED_PASSWORD_HERE
|
||||
|
||||
# Root password (uncomment for interactive)
|
||||
# rootpw
|
||||
|
||||
# User configuration
|
||||
user --name=saw-user --password=changeme --groups=wheel --shell=/bin/bash
|
||||
# After lockdown, user will be removed from wheel group
|
||||
|
||||
# SELinux configuration
|
||||
selinux --enforcing
|
||||
|
||||
# Firewall configuration
|
||||
firewall --enabled --service=ssh
|
||||
|
||||
# Timezone
|
||||
timezone America/New_York --utc
|
||||
|
||||
# System bootloader
|
||||
bootloader --location=partition --boot-drive=sda
|
||||
|
||||
# Partition information
|
||||
clearpart --all --initlabel
|
||||
part / --fstype="ext4" --size=20480 --grow
|
||||
part /boot/efi --fstype="efi" --size=512 --grow
|
||||
|
||||
# Repositories
|
||||
repo --name=fedora --baseurl=file:///mnt/source
|
||||
repo --name=updates --baseurl=file:///mnt/source/updates
|
||||
|
||||
# Package selection
|
||||
%packages
|
||||
@^kinoite-desktop
|
||||
@base
|
||||
@core
|
||||
@standard
|
||||
# Add your custom packages here
|
||||
# Example: vim-enhanced git wget
|
||||
# Remove unnecessary packages
|
||||
-ibus-angry
|
||||
-ibus-bopomofo
|
||||
-ibus-chewing
|
||||
-ibus-hangul
|
||||
-ibus-kkc
|
||||
-ibus-pinyin
|
||||
-ibus-array
|
||||
-ibus-typing-booster
|
||||
-ibus-m17n
|
||||
-ibus-rawcode
|
||||
-ibus-lua
|
||||
-ibus-sayura
|
||||
-ibus-table
|
||||
-ibus-table-cantonese
|
||||
-ibus-table-erbi
|
||||
-ibus-table-ipa
|
||||
-ibus-table-jyutping
|
||||
-ibus-table-wubi
|
||||
-ibus-table-wbx
|
||||
-ibus-table-wm
|
||||
-ibus-table-wm86
|
||||
-ibus-table-wm95
|
||||
-ibus-table-wm98
|
||||
-ibus-table-wubi
|
||||
-ibus-table-wubi-huizhou
|
||||
-ibus-table-wubi-pinyin
|
||||
-ibus-table-wubi-wx
|
||||
-ibus-table-wubi-wx86
|
||||
-ibus-table-wubi-wx95
|
||||
-ibus-table-wubi-wx98
|
||||
-ibus-table-wubi-wx98p
|
||||
-ibus-table-wubi-wx98p2
|
||||
-ibus-table-wubi-wx98p2b
|
||||
-ibus-table-wubi-wx98pb
|
||||
-ibus-table-wubi-wx98pbc
|
||||
-ibus-table-wubi-wx98pbcd
|
||||
-ibus-table-wubi-wx98pbcd
|
||||
%end
|
||||
|
||||
# CA Certificate Installation
|
||||
%include /tmp/kickstart-ca-certificate.ks
|
||||
|
||||
# WireGuard Configuration
|
||||
%include /tmp/kickstart-wireguard.ks
|
||||
|
||||
# Package Verification Configuration
|
||||
%include /tmp/kickstart-package-verification.ks
|
||||
|
||||
# Post-install configuration
|
||||
%post --erroronfail
|
||||
|
||||
# Remove user from wheel group (disable sudo)
|
||||
gpasswd -d saw-user wheel
|
||||
|
||||
# Disable root SSH login
|
||||
sed -i 's/^PermitRootLogin yes/PermitRootLogin no/' /etc/ssh/sshd_config
|
||||
|
||||
# Enable auditd
|
||||
systemctl enable auditd
|
||||
|
||||
# Enable SELinux
|
||||
setenforce 1
|
||||
|
||||
# Configure DNS to use VPN gateway only
|
||||
cat > /etc/dnsmasq.d/vpn-dns.conf << 'EOF'
|
||||
# VPN-only DNS configuration
|
||||
# All DNS queries go through VPN gateway
|
||||
server=10.0.0.1
|
||||
server=::1
|
||||
bogus-priv
|
||||
no-resolv
|
||||
no-poll
|
||||
no-hosts
|
||||
cache-size=10000
|
||||
log-facility=@/var/log/dnsmasq.log
|
||||
EOF
|
||||
|
||||
# Configure resolv.conf to use dnsmasq
|
||||
echo "nameserver 127.0.0.1" > /etc/resolv.conf
|
||||
|
||||
# Configure firewall
|
||||
cat > /etc/firewalld/services/wireguard.xml << 'EOF'
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<service>
|
||||
<short>WireGuard</short>
|
||||
<description>WireGuard VPN tunnel</description>
|
||||
<port protocol="udp" port="51820"/>
|
||||
</service>
|
||||
EOF
|
||||
|
||||
# Add WireGuard to firewall
|
||||
firewall-cmd --permanent --add-service=wireguard
|
||||
firewall-cmd --permanent --add-port=51820/udp
|
||||
|
||||
# Configure DNF to require signatures
|
||||
cat > /etc/dnf/dnf.conf << 'EOF'
|
||||
# DNF configuration for SAW
|
||||
# Require GPG signature verification
|
||||
gpgcheck=1
|
||||
repo_gpgcheck=1
|
||||
# Disable metadata cache (force refresh)
|
||||
metadata_expire=1h
|
||||
# Disable fastest mirror (use direct repos)
|
||||
fastestmirror=False
|
||||
EOF
|
||||
|
||||
# Create rpm-ostree configuration
|
||||
cat > /etc/rpm-ostreed.conf << 'EOF'
|
||||
[Service]
|
||||
# Auto-download updates
|
||||
DownloadOnly=true
|
||||
# Keep previous deployments
|
||||
KeepOld=2
|
||||
# Enable automatic cleanup
|
||||
AutomaticCleanup=true
|
||||
EOF
|
||||
|
||||
# Set up package verification scripts
|
||||
mkdir -p /usr/local/bin
|
||||
mkdir -p /etc/package-verification
|
||||
|
||||
# Copy CA certificate to DNF key directory
|
||||
cp /etc/pki/ca-trust/source/anchors/ca.crt /etc/pki/rpm-gpg/RPM-GPG-KEY-custom-ca
|
||||
rpm --import /etc/pki/rpm-gpg/RPM-GPG-KEY-custom-ca
|
||||
|
||||
# Create package verification script
|
||||
cat > /usr/local/bin/verify-package.sh << 'VERIFYEOF'
|
||||
#!/bin/bash
|
||||
# Package verification script
|
||||
# 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
|
||||
# Check CRL signature
|
||||
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
|
||||
VERIFYEOF
|
||||
|
||||
chmod +x /usr/local/bin/verify-package.sh
|
||||
|
||||
# Create update approval script
|
||||
cat > /usr/local/bin/approve-update.sh << 'APPROVEEOF'
|
||||
#!/bin/bash
|
||||
# Update approval script for SAW
|
||||
# Shows update details and requires confirmation
|
||||
|
||||
echo "=== Fedora Kinoite SAW Update Approval ==="
|
||||
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
|
||||
APPROVEEOF
|
||||
|
||||
chmod +x /usr/local/bin/approve-update.sh
|
||||
|
||||
# Create daily CRL updater script
|
||||
cat > /usr/local/bin/daily-crl-update.sh << 'CRLEOF'
|
||||
#!/bin/bash
|
||||
# Daily CRL update script for SAW
|
||||
# Downloads and verifies CRL from CA server
|
||||
|
||||
CRL_URL="https://your-ca-server.com/crl.pem"
|
||||
CRL_FILE="/etc/pki/ca-trust/source/anchors/crl.pem"
|
||||
CA_KEY="/etc/pki/ca-trust/source/anchors/ca.crt"
|
||||
LOG_FILE="/var/log/crl-update.log"
|
||||
|
||||
echo "$(date): Starting CRL update" >> "$LOG_FILE"
|
||||
|
||||
# Download CRL
|
||||
if ! curl -s -o "$CRL_FILE" "$CRL_URL"; then
|
||||
echo "$(date): ERROR: Failed to download CRL" >> "$LOG_FILE"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "$(date): CRL downloaded successfully" >> "$LOG_FILE"
|
||||
|
||||
# Verify CRL signature
|
||||
if ! openssl crl -in "$CRL_FILE" -CAfile "$CA_KEY" -noout 2>&1; then
|
||||
echo "$(date): ERROR: CRL signature verification failed" >> "$LOG_FILE"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "$(date): CRL signature verified" >> "$LOG_FILE"
|
||||
|
||||
# Update CA trust
|
||||
update-ca-trust extract
|
||||
|
||||
echo "$(date): CRL update completed successfully" >> "$LOG_FILE"
|
||||
|
||||
# Check for packages with revoked certificates
|
||||
if rpm -qa --queryformat='%{NAME} %{SIGPGP:pgpsig}\n' 2>/dev/null | grep -q "0x0"; then
|
||||
echo "$(date): WARNING: Found packages with revoked certificates" >> "$LOG_FILE"
|
||||
fi
|
||||
|
||||
exit 0
|
||||
CRLEOF
|
||||
|
||||
chmod +x /usr/local/bin/daily-crl-update.sh
|
||||
|
||||
# Create security audit script
|
||||
cat > /usr/local/bin/security-audit.sh << 'AUDITEOF'
|
||||
#!/bin/bash
|
||||
# Security audit script for SAW
|
||||
|
||||
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:"
|
||||
systemctl is-active auditd || echo " auditd not running"
|
||||
echo ""
|
||||
|
||||
# Check firewall
|
||||
echo "5. Firewall Status:"
|
||||
firewall-cmd --state 2>/dev/null || echo " firewalld not running"
|
||||
echo ""
|
||||
|
||||
# Check VPN
|
||||
echo "6. VPN Status:"
|
||||
wg show 2>/dev/null || echo " WireGuard not configured"
|
||||
echo ""
|
||||
|
||||
# Check for recent audit events
|
||||
echo "7. Recent Audit Events:"
|
||||
ausearch -m all -ts recent 2>/dev/null | head -10
|
||||
echo ""
|
||||
|
||||
# Check for failed sudo attempts
|
||||
echo "8. Failed Sudo Attempts:"
|
||||
grep "authentication failure" /var/log/secure 2>/dev/null | tail -5
|
||||
echo ""
|
||||
|
||||
echo "=== Audit Complete ==="
|
||||
AUDITEOF
|
||||
|
||||
chmod +x /usr/local/bin/security-audit.sh
|
||||
|
||||
# Create check-verification script
|
||||
cat > /usr/local/bin/check-verification.sh << 'CHECKEOF'
|
||||
#!/bin/bash
|
||||
# Check all package verifications for SAW
|
||||
|
||||
echo "=== Package Verification Check ==="
|
||||
|
||||
# Check CA certificate
|
||||
if [ -f /etc/pki/rpm-gpg/RPM-GPG-KEY-custom-ca ]; then
|
||||
echo "OK: CA certificate installed"
|
||||
else
|
||||
echo "ERROR: CA certificate not found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check DNF configuration
|
||||
if grep -q "^gpgcheck=1" /etc/dnf/dnf.conf; then
|
||||
echo "OK: DNF signature verification enabled"
|
||||
else
|
||||
echo "ERROR: DNF signature verification not enabled"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check SELinux
|
||||
if [ "$(sestatus | grep "Current mode" | awk '{print $3}')" = "enforcing" ]; then
|
||||
echo "OK: SELinux enforcing"
|
||||
else
|
||||
echo "WARNING: SELinux not enforcing"
|
||||
fi
|
||||
|
||||
# Check auditd
|
||||
if systemctl is-active auditd >/dev/null 2>&1; then
|
||||
echo "OK: Auditd running"
|
||||
else
|
||||
echo "WARNING: Auditd not running"
|
||||
fi
|
||||
|
||||
# Check firewall
|
||||
if firewall-cmd --state 2>/dev/null | grep -q "running"; then
|
||||
echo "OK: Firewall running"
|
||||
else
|
||||
echo "WARNING: Firewall not running"
|
||||
fi
|
||||
|
||||
echo "=== Verification Complete ==="
|
||||
CHECKEOF
|
||||
|
||||
chmod +x /usr/local/bin/check-verification.sh
|
||||
|
||||
# Set up CRL update cron job
|
||||
cat > /etc/cron.d/crl-update << 'CRONEOF'
|
||||
# Daily CRL update for SAW
|
||||
# Run at 2:00 AM
|
||||
0 2 * * * root /usr/local/bin/daily-crl-update.sh >> /var/log/crl-update.log 2>&1
|
||||
CRONEOF
|
||||
|
||||
# Create initial VPN configuration
|
||||
cat > /tmp/wireguard.conf << 'WGEOF'
|
||||
[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
|
||||
# Replace with your VPN gateway IP
|
||||
AllowedIPs = 10.0.0.0/24
|
||||
WGEOF
|
||||
|
||||
# Copy WireGuard config to system
|
||||
cp /tmp/wireguard.conf /etc/wireguard/wg0.conf
|
||||
chmod 600 /etc/wireguard/wg0.conf
|
||||
|
||||
# Enable WireGuard service
|
||||
systemctl enable wg-quick@wg0
|
||||
|
||||
# Final system updates
|
||||
dnf update -y
|
||||
|
||||
# Clean up
|
||||
dnf clean all
|
||||
rm -rf /tmp/kickstart-*.ks
|
||||
rm -rf /tmp/wireguard.conf
|
||||
|
||||
exit 0
|
||||
%end
|
||||
```
|
||||
|
||||
## Customization Instructions
|
||||
|
||||
### 1. Generate Root Password
|
||||
|
||||
```bash
|
||||
# Generate encrypted password
|
||||
openssl passwd -6
|
||||
|
||||
# Replace YOUR_ENCRYPTED_PASSWORD_HERE with the output
|
||||
```
|
||||
|
||||
### 2. Configure CA Certificate
|
||||
|
||||
Create a separate file `kickstart-ca-certificate.ks`:
|
||||
|
||||
```bash
|
||||
# Copy CA certificate
|
||||
%include /tmp/kickstart-ca-certificate.ks
|
||||
|
||||
# Create kickstart-ca-certificate.ks:
|
||||
# Copy CA certificate to system
|
||||
mkdir -p /etc/pki/ca-trust/source/anchors
|
||||
cp /run/install/repo/ca.crt /etc/pki/ca-trust/source/anchors/ca.crt
|
||||
|
||||
# Update CA trust database
|
||||
update-ca-trust extract
|
||||
|
||||
# Copy to DNF key directory
|
||||
cp /etc/pki/ca-trust/source/anchors/ca.crt \
|
||||
/etc/pki/rpm-gpg/RPM-GPG-KEY-custom-ca
|
||||
|
||||
# Import into RPM database
|
||||
rpm --import /etc/pki/rpm-gpg/RPM-GPG-KEY-custom-ca
|
||||
```
|
||||
|
||||
### 3. Configure WireGuard
|
||||
|
||||
Update the WireGuard configuration in the kickstart:
|
||||
|
||||
```bash
|
||||
# Replace these values:
|
||||
PrivateKey = YOUR_PRIVATE_KEY_HERE
|
||||
Address = 10.0.0.2/24
|
||||
DNS = 10.0.0.1
|
||||
PublicKey = YOUR_SERVER_PUBLIC_KEY_HERE
|
||||
Endpoint = vpn.example.com:51820
|
||||
```
|
||||
|
||||
### 4. Add Custom Packages
|
||||
|
||||
Add your packages to the `%packages` section:
|
||||
|
||||
```bash
|
||||
%packages
|
||||
@^kinoite-desktop
|
||||
@base
|
||||
@core
|
||||
@standard
|
||||
# Add your custom packages
|
||||
your-package-1
|
||||
your-package-2
|
||||
# Remove unnecessary packages (optional)
|
||||
-ibus-angry
|
||||
%end
|
||||
```
|
||||
|
||||
### 5. Configure Package Verification
|
||||
|
||||
Update the package verification section with your CA details:
|
||||
|
||||
```bash
|
||||
# Copy CA certificate
|
||||
cp /etc/pki/ca-trust/source/anchors/ca.crt \
|
||||
/etc/pki/rpm-gpg/RPM-GPG-KEY-custom-ca
|
||||
|
||||
# Verify CA key format
|
||||
openssl x509 -in /etc/pki/ca-trust/source/anchors/ca.crt -text -noout
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Kickstart Validation
|
||||
|
||||
```bash
|
||||
# Validate kickstart syntax
|
||||
ksvalidator kinoite-saw.ks
|
||||
|
||||
# Check for errors
|
||||
# Fix any syntax errors
|
||||
```
|
||||
|
||||
### ISO Build Debugging
|
||||
|
||||
```bash
|
||||
# Add debug output
|
||||
%post --erroronfail --log=/tmp/kickstart.log
|
||||
|
||||
# Check log after build
|
||||
cat /tmp/kickstart.log
|
||||
```
|
||||
|
||||
### Network Issues
|
||||
|
||||
```bash
|
||||
# Add static network configuration
|
||||
network --bootproto=static --ip=192.168.1.100 \
|
||||
--netmask=255.255.255.0 --gateway=192.168.1.1 \
|
||||
--nameserver=8.8.8.8 --device=eth0
|
||||
```
|
||||
|
||||
### Package Repository Issues
|
||||
|
||||
```bash
|
||||
# Add additional repositories
|
||||
repo --name=custom --baseurl=http://your-repo.com
|
||||
```
|
||||
|
||||
## Security Considerations
|
||||
|
||||
1. **CA Key Security:**
|
||||
- Store CA private key offline
|
||||
- Use strong encryption for keys
|
||||
- Never include private keys in kickstart
|
||||
|
||||
2. **Package Signing:**
|
||||
- Sign all packages with your CA
|
||||
- Verify signatures before installation
|
||||
- Keep CRL updated
|
||||
|
||||
3. **Network Security:**
|
||||
- Use VPN for all traffic
|
||||
- Block all direct internet access
|
||||
- Implement DNS lockdown
|
||||
|
||||
4. **Update Security:**
|
||||
- Require approval for updates
|
||||
- Verify update signatures
|
||||
- Test updates before deployment
|
||||
|
||||
## References
|
||||
|
||||
- [Kickstart Syntax Reference](https://pykickstart.readthedocs.io/)
|
||||
- [Fedora Kinoite Installation](https://kinoite.fedoraproject.org/)
|
||||
- [RPM Signature Verification](https://docs.fedoraproject.org/en-US/fedora-coreos/security-verification/)
|
||||
- [WireGuard Documentation](https://www.wireguard.com/install/)
|
||||
|
||||
---
|
||||
|
||||
**Previous:** [Installation Guide](INSTALLATION_GUIDE.md)
|
||||
**Next:** [Lockdown Script](../post-install/lockdown.sh)
|
||||
213
package-verification/ca/README.md
Normal file
213
package-verification/ca/README.md
Normal file
@ -0,0 +1,213 @@
|
||||
# Package Verification CA Configuration
|
||||
|
||||
## Overview
|
||||
|
||||
This directory contains the Certificate Authority (CA) configuration for package verification.
|
||||
|
||||
## Files
|
||||
|
||||
```
|
||||
package-verification/ca/
|
||||
├── ca.crt # CA certificate (PLACEHOLDER - you must add your own)
|
||||
├── ca.key # CA private key (PLACEHOLDER - NEVER include this in the ISO)
|
||||
├── crl.pem # Certificate Revocation List (PLACEHOLDER)
|
||||
└── README.md # This file
|
||||
```
|
||||
|
||||
## CA Certificate
|
||||
|
||||
### What is a CA Certificate?
|
||||
|
||||
A CA (Certificate Authority) certificate is used to sign and verify packages. It ensures:
|
||||
|
||||
1. **Integrity** - Packages haven't been modified
|
||||
2. **Authenticity** - Packages come from your trusted source
|
||||
3. **Non-repudiation** - Can prove who signed the package
|
||||
|
||||
### Creating Your CA Certificate
|
||||
|
||||
```bash
|
||||
# Generate CA private key
|
||||
openssl genrsa -out ca.key 4096
|
||||
|
||||
# Generate CA certificate
|
||||
openssl req -x509 -new -nodes -key ca.key -sha256 -days 3650 -out ca.crt
|
||||
|
||||
# Verify certificate
|
||||
openssl x509 -in ca.crt -text -noout
|
||||
```
|
||||
|
||||
### Installing CA Certificate
|
||||
|
||||
The CA certificate should be:
|
||||
|
||||
1. **Embedded in ISO** - During ISO build
|
||||
2. **Installed to system** - `/etc/pki/rpm-gpg/RPM-GPG-KEY-custom-ca`
|
||||
3. **Imported into RPM database** - `rpm --import`
|
||||
4. **Added to CA trust** - `update-ca-trust`
|
||||
|
||||
## CA Private Key
|
||||
|
||||
### Security Requirements
|
||||
|
||||
The CA private key is **CRITICAL** to protect:
|
||||
|
||||
1. **Never include in ISO** - Only use on signing machine
|
||||
2. **Store offline** - USB drive or air-gapped system
|
||||
3. **Use strong encryption** - AES-256 with passphrase
|
||||
4. **Back up securely** - Multiple encrypted backups
|
||||
5. **Use hardware token** - HSM for maximum security
|
||||
|
||||
### Signing Packages
|
||||
|
||||
```bash
|
||||
# Sign RPM packages
|
||||
rpm --define "%_gpg_name Your CA Name" --addsign package.rpm
|
||||
|
||||
# Sign with specific key
|
||||
rpm --define "%_gpg_name Your CA Name" --addsign --define '_gpg_transport_key YOUR_KEY_ID' package.rpm
|
||||
|
||||
# Verify signature
|
||||
rpm --checksig package.rpm
|
||||
```
|
||||
|
||||
## Certificate Revocation List (CRL)
|
||||
|
||||
### What is a CRL?
|
||||
|
||||
A CRL is a list of certificates that have been revoked before their expiration date. It's used to:
|
||||
|
||||
1. **Detect compromised certificates**
|
||||
2. **Block revoked packages**
|
||||
3. **Respond to security incidents**
|
||||
4. **Maintain trust**
|
||||
|
||||
### Creating a CRL
|
||||
|
||||
```bash
|
||||
# Create a certificate to revoke (for testing)
|
||||
openssl req -new -nodes -out test.csr
|
||||
openssl ca -in test.csr -out test.crt
|
||||
|
||||
# Revoke the certificate
|
||||
openssl ca -revoke test.crt
|
||||
|
||||
# Generate CRL
|
||||
openssl ca -gencrl -out crl.pem
|
||||
|
||||
# Verify CRL
|
||||
openssl crl -in crl.pem -noout -text
|
||||
```
|
||||
|
||||
### CRL Distribution
|
||||
|
||||
The CRL should be:
|
||||
|
||||
1. **Hosted on secure server** - HTTPS with authentication
|
||||
2. **Signed by CA** - Ensure CRL authenticity
|
||||
3. **Updated regularly** - Daily recommended
|
||||
4. **Cached locally** - For offline verification
|
||||
|
||||
## Package Signing
|
||||
|
||||
### Creating Signed Packages
|
||||
|
||||
1. **Create package** - Build your RPM package
|
||||
2. **Sign package** - Use CA private key to sign
|
||||
3. **Distribute** - Share signed package
|
||||
4. **Verify** - Recipients verify signature
|
||||
|
||||
### Example Package Signing Workflow
|
||||
|
||||
```bash
|
||||
# Step 1: Build package
|
||||
rpmbuild -bb your-package.spec
|
||||
|
||||
# Step 2: Sign package
|
||||
rpm --define "%_gpg_name Your CA Name" --addsign ~/rpmbuild/RPMS/x86_64/your-package.rpm
|
||||
|
||||
# Step 3: Verify signature
|
||||
rpm --checksig ~/rpmbuild/RPMS/x86_64/your-package.rpm
|
||||
```
|
||||
|
||||
## Security Best Practices
|
||||
|
||||
### CA Key Management
|
||||
|
||||
1. **Generate on air-gapped system**
|
||||
2. **Store in encrypted storage**
|
||||
3. **Use hardware security module (HSM)**
|
||||
4. **Implement key rotation**
|
||||
5. **Maintain audit trail**
|
||||
|
||||
### Certificate Management
|
||||
|
||||
1. **Set appropriate validity period** - 1-3 years for CA
|
||||
2. **Use strong algorithms** - RSA 4096, ECDSA P-256
|
||||
3. **Implement certificate policies**
|
||||
4. **Maintain certificate registry**
|
||||
5. **Track certificate lifecycle**
|
||||
|
||||
### CRL Management
|
||||
|
||||
1. **Update daily** - Ensure current revocation status
|
||||
2. **Sign CRL** - Use CA key for authenticity
|
||||
3. **Cache CRL** - For offline verification
|
||||
4. **Monitor expiration** - Renew before expiration
|
||||
5. **Test revocation** - Verify revocation works
|
||||
|
||||
### Package Signing
|
||||
|
||||
1. **Sign all packages** - No unsigned packages
|
||||
2. **Verify before install** - Always check signature
|
||||
3. **Log all operations** - Audit trail
|
||||
4. **Use separate signing keys** - For different purposes
|
||||
5. **Implement key rotation** - Regularly rotate keys
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### CA Certificate Not Found
|
||||
|
||||
```bash
|
||||
# Verify certificate exists
|
||||
ls -la /etc/pki/rpm-gpg/RPM-GPG-KEY-custom-ca
|
||||
|
||||
# Check certificate format
|
||||
openssl x509 -in /etc/pki/rpm-gpg/RPM-GPG-KEY-custom-ca -text -noout
|
||||
```
|
||||
|
||||
### Package Signature Verification Failed
|
||||
|
||||
```bash
|
||||
# Verify package signature
|
||||
rpm --checksig package.rpm
|
||||
|
||||
# Check CA key is imported
|
||||
rpm -q gpg-pubkey
|
||||
|
||||
# Re-import CA key
|
||||
rpm --import /etc/pki/rpm-gpg/RPM-GPG-KEY-custom-ca
|
||||
```
|
||||
|
||||
### CRL Verification Failed
|
||||
|
||||
```bash
|
||||
# Verify CRL format
|
||||
openssl crl -in crl.pem -noout -text
|
||||
|
||||
# Check CRL signature
|
||||
openssl crl -in crl.pem -CAfile ca.crt -noout
|
||||
|
||||
# Verify CRL dates
|
||||
openssl crl -in crl.pem -noout -text | grep -E "(Last Update|Next Update)"
|
||||
```
|
||||
|
||||
## References
|
||||
|
||||
- [OpenSSL Documentation](https://www.openssl.org/docs/)
|
||||
- [RPM Signature Documentation](https://docs.fedoraproject.org/en-US/fedora-coreos/security-verification/)
|
||||
- [Certificate Management Best Practices](https://csrc.nist.gov/publications)
|
||||
|
||||
## Contact
|
||||
|
||||
For questions about CA configuration, contact your security administrator.
|
||||
444
package-verification/verify-crl.sh
Normal file
444
package-verification/verify-crl.sh
Normal file
@ -0,0 +1,444 @@
|
||||
#!/bin/bash
|
||||
# CRL (Certificate Revocation List) Update Script for SAW
|
||||
# Version: 1.0
|
||||
# Date: 2026-04-02
|
||||
#
|
||||
# This script downloads, verifies, and installs the Certificate Revocation List (CRL)
|
||||
# from your Certificate Authority (CA) server. It ensures that packages signed with
|
||||
# revoked certificates are not installed.
|
||||
#
|
||||
# Usage: sudo ./verify-crl.sh [--download] [--verify] [--update] [--status]
|
||||
#
|
||||
# SECURITY RATIONALE:
|
||||
# CRL checking is critical for security because it:
|
||||
# 1. Detects compromised CA keys
|
||||
# 2. Revokes access for compromised certificates
|
||||
# 3. Prevents installation of packages signed with revoked keys
|
||||
# 4. Provides a mechanism to respond to security incidents
|
||||
# 5. Meets compliance requirements (PCI-DSS, HIPAA, etc.)
|
||||
#
|
||||
# How CRL works:
|
||||
# 1. CA maintains a list of revoked certificates
|
||||
# 2. CRL is signed by CA to prevent tampering
|
||||
# 3. System downloads CRL regularly
|
||||
# 4. Before installing package, checks if certificate is revoked
|
||||
# 5. If revoked, installation is blocked
|
||||
#
|
||||
# Reference: https://www.openssl.org/docs/man1.1.1/man1/crl.html
|
||||
|
||||
set -e
|
||||
|
||||
# Configuration
|
||||
CRL_URL="https://your-ca-server.com/crl.pem"
|
||||
CRL_FILE="/etc/pki/ca-trust/source/anchors/crl.pem"
|
||||
CA_KEY_PATH="/etc/pki/rpm-gpg/RPM-GPG-KEY-custom-ca"
|
||||
CRL_CACHE_FILE="/var/cache/crl/crl.pem"
|
||||
LOG_FILE="/var/log/crl-update.log"
|
||||
CRL_UPDATE_SCRIPT="/usr/local/bin/daily-crl-update.sh"
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
# Print functions
|
||||
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
|
||||
}
|
||||
|
||||
# Download CRL from CA server
|
||||
download_crl() {
|
||||
log_info "Downloading CRL from: $CRL_URL"
|
||||
|
||||
# Check if URL is configured
|
||||
if [ "$CRL_URL" = "https://your-ca-server.com/crl.pem" ]; then
|
||||
log_error "CRL URL not configured"
|
||||
log_info "Edit the script and set CRL_URL to your CA server"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Create cache directory
|
||||
mkdir -p /var/cache/crl
|
||||
|
||||
# Download CRL
|
||||
if ! curl -s -o "$CRL_CACHE_FILE" "$CRL_URL"; then
|
||||
log_error "Failed to download CRL"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log_info "CRL downloaded to: $CRL_CACHE_FILE"
|
||||
|
||||
# Verify download was successful
|
||||
if [ ! -f "$CRL_CACHE_FILE" ]; then
|
||||
log_error "CRL file not created"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -s "$CRL_CACHE_FILE" ]; then
|
||||
log_error "CRL file is empty"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log_info "CRL file size: $(du -h "$CRL_CACHE_FILE" | cut -f1)"
|
||||
}
|
||||
|
||||
# Verify CRL signature
|
||||
#
|
||||
# SECURITY RATIONALE:
|
||||
# Verifying the CRL signature ensures:
|
||||
# 1. CRL hasn't been tampered with during download
|
||||
# 2. CRL comes from your trusted CA
|
||||
# 3. CRL is authentic and hasn't been replaced by attacker
|
||||
# 4. CRL is valid and hasn't expired
|
||||
#
|
||||
# The verification process:
|
||||
# 1. Check if CRL is properly formatted
|
||||
# 2. Verify CRL signature using CA public key
|
||||
# 3. Check CRL validity period
|
||||
# 4. Ensure CRL hasn't expired
|
||||
verify_crl() {
|
||||
log_info "Verifying CRL signature..."
|
||||
|
||||
# Check if CA key exists
|
||||
if [ ! -f "$CA_KEY_PATH" ]; then
|
||||
log_error "CA key not found: $CA_KEY_PATH"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check if CRL exists
|
||||
if [ ! -f "$CRL_CACHE_FILE" ]; then
|
||||
log_error "CRL not found: $CRL_CACHE_FILE"
|
||||
log_info "Run with --download first"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Verify CRL format
|
||||
if ! openssl crl -in "$CRL_CACHE_FILE" -noout 2>/dev/null; then
|
||||
log_error "CRL format is invalid"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Verify CRL signature
|
||||
if ! openssl crl -in "$CRL_CACHE_FILE" -CAfile "$CA_KEY_PATH" -noout 2>/dev/null; then
|
||||
log_error "CRL signature verification failed"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Get CRL information
|
||||
local crl_info=$(openssl crl -in "$CRL_CACHE_FILE" -noout -text 2>/dev/null)
|
||||
|
||||
# Extract CRL dates
|
||||
local this_update=$(echo "$crl_info" | grep "Last Update:" | head -1)
|
||||
local next_update=$(echo "$crl_info" | grep "Next Update:" | head -1)
|
||||
|
||||
log_info "CRL Last Update: $this_update"
|
||||
log_info "CRL Next Update: $next_update"
|
||||
|
||||
# Check if CRL is expired
|
||||
local next_update_date=$(echo "$next_update" | awk '{print $4, $5, $6, $7, $8, $9}')
|
||||
local current_date=$(date)
|
||||
|
||||
if [[ "$current_date" > "$next_update_date" ]]; then
|
||||
log_warning "CRL has expired!"
|
||||
log_warning "CRL should be updated immediately"
|
||||
else
|
||||
log_info "CRL is valid"
|
||||
fi
|
||||
|
||||
log_info "CRL signature verified successfully"
|
||||
}
|
||||
|
||||
# Update CA trust with CRL
|
||||
update_ca_trust() {
|
||||
log_info "Updating CA trust database..."
|
||||
|
||||
# Copy CRL to CA trust source
|
||||
mkdir -p /etc/pki/ca-trust/source/anchors
|
||||
cp "$CRL_CACHE_FILE" /etc/pki/ca-trust/source/anchors/crl.pem
|
||||
|
||||
# Update CA trust
|
||||
update-ca-trust extract
|
||||
|
||||
log_info "CA trust database updated"
|
||||
}
|
||||
|
||||
# Check revoked certificates
|
||||
check_revoked() {
|
||||
log_info "Checking for revoked certificates..."
|
||||
|
||||
# Check if CRL exists
|
||||
if [ ! -f "$CRL_FILE" ]; then
|
||||
log_warning "CRL not found: $CRL_FILE"
|
||||
log_info "Run with --download and --verify first"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Check if CA key exists
|
||||
if [ ! -f "$CA_KEY_PATH" ]; then
|
||||
log_error "CA key not found: $CA_KEY_PATH"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Get list of installed packages with signatures
|
||||
local revoked_count=0
|
||||
local total_count=0
|
||||
|
||||
log_info "Scanning installed packages..."
|
||||
|
||||
# Get all installed packages
|
||||
local packages=$(rpm -qa --queryformat='%{NAME} %{SIGPGP:pgpsig}\n' 2>/dev/null | head -100)
|
||||
|
||||
for pkg in $packages; do
|
||||
# Extract certificate ID from signature
|
||||
local cert_id=$(echo "$pkg" | awk '{print $2}' | cut -d: -f2)
|
||||
|
||||
if [ -n "$cert_id" ]; then
|
||||
# Check if certificate is in CRL
|
||||
if openssl crl -in "$CRL_FILE" -noout -text 2>/dev/null | grep -q "$cert_id"; then
|
||||
log_error "REVOKED: Package signed with revoked certificate: $cert_id"
|
||||
((revoked_count++))
|
||||
fi
|
||||
fi
|
||||
|
||||
((total_count++))
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "=== Revocation Check Summary ==="
|
||||
echo "Total packages checked: $total_count"
|
||||
echo "Revoked certificates found: $revoked_count"
|
||||
echo ""
|
||||
|
||||
if [ $revoked_count -gt 0 ]; then
|
||||
log_error "WARNING: $revoked_count packages have revoked certificates!"
|
||||
log_info "These packages should be removed and reinstalled"
|
||||
return 1
|
||||
fi
|
||||
|
||||
log_info "No revoked certificates found"
|
||||
return 0
|
||||
}
|
||||
|
||||
# Display CRL status
|
||||
show_status() {
|
||||
echo ""
|
||||
echo "=== CRL Status ==="
|
||||
echo ""
|
||||
|
||||
# Check if CRL exists
|
||||
if [ -f "$CRL_CACHE_FILE" ]; then
|
||||
echo "CRL Cache File: $CRL_CACHE_FILE"
|
||||
echo "Size: $(du -h "$CRL_CACHE_FILE" | cut -f1)"
|
||||
echo "Modified: $(stat -c %y "$CRL_CACHE_FILE")"
|
||||
echo ""
|
||||
|
||||
# Show CRL information
|
||||
if openssl crl -in "$CRL_CACHE_FILE" -noout -text 2>/dev/null | head -10; then
|
||||
echo ""
|
||||
fi
|
||||
|
||||
# Check if valid
|
||||
if openssl crl -in "$CRL_CACHE_FILE" -CAfile "$CA_KEY_PATH" -noout 2>/dev/null; then
|
||||
echo "Status: VALID"
|
||||
echo "Signed by: $CA_KEY_PATH"
|
||||
else
|
||||
echo "Status: INVALID"
|
||||
fi
|
||||
else
|
||||
echo "CRL Cache File: NOT FOUND"
|
||||
echo "Status: CRL has not been downloaded"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "CA Key: $CA_KEY_PATH"
|
||||
if [ -f "$CA_KEY_PATH" ]; then
|
||||
echo "Status: FOUND"
|
||||
else
|
||||
echo "Status: NOT FOUND"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Create daily CRL update script
|
||||
create_daily_script() {
|
||||
log_info "Creating daily CRL update script..."
|
||||
|
||||
cat > "$CRL_UPDATE_SCRIPT" << 'DAILYSCHEDULE'
|
||||
#!/bin/bash
|
||||
# Daily CRL update script for SAW
|
||||
# This script is run by cron to keep CRL up to date
|
||||
|
||||
CRL_URL="https://your-ca-server.com/crl.pem"
|
||||
CRL_CACHE_FILE="/var/cache/crl/crl.pem"
|
||||
CA_KEY_PATH="/etc/pki/rpm-gpg/RPM-GPG-KEY-custom-ca"
|
||||
LOG_FILE="/var/log/crl-update.log"
|
||||
|
||||
# Function to log
|
||||
log_info() {
|
||||
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" >> "$LOG_FILE"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo "[$(date '+%Y-%m-%d %H:%M:%S')] [ERROR] $1" >> "$LOG_FILE"
|
||||
}
|
||||
|
||||
log_warning() {
|
||||
echo "[$(date '+%Y-%m-%d %H:%M:%S')] [WARNING] $1" >> "$LOG_FILE"
|
||||
}
|
||||
|
||||
log_info "Starting CRL update"
|
||||
|
||||
# Check if CRL URL is configured
|
||||
if [ "$CRL_URL" = "https://your-ca-server.com/crl.pem" ]; then
|
||||
log_error "CRL URL not configured"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Download CRL
|
||||
if ! curl -s -o "$CRL_CACHE_FILE" "$CRL_URL"; then
|
||||
log_error "Failed to download CRL"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log_info "CRL downloaded successfully"
|
||||
|
||||
# Verify CRL
|
||||
if ! openssl crl -in "$CRL_CACHE_FILE" -CAfile "$CA_KEY_PATH" -noout 2>/dev/null; then
|
||||
log_error "CRL verification failed"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log_info "CRL verified successfully"
|
||||
|
||||
# Update CA trust
|
||||
update-ca-trust extract
|
||||
|
||||
log_info "CRL update completed successfully"
|
||||
DAILYSCHEDULE
|
||||
|
||||
chmod +x "$CRL_UPDATE_SCRIPT"
|
||||
log_info "Daily update script created: $CRL_UPDATE_SCRIPT"
|
||||
}
|
||||
|
||||
# Set up cron job for daily updates
|
||||
setup_cron() {
|
||||
log_info "Setting up cron job for daily CRL updates..."
|
||||
|
||||
# Create cron job
|
||||
cat > /etc/cron.d/saw-crl-update << 'CRONJOB'
|
||||
# Daily CRL update for SAW
|
||||
# Run at 2:00 AM
|
||||
0 2 * * * root /usr/local/bin/daily-crl-update.sh >> /var/log/crl-update.log 2>&1
|
||||
CRONJOB
|
||||
|
||||
chmod 644 /etc/cron.d/saw-crl-update
|
||||
log_info "Cron job created"
|
||||
log_info "CRL will be updated daily at 2:00 AM"
|
||||
}
|
||||
|
||||
# Display help
|
||||
show_help() {
|
||||
cat << 'HELP'
|
||||
CRL Update Script for SAW
|
||||
=========================
|
||||
|
||||
Usage: ./verify-crl.sh [OPTIONS]
|
||||
|
||||
Options:
|
||||
--download Download CRL from CA server
|
||||
--verify Verify CRL signature
|
||||
--update Update CA trust database
|
||||
--check Check for revoked certificates
|
||||
--status Show CRL status
|
||||
--cron Set up daily cron job
|
||||
--help Show this help message
|
||||
|
||||
Examples:
|
||||
# Download and verify CRL
|
||||
./verify-crl.sh --download --verify --update
|
||||
|
||||
# Check status
|
||||
./verify-crl.sh --status
|
||||
|
||||
# Check for revoked certificates
|
||||
./verify-crl.sh --check
|
||||
|
||||
Security Notes:
|
||||
- CRL should be updated daily
|
||||
- CRL is signed by CA to prevent tampering
|
||||
- Packages signed with revoked certificates will be blocked
|
||||
- CRL expiration should be monitored
|
||||
|
||||
Reference: https://www.openssl.org/docs/man1.1.1/man1/crl.html
|
||||
HELP
|
||||
}
|
||||
|
||||
# Main execution
|
||||
main() {
|
||||
echo ""
|
||||
echo "=========================================="
|
||||
echo " CRL Update Script"
|
||||
echo " Fedora Kinoite SAW"
|
||||
echo "=========================================="
|
||||
echo ""
|
||||
|
||||
check_root
|
||||
|
||||
case "${1:-}" in
|
||||
--download)
|
||||
download_crl
|
||||
;;
|
||||
--verify)
|
||||
check_root
|
||||
verify_crl
|
||||
;;
|
||||
--update)
|
||||
check_root
|
||||
verify_crl
|
||||
update_ca_trust
|
||||
;;
|
||||
--check)
|
||||
check_root
|
||||
check_revoked
|
||||
;;
|
||||
--status)
|
||||
show_status
|
||||
;;
|
||||
--cron)
|
||||
check_root
|
||||
create_daily_script
|
||||
setup_cron
|
||||
;;
|
||||
--help|-h)
|
||||
show_help
|
||||
;;
|
||||
*)
|
||||
show_help
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Run main function
|
||||
main "$@"
|
||||
399
package-verification/verify-signature.sh
Normal file
399
package-verification/verify-signature.sh
Normal file
@ -0,0 +1,399 @@
|
||||
#!/bin/bash
|
||||
# Package Signature Verification Script for SAW
|
||||
# Version: 1.0
|
||||
# Date: 2026-04-02
|
||||
#
|
||||
# This script verifies package signatures against your custom Certificate Authority (CA).
|
||||
# It ensures that only packages signed by your trusted CA can be installed on the system.
|
||||
#
|
||||
# Usage: sudo ./verify-signature.sh <package.rpm>
|
||||
# sudo ./verify-signature.sh --verify-all
|
||||
# sudo ./verify-signature.sh --check-all
|
||||
#
|
||||
# SECURITY RATIONALE:
|
||||
# Package verification is critical for security because it:
|
||||
# 1. Prevents installation of malicious packages (tampering detection)
|
||||
# 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 the package)
|
||||
# 5. Prevents supply chain attacks
|
||||
# 6. Blocks unsigned packages completely
|
||||
#
|
||||
# Implementation Details:
|
||||
# - Uses RPM GPG signature verification
|
||||
# - Verifies against custom CA public key
|
||||
# - Checks CRL (Certificate Revocation List) for revoked certificates
|
||||
# - Logs all verification attempts
|
||||
#
|
||||
# Reference: https://docs.fedoraproject.org/en-US/fedora-coreos/security-verification/
|
||||
|
||||
set -e
|
||||
|
||||
# Configuration
|
||||
CA_KEY_PATH="/etc/pki/rpm-gpg/RPM-GPG-KEY-custom-ca"
|
||||
CRL_FILE="/etc/pki/ca-trust/source/anchors/crl.pem"
|
||||
LOG_FILE="/var/log/package-verification.log"
|
||||
VERIFY_SCRIPT_DIR="/usr/local/bin"
|
||||
CONFIG_FILE="/etc/saw/package-verification.conf"
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
# Print functions
|
||||
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 CA key exists
|
||||
check_ca_key() {
|
||||
if [ ! -f "$CA_KEY_PATH" ]; then
|
||||
log_error "CA key not found: $CA_KEY_PATH"
|
||||
log_info "Package verification requires CA key"
|
||||
log_info "Copy your CA public key to: $CA_KEY_PATH"
|
||||
exit 1
|
||||
fi
|
||||
log_info "CA key found: $CA_KEY_PATH"
|
||||
}
|
||||
|
||||
# Verify a single package
|
||||
#
|
||||
# SECURITY RATIONALE:
|
||||
# Verifying individual package signatures ensures:
|
||||
# 1. Package wasn't tampered with during download
|
||||
# 2. Package comes from trusted source
|
||||
# 3. Package wasn't modified by attacker
|
||||
# 4. Package hasn't been replaced with malicious version
|
||||
#
|
||||
# The verification process:
|
||||
# 1. Extract package header
|
||||
# 2. Verify RSA signature using CA public key
|
||||
# 3. Compare hash of package contents
|
||||
# 4. Check signature validity
|
||||
#
|
||||
# If verification fails, the package should NOT be installed.
|
||||
verify_package() {
|
||||
local package="$1"
|
||||
|
||||
log_info "Verifying package: $package"
|
||||
|
||||
# Check if file exists
|
||||
if [ ! -f "$package" ]; then
|
||||
log_error "Package not found: $package"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Verify package signature
|
||||
if ! rpm --checksig "$package" > /dev/null 2>&1; then
|
||||
log_error "Package signature verification failed: $package"
|
||||
log_info "Package may be tampered or unsigned"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Extract signature info
|
||||
local sig_info=$(rpm --checksig "$package" 2>&1)
|
||||
|
||||
# Check if signed with our CA
|
||||
if echo "$sig_info" | grep -q "$CA_KEY_PATH"; then
|
||||
log_info "Package verified successfully (signed with CA)"
|
||||
else
|
||||
log_warning "Package signed but not with our CA"
|
||||
log_info "Signature info: $sig_info"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Verify CRL if available
|
||||
if [ -f "$CRL_FILE" ]; then
|
||||
log_info "Checking CRL for revoked certificates..."
|
||||
|
||||
# Extract certificate ID from signature
|
||||
local cert_id=$(rpm --queryformat='%{SIGPGP:pgpsig}\n' -p "$package" 2>/dev/null | head -1 | awk '{print $2}' | cut -d: -f2)
|
||||
|
||||
if [ -n "$cert_id" ]; then
|
||||
# Check if certificate is revoked
|
||||
if openssl crl -in "$CRL_FILE" -noout -text 2>/dev/null | grep -q "$cert_id"; then
|
||||
log_error "Package signed with REVOKED certificate: $cert_id"
|
||||
return 1
|
||||
fi
|
||||
log_info "Certificate not revoked: $cert_id"
|
||||
fi
|
||||
fi
|
||||
|
||||
log_info "Package verified successfully: $package"
|
||||
return 0
|
||||
}
|
||||
|
||||
# Verify all installed packages
|
||||
#
|
||||
# SECURITY RATIONALE:
|
||||
# Regular verification of all installed packages ensures:
|
||||
# 1. No packages were tampered with after installation
|
||||
# 2. No malicious packages were added
|
||||
# 3. All packages are still properly signed
|
||||
# 4. Any compromised packages are detected
|
||||
#
|
||||
# This should be run regularly (weekly/monthly) to ensure
|
||||
# the integrity of the entire system.
|
||||
verify_all_packages() {
|
||||
log_info "Verifying all installed packages..."
|
||||
|
||||
local failed=0
|
||||
local total=0
|
||||
local revoked=0
|
||||
|
||||
# Get list of all installed packages
|
||||
local packages=$(rpm -qa --queryformat='%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}.rpm\n' 2>/dev/null)
|
||||
|
||||
for pkg in $packages; do
|
||||
# Get package file path
|
||||
local pkg_path=$(rpm -ql "$pkg" 2>/dev/null | head -1 | xargs dirname)
|
||||
local pkg_name=$(echo "$pkg" | sed 's/\.rpm$//')
|
||||
|
||||
# Check if package is signed
|
||||
if rpm --queryformat='%{SIGPGP:pgpsig}\n' -q "$pkg_name" 2>/dev/null | grep -q "0x0"; then
|
||||
log_warning "Package $pkg_name has no signature"
|
||||
((failed++))
|
||||
else
|
||||
# Check signature
|
||||
if ! rpm --queryformat='%{SIGPGP:pgpsig}\n' -q "$pkg_name" 2>/dev/null | grep -q "0x"; then
|
||||
log_warning "Package $pkg_name signature verification failed"
|
||||
((failed++))
|
||||
else
|
||||
log_info "Package $pkg_name verified"
|
||||
fi
|
||||
fi
|
||||
|
||||
((total++))
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "=== Verification Summary ==="
|
||||
echo "Total packages: $total"
|
||||
echo "Verified: $((total - failed))"
|
||||
echo "Failed: $failed"
|
||||
echo "Revoked: $revoked"
|
||||
echo ""
|
||||
|
||||
if [ $failed -gt 0 ]; then
|
||||
log_error "Some packages failed verification!"
|
||||
return 1
|
||||
fi
|
||||
|
||||
log_info "All packages verified successfully"
|
||||
return 0
|
||||
}
|
||||
|
||||
# Check DNF configuration
|
||||
check_dnf_config() {
|
||||
log_info "Checking DNF configuration..."
|
||||
|
||||
if [ ! -f /etc/dnf/dnf.conf ]; then
|
||||
log_warning "DNF configuration not found"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Check if signature verification is enabled
|
||||
if grep -q "^gpgcheck=1" /etc/dnf/dnf.conf; then
|
||||
log_info "DNF signature verification enabled"
|
||||
else
|
||||
log_error "DNF signature verification NOT enabled"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Check if repo signature verification is enabled
|
||||
if grep -q "^repo_gpgcheck=1" /etc/dnf/dnf.conf; then
|
||||
log_info "DNF repo signature verification enabled"
|
||||
else
|
||||
log_error "DNF repo signature verification NOT enabled"
|
||||
return 1
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# Verify DNF cache
|
||||
verify_dnf_cache() {
|
||||
log_info "Verifying DNF cache..."
|
||||
|
||||
# Check if DNF cache exists
|
||||
if [ ! -d /var/cache/dnf ]; then
|
||||
log_warning "DNF cache not found"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# List cached packages
|
||||
log_info "Cached packages:"
|
||||
find /var/cache/dnf -name "*.rpm" -type f 2>/dev/null | while read -r pkg; do
|
||||
local pkg_name=$(basename "$pkg")
|
||||
if rpm --checksig "$pkg" > /dev/null 2>&1; then
|
||||
log_info " $pkg_name: VERIFIED"
|
||||
else
|
||||
log_error " $pkg_name: FAILED"
|
||||
fi
|
||||
done
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# Create package verification script for users
|
||||
create_user_script() {
|
||||
log_info "Creating user package verification script..."
|
||||
|
||||
cat > "$VERIFY_SCRIPT_DIR/verify-package.sh" << 'VERIFYSCRIPT'
|
||||
#!/bin/bash
|
||||
# Package verification script for SAW
|
||||
# Usage: ./verify-package.sh <package.rpm>
|
||||
|
||||
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"
|
||||
echo "Contact system administrator"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check if file is provided
|
||||
if [ $# -lt 1 ]; then
|
||||
echo "Usage: $0 <package.rpm>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
PACKAGE="$1"
|
||||
|
||||
# Check if file exists
|
||||
if [ ! -f "$PACKAGE" ]; then
|
||||
echo "ERROR: Package not found: $PACKAGE"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Verify package signature
|
||||
if ! rpm --checksig "$PACKAGE" > /dev/null 2>&1; then
|
||||
echo "ERROR: Package signature verification failed: $PACKAGE"
|
||||
echo "Package may be tampered or unsigned"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Extract signature info
|
||||
SIG_INFO=$(rpm --checksig "$PACKAGE" 2>&1)
|
||||
|
||||
# Check if signed with our CA
|
||||
if echo "$SIG_INFO" | grep -q "$CA_KEY"; then
|
||||
echo "OK: Package verified successfully (signed with CA)"
|
||||
else
|
||||
echo "WARNING: Package signed but not with our CA"
|
||||
echo "Signature info: $SIG_INFO"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Verify CRL if available
|
||||
if [ -f "$CRL_FILE" ]; then
|
||||
echo "Checking CRL for revoked certificates..."
|
||||
if ! openssl crl -in "$CRL_FILE" -CAfile "$CA_KEY" -noout 2>/dev/null; then
|
||||
echo "WARNING: CRL verification failed"
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "Package verification complete"
|
||||
exit 0
|
||||
VERIFYSCRIPT
|
||||
|
||||
chmod +x "$VERIFY_SCRIPT_DIR/verify-package.sh"
|
||||
log_info "User verification script created"
|
||||
}
|
||||
|
||||
# Display help
|
||||
show_help() {
|
||||
cat << 'HELP'
|
||||
Package Signature Verification Script for SAW
|
||||
==============================================
|
||||
|
||||
Usage: ./verify-signature.sh [OPTIONS] <package.rpm>
|
||||
|
||||
Options:
|
||||
--verify-all Verify all installed packages
|
||||
--check-all Check all packages (same as --verify-all)
|
||||
--check-dnf Check DNF configuration
|
||||
--verify-dnf Verify DNF cache
|
||||
--help Show this help message
|
||||
|
||||
Examples:
|
||||
# Verify a single package
|
||||
./verify-signature.sh package.rpm
|
||||
|
||||
# Verify all installed packages
|
||||
./verify-signature.sh --verify-all
|
||||
|
||||
# Check DNF configuration
|
||||
./verify-signature.sh --check-dnf
|
||||
|
||||
Security Notes:
|
||||
- This script requires root privileges
|
||||
- Only packages signed by your CA will pass verification
|
||||
- Unsigned packages will be rejected
|
||||
- Packages with revoked certificates will be rejected
|
||||
|
||||
Reference: https://docs.fedoraproject.org/en-US/fedora-coreos/security-verification/
|
||||
HELP
|
||||
}
|
||||
|
||||
# Main execution
|
||||
main() {
|
||||
echo ""
|
||||
echo "=========================================="
|
||||
echo " Package Signature Verification"
|
||||
echo " Fedora Kinoite SAW"
|
||||
echo "=========================================="
|
||||
echo ""
|
||||
|
||||
check_root
|
||||
check_ca_key
|
||||
|
||||
case "${1:-}" in
|
||||
--verify-all|--check-all)
|
||||
verify_all_packages
|
||||
;;
|
||||
--check-dnf)
|
||||
check_dnf_config
|
||||
;;
|
||||
--verify-dnf)
|
||||
verify_dnf_cache
|
||||
;;
|
||||
--help|-h)
|
||||
show_help
|
||||
;;
|
||||
*)
|
||||
if [ $# -lt 1 ]; then
|
||||
show_help
|
||||
exit 0
|
||||
fi
|
||||
verify_package "$1"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Run main function
|
||||
main "$@"
|
||||
414
post-install/configure-vpn.sh
Normal file
414
post-install/configure-vpn.sh
Normal file
@ -0,0 +1,414 @@
|
||||
#!/bin/bash
|
||||
# WireGuard VPN Configuration Script for SAW
|
||||
# Version: 1.0
|
||||
# Date: 2026-04-02
|
||||
#
|
||||
# This script configures WireGuard VPN for the Secure Air-Gapped Workstation (SAW)
|
||||
# It sets up the VPN tunnel, configures routing, and implements kill-switch functionality.
|
||||
#
|
||||
# Usage: sudo ./configure-vpn.sh
|
||||
#
|
||||
# SECURITY RATIONALE:
|
||||
# WireGuard is used because it provides:
|
||||
# 1. Modern, fast encryption (ChaCha20-Poly1305, Curve25519)
|
||||
# 2. Simple configuration
|
||||
# 3. Kernel-native implementation (faster than userspace)
|
||||
# 4. Strong security properties
|
||||
# 5. Easy to audit and verify
|
||||
#
|
||||
# The kill-switch ensures:
|
||||
# - No traffic leaks when VPN is down
|
||||
# - No direct internet access
|
||||
# - All traffic must go through encrypted VPN tunnel
|
||||
#
|
||||
# Reference: https://www.wireguard.com/install/
|
||||
|
||||
set -e
|
||||
|
||||
# Configuration
|
||||
VPN_CONFIG_FILE="/etc/wireguard/wg0.conf"
|
||||
VPN_INTERFACE="wg0"
|
||||
VPN_RULE_FILE="/etc/iproute2/rt_tables.d/51820-wg0"
|
||||
KILL_SWITCH_SCRIPT="/usr/local/bin/wg-kill-switch.sh"
|
||||
LOG_FILE="/var/log/wg-vpn-config.log"
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
# Print functions
|
||||
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 WireGuard is installed
|
||||
check_wireguard() {
|
||||
log_info "Checking WireGuard installation..."
|
||||
|
||||
if ! command -v wg &> /dev/null; then
|
||||
log_warning "WireGuard not installed"
|
||||
log_info "Installing WireGuard..."
|
||||
dnf install -y wireguard-dkms wireguard-tools
|
||||
fi
|
||||
|
||||
# Check if kernel module is loaded
|
||||
if ! lsmod | grep -q wireguard; then
|
||||
log_info "Loading WireGuard kernel module..."
|
||||
modprobe wireguard
|
||||
fi
|
||||
|
||||
log_info "WireGuard is ready"
|
||||
}
|
||||
|
||||
# Validate WireGuard configuration
|
||||
validate_config() {
|
||||
log_info "Validating WireGuard configuration..."
|
||||
|
||||
if [ ! -f "$VPN_CONFIG_FILE" ]; then
|
||||
log_error "WireGuard configuration not found: $VPN_CONFIG_FILE"
|
||||
log_info "Please create the configuration file first"
|
||||
|
||||
# Show example configuration
|
||||
cat << 'EXAMPLE'
|
||||
Example WireGuard Configuration:
|
||||
|
||||
[Interface]
|
||||
# Your WireGuard private key (generated with: wg genkey)
|
||||
PrivateKey = YOUR_PRIVATE_KEY_HERE
|
||||
# VPN interface IP and subnet
|
||||
Address = 10.0.0.2/24
|
||||
# DNS servers (should be VPN gateway)
|
||||
DNS = 10.0.0.1
|
||||
|
||||
[Peer]
|
||||
# VPN server's public key
|
||||
PublicKey = YOUR_SERVER_PUBLIC_KEY_HERE
|
||||
# VPN server's endpoint (IP:port)
|
||||
Endpoint = vpn.example.com:51820
|
||||
# Which IPs to route through VPN (0.0.0.0/0 = all traffic)
|
||||
AllowedIPs = 0.0.0.0/0, ::/0
|
||||
EXAMPLE
|
||||
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check private key permissions
|
||||
if [ -f "$VPN_CONFIG_FILE" ]; then
|
||||
local perm=$(stat -c %a "$VPN_CONFIG_FILE")
|
||||
if [ "$perm" != "600" ]; then
|
||||
log_warning "WireGuard config permissions: $perm (should be 600)"
|
||||
chmod 600 "$VPN_CONFIG_FILE"
|
||||
log_info "Fixed permissions to 600"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Validate configuration syntax
|
||||
if ! wg-quick parse-config "$VPN_CONFIG_FILE" >/dev/null 2>&1; then
|
||||
log_error "Invalid WireGuard configuration"
|
||||
wg-quick parse-config "$VPN_CONFIG_FILE"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log_info "WireGuard configuration validated"
|
||||
}
|
||||
|
||||
# Create routing table
|
||||
create_routing_table() {
|
||||
log_info "Creating routing table for WireGuard..."
|
||||
|
||||
# Create routing table definition
|
||||
cat > "$VPN_RULE_FILE" << 'ROUTES'
|
||||
# WireGuard routing table
|
||||
# Table 51820 is used for WireGuard traffic
|
||||
51820 wg0
|
||||
ROUTES
|
||||
|
||||
# Add default route through WireGuard interface
|
||||
ip route add default dev "$VPN_INTERFACE" table 51820 2>/dev/null || true
|
||||
|
||||
log_info "Routing table created"
|
||||
}
|
||||
|
||||
# Configure iptables rules
|
||||
configure_firewall() {
|
||||
log_info "Configuring firewall rules for WireGuard..."
|
||||
|
||||
# Check if iptables is available
|
||||
if ! command -v iptables &> /dev/null; then
|
||||
log_warning "iptables not available, skipping"
|
||||
return
|
||||
fi
|
||||
|
||||
# Allow WireGuard traffic
|
||||
iptables -A OUTPUT -o "$VPN_INTERFACE" -j ACCEPT 2>/dev/null || true
|
||||
iptables -A INPUT -i "$VPN_INTERFACE" -j ACCEPT 2>/dev/null || true
|
||||
|
||||
# Block all other outbound traffic
|
||||
iptables -A OUTPUT ! -o lo ! -o "$VPN_INTERFACE" -j REJECT 2>/dev/null || true
|
||||
|
||||
# Allow loopback
|
||||
iptables -A OUTPUT -o lo -j ACCEPT 2>/dev/null || true
|
||||
|
||||
log_info "Firewall rules configured"
|
||||
}
|
||||
|
||||
# Create kill-switch script
|
||||
#
|
||||
# SECURITY RATIONALE:
|
||||
# The kill-switch is critical for security because it:
|
||||
# 1. Prevents traffic leaks when VPN is down
|
||||
# 2. Ensures all traffic goes through encrypted tunnel
|
||||
# 3. Blocks DNS leaks (DNS only works through VPN)
|
||||
# 4. Prevents data exfiltration when VPN is unavailable
|
||||
# 5. Provides "fail closed" security model
|
||||
#
|
||||
# How it works:
|
||||
# - Monitors VPN interface status
|
||||
# - If VPN goes down, blocks all traffic (except loopback)
|
||||
# - If VPN comes up, allows traffic through VPN
|
||||
# - Can be configured to allow specific services (like update servers)
|
||||
create_kill_switch() {
|
||||
log_info "Creating kill-switch script..."
|
||||
|
||||
cat > "$KILL_SWITCH_SCRIPT" << 'KILLSCRIPT'
|
||||
#!/bin/bash
|
||||
# WireGuard Kill-Switch Script for SAW
|
||||
# This script blocks all traffic when VPN is down
|
||||
|
||||
VPN_INTERFACE="wg0"
|
||||
LOG_FILE="/var/log/wg-kill-switch.log"
|
||||
|
||||
# Function to enable kill-switch
|
||||
enable_killswitch() {
|
||||
log_info "Enabling kill-switch..."
|
||||
|
||||
# Flush existing rules
|
||||
iptables -F OUTPUT 2>/dev/null || true
|
||||
|
||||
# Allow loopback
|
||||
iptables -A OUTPUT -o lo -j ACCEPT
|
||||
|
||||
# Allow established connections (for cleanup)
|
||||
iptables -A OUTPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
|
||||
|
||||
# Block all other outbound traffic
|
||||
iptables -A OUTPUT -j REJECT
|
||||
|
||||
log_info "Kill-switch enabled"
|
||||
}
|
||||
|
||||
# Function to disable kill-switch
|
||||
disable_killswitch() {
|
||||
log_info "Disabling kill-switch..."
|
||||
|
||||
# Flush rules
|
||||
iptables -F OUTPUT 2>/dev/null || true
|
||||
|
||||
# Allow all traffic (for VPN configuration)
|
||||
iptables -A OUTPUT -j ACCEPT
|
||||
|
||||
log_info "Kill-switch disabled"
|
||||
}
|
||||
|
||||
# Function to check VPN status
|
||||
check_vpn() {
|
||||
if ip link show "$VPN_INTERFACE" >/dev/null 2>&1; then
|
||||
# VPN interface exists and is up
|
||||
if ip addr show "$VPN_INTERFACE" | grep -q "state UP"; then
|
||||
return 0 # VPN is up
|
||||
fi
|
||||
fi
|
||||
return 1 # VPN is down
|
||||
}
|
||||
|
||||
# Function to log
|
||||
log_info() {
|
||||
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" >> "$LOG_FILE"
|
||||
}
|
||||
|
||||
# Main logic
|
||||
case "$1" in
|
||||
enable)
|
||||
enable_killswitch
|
||||
;;
|
||||
disable)
|
||||
disable_killswitch
|
||||
;;
|
||||
status)
|
||||
if check_vpn; then
|
||||
echo "VPN is UP"
|
||||
exit 0
|
||||
else
|
||||
echo "VPN is DOWN"
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
# Check VPN and act accordingly
|
||||
if check_vpn; then
|
||||
disable_killswitch
|
||||
else
|
||||
enable_killswitch
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
KILLSCRIPT
|
||||
|
||||
chmod +x "$KILL_SWITCH_SCRIPT"
|
||||
log_info "Kill-switch script created"
|
||||
}
|
||||
|
||||
# Configure systemd service for kill-switch
|
||||
configure_killswitch_service() {
|
||||
log_info "Configuring kill-switch systemd service..."
|
||||
|
||||
cat > /etc/systemd/system/wg-killswitch.service << 'KILLSERVICE'
|
||||
[Unit]
|
||||
Description=WireGuard Kill-Switch Service
|
||||
After=network.target wg-quick@wg0.service
|
||||
Wants=wg-quick@wg0.service
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
ExecStart=/usr/local/bin/wg-kill-switch.sh
|
||||
RemainAfterExit=yes
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
KILLSERVICE
|
||||
|
||||
systemctl daemon-reload
|
||||
systemctl enable wg-killswitch
|
||||
log_info "Kill-switch service configured"
|
||||
}
|
||||
|
||||
# Configure DNS to use VPN gateway
|
||||
configure_dns() {
|
||||
log_info "Configuring DNS to use VPN gateway..."
|
||||
|
||||
# Read VPN config to get DNS settings
|
||||
if [ -f "$VPN_CONFIG_FILE" ]; then
|
||||
local dns_servers=$(grep "^DNS =" "$VPN_CONFIG_FILE" | cut -d'=' -f2 | tr -d ' ')
|
||||
|
||||
if [ -n "$dns_servers" ]; then
|
||||
# Configure resolv.conf
|
||||
echo "# WireGuard DNS configuration" > /etc/resolv.conf
|
||||
echo "# Generated by configure-vpn.sh" >> /etc/resolv.conf
|
||||
|
||||
# Add DNS servers
|
||||
for server in $dns_servers; do
|
||||
echo "nameserver $server" >> /etc/resolv.conf
|
||||
done
|
||||
|
||||
log_info "DNS configured to use VPN gateway"
|
||||
else
|
||||
log_warning "No DNS servers specified in WireGuard config"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Configure systemd-resolved to use VPN DNS
|
||||
if command -v systemctl &> /dev/null; then
|
||||
systemctl restart systemd-resolved 2>/dev/null || true
|
||||
fi
|
||||
}
|
||||
|
||||
# Test VPN connectivity
|
||||
test_vpn() {
|
||||
log_info "Testing VPN connectivity..."
|
||||
|
||||
# Check if VPN interface is up
|
||||
if ip link show "$VPN_INTERFACE" | grep -q "state UP"; then
|
||||
log_info "VPN interface is UP"
|
||||
|
||||
# Check connectivity
|
||||
if ping -c 3 -I "$VPN_INTERFACE" 8.8.8.8 >/dev/null 2>&1; then
|
||||
log_info "VPN connectivity test passed"
|
||||
else
|
||||
log_warning "VPN connectivity test failed (may need to wait for routing)"
|
||||
fi
|
||||
else
|
||||
log_warning "VPN interface is not UP"
|
||||
log_info "Run 'wg-quick up wg0' to start VPN"
|
||||
fi
|
||||
}
|
||||
|
||||
# Display configuration
|
||||
show_config() {
|
||||
echo ""
|
||||
echo "=== WireGuard Configuration ==="
|
||||
echo ""
|
||||
echo "Configuration file: $VPN_CONFIG_FILE"
|
||||
echo "VPN interface: $VPN_INTERFACE"
|
||||
echo ""
|
||||
|
||||
if [ -f "$VPN_CONFIG_FILE" ]; then
|
||||
echo "Current configuration:"
|
||||
echo "----------------------------------------"
|
||||
grep -v "^#" "$VPN_CONFIG_FILE" | grep -v "^$" | sed 's/^/ /'
|
||||
echo "----------------------------------------"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "Kill-switch script: $KILL_SWITCH_SCRIPT"
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Main execution
|
||||
main() {
|
||||
echo ""
|
||||
echo "=========================================="
|
||||
echo " WireGuard VPN Configuration"
|
||||
echo " Fedora Kinoite SAW"
|
||||
echo "=========================================="
|
||||
echo ""
|
||||
|
||||
check_root
|
||||
check_wireguard
|
||||
validate_config
|
||||
create_routing_table
|
||||
configure_firewall
|
||||
create_kill_switch
|
||||
configure_killswitch_service
|
||||
configure_dns
|
||||
show_config
|
||||
|
||||
echo ""
|
||||
echo "=========================================="
|
||||
echo " VPN Configuration Complete!"
|
||||
echo "=========================================="
|
||||
echo ""
|
||||
echo "Next steps:"
|
||||
echo "1. Edit WireGuard config: sudo nano $VPN_CONFIG_FILE"
|
||||
echo "2. Start VPN: sudo wg-quick up wg0"
|
||||
echo "3. Test VPN: ping -I wg0 8.8.8.8"
|
||||
echo "4. Check status: wg show"
|
||||
echo ""
|
||||
echo "To start VPN automatically on boot:"
|
||||
echo " sudo systemctl enable wg-quick@wg0"
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Run main function
|
||||
main "$@"
|
||||
1047
post-install/lockdown.sh
Normal file
1047
post-install/lockdown.sh
Normal file
File diff suppressed because it is too large
Load Diff
374
post-install/setup-updates.sh
Normal file
374
post-install/setup-updates.sh
Normal file
@ -0,0 +1,374 @@
|
||||
#!/bin/bash
|
||||
# Update Approval System for SAW
|
||||
# Version: 1.0
|
||||
# Date: 2026-04-02
|
||||
#
|
||||
# This script implements a controlled workflow for applying system updates,
|
||||
# ensuring that updates are reviewed and approved before being applied.
|
||||
#
|
||||
# Usage: sudo ./approve-update.sh
|
||||
#
|
||||
# SECURITY RATIONALE:
|
||||
# The update approval system provides:
|
||||
# 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
|
||||
# 6. Ensures updates don't break security controls
|
||||
#
|
||||
# How it works:
|
||||
# 1. Check for available updates
|
||||
# 2. Display update details to user
|
||||
# 3. Require explicit approval
|
||||
# 4. Log all update activities
|
||||
# 5. Enable easy rollback if needed
|
||||
#
|
||||
# Reference: https://docs.fedoraproject.org/en-US/fedora-coreos/atomic-updates/
|
||||
|
||||
set -e
|
||||
|
||||
# Configuration
|
||||
LOG_FILE="/var/log/update-approval.log"
|
||||
APPROVAL_LOG="/var/log/update-approval-records.log"
|
||||
LOCK_FILE="/var/lock/saw-update-lock"
|
||||
UPDATE_LOG="/var/log/saw-updates.log"
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
CYAN='\033[0;36m'
|
||||
NC='\033[0m'
|
||||
|
||||
# Print functions
|
||||
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"
|
||||
}
|
||||
|
||||
log_approval() {
|
||||
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
|
||||
local user=$(whoami)
|
||||
echo "[$timestamp] User: $user - $1" | tee -a "$APPROVAL_LOG"
|
||||
}
|
||||
|
||||
# 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 another update is running
|
||||
check_lock() {
|
||||
if [ -f "$LOCK_FILE" ]; then
|
||||
log_error "Another update is already running"
|
||||
log_info "Lock file: $LOCK_FILE"
|
||||
log_info "If this is an error, remove the lock file"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Create lock file
|
||||
touch "$LOCK_FILE"
|
||||
log_info "Lock file created"
|
||||
|
||||
# Set up trap to remove lock on exit
|
||||
trap 'rm -f "$LOCK_FILE"; log_info "Lock file removed"; exit' EXIT
|
||||
}
|
||||
|
||||
# Check for updates
|
||||
check_updates() {
|
||||
log_info "Checking for updates..."
|
||||
|
||||
# Use rpm-ostree to check for updates
|
||||
if ! rpm-ostree update --check 2>&1 | tee /tmp/update-check.txt; then
|
||||
# No updates available
|
||||
log_info "No updates available"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Save update information
|
||||
cp /tmp/update-check.txt /var/log/saw-latest-update.txt
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# Display update details
|
||||
display_updates() {
|
||||
echo ""
|
||||
echo "=========================================="
|
||||
echo " Available Updates"
|
||||
echo "=========================================="
|
||||
echo ""
|
||||
|
||||
if [ -f /tmp/update-check.txt ]; then
|
||||
cat /tmp/update-check.txt
|
||||
else
|
||||
echo "No update information available"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "=========================================="
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Get approval
|
||||
#
|
||||
# SECURITY RATIONALE:
|
||||
# The approval process ensures:
|
||||
# 1. Authorized personnel review updates
|
||||
# 2. Updates are documented
|
||||
# 3. No surprise updates
|
||||
# 4. Accountability for update decisions
|
||||
# 5. Audit trail for compliance
|
||||
#
|
||||
# We use a simple yes/no approval with logging.
|
||||
get_approval() {
|
||||
echo "=========================================="
|
||||
echo " Update Approval Request"
|
||||
echo "=========================================="
|
||||
echo ""
|
||||
echo "The following updates are available:"
|
||||
echo ""
|
||||
cat /tmp/update-check.txt | head -50
|
||||
echo ""
|
||||
echo "=========================================="
|
||||
echo ""
|
||||
|
||||
# Ask for approval
|
||||
read -p "Do you want to apply these updates? (yes/no): " response
|
||||
|
||||
case "$response" in
|
||||
yes|YES|y|Y)
|
||||
log_approval "APPROVED: User $USER approved update"
|
||||
return 0
|
||||
;;
|
||||
no|NO|n|N)
|
||||
log_approval "REJECTED: User $USER rejected update"
|
||||
log_info "Update cancelled by user"
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
log_error "Invalid response"
|
||||
log_info "Please enter 'yes' or 'no'"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Apply updates
|
||||
apply_updates() {
|
||||
log_info "Applying updates..."
|
||||
|
||||
# Show warning
|
||||
echo ""
|
||||
echo "=========================================="
|
||||
echo " WARNING"
|
||||
echo "=========================================="
|
||||
echo ""
|
||||
echo "This will apply system updates."
|
||||
echo "A reboot may be required."
|
||||
echo "Ensure you have backups."
|
||||
echo ""
|
||||
echo "Updates will be applied to:"
|
||||
echo "- System packages"
|
||||
echo "- Kernel"
|
||||
echo "- Security updates"
|
||||
echo ""
|
||||
echo "This process may take several minutes."
|
||||
echo ""
|
||||
|
||||
read -p "Are you sure you want to proceed? (yes/no): " confirm
|
||||
|
||||
if [ "$confirm" != "yes" ]; then
|
||||
log_approval "CANCELLED: User $USER cancelled update"
|
||||
log_info "Update cancelled"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
log_approval "STARTED: User $USER started update"
|
||||
|
||||
# Apply updates
|
||||
if rpm-ostree upgrade; then
|
||||
log_approval "COMPLETED: User $USER completed update"
|
||||
log_info "Updates applied successfully"
|
||||
log_info "A reboot is recommended"
|
||||
return 0
|
||||
else
|
||||
log_approval "FAILED: User $USER update failed"
|
||||
log_error "Update failed"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Show update history
|
||||
show_history() {
|
||||
echo ""
|
||||
echo "=========================================="
|
||||
echo " Update History"
|
||||
echo "=========================================="
|
||||
echo ""
|
||||
|
||||
if [ -f "$APPROVAL_LOG" ]; then
|
||||
cat "$APPROVAL_LOG" | tail -20
|
||||
else
|
||||
echo "No update history available"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Show update status
|
||||
show_status() {
|
||||
echo ""
|
||||
echo "=========================================="
|
||||
echo " Update Status"
|
||||
echo "=========================================="
|
||||
echo ""
|
||||
|
||||
# Show current deployment
|
||||
echo "Current Deployment:"
|
||||
rpm-ostree status | head -5
|
||||
echo ""
|
||||
|
||||
# Show pending deployment
|
||||
echo "Pending Deployment:"
|
||||
if [ -f /tmp/update-check.txt ]; then
|
||||
echo "Updates available for review"
|
||||
else
|
||||
echo "No updates pending"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Show last update
|
||||
echo "Last Update:"
|
||||
if [ -f /var/log/saw-latest-update.txt ]; then
|
||||
echo "Last update check: $(stat -c %y /var/log/saw-latest-update.txt)"
|
||||
else
|
||||
echo "No recent update check"
|
||||
fi
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Setup update check cron job
|
||||
setup_cron() {
|
||||
log_info "Setting up update check cron job..."
|
||||
|
||||
# Create cron job for daily check
|
||||
cat > /etc/cron.d/saw-update-check << 'CRONJOB'
|
||||
# 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
|
||||
CRONJOB
|
||||
|
||||
chmod 644 /etc/cron.d/saw-update-check
|
||||
log_info "Cron job created"
|
||||
log_info "Updates will be checked daily at 6:00 AM"
|
||||
}
|
||||
|
||||
# Display help
|
||||
show_help() {
|
||||
cat << 'HELP'
|
||||
Update Approval System for SAW
|
||||
==============================
|
||||
|
||||
Usage: ./approve-update.sh [OPTIONS]
|
||||
|
||||
Options:
|
||||
--check Check for available updates
|
||||
--approve Check and prompt for approval
|
||||
--apply Apply updates (requires approval first)
|
||||
--history Show update history
|
||||
--status Show update status
|
||||
--cron Set up daily update check
|
||||
--help Show this help message
|
||||
|
||||
Examples:
|
||||
# Check for updates
|
||||
./approve-update.sh --check
|
||||
|
||||
# Check and prompt for approval
|
||||
./approve-update.sh --approve
|
||||
|
||||
# Apply updates
|
||||
./approve-update.sh --apply
|
||||
|
||||
Security Notes:
|
||||
- Updates require explicit approval
|
||||
- All approval decisions are logged
|
||||
- Updates can be reviewed before applying
|
||||
- Rollback is available if needed
|
||||
|
||||
Reference: https://docs.fedoraproject.org/en-US/fedora-coreos/atomic-updates/
|
||||
HELP
|
||||
}
|
||||
|
||||
# Main execution
|
||||
main() {
|
||||
echo ""
|
||||
echo "=========================================="
|
||||
echo " Update Approval System"
|
||||
echo " Fedora Kinoite SAW"
|
||||
echo "=========================================="
|
||||
echo ""
|
||||
|
||||
check_root
|
||||
check_lock
|
||||
|
||||
case "${1:-}" in
|
||||
--check)
|
||||
if check_updates; then
|
||||
display_updates
|
||||
log_info "Updates available for review"
|
||||
else
|
||||
log_info "No updates available"
|
||||
fi
|
||||
;;
|
||||
--approve)
|
||||
check_updates
|
||||
display_updates
|
||||
get_approval
|
||||
;;
|
||||
--apply)
|
||||
if [ ! -f /tmp/update-check.txt ]; then
|
||||
log_error "No update information available"
|
||||
log_info "Run with --check first"
|
||||
exit 1
|
||||
fi
|
||||
display_updates
|
||||
get_approval
|
||||
apply_updates
|
||||
;;
|
||||
--history)
|
||||
show_history
|
||||
;;
|
||||
--status)
|
||||
show_status
|
||||
;;
|
||||
--cron)
|
||||
setup_cron
|
||||
;;
|
||||
--help|-h)
|
||||
show_help
|
||||
;;
|
||||
*)
|
||||
show_help
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Run main function
|
||||
main "$@"
|
||||
329
scripts/check-verification.sh
Normal file
329
scripts/check-verification.sh
Normal file
@ -0,0 +1,329 @@
|
||||
#!/bin/bash
|
||||
# Package Verification Check Script for SAW
|
||||
# Version: 1.0
|
||||
# Date: 2026-04-02
|
||||
#
|
||||
# This script checks the package verification configuration and verifies
|
||||
# that all installed packages are properly signed.
|
||||
#
|
||||
# Usage: sudo ./check-verification.sh
|
||||
#
|
||||
# SECURITY RATIONALE:
|
||||
# Regular verification of all installed packages ensures:
|
||||
# 1. No packages were tampered with after installation
|
||||
# 2. No malicious packages were added
|
||||
# 3. All packages are still properly signed
|
||||
# 4. Any compromised packages are detected early
|
||||
#
|
||||
# This should be run regularly (weekly/monthly) as part of security
|
||||
# monitoring and compliance verification.
|
||||
#
|
||||
# Reference: https://docs.fedoraproject.org/en-US/fedora-coreos/security-verification/
|
||||
|
||||
set -e
|
||||
|
||||
# Configuration
|
||||
CA_KEY_PATH="/etc/pki/rpm-gpg/RPM-GPG-KEY-custom-ca"
|
||||
CRL_FILE="/etc/pki/ca-trust/source/anchors/crl.pem"
|
||||
LOG_FILE="/var/log/package-verification-check.log"
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
CYAN='\033[0;36m'
|
||||
NC='\033[0m'
|
||||
|
||||
# Print functions
|
||||
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 CA certificate
|
||||
check_ca_certificate() {
|
||||
echo -e "${CYAN}1. CA Certificate Check${NC}"
|
||||
echo "=========================================="
|
||||
|
||||
if [ -f "$CA_KEY_PATH" ]; then
|
||||
log_info "PASS: CA certificate installed"
|
||||
echo " Path: $CA_KEY_PATH"
|
||||
echo " Status: PRESENT"
|
||||
|
||||
# Show certificate details
|
||||
openssl x509 -in "$CA_KEY_PATH" -noout -subject -issuer -dates 2>/dev/null | sed 's/^/ /'
|
||||
else
|
||||
log_error "FAIL: CA certificate not found"
|
||||
echo " Path: $CA_KEY_PATH"
|
||||
echo " Status: MISSING"
|
||||
echo " Action: Copy CA certificate to $CA_KEY_PATH"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Check DNF configuration
|
||||
check_dnf_config() {
|
||||
echo -e "${CYAN}2. DNF Configuration Check${NC}"
|
||||
echo "=========================================="
|
||||
|
||||
if [ ! -f /etc/dnf/dnf.conf ]; then
|
||||
log_error "FAIL: DNF configuration not found"
|
||||
echo " Status: FILE NOT FOUND"
|
||||
echo " Action: Create /etc/dnf/dnf.conf"
|
||||
echo ""
|
||||
return
|
||||
fi
|
||||
|
||||
# Check gpgcheck
|
||||
if grep -q "^gpgcheck=1" /etc/dnf/dnf.conf; then
|
||||
log_info "PASS: Package GPG check enabled"
|
||||
echo " Status: ENABLED"
|
||||
else
|
||||
log_error "FAIL: Package GPG check not enabled"
|
||||
echo " Status: DISABLED"
|
||||
echo " Action: Add 'gpgcheck=1' to /etc/dnf/dnf.conf"
|
||||
fi
|
||||
|
||||
# Check repo_gpgcheck
|
||||
if grep -q "^repo_gpgcheck=1" /etc/dnf/dnf.conf; then
|
||||
log_info "PASS: Repository GPG check enabled"
|
||||
echo " Status: ENABLED"
|
||||
else
|
||||
log_error "FAIL: Repository GPG check not enabled"
|
||||
echo " Status: DISABLED"
|
||||
echo " Action: Add 'repo_gpgcheck=1' to /etc/dnf/dnf.conf"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Check RPM database
|
||||
check_rpm_database() {
|
||||
echo -e "${CYAN}3. RPM Database Check${NC}"
|
||||
echo "=========================================="
|
||||
|
||||
# Check if CA key is imported
|
||||
if rpm -q gpg-pubkey 2>/dev/null | grep -q "$(basename $CA_KEY_PATH)"; then
|
||||
log_info "PASS: CA key imported into RPM database"
|
||||
echo " Status: IMPORTED"
|
||||
else
|
||||
log_warning "WARNING: CA key not imported into RPM database"
|
||||
echo " Status: NOT IMPORTED"
|
||||
echo " Action: Run 'rpm --import $CA_KEY_PATH'"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Check audit script
|
||||
check_audit_script() {
|
||||
echo -e "${CYAN}4. Audit Script Check${NC}"
|
||||
echo "=========================================="
|
||||
|
||||
if [ -f /usr/local/bin/verify-package.sh ]; then
|
||||
log_info "PASS: Package verification script exists"
|
||||
echo " Path: /usr/local/bin/verify-package.sh"
|
||||
echo " Status: PRESENT"
|
||||
|
||||
if [ -x /usr/local/bin/verify-package.sh ]; then
|
||||
log_info "PASS: Script is executable"
|
||||
echo " Status: EXECUTABLE"
|
||||
else
|
||||
log_warning "WARNING: Script is not executable"
|
||||
echo " Status: NOT EXECUTABLE"
|
||||
echo " Action: Run 'chmod +x /usr/local/bin/verify-package.sh'"
|
||||
fi
|
||||
else
|
||||
log_warning "WARNING: Package verification script not found"
|
||||
echo " Status: NOT FOUND"
|
||||
echo " Action: Run the lockdown script to create the script"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Check CRL file
|
||||
check_crl() {
|
||||
echo -e "${CYAN}5. CRL Check${NC}"
|
||||
echo "=========================================="
|
||||
|
||||
if [ -f "$CRL_FILE" ]; then
|
||||
log_info "PASS: CRL file exists"
|
||||
echo " Path: $CRL_FILE"
|
||||
echo " Size: $(du -h "$CRL_FILE" | cut -f1)"
|
||||
|
||||
# Check if CRL is valid
|
||||
if openssl crl -in "$CRL_FILE" -noout 2>/dev/null; then
|
||||
log_info "PASS: CRL is valid"
|
||||
echo " Status: VALID"
|
||||
|
||||
# Show CRL dates
|
||||
openssl crl -in "$CRL_FILE" -noout -text 2>/dev/null | grep -E "(Last Update|Next Update):" | sed 's/^/ /'
|
||||
else
|
||||
log_error "FAIL: CRL is invalid"
|
||||
echo " Status: INVALID"
|
||||
fi
|
||||
else
|
||||
log_warning "WARNING: CRL file not found"
|
||||
echo " Path: $CRL_FILE"
|
||||
echo " Status: NOT FOUND"
|
||||
echo " Action: Run daily-crl-update.sh to download CRL"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Check firewall rules
|
||||
check_firewall() {
|
||||
echo -e "${CYAN}6. Firewall Check${NC}"
|
||||
echo "=========================================="
|
||||
|
||||
if command -v firewall-cmd &> /dev/null; then
|
||||
if firewall-cmd --state 2>/dev/null | grep -q "running"; then
|
||||
log_info "PASS: Firewall is running"
|
||||
echo " Status: RUNNING"
|
||||
|
||||
# Show active rules
|
||||
echo " Active Zones:"
|
||||
firewall-cmd --list-zones 2>/dev/null | sed 's/^/ /'
|
||||
|
||||
echo " Active Services:"
|
||||
firewall-cmd --list-services 2>/dev/null | sed 's/^/ /'
|
||||
else
|
||||
log_warning "WARNING: Firewall is not running"
|
||||
echo " Status: NOT RUNNING"
|
||||
fi
|
||||
else
|
||||
log_warning "WARNING: Firewall command not found"
|
||||
echo " Status: COMMAND NOT FOUND"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Check SELinux
|
||||
check_selinux() {
|
||||
echo -e "${CYAN}7. SELinux Check${NC}"
|
||||
echo "=========================================="
|
||||
|
||||
local selinux_status=$(sestatus 2>/dev/null || echo "SELinux not available")
|
||||
|
||||
if echo "$selinux_status" | grep -q "Current mode: enforcing"; then
|
||||
log_info "PASS: SELinux is enforcing"
|
||||
echo " Status: ENFORCING"
|
||||
else
|
||||
log_warning "WARNING: SELinux is not enforcing"
|
||||
echo " Status: $(echo "$selinux_status" | grep "Current mode" | awk '{print $3}')"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Check auditd
|
||||
check_auditd() {
|
||||
echo -e "${CYAN}8. Auditd Check${NC}"
|
||||
echo "=========================================="
|
||||
|
||||
if systemctl is-active auditd >/dev/null 2>&1; then
|
||||
log_info "PASS: Auditd is running"
|
||||
echo " Status: RUNNING"
|
||||
else
|
||||
log_warning "WARNING: Auditd is not running"
|
||||
echo " Status: NOT RUNNING"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Check for unsigned packages
|
||||
check_unsigned_packages() {
|
||||
echo -e "${CYAN}9. Unsigned Packages Check${NC}"
|
||||
echo "=========================================="
|
||||
|
||||
local unsigned_count=0
|
||||
local total_count=0
|
||||
|
||||
# Get list of installed packages
|
||||
while IFS= read -r pkg; do
|
||||
# Check if package has signature
|
||||
if ! rpm -q "$pkg" --queryformat='%{SIGPGP:pgpsig}\n' 2>/dev/null | grep -q "0x"; then
|
||||
log_warning "WARNING: Package has no signature: $pkg"
|
||||
((unsigned_count++))
|
||||
fi
|
||||
((total_count++))
|
||||
done < <(rpm -qa --queryformat='%{NAME}\n' 2>/dev/null | head -100)
|
||||
|
||||
echo " Total packages checked: $total_count"
|
||||
echo " Unsigned packages found: $unsigned_count"
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Generate summary
|
||||
generate_summary() {
|
||||
echo ""
|
||||
echo "=========================================="
|
||||
echo " Package Verification Summary"
|
||||
echo " Date: $(date '+%Y-%m-%d %H:%M:%S')"
|
||||
echo " Hostname: $(hostname)"
|
||||
echo "=========================================="
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Main execution
|
||||
main() {
|
||||
echo ""
|
||||
echo "=========================================="
|
||||
echo " Package Verification Check"
|
||||
echo " Fedora Kinoite SAW"
|
||||
echo " Version: 1.0"
|
||||
echo "=========================================="
|
||||
echo ""
|
||||
|
||||
check_root
|
||||
check_ca_certificate
|
||||
check_dnf_config
|
||||
check_rpm_database
|
||||
check_audit_script
|
||||
check_crl
|
||||
check_firewall
|
||||
check_selinux
|
||||
check_auditd
|
||||
check_unsigned_packages
|
||||
generate_summary
|
||||
|
||||
echo ""
|
||||
echo "=========================================="
|
||||
echo " Check Complete!"
|
||||
echo "=========================================="
|
||||
echo ""
|
||||
echo "Recommendations:"
|
||||
echo "- Review any WARNING or ERROR items"
|
||||
echo "- Run this script weekly for critical systems"
|
||||
echo "- Run this script monthly for standard systems"
|
||||
echo "- Keep CA certificates secure"
|
||||
echo "- Regularly update CRL"
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Run main function
|
||||
main "$@"
|
||||
149
scripts/daily-crl-update.sh
Normal file
149
scripts/daily-crl-update.sh
Normal file
@ -0,0 +1,149 @@
|
||||
#!/bin/bash
|
||||
# Daily CRL Update Script for SAW
|
||||
# Version: 1.0
|
||||
# Date: 2026-04-02
|
||||
#
|
||||
# This script is designed to be run as a cron job to keep the Certificate
|
||||
# Revocation List (CRL) up to date. It downloads, verifies, and installs
|
||||
# the CRL from your CA server.
|
||||
#
|
||||
# This script should be scheduled to run daily, typically at 2:00 AM.
|
||||
#
|
||||
# Setup:
|
||||
# 1. Copy this script to /usr/local/bin/daily-crl-update.sh
|
||||
# 2. Make it executable: chmod +x /usr/local/bin/daily-crl-update.sh
|
||||
# 3. Configure the CRL_URL variable below with your CA server
|
||||
# 4. Add to crontab: 0 2 * * * root /usr/local/bin/daily-crl-update.sh
|
||||
#
|
||||
# SECURITY RATIONALE:
|
||||
# Regular CRL updates are critical because:
|
||||
# 1. CRLs are updated when certificates are revoked
|
||||
# 2. Outdated CRLs may allow revoked certificates
|
||||
# 3. Daily updates ensure current revocation status
|
||||
# 4. Meets compliance requirements for regular updates
|
||||
# 5. Provides defense against compromised CA keys
|
||||
#
|
||||
# Reference: https://www.openssl.org/docs/man1.1.1/man1/crl.html
|
||||
|
||||
set -e
|
||||
|
||||
# Configuration
|
||||
# TODO: Update this with your CA server URL
|
||||
CRL_URL="https://your-ca-server.com/crl.pem"
|
||||
CRL_CACHE_FILE="/var/cache/crl/crl.pem"
|
||||
CA_KEY_PATH="/etc/pki/rpm-gpg/RPM-GPG-KEY-custom-ca"
|
||||
LOG_FILE="/var/log/crl-update.log"
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
# Print functions
|
||||
log_info() {
|
||||
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
|
||||
echo -e "${GREEN}[$timestamp]${NC} [INFO] $1" | tee -a "$LOG_FILE"
|
||||
}
|
||||
|
||||
log_warning() {
|
||||
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
|
||||
echo -e "${YELLOW}[$timestamp]${NC} [WARNING] $1" | tee -a "$LOG_FILE"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
|
||||
echo -e "${RED}[$timestamp]${NC} [ERROR] $1" | tee -a "$LOG_FILE"
|
||||
}
|
||||
|
||||
# Main execution
|
||||
main() {
|
||||
echo "=========================================="
|
||||
echo " Daily CRL Update"
|
||||
echo " Fedora Kinoite SAW"
|
||||
echo "=========================================="
|
||||
echo ""
|
||||
|
||||
# Check if CRL URL is configured
|
||||
if [ "$CRL_URL" = "https://your-ca-server.com/crl.pem" ]; then
|
||||
log_error "CRL URL not configured"
|
||||
log_info "Please edit this script and set CRL_URL to your CA server"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Create cache directory
|
||||
mkdir -p /var/cache/crl
|
||||
|
||||
# Download CRL
|
||||
log_info "Downloading CRL from: $CRL_URL"
|
||||
|
||||
if ! curl -s -o "$CRL_CACHE_FILE" "$CRL_URL"; then
|
||||
log_error "Failed to download CRL"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log_info "CRL downloaded successfully"
|
||||
|
||||
# Verify CRL
|
||||
log_info "Verifying CRL signature..."
|
||||
|
||||
if [ ! -f "$CA_KEY_PATH" ]; then
|
||||
log_error "CA key not found: $CA_KEY_PATH"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! openssl crl -in "$CRL_CACHE_FILE" -CAfile "$CA_KEY_PATH" -noout 2>/dev/null; then
|
||||
log_error "CRL verification failed"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log_info "CRL verified successfully"
|
||||
|
||||
# Get CRL information
|
||||
local this_update=$(openssl crl -in "$CRL_CACHE_FILE" -noout -text 2>/dev/null | grep "Last Update:" | head -1)
|
||||
log_info "CRL Last Update: $this_update"
|
||||
|
||||
# Update CA trust
|
||||
log_info "Updating CA trust database..."
|
||||
|
||||
# Copy CRL to CA trust source
|
||||
mkdir -p /etc/pki/ca-trust/source/anchors
|
||||
cp "$CRL_CACHE_FILE" /etc/pki/ca-trust/source/anchors/crl.pem
|
||||
|
||||
# Update CA trust
|
||||
update-ca-trust extract
|
||||
|
||||
log_info "CA trust database updated"
|
||||
|
||||
# Check for revoked certificates in installed packages
|
||||
log_info "Checking for revoked certificates..."
|
||||
|
||||
local revoked_count=0
|
||||
|
||||
# Check a sample of packages (first 100)
|
||||
while IFS= read -r pkg; do
|
||||
# Extract certificate ID from signature
|
||||
local cert_id=$(echo "$pkg" | awk '{print $2}' | cut -d: -f2)
|
||||
|
||||
if [ -n "$cert_id" ]; then
|
||||
# Check if certificate is in CRL
|
||||
if openssl crl -in "$CRL_CACHE_FILE" -noout -text 2>/dev/null | grep -q "$cert_id"; then
|
||||
log_error "REVOKED: Package signed with revoked certificate: $cert_id"
|
||||
((revoked_count++))
|
||||
fi
|
||||
fi
|
||||
done < <(rpm -qa --queryformat='%{NAME} %{SIGPGP:pgpsig}\n' 2>/dev/null | head -100)
|
||||
|
||||
if [ $revoked_count -gt 0 ]; then
|
||||
log_warning "WARNING: $revoked_count packages have revoked certificates!"
|
||||
log_info "These packages should be reviewed"
|
||||
else
|
||||
log_info "No revoked certificates found in checked packages"
|
||||
fi
|
||||
|
||||
log_info "CRL update completed successfully"
|
||||
}
|
||||
|
||||
# Run main function
|
||||
main "$@"
|
||||
609
scripts/security-audit.sh
Normal file
609
scripts/security-audit.sh
Normal file
@ -0,0 +1,609 @@
|
||||
#!/bin/bash
|
||||
# Security Audit Script for SAW
|
||||
# Version: 1.0
|
||||
# Date: 2026-04-02
|
||||
#
|
||||
# This script performs a comprehensive security audit of the Fedora Kinoite SAW system.
|
||||
# It checks all security controls and provides a detailed report of the system's
|
||||
# security posture.
|
||||
#
|
||||
# Usage: sudo ./security-audit.sh
|
||||
#
|
||||
# SECURITY RATIONALE:
|
||||
# Regular security audits are essential for:
|
||||
# 1. Verifying security controls are still in place
|
||||
# 2. Detecting unauthorized changes
|
||||
# 3. Identifying potential security issues
|
||||
# 4. Providing compliance documentation
|
||||
# 5. Tracking security posture over time
|
||||
#
|
||||
# This script should be run:
|
||||
# - Weekly for critical systems
|
||||
# - Monthly for standard systems
|
||||
# - After any system changes
|
||||
# - Before and after security updates
|
||||
#
|
||||
# Reference: NSA Linux Security Hardening Guide, CIS Fedora Benchmarks
|
||||
|
||||
set -e
|
||||
|
||||
# Configuration
|
||||
LOG_FILE="/var/log/security-audit.log"
|
||||
AUDIT_DATE=$(date '+%Y-%m-%d %H:%M:%S')
|
||||
AUDIT_REPORT="/var/log/security-audit-report-$(date +%Y%m%d).txt"
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
CYAN='\033[0;36m'
|
||||
NC='\033[0m'
|
||||
|
||||
# Print functions
|
||||
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 sudo access
|
||||
#
|
||||
# SECURITY RATIONALE:
|
||||
# Sudo access should be disabled in SAW because:
|
||||
# 1. Prevents privilege escalation attacks
|
||||
# 2. Reduces attack surface
|
||||
# 3. Enforces least privilege principle
|
||||
# 4. Makes lateral movement harder
|
||||
# 5. All admin operations should go through controlled workflows
|
||||
#
|
||||
# This check verifies:
|
||||
# - User is not in wheel group
|
||||
# - Root SSH login is disabled
|
||||
# - No sudo commands available
|
||||
check_sudo_access() {
|
||||
echo -e "${CYAN}1. Sudo Access Check${NC}"
|
||||
echo "=========================================="
|
||||
|
||||
local user=$(whoami)
|
||||
|
||||
# Check if user is in wheel group
|
||||
if id -nG "$user" | grep -q wheel; then
|
||||
log_error "FAIL: User '$user' is in wheel group"
|
||||
echo " Status: SUDO ENABLED (Security Risk)"
|
||||
return 1
|
||||
else
|
||||
log_info "PASS: User '$user' is not in wheel group"
|
||||
echo " Status: SUDO DISABLED"
|
||||
fi
|
||||
|
||||
# Check root SSH login
|
||||
if [ -f /etc/ssh/sshd_config ]; then
|
||||
if grep -q "^PermitRootLogin yes" /etc/ssh/sshd_config || \
|
||||
grep -q "^#PermitRootLogin yes" /etc/ssh/sshd_config; then
|
||||
log_warning "WARNING: Root SSH login may be enabled"
|
||||
echo " Check: /etc/ssh/sshd_config"
|
||||
else
|
||||
log_info "PASS: Root SSH login is disabled"
|
||||
echo " Status: ROOT SSH DISABLED"
|
||||
fi
|
||||
fi
|
||||
|
||||
echo ""
|
||||
return 0
|
||||
}
|
||||
|
||||
# Check SELinux
|
||||
#
|
||||
# SECURITY RATIONALE:
|
||||
# SELinux (Security-Enhanced Linux) provides:
|
||||
# 1. Mandatory Access Control (MAC)
|
||||
# 2. Protection against privilege escalation
|
||||
# 3. Application containment
|
||||
# 4. Fine-grained access control
|
||||
# 5. Defense in depth
|
||||
#
|
||||
# SELinux should be in enforcing mode to provide protection.
|
||||
check_selinux() {
|
||||
echo -e "${CYAN}2. SELinux Check${NC}"
|
||||
echo "=========================================="
|
||||
|
||||
local selinux_status=$(sestatus 2>/dev/null || echo "SELinux not available")
|
||||
|
||||
if echo "$selinux_status" | grep -q "Current mode: enforcing"; then
|
||||
log_info "PASS: SELinux is in enforcing mode"
|
||||
echo " Status: ENFORCING"
|
||||
echo " Mode: Mandatory Access Control active"
|
||||
else
|
||||
log_warning "WARNING: SELinux is not in enforcing mode"
|
||||
echo " Status: $(echo "$selinux_status" | grep "Current mode" | awk '{print $3}')"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
return 0
|
||||
}
|
||||
|
||||
# Check audit daemon
|
||||
#
|
||||
# SECURITY RATIONALE:
|
||||
# auditd (audit daemon) provides:
|
||||
# 1. Comprehensive system auditing
|
||||
# 2. Logging of security-relevant events
|
||||
# 3. Detection of unauthorized access
|
||||
# 4. Forensic capabilities
|
||||
# 5. Compliance with security standards
|
||||
#
|
||||
# auditd should be running to ensure security events are logged.
|
||||
check_auditd() {
|
||||
echo -e "${CYAN}3. Audit Daemon Check${NC}"
|
||||
echo "=========================================="
|
||||
|
||||
if systemctl is-active auditd >/dev/null 2>&1; then
|
||||
log_info "PASS: Auditd is running"
|
||||
echo " Status: RUNNING"
|
||||
echo " Logging: All security events"
|
||||
else
|
||||
log_warning "WARNING: Auditd is not running"
|
||||
echo " Status: NOT RUNNING"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
return 0
|
||||
}
|
||||
|
||||
# Check firewall
|
||||
#
|
||||
# SECURITY RATIONALE:
|
||||
# The firewall provides:
|
||||
# 1. Network traffic filtering
|
||||
# 2. Egress filtering (outbound)
|
||||
# 3. Ingress filtering (inbound)
|
||||
# 4. Network segmentation
|
||||
# 5. Protection against network attacks
|
||||
#
|
||||
# The firewall should be:
|
||||
# - Active
|
||||
# - Configured with default deny
|
||||
# - Only allowing necessary traffic
|
||||
check_firewall() {
|
||||
echo -e "${CYAN}4. Firewall Check${NC}"
|
||||
echo "=========================================="
|
||||
|
||||
if command -v firewall-cmd &> /dev/null; then
|
||||
if firewall-cmd --state 2>/dev/null | grep -q "running"; then
|
||||
log_info "PASS: Firewall is running"
|
||||
echo " Status: RUNNING"
|
||||
|
||||
# Show active zones
|
||||
echo " Active Zones:"
|
||||
firewall-cmd --list-zones 2>/dev/null | sed 's/^/ /'
|
||||
|
||||
# Show services
|
||||
echo " Active Services:"
|
||||
firewall-cmd --list-services 2>/dev/null | sed 's/^/ /'
|
||||
else
|
||||
log_warning "WARNING: Firewall is not running"
|
||||
echo " Status: NOT RUNNING"
|
||||
fi
|
||||
else
|
||||
log_warning "WARNING: Firewall command not found"
|
||||
echo " Status: COMMAND NOT FOUND"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
return 0
|
||||
}
|
||||
|
||||
# Check VPN
|
||||
#
|
||||
# SECURITY RATIONALE:
|
||||
# WireGuard VPN provides:
|
||||
# 1. Encrypted network traffic
|
||||
# 2. Protection against network eavesdropping
|
||||
# 3. Traffic routing through secure tunnel
|
||||
# 4. Kill-switch protection
|
||||
# 5. Centralized network control
|
||||
#
|
||||
# VPN should be:
|
||||
# - Active and connected
|
||||
# - Routing all traffic
|
||||
# - Using strong encryption
|
||||
check_vpn() {
|
||||
echo -e "${CYAN}5. VPN Check${NC}"
|
||||
echo "=========================================="
|
||||
|
||||
if command -v wg &> /dev/null; then
|
||||
if wg show >/dev/null 2>&1; then
|
||||
log_info "PASS: WireGuard is active"
|
||||
echo " Status: ACTIVE"
|
||||
echo " Interface: $(wg show | grep 'interface:' | awk '{print $2}')"
|
||||
|
||||
# Show peers
|
||||
echo " Peers:"
|
||||
wg show 2>/dev/null | grep 'peer:' | sed 's/^/ /'
|
||||
|
||||
# Show allowed IPs
|
||||
echo " Allowed IPs:"
|
||||
wg show 2>/dev/null | grep 'allowed-ips:' | sed 's/^/ /'
|
||||
else
|
||||
log_warning "WARNING: WireGuard is not active"
|
||||
echo " Status: NOT ACTIVE"
|
||||
fi
|
||||
else
|
||||
log_warning "WARNING: WireGuard not installed"
|
||||
echo " Status: NOT INSTALLED"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
return 0
|
||||
}
|
||||
|
||||
# Check DNS configuration
|
||||
#
|
||||
# SECURITY RATIONALE:
|
||||
# DNS lockdown provides:
|
||||
# 1. Prevention of DNS leaks
|
||||
# 2. All DNS through VPN tunnel
|
||||
# 3. Protection against DNS tracking
|
||||
# 4. Centralized DNS logging
|
||||
# 5. Block malicious domains
|
||||
#
|
||||
# DNS should be:
|
||||
# - Configured to use VPN gateway only
|
||||
# - Not allowing external DNS
|
||||
# - Using secure DNS resolution
|
||||
check_dns() {
|
||||
echo -e "${CYAN}6. DNS Configuration Check${NC}"
|
||||
echo "=========================================="
|
||||
|
||||
if [ -f /etc/resolv.conf ]; then
|
||||
local dns_servers=$(grep "^nameserver" /etc/resolv.conf | awk '{print $2}')
|
||||
|
||||
if echo "$dns_servers" | grep -q "127.0.0.1"; then
|
||||
log_info "PASS: DNS configured to use local resolver"
|
||||
echo " Status: LOCAL DNS"
|
||||
echo " Servers: $dns_servers"
|
||||
elif echo "$dns_servers" | grep -q "10.0.0.1"; then
|
||||
log_info "PASS: DNS configured to use VPN gateway"
|
||||
echo " Status: VPN DNS"
|
||||
echo " Servers: $dns_servers"
|
||||
else
|
||||
log_warning "WARNING: DNS may not be properly configured"
|
||||
echo " Status: EXTERNAL DNS"
|
||||
echo " Servers: $dns_servers"
|
||||
fi
|
||||
else
|
||||
log_warning "WARNING: /etc/resolv.conf not found"
|
||||
echo " Status: FILE NOT FOUND"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
return 0
|
||||
}
|
||||
|
||||
# Check package verification
|
||||
#
|
||||
# SECURITY RATIONALE:
|
||||
# Package verification ensures:
|
||||
# 1. Only signed packages installed
|
||||
# 2. Package integrity maintained
|
||||
# 3. Authentic package source
|
||||
# 4. Protection against tampering
|
||||
# 5. Supply chain attack prevention
|
||||
#
|
||||
# Package verification should be:
|
||||
# - Enabled in DNF configuration
|
||||
# - CA key imported
|
||||
# - CRL checking enabled
|
||||
check_package_verification() {
|
||||
echo -e "${CYAN}7. Package Verification Check${NC}"
|
||||
echo "=========================================="
|
||||
|
||||
# Check CA key
|
||||
if [ -f /etc/pki/rpm-gpg/RPM-GPG-KEY-custom-ca ]; then
|
||||
log_info "PASS: CA key installed"
|
||||
echo " Status: CA KEY PRESENT"
|
||||
else
|
||||
log_warning "WARNING: CA key not found"
|
||||
echo " Status: CA KEY MISSING"
|
||||
fi
|
||||
|
||||
# Check DNF configuration
|
||||
if [ -f /etc/dnf/dnf.conf ]; then
|
||||
if grep -q "^gpgcheck=1" /etc/dnf/dnf.conf; then
|
||||
log_info "PASS: Package signature verification enabled"
|
||||
echo " Status: GPG CHECK ENABLED"
|
||||
else
|
||||
log_warning "WARNING: Package signature verification not enabled"
|
||||
echo " Status: GPG CHECK DISABLED"
|
||||
fi
|
||||
|
||||
if grep -q "^repo_gpgcheck=1" /etc/dnf/dnf.conf; then
|
||||
log_info "PASS: Repository signature verification enabled"
|
||||
echo " Status: REPO GPG CHECK ENABLED"
|
||||
else
|
||||
log_warning "WARNING: Repository signature verification not enabled"
|
||||
echo " Status: REPO GPG CHECK DISABLED"
|
||||
fi
|
||||
else
|
||||
log_warning "WARNING: DNF configuration not found"
|
||||
echo " Status: FILE NOT FOUND"
|
||||
fi
|
||||
|
||||
# Check audit script
|
||||
if [ -f /usr/local/bin/verify-package.sh ]; then
|
||||
log_info "PASS: Package verification script exists"
|
||||
echo " Status: SCRIPT PRESENT"
|
||||
else
|
||||
log_warning "WARNING: Package verification script not found"
|
||||
echo " Status: SCRIPT MISSING"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
return 0
|
||||
}
|
||||
|
||||
# Check kernel hardening
|
||||
#
|
||||
# SECURITY RATIONALE:
|
||||
# Kernel hardening provides:
|
||||
# 1. Protection against kernel exploits
|
||||
# 2. Memory layout randomization
|
||||
# 3. Protection against buffer overflows
|
||||
# 4. Control Flow Integrity
|
||||
# 5. Defense in depth
|
||||
#
|
||||
# Kernel hardening should include:
|
||||
# - ASLR enabled
|
||||
# - NX bit enabled
|
||||
# - Stack protection
|
||||
# - SELinux enabled
|
||||
check_kernel_hardening() {
|
||||
echo -e "${CYAN}8. Kernel Hardening Check${NC}"
|
||||
echo "=========================================="
|
||||
|
||||
# Check ASLR
|
||||
if [ -f /proc/sys/kernel/randomize_va_space ]; then
|
||||
local aslr=$(cat /proc/sys/kernel/randomize_va_space)
|
||||
if [ "$aslr" = "2" ]; then
|
||||
log_info "PASS: ASLR enabled"
|
||||
echo " Status: FULL ASLR (Randomize everything)"
|
||||
elif [ "$aslr" = "1" ]; then
|
||||
log_info "PASS: ASLR enabled"
|
||||
echo " Status: BASIC ASLR (Standard)"
|
||||
else
|
||||
log_warning "WARNING: ASLR not enabled"
|
||||
echo " Status: ASLR DISABLED"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Check SELinux
|
||||
if command -v getenforce &> /dev/null; then
|
||||
if [ "$(getenforce)" = "Enforcing" ]; then
|
||||
log_info "PASS: SELinux enforcing"
|
||||
echo " Status: ENFORCING"
|
||||
else
|
||||
log_warning "WARNING: SELinux not enforcing"
|
||||
echo " Status: $(getenforce)"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Check for ExecShield
|
||||
if [ -f /proc/sys/kernel/exec-shield ]; then
|
||||
local execshield=$(cat /proc/sys/kernel/exec-shield)
|
||||
if [ "$execshield" = "1" ]; then
|
||||
log_info "PASS: ExecShield enabled"
|
||||
echo " Status: EXEC SHIELD ENABLED"
|
||||
fi
|
||||
fi
|
||||
|
||||
echo ""
|
||||
return 0
|
||||
}
|
||||
|
||||
# Check for suspicious processes
|
||||
#
|
||||
# SECURITY RATIONALE:
|
||||
# Suspicious processes check provides:
|
||||
# 1. Detection of unauthorized processes
|
||||
# 2. Identification of potential malware
|
||||
# 3. Monitoring of system activity
|
||||
# 4. Early detection of compromise
|
||||
#
|
||||
# This check looks for:
|
||||
# - Unknown processes
|
||||
# - Processes running as root
|
||||
# - Suspicious network processes
|
||||
check_suspicious_processes() {
|
||||
echo -e "${CYAN}9. Suspicious Processes Check${NC}"
|
||||
echo "=========================================="
|
||||
|
||||
# Check for processes running as root
|
||||
local root_processes=$(ps aux | grep "root" | wc -l)
|
||||
if [ "$root_processes" -gt 50 ]; then
|
||||
log_warning "WARNING: High number of root processes: $root_processes"
|
||||
echo " Status: $root_processes root processes running"
|
||||
else
|
||||
log_info "PASS: Reasonable number of root processes"
|
||||
echo " Status: $root_processes root processes running"
|
||||
fi
|
||||
|
||||
# Check for suspicious processes
|
||||
local suspicious=$(ps aux | grep -E "(nc|netcat|nmap|hydra|metasploit)" | grep -v grep)
|
||||
if [ -n "$suspicious" ]; then
|
||||
log_error "FAIL: Suspicious processes detected!"
|
||||
echo " Status: SUSPICIOUS PROCESSES FOUND"
|
||||
echo "$suspicious" | sed 's/^/ /'
|
||||
else
|
||||
log_info "PASS: No suspicious processes detected"
|
||||
echo " Status: NO SUSPICIOUS PROCESSES"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
return 0
|
||||
}
|
||||
|
||||
# Check audit logs
|
||||
#
|
||||
# SECURITY RATIONALE:
|
||||
# Audit log review provides:
|
||||
# 1. Detection of security events
|
||||
# 2. Identification of unauthorized access
|
||||
# 3. Forensic information
|
||||
# 4. Compliance documentation
|
||||
# 5. Security incident response
|
||||
#
|
||||
# This check looks for:
|
||||
# - Failed sudo attempts
|
||||
# - Privilege escalation attempts
|
||||
# - File access to sensitive files
|
||||
# - System configuration changes
|
||||
check_audit_logs() {
|
||||
echo -e "${CYAN}10. Audit Log Check${NC}"
|
||||
echo "=========================================="
|
||||
|
||||
# Check for failed sudo attempts
|
||||
if [ -f /var/log/secure ]; then
|
||||
local sudo_failures=$(grep "authentication failure" /var/log/secure 2>/dev/null | wc -l)
|
||||
if [ "$sudo_failures" -gt 0 ]; then
|
||||
log_warning "WARNING: Failed sudo attempts detected: $sudo_failures"
|
||||
echo " Status: $sudo_failures failed sudo attempts"
|
||||
echo " Last 5 failures:"
|
||||
grep "authentication failure" /var/log/secure 2>/dev/null | tail -5 | sed 's/^/ /'
|
||||
else
|
||||
log_info "PASS: No failed sudo attempts"
|
||||
echo " Status: No failed sudo attempts"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Check auditd logs
|
||||
if command -v ausearch &> /dev/null; then
|
||||
local avc_denials=$(ausearch -m avc -ts recent 2>/dev/null | wc -l)
|
||||
if [ "$avc_denials" -gt 0 ]; then
|
||||
log_warning "WARNING: SELinux denials detected: $avc_denials"
|
||||
echo " Status: $avc_denials SELinux denials"
|
||||
else
|
||||
log_info "PASS: No SELinux denials"
|
||||
echo " Status: No SELinux denials"
|
||||
fi
|
||||
fi
|
||||
|
||||
echo ""
|
||||
return 0
|
||||
}
|
||||
|
||||
# Check file permissions
|
||||
#
|
||||
# SECURITY RATIONALE:
|
||||
# File permissions check provides:
|
||||
# 1. Detection of overly permissive files
|
||||
# 2. Verification of sensitive file protection
|
||||
# 3. Identification of potential security issues
|
||||
# 4. Compliance with security standards
|
||||
#
|
||||
# This check looks for:
|
||||
# - World-writable files in critical directories
|
||||
# - Files with incorrect ownership
|
||||
# - SUID/SGID files
|
||||
check_file_permissions() {
|
||||
echo -e "${CYAN}11. File Permissions Check${NC}"
|
||||
echo "=========================================="
|
||||
|
||||
# Check /etc permissions
|
||||
local etc_world_writable=$(find /etc -type f -perm -0002 2>/dev/null | wc -l)
|
||||
if [ "$etc_world_writable" -gt 0 ]; then
|
||||
log_warning "WARNING: World-writable files in /etc: $etc_world_writable"
|
||||
echo " Status: $etc_world_writable world-writable files"
|
||||
else
|
||||
log_info "PASS: No world-writable files in /etc"
|
||||
echo " Status: No world-writable files"
|
||||
fi
|
||||
|
||||
# Check for SUID/SGID files
|
||||
local suid_files=$(find /usr -type f \( -perm -4000 -o -perm -2000 \) 2>/dev/null | wc -l)
|
||||
if [ "$suid_files" -gt 100 ]; then
|
||||
log_warning "WARNING: High number of SUID/SGID files: $suid_files"
|
||||
echo " Status: $suid_files SUID/SGID files"
|
||||
else
|
||||
log_info "PASS: Reasonable number of SUID/SGID files"
|
||||
echo " Status: $suid_files SUID/SGID files"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
return 0
|
||||
}
|
||||
|
||||
# Generate summary
|
||||
generate_summary() {
|
||||
echo ""
|
||||
echo "=========================================="
|
||||
echo " Security Audit Summary"
|
||||
echo "=========================================="
|
||||
echo ""
|
||||
echo "Date: $AUDIT_DATE"
|
||||
echo "Hostname: $(hostname)"
|
||||
echo "OS: $(cat /etc/os-release | grep PRETTY_NAME | cut -d'"' -f2)"
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Main execution
|
||||
main() {
|
||||
echo ""
|
||||
echo "=========================================="
|
||||
echo " Fedora Kinoite SAW Security Audit"
|
||||
echo " Version: 1.0"
|
||||
echo " Date: 2026-04-02"
|
||||
echo "=========================================="
|
||||
echo ""
|
||||
|
||||
check_root
|
||||
|
||||
# Run all checks
|
||||
check_sudo_access
|
||||
check_selinux
|
||||
check_auditd
|
||||
check_firewall
|
||||
check_vpn
|
||||
check_dns
|
||||
check_package_verification
|
||||
check_kernel_hardening
|
||||
check_suspicious_processes
|
||||
check_audit_logs
|
||||
check_file_permissions
|
||||
|
||||
# Generate summary
|
||||
generate_summary
|
||||
|
||||
echo ""
|
||||
echo "=========================================="
|
||||
echo " Audit Complete!"
|
||||
echo "=========================================="
|
||||
echo ""
|
||||
echo "Recommendations:"
|
||||
echo "- Review any WARNING or ERROR items"
|
||||
echo "- Run this script weekly for critical systems"
|
||||
echo "- Run this script monthly for standard systems"
|
||||
echo "- Keep audit logs secure"
|
||||
echo "- Test rollback capability regularly"
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Run main function
|
||||
main "$@"
|
||||
Loading…
x
Reference in New Issue
Block a user