#!/bin/bash ################################################################################################ # macskeyinstaller.sh # Last Updated: January 13, 2025 # # macOS OpenSSH Client Patcher for Hardware Security Key Support (ED25519-SK With YubiKey Etc.) # Check out https://gist.github.com/BertanT/9d222da115ca2d1274ef34735c4260cf for details! # # Copyright 2025 Mehmet Bertan Tarakcioglu (github.com/BertanT), under the MIT License. ################################################################################################ # Exit early if there is an error set -e printf "\n* Hello! This script compiles and installs the security key provider for the built-in macOS Open SSH client to enable support for hardware security keys (such as a YubiKey)." # Check if the script is running as root if [ "$(id -u)" -eq 0 ]; then printf "\n!!! Error: For safety reasons, this script cannot be run as root. If using sudo, please try again without it.\n" >&2 exit 1 fi # Function to compare macOS version strings version_ge() { [[ "$(echo -e "$1\n$2" | sort -V | head -n1)" == "$2" ]] } # function to get the current macOS Version macos_version=$(sw_vers -productVersion | cut -d '.' -f1,2) # Check if running at least macOS Sonoma if ! version_ge "$macos_version" "14.0"; then printf "\n!!! Error: macOS version is $macos_version. This script requires at least macOS Sonoma (14.0).\n" >&2 exit 1 fi printf "\n* macOS version is supported!" # Check if Homebrew is installed if ! which brew &>/dev/null; then printf "\n!!! Error: This script requires Homebrew to install dependencies! Please install Homebrew and try again\n" >&2 exit 1 fi printf "\n* Homebrew is installed!" printf "\n* Cloning the latest main branch of openssh-portable from GitHub. This may take a while..." git clone --quiet https://github.com/openssh/openssh-portable.git cd openssh-portable printf "\n* Installing dependencies from Homebrew: libfido2, openssl, autoconf, automake, libtool\n" brew install libfido2 openssl autoconf automake libtool pkgconf printf "\n* Generating configuration script." autoreconf -i printf "\n* Exporting flags." # Determine the CPU architecture and set the appropriate Homebrew base path if [[ "$(uname -m)" == "arm64" ]]; then BREW_PREFIX="/opt/homebrew" elif [[ "$(uname -m)" == "x86_64" ]]; then BREW_PREFIX="/usr/local" else echo "!!! Error: Unknown CPU architecture $(uname -m).\n" >&2 exit 1 fi # Get the appropriate paths for the Homebrew dependecies as they differ through version BREW_OPENSSL_PATH=$(ls -d $BREW_PREFIX/Cellar/openssl@3/*) BREW_LIBFIDO2_PATH=$(ls -d $BREW_PREFIX/Cellar/libfido2/*) export CFLAGS="-L$BREW_OPENSSL_PATH/lib -I$BREW_OPENSSL_PATH/include -L$BREW_LIBFIDO2_PATH/lib -I$BREW_LIBFIDO2_PATH/include -Wno-error=implicit-function-declaration" export LDFLAGS="-L$BREW_OPENSSL_PATH/lib -L$BREW_LIBFIDO2_PATH/lib" printf "\n* Configuring build. This may take a while..." ./configure --quiet --with-security-key-standalone printf "\n* Cleaning previous build for good measure." make --quiet clean printf "\n* Building OpenSSH Portable." make --quiet printf "\n* Copying the Security Key Provider library to /usr/local/lib." printf "\n We need root privileges to modify system files.\n" sudo mkdir -p /usr/local/lib sudo mv sk-libfido2.dylib /usr/local/lib/ # Check if the script is running in ZSH. If not, give the user instructions. # Otherwise, modify .zshenv to set the environment variable if not already present. if [[ "$SHELL" == *zsh* ]]; then printf "\n* Configuring the ~/.zshenv for the System SSH use the Security Key Provider we just built" if ! grep -q "export SSH_SK_PROVIDER=/usr/local/lib/sk-libfido2.dylib" "$HOME/.zshenv" &>/dev/null; then echo "export SSH_SK_PROVIDER=/usr/local/lib/sk-libfido2.dylib" >> "$HOME/.zshenv" printf "\n* Added SSH_SK_PROVIDER to ~/.zshenv." else printf "\n* SSH_SK_PROVIDER is already configured in ~/.zshenv." fi else printf "*\n Since you are not on ZSH, you will need to manually configure your shell profile to tell the System SSH client to use the Security Key Provider we just built." printf "\n The shell profile should export the environment variable as follows:" printf "\n export SSH_SK_PROVIDER=/usr/local/lib/sk-libfido2.dylib" fi printf "\n* Exiting the directory and deleting the repository we cloned. We don't need it anymore" cd .. rm -rf openssh-portable printf "\n\n* That's it! After restarting your terminal session, you can plug in your hardware security key and test the installation using:" printf "\n ssh-keygen -t ed25519-sk -O resident -O verify-required -C \"Your Comment\"" printf "\n\nHave a nice day! :)\n\n"