@aaricchen1991/n2-cli 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +92 -0
- package/assets/deploy/deploy.sh +172 -0
- package/assets/deploy/domains.yaml +18 -0
- package/assets/deploy/lib/common.sh +62 -0
- package/assets/deploy/nginx/n2.conf +162 -0
- package/assets/deploy/server-setup.sh +285 -0
- package/assets/deploy/ssl/README.md +320 -0
- package/assets/deploy/ssl/check-and-setup-ssl.sh +222 -0
- package/assets/deploy/ssl/domains.txt +3 -0
- package/assets/deploy/ssl/renew-ssl.sh +236 -0
- package/assets/deploy/ssl/setup-ssl.sh +474 -0
- package/dist/cli.d.ts +7 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +186 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/config.d.ts +29 -0
- package/dist/commands/config.d.ts.map +1 -0
- package/dist/commands/config.js +134 -0
- package/dist/commands/config.js.map +1 -0
- package/dist/commands/config.test.d.ts +2 -0
- package/dist/commands/config.test.d.ts.map +1 -0
- package/dist/commands/config.test.js +215 -0
- package/dist/commands/config.test.js.map +1 -0
- package/dist/commands/init.d.ts +10 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +106 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/init.test.d.ts +2 -0
- package/dist/commands/init.test.d.ts.map +1 -0
- package/dist/commands/init.test.js +70 -0
- package/dist/commands/init.test.js.map +1 -0
- package/dist/commands/nginx.d.ts +10 -0
- package/dist/commands/nginx.d.ts.map +1 -0
- package/dist/commands/nginx.js +72 -0
- package/dist/commands/nginx.js.map +1 -0
- package/dist/commands/nginx.test.d.ts +2 -0
- package/dist/commands/nginx.test.d.ts.map +1 -0
- package/dist/commands/nginx.test.js +75 -0
- package/dist/commands/nginx.test.js.map +1 -0
- package/dist/commands/ssl-logs.d.ts +17 -0
- package/dist/commands/ssl-logs.d.ts.map +1 -0
- package/dist/commands/ssl-logs.js +55 -0
- package/dist/commands/ssl-logs.js.map +1 -0
- package/dist/commands/ssl-logs.test.d.ts +2 -0
- package/dist/commands/ssl-logs.test.d.ts.map +1 -0
- package/dist/commands/ssl-logs.test.js +54 -0
- package/dist/commands/ssl-logs.test.js.map +1 -0
- package/dist/commands/ssl.d.ts +16 -0
- package/dist/commands/ssl.d.ts.map +1 -0
- package/dist/commands/ssl.js +105 -0
- package/dist/commands/ssl.js.map +1 -0
- package/dist/commands/ssl.test.d.ts +2 -0
- package/dist/commands/ssl.test.d.ts.map +1 -0
- package/dist/commands/ssl.test.js +95 -0
- package/dist/commands/ssl.test.js.map +1 -0
- package/dist/lib/config-store.d.ts +14 -0
- package/dist/lib/config-store.d.ts.map +1 -0
- package/dist/lib/config-store.js +111 -0
- package/dist/lib/config-store.js.map +1 -0
- package/dist/lib/config-store.test.d.ts +2 -0
- package/dist/lib/config-store.test.d.ts.map +1 -0
- package/dist/lib/config-store.test.js +173 -0
- package/dist/lib/config-store.test.js.map +1 -0
- package/dist/lib/domains.d.ts +37 -0
- package/dist/lib/domains.d.ts.map +1 -0
- package/dist/lib/domains.js +134 -0
- package/dist/lib/domains.js.map +1 -0
- package/dist/lib/domains.test.d.ts +2 -0
- package/dist/lib/domains.test.d.ts.map +1 -0
- package/dist/lib/domains.test.js +141 -0
- package/dist/lib/domains.test.js.map +1 -0
- package/dist/lib/logger.d.ts +19 -0
- package/dist/lib/logger.d.ts.map +1 -0
- package/dist/lib/logger.js +58 -0
- package/dist/lib/logger.js.map +1 -0
- package/dist/lib/nginx.d.ts +7 -0
- package/dist/lib/nginx.d.ts.map +1 -0
- package/dist/lib/nginx.js +86 -0
- package/dist/lib/nginx.js.map +1 -0
- package/dist/lib/nginx.test.d.ts +2 -0
- package/dist/lib/nginx.test.d.ts.map +1 -0
- package/dist/lib/nginx.test.js +46 -0
- package/dist/lib/nginx.test.js.map +1 -0
- package/dist/lib/paths.d.ts +13 -0
- package/dist/lib/paths.d.ts.map +1 -0
- package/dist/lib/paths.js +36 -0
- package/dist/lib/paths.js.map +1 -0
- package/dist/lib/paths.test.d.ts +2 -0
- package/dist/lib/paths.test.d.ts.map +1 -0
- package/dist/lib/paths.test.js +52 -0
- package/dist/lib/paths.test.js.map +1 -0
- package/package.json +34 -0
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# 服务器初始化脚本
|
|
3
|
+
# 在服务器上运行此脚本来设置部署环境
|
|
4
|
+
# 支持 Alibaba Cloud Linux 3 和 Ubuntu/Debian 系统
|
|
5
|
+
|
|
6
|
+
set -e
|
|
7
|
+
|
|
8
|
+
log() {
|
|
9
|
+
echo "[$(date +'%Y-%m-%d %H:%M:%S')] $1"
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
error() {
|
|
13
|
+
echo "[ERROR] $1" >&2
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
warning() {
|
|
17
|
+
echo "[WARNING] $1"
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
# 检测操作系统
|
|
21
|
+
detect_os() {
|
|
22
|
+
if [ -f /etc/os-release ]; then
|
|
23
|
+
. /etc/os-release
|
|
24
|
+
OS_ID="$ID"
|
|
25
|
+
OS_VERSION_ID="$VERSION_ID"
|
|
26
|
+
|
|
27
|
+
# 检测是否为 Alibaba Cloud Linux
|
|
28
|
+
if [ "$ID" = "alinux" ] || [ "$ID" = "alios" ] || grep -q "Alibaba Cloud Linux" /etc/os-release 2>/dev/null; then
|
|
29
|
+
OS_TYPE="alinux"
|
|
30
|
+
PKG_MANAGER="yum"
|
|
31
|
+
NGINX_USER="nginx"
|
|
32
|
+
NGINX_GROUP="nginx"
|
|
33
|
+
NGINX_CONFIG_DIR="/etc/nginx/conf.d"
|
|
34
|
+
# 检测是否为 CentOS/RHEL
|
|
35
|
+
elif [ "$ID" = "centos" ] || [ "$ID" = "rhel" ] || [ "$ID" = "rocky" ] || [ "$ID" = "almalinux" ]; then
|
|
36
|
+
OS_TYPE="rhel"
|
|
37
|
+
PKG_MANAGER="yum"
|
|
38
|
+
NGINX_USER="nginx"
|
|
39
|
+
NGINX_GROUP="nginx"
|
|
40
|
+
NGINX_CONFIG_DIR="/etc/nginx/conf.d"
|
|
41
|
+
# 检测是否为 Ubuntu/Debian
|
|
42
|
+
elif [ "$ID" = "ubuntu" ] || [ "$ID" = "debian" ]; then
|
|
43
|
+
OS_TYPE="debian"
|
|
44
|
+
PKG_MANAGER="apt-get"
|
|
45
|
+
NGINX_USER="www-data"
|
|
46
|
+
NGINX_GROUP="www-data"
|
|
47
|
+
NGINX_CONFIG_DIR="/etc/nginx/sites-available"
|
|
48
|
+
NGINX_ENABLED_DIR="/etc/nginx/sites-enabled"
|
|
49
|
+
else
|
|
50
|
+
OS_TYPE="unknown"
|
|
51
|
+
warning "未识别的操作系统: $ID"
|
|
52
|
+
# 默认使用 yum 和 nginx 用户组(Alibaba Cloud Linux 3 兼容)
|
|
53
|
+
PKG_MANAGER="yum"
|
|
54
|
+
NGINX_USER="nginx"
|
|
55
|
+
NGINX_GROUP="nginx"
|
|
56
|
+
NGINX_CONFIG_DIR="/etc/nginx/conf.d"
|
|
57
|
+
fi
|
|
58
|
+
|
|
59
|
+
log "检测到操作系统: $OS_ID $OS_VERSION_ID (类型: $OS_TYPE)"
|
|
60
|
+
else
|
|
61
|
+
error "无法检测操作系统,/etc/os-release 不存在"
|
|
62
|
+
exit 1
|
|
63
|
+
fi
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
# 检查并配置阿里云镜像源(仅 Alibaba Cloud Linux)
|
|
67
|
+
check_aliyun_mirror() {
|
|
68
|
+
if [ "$OS_TYPE" = "alinux" ]; then
|
|
69
|
+
if [ -f /etc/yum.repos.d/CentOS-Base.repo ] || [ -f /etc/yum.repos.d/epel.repo ]; then
|
|
70
|
+
# 检查是否已配置阿里云镜像源
|
|
71
|
+
if grep -q "mirrors.aliyun.com" /etc/yum.repos.d/*.repo 2>/dev/null; then
|
|
72
|
+
log "已检测到阿里云镜像源配置"
|
|
73
|
+
else
|
|
74
|
+
warning "建议配置阿里云镜像源以加速软件包下载"
|
|
75
|
+
warning "可以运行: sudo sed -i 's|^mirrorlist=|#mirrorlist=|g' /etc/yum.repos.d/*.repo"
|
|
76
|
+
warning "然后运行: sudo sed -i 's|^#baseurl=http://mirror.centos.org|baseurl=https://mirrors.aliyun.com|g' /etc/yum.repos.d/*.repo"
|
|
77
|
+
fi
|
|
78
|
+
fi
|
|
79
|
+
fi
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
# 安装 Nginx(如果未安装)
|
|
83
|
+
install_nginx() {
|
|
84
|
+
if command -v nginx >/dev/null 2>&1; then
|
|
85
|
+
log "Nginx 已安装: $(nginx -v 2>&1)"
|
|
86
|
+
return 0
|
|
87
|
+
fi
|
|
88
|
+
|
|
89
|
+
log "Nginx 未安装,开始安装..."
|
|
90
|
+
|
|
91
|
+
if [ "$PKG_MANAGER" = "yum" ]; then
|
|
92
|
+
# 更新 yum 缓存
|
|
93
|
+
yum makecache -y || true
|
|
94
|
+
|
|
95
|
+
# 安装 Nginx
|
|
96
|
+
if yum install -y nginx; then
|
|
97
|
+
log "Nginx 安装成功"
|
|
98
|
+
else
|
|
99
|
+
error "Nginx 安装失败"
|
|
100
|
+
exit 1
|
|
101
|
+
fi
|
|
102
|
+
elif [ "$PKG_MANAGER" = "apt-get" ]; then
|
|
103
|
+
# 更新 apt 缓存
|
|
104
|
+
apt-get update -y
|
|
105
|
+
|
|
106
|
+
# 安装 Nginx
|
|
107
|
+
if apt-get install -y nginx; then
|
|
108
|
+
log "Nginx 安装成功"
|
|
109
|
+
else
|
|
110
|
+
error "Nginx 安装失败"
|
|
111
|
+
exit 1
|
|
112
|
+
fi
|
|
113
|
+
else
|
|
114
|
+
error "不支持的包管理器: $PKG_MANAGER"
|
|
115
|
+
exit 1
|
|
116
|
+
fi
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
# 检查是否以 root 或 sudo 权限运行
|
|
120
|
+
if [ "$EUID" -ne 0 ]; then
|
|
121
|
+
error "请使用 sudo 运行此脚本"
|
|
122
|
+
exit 1
|
|
123
|
+
fi
|
|
124
|
+
|
|
125
|
+
# 检测操作系统
|
|
126
|
+
detect_os
|
|
127
|
+
|
|
128
|
+
# 检查阿里云镜像源(仅 Alibaba Cloud Linux)
|
|
129
|
+
if [ "$OS_TYPE" = "alinux" ]; then
|
|
130
|
+
check_aliyun_mirror
|
|
131
|
+
fi
|
|
132
|
+
|
|
133
|
+
log "开始服务器初始化..."
|
|
134
|
+
|
|
135
|
+
# 安装 Nginx(如果未安装)
|
|
136
|
+
install_nginx
|
|
137
|
+
|
|
138
|
+
# 创建部署目录(支持 admin, tenant, client, tools, website)
|
|
139
|
+
log "创建部署目录..."
|
|
140
|
+
for app in admin tenant client tools website; do
|
|
141
|
+
mkdir -p "/var/www/${app}"
|
|
142
|
+
mkdir -p "/var/www/backups/${app}"
|
|
143
|
+
mkdir -p "/tmp/deploy-${app}"
|
|
144
|
+
done
|
|
145
|
+
mkdir -p /opt/deploy
|
|
146
|
+
|
|
147
|
+
# 创建 SSL 证书目录
|
|
148
|
+
log "创建 SSL 证书目录..."
|
|
149
|
+
mkdir -p /etc/nginx/ssl
|
|
150
|
+
chmod 755 /etc/nginx/ssl
|
|
151
|
+
|
|
152
|
+
# 设置权限(使用检测到的用户组)
|
|
153
|
+
log "设置目录权限 (用户组: ${NGINX_USER}:${NGINX_GROUP})..."
|
|
154
|
+
chown -R ${NGINX_USER}:${NGINX_GROUP} /var/www
|
|
155
|
+
chmod -R 755 /var/www
|
|
156
|
+
|
|
157
|
+
# 检查部署脚本是否存在
|
|
158
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
159
|
+
DEPLOY_SCRIPT="${SCRIPT_DIR}/deploy.sh"
|
|
160
|
+
|
|
161
|
+
if [ ! -f "$DEPLOY_SCRIPT" ]; then
|
|
162
|
+
error "部署脚本不存在: $DEPLOY_SCRIPT"
|
|
163
|
+
error "请确保在项目根目录的 scripts 目录下运行此脚本"
|
|
164
|
+
exit 1
|
|
165
|
+
fi
|
|
166
|
+
|
|
167
|
+
# 复制部署脚本
|
|
168
|
+
log "安装部署脚本..."
|
|
169
|
+
cp "$DEPLOY_SCRIPT" /opt/deploy/
|
|
170
|
+
chmod +x /opt/deploy/deploy.sh
|
|
171
|
+
|
|
172
|
+
# 复制 SSL 脚本、域名列表与公共库(如果存在)
|
|
173
|
+
SSL_SCRIPT_DIR="${SCRIPT_DIR}/ssl"
|
|
174
|
+
LIB_DIR="${SCRIPT_DIR}/lib"
|
|
175
|
+
if [ -d "$SSL_SCRIPT_DIR" ]; then
|
|
176
|
+
log "安装 SSL 证书脚本..."
|
|
177
|
+
mkdir -p /opt/ssl
|
|
178
|
+
cp "$SSL_SCRIPT_DIR"/*.sh /opt/ssl/ 2>/dev/null || true
|
|
179
|
+
[ -f "$SSL_SCRIPT_DIR/domains.txt" ] && cp "$SSL_SCRIPT_DIR/domains.txt" /opt/ssl/ 2>/dev/null || true
|
|
180
|
+
[ -f "$LIB_DIR/common.sh" ] && cp "$LIB_DIR/common.sh" /opt/ssl/ 2>/dev/null || true
|
|
181
|
+
chmod +x /opt/ssl/*.sh 2>/dev/null || true
|
|
182
|
+
log "SSL 脚本已安装到 /opt/ssl/"
|
|
183
|
+
fi
|
|
184
|
+
|
|
185
|
+
# 创建占位 SSL 证书,使 nginx -t 在首次部署时能通过(后续 n2-deploy ssl 会用 acme.sh 申请真实证书覆盖)
|
|
186
|
+
# 域名列表来自部署脚本目录下 ssl/domains.txt(由 n2-deploy init 根据配置生成)
|
|
187
|
+
log "创建占位 SSL 证书(供 Nginx 配置验证,后续将由 acme.sh 替换为正式证书)..."
|
|
188
|
+
DOMAINS_FILE="${SCRIPT_DIR}/ssl/domains.txt"
|
|
189
|
+
if [ ! -f "$DOMAINS_FILE" ]; then
|
|
190
|
+
error "域名列表不存在: $DOMAINS_FILE,请先执行 n2-deploy init(会从配置生成 domains.txt)"
|
|
191
|
+
exit 1
|
|
192
|
+
fi
|
|
193
|
+
PLACEHOLDER_DOMAINS=""
|
|
194
|
+
while IFS= read -r line || [ -n "$line" ]; do
|
|
195
|
+
line=$(echo "$line" | tr -d '\r')
|
|
196
|
+
[ -z "$line" ] && continue
|
|
197
|
+
PLACEHOLDER_DOMAINS="${PLACEHOLDER_DOMAINS} ${line}"
|
|
198
|
+
done < "$DOMAINS_FILE"
|
|
199
|
+
if command -v openssl >/dev/null 2>&1; then
|
|
200
|
+
PLACEHOLDER_KEY="/etc/nginx/ssl/.placeholder.key"
|
|
201
|
+
PLACEHOLDER_CRT="/etc/nginx/ssl/.placeholder.crt"
|
|
202
|
+
openssl req -x509 -nodes -days 1 -newkey rsa:2048 \
|
|
203
|
+
-keyout "$PLACEHOLDER_KEY" -out "$PLACEHOLDER_CRT" \
|
|
204
|
+
-subj "/CN=placeholder" 2>/dev/null || true
|
|
205
|
+
if [ -f "$PLACEHOLDER_CRT" ] && [ -f "$PLACEHOLDER_KEY" ]; then
|
|
206
|
+
for domain in $PLACEHOLDER_DOMAINS; do
|
|
207
|
+
cp "$PLACEHOLDER_CRT" "/etc/nginx/ssl/${domain}.crt"
|
|
208
|
+
cp "$PLACEHOLDER_KEY" "/etc/nginx/ssl/${domain}.key"
|
|
209
|
+
done
|
|
210
|
+
rm -f "$PLACEHOLDER_CRT" "$PLACEHOLDER_KEY"
|
|
211
|
+
chmod 644 /etc/nginx/ssl/*.crt 2>/dev/null || true
|
|
212
|
+
chmod 600 /etc/nginx/ssl/*.key 2>/dev/null || true
|
|
213
|
+
chown root:root /etc/nginx/ssl/*.crt /etc/nginx/ssl/*.key 2>/dev/null || true
|
|
214
|
+
log "占位证书已创建,Nginx 可正常启动;部署流程中将申请正式证书并覆盖"
|
|
215
|
+
else
|
|
216
|
+
warning "无法生成占位证书,若 Nginx 配置验证失败请先手动申请 SSL 证书"
|
|
217
|
+
fi
|
|
218
|
+
else
|
|
219
|
+
warning "未找到 openssl,跳过占位证书创建;若 Nginx 配置验证失败请先安装 openssl 并申请 SSL 证书"
|
|
220
|
+
fi
|
|
221
|
+
|
|
222
|
+
# 安装 Nginx 配置(n2.conf 由 n2-deploy nginx 根据配置生成)
|
|
223
|
+
log "安装 Nginx 配置..."
|
|
224
|
+
NGINX_CONF_NAME="n2.conf"
|
|
225
|
+
NGINX_CONFIG="${SCRIPT_DIR}/nginx/${NGINX_CONF_NAME}"
|
|
226
|
+
if [ -f "$NGINX_CONFIG" ]; then
|
|
227
|
+
if [ "$OS_TYPE" = "debian" ]; then
|
|
228
|
+
# Ubuntu/Debian 使用 sites-available 和 sites-enabled
|
|
229
|
+
mkdir -p "$NGINX_CONFIG_DIR"
|
|
230
|
+
mkdir -p "$NGINX_ENABLED_DIR"
|
|
231
|
+
cp "$NGINX_CONFIG" "$NGINX_CONFIG_DIR/${NGINX_CONF_NAME}"
|
|
232
|
+
|
|
233
|
+
# 创建符号链接
|
|
234
|
+
if [ -L "$NGINX_ENABLED_DIR/${NGINX_CONF_NAME}" ]; then
|
|
235
|
+
rm "$NGINX_ENABLED_DIR/${NGINX_CONF_NAME}"
|
|
236
|
+
fi
|
|
237
|
+
ln -sf "$NGINX_CONFIG_DIR/${NGINX_CONF_NAME}" "$NGINX_ENABLED_DIR/${NGINX_CONF_NAME}"
|
|
238
|
+
NGINX_CONFIG_PATH="$NGINX_ENABLED_DIR/${NGINX_CONF_NAME}"
|
|
239
|
+
else
|
|
240
|
+
# Alibaba Cloud Linux 3 / CentOS/RHEL 使用 conf.d
|
|
241
|
+
mkdir -p "$NGINX_CONFIG_DIR"
|
|
242
|
+
cp "$NGINX_CONFIG" "$NGINX_CONFIG_DIR/${NGINX_CONF_NAME}"
|
|
243
|
+
NGINX_CONFIG_PATH="$NGINX_CONFIG_DIR/${NGINX_CONF_NAME}"
|
|
244
|
+
fi
|
|
245
|
+
|
|
246
|
+
log "Nginx 配置文件已安装到: $NGINX_CONFIG_PATH"
|
|
247
|
+
|
|
248
|
+
# 测试 Nginx 配置
|
|
249
|
+
if nginx -t; then
|
|
250
|
+
log "Nginx 配置验证成功"
|
|
251
|
+
# 尝试重新加载 Nginx(如果正在运行)
|
|
252
|
+
if systemctl is-active --quiet nginx 2>/dev/null; then
|
|
253
|
+
log "重新加载 Nginx 配置..."
|
|
254
|
+
systemctl reload nginx || warning "Nginx 重新加载失败,请手动检查"
|
|
255
|
+
else
|
|
256
|
+
log "Nginx 未运行,请手动启动: sudo systemctl start nginx"
|
|
257
|
+
fi
|
|
258
|
+
else
|
|
259
|
+
error "Nginx 配置验证失败,请检查配置文件"
|
|
260
|
+
exit 1
|
|
261
|
+
fi
|
|
262
|
+
else
|
|
263
|
+
error "Nginx 配置文件不存在: $NGINX_CONFIG"
|
|
264
|
+
error "请先执行 n2-deploy nginx --config <config.yaml> --output <path>/n2.conf 生成 n2.conf"
|
|
265
|
+
exit 1
|
|
266
|
+
fi
|
|
267
|
+
|
|
268
|
+
log "服务器初始化完成!"
|
|
269
|
+
log ""
|
|
270
|
+
log "系统信息:"
|
|
271
|
+
log " - 操作系统: $OS_ID $OS_VERSION_ID"
|
|
272
|
+
log " - 包管理器: $PKG_MANAGER"
|
|
273
|
+
log " - Nginx 用户组: ${NGINX_USER}:${NGINX_GROUP}"
|
|
274
|
+
log " - Nginx 配置路径: $NGINX_CONFIG_PATH"
|
|
275
|
+
log ""
|
|
276
|
+
log "后续步骤:"
|
|
277
|
+
log "1. 编辑 $NGINX_CONFIG_PATH,修改域名配置"
|
|
278
|
+
log "2. 配置 GitHub Secrets (DEPLOY_HOST, DEPLOY_SSH_KEY, ALIYUN_ACCESS_KEY_ID, ALIYUN_ACCESS_KEY_SECRET)"
|
|
279
|
+
log "3. 设置 SSH 密钥认证"
|
|
280
|
+
log "4. 配置 SSL 证书(使用 acme.sh + 阿里云 DNS API):"
|
|
281
|
+
log " - 设置环境变量: export Ali_Key=\"your_key\" && export Ali_Secret=\"your_secret\""
|
|
282
|
+
log " - 运行脚本: sudo ./scripts/ssl/setup-ssl.sh"
|
|
283
|
+
log " - 详细说明请参考: scripts/ssl/README.md"
|
|
284
|
+
log "5. 重启 Nginx: sudo systemctl restart nginx"
|
|
285
|
+
|
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
# SSL 证书配置说明
|
|
2
|
+
|
|
3
|
+
本文档说明如何使用 acme.sh 和阿里云 DNS API 自动申请和续期 Let's Encrypt SSL 证书。
|
|
4
|
+
|
|
5
|
+
## 概述
|
|
6
|
+
|
|
7
|
+
本方案使用 [acme.sh](https://github.com/acmesh-official/acme.sh) 工具,通过阿里云 DNS API 进行域名验证,自动申请和续期 Let's Encrypt 证书。这种方式不需要开放 80 端口,适合服务器防火墙严格限制的场景。
|
|
8
|
+
|
|
9
|
+
## 前置要求
|
|
10
|
+
|
|
11
|
+
1. **域名已解析到服务器**:确保域名 DNS 已正确配置
|
|
12
|
+
2. **阿里云账号**:拥有域名管理权限的阿里云账号
|
|
13
|
+
3. **阿里云 API 凭证**:AccessKey ID 和 AccessKey Secret
|
|
14
|
+
|
|
15
|
+
## 获取阿里云 API 凭证
|
|
16
|
+
|
|
17
|
+
### 1. 创建 RAM 用户(推荐)
|
|
18
|
+
|
|
19
|
+
为了安全,建议创建一个专门的 RAM 用户用于 DNS API 操作:
|
|
20
|
+
|
|
21
|
+
1. 登录 [阿里云控制台](https://ecs.console.aliyun.com/)
|
|
22
|
+
2. 进入 **访问控制(RAM)** > **用户** > **创建用户**
|
|
23
|
+
3. 填写用户信息,勾选 **编程访问**(获取 AccessKey)
|
|
24
|
+
4. 记录 AccessKey ID 和 AccessKey Secret(只显示一次,请妥善保管)
|
|
25
|
+
|
|
26
|
+
### 2. 授权 RAM 用户
|
|
27
|
+
|
|
28
|
+
为 RAM 用户授予 DNS 管理权限:
|
|
29
|
+
|
|
30
|
+
1. 在用户列表中,点击新创建的用户
|
|
31
|
+
2. 进入 **权限管理** > **添加权限**
|
|
32
|
+
3. 选择 **AliyunDNSFullAccess**(DNS 完全管理权限)
|
|
33
|
+
- 或者创建自定义策略,仅授予以下权限:
|
|
34
|
+
- `alidns:DescribeDomainRecords` - 查询域名解析记录
|
|
35
|
+
- `alidns:AddDomainRecord` - 添加域名解析记录
|
|
36
|
+
- `alidns:DeleteDomainRecord` - 删除域名解析记录
|
|
37
|
+
|
|
38
|
+
### 3. 获取 AccessKey
|
|
39
|
+
|
|
40
|
+
1. 在用户详情页,进入 **安全信息** > **创建 AccessKey**
|
|
41
|
+
2. 记录 **AccessKey ID** 和 **AccessKey Secret**
|
|
42
|
+
|
|
43
|
+
## 配置步骤
|
|
44
|
+
|
|
45
|
+
### 方法一:手动配置(首次设置)
|
|
46
|
+
|
|
47
|
+
1. **设置环境变量**
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
export Ali_Key="your_access_key_id"
|
|
51
|
+
export Ali_Secret="your_access_key_secret"
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
2. **运行证书申请脚本**
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
cd /path/to/deploy-package/assets/deploy/ssl
|
|
58
|
+
sudo ./setup-ssl.sh --domains "your-domain.com"
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
域名列表由部署配置(如 `~/.deploy/config.yaml` 或包内 `assets/deploy/domains.yaml`)管理;执行 `n2-deploy init` 或 `n2-deploy ssl` 会根据配置生成/更新 `domains.txt`。`check-and-setup-ssl.sh` 会按 `domains.txt` 自动为所有域名申请证书。
|
|
62
|
+
|
|
63
|
+
脚本会自动:
|
|
64
|
+
|
|
65
|
+
- 安装 acme.sh(如果未安装)
|
|
66
|
+
- 为指定域名申请证书
|
|
67
|
+
- 安装证书到 `/etc/nginx/ssl/`
|
|
68
|
+
- 配置自动续期
|
|
69
|
+
|
|
70
|
+
### 方法二:通过 GitHub Secrets(CI/CD 自动化)
|
|
71
|
+
|
|
72
|
+
1. **配置 GitHub Secrets**
|
|
73
|
+
|
|
74
|
+
在 GitHub 仓库设置中添加以下 Secrets:
|
|
75
|
+
|
|
76
|
+
- `ALIYUN_ACCESS_KEY_ID`: 阿里云 AccessKey ID
|
|
77
|
+
- `ALIYUN_ACCESS_KEY_SECRET`: 阿里云 AccessKey Secret
|
|
78
|
+
|
|
79
|
+
2. **在 CI/CD 中使用**
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
export Ali_Key="${{ secrets.ALIYUN_ACCESS_KEY_ID }}"
|
|
83
|
+
export Ali_Secret="${{ secrets.ALIYUN_ACCESS_KEY_SECRET }}"
|
|
84
|
+
sudo ./scripts/ssl/setup-ssl.sh
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### 自定义域名
|
|
88
|
+
|
|
89
|
+
如果需要使用不同的域名,可以通过参数指定:
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
sudo ./setup-ssl.sh --domains "tools.example.com,qxtool.vip,www.qxtool.vip"
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### 调试模式
|
|
96
|
+
|
|
97
|
+
如果证书申请失败,可以使用 `--debug` 参数启用调试模式,获取更详细的错误信息:
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
sudo ./setup-ssl.sh --debug --ali-key "your_key" --ali-secret "your_secret"
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
调试模式会:
|
|
104
|
+
|
|
105
|
+
- 显示详细的执行日志
|
|
106
|
+
- 输出完整的 acme.sh 命令
|
|
107
|
+
- 提供更详细的错误信息和建议
|
|
108
|
+
|
|
109
|
+
## 证书续期
|
|
110
|
+
|
|
111
|
+
### 自动续期
|
|
112
|
+
|
|
113
|
+
acme.sh 会自动创建 cron 任务,证书将在到期前 30 天自动续期。无需手动操作。
|
|
114
|
+
|
|
115
|
+
### 手动续期
|
|
116
|
+
|
|
117
|
+
如果需要手动续期证书:
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
sudo ./scripts/ssl/renew-ssl.sh
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
强制续期所有证书(即使未到期):
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
sudo ./scripts/ssl/renew-ssl.sh --force
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### 查看续期任务
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
crontab -l
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## 证书文件位置
|
|
136
|
+
|
|
137
|
+
证书文件安装在以下位置:
|
|
138
|
+
|
|
139
|
+
- **Tools 域名证书**:
|
|
140
|
+
- 证书:`/etc/nginx/ssl/tools.qxtool.vip.crt`
|
|
141
|
+
- 私钥:`/etc/nginx/ssl/tools.qxtool.vip.key`
|
|
142
|
+
|
|
143
|
+
- **Website 域名证书**:
|
|
144
|
+
- 证书:`/etc/nginx/ssl/qxtool.vip.crt`
|
|
145
|
+
- 私钥:`/etc/nginx/ssl/qxtool.vip.key`
|
|
146
|
+
|
|
147
|
+
## 故障排查
|
|
148
|
+
|
|
149
|
+
### 1. 证书申请失败
|
|
150
|
+
|
|
151
|
+
**错误:无法找到域名**
|
|
152
|
+
|
|
153
|
+
- 检查域名 DNS 是否正确解析到服务器
|
|
154
|
+
- 确认域名在阿里云 DNS 中已添加
|
|
155
|
+
|
|
156
|
+
**错误:API 凭证无效**
|
|
157
|
+
|
|
158
|
+
- 检查 AccessKey ID 和 Secret 是否正确
|
|
159
|
+
- 确认 RAM 用户已授予 DNS 管理权限
|
|
160
|
+
- 验证 AccessKey 是否已启用
|
|
161
|
+
|
|
162
|
+
**错误:DNS 记录添加失败(Error adding TXT record)**
|
|
163
|
+
|
|
164
|
+
这是最常见的错误,可能的原因和解决方法:
|
|
165
|
+
|
|
166
|
+
1. **检查 RAM 用户权限**
|
|
167
|
+
- 登录阿里云控制台,检查 RAM 用户是否拥有 `AliyunDNSFullAccess` 权限
|
|
168
|
+
- 或者至少拥有以下权限:
|
|
169
|
+
- `alidns:DescribeDomainRecords` - 查询域名解析记录
|
|
170
|
+
- `alidns:AddDomainRecord` - 添加域名解析记录
|
|
171
|
+
- `alidns:DeleteDomainRecord` - 删除域名解析记录
|
|
172
|
+
|
|
173
|
+
2. **确认域名在阿里云 DNS 管理**
|
|
174
|
+
- 登录 [阿里云 DNS 控制台](https://dns.console.aliyun.com/)
|
|
175
|
+
- 确认域名 `qxtool.vip` 已在控制台添加
|
|
176
|
+
- 确认子域名 `tools.qxtool.vip` 的 DNS 解析由阿里云管理
|
|
177
|
+
|
|
178
|
+
3. **验证 API 凭证**
|
|
179
|
+
|
|
180
|
+
```bash
|
|
181
|
+
# 检查环境变量是否正确设置
|
|
182
|
+
echo "AccessKey ID: ${Ali_Key:0:10}..."
|
|
183
|
+
echo "AccessKey Secret: ${Ali_Secret:0:10}..."
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
4. **使用调试模式重新运行**
|
|
187
|
+
|
|
188
|
+
```bash
|
|
189
|
+
sudo ./setup-ssl.sh --debug --ali-key "your_key" --ali-secret "your_secret"
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
5. **查看详细日志**
|
|
193
|
+
|
|
194
|
+
```bash
|
|
195
|
+
# 查看 acme.sh 日志
|
|
196
|
+
tail -100 ~/.acme.sh/acme.sh.log
|
|
197
|
+
|
|
198
|
+
# 实时查看日志
|
|
199
|
+
tail -f ~/.acme.sh/acme.sh.log
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
6. **手动测试 DNS API**
|
|
203
|
+
- 如果问题持续,可以尝试手动调用阿里云 DNS API 测试权限
|
|
204
|
+
- 参考 [阿里云 DNS API 文档](https://help.aliyun.com/document_detail/29739.html)
|
|
205
|
+
|
|
206
|
+
### 2. 证书续期失败
|
|
207
|
+
|
|
208
|
+
**检查证书到期时间**:
|
|
209
|
+
|
|
210
|
+
```bash
|
|
211
|
+
openssl x509 -in /etc/nginx/ssl/tools.qxtool.vip.crt -noout -dates
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
**查看 acme.sh 日志**:
|
|
215
|
+
|
|
216
|
+
```bash
|
|
217
|
+
tail -f ~/.acme.sh/acme.sh.log
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
**手动测试续期**:
|
|
221
|
+
|
|
222
|
+
```bash
|
|
223
|
+
sudo ~/.acme.sh/acme.sh --renew -d tools.qxtool.vip --force
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
### 3. Nginx 配置错误
|
|
227
|
+
|
|
228
|
+
**测试 Nginx 配置**:
|
|
229
|
+
|
|
230
|
+
```bash
|
|
231
|
+
sudo nginx -t
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
**查看 Nginx 错误日志**:
|
|
235
|
+
|
|
236
|
+
```bash
|
|
237
|
+
sudo tail -f /var/log/nginx/error.log
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
**检查证书文件权限**:
|
|
241
|
+
|
|
242
|
+
```bash
|
|
243
|
+
ls -la /etc/nginx/ssl/
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
应该显示:
|
|
247
|
+
|
|
248
|
+
- `.key` 文件权限为 `600`,所有者 `root:root`
|
|
249
|
+
- `.crt` 文件权限为 `644`,所有者 `root:root`
|
|
250
|
+
|
|
251
|
+
### 4. acme.sh 未找到
|
|
252
|
+
|
|
253
|
+
如果提示 `acme.sh: command not found`:
|
|
254
|
+
|
|
255
|
+
```bash
|
|
256
|
+
# 检查安装位置
|
|
257
|
+
ls -la ~/.acme.sh/
|
|
258
|
+
|
|
259
|
+
# 添加到 PATH
|
|
260
|
+
export PATH="$HOME/.acme.sh:$PATH"
|
|
261
|
+
|
|
262
|
+
# 或使用完整路径
|
|
263
|
+
~/.acme.sh/acme.sh --version
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
## 安全建议
|
|
267
|
+
|
|
268
|
+
1. **最小权限原则**:为 RAM 用户仅授予必要的 DNS 管理权限
|
|
269
|
+
2. **定期轮换密钥**:建议每 3-6 个月更换一次 AccessKey
|
|
270
|
+
3. **保护私钥**:证书私钥文件权限应设置为 600,仅 root 可读
|
|
271
|
+
4. **监控续期**:定期检查证书续期是否成功
|
|
272
|
+
5. **备份证书**:定期备份证书文件到安全位置
|
|
273
|
+
|
|
274
|
+
## 相关资源
|
|
275
|
+
|
|
276
|
+
- [acme.sh 官方文档](https://github.com/acmesh-official/acme.sh)
|
|
277
|
+
- [阿里云 DNS API 文档](https://help.aliyun.com/document_detail/29739.html)
|
|
278
|
+
- [Let's Encrypt 文档](https://letsencrypt.org/docs/)
|
|
279
|
+
|
|
280
|
+
## 常见问题
|
|
281
|
+
|
|
282
|
+
### Q: 证书有效期是多久?
|
|
283
|
+
|
|
284
|
+
A: Let's Encrypt 证书有效期为 90 天,acme.sh 会在到期前 30 天自动续期。
|
|
285
|
+
|
|
286
|
+
### Q: 续期会影响服务吗?
|
|
287
|
+
|
|
288
|
+
A: 不会。续期后会自动重新加载 nginx,不会中断服务。
|
|
289
|
+
|
|
290
|
+
### Q: 可以申请通配符证书吗?
|
|
291
|
+
|
|
292
|
+
A: 可以。acme.sh 支持通配符证书,使用 `-d "*.example.com"` 参数即可。
|
|
293
|
+
|
|
294
|
+
### Q: 如何查看证书信息?
|
|
295
|
+
|
|
296
|
+
A: 使用以下命令:
|
|
297
|
+
|
|
298
|
+
```bash
|
|
299
|
+
openssl x509 -in /etc/nginx/ssl/tools.qxtool.vip.crt -noout -text
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
### Q: 证书申请需要多长时间?
|
|
303
|
+
|
|
304
|
+
A: 通常 1-3 分钟,取决于 DNS 记录传播速度。
|
|
305
|
+
|
|
306
|
+
### Q: 遇到 "Error adding TXT record" 错误怎么办?
|
|
307
|
+
|
|
308
|
+
A: 这通常表示阿里云 DNS API 调用失败。请按以下步骤排查:
|
|
309
|
+
|
|
310
|
+
1. **检查权限**:确认 RAM 用户拥有 DNS 管理权限
|
|
311
|
+
2. **确认域名**:确认域名在阿里云 DNS 控制台已添加
|
|
312
|
+
3. **验证凭证**:检查 AccessKey 是否正确且已启用
|
|
313
|
+
4. **查看日志**:运行 `tail -100 ~/.acme.sh/acme.sh.log` 查看详细错误
|
|
314
|
+
5. **使用调试模式**:使用 `--debug` 参数重新运行脚本
|
|
315
|
+
|
|
316
|
+
如果问题仍然存在,请检查:
|
|
317
|
+
|
|
318
|
+
- 网络连接是否正常
|
|
319
|
+
- 阿里云 API 服务是否可用
|
|
320
|
+
- 是否有防火墙或安全组限制
|