package deployment import ( "context" "crypto/ed25519" "crypto/rand" "encoding/pem" "strings" "testing" "time" "golang.org/x/crypto/ssh" ) type staticSecretReader map[string]map[string]string func (s staticSecretReader) Get(_ context.Context, reference string) (map[string]string, error) { return s[reference], nil } func TestSSHFactoryParsesEncryptedPrivateKeyBeforeDialing(t *testing.T) { _, key, err := ed25519.GenerateKey(rand.Reader) if err != nil { t.Fatalf("failed to generate test private key: %v", err) } block, err := ssh.MarshalPrivateKeyWithPassphrase(key, "scert-deployment-test", []byte("deployment-passphrase")) if err != nil { t.Fatalf("failed to encrypt test private key: %v", err) } privateKey := string(pem.EncodeToMemory(block)) server := Server{ ID: "server-test", Name: "test server", Address: "127.0.0.1", Port: 1, Username: "deploy", PrivateKeyRef: "secret://ssh/test", HostKeyFingerprint: "SHA256:test", CertificatePath: "/tmp/cert.pem", PrivateKeyPath: "/tmp/key.pem", CertificateChainPath: "/tmp/fullchain.pem", WebServer: WebServerNginx, } factory := SSHFactory{ Secrets: staticSecretReader{server.PrivateKeyRef: {"private_key": privateKey, "passphrase": "deployment-passphrase"}}, Timeout: 100 * time.Millisecond, } if _, err := factory.Connect(t.Context(), server); err == nil || !strings.Contains(err.Error(), "连接 SSH 服务器失败") { t.Fatalf("encrypted private key should parse before the expected dial failure: %v", err) } factory.Secrets = staticSecretReader{server.PrivateKeyRef: {"private_key": privateKey, "passphrase": "wrong-passphrase"}} if _, err := factory.Connect(t.Context(), server); err == nil || !strings.Contains(err.Error(), "解析 SSH 私钥失败") { t.Fatalf("wrong passphrase should be rejected before dialing: %v", err) } } func TestValidateCommandRejectsShellControlLines(t *testing.T) { for _, command := range [][]string{ {"nginx", "-s", "reload\nrm -rf /"}, {"nginx", "", "reload"}, {"nginx", "-s", "reload\x00"}, } { if err := validateCommand(command); err == nil { t.Fatalf("不安全远程命令未被拒绝:%q", command) } } } func TestValidateCommandAllowsShellScriptArgument(t *testing.T) { command := []string{"sh", "-c", "set -eu\nprintf '%s\\n' 'ok'"} if err := validateCommand(command); err != nil { t.Fatalf("shell script argument should be accepted: %v", err) } } func TestJoinShellArgsQuotesArguments(t *testing.T) { command := []string{"nginx", "-c", "/etc/nginx/my config.conf", "reload'check"} joined := joinShellArgs(command) if !strings.Contains(joined, "'/etc/nginx/my config.conf'") || !strings.Contains(joined, "'reload'\\''check'") { 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) } } func TestAuthorizedKeyInstallScriptEnablesRootPublicKeyAuthentication(t *testing.T) { script := authorizedKeyInstallScript("encoded-public-key") for _, required := range []string{ "PubkeyAuthentication yes", "AuthorizedKeysFile .ssh/authorized_keys", "sshd -t", "systemctl reload ssh", } { if !strings.Contains(script, required) { t.Fatalf("authorized key script is missing %q: %s", required, script) } } }