Loading an OpenSSH Hostkey From a Hardware Token on FreeBSD

I had a requirement for creating an sftp server that needs strong client and host authentication.  The host needs to know its an authorized client connection, and the client needs to know its really the host its connecting to. SSH and public key crypto is great for this, but what if someone steals a copy of your private key ? What if someone breaks into your host and makes off with your hostkey ?  Until you detect the compromise and revoke and regenerate keys, you run the risk of a man in the middle attack among other things.
One way to mitigate this risk is by keeping your private keys on a hardware token on both sides.

Test setup: FreeBSD RELENG 10, Aladin eToken 64k (old Style with pkcs15 support). From the ports, OpenCT, OpenSC.  I built them from the ports as I wanted OpenSC to use OpenCT as the driver to interact with the Safenet eToken.

Lets start by erasing the token and setting up a pkcs15 filesystem.  Note, you might need to initialize the eToken on a Windows box to start from scratch.  Dont use these PINs in production. They are there just as an example!

0{sftp}# pkcs15-init -E
Using reader with a card: Aladdin eToken PRO 64k
0{sftp}# pkcs15-init -C -P --pin 12345678 --puk 999999 -a 01 --label "server1" --so-pin 12345678 --so-puk 999999 -T
Using reader with a card: Aladdin eToken PRO 64k
0{sftp}# 

Now lets generate the actual private and public key on the token itself. There are two ways you can do this. You can either generate the key off the token and then import it, or you can ask the token to generate it on its own hardware. I think there are caveats to both approaches. If your token dies a hardware death, or lets say a malicious employee or hacker decides to lock the token by too many bad guesses, you are SOL and will need to generate a new key, and then have the entailing fallout from that. Also, how good is the crypto on the token ? Everyone loves to beat up OpenSSL, but it is well vetted, and the RND in the *BSD world is very well vetted and understood. Can the same be said for the software on the token ? I am not sure either way.

0{sftp}# pkcs15-init -G rsa/2048 -a 01 --pin 12345678 --so-pin 12345678 -u decrypt,sign                   
Using reader with a card: Aladdin eToken PRO 64k
0{sftp}# 

We now have an RSA pair of keys on the token– private and public. Lets read the actual public key in an ssh friendly format.

0{sftp}# pkcs15-tool -k
Using reader with a card: Aladdin eToken PRO 64k
Private RSA Key [Private Key]
        Object Flags   : [0x3], private, modifiable
        Usage          : [0x2E], decrypt, sign, signRecover, unwrap
        Access Flags   : [0x1D], sensitive, alwaysSensitive, neverExtract, local
        ModLength      : 2048
        Key ref        : 16 (0x10)
        Native         : yes
        Path           : 3f005015
        Auth ID        : 01
        ID             : b146eef6387d12dd3431c758666e18785235bb7b
        MD:guid        : {cf3b339c-1ad9-b7f4-75a8-c530137c8751}
          :cmap flags  : 0x0
          :sign        : 0
          :key-exchange: 0

0{sftp}# pkcs15-tool --read-ssh-key b146eef6387d12dd3431c758666e18785235bb7b
Using reader with a card: Aladdin eToken PRO 64k
ssh-rsa AAAAB3NzaC1yc2EAAAAFAL4a91UAAAEBAId3Qzp2kfa8CEcP7x4ooCPw99szSfJIT6MnRNYLK2KUP/TTuMY6qi6Y2KKSaKyDHpJj6BDPLQ4i+z535+N+iZ/9Vw9sJv70brmBGkNLq2CsRBENCJeMVapcG5hbCrnVsn/GiEgdSZzF9mxC4o9v+d2ScbEwKsr1X5FDCcMyWUrwM3ioggQHK4eqB3Wv0WBFo8oNYHqymXiGs5WQ9bF4Mlvpvwbk2mzQUbEtX1xaCK2ehpgtpfTyQVTfVTKfh+eAPGZSmO6DnpITFHt3EE2JLw/Ar+7ERXmbHToG1A7/cIMhGMfdVaTvgnWbtnTA74cnqojddNVGrZoGS5I9VmR/5a0=
0{sftp}# 

Lets now use that key for the server. To setup our sftp server, I will create a separate instance listening on port 26. We use the stock OpenSSH config for now. We copy over all the default configs as well as the pre-existing ssh keys. Make the following changes to the config you copied over

0{sftp}# cp -pR /etc/ssh /etc/ssh-26
0{sftp}# diff -u ../ssh/sshd_config sshd_config
--- ../ssh/sshd_config  2015-05-15 18:32:43.945683898 -0400
+++ sshd_config 2015-05-20 09:25:30.834911943 -0400
@@ -14,7 +14,7 @@
 # Note that some of FreeBSD's defaults differ from OpenBSD's, and
 # FreeBSD has a few additional options.
 
-#Port 22
+Port 26
 #AddressFamily any
 #ListenAddress 0.0.0.0
 #ListenAddress ::
@@ -25,10 +25,11 @@
 # HostKey for protocol version 1
 #HostKey /etc/ssh/ssh_host_key
 # HostKeys for protocol version 2
-#HostKey /etc/ssh/ssh_host_rsa_key
+HostKey /etc/ssh-26/ssh_host_rsa-from-agent.pub
 #HostKey /etc/ssh/ssh_host_dsa_key
 #HostKey /etc/ssh/ssh_host_ecdsa_key
 #HostKey /etc/ssh/ssh_host_ed25519_key
+HostKeyAgent /root/etoken-agent
 
 # Lifetime and size of ephemeral version 1 server key
 #KeyRegenerationInterval 1h
@@ -55,6 +56,7 @@
 
 # The default is to check both .ssh/authorized_keys and .ssh/authorized_keys2
 #AuthorizedKeysFile .ssh/authorized_keys .ssh/authorized_keys2
+AuthorizedKeysFile /etc/ssh-26/authorized_keys/%u
 
 #AuthorizedPrincipalsFile none
 
@@ -146,3 +148,10 @@
 #      AllowTcpForwarding no
 #      PermitTTY no
 #      ForceCommand cvs server
+
+Match Group sftponly
+   ChrootDirectory %h
+   ForceCommand internal-sftp
+   AllowTcpForwarding no
+   PermitTunnel no
+   X11Forwarding no
1{sftp}#  

Lets now create the public_key for the server

0{sftp}# pkcs15-tool --read-ssh-key b146eef6387d12dd3431c758666e18785235bb7b > /etc/ssh-26/ssh_host_rsa-from-agent.pub
Using reader with a card: Aladdin eToken PRO 64k
0{sftp}# chmod 600 /etc/ssh-26/ssh_host_rsa-from-agent.pub 

When the server sees that its just the public key and not the private key, the daemon will look to the defined agent socket to do all the necessary private key transformations. So pick your socket location in a place on your server that only root has access to.
Next, we fire up the agent with the socket that the server expects to communicate with. We then add to the agent via the pkcs#11 interface, the path that will let the private key do its magic on the token.

0{sftp}# ssh-agent -a /root/etoken-agent
setenv SSH_AUTH_SOCK /root/etoken-agent;
setenv SSH_AGENT_PID 25563;
echo Agent pid 25563;
0{sftp}# setenv SSH_AUTH_SOCK /root/etoken-agent;
0{sftp}# ssh-add -s /usr/local/lib/opensc-pkcs11.so
Enter passphrase for PKCS#11: 
Card added: /usr/local/lib/opensc-pkcs11.so
0{sftp}# 

We are now ready to start up the server. Initially, try and do it via debug mode

0{sftp}# /usr/sbin/sshd -d -f /etc/ssh-26/sshd_config
debug1: HPN Buffer Size: 65536
debug1: sshd version OpenSSH_6.6.1p1_hpn13v11 FreeBSD-20140420, OpenSSL 1.0.1m-freebsd 19 Mar 2015
debug1: key_parse_private2: missing begin marker
debug1: key_parse_private_pem: PEM_read_PrivateKey failed
debug1: read PEM private key done: type 
debug1: will rely on agent for hostkey /etc/ssh-26/ssh_host_rsa-from-agent.pub
debug1: private host key: #0 type 1 RSA
debug1: rexec_argv[0]='/usr/sbin/sshd'
debug1: rexec_argv[1]='-d'
debug1: rexec_argv[2]='-f'
debug1: rexec_argv[3]='/etc/ssh-26/sshd_config'
debug1: Bind to port 26 on ::.
debug1: Server TCP RWIN socket size: 65536
debug1: HPN Buffer Size: 65536
Server listening on :: port 26.
debug1: Bind to port 26 on 0.0.0.0.
debug1: Server TCP RWIN socket size: 65536
debug1: HPN Buffer Size: 65536
Server listening on 0.0.0.0 port 26.

In another session, lets just do a keyscan to see what the server serves up and see that it indeed matches the public key that we know

% ssh-keyscan -t rsa  -p 26 localhost  
# localhost SSH-2.0-OpenSSH_6.6.1_hpn13v11 FreeBSD-20140420
localhost ssh-rsa AAAAB3NzaC1yc2EAAAAFAL4a91UAAAEBAId3Qzp2kfa8CEcP7x4ooCPw99szSfJIT6MnRNYLK2KUP/TTuMY6qi6Y2KKSaKyDHpJj6BDPLQ4i+z535+N+iZ/9Vw9sJv70brmBGkNLq2CsRBENCJeMVapcG5hbCrnVsn/GiEgdSZzF9mxC4o9v+d2ScbEwKsr1X5FDCcMyWUrwM3ioggQHK4eqB3Wv0WBFo8oNYHqymXiGs5WQ9bF4Mlvpvwbk2mzQUbEtX1xaCK2ehpgtpfTyQVTfVTKfh+eAPGZSmO6DnpITFHt3EE2JLw/Ar+7ERXmbHToG1A7/cIMhGMfdVaTvgnWbtnTA74cnqojddNVGrZoGS5I9VmR/5a0=

Lets create the user now to ssh in. In production do the same pkcs15 key generation on the client’s hardware token. But for this example, we will use a traditional ssh key file

0{sftp}# pw groupadd sftponly
0{sftp}# pw useradd testuser1 -g sftponly -m
0{sftp}# chown root /home/testuser1
0{sftp}# mkdir /home/testuser1/files
0{sftp}# chown testuser1 /home/testuser1/files
0{sftp}# chflags schg /home/testuser1
0{sftp}# 

We create the user and add them to the sftp only group. We ask the user for their public key, and we place it in the directory /etc/ssh-26/authorized_keys directory.

$ sftp -P 26 192.168.1.1
The authenticity of host '[192.168.1.1]:26 ([192.168.1.1]:26)' can't be established.
RSA key fingerprint is 58:59:a9:09:3c:c5:92:91:60:dc:d9:f5:0d:d7:92:95.
No matching host key fingerprint found in DNS.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '[192.168.1.1]:26' (RSA) to the list of known hosts.
Enter passphrase for key '/home/testuser1/.ssh/id_rsa': 
Connected to 192.168.1.1.
sftp> dir
files  
sftp> pwd
Remote working directory: /
sftp> 

On the server, we check

0{sftp}# ssh-keygen -lf ssh_host_rsa-from-agent.pub 
2048 58:59:a9:09:3c:c5:92:91:60:dc:d9:f5:0d:d7:92:95 ssh_host_rsa-from-agent.pub (RSA)
0{sftp}# 
This entry was posted in freebsd and tagged , , , , , , , . Bookmark the permalink.

Leave a Reply