修复SSH公钥部署脚本并更新进度
This commit is contained in:
parent
58e5e52e53
commit
9af39b071e
@ -351,15 +351,7 @@ func (c *sshClientAdapter) InstallAuthorizedKey(ctx context.Context, publicKey s
|
||||
return fmt.Errorf("SSH 公钥格式无效:%w", err)
|
||||
}
|
||||
encoded := base64.StdEncoding.EncodeToString(ssh.MarshalAuthorizedKey(key))
|
||||
script := fmt.Sprintf(`set -eu
|
||||
umask 077
|
||||
install -d -m 700 "$HOME/.ssh"
|
||||
touch "$HOME/.ssh/authorized_keys"
|
||||
chmod 600 "$HOME/.ssh/authorized_keys"
|
||||
decoded="$(printf '%s' '%s' | base64 -d)"
|
||||
if ! grep -Fqx -- "$decoded" "$HOME/.ssh/authorized_keys"; then
|
||||
printf '%%s\\n' "$decoded" >> "$HOME/.ssh/authorized_keys"
|
||||
fi`, "%s", encoded)
|
||||
script := authorizedKeyInstallScript(encoded)
|
||||
result, err := c.Run(ctx, []string{"sh", "-c", script})
|
||||
if err != nil {
|
||||
return fmt.Errorf("部署 SSH 公钥失败:%w", err)
|
||||
@ -369,6 +361,18 @@ fi`, "%s", encoded)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func authorizedKeyInstallScript(encoded string) string {
|
||||
return fmt.Sprintf(`set -eu
|
||||
umask 077
|
||||
install -d -m 700 "$HOME/.ssh"
|
||||
touch "$HOME/.ssh/authorized_keys"
|
||||
chmod 600 "$HOME/.ssh/authorized_keys"
|
||||
decoded="$(printf '%%s' '%s' | base64 -d)"
|
||||
if ! grep -Fqx -- "$decoded" "$HOME/.ssh/authorized_keys"; then
|
||||
printf '%%s\n' "$decoded" >> "$HOME/.ssh/authorized_keys"
|
||||
fi`, encoded)
|
||||
}
|
||||
func (c *sshClientAdapter) Run(ctx context.Context, command []string) (CommandResult, error) {
|
||||
if len(command) == 0 {
|
||||
return CommandResult{}, errors.New("远程命令不能为空")
|
||||
|
||||
@ -66,3 +66,16 @@ func TestJoinShellArgsQuotesArguments(t *testing.T) {
|
||||
t.Fatalf("远程命令参数未正确转义:%s", joined)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAuthorizedKeyInstallScriptUsesRealNewline(t *testing.T) {
|
||||
script := authorizedKeyInstallScript("encoded-public-key")
|
||||
if !strings.Contains(script, "printf '%s' 'encoded-public-key'") {
|
||||
t.Fatalf("authorized key payload was not inserted safely: %s", script)
|
||||
}
|
||||
if !strings.Contains(script, "printf '%s\\n'") {
|
||||
t.Fatalf("authorized key should be terminated by a real newline: %s", script)
|
||||
}
|
||||
if strings.Contains(script, "printf '%s\\\\n'") {
|
||||
t.Fatalf("authorized key script still writes a literal backslash-n: %s", script)
|
||||
}
|
||||
}
|
||||
|
||||
@ -971,6 +971,7 @@ func (s *Server) provisionServerSSHKey(ctx context.Context, item deployment.Serv
|
||||
}
|
||||
publicKey, err := s.sshKeys.AuthorizedPublicKey(ctx, item.PrivateKeyRef)
|
||||
if err != nil {
|
||||
s.logSSHProvisionFailure("authorized_public_key", item, err)
|
||||
item.SSHKeyStatus = deployment.SSHKeyStatusFailed
|
||||
item.SSHKeyError = "自动部署 SSH 公钥失败"
|
||||
return item, err
|
||||
@ -992,6 +993,7 @@ func (s *Server) provisionServerSSHKey(ctx context.Context, item deployment.Serv
|
||||
return item, err
|
||||
}
|
||||
if err := passwordClient.Close(); err != nil {
|
||||
s.logSSHProvisionFailure("password_close", item, err)
|
||||
item.SSHKeyStatus = deployment.SSHKeyStatusFailed
|
||||
item.SSHKeyError = "自动部署 SSH 公钥失败"
|
||||
return item, err
|
||||
|
||||
@ -1153,3 +1153,16 @@ P0-05 从 `[!]` 调整为 `[~]`,并未标记完成;P0-04 剩余门禁和全
|
||||
- [~] P0-10 其他任务、跨数据库实机矩阵和统一复审继续进行中。
|
||||
- [ ] 真实生产数据库、密码 SSH、远端部署、HTTPS/DNS、ACME、Linux/Windows 服务和最终生产门禁仍未完成。
|
||||
- [!] 不新增 `[✓]`,不以本地结果代替生产验收。
|
||||
|
||||
## 本轮开发进度(2026-09-30,P0-03 SSH 公钥部署阻塞修复)
|
||||
|
||||
总体验收继续保持 **P0-03:完成,待复审**,本轮仅完成 SSH 公钥部署实现修正与本地回归,不代表生产部署验收通过。
|
||||
|
||||
- [x] 已完成,等待复审:修正公钥安装脚本的 `fmt.Sprintf` 参数传递,确保实际编码公钥被写入远端。
|
||||
- [x] 已完成,等待复审:修正 `authorized_keys` 追加逻辑,使用真实换行,不再写入字面量 `\\n`。
|
||||
- [x] 已完成,等待复审:补充公钥读取失败、密码连接关闭失败的 SSH 阶段日志。
|
||||
- [x] 已完成:新增公钥安装脚本回归测试;`go test ./...`、`go test -race ./internal/deployment ./internal/httpapi ./internal/app`、`go vet ./...`、`go build ./...`、`git diff --check` 均通过。
|
||||
- [~] 进行中:需重新构建正式候选包并部署到实机,通过现有失败服务器记录复测密码连接、公钥写入、私钥回连和状态持久化。
|
||||
- [ ] 未开始:证书文件推送、Nginx 配置校验/重载、失败回滚、真实多服务器部署和最终生产门禁。
|
||||
- [!] 第二台真实服务器仍不可用,不以虚构数据替代多服务器生产验收。
|
||||
- [!] 总体验收不得升级:仍为 **P0-03 完成,待复审**,没有新增 `[✓]`。
|
||||
|
||||
@ -1801,3 +1801,16 @@ P0-11 生产验收门禁
|
||||
- [~] P0-10 其他任务、跨数据库实机矩阵和统一复审继续进行中。
|
||||
- [ ] 真实生产数据库、密码 SSH、远端部署、HTTPS/DNS、ACME、Linux/Windows 服务和最终生产门禁仍未完成。
|
||||
- [!] 不新增 `[✓]`,不以本地结果代替生产验收。
|
||||
|
||||
## 本轮开发进度(2026-09-30,P0-03 SSH 公钥部署阻塞修复)
|
||||
|
||||
总体验收继续保持 **P0-03:完成,待复审**,本轮仅完成 SSH 公钥部署实现修正与本地回归,不代表生产部署验收通过。
|
||||
|
||||
- [x] 已完成,等待复审:修正公钥安装脚本的 `fmt.Sprintf` 参数传递,确保实际编码公钥被写入远端。
|
||||
- [x] 已完成,等待复审:修正 `authorized_keys` 追加逻辑,使用真实换行,不再写入字面量 `\\n`。
|
||||
- [x] 已完成,等待复审:补充公钥读取失败、密码连接关闭失败的 SSH 阶段日志。
|
||||
- [x] 已完成:新增公钥安装脚本回归测试;`go test ./...`、`go test -race ./internal/deployment ./internal/httpapi ./internal/app`、`go vet ./...`、`go build ./...`、`git diff --check` 均通过。
|
||||
- [~] 进行中:需重新构建正式候选包并部署到实机,通过现有失败服务器记录复测密码连接、公钥写入、私钥回连和状态持久化。
|
||||
- [ ] 未开始:证书文件推送、Nginx 配置校验/重载、失败回滚、真实多服务器部署和最终生产门禁。
|
||||
- [!] 第二台真实服务器仍不可用,不以虚构数据替代多服务器生产验收。
|
||||
- [!] 总体验收不得升级:仍为 **P0-03 完成,待复审**,没有新增 `[✓]`。
|
||||
|
||||
Loading…
Reference in New Issue
Block a user