Alpine Linux 常用管理脚本备份:防火墙、SSL 证书与 SSH 密钥

备份三份 Alpine Linux 下使用的管理脚本,分别用于 IPv4 防火墙加固、acme.sh 申请 SSL 证书,以及 root 用户 SSH 密钥管理。

这里备份三份 Alpine Linux 下常用的管理脚本,方便以后迁移服务器、重装系统或临时排查时直接查阅。

三份原始脚本也保存在本文同级 assets/ 目录中:

这些脚本会修改系统防火墙、证书目录、SSH 配置和 root 用户密钥。实际执行前建议先完整阅读脚本内容,并确认当前 SSH 会话、开放端口、域名解析和系统服务状态。

Alpine IPv4 防火墙脚本

这个脚本用于安装 iptables 与 OpenRC 服务组件,清空旧规则后设置默认入站丢弃策略,并放行 SSH、HTTP、HTTPS、800010521 等 TCP 端口,以及 UDP 443

#!/bin/sh
# ============================================================
# Alpine Linux 严谨版防火墙脚本 (IPv4 专用)
# 针对 sing-box 性能优化 & 安全加固
# ============================================================

set -e

# 颜色输出
info='\033[32m[INFO]\033[0m'
warn='\033[33m[WARN]\033[0m'

echo -e "$info 安装 iptables 及其服务组件..."
apk add --no-cache iptables iptables-openrc

echo -e "$info 清空旧规则并预设安全策略..."
# 关键:先设为 ACCEPT,防止清理规则瞬间 SSH 掉线
iptables -P INPUT ACCEPT
iptables -F
iptables -X

# 默认策略:严防死守
iptables -P INPUT DROP
iptables -P FORWARD DROP
iptables -P OUTPUT ACCEPT

echo -e "$info 注入严谨优先级规则..."

# 1. 第一优先级:已建立的连接(确保 sing-box 现有流量直接通过,性能最高)
iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT

# 2. 第二优先级:本地回环 (127.0.0.1)
iptables -A INPUT -i lo -j ACCEPT

# 3. 开放 TCP 业务端口
for port in 22 80 443 8000 10521; do
  iptables -A INPUT -p tcp --dport $port -j ACCEPT
done

# 4. 开放 sing-box UDP 监听端口
iptables -A INPUT -p udp --dport 443 -j ACCEPT

# 5. ICMP (Ping) 控制:允许有限度的探测,拒绝洪水攻击
iptables -A INPUT -p icmp --icmp-type echo-request -m limit --limit 1/s --limit-burst 5 -j ACCEPT
iptables -A INPUT -p icmp -j DROP

echo -e "$info 正在持久化并启动服务..."
# Alpine 必须先保存到文件,否则 start 可能会加载旧配置
/etc/init.d/iptables save

# 设置开机自启
rc-update add iptables default

# 重启服务以确保内存规则与文件同步
rc-service iptables restart

echo -e "\n\033[32m✅ IPv4 防火墙加固完成!\033[0m"
echo "-------------------------------------------"
iptables -L INPUT -n -v
echo "-------------------------------------------"

Alpine SSL 证书申请脚本

这个脚本使用 acme.sh 和 Let’s Encrypt,通过 standalone 模式申请证书,并把证书安装到 /etc/ssl/<domain>/

#!/bin/bash
#
# 一键申请 SSL 证书脚本 (默认使用 Let's Encrypt) - 针对 Alpine Linux
#

# --- 脚本设置与错误处理 ---
set -eEuo pipefail
# 在发生错误时,将错误信息输出到 stderr 并退出
# `$BASH_SOURCE` 和 `$LINENO` 用于指示错误发生的脚本文件和行号
trap 'echo -e "\033[31m❌ 脚本在 [\033[1m$BASH_SOURCE:$LINENO\033[0m\033[31m] 行发生错误\033[0m" >&2; exit 1' ERR

# --- ANSI 颜色代码 ---
RED='\033[31m'
GREEN='\033[32m'
YELLOW='\033[33m'
BOLD='\033[1m'
RESET='\033[0m'

# --- 全局变量 ---
DOMAIN=""
EMAIL=""
CA_SERVER="letsencrypt"
OS_TYPE=""
PKG_MANAGER=""
ACME_INSTALL_PATH="$HOME/.acme.sh"
CERT_KEY_DIR="" # 动态设置为 /etc/ssl/您的域名/
ACME_CMD="" # 动态查找的 acme.sh 命令路径

# --- 函数定义 ---

# 检查并确保以 root 权限运行
check_root() {
    if [ "$EUID" -ne 0 ]; then
        echo -e "${RED}❌ 错误:请使用 root 权限运行此脚本。${RESET}" >&2
        exit 1
    fi
    echo -e "${GREEN}✅ Root 权限检查通过。${RESET}"
}

# 获取用户输入并校验格式
get_user_input() {
    read -r -p "请输入域名: " DOMAIN
    if ! [[ "$DOMAIN" =~ ^[a-zA-Z0-9.-]+$ ]]; then
        echo -e "${RED}❌ 错误:域名格式不正确!${RESET}" >&2; exit 1;
    fi

    read -r -p "请输入电子邮件地址: " EMAIL
    if ! [[ "$EMAIL" =~ ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$ ]]; then
        echo -e "${RED}❌ 错误:电子邮件格式不正确!${RESET}" >&2; exit 1;
    fi

    echo -e "${GREEN}✅ 用户信息收集完成 (默认使用 Let's Encrypt)。${RESET}"
}

# 检测操作系统并设置相关变量 (适配 Alpine)
detect_os() {
    if grep -qi "alpine" /etc/os-release; then
        OS_TYPE="alpine"; PKG_MANAGER="apk"
    elif grep -qi "ubuntu" /etc/os-release; then
        OS_TYPE="ubuntu"; PKG_MANAGER="apt"
    elif grep -qi "debian" /etc/os-release; then
        OS_TYPE="debian"; PKG_MANAGER="apt"
    # ... (其他系统检测逻辑保留)
    else
        echo -e "${RED}❌ 错误:不支持的操作系统!${RESET}" >&2; exit 1
    fi
    echo -e "${GREEN}✅ 检测到操作系统: $OS_TYPE ($PKG_MANAGER)。${RESET}"
}

# 安装依赖 (适配 Alpine: 使用 apk,将 cron 替换为 cronie,并安装 curl/socat)
install_dependencies() {
    local dependencies=()

    if [[ "$OS_TYPE" == "alpine" ]]; then
        dependencies=("curl" "socat" "cronie") 
        apk update >/dev/null 2>&1
        for pkg in "${dependencies[@]}"; do
            if ! apk info -e "$pkg" &>/dev/null; then
                echo -e "${YELLOW}安装依赖: $pkg...${RESET}"
                apk add "$pkg" >/dev/null 2>&1 || { echo -e "${RED}❌ 错误:安装 $pkg 失败${RESET}" >&2; exit 1; }
            fi
        done
        # 确保 cronie 服务启动并自启
        rc-service crond start >/dev/null 2>&1 || echo -e "${YELLOW}警告:无法启动 crond 服务。${RESET}" >&2
        rc-update add crond default 2>/dev/null || true

    # ... (其他系统安装逻辑保留)
    
    else
        # 为了简洁,非 Alpine 系统直接假设安装失败
        echo -e "${RED}❌ 错误:依赖安装失败,请手动检查。${RESET}" >&2
        exit 1
    fi

    echo -e "${GREEN}✅ 依赖安装完成。${RESET}"
}

# 下载安装 acme.sh
download_acme() {
    if [ ! -d "$ACME_INSTALL_PATH" ]; then
        curl -fsSL https://get.acme.sh | sh -s -- home "$ACME_INSTALL_PATH" >/dev/null 2>&1 || { echo -e "${RED}❌ 错误:下载 acme.sh 失败,请检查网络连接${RESET}" >&2; exit 1; }
        echo -e "${GREEN}✅ acme.sh 下载完成。${RESET}"
    else
        true
    fi
}

# 查找 acme.sh 命令路径
find_acme_cmd() {
    export PATH="$ACME_INSTALL_PATH:$PATH"
    ACME_CMD=$(command -v acme.sh)
    if [ -z "$ACME_CMD" ]; then
        echo -e "${RED}❌ 错误:找不到 acme.sh 命令。请检查安装或 PATH。${RESET}" >&2
        exit 1
    fi
    echo -e "${GREEN}✅ 找到 acme.sh 可执行文件。${RESET}"
}

# 更新 acme.sh
update_acme() {
    "$ACME_CMD" --upgrade >/dev/null 2>&1 || echo -e "${YELLOW}警告:acme.sh 更新失败${RESET}" >&2
    "$ACME_CMD" --update-account --days 60 >/dev/null 2>&1 || echo -e "${YELLOW}警告:acme.sh 账户信息更新失败${RESET}" >/dev/null
    echo -e "${GREEN}✅ acme.sh 更新完成。${RESET}"
}

# 申请 SSL 证书 (使用 rc-service 适配 Alpine)
issue_cert() {
    # 使用 rc-service 来停止/启动 Nginx,以释放 80 端口。
    if ! "$ACME_CMD" --issue --standalone -d "$DOMAIN" --server "$CA_SERVER" --force \
        --pre-hook "rc-service nginx stop 2>/dev/null || true" \
        --post-hook "rc-service nginx start 2>/dev/null || true" >/dev/null 2>&1; then
        echo -e "${RED}❌ 错误:证书申请失败。${RESET}" >&2
        echo "  正在进行清理..." >&2
        "$ACME_CMD" --revoke -d "$DOMAIN" --server "$CA_SERVER" >/dev/null 2>&1 || true
        "$ACME_CMD" --remove -d "$DOMAIN" --server "$CA_SERVER" >/dev/null 2>&1 || true
        exit 1
    fi
    echo -e "${GREEN}✅ 证书申请成功!${RESET}"
}

# 安装证书 (移除所有 sudo)
install_cert() {
    # 设置统一的证书安装目录
    CERT_KEY_DIR="/etc/ssl/$DOMAIN"
    
    # 移除 sudo
    mkdir -p "$CERT_KEY_DIR" >/dev/null 2>&1 || { echo -e "${RED}❌ 错误:创建证书目录失败${RESET}" >&2; exit 1; }

    # 移除 sudo
    if "$ACME_CMD" --installcert -d "$DOMAIN" \
        --key-file       "${CERT_KEY_DIR}/${DOMAIN}.key" \
        --fullchain-file "${CERT_KEY_DIR}/${DOMAIN}.crt" \
        --reloadcmd "rc-service nginx reload 2>/dev/null || true" >/dev/null 2>&1; then

        # 移除 sudo
        chmod 600 "${CERT_KEY_DIR}/${DOMAIN}.key" >/dev/null 2>&1 || echo -e "${YELLOW}警告:设置私钥文件权限失败。${RESET}" >&2
        # 移除 sudo
        chown root:root "${CERT_KEY_DIR}/${DOMAIN}.key" >/dev/null 2>&1 || echo -e "${YELLOW}警告:设置私钥文件所有者失败。${RESET}" >&2
        echo -e "${GREEN}✅ 证书安装完成。${RESET}"
    else
        echo -e "${RED}❌ 错误:证书安装失败!${RESET}" >&2
        exit 1
    fi
}

# --- 主体逻辑 ---

check_root
get_user_input
detect_os

echo "➡️ 依赖安装中..." >&2
install_dependencies

download_acme
find_acme_cmd # 在调用 acme.sh 之前查找命令

update_acme # 更新 acme.sh

echo "➡️ 证书申请中..." >&2
issue_cert # 申请证书
install_cert # 安装证书并设置权限

echo "➡️ 配置自动续期..." >&2
# 移除 sudo
"$ACME_CMD" --install-cronjob >/dev/null 2>&1 || echo -e "${YELLOW}警告:配置 acme.sh 自动续期任务失败。请手动运行 '\$HOME/.acme.sh/acme.sh --install-cronjob' 进行配置。${RESET}" >&2

echo -e "${GREEN}✅ 自动续期已通过 acme.sh 内置功能配置。${RESET}" >&2


echo "==============================================="
echo -e "${GREEN}✅ 脚本执行完毕。${RESET}"
echo "==============================================="
echo -e "${GREEN}证书文件: ${BOLD}${CERT_KEY_DIR}/${DOMAIN}.crt${RESET}"
echo -e "${GREEN}私钥文件: ${BOLD}${CERT_KEY_DIR}/${DOMAIN}.key${RESET}"
echo -e "${GREEN}自动续期已通过 acme.sh 内置功能配置完成。"
echo -e "${YELLOW}提示: 您可以通过 'crontab -l'来检查任务是否成功设置。${RESET}" >&2
echo -e "${YELLOW}重要提示: 请确保服务器的 80 端口(用于 Let's Encrypt 验证)以及您自己的服务端口是开放的。${RESET}" >&2

echo "==============================================="

exit 0

Alpine root SSH 密钥管理脚本

这个脚本提供一个交互面板,用于生成 ED25519 密钥、导入已有公钥、从 GitHub 拉取公钥,并强制刷新 Alpine 的 sshd_config

#!/bin/ash

# 强制定义路径
HOME="/root"

# 颜色定义
gl_lv='\033[32m'
gl_huang='\033[33m'
gl_hong='\033[31m'
gl_bai='\033[0m'
gl_hui='\e[37m'

# 检查权限与依赖
init_check() {
    if [ "$(id -u)" -ne 0 ]; then
        echo -e "${gl_huang}提示: ${gl_bai}该功能需要 root 用户才能运行!"
        exit 1
    fi
    # 自动安装 Alpine 缺失的常用工具
    if ! command -v curl >/dev/null 2>&1 || ! command -v nano >/dev/null 2>&1; then
        apk add --no-cache curl nano openssh-keygen openssh-client >/dev/null 2>&1
    fi
}

# 获取 IP 地址
ip_address() {
    ipv4_address=$(curl -s --connect-timeout 5 https://ipinfo.io/ip)
    [ -z "$ipv4_address" ] && ipv4_address="VPS_IP"
}

# 重启 SSH 服务
restart_ssh() {
    rc-service sshd restart >/dev/null 2>&1
}

# 开启密钥模式 (核心逻辑:查缺补漏,防止重复)
sshkey_on() {
    local conf="/etc/ssh/sshd_config"
    [ ! -f "${conf}.bak" ] && cp "$conf" "${conf}.bak"
    
    # 定义核心配置项
    configs="
PermitRootLogin prohibit-password
PasswordAuthentication no
PubkeyAuthentication yes
ChallengeResponseAuthentication no
KbdInteractiveAuthentication no
UsePAM no
"

    # 1. 尝试修改已有项(包含被注释的行)
    echo "$configs" | while read -r line; do
        [ -z "$line" ] && continue
        key=$(echo "$line" | awk '{print $1}')
        val=$(echo "$line" | awk '{print $2}')
        # 匹配任何以该 key 开头的行(无论前面是否有 # 或空格)并替换
        sed -i "s|^\s*#\?\s*$key\s.*|$key $val|g" "$conf"
    done

    # 2. 查缺补漏:如果文件中完全没出现过该 key,则追加
    echo "$configs" | while read -r line; do
        [ -z "$line" ] && continue
        key=$(echo "$line" | awk '{print $1}')
        if ! grep -qi "^\s*$key\s" "$conf"; then
            echo "$line" >> "$conf"
        fi
    done

    # 3. 处理 Alpine 特有配置:注释掉 Include 指令,清理子配置
    sed -i 's/^Include /#Include /g' "$conf"
    [ -d /etc/ssh/sshd_config.d ] && rm -f /etc/ssh/sshd_config.d/*
    
    restart_ssh
    echo -e "${gl_lv}SSH 安全配置已查缺补漏并强制刷新,密钥模式已生效${gl_bai}"
    sleep 2
}

# 确保目录权限正确
ensure_ssh_dir() {
    [ ! -d "$HOME/.ssh" ] && mkdir -p "$HOME/.ssh"
    chmod 700 "$HOME/.ssh"
    [ ! -f "$HOME/.ssh/authorized_keys" ] && touch "$HOME/.ssh/authorized_keys"
    chmod 600 "$HOME/.ssh/authorized_keys"
}

# 1. 生成新密钥对
add_sshkey() {
    ensure_ssh_dir
    local key_path="$HOME/.ssh/sshkey"
    
    ssh-keygen -t ed25519 -C "admin@vps" -f "$key_path" -N ""
    cat "${key_path}.pub" >> "$HOME/.ssh/authorized_keys"
    
    ip_address
    echo -e "\n${gl_lv}密钥对生成成功!${gl_bai}"
    echo -e "请保存私钥,建议文件名: ${gl_huang}${ipv4_address}_ssh.key${gl_bai}"
    echo "------------------------------------------------"
    cat "$key_path"
    echo "------------------------------------------------"
    sshkey_on
}

# 2. 手动导入公钥
import_sshkey() {
    ensure_ssh_dir
    printf "${gl_hui}请粘贴公钥内容: ${gl_bai}"
    read pub_content
    if [ -z "$pub_content" ]; then
        echo -e "${gl_hong}错误:输入内容为空${gl_bai}"; return 1
    fi
    echo "$pub_content" >> "$HOME/.ssh/authorized_keys"
    sshkey_on
}

# 3. 从 GitHub 导入
import_github() {
    ensure_ssh_dir
    printf "${gl_hui}请输入 GitHub 用户名: ${gl_bai}"
    read username
    if [ -n "$username" ]; then
        curl -fsSL "https://github.com/${username}.keys" >> "$HOME/.ssh/authorized_keys"
        echo -e "${gl_lv}GitHub 公钥导入尝试完成${gl_bai}"
        sshkey_on
    fi
}

# 主菜单
sshkey_panel() {
    init_check
    while true; do
        clear
        REAL_STATUS=$(grep -i "^PubkeyAuthentication" /etc/ssh/sshd_config | awk '{print $2}' | tr '[:upper:]' '[:lower:]')
        IS_KEY_ENABLED="${gl_hui}未启用${gl_bai}"
        [ "$REAL_STATUS" = "yes" ] && IS_KEY_ENABLED="${gl_lv}已启用${gl_bai}"
        
        echo -e "Alpine SSH 密钥管理面板 ${IS_KEY_ENABLED}"
        echo "------------------------------------------------"
        echo "1. 生成新密钥对 (ED25519)"
        echo "2. 手动输入已有公钥"
        echo "3. 从 GitHub 导入公钥"
        echo "4. 编辑公钥文件 (authorized_keys)"
        echo "5. 查看当前密钥信息"
        echo "0. 退出"
        echo "------------------------------------------------"
        printf "请输入选择: "
        read choice
        case $choice in
            1) add_sshkey ;;
            2) import_sshkey ;;
            3) import_github ;;
            4) nano "$HOME/.ssh/authorized_keys" ;;
            5)
                echo -e "\n${gl_huang}--- 已授权公钥 ---${gl_bai}"
                [ -s "$HOME/.ssh/authorized_keys" ] && cat "$HOME/.ssh/authorized_keys" || echo "为空"
                echo -e "\n${gl_huang}--- 本地私钥 (如有) ---${gl_bai}"
                [ -f "$HOME/.ssh/sshkey" ] && cat "$HOME/.ssh/sshkey" || echo "未找到"
                printf "\n按回车继续..."; read dummy ;;
            0) exit 0 ;;
            *) echo "无效选择"; sleep 1 ;;
        esac
    done
}

sshkey_panel

使用方式

下载对应脚本后,先阅读并按自己的端口、域名、SSH 策略做必要调整。确认无误后再执行:

chmod +x script.sh
./script.sh

如果是远程服务器,尤其是防火墙和 SSH 配置脚本,建议先保留一个已登录的备用 SSH 会话,避免规则写错后把自己锁在外面。