{"record":{"id":"40e66fdef8928d08","repo":"hashicorp/terraform","slug":"failed-to-read-ssh-private-key-password-protected","errorCode":null,"errorMessage":"Failed to read ssh private key: password protected keys are\nnot supported. Please decrypt the key prior to use.","messagePattern":"Failed to read ssh private key: password protected keys are\nnot supported\\. Please decrypt the key prior to use\\.","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/communicator/ssh/provisioner.go","lineNumber":429,"sourceCode":"\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\n}\n\nfunc readPrivateKey(pk string) (ssh.AuthMethod, error) {\n\t// We parse the private key on our own first so that we can\n\t// show a nicer error if the private key has a password.\n\tblock, _ := pem.Decode([]byte(pk))\n\tif block == nil {\n\t\treturn nil, errors.New(\"Failed to read ssh private key: no key found\")\n\t}\n\tif block.Headers[\"Proc-Type\"] == \"4,ENCRYPTED\" {\n\t\treturn nil, errors.New(\n\t\t\t\"Failed to read ssh private key: password protected keys are\\n\" +\n\t\t\t\t\"not supported. Please decrypt the key prior to use.\")\n\t}\n\n\tsigner, err := ssh.ParsePrivateKey([]byte(pk))\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"Failed to parse ssh private key: %s\", err)\n\t}\n\n\treturn ssh.PublicKeys(signer), nil\n}\n\nfunc connectToAgent(connInfo *connectionInfo) (*sshAgent, error) {\n\tif !connInfo.Agent {\n\t\t// No agent configured\n\t\treturn nil, nil\n\t}\n","sourceCodeStart":411,"sourceCodeEnd":447,"githubUrl":"https://github.com/hashicorp/terraform/blob/c9def3e214014c1188faabfc4a5bde5095139765/internal/communicator/ssh/provisioner.go#L411-L447","documentation":"Thrown by readPrivateKey() when the decoded PEM block carries the legacy header 'Proc-Type: 4,ENCRYPTED', i.e. the key is passphrase-protected in the traditional OpenSSL PEM format. Terraform's SSH communicator has no way to supply or prompt for a passphrase, so it refuses to load the key.","triggerScenarios":"Using an RSA key generated with `ssh-keygen -t rsa` (old default) or `openssl genrsa -des3` that prompted for and stored a passphrase, then referencing it in a connection block.","commonSituations":"Company security policy mandates passphrase-protected keys; an operator reused a personal passphrase key; keys generated with `openssl` which encrypts by default.","solutions":["Decrypt the key in place: ssh-keygen -p -f ~/.ssh/id_rsa (enter old passphrase, leave new blank).","Re-emit a decrypted copy: openssl rsa -in encrypted.key -out plain.key, then chmod 600 plain.key.","Generate a fresh unencrypted key: ssh-keygen -t ed25519 -f deploy_key -N ''.","Switch to SSH agent forwarding (agent = true) so the passphrase-protected key never reaches Terraform."],"exampleFix":"# decrypt the existing key without exposing the passphrase to Terraform\n# shell:\n#   ssh-keygen -p -f ~/.ssh/id_rsa   # then enter empty new passphrase\n# config unchanged:\nconnection {\n  private_key = file(\"~/.ssh/id_rsa\")\n}","handlingStrategy":"validation","validationCode":"# detect an encrypted PEM header before use\nlocals {\n  key_bytes = file(var.ssh_key_path)\n  encrypted = strcontains(local.key_bytes, \"ENCRYPTED\")\n}\ncheck \"key_not_encrypted\" {\n  assert {\n    condition     = !local.encrypted\n    error_message = \"private key is passphrase-protected; decrypt it first\"\n  }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Standardize on unencrypted deploy keys generated with -N '' (empty passphrase).","For passphrase-protected personal keys, use agent forwarding (agent = true) instead of private_key.","Add a CI lint that rejects keys whose PEM header contains ENCRYPTED or 'Proc-Type'."],"tags":["ssh","encryption","passphrase","connection"],"analyzedSha":"c9def3e214014c1188faabfc4a5bde5095139765","analyzedAt":"2026-08-07T15:39:49.278Z","schemaVersion":2},"datasetVersion":"2026-08-07T20:17:04.800Z"}