When performing certain operations, sometimes Git asks for credentials. For example, when you clone, pull, fetch, or push a repository over HTTP, Git may ask for the credentials required to authenticate against a remote host like GitLab or GitHub. Or maybe you contribute patches through git-send-email(1) and are prompted for your email credentials. Regardless, no one likes having to type in credentials all the time. Thankfully Git has some options available to improve our quality of life.
Some git-credential(1) background
Git uses the git-credential(1) command as its internal interface for storing and retrieving credentials. Below is an example where Git provides the required protocol and host information on stdin, but prompts the user for a username and password:
$ git credential fill <<EOF
> protocol=https
> host=example.com
> EOF
# The user is prompted for credentials.
Username for 'https://example.com': foo
Password for 'https://foo@example.com':
# All credential info is output to stdout.
protocol=https
host=example.com
username=foo
password=bar
Once the command receives the credentials for the user, the information is formatted and printed on stdout. Git can then easily parse this output to use when needed. This is neat, but still doesn’t answer how Git can retrieve stored credentials for us. This is where the Git credential configuration can help.
One option is to statically configure a username for a given authentication
context in the Git config. The below example shows a username configured for
use when Git needs credentials for https://example.com
:
[credential "https://example.com"]
username = foo
This allows Git to retrieve the username without having to prompt the user for it as seen in the example below:
$ git credential fill <<EOF
> protocol=https
> host=example.com
> EOF
Password for 'https://foo@example.com':
protocol=https
host=example.com
username=foo
password=bar
Credential helpers
To handle more than just usernames, Git has another mechanism called credential helpers. These are external programs specified in the Git config that can retrieve both usernames and passwords. Git includes two built-in helpers that can be configured for use.
- cache - Temporarily caches credentials in memory to reduce repetition. See git-credential-cache(1)
- store - Indefinitely stores credentials on disk unencrypted. See git-credential-store(1)
To use the cache
credential helper for all repositories, run git config --global credential.helper cache
or add the following configuration to your
Git config.
[credential]
helper = cache
Git also allows for the use of custom credential helpers and some may already be installed on your system. This is great if you want to retrieve stored passwords, but are not a fan of leaving them on the disk unencrypted. Check out gitcredentials(7) for more information.
A custom helper for git-send-email(1)
This was alluded to at the beginning, but the reason I was looking into
credential management in Git was to avoid typing my email password when sending
patches with git-send-email(1).
There is the sendemail.smtpPass
option available, but I did not like the idea
of storing my email password in plain text and in my Git config. I also was not
a fan of the store
credential helper for the same reason. In my workflow, I
retrieve my email password with pass(1) and
have integrated it into my terminal email client of choice to receive and send
email. I wanted to create a custom credential helper to reuse the same
approach. At the start, my Git config for git-send-email(1) looked like the
following:
[sendemail]
smtpserver = smtp.example.com
smtpuser = foo@example.com
smtpencryption = tls
smtpserverport = 587
Git already knows the protocol, host, and username so our credential helper only needs to provide the password. Another thing to consider is that the credential helper should be scoped to only my email’s authentication context. This prevents the credential helper from being used for other unrelated Git operations requiring authentication.
As for the actual credential helper, I choose to implement a simple inline
shell script to print the formatted password output. Git knows to execute the
helper as a shell script when prepended with !
. Below is an example config:
; The username and host must be URL encoded. The `@` between is fine.
[credential "smtp://foo%40example.com@smtp.example.com%3a587"]
helper = "!f() { echo password=$(pass foo@example.com); }; f"
With this configuration, I only have to remember my master password which is nice and handy. Custom credential helpers could certainly be developed to tie into various other password managers as well. Overall, there may be better ways to do this, but it was an interesting journey nonetheless.