Using a Yubikey to Secure SSH on macOS (Minimalist Version)

SSH is critical in most people’s devops process, be it remote server logins or Git commits. After reading about one too many stories about companies getting hacked that way, I decided to use Yubikeys to store my private SSH keys.

You can either use the PIV- or OpenPGP module for this purpose. I decided to use the former because it’s better integrated and seems to be more reliable. There are a number of guides available online. They all required some tinkering and small adjustment for macOS. So here is my own complete guide.

Install Dependencies

Start by installing two required packages from Homebrew

$ brew install yubico-piv-tool opensc

Next you need to copy the OpenSC PKCS11 driver to a new location, so SSH-Agent can pick it up. By default Homebrew will symlink it, which does not work.

$ rm /usr/local/lib/
$ cp $(brew list opensc | grep lib/ /usr/local/lib/

As a last setup step, add the following line to ~/.ssh/config, so SSH will pick it up when authenticating to a remote server. You can either add it at the top or below a Host block to only apply to that host.

PKCS11Provider /usr/local/lib/

Generate Private Keys and Store on Yubikey

You could generate the private key directly on the Yubikey and it will never leave the key. This is great for security but also means you can’t make a backup or copy it to a second Yubikey as backup. For that reason we will securely generate a private SSH key on a RAM disk and then copy it to two Yubikeys.

Start by creating a RAM disk and going into the mount point

$ diskutil erasevolume HFS+ RAMDisk `hdiutil attach -nomount ram://2048`
$ cd /Volumes/RAMDisk

Next generate a new private RSA key (only this specific format and length is supported) and a public key and certificate in the correct format.

$ ssh-keygen -m PEM -t rsa -b 2048 -o -a 100 -C yubikey -f yubikey
$ ssh-keygen -e -f ./ -m PKCS8 >
$ yubico-piv-tool -a verify-pin -a selfsign-certificate -s 9a -S "/CN=SSH key/" --valid-days=3650 -i -o cert.pem

You should now see four files on your RAM disk. The commands below will copy the private key to a Yubikey and also add the self-signed certificate. The last step is mostly to comply with the PIV standard and not really related to the SSH login we want. You can repeat this step for every additional Yubikey you want to seed with this particular private SSH key.

You can customize the touch- and PIN policy to your linking. The command below requires a touch whenever the key is used.

$ yubico-piv-tool -s 9a --pin-policy=once --touch-policy=always -a import-key -i yubikey
$ yubico-piv-tool -a verify -a import-certificate -s 9a -i cert.pem

Using the Yubikey for SSH Logins

Now you are ready to log in to a remote server using the private SSH key stored on the Yubikey. To test the new setup, add the public key to ~/.ssh/authorized_keys or any other place appropriate for the service you are using. You can view the public key using either of those commands, even after you remove the RAM disk.

$ cat ./  # public key saved on RAM disk
$ ssh-keygen -D /usr/local/lib/  # dump directly from Yubikey

After adding the public key to a test server, log in like this:

$ ssh -v -I /usr/local/lib/

If it works, you will see those lines and the Yubikey will start flashing to signal it’s waiting for a touch.

debug1: Offering public key: /usr/local/lib/ RSA SHA256:aeq9rAsbxxxxxxxFWG4 token agent
debug1: Server accepts key: /usr/local/lib/ RSA SHA256:aeq9rAsbxxxxxxFWG4 token agent

For convenience, you can link your hardware key with SSH-agent to avoid entering the PIN all the time. The first command will load the key, the second one will unload it. This will even survive prolonged hibernation. If someone removes the key or restarts the machine, a PIN will be required.

$ ssh-add -s /usr/local/lib/  # add key
$ ssh-add -e /usr/local/lib/  # remove key
$ ssh-add -L  # list available keys with public key

Now you should be ready to use the new, secure SSH key in production. Be sure to keep a backup on a second Yubikey in a save place and unmount the RAM disk after validating it works.

Here some usage ideas. You can use the key in any place that uses SSH.

  • SSH login to important production servers
  • Secure SSH proxy to a bastion inside a private network
  • Secure backups with You could set all server-keys as append-only and use the Yubikey for full access for pruning.
  • Login to a Git code repo. Be sure to use SSH, not HTTPS.


Recognize Emails with Spoofed Sender

Recently I noticed a rise in spam emails with spoofed From-headers. This is unusual because almost all mail servers will require users to log in before sending emails. Below a typical example, which was already flagged as spam.

As you can see, the sender- and recipient addresses are the same in Apple Mail. The content implies that the user’s mailbox was hacked.

When checking the server logs, I quickly noticed that the email was actually sent from a different sender-address than the one shown here. to be precise. But why won’t this address show up as From address?

Turns out that it’s possible to pass any From header in the email body, even though the SMTP MAIL FROM said something different. While this is surely suspicious, it’s common practice for email services, like Amazon SES or Sendgrid.

If you wish to know the actual sender, you need to look at the Return-Path field also known as envelope sender or bounce address. It will have the actual sender address that was used for authentication. In my opinion it should also be displayed in the email client, if different from the From header. Below an example header with the relevant parts in bold.

Received: from ( [])
	by (Postfix) with ESMTPS id DADE6180385A
	for ; Sat, 23 Mar 2019 13:37:34 +0800 (+08)
Received: from [] (unknown [])
	(using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits))
	(Client did not present a certificate)
	by (Postfix) with ESMTPSA id 33DBFF30D6DB
	for manu@***; Sat, 23 Mar 2019 14:37:13 +0900 (JST)
To: manu@***
X-aid: 0776676839
List-ID: 10f2hkdwzncc5z0xhusfi99g.iud3kqvly5b6il5czck95ezocwxr8kf5cdj
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:15.0) Gecko/20120824
Date: Sat, 23 Mar 2019 06:37:16 +0100
Feedback-ID: 58286146:06631151.375932:us93:ve
Subject: ***** SPAM 46.3 ***** manu
From: manu@***

Now, what can you do to prevent this kind of spamming? Spamassassin already has a rule called HEADER_FROM_DIFFERENT_DOMAINS that will trigger in those cases. Sadly, you can’t give a high score to this rule, since legitimate services are sending for different domains all the time.

So the only option left is to educate your users about the Return-Path header and tell them to look at it when it’s an important email.

Update all macOS Apps with a Single Command

Updates are important. While not as great as on Linux, you can use different package managers to manage all installed software and easily keep applications updated. Here the command I use. Requires homebrew, homebrew-cask-upgrade and mas.

function update-all {
    # update cli homebrew
    brew update
    brew upgrade
    brew prune
    brew cleanup

    # Homebrew cask (via
    brew cu -a -y --cleanup
    brew cleanup --force -s && rm -rf $(brew --cache)

    # Node
    npm update -g

    # Apple App store
    mas upgrade
    softwareupdate --install --all


Find sites vulnerable to WordPress Content Injection Vulnerability

WordPress’ update cycle is reaching the speed of Windows XP. Even Google is sending out warnings, urging site owners to update.  For me they were not accurate, but there are still many vulnerable sites out there.

One could – for example – use Nerdydata to search the internet’s source code for vulnerable WP versions. A simple search across their “Popular sites” dataset reveals close to 300 matches.

Regex used: ver=4.7(\.1)?’ 

Using the same trick, you could also identify vulnerable WP installs you are managing. Here a GIST to a short Python script.

Optimize Spamassassin Detection Rate

Email is a terrible way to communicate and should be avoided where possible. Unfortunately it is also the lowest common denominator on the web and will continue to be for the near future.

In the early days of the internet it was easy to run your own mailserver. Due to the absurd quantity of spam this task got increasingly harder and many tech-savvy people gave up and switched to Gmail or other services. This is a pity because a decentralized email infrastructure is harder to surveil, subpoena or shut down. I encourage everyone to run their own mail service if possible.

In this guide I will summarize the steps needed to get an effective spamassassin (SA) setup. Continue reading

SSLv3 no longer supported

I had SSLv3 disabled for HTTP for quite some time. In the light of recent event, it is now also disabled for IMAP and SMTP. If you run into any trouble, let us know or update your clients.

Case study: Howard Johnson Hotel – how NOT to sell your Wifi

nessus-1Few things are more annoying than hotels, who think they need to earn some extra cash by charging people for wireless internet. Given the low to nonexistent cost of providing the service, they should also charge for warm water or fresh air, when following the same logic.

In the case of a local Howard-Johnson (HoJo) hotel, the wifi’s paywall was so badly implemented, it actually posed a threat to the rest of the hotel. Below, I will describe 4 options, anyone can use to get free internet at this particular hotel. Continue reading

Setting Postfix to encrypt all traffic when talking to other mailservers

Update Aug 9, 2013: The biggest German email providers are currently running a big marketing campaign and promise secure email. They are using the same technique described on this page. After checking my logs, I can confirm that GMX-emails were delivered unencrypted on Aug 5, but arrived encrypted on Aug 6.

Thanks to Mr. Snowden, we know two important facts about the world of security and email:

First, most governments in the world will eavesdrop and store your communication, if they get the chance. They don’t have a specific reason and the benefits are highly disputed.

Second, your users can’t/won’t use PGP or S/MIME to encrypt their email.

The job is left to admins. We need to maximize usability and compatibility, while ensuring that user data stays confidential. If you are running Postfix, I’d like to draw your attention to some useful settings that will protect your user’s email in transit. If emails stay on the same server or the other server is secured as well, there is little chance to intercept messages on a big scale. If your users are sending emails to Gmail or Hotmail, then interception is still possible at the receiving end.

Make Postfix encrypt messages at all stages of delivery.
Figure 1: Vulnerability of email-messages in transit.

Continue reading

UPC Vienna Wifi-Passwords easily broken with GPUs

With a majority of households using wireless access points (AP) for internet, secure Wifi has become a key consideration in people’s digital lifes. The outdated encryption standard WEP, which is rarely used any more can be broken in less than 10 minutes. For the newer WPA no direct attack exists so far. The only way is to try a large number of passwords.

Recent advances in parallel computing and graphic cards (GPU) have drastically shifted the odds in favour of potential attackers. With an ordinary CPU, found in most desktop computers and laptops, about 500 Pairwise Master Key (PMK) can be evaluated each second. Modern GPUs can calculate up to 80 000 PMKs per second.

Such an increase in speed doesn’t mean that each and every wireless network is at risk, it only shifts the vulnerable keylength further up. Consider this: At 500 PMKs per second it would take approximately 3 years to break a 6-digit password that uses upper- and lower letters, as well as digits. When using a high-end GPU, 3 years become 5.5 days.

Since password complexity increases in an exponential fashion, slightly longer passwords offer a radically better protection. A 8-letter password from the same number space would take 50 years to crack, even on a high-end GPU.

As we can see, faster cracking techniques only affect password length at the margin, if done right. At UPC Vienna, in Austria this principle is not as well understood. The routers that come bundeld with their broadband connection use an 8-letter password that only seems to consist of capital letters. (I only have access to a limited sample at this time.)

When doing the same calculations as before, such a password took about 7 years to crack, when done on a standard CPU. This would be a huge effort, just to steal your neighbors internet. If we throm in GPUs, the story changes. 7 years become about 16 days on a single workstation. This timespan is quite doable. When performing the calculations in Amazon’s EC2 cloud each UPC-password can be cracked at the cost of about 180 EUR in 3-4 hours. This number is expeced to come down in the following months, due to an increased interest in the topic.

Since it’s not feasible for UPC to change hundreds of thousands of passwords in Vienna (and maybe the whole of Austria), consumers need to act for themselves and pick a more secure key for their AP.


Secure backups in an untrusted cloud with duplicity

These days it’s quite easy to get incredible deals on shared hosting space. Mostly with dodgy providers in the US or former cyberlockers (like filesonic). They don’t give you SSH access, but only FTP. Rsync doesn’t work with FTP, but duplicity does. While not as robust and fast as rsync, it’s built to work with any service that can store files. Examples include any FTP-server, Amazon S3 and SFTP.

Many tutorials suggest using a GnuPG-key for asymetric encryption, but for only protecting your content from the hosting provider, a simple symetric password should work just as well and makes things easier. So a simple working example would be:

[cc lang=”bash” width=”100%” noborder=”1″ theme=”dawn”]

export PASSPHRASE=xxx
export FTP_PASSWORD=xxx
export TARGET=

duplicity -v8 \
–exclude $HOME/’Library/Logs’ \
–exclude-regexp ‘\.DS_Store’ \
–exclude-regexp ‘^~\$.*’ \

This works well on both Linux and Mac OSX machines. Extended options can be found on the duplicity man pages.