Last active
January 16, 2024 12:28
-
-
Save ChristopherA/5740eb90e2875832d9c707b49b3b89a7 to your computer and use it in GitHub Desktop.
macOS GitHub, SSH & GPG Setup
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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