diff --git a/conf-sshd.sh b/conf-sshd.sh index 9935835..de97ccf 100755 --- a/conf-sshd.sh +++ b/conf-sshd.sh @@ -1,4 +1,6 @@ #!/bin/bash +set -e +set -o pipefail ########## 一些配置 ########## @@ -11,10 +13,10 @@ script_url="{{ SCRIPT_URL }}" ############ 脚本区 ########## -script_params=$* +script_params=("$@") has_param() { - for param in $script_params; do - for tParam in $@; do + for param in "${script_params[@]}"; do + for tParam in "$@"; do if [ "$tParam" == "$param" ]; then echo "true" return @@ -26,15 +28,16 @@ has_param() { get_param_value() { local find=false - for param in $script_params; do + for param in "${script_params[@]}"; do if [ "$find" == "true" ]; then if [[ $param == -* ]]; then return fi - echo $param + + echo "$param" return fi - for tParam in $@; do + for tParam in "$@"; do if [ "$tParam" == "$param" ]; then find=true break @@ -44,67 +47,161 @@ get_param_value() { } # 帮助信息. -if [ $(has_param "-h" "--help") == "true" ]; then +if [ "$(has_param "-h" "--help")" == "true" ]; then echo "Usage: $0 [options]" echo "Options:" - echo " -h, --help Print this help message." + echo " -h, --help Print this help message." echo "" echo "Available to any user: " - echo " -c, --cron [cron | false] Configure Crontab to automatically update ssh keys," - echo " Cron expression can be specified, If false is specified, " - echo " Crontab settings will be deleted automatically." + echo " -c, --cron \" | false\" Configure Crontab to automatically update ssh keys." + echo " Cron expression MUST be quoted (e.g., \"* 0 * * *\")." + echo " If 'false' is specified, Crontab settings will be deleted." echo "" - echo " -o, --only-update-keys Only update SSH keys, do not configure ssh server." - echo " -u, --update-self Update this script to the latest version." + echo " -o, --only-update-keys Only update SSH keys, do not configure ssh server." + echo " -u, --update-self Update this script to the latest version." + echo " --uninstall Uninstall this script (remove cron and local files)." echo "" echo "only available when the script is executed as root:" - echo " -n, --no-install-sshd Do not install SSH Server." - echo " -p, --allow-root-passwd Allow Root to log in with a password." + echo " -n, --no-install-sshd Do not install SSH Server." + echo " -p, --allow-root-passwd Allow Root to log in with a password." echo "" exit 0 fi update_sshkeys() { - if [ "$sshkey_url" == "" ]; then - echo "Please specify the URL of the SSH public key." + if [ "$sshkey_url" == "" ] || [[ "$sshkey_url" == "{{"* ]]; then + echo "ERROR: sshkey_url is not configured." exit 1 fi + echo "Downloading SSH public key from '$sshkey_url'" mkdir -p ~/.ssh - local ssh_keys=$(curl -s $sshkey_url) - if [ $? -ne 0 ] || [ "$ssh_keys" == "" ]; then - echo "Failed to download SSH public key at $(date '+%Y-%m-%d %H:%M:%S')" + chmod 700 ~/.ssh + + local dl_tmp_file=~/.ssh/authorized_keys.dl.tmp + if ! curl -sL "$sshkey_url" -o "$dl_tmp_file"; then + echo "Failed to download SSH public key at $(date '+%Y-m-d %H:%M:%S')" + rm -f "$dl_tmp_file" exit 1 fi + + if [ ! -s "$dl_tmp_file" ] || ! grep -qE "(ssh-rsa|ssh-ed25519|ecdsa-sha2-nistp)" "$dl_tmp_file"; then + echo "Downloaded file is empty or does not contain valid SSH key types at $(date '+%Y-m-d %H:%M:%S')" + echo "Aborting update to prevent lockout." + rm -f "$dl_tmp_file" + exit 1 + fi + echo "-------------------- SSH Keys --------------------" - echo "$ssh_keys" + cat "$dl_tmp_file" echo "--------------------------------------------------" - echo "$ssh_keys" > ~/.ssh/authorized_keys - chmod 600 ~/.ssh/authorized_keys - # 输出更新成功,需要附带时间日期 - echo "SSH public key updated successfully at $(date '+%Y-%m-%d %H:%M:%S')" + + local auth_file=~/.ssh/authorized_keys + local new_auth_file=~/.ssh/authorized_keys.new.tmp + + # 受管理文本块标记 + local begin_marker="# --- BEGIN MANAGED BY CONF-SSHD SCRIPT ---" + local end_marker="# --- END MANAGED BY CONF-SSHD SCRIPT ---" + + local managed_block_found=false + local inside_managed_block=false + + touch "$auth_file" + true > "$new_auth_file" + + # 逐行读取文件 + while IFS= read -r line; do + if [ "$line" == "$begin_marker" ]; then + managed_block_found=true + inside_managed_block=true + + { + echo "" # 确保和前面的内容有空行 + echo "$begin_marker" + cat "$dl_tmp_file" + echo "$end_marker" + } >> "$new_auth_file" + + elif [ "$line" == "$end_marker" ]; then + inside_managed_block=false + elif [ "$inside_managed_block" == "false" ]; then + echo "$line" >> "$new_auth_file" + fi + done < "$auth_file" + + if [ "$managed_block_found" == "false" ]; then + { + echo "" # 确保和前面的内容有空行 + echo "$begin_marker" + cat "$dl_tmp_file" + echo "$end_marker" + } >> "$new_auth_file" + fi + + mv "$new_auth_file" "$auth_file" + rm -f "$dl_tmp_file" + + chmod 600 "$auth_file" + echo "SSH public key updated successfully (managed block only) at $(date '+%Y-m-d %H:%M:%S')" } +# 检查是否指定了 --uninstall +if [ "$(has_param "--uninstall")" == "true" ]; then + echo "Uninstalling conf-sshd (disabling auto-updates)..." + + if [ "$(command -v crontab)" != "" ]; then + echo "Removing Crontab entry..." + (crontab -l 2>/dev/null || true | grep -v "conf-sshd.sh") | crontab - + else + echo "Crontab utility not found, skipping Crontab removal." + fi + + script_dir="$HOME/.conf-sshd" + if [ -d "$script_dir" ]; then + echo "Removing script files from $script_dir..." + rm -rf "$script_dir" + else + echo "Script directory $script_dir not found, skipping removal." + fi + + echo "Uninstall complete." + echo "Note: authorized_keys managed block and sshd_config were NOT affected." + exit 0 +fi + # 检查是否只更新密钥. -if [ $(has_param "-o" "--only-update-keys") == "true" ]; then +if [ "$(has_param "-o" "--only-update-keys")" == "true" ]; then update_sshkeys exit 0 fi # 检查是否指定了 --update-self -if [ $(has_param "-u" "--update-self") == "true" ]; then +if [ "$(has_param "-u" "--update-self")" == "true" ]; then echo "Updating conf-sshd script..." - cp $0 ~/.conf-sshd/conf-sshd.sh.bak - curl -s $script_url > $0 || cp ~/.conf-sshd/conf-sshd.sh.bak $0 && echo "Script update failed at $(date '+%Y-%m-%d %H:%M:%S')" && exit 1 - chmod +x ~/.conf-sshd/conf-sshd.sh + mkdir -p ~/.conf-sshd # 确保目录存在 + target_script=~/.conf-sshd/conf-sshd.sh + + if [ -f "$target_script" ]; then + cp "$target_script" "$target_script.bak" + fi + + # 下载到临时文件 + if ! curl -sL "$script_url" -o "$target_script.tmp"; then + echo "Script download failed at $(date '+%Y-%m-%d %H:%M:%S')" + rm -f "$target_script.tmp" + exit 1 + fi + + mv "$target_script.tmp" "$target_script" + + chmod +x "$target_script" echo "Script updated successfully at $(date '+%Y-%m-%d %H:%M:%S')" exit 0 fi -# 检查 /usr/sbin/sshd 是否存在,且 /usr/sbin/sshd 执行后退出代码为 0 -/usr/sbin/sshd -T > /dev/null -if [ $? -ne 0 ] && [ $(has_param "-n" "--no-install-sshd") == "false" ]; then - if [ $(id -u) -eq 0 ]; then +# 检查 SSHD 是否安装. +if ! /usr/sbin/sshd -T > /dev/null 2>&1 && [ "$(has_param "-n" "--no-install-sshd")" == "false" ]; then + if [ "$(id -u)" -eq 0 ]; then echo "The ssh server is not installed, and the script is executed as root, so it will be installed." if [ -f /etc/redhat-release ]; then yum install -y openssh-server @@ -122,23 +219,32 @@ else fi # 检查是否指定了 --allow-root-passwd -if [ $(has_param "-p" "--allow-root-passwd") == "true" ]; then +if [ "$(has_param "-p" "--allow-root-passwd")" == "true" ]; then # 检查当前用户是否为 root - if [ $(id -u) -eq 0 ]; then - # 获取参数值 + if [ "$(id -u)" -eq 0 ]; then allow_root_passwd=$(get_param_value "-p" "--allow-root-passwd" | tr '[:upper:]' '[:lower:]') + + sshd_config_file="/etc/ssh/sshd_config" + new_sshd_permit_root_login_setting="" + if [ "$allow_root_passwd" == "yes" ]; then - # 设置允许 root 使用密码登录 - sed -i 's/^#?PermitRootLogin.*/PermitRootLogin yes/g' /etc/ssh/sshd_config - echo "Root user is allowed to log in with password." + new_sshd_permit_root_login_setting="PermitRootLogin yes" + echo "Setting: Root user is allowed to log in with password." elif [ "$allow_root_passwd" == "no" ]; then - # 设置禁止 root 使用密码登录 - sed -i 's/^#?PermitRootLogin.*/PermitRootLogin prohibit-password/g' /etc/ssh/sshd_config - echo "Root user is prohibited from logging in with password." + new_sshd_permit_root_login_setting="PermitRootLogin prohibit-password" + echo "Setting: Root user is prohibited from logging in with password." else - echo "Please specify whether to allow root to log in with a password." + echo "Please specify 'yes' or 'no' for --allow-root-passwd." exit 1 fi + + if grep -qE '^#?PermitRootLogin' "$sshd_config_file"; then + sed -i "s@^#?PermitRootLogin.*@$new_sshd_permit_root_login_setting@g" "$sshd_config_file" + else + echo "$new_sshd_permit_root_login_setting" >> "$sshd_config_file" + fi + echo "SSHD config updated. Please restart sshd service to apply changes." + else echo "The script is executed as a non-root user and cannot set whether to allow root to log in with a password." exit 1 @@ -149,10 +255,10 @@ fi update_sshkeys # 检查是否指定了 --cron -if [ $(has_param "-c" "--cron") == "true" ]; then +if [ "$(has_param "-c" "--cron")" == "true" ]; then # 检查 Crontab 是否已安装 if [ "$(command -v crontab)" == "" ]; then - if [ $(id -u) -eq 0 ]; then + if [ "$(id -u)" -eq 0 ]; then echo "The crontab is not installed, and the script is executed as a root user, so it will be installed." if [ -f /etc/redhat-release ]; then yum install -y crontabs @@ -171,11 +277,11 @@ if [ $(has_param "-c" "--cron") == "true" ]; then cron=$(get_param_value "-c" "--cron" | tr '[:upper:]' '[:lower:]') if [ "$cron" == "false" ]; then # 检查 Crontab 是否已经设置 - if [ "$(crontab -l | grep "conf-sshd.sh")" == "" ]; then - echo "Crontab will not be configured." + if [ "$( (crontab -l 2>/dev/null || true) | grep -c "conf-sshd.sh" )" -eq 0 ]; then + echo "Crontab already clean. Will not be configured." exit 0 else - crontab -l | grep -v "conf-sshd.sh" | crontab - + (crontab -l 2>/dev/null || true) | grep -v "conf-sshd.sh" | crontab - echo "Crontab has been removed." exit 0 fi @@ -186,20 +292,22 @@ if [ $(has_param "-c" "--cron") == "true" ]; then # 将当前脚本移动到 ~/.conf-sshd/conf-sshd.sh 中. mkdir -p ~/.conf-sshd # 检查当前脚本是否为文件 - if [ ! -f $0 ]; then + if [ ! -f "$0" ]; then echo "Downloading conf-sshd script..." - curl -o ~/.conf-sshd/conf-sshd.sh $script_url + curl -oL ~/.conf-sshd/conf-sshd.sh "$script_url" else echo "Copying conf-sshd script..." - cp $0 ~/.conf-sshd/conf-sshd.sh + cp "$0" ~/.conf-sshd/conf-sshd.sh fi chmod +x ~/.conf-sshd/conf-sshd.sh echo "Install conf-sshd script successfully." # 将当前脚本追加到当前用户的 Crontab 中 - crontab -l > ~/.conf-sshd/crontab.old - echo "$cron \"/bin/bash ~/.conf-sshd/conf-sshd.sh -o\" >> ~/.conf-sshd/run.log" >> ~/.conf-sshd/crontab.old - crontab ~/.conf-sshd/crontab.old - rm ~/.conf-sshd/crontab.old + echo "Configuring Crontab..." + cron_command="\"/bin/bash ~/.conf-sshd/conf-sshd.sh -o\" >> ~/.conf-sshd/run.log 2>&1" + cron_job="$cron $cron_command" + + (crontab -l 2>/dev/null || true | grep -v "conf-sshd.sh") | { cat; echo "$cron_job"; } | crontab - + echo "Crontab has been configured.(Cron: '$cron')" fi fi