hashicorp/terraform · error
Failed to read ssh private key: password protected keys…
Error message
Failed to read ssh private key: password protected keys are not supported. Please decrypt the key prior to use.
What it means
Returned by `readPrivateKey` when the PEM block carries the legacy `Proc-Type: 4,ENCRYPTED` header, indicating the private key is passphrase-protected. Terraform's SSH auth does not support decrypting password-protected keys, so it refuses up front with this guidance to decrypt the key first.
Solutions
- Generate a new key without a passphrase: `ssh-keygen -t ed25519 -N '' -f deploy_key`.
- Decrypt the existing key: `ssh-keygen -p -f id_rsa` (enter old passphrase, leave new empty) or `openssl rsa -in encrypted.key -out plain.key`.
- Store the unencrypted key securely (secret manager) and reference it; never commit it.
Example fix
# decrypt an existing encrypted key before use
# ssh-keygen -p -f ~/.ssh/id_rsa # remove passphrase
# then:
connection { type = "ssh" private_key = file("~/.ssh/id_rsa") } Defensive patterns
Strategy: validation
Validate before calling
// Reject encrypted PEM keys before attempting to use them:
block, _ := pem.Decode([]byte(key))
if block != nil && block.Headers["Proc-Type"] == "4,ENCRYPTED" {
return errors.New("private_key is passphrase-protected; decrypt it first")
} Type guard
// isEncryptedPEM reports whether a PEM private key is passphrase-protected.
func isEncryptedPEM(key string) bool {
block, _ := pem.Decode([]byte(key))
return block != nil && block.Headers["Proc-Type"] == "4,ENCRYPTED"
} Try / catch
if _, err := readPrivateKey(privateKey); err != nil {
if strings.Contains(err.Error(), "password protected keys") {
return errors.New("decrypt the ssh key (ssh-keygen -p) before use")
}
return err
} Prevention
- Generate deployment keys without a passphrase: `ssh-keygen -N ''`.
- Store unencrypted keys in a secrets manager, never in source control.
- Document that passphrase-protected keys are unsupported by the SSH provisioner.
When it happens
Trigger: `pem.Decode` succeeds but `block.Headers["Proc-Type"] == "4,ENCRYPTED"` — the supplied key was generated with a passphrase (e.g. `ssh-keygen -N 'secret' ...` in older PEM format, or a converted OpenSSL key with `-aes256`).
Common situations: Reusing a personal passphrase-protected key for a provisioner; older RSA keys in traditional PEM format that carry DEK-Info/Proc-Type encryption headers.
Related errors
- Failed to read ssh private key: no key found
- connection type ' ' not supported
- Error connecting to bastion
- Error connecting to proxy
- host for provisioner cannot be empty
AI-assisted analysis of hashicorp/terraform@d32a084675 (2026-08-11).
Data as JSON: /api/errors/40e66fdef8928d08.
Report an issue: GitHub.
Appendix: 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 d32a084675)