- Complete rewrite of secharden-2fa.sh with full 2FA implementation - SSH 2FA using Google Authenticator with publickey + TOTP authentication - Cockpit web interface 2FA with custom PAM configuration - Webmin 2FA support with automatic detection and configuration - User setup automation with QR codes and backup codes generation - Gradual rollout support using nullok for phased deployment - Automatic configuration backup and restore procedures - Add 2fa-validation.sh security test for comprehensive validation - Create TSYS-2FA-GUIDE.md with complete implementation documentation - Add DEVELOPMENT-GUIDELINES.md with coding standards and best practices - Optimize package installation with single apt-get commands for performance The 2FA implementation provides enterprise-grade security while maintaining usability and proper emergency access procedures. Includes comprehensive testing, documentation, and follows established security best practices. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
310 lines
8.9 KiB
Bash
Executable File
310 lines
8.9 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
# Two-Factor Authentication Validation Test
|
|
# Validates 2FA configuration for SSH, Cockpit, and Webmin
|
|
|
|
set -euo pipefail
|
|
|
|
PROJECT_ROOT="$(dirname "$(realpath "${BASH_SOURCE[0]}")")/../.."
|
|
|
|
function test_2fa_packages() {
|
|
echo "🔍 Testing 2FA package installation..."
|
|
|
|
local packages=("libpam-google-authenticator" "qrencode")
|
|
local failed=0
|
|
|
|
for package in "${packages[@]}"; do
|
|
if dpkg -l | grep -q "^ii.*$package"; then
|
|
echo "✅ Package installed: $package"
|
|
else
|
|
echo "❌ Package missing: $package"
|
|
((failed++))
|
|
fi
|
|
done
|
|
|
|
# Check if google-authenticator command exists
|
|
if command -v google-authenticator >/dev/null 2>&1; then
|
|
echo "✅ Google Authenticator command available"
|
|
else
|
|
echo "❌ Google Authenticator command not found"
|
|
((failed++))
|
|
fi
|
|
|
|
return $failed
|
|
}
|
|
|
|
function test_ssh_2fa_config() {
|
|
echo "🔍 Testing SSH 2FA configuration..."
|
|
|
|
local ssh_config="/etc/ssh/sshd_config"
|
|
local failed=0
|
|
|
|
# Check required SSH settings
|
|
if grep -q "^ChallengeResponseAuthentication yes" "$ssh_config"; then
|
|
echo "✅ ChallengeResponseAuthentication enabled"
|
|
else
|
|
echo "❌ ChallengeResponseAuthentication not enabled"
|
|
((failed++))
|
|
fi
|
|
|
|
if grep -q "^UsePAM yes" "$ssh_config"; then
|
|
echo "✅ UsePAM enabled"
|
|
else
|
|
echo "❌ UsePAM not enabled"
|
|
((failed++))
|
|
fi
|
|
|
|
if grep -q "^AuthenticationMethods publickey,keyboard-interactive" "$ssh_config"; then
|
|
echo "✅ AuthenticationMethods configured for 2FA"
|
|
else
|
|
echo "❌ AuthenticationMethods not configured for 2FA"
|
|
((failed++))
|
|
fi
|
|
|
|
return $failed
|
|
}
|
|
|
|
function test_pam_2fa_config() {
|
|
echo "🔍 Testing PAM 2FA configuration..."
|
|
|
|
local pam_sshd="/etc/pam.d/sshd"
|
|
local failed=0
|
|
|
|
# Check if PAM includes Google Authenticator
|
|
if grep -q "pam_google_authenticator.so" "$pam_sshd"; then
|
|
echo "✅ PAM Google Authenticator module configured"
|
|
else
|
|
echo "❌ PAM Google Authenticator module not configured"
|
|
((failed++))
|
|
fi
|
|
|
|
# Check if nullok is present (allows users without 2FA setup)
|
|
if grep -q "pam_google_authenticator.so nullok" "$pam_sshd"; then
|
|
echo "✅ PAM nullok option configured (allows gradual rollout)"
|
|
else
|
|
echo "⚠️ PAM nullok option not configured (immediate enforcement)"
|
|
fi
|
|
|
|
return $failed
|
|
}
|
|
|
|
function test_cockpit_2fa_config() {
|
|
echo "🔍 Testing Cockpit 2FA configuration..."
|
|
|
|
local cockpit_config="/etc/cockpit/cockpit.conf"
|
|
local cockpit_pam="/etc/pam.d/cockpit"
|
|
local failed=0
|
|
|
|
# Check if Cockpit is installed
|
|
if ! command -v cockpit-ws >/dev/null 2>&1; then
|
|
echo "⚠️ Cockpit not installed, skipping test"
|
|
return 0
|
|
fi
|
|
|
|
# Check Cockpit configuration
|
|
if [[ -f "$cockpit_config" ]]; then
|
|
echo "✅ Cockpit configuration file exists"
|
|
else
|
|
echo "❌ Cockpit configuration file missing"
|
|
((failed++))
|
|
fi
|
|
|
|
# Check Cockpit PAM configuration
|
|
if [[ -f "$cockpit_pam" ]] && grep -q "pam_google_authenticator.so" "$cockpit_pam"; then
|
|
echo "✅ Cockpit PAM 2FA configured"
|
|
else
|
|
echo "❌ Cockpit PAM 2FA not configured"
|
|
((failed++))
|
|
fi
|
|
|
|
return $failed
|
|
}
|
|
|
|
function test_webmin_2fa_config() {
|
|
echo "🔍 Testing Webmin 2FA configuration..."
|
|
|
|
local webmin_config="/etc/webmin/miniserv.conf"
|
|
local failed=0
|
|
|
|
# Check if Webmin is installed
|
|
if [[ ! -f "$webmin_config" ]]; then
|
|
echo "⚠️ Webmin not installed, skipping test"
|
|
return 0
|
|
fi
|
|
|
|
# Check Webmin 2FA settings
|
|
if grep -q "^twofactor_provider=totp" "$webmin_config"; then
|
|
echo "✅ Webmin TOTP provider configured"
|
|
else
|
|
echo "❌ Webmin TOTP provider not configured"
|
|
((failed++))
|
|
fi
|
|
|
|
if grep -q "^twofactor=1" "$webmin_config"; then
|
|
echo "✅ Webmin 2FA enabled"
|
|
else
|
|
echo "❌ Webmin 2FA not enabled"
|
|
((failed++))
|
|
fi
|
|
|
|
return $failed
|
|
}
|
|
|
|
function test_user_2fa_setup() {
|
|
echo "🔍 Testing user 2FA setup preparation..."
|
|
|
|
local users=("localuser" "root")
|
|
local failed=0
|
|
|
|
for user in "${users[@]}"; do
|
|
if id "$user" &>/dev/null; then
|
|
# Check if setup script exists
|
|
if [[ -f "/tmp/setup-2fa-$user.sh" ]]; then
|
|
echo "✅ 2FA setup script exists for user: $user"
|
|
else
|
|
echo "❌ 2FA setup script missing for user: $user"
|
|
((failed++))
|
|
fi
|
|
|
|
# Check if instructions exist
|
|
if [[ -f "/home/$user/2fa-setup-instructions.txt" ]]; then
|
|
echo "✅ 2FA instructions exist for user: $user"
|
|
else
|
|
echo "❌ 2FA instructions missing for user: $user"
|
|
((failed++))
|
|
fi
|
|
else
|
|
echo "⚠️ User $user not found, skipping"
|
|
fi
|
|
done
|
|
|
|
return $failed
|
|
}
|
|
|
|
function test_service_status() {
|
|
echo "🔍 Testing service status..."
|
|
|
|
local failed=0
|
|
|
|
# Test SSH service
|
|
if systemctl is-active sshd >/dev/null 2>&1; then
|
|
echo "✅ SSH service is running"
|
|
else
|
|
echo "❌ SSH service is not running"
|
|
((failed++))
|
|
fi
|
|
|
|
# Test SSH configuration
|
|
if sshd -t 2>/dev/null; then
|
|
echo "✅ SSH configuration is valid"
|
|
else
|
|
echo "❌ SSH configuration is invalid"
|
|
((failed++))
|
|
fi
|
|
|
|
# Test Cockpit service if installed
|
|
if systemctl is-enabled cockpit.socket >/dev/null 2>&1; then
|
|
if systemctl is-active cockpit.socket >/dev/null 2>&1; then
|
|
echo "✅ Cockpit service is running"
|
|
else
|
|
echo "❌ Cockpit service is not running"
|
|
((failed++))
|
|
fi
|
|
fi
|
|
|
|
# Test Webmin service if installed
|
|
if systemctl is-enabled webmin >/dev/null 2>&1; then
|
|
if systemctl is-active webmin >/dev/null 2>&1; then
|
|
echo "✅ Webmin service is running"
|
|
else
|
|
echo "❌ Webmin service is not running"
|
|
((failed++))
|
|
fi
|
|
fi
|
|
|
|
return $failed
|
|
}
|
|
|
|
function test_backup_existence() {
|
|
echo "🔍 Testing backup existence..."
|
|
|
|
local backup_dir="/root/backup"
|
|
local failed=0
|
|
|
|
if [[ -d "$backup_dir" ]]; then
|
|
# Look for recent 2FA backups
|
|
local recent_backups=$(find "$backup_dir" -name "2fa-*" -type d -newer /etc/ssh/sshd_config 2>/dev/null | wc -l)
|
|
|
|
if [[ $recent_backups -gt 0 ]]; then
|
|
echo "✅ Recent 2FA backup found in $backup_dir"
|
|
else
|
|
echo "⚠️ No recent 2FA backups found"
|
|
fi
|
|
else
|
|
echo "❌ Backup directory does not exist"
|
|
((failed++))
|
|
fi
|
|
|
|
return $failed
|
|
}
|
|
|
|
function test_2fa_enforcement() {
|
|
echo "🔍 Testing 2FA enforcement level..."
|
|
|
|
local pam_sshd="/etc/pam.d/sshd"
|
|
|
|
# Check if nullok is used (allows users without 2FA)
|
|
if grep -q "pam_google_authenticator.so nullok" "$pam_sshd"; then
|
|
echo "⚠️ 2FA enforcement: GRADUAL (nullok allows users without 2FA)"
|
|
echo " Users can log in without 2FA during setup phase"
|
|
else
|
|
echo "✅ 2FA enforcement: STRICT (all users must have 2FA)"
|
|
echo " All users must have 2FA configured to log in"
|
|
fi
|
|
|
|
return 0
|
|
}
|
|
|
|
# Main test execution
|
|
function main() {
|
|
echo "🔒 Running Two-Factor Authentication Validation Tests"
|
|
echo "=================================================="
|
|
|
|
local total_failures=0
|
|
|
|
# Run all 2FA validation tests
|
|
test_2fa_packages || ((total_failures++))
|
|
test_ssh_2fa_config || ((total_failures++))
|
|
test_pam_2fa_config || ((total_failures++))
|
|
test_cockpit_2fa_config || ((total_failures++))
|
|
test_webmin_2fa_config || ((total_failures++))
|
|
test_user_2fa_setup || ((total_failures++))
|
|
test_service_status || ((total_failures++))
|
|
test_backup_existence || ((total_failures++))
|
|
test_2fa_enforcement || ((total_failures++))
|
|
|
|
echo "=================================================="
|
|
|
|
if [[ $total_failures -eq 0 ]]; then
|
|
echo "✅ All 2FA validation tests passed"
|
|
echo ""
|
|
echo "📋 Next Steps:"
|
|
echo "1. Run user setup scripts: /tmp/setup-2fa-*.sh"
|
|
echo "2. Test 2FA login from another terminal"
|
|
echo "3. Remove nullok from PAM config for strict enforcement"
|
|
exit 0
|
|
else
|
|
echo "❌ $total_failures 2FA validation tests failed"
|
|
echo ""
|
|
echo "🔧 Troubleshooting:"
|
|
echo "1. Re-run secharden-2fa.sh script"
|
|
echo "2. Check system logs: journalctl -u sshd"
|
|
echo "3. Verify package installation"
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
# Run main if executed directly
|
|
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
|
main "$@"
|
|
fi |