feat(tools): add root-ssh-login tool to enable or disable root SSH password login
- add tools/root-ssh-login.sh with interactive menu, --enable/--disable/--status flags, and sshd drop-in config management - register root-ssh-login in lxs.sh dispatch and tools/index.sh interactive menu - document new tool in README.md command reference and project structure
This commit is contained in:
@@ -52,6 +52,7 @@ lxs help # Show help
|
||||
| `lxs tool harden` | Baseline hardening: UFW + fail2ban + SSH key-only + unattended-upgrades |
|
||||
| `lxs tool root-password` | Change the root password (interactive or generated) |
|
||||
| `lxs tool update` | Update the server (apt update + upgrade + autoremove + autoclean) |
|
||||
| `lxs tool root-ssh-login` | Enable or disable root login over SSH with a password |
|
||||
|
||||
## Project structure
|
||||
|
||||
@@ -74,7 +75,8 @@ lxs/
|
||||
├── server-benchmark.sh
|
||||
├── harden.sh
|
||||
├── root-password.sh
|
||||
└── update-server.sh
|
||||
├── update-server.sh
|
||||
└── root-ssh-login.sh
|
||||
```
|
||||
|
||||
After `lxs setup`, the full tree lives in `/usr/local/share/lxs/` and sub-scripts execute from disk. If `lxs.sh` is run without being installed (the one-liner mode), it falls back to downloading sub-scripts on demand.
|
||||
|
||||
@@ -289,6 +289,7 @@ cmd_tool() {
|
||||
harden) download_and_run "tools/harden.sh" "$@" ;;
|
||||
root-password) download_and_run "tools/root-password.sh" "$@" ;;
|
||||
update) download_and_run "tools/update-server.sh" "$@" ;;
|
||||
root-ssh-login) download_and_run "tools/root-ssh-login.sh" "$@" ;;
|
||||
"") echo -e "${RED}[✗] Missing tool name. Try: lxs help${NC}"; return 1 ;;
|
||||
*) echo -e "${RED}[✗] Unknown tool: $tool. Try: lxs help${NC}"; return 1 ;;
|
||||
esac
|
||||
|
||||
+4
-2
@@ -62,9 +62,10 @@ menu_tools() {
|
||||
echo -e " ${YELLOW}[3]${NC} Harden Server"
|
||||
echo -e " ${GREEN}[4]${NC} Change Root Password"
|
||||
echo -e " ${CYAN}[5]${NC} Update Server"
|
||||
echo -e " ${YELLOW}[6]${NC} Root SSH Password Login"
|
||||
echo -e " ${RED}[0]${NC} Back"
|
||||
echo ""
|
||||
echo -e -n "${BOLD}Choice [0-5]: ${NC}"
|
||||
echo -e -n "${BOLD}Choice [0-6]: ${NC}"
|
||||
read -r choice
|
||||
|
||||
case $choice in
|
||||
@@ -73,8 +74,9 @@ menu_tools() {
|
||||
3) run_sibling "tools/harden.sh" ;;
|
||||
4) run_sibling "tools/root-password.sh" ;;
|
||||
5) run_sibling "tools/update-server.sh" ;;
|
||||
6) run_sibling "tools/root-ssh-login.sh" ;;
|
||||
0) return ;;
|
||||
*) echo -e "${RED}[✗] Invalid option. Please select 0-5.${NC}"; sleep 1; continue ;;
|
||||
*) echo -e "${RED}[✗] Invalid option. Please select 0-6.${NC}"; sleep 1; continue ;;
|
||||
esac
|
||||
|
||||
if [ "$choice" != "0" ]; then
|
||||
|
||||
Executable
+189
@@ -0,0 +1,189 @@
|
||||
#!/bin/bash
|
||||
|
||||
# LXS - Root SSH password login
|
||||
# Description: Enable or disable root login over SSH with a password
|
||||
# Author: LXS
|
||||
# Date: 2025
|
||||
|
||||
# Load LXS common library (colors, separator, run_spinner, loggers, helpers)
|
||||
LXS_RAW_BASE="${LXS_RAW_BASE:-https://git.hyko.cx/hykocx/lxs/raw/branch/main}"
|
||||
_lib=$(curl -fsSL "${LXS_RAW_BASE}/lib/common.sh") || { echo "Failed to fetch lib/common.sh" >&2; exit 1; }
|
||||
eval "$_lib"
|
||||
unset _lib
|
||||
export LXS_LOG_FILE="/tmp/lxs_root_ssh_login.log"
|
||||
|
||||
require_root "$0" "$@"
|
||||
|
||||
set -u
|
||||
|
||||
# Drop-in path. Numeric prefix `00-` makes it win over distro defaults — sshd
|
||||
# applies the first match per option across the included files.
|
||||
DROPIN_FILE="/etc/ssh/sshd_config.d/00-lxs-root-login.conf"
|
||||
SSH_SERVICE="ssh"
|
||||
command -v systemctl >/dev/null 2>&1 && systemctl list-unit-files 2>/dev/null | grep -q '^sshd\.service' && SSH_SERVICE="sshd"
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════════════════
|
||||
# Arguments
|
||||
# ═══════════════════════════════════════════════════════════════════════════
|
||||
|
||||
ACTION=""
|
||||
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--enable|enable) ACTION="enable" ;;
|
||||
--disable|disable) ACTION="disable" ;;
|
||||
--status|status) ACTION="status" ;;
|
||||
-h|--help)
|
||||
cat <<EOF
|
||||
Usage: root-ssh-login.sh [action]
|
||||
|
||||
Actions:
|
||||
--enable Allow root to log in over SSH with a password
|
||||
--disable Disallow root password login (restore SSH defaults)
|
||||
--status Show the current effective settings
|
||||
-h, --help Show this help
|
||||
|
||||
With no action, an interactive menu is shown.
|
||||
|
||||
Note: the change is written to ${DROPIN_FILE}
|
||||
and applied by reloading the SSH service after a successful 'sshd -t'.
|
||||
EOF
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo -e "${RED}Unknown option: $arg${NC}" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════════════════
|
||||
# Pre-checks
|
||||
# ═══════════════════════════════════════════════════════════════════════════
|
||||
|
||||
if [ ! -d /etc/ssh ] || ! command -v sshd >/dev/null 2>&1; then
|
||||
err "OpenSSH server is not installed."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════════════════
|
||||
# Helpers
|
||||
# ═══════════════════════════════════════════════════════════════════════════
|
||||
|
||||
current_setting() {
|
||||
local key=$1
|
||||
sshd -T 2>/dev/null | awk -v k="${key,,}" 'tolower($1)==k {print $2; exit}'
|
||||
}
|
||||
|
||||
show_status() {
|
||||
local permit pwauth
|
||||
permit=$(current_setting PermitRootLogin)
|
||||
pwauth=$(current_setting PasswordAuthentication)
|
||||
echo -e "${WHITE}${BOLD}Current SSH login settings${NC}"
|
||||
show_separator
|
||||
echo -e " PermitRootLogin: ${BOLD}${permit:-unknown}${NC}"
|
||||
echo -e " PasswordAuthentication: ${BOLD}${pwauth:-unknown}${NC}"
|
||||
if [ -f "$DROPIN_FILE" ]; then
|
||||
echo -e " Drop-in: ${GRAY}${DROPIN_FILE}${NC}"
|
||||
else
|
||||
echo -e " Drop-in: ${GRAY}(none — distro defaults)${NC}"
|
||||
fi
|
||||
show_separator
|
||||
if [ "${permit:-}" = "yes" ] && [ "${pwauth:-}" = "yes" ]; then
|
||||
warn "Root password login is currently ENABLED."
|
||||
else
|
||||
ok "Root password login is currently DISABLED."
|
||||
fi
|
||||
}
|
||||
|
||||
reload_sshd() {
|
||||
if ! sshd -t 2>>"$LXS_LOG_FILE"; then
|
||||
err "sshd config test failed — see ${LXS_LOG_FILE}. Reverting."
|
||||
return 1
|
||||
fi
|
||||
if systemctl reload "$SSH_SERVICE" 2>>"$LXS_LOG_FILE"; then
|
||||
ok "${SSH_SERVICE} reloaded"
|
||||
return 0
|
||||
fi
|
||||
# Fall back to restart (some minimal images ship without reload support)
|
||||
if systemctl restart "$SSH_SERVICE" 2>>"$LXS_LOG_FILE"; then
|
||||
ok "${SSH_SERVICE} restarted"
|
||||
return 0
|
||||
fi
|
||||
err "Failed to reload/restart ${SSH_SERVICE} — see ${LXS_LOG_FILE}"
|
||||
return 1
|
||||
}
|
||||
|
||||
enable_root_login() {
|
||||
# Warn if root has no password — enabling password auth would be useless.
|
||||
local pw_status
|
||||
pw_status=$(passwd -S root 2>/dev/null | awk '{print $2}')
|
||||
case "$pw_status" in
|
||||
L|NP)
|
||||
warn "Root account has no usable password (status: ${pw_status})."
|
||||
warn "Run 'lxs tool root-password' first, otherwise login will still fail."
|
||||
;;
|
||||
esac
|
||||
|
||||
cat > "${DROPIN_FILE}.tmp" <<'EOF'
|
||||
# Managed by LXS (lxs tool root-ssh-login). Remove this file to revert.
|
||||
PermitRootLogin yes
|
||||
PasswordAuthentication yes
|
||||
EOF
|
||||
chmod 644 "${DROPIN_FILE}.tmp"
|
||||
mv "${DROPIN_FILE}.tmp" "$DROPIN_FILE"
|
||||
|
||||
if ! reload_sshd; then
|
||||
rm -f "$DROPIN_FILE"
|
||||
reload_sshd >/dev/null 2>&1 || true
|
||||
return 1
|
||||
fi
|
||||
ok "Root password login over SSH is now ENABLED"
|
||||
warn "This weakens server security. Disable it again when no longer needed:"
|
||||
echo -e " ${GRAY}lxs tool root-ssh-login --disable${NC}"
|
||||
}
|
||||
|
||||
disable_root_login() {
|
||||
if [ ! -f "$DROPIN_FILE" ]; then
|
||||
info "Drop-in not present — root password login already follows SSH defaults."
|
||||
else
|
||||
local backup="${DROPIN_FILE}.bak.$$"
|
||||
mv "$DROPIN_FILE" "$backup"
|
||||
if ! reload_sshd; then
|
||||
mv "$backup" "$DROPIN_FILE"
|
||||
reload_sshd >/dev/null 2>&1 || true
|
||||
return 1
|
||||
fi
|
||||
rm -f "$backup"
|
||||
fi
|
||||
ok "Root password login over SSH is now DISABLED"
|
||||
echo ""
|
||||
show_status
|
||||
}
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════════════════
|
||||
# Menu (when no action is given on the CLI)
|
||||
# ═══════════════════════════════════════════════════════════════════════════
|
||||
|
||||
if [ -z "$ACTION" ]; then
|
||||
show_status
|
||||
echo ""
|
||||
echo -e " ${GREEN}[1]${NC} Enable root SSH password login"
|
||||
echo -e " ${YELLOW}[2]${NC} Disable root SSH password login"
|
||||
echo -e " ${RED}[0]${NC} Cancel"
|
||||
echo ""
|
||||
echo -e -n "${BOLD}Choice [0-2]: ${NC}"
|
||||
read -r choice
|
||||
case "$choice" in
|
||||
1) ACTION="enable" ;;
|
||||
2) ACTION="disable" ;;
|
||||
0|"") info "Cancelled."; exit 0 ;;
|
||||
*) err "Invalid option."; exit 1 ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
case "$ACTION" in
|
||||
enable) enable_root_login ;;
|
||||
disable) disable_root_login ;;
|
||||
status) show_status ;;
|
||||
esac
|
||||
Reference in New Issue
Block a user