Activating github two-factor authentication (2FA) offers an indubitable
security boost, with one notable side effect: https
authentication
requires entering a Personal Access Token instead of password, as very
clearly explained in the official
github documentation , which states:
The command line prompt won’t specify that you should enter your personal access token when it asks for your password.
So everything looks like it stays the same, except now I have to enter
a random 32-character long Personal Access Token (PAT), instead of my
former, sensibly memorable, and readily typeable password. But I liked
things the old way! This blog entry describes the process I went
through to effectively restore the previous behaviour of the git prompt
prior to me switching on 2FA on github, enabling me to type a password
for git push
, instead of the un-typeable PAT.
Many – maybe most? – people are likely content with SSH authentication,
which avoids any of these issues, and simply allows your git push
commands to be identified through connecting your local ssh
agent with
github to do the authentication. git push
then just works. My problem
with this is twofold:
I like having to type my name and password. It is impossible for me to type my name and PAT. For a brief moment after having switched on 2FA on github, i feared that i was going to have to constantly copy-paste my PAT for every commit. I didn’t wanna do that, so i did the following … but first a brief digression into my SSL habits.
I use OpenSSL a lot. I encrypt any and all sensitive information, and use a host of local scripts and bash aliases to do so. I wasn’t going to leave my github PAT just lying around on my machine, so it naturally gets encrypted too, simply by storing it as a single line in a text file, and typing:
openssl des3 -salt -md sha256 -pbkdf2 -in gitpat.txt -out gitpat
That command prompts me to enter and repeat a password. See the
OpenSSL manual for what all those flags mean; or just believe me
that they ensure that it’s really encrypted. Delete gitpat.txt
— and
don’t forget any extra files like .gitpat.txt.un~
on linux, or
whatever traces might be left lying around on other operating systems —
and your PAT is secure. Decrypting pretty much just reverses the above:
openssl des3 -salt -md sha256 -pbkdf2 -d -in gitpat -out gitpat.txt
Then i’ve got my token in gitpat.txt
, which i can … copy-and-paste
each time i need to git push
? No way! And so … on to my solution.
My solution involved two main tricks:
git push origin master
, where
origin
can be identified via git remote -v
as something like
https://github.com/mpadge/<repo>
, and which necessitates entering
"mpadge"
and my PAT, with git push https://mpadge:<PAT>@github.com/mpadge/<repo>
, where the PAT is
passed directly to github, circumventing the need to enter it
manually, so that the push is directly sent and accepted; andgit push
into the form above
with my PAT embedded.The second of those steps looks, in the form of a bash
script, like
this:
read -s -p "Enter Password: " PASS
echo ""
openssl des3 -salt -md sha256 -pbkdf2 -d -in /<my>/<secret>/<path>/gitpat -out gitpat.txt -pass pass:$PASS
PASS=""
PAT=$(<aaagit.txt)
rm aaagit.txt
I then have a variable, "PAT"
, containing my PAT, with no other traces
of its value, or of my password, left on my machine. Note that the
password required is whatever was entered for the initial encryption of
gitpat.txt
to gitpat
.
The first step then inserts this PAT, and my github user name, into a
git push
command via the following bash
code, presuming here that my
github user name is stored in a variable named UNAME
:
REMOTE=$(git remote -v | head -n 1)
# REMOTE="origin https://github.com/<org>/<repo> (fetch)" (or similar)
# function to cut string by delimiter
cut () {
local s=$REMOTE$1
while [[ $s ]]; do
array+=( "${s%%"$1"*}" );
s=${s#*"$1"};
done;
}
# cut terminal bit "(fetch)" from remote, returning first part as array[0]:
array=();
cut " "
REMOTE="${array[0]}"
# cut remainder around "github.com", returning 2nd part as "/<org>/<repo>"
array=();
cut "github.com"
# convert REMOTE given above to
# REMOTE="https://<UNAME>:<PAT>@github.com/<org>/<repo>" (or similar)
printf -v REMOTE "https://%s:%s@github.com%s" "$UNAME" "$PAT" "${array[1]}"
echo $REMOTE
That script gives our desired output:
## [1] "https://mpadge:<mypat>@github.com/<org>/<repo>"
My solution then just involved combining those two tricks within a
single script, designed to almost but not quite reflect the old git push
prompt and behaviour i was trying to emulate, and including an
additional option to call the script with an extra parameter specifying
the branch to push to, or otherwise defaulting to the current branch:
#!/bin/bash
read -p "User name for 'https://github.com': " UNAME
read -s -p "Password (NOT PAT) for 'https://$UNAME@github.com' " PASS
echo ""
openssl des3 -salt -md sha256 -pbkdf2 -d -in /<my>/<secret>/<path>/gitpat -out gitpat.txt -pass pass:$PASS
PASS=""
PAT=$(<aaagit.txt)
rm aaagit.txt
# get git branch:
if [ "$1" == "" ]; then
BRANCH=$(git branch --show-current)
else
BRANCH=$1
fi
REMOTE=$(git remote -v | head -n 1)
# REMOTE="origin https://github.com/<org>/<repo> (fetch)" (or similar)
# function to cut string by delimiter
cut () {
local s=$REMOTE$1
while [[ $s ]]; do
array+=( "${s%%"$1"*}" );
s=${s#*"$1"};
done;
}
# cut terminal bit "(fetch)" from remote, returning first part as array[0]:
array=();
cut " "
REMOTE="${array[0]}"
# cut remainder around "github.com", returning 2nd part as "/<org>/<repo>"
array=();
cut "github.com"
# convert REMOTE given above to
# REMOTE="https://<UNAME>:<PAT>@github.com/<org>/<repo>" (or similar)
printf -v REMOTE "https://%s:%s@github.com%s" "$UNAME" "$PAT" "${array[1]}"
git push $REMOTE $BRANCH
# clear variables:
PAT=""
REMOTE=""
I then only needed to set an alias to that script in ~/.bash_aliases
,
along the lines of
alias gitpush="bash /<my>/<secret>/<path>/gitpatscript.bash"
and then replace my former git push
with gitpush
, to enable me to
once again type in my password like i always liked to do.
Copyright © 2019--22 mark padgham