hashicorp/terraform · error
Failed to read ssh private key: password protected keys are
Error message
Failed to read ssh private key: password protected keys are not supported. Please decrypt the key prior to use.
What it means
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.
Source
Thrown at internal/communicator/ssh/provisioner.go:429
}
ucertSigner, err := ssh.NewCertSigner(pcert.(*ssh.Certificate), usigner)
if err != nil {
return nil, fmt.Errorf("failed to create cert signer %q: %s", usigner, err)
}
return ssh.PublicKeys(ucertSigner), nil
}
func readPrivateKey(pk string) (ssh.AuthMethod, error) {
// We parse the private key on our own first so that we can
// show a nicer error if the private key has a password.
block, _ := pem.Decode([]byte(pk))
if block == nil {
return nil, errors.New("Failed to read ssh private key: no key found")
}
if block.Headers["Proc-Type"] == "4,ENCRYPTED" {
return nil, errors.New(
"Failed to read ssh private key: password protected keys are\n" +
"not supported. Please decrypt the key prior to use.")
}
signer, err := ssh.ParsePrivateKey([]byte(pk))
if err != nil {
return nil, fmt.Errorf("Failed to parse ssh private key: %s", err)
}
return ssh.PublicKeys(signer), nil
}
func connectToAgent(connInfo *connectionInfo) (*sshAgent, error) {
if !connInfo.Agent {
// No agent configured
return nil, nil
}
View on GitHub (pinned to c9def3e214)
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.
Example fix
# decrypt the existing key without exposing the passphrase to Terraform
# shell:
# ssh-keygen -p -f ~/.ssh/id_rsa # then enter empty new passphrase
# config unchanged:
connection {
private_key = file("~/.ssh/id_rsa")
} Defensive patterns
Strategy: validation
Validate before calling
# detect an encrypted PEM header before use
locals {
key_bytes = file(var.ssh_key_path)
encrypted = strcontains(local.key_bytes, "ENCRYPTED")
}
check "key_not_encrypted" {
assert {
condition = !local.encrypted
error_message = "private key is passphrase-protected; decrypt it first"
}
} Prevention
- 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'.
When it happens
Trigger: 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.
Common situations: Company security policy mandates passphrase-protected keys; an operator reused a personal passphrase key; keys generated with `openssl` which encrypts by default.
Related errors
- ssh client is not connected
- Failed to read ssh private key: no key found
- connection type '%s' not supported
- SSH authentication failed (%s@%s): %w
- SCP failed to start. This usually means that SCP is not prop
AI-assisted analysis of hashicorp/terraform@c9def3e214 (2026-08-07).
Data as JSON: /api/errors/40e66fdef8928d08.
Report an issue: GitHub.