Add support for SSH certificate-based authentication
Something along the lines of https://carlosbecker.com/posts/golang-ssh-client-certificates/ would be nice.
For others coming here not understanding why their cert-based auth fails, one reason for getting the following error could be that you are authenticating using the private key, but (unlike ssh CLI, which appends -cert.pub to the -i flags value), Pulumi does not try to find the cert automatically:
after 1 failed attempts: ssh: handshake failed: ssh: unable to authenticate, attempted methods [none publickey], no supported methods remain
Hi @brncsk, could you share the code you're using that's causing the error?
but (unlike ssh CLI, which appends -cert.pub to the -i flags value), Pulumi does not try to find the cert automatically: ... attempted methods [none publickey]
From the error message it seems a public key was indeed provided to the server. Are you sure the remote end is configured correctly, and the cert is correct?
Hi @blampe,
From the error message it seems a public key was indeed provided to the server. Are you sure the remote end is configured correctly, and the cert is correct?
Yes, I'm sure. The remote sshd is correctly configured for cert-based auth, and I can authenticate successfully by providing the -i flag to ssh(1) and specifying my keyfile.
The problem is that there's an implicit logic in ssh(1) that, when provided with a private key using the -i flag, also looks for a matching *-cert.pub file in the same directory, and uses that as the cert.
The man page provides more information (look for the section on the -i identity_file flag.)
My understanding is that for cert-based auth, you must provide both the key and the cert to the server, and pulumi-command provides no facility for the latter.
I'm afraid I wasn't able to reproduce this on my machine. I'm running on Darwin 14.7 targeting a panubo/docker-sshd ssh server running in docker. Here's the program I used:
import * as command from "@pulumi/command";
import * as fs from "fs";
const privateKey = fs.readFileSync("id_rsa", "utf8");
const cmd = new command.remote.Command("example", {
create: "echo 'Hello, World!'",
delete: "echo 'Goodbye, World!'",
connection: {
host: "localhost",
port: 2222,
user: "www",
privateKey
}
}, {
})
export const output = cmd.stdout
@mjeffryes Have you set up certificate-based authentication in sshd?
If you have not, this post explains how to do so: https://goteleport.com/blog/how-to-configure-ssh-certificate-based-authentication/
Ah, I see what you mean. I was able to setup the sshd server for certificate auth and reproduce. My steps below for posterity:
- Create user CA
ssh-keygen -t rsa -b 4096 -f user_ca -C user_ca
- Create user-key and sign:
ssh-keygen -f user-key -b 4096 -t rsa
ssh-keygen -s user_ca -I [email protected] -n www -V +1d user-key.pub
- Create configuration file for enabling cert auth
mkdir entrypoint.d
cat 'echo "TrustedUserCAKeys /etc/ssh/user_ca.pub" >>/etc/ssh/sshd_config' > entrypoint.d/enable_cert_auth.sh
chmod a+x entrypoint.d/enable_cert_auth.sh
- Start sshd server in docker
docker run -ti -p 2222:22 \
-v $(pwd)/user_ca.pub:/etc/ssh/user_ca.pub:ro \
-v $(pwd)/entrypoint.d/:/etc/entrypoint.d/ \
-v $(pwd)/keys/:/etc/ssh/keys/ \
-e SSH_USERS="www:49:49"\
quay.io/panubo/sshd:1.9.0
- Verify cert auth with ssh:
ssh www@localhost -p 2222 -i user-key
- Observe failure of cert auth with command in
pulumi up. (Using the program above, but substitutinguser-keyforid_rsato load private key.)
FWIW, I was able to work around this limitation by adding the user-key and cert to the ssh-agent and removing the explicit privateKey field from my connection configuration. (The provider supports specifying an alternate[agentSocketPath](https://www.pulumi.com/registry/packages/command/api-docs/remote/command/#agentsocketpath_nodejs) if you wanted to avoid adding the key to the agent at SSH_AUTH_SOCK.)
Still might be nice if we supported passing the cert with the private key in the config, but hopefully this helps anyone trying to use cert based auth in the meantime.