这里备份三份 Alpine Linux 下常用的管理脚本,方便以后迁移服务器、重装系统或临时排查时直接查阅。
三份原始脚本也保存在本文同级 assets/ 目录中:
这些脚本会修改系统防火墙、证书目录、SSH 配置和 root 用户密钥。实际执行前建议先完整阅读脚本内容,并确认当前 SSH 会话、开放端口、域名解析和系统服务状态。
Alpine IPv4 防火墙脚本
这个脚本用于安装 iptables 与 OpenRC 服务组件,清空旧规则后设置默认入站丢弃策略,并放行 SSH、HTTP、HTTPS、8000、10521 等 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 0Alpine 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 会话,避免规则写错后把自己锁在外面。