{"record":{"id":"58bd12f10113ee32","repo":"hashicorp/terraform","slug":"failed-to-parse-private-key-q-s","errorCode":null,"errorMessage":"failed to parse private key %q: %s","messagePattern":"failed to parse private key %q: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/communicator/ssh/provisioner.go","lineNumber":400,"sourceCode":"\n\tif opts.password != \"\" {\n\t\tconf.Auth = append(conf.Auth, ssh.Password(opts.password))\n\t\tconf.Auth = append(conf.Auth, ssh.KeyboardInteractive(\n\t\t\tPasswordKeyboardInteractive(opts.password)))\n\t}\n\n\tif opts.sshAgent != nil {\n\t\tconf.Auth = append(conf.Auth, opts.sshAgent.Auth())\n\t}\n\n\treturn conf, nil\n}\n\n// Create a Cert Signer and return ssh.AuthMethod\nfunc signCertWithPrivateKey(pk string, certificate string) (ssh.AuthMethod, error) {\n\trawPk, err := ssh.ParseRawPrivateKey([]byte(pk))\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to parse private key %q: %s\", pk, err)\n\t}\n\n\tpcert, _, _, _, err := ssh.ParseAuthorizedKey([]byte(certificate))\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to parse certificate %q: %s\", certificate, err)\n\t}\n\n\tusigner, err := ssh.NewSignerFromKey(rawPk)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to create signer from raw private key %q: %s\", rawPk, err)\n\t}\n\n\tucertSigner, err := ssh.NewCertSigner(pcert.(*ssh.Certificate), usigner)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to create cert signer %q: %s\", usigner, err)\n\t}\n\n\treturn ssh.PublicKeys(ucertSigner), nil","sourceCodeStart":382,"sourceCodeEnd":418,"githubUrl":"https://github.com/hashicorp/terraform/blob/d32a084675427f5ac3f7d2868578ef8b2c1dc525/internal/communicator/ssh/provisioner.go#L382-L418","documentation":"In signCertWithPrivateKey, the user-supplied private key string is parsed via ssh.ParseRawPrivateKey. If the key is not valid PEM, is corrupted, uses an unsupported algorithm, or is in the wrong format, parsing fails. SECURITY NOTE: the error message interpolates the full private key material via %q, potentially exposing secrets in logs and error output.","triggerScenarios":"Configuring an SSH connection with both a private_key and a certificate where the private_key value is not a parseable OpenSSH/PKCS PEM key. Triggered by: pasting a public key instead of a private key, providing an OpenSSH new-format key that ParseRawPrivateKey doesn't support in older x/crypto versions, truncated key, or wrong-line-ending corruption.","commonSituations":"User accidentally references the .pub file in private_key. Key was generated with a newer ssh-keygen format (ed25519 or RFC 4716) unsupported by the linked golang.org/x/crypto version. Key pasted from a secrets manager that stripped newlines. Key has Windows CRLF line endings that confuse the PEM decoder.","solutions":["Verify the private_key value is a complete, unmodified PEM private key (begins with -----BEGIN ... PRIVATE KEY-----).","Ensure you are not accidentally using the .pub file or a public key string.","If using ed25519 or new-format keys, update golang.org/x/crypto to a version that supports them, or regenerate an RSA/ECDSA key.","Check for newline corruption: the key must preserve all newlines. If loading from a file, use file() not a variable that may have been mangled.","Strip any trailing whitespace or CRLF line endings from the key material."],"exampleFix":"# before — accidentally using public key or mangled key\nconnection {\n  private_key = var.some_key  # might be .pub or truncated\n  certificate  = var.cert\n}\n\n# after — valid PEM private key with preserved newlines\nconnection {\n  private_key = file(\"~/.ssh/id_rsa\")       # not id_rsa.pub\ncertificate  = file(\"~/.ssh/id_rsa-cert.pub\")\n}","handlingStrategy":"validation","validationCode":"// Pre-validate the private key parses before using it in a connection\nimport \"golang.org/x/crypto/ssh\"\n\nfunc validatePrivateKey(pk string) error {\n    _, err := ssh.ParseRawPrivateKey([]byte(pk))\n    if err != nil {\n        // Do NOT log pk contents — it is secret\n        return fmt.Errorf(\"private key is not parseable: %w\", err)\n    }\n    return nil\n}","typeGuard":"func isValidPEMPrivateKey(pk string) bool {\n    block, _ := pem.Decode([]byte(pk))\n    return block != nil &&\n        (strings.Contains(block.Type, \"PRIVATE KEY\")) &&\n        block.Headers[\"Proc-Type\"] != \"4,ENCRYPTED\"\n}","tryCatchPattern":"// Wrap signCertWithPrivateKey; never log the key material\nsigner, err := signCertWithPrivateKey(privateKey, cert)\nif err != nil {\n    // Log a generic message — DO NOT include pk or certificate in logs\n    return fmt.Errorf(\"SSH certificate authentication setup failed: %w\", sanitizeErr(err))\n}","preventionTips":["Never log or display private key material — the current error message interpolates pk via %q, which is a security risk.","Validate keys with ssh-keygen -y -f keyfile before using them in Terraform.","Use file() references for keys rather than pasting into variables to avoid newline corruption.","Keep golang.org/x/crypto updated to support modern key algorithms."],"tags":["ssh","crypto","private-key","certificate","security"],"backgroundTag":null,"analyzedSha":"d32a084675427f5ac3f7d2868578ef8b2c1dc525","analyzedAt":"2026-08-11T18:43:52.779Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}