Skip to content

Instantly share code, notes, and snippets.

@ChristopherA
Last active January 16, 2024 12:28
Show Gist options
  • Save ChristopherA/5740eb90e2875832d9c707b49b3b89a7 to your computer and use it in GitHub Desktop.
Save ChristopherA/5740eb90e2875832d9c707b49b3b89a7 to your computer and use it in GitHub Desktop.
macOS GitHub, SSH & GPG Setup
#!/usr/bin/env zsh
#===========================================================================
# * INFO
#
# macOS GitHub & GPG Setup - ./macOS_GitHub_SSH_GPG_setup.sh
#
# THIS IS A WORK IN PROGRESS, AND CURRENTLY IS ONLY FOR MACOS MONTEREY
# By Christopher Allen @ChristopherA https://github.com/christophera/
set -u
# VMware detection. Used to set VMware specific preferences.
if [ ! -z "$(echo $osx_hardware_model | grep VMware)" ]; then
osx_hardware_model_is_vm = true
printf "This macOS is running inside of VMware $osx_hardware_model.\n"
# else
# printf "This macOS is NOT running under VMware. ABORT\n"
# exit 1
fi
# What Hardware Model are we executing inside?
OSX_Hardware_Model=$(/usr/sbin/sysctl -n hw.model)
# VMware detection. Used to set VMware specific preferences.
if [ ! -z "$(echo $osx_hardware_model | grep VMware)" ]; then
osx_hardware_model_is_vm = true
# else
# printf "This macOS is NOT running under VMware. ABORT\n"
# exit 1
fi
# Which version and build of macOS are we executing under?
read OSX_Product_Version OSX_Vers_Major OSX_Vers_Minor OSX_Vers_Patch \
<<<$(/usr/bin/sw_vers -productVersion | awk -F. '{print $0 " " $1 " " $2 " " $3}')
OSX_Build_Version="$(/usr/bin/sw_vers -buildVersion)"
# Testing only using macOS Monterey 12 or Ventura 13
if [[ $Vers_Major -lt 12 ]] || [[ $Vers_Major -gt 13 ]] ; then
printf "WARNING: this script not fully tested on $Distro! Currently tested on macOS Monterey 12 and Ventura 13.\n"
#else
# printf "This script is only for macOS Monterey 12 and Ventura 13! EXIT!\n"
# exit 1
fi
OSX_CPU="$(/usr/bin/uname -m)"
# Keep-alive: update existing `sudo` time stamp until script has finished
while true; do
sudo -n true
sleep 60
kill -0 "$$" || exit
done 2>/dev/null &
# Prevent sleeping during script execution, as long as the machine is on AC power
caffeinate -s -w $$ &
# You must first `brew install gh` and do `gh auth login`.
if [[ $(command -v gh) == "" ]]; then
printf "No \`gh\` Installed!\n"
brew install gh
printf "Installed \`gh\`.\n"
fi
if ! (gh auth status --hostname "github.com" >/dev/null 2>&1); then
printf "You are not logged into \`gh\`!\n"
gh auth login
printf "Added.\n"
fi
# Completion of `gh auth login` will result in values like this in `~/.config/gh/hosts.yml``
#
# github.com:
# user: ChristopherA
# oauth_token: <base64?>
# git_protocol: ssh
# Extract values from `~/.config/gh/hosts.yml`
My_GitHub_User_Name=$(
cat ~/.config/gh/hosts.yml |
sed -n "/user/p" |
sed "s/.*user: \(.*\)/\1/"
)
My_GitHub_Oauth_Token=$(
cat ~/.config/gh/hosts.yml |
sed -n "/oauth_token/p" |
sed "s/.*oauth_token: \(.*\)/\1/"
)
My_GitHub_Git_Protocol=$(
cat ~/.config/gh/hosts.yml |
sed -n "/git_protocol/p" |
sed "s/.*git_protocol: \(.*\)/\1/"
)
# Get .gpg public key registered with GitHub (e.g. https://github.com/christophera.gpg)
#
# to test errors
# My_GitHub_User_Name="xyzzy" # valid user, no gpg key
# My_GitHub_User_Name="asldkkfjskdkfj" # no such user
My_GitHub_GPG_Public_Key=$(curl https://github.com/$My_GitHub_User_Name.gpg 2>/dev/null)
if [ -z "$My_GitHub_GPG_Public_Key" ] ||
[ ! -z "$(echo $My_GitHub_GPG_Public_Key | grep 'uploaded')" ] ||
[ ! -z "$(echo $My_GitHub_GPG_Public_Key | grep 'Found')" ]; then
printf "ERROR: Unable to get your GitHub GPG Public Key from https://github.com/$My_GitHub_User_Name.gpg\!\n"
exit
fi
My_GitHub_GPG_Public_Key_Fingerprint=$(
echo $My_GitHub_GPG_Public_Key 2>/dev/null |
gpg --with-colons --import-options show-only --import --fingerprint 2>/dev/null |
awk -F: '$1 == "fpr" {print $10}' |
head -1
)
My_GitHub_GPG_Public_Key_Long_ID=${My_GitHub_GPG_Public_Key_Fingerprint: -16}
echo $My_GitHub_GPG_Public_Key >~/.gnupg/GitHub.$My_GitHub_User_Name.$My_GitHub_GPG_Public_Key_Long_ID.pubkey.gpg
My_GitHub_GPG_Public_Key_File_Path=~/.gnupg/GitHub.${My_GitHub_User_Name}.${My_GitHub_GPG_Public_Key_Long_ID}.pubkey.gpg
My_GitHub_GPG_Private_Key_File_Path=~/.gnupg/GitHub.${My_GitHub_User_Name}.${My_GitHub_GPG_Public_Key_Long_ID}.privkey.gpg
# Get Github User's email from git public key
#
# could also do from file path rather than echo variable:
# cat $My_GitHub_GPG_Public_Key_File_Path |
# gpg --list-packets --textmode |
# sed -n -E 's/^:user ID packet: "(.*)"$/\1/p'
My_GitHub_GPG_User_Name=$(
echo $My_GitHub_GPG_Public_Key |
gpg --list-packets --textmode |
sed -n -E 's/^:user ID packet: "(.*)"$/\1/p' |
awk -F'<|>' '{print $1}'
)
My_GitHub_GPG_User_Email=$(
echo $My_GitHub_GPG_Public_Key |
gpg --list-packets --textmode |
sed -n -E 's/^:user ID packet: "(.*)"$/\1/p' |
awk -F'<|>' '{print $2}'
# alternatively
# grep -EiEio '\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}\b'
)
# Set Git Config Preferences
# user.name
printf "My_GitHub_GPG_User_Name = $My_GitHub_GPG_User_Name\n"
git config --global user.name "$My_GitHub_GPG_User_Name"
# user.email
printf "My_GitHub_GPG_User_Email = $My_GitHub_GPG_User_Email\n"
git config --global user.email "$My_GitHub_GPG_User_Email"
# Set Github Config Preferences
# github.user
printf "My_GitHub_User_Name = $My_GitHub_User_Name\n"
git config --global github.user "$My_GitHub_User_Name"
# github.tokentype
printf "My_GitHub_Git_Protocol = $My_GitHub_Git_Protocol\n"
git config --global github.tokentype "$My_GitHub_Git_Protocol"
# github.token
printf "My_GitHub_Oauth_Token = $My_GitHub_Oauth_Token\n"
git config --global github.token "$My_GitHub_Oauth_Token"
# set GPG Config Preferences
git config gpg.program $(brew --prefix)/bin/gpg
git config --global user.signingkey $My_GitHub_GPG_Public_Key_Long_ID
git config commit.gpgsign true
git config --global credential.helper osxkeychain
# Set owner & permissions for ~/.ssh
# TODO: test if owner is correct first before doing `sudo`
sudo chown -R $(whoami):admin ~/.ssh/
find ~/.ssh -type d -exec sudo chmod 700 {} \;
find ~/.ssh -type f -exec sudo chmod 600 {} \;
sudo chmod -R 644 ~/.ssh/*.pub
# Set owner and permissions for ~/.gnupg
# TODO: test if owner is correct first before doing `sudo`
sudo chown -R $(whoami):admin ~/.gnupg/
find ~/.gnupg -type d -exec sudo chmod 700 {} \;
find ~/.gnupg -type f -exec sudo chmod 600 {} \;
# delete any old github credential from keychain
printf "protocol=https\\nhost=github.com\\n" | git credential-osxkeychain erase
# add new oauth credential token to keychain
printf "protocol=https\\nhost=github.com\\nusername=%s\\npassword=%s\\n" \
"$My_GitHub_User_Name" "$My_GitHub_Oauth_Token" |
git credential-osxkeychain store
# TODO: wrap this in tests to see if private key exists and is default
gpg --import $My_GitHub_GPG_Private_Key_File_Path
## Ultimate Trust for master key (from https://stackoverflow.com/questions/13116457/how-to-make-auto-trust-gpg-public-key)
(
echo trust &
echo 5 &
echo y &
echo quit
) | gpg --command-fd 0 --edit-key $My_GitHub_GPG_Public_Key_Long_ID
# Setup gpg.conf and gpg-agent.conf configuration
echo "keyid-format long" >>~/.gnupg/gpg.conf
echo "pinentry-program $(brew --prefix)/bin/pinentry-mac" >>~/.gnupg/gpg-agent.conf
killall gpg-agent
# Run gpg-agent in daemon mode
gpg-agent --daemon
gpgconf --kill gpg-agent
export GPG_TTY=$(tty)
# Set Misc. Other Git Config Preferences
git config --global init.defaultBranch main
# * TEST
echo "test" | gpg --clearsign
# TODO - To Investigate:
# * Store GPG secret key in macOS keychain?
# OSX_Local_Keychain="login.keychain"
# OSX_Current_User=$( /usr/bin/stat -f "%Su" /dev/console )
# OSX_Keychain_Secret_ID="org.gngpg.secretkey"
# # OSX_Keychain_Secret_Value is the secret associate with the id
#
# ## SET SECRET FROM OSXKEYCHAIN
# Result="$(security add-generic-password -D secret -U -a $OSX_Current_User -s "$OSX_Keychain_Secret_ID" -w "$OSX_Keychain_Secret_Value" $OSX_Local_Keychain)"
# echo $Result
## TODO: We need to do some better error handling here.
#
# ## GET
# OSX_Keychain_Secret_Value="$(security find-generic-password -a $OSX_Current_User -s "$OSX_Keychain_Secret_ID" -w $OSX_Local_Keychain)"
# ## TODO: We need to do some better error handling here. For instance, if result is
# ## "security: SecKeychainSearchCopyNext: The specified item could not be found in the keychain."
# ## then we need to return this error so the function calling check and do the correct thing.
# ## in particular, in `profile check` the `Desired_Host_Name=` will be empty on first use as
# ## "$Device_Serial_Number.hostname" has not been set yet.
#
# ## DELETE SECRET FROM OSXKEYCHAIN
# Result="$(security delete-generic-password -a $OSX_Current_User -s "$OSX_Keychain_Secret_ID" $OSX_Local_Keychain)"
# echo $Result
## TODO: We need to do some better error handling here.
# * Leverage GPG password on macOS Keychain
# https://www.funtoo.org/Keychain
# * Other credentials storage options
# https://git-scm.com/book/en/v2/Git-Tools-Credential-Storage
# * is this real? or use credential.helper?
# git config --global credential.credentialStore keychain
# * cache timeout
# git config --global credential.helper 'cache --timeout=3600'
# * leveraging osxkeychain
# https://jeanklaas.com/blog/use_git_credential_store_instead_of_keychain/
# http://www.macfreek.nl/memory/Git_Passwords_in_the_Keychain
# https://stackoverflow.com/questions/53419660/how-to-add-credentials-from-the-command-line-using-git-credential-osxkeychain-s
# * erase the keychain entry
# https://stackoverflow.com/questions/11067818/how-do-you-reset-the-stored-credentials-in-git-credential-osxkeychain
# https://docs.github.com/en/get-started/getting-started-with-git/updating-credentials-from-the-macos-keychain
# git credential-osxkeychain erase
# host=github.com
# protocol=https
# https://gist.github.com/jgunnink/00714353f09855220cc54c0132f88cd9
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment