configure_ssh_2fa only enabled the deprecated ChallengeResponseAuthentication directive (removed as a usable knob in modern OpenSSH; it no longer controls keyboard-interactive). The base tsys-sshd-config ships KbdInteractiveAuthentication no, so on Debian 13 (OpenSSH 9.x/10.x) keyboard-interactive stayed disabled. With AuthenticationMethods set to "publickey,keyboard-interactive", sshd -t then failed: Disabled method "keyboard-interactive" in AuthenticationMethods list ... AuthenticationMethods cannot be satisfied by enabled authentication methods which aborted provisioning under errexit. Add the modern KbdInteractiveAuthentication yes directive alongside the legacy one so 2FA works on both current and older OpenSSH. 🤖 Generated with [Crush](https://github.com/charmassociates/crush) Assisted-by: GLM-5 via Crush <crush@charm.land>
418 lines
12 KiB
Bash
418 lines
12 KiB
Bash
#!/bin/bash
|
|
|
|
# TSYS Security Hardening - Two-Factor Authentication
|
|
# Implements 2FA for SSH, Cockpit, and Webmin services
|
|
# Uses Google Authenticator (TOTP) for time-based tokens
|
|
|
|
|
|
#####
|
|
#Core framework functions...
|
|
#####
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
|
|
export PROJECT_ROOT_PATH
|
|
PROJECT_ROOT_PATH="$(cd "$SCRIPT_DIR/../../.." && pwd)"
|
|
|
|
export GIT_VENDOR_PATH_ROOT
|
|
GIT_VENDOR_PATH_ROOT="$PROJECT_ROOT_PATH/vendor/git@git.knownelement.com/29418/"
|
|
|
|
export KNELShellFrameworkRoot
|
|
KNELShellFrameworkRoot="$GIT_VENDOR_PATH_ROOT/KNEL/KNELShellFramework"
|
|
|
|
source "$KNELShellFrameworkRoot/Framework-ConfigFiles/FrameworkVars"
|
|
|
|
for framework_include_file in "$KNELShellFrameworkRoot"/Framework-Includes/*; do
|
|
source "$framework_include_file"
|
|
done
|
|
|
|
for project_include_file in "$PROJECT_ROOT_PATH"/Project-Includes/*; do
|
|
source "$project_include_file"
|
|
done
|
|
|
|
# 2FA Configuration
|
|
BACKUP_DIR="/root/backup/2fa"
|
|
PAM_CONFIG_DIR="/etc/pam.d"
|
|
SSH_CONFIG="/etc/ssh/sshd_config"
|
|
COCKPIT_CONFIG="/etc/cockpit/cockpit.conf"
|
|
|
|
# Create backup directory
|
|
mkdir -p "$BACKUP_DIR"
|
|
|
|
print_info "TSYS Two-Factor Authentication Setup"
|
|
|
|
# Backup existing configurations
|
|
function backup_configs() {
|
|
print_info "Creating backup of existing configurations..."
|
|
|
|
# Backup SSH configuration
|
|
if [[ -f "$SSH_CONFIG" ]]; then
|
|
cp "$SSH_CONFIG" "$BACKUP_DIR/sshd_config.bak"
|
|
print_info "SSH config backed up"
|
|
fi
|
|
|
|
# Backup PAM configurations
|
|
if [[ -d "$PAM_CONFIG_DIR" ]]; then
|
|
cp -r "$PAM_CONFIG_DIR" "$BACKUP_DIR/pam.d.bak"
|
|
print_info "PAM configs backed up"
|
|
fi
|
|
|
|
# Backup Cockpit configuration if exists
|
|
if [[ -f "$COCKPIT_CONFIG" ]]; then
|
|
cp "$COCKPIT_CONFIG" "$BACKUP_DIR/cockpit.conf.bak"
|
|
print_info "Cockpit config backed up"
|
|
fi
|
|
|
|
print_info "Backup completed: $BACKUP_DIR"
|
|
}
|
|
|
|
# Install required packages
|
|
function install_2fa_packages() {
|
|
print_info "Installing 2FA packages..."
|
|
|
|
# Update package cache
|
|
apt-get update
|
|
|
|
# Install Google Authenticator PAM module
|
|
# Install QR code generator for terminal display
|
|
apt-get install -y libpam-google-authenticator qrencode
|
|
|
|
print_info "2FA packages installed successfully"
|
|
}
|
|
|
|
# Configure SSH for 2FA
|
|
function configure_ssh_2fa() {
|
|
print_info "Configuring SSH for 2FA..."
|
|
|
|
# Configure SSH daemon
|
|
print_info "Updating SSH configuration..."
|
|
|
|
# Enable challenge-response authentication
|
|
if ! grep -q "^ChallengeResponseAuthentication yes" "$SSH_CONFIG"; then
|
|
sed -i 's/^ChallengeResponseAuthentication.*/ChallengeResponseAuthentication yes/' "$SSH_CONFIG" || \
|
|
echo "ChallengeResponseAuthentication yes" >> "$SSH_CONFIG"
|
|
fi
|
|
|
|
if ! grep -q "^KbdInteractiveAuthentication yes" "$SSH_CONFIG"; then
|
|
sed -i 's/^KbdInteractiveAuthentication.*/KbdInteractiveAuthentication yes/' "$SSH_CONFIG" || \
|
|
echo "KbdInteractiveAuthentication yes" >> "$SSH_CONFIG"
|
|
fi
|
|
|
|
# Enable PAM authentication
|
|
if ! grep -q "^UsePAM yes" "$SSH_CONFIG"; then
|
|
sed -i 's/^UsePAM.*/UsePAM yes/' "$SSH_CONFIG" || \
|
|
echo "UsePAM yes" >> "$SSH_CONFIG"
|
|
fi
|
|
|
|
# Configure authentication methods (key + 2FA)
|
|
if ! grep -q "^AuthenticationMethods" "$SSH_CONFIG"; then
|
|
echo "AuthenticationMethods publickey,keyboard-interactive" >> "$SSH_CONFIG"
|
|
else
|
|
sed -i 's/^AuthenticationMethods.*/AuthenticationMethods publickey,keyboard-interactive/' "$SSH_CONFIG"
|
|
fi
|
|
|
|
print_info "SSH configuration updated"
|
|
}
|
|
|
|
# Configure PAM for 2FA
|
|
function configure_pam_2fa() {
|
|
print_info "Configuring PAM for 2FA..."
|
|
|
|
# Create backup of original PAM SSH config
|
|
cp "$PAM_CONFIG_DIR/sshd" "$PAM_CONFIG_DIR/sshd.bak.$(date +%Y%m%d)"
|
|
|
|
# Configure PAM to use Google Authenticator
|
|
cat > "$PAM_CONFIG_DIR/sshd" << 'EOF'
|
|
# PAM configuration for SSH with 2FA
|
|
# Standard Un*x authentication
|
|
@include common-auth
|
|
|
|
# Google Authenticator 2FA
|
|
auth required pam_google_authenticator.so nullok
|
|
|
|
# Standard Un*x authorization
|
|
@include common-account
|
|
|
|
# SELinux needs to be the first session rule
|
|
session required pam_selinux.so close
|
|
session required pam_loginuid.so
|
|
|
|
# Standard Un*x session setup and teardown
|
|
@include common-session
|
|
|
|
# Print the message of the day upon successful login
|
|
session optional pam_motd.so motd=/run/motd.dynamic
|
|
session optional pam_motd.so noupdate
|
|
|
|
# Print the status of the user's mailbox upon successful login
|
|
session optional pam_mail.so standard noenv
|
|
|
|
# Set up user limits from /etc/security/limits.conf
|
|
session required pam_limits.so
|
|
|
|
# SELinux needs to intervene at login time
|
|
session required pam_selinux.so open
|
|
|
|
# Standard Un*x password updating
|
|
@include common-password
|
|
EOF
|
|
|
|
print_info "PAM configuration updated for SSH 2FA"
|
|
}
|
|
|
|
# Configure Cockpit for 2FA
|
|
function configure_cockpit_2fa() {
|
|
print_info "Configuring Cockpit for 2FA..."
|
|
|
|
# Create Cockpit config directory if it doesn't exist
|
|
mkdir -p "$(dirname "$COCKPIT_CONFIG")"
|
|
|
|
# Configure Cockpit to use PAM with 2FA
|
|
cat > "$COCKPIT_CONFIG" << 'EOF'
|
|
[WebService]
|
|
# Enable 2FA for Cockpit web interface
|
|
LoginTitle = TSYS Server Management
|
|
LoginTo = 300
|
|
RequireHost = true
|
|
|
|
[Session]
|
|
# Use PAM for authentication (includes 2FA)
|
|
Banner = /etc/cockpit/issue.cockpit
|
|
IdleTimeout = 15
|
|
EOF
|
|
|
|
# Create PAM configuration for Cockpit
|
|
cat > "$PAM_CONFIG_DIR/cockpit" << 'EOF'
|
|
# PAM configuration for Cockpit with 2FA
|
|
auth requisite pam_nologin.so
|
|
auth required pam_env.so
|
|
auth required pam_faillock.so preauth
|
|
auth sufficient pam_unix.so try_first_pass
|
|
auth required pam_google_authenticator.so nullok
|
|
auth required pam_faillock.so authfail
|
|
auth required pam_deny.so
|
|
|
|
account required pam_nologin.so
|
|
account include system-auth
|
|
account required pam_faillock.so
|
|
|
|
session required pam_selinux.so close
|
|
session required pam_loginuid.so
|
|
session optional pam_keyinit.so force revoke
|
|
session include system-auth
|
|
session required pam_selinux.so open
|
|
session optional pam_motd.so
|
|
EOF
|
|
|
|
print_info "Cockpit 2FA configuration completed"
|
|
}
|
|
|
|
# Configure Webmin for 2FA (if installed)
|
|
function configure_webmin_2fa() {
|
|
print_info "Checking for Webmin installation..."
|
|
|
|
local webmin_config="/etc/webmin/miniserv.conf"
|
|
|
|
if [[ -f "$webmin_config" ]]; then
|
|
print_info "Webmin found, configuring 2FA..."
|
|
|
|
# Stop webmin service
|
|
systemctl stop webmin || true
|
|
|
|
# Enable 2FA in Webmin configuration
|
|
sed -i 's/^twofactor_provider=.*/twofactor_provider=totp/' "$webmin_config" || \
|
|
echo "twofactor_provider=totp" >> "$webmin_config"
|
|
|
|
# Enable 2FA requirement
|
|
sed -i 's/^twofactor=.*/twofactor=1/' "$webmin_config" || \
|
|
echo "twofactor=1" >> "$webmin_config"
|
|
|
|
# Start webmin service
|
|
systemctl start webmin || true
|
|
|
|
print_info "Webmin 2FA configuration completed"
|
|
else
|
|
print_info "Webmin not found, skipping configuration"
|
|
fi
|
|
}
|
|
|
|
# Setup 2FA for users
|
|
function setup_user_2fa() {
|
|
print_info "Setting up 2FA for system users..."
|
|
|
|
local users=("localuser" "root")
|
|
|
|
for user in "${users[@]}"; do
|
|
if id "$user" &>/dev/null; then
|
|
print_info "Setting up 2FA for user: $user"
|
|
|
|
local user_home
|
|
user_home="$(getent passwd "$user" | cut -d: -f6)"
|
|
if [[ -z "$user_home" ]]; then
|
|
print_info "No home directory for $user, skipping"
|
|
continue
|
|
fi
|
|
|
|
# Create 2FA setup script for user
|
|
cat > "/tmp/setup-2fa-$user.sh" << 'EOF'
|
|
#!/bin/bash
|
|
echo "Setting up Google Authenticator for user: $USER"
|
|
echo "Please follow the prompts to configure 2FA:"
|
|
echo "1. Answer 'y' to update your time-based token"
|
|
echo "2. Scan the QR code with your authenticator app"
|
|
echo "3. Save the backup codes in a secure location"
|
|
echo "4. Answer 'y' to the remaining questions for security"
|
|
echo ""
|
|
google-authenticator -t -d -f -r 3 -R 30 -W
|
|
EOF
|
|
|
|
chmod +x "/tmp/setup-2fa-$user.sh"
|
|
|
|
# Instructions for user setup
|
|
cat > "$user_home/2fa-setup-instructions.txt" << EOF
|
|
TSYS Two-Factor Authentication Setup Instructions
|
|
==============================================
|
|
|
|
Your system has been configured for 2FA. To complete setup:
|
|
|
|
1. Install an authenticator app on your phone:
|
|
- Google Authenticator
|
|
- Authy
|
|
- Microsoft Authenticator
|
|
|
|
2. Run the setup command:
|
|
sudo /tmp/setup-2fa-$user.sh
|
|
|
|
3. Follow the prompts:
|
|
- Scan the QR code with your app
|
|
- Save the backup codes securely
|
|
- Answer 'y' to security questions
|
|
|
|
4. Test your setup:
|
|
- SSH to the server
|
|
- Enter your 6-digit code when prompted
|
|
|
|
IMPORTANT: Save backup codes in a secure location!
|
|
Without them, you may be locked out if you lose your phone.
|
|
|
|
For support, contact your system administrator.
|
|
EOF
|
|
|
|
chown "$user:$user" "$user_home/2fa-setup-instructions.txt"
|
|
print_info "2FA setup prepared for user: $user"
|
|
else
|
|
print_info "User $user not found, skipping"
|
|
fi
|
|
done
|
|
}
|
|
|
|
# Restart services
|
|
function restart_services() {
|
|
print_info "Restarting services..."
|
|
|
|
# Test SSH configuration
|
|
if sshd -t; then
|
|
systemctl restart sshd
|
|
print_info "SSH service restarted"
|
|
else
|
|
print_error "SSH configuration test failed"
|
|
return 1
|
|
fi
|
|
|
|
# Restart Cockpit if installed
|
|
if systemctl is-enabled cockpit.socket &>/dev/null; then
|
|
systemctl restart cockpit.socket
|
|
print_info "Cockpit service restarted"
|
|
fi
|
|
|
|
# Restart Webmin if installed
|
|
if systemctl is-enabled webmin &>/dev/null; then
|
|
systemctl restart webmin
|
|
print_info "Webmin service restarted"
|
|
fi
|
|
}
|
|
|
|
# Validation and testing
|
|
function validate_2fa_setup() {
|
|
print_info "Validating 2FA setup..."
|
|
|
|
# Check if Google Authenticator is installed
|
|
if command -v google-authenticator &>/dev/null; then
|
|
print_info "Google Authenticator installed"
|
|
else
|
|
print_error "Google Authenticator not found"
|
|
return 1
|
|
fi
|
|
|
|
# Check SSH configuration
|
|
if grep -q "AuthenticationMethods publickey,keyboard-interactive" "$SSH_CONFIG"; then
|
|
print_info "SSH 2FA configuration valid"
|
|
else
|
|
print_error "SSH 2FA configuration invalid"
|
|
return 1
|
|
fi
|
|
|
|
# Check PAM configuration
|
|
if grep -q "pam_google_authenticator.so" "$PAM_CONFIG_DIR/sshd"; then
|
|
print_info "PAM 2FA configuration valid"
|
|
else
|
|
print_error "PAM 2FA configuration invalid"
|
|
return 1
|
|
fi
|
|
|
|
# Check service status
|
|
if systemctl is-active sshd &>/dev/null; then
|
|
print_info "SSH service is running"
|
|
else
|
|
print_error "SSH service is not running"
|
|
return 1
|
|
fi
|
|
|
|
print_info "2FA validation completed successfully"
|
|
}
|
|
|
|
# Display final instructions
|
|
function show_final_instructions() {
|
|
print_info "2FA Setup Completed"
|
|
|
|
print_info "Two-Factor Authentication has been configured for:"
|
|
print_info "- SSH (requires key + 2FA token)"
|
|
print_info "- Cockpit web interface"
|
|
if [[ -f "/etc/webmin/miniserv.conf" ]]; then
|
|
print_info "- Webmin administration panel"
|
|
fi
|
|
|
|
print_info "IMPORTANT: Complete user setup immediately!"
|
|
print_info "1. Check /home/*/2fa-setup-instructions.txt for user setup"
|
|
print_info "2. Run setup scripts for each user"
|
|
print_info "3. Test 2FA before logging out"
|
|
|
|
print_info "Backup location: $BACKUP_DIR"
|
|
print_info "To disable 2FA, restore configurations from backup"
|
|
|
|
print_info "2FA setup completed successfully!"
|
|
}
|
|
|
|
# Main execution
|
|
function main() {
|
|
# Check if running as root
|
|
if [[ $EUID -ne 0 ]]; then
|
|
print_error "This script must be run as root"
|
|
exit 1
|
|
fi
|
|
|
|
# Execute setup steps
|
|
backup_configs
|
|
install_2fa_packages
|
|
configure_ssh_2fa
|
|
configure_pam_2fa
|
|
configure_cockpit_2fa
|
|
configure_webmin_2fa
|
|
setup_user_2fa
|
|
restart_services
|
|
validate_2fa_setup
|
|
show_final_instructions
|
|
}
|
|
|
|
# Run main function
|
|
main "$@" |