Saturday, January 27, 2018

Can I cache git credentials on a per-project basis?

Leave a Comment

Several people are working on several projects on a single webserver via a network share. Each project has their own git repository. When starting a project, we have a personal development environment per developer working on the project and a staging environment for each project. All files are owned by www-data, because this is the user that Apache uses.

To prevent us from having to type our username and password several times when pulling, pushing and switching to a new branch, we are currently using the credential cache (as found here).

$ git config --global credential.helper cache --timeout=900 

The problem we are facing is that when someone (user 1) performs an authenticated git action, they enter their credentials. Within the timeout, someone else (user 2) performs an authenticated git action in their own repository, which uses the credentials of user 1. This will cause one of two things to happen:

  • User 2 gets an error that the repository does not exist. This is because user 1 does not have the rights to perform actions on the repository of user 2.
  • User 2 pushes a commit (with them as author), using the account of user 1. The push shows up in the history of user 1.

I think this issue can be partly mitigated by adding the username to the git repository url (e.g. username@git.domain.ext/repo/name.git), but this only works in the beginning stages where we have personal development environments per user. The staging environment needs to be accessed by multiple people, so we cant hardcode the username. After we have done initial development and the project has gone live, we clean up development environments, because we don't have infinite space. If we need to make changes after we have cleaned up personal development environments, we usually use the staging environment to do so, which would cause the same issue to happen.

The git config --global credential.helper command causes the credentials to be stored server-wide. Lowering the timeout only helps so much. Can we cache credentials per development environment instead?

4 Answers

Answers 1

I couldn't find an option to match exactly what you're after, so I wrote one: a git credential helper that stores credentials on a per-shell basis.

It sets up a temporary encryption key and a temporary directory for data on login, uses them to store the username and password given by git in the store phase of the credential helper process, then gives them back in the get phase.

Caveats:

  1. I've tested it but only in a fairly limited way. It works: two separate login shells for the same user can have separate cached credentials.
  2. It's fairly naive: it ignores any username and repo URL given by git when storing and retrieving credentials.
  3. It will leave behind a temp directory with a few small files for every login.
  4. It stores credentials encrypted, but I make no claims about how secure this is in practice.
  5. It requires Python 3.6, pycrypto, and bash; I've tested it on linux and macOS. It should be fairly easy to adapt for other setups.

Open an issue if you run into any trouble and I'll see what I can do.

Answers 2

One mitigating solution I have seen in that kind of shared environment is to have a wrapper shell script named 'git' which will:

  • overshadow the actual git command
  • be launched by a dedicated account (through sudo -u xxx)
  • read credentials from a temp file named after the ps id (file owned by xxx, not readable by the original user)
  • if that temp file does not exist (on the first git call), creates it and store username/password.
  • use that "store" helper for pull/push/clone commands, with a command-local config /usr/bin/git -c credential.helper 'store --file=/path/to/temp/credentials' ..., again executed by the wrapper as xxx.

That same script can not only ask for credential, but also ask for the authorname/email, and add that to its git command, settings local GIT_AUTHOR_NAME/EMAIL for each /usr/bin/git calls.

The idea is to do git command with local configs (local to the git command itself)

Answers 3

Here are two possible solutions to your problem:

1 - have one OS user per developer.

This is the cleaner option: Each of the developers would connect to a different user account. Add these users to the group www-data and set the file options accordingly: allow read and write by the group members.

Each users's authentication will then be managed independently.

2 - store the configuration per project

Currently, the --global storage of user credentials is done at OS user level. Instead, you may use --local, in order to store the credentials at project level.

This will solve the described problem between users 1 and 2. However, you still may lack some traceability working this way: in case two developers work on the same project, if user2 pushes some changes shortly after user1, user1's authentication details will be used, and it will look like she is the person who did the push - whereas it's user2.

Read git config --help for detailed information about the --local option and data storage locations.

Answers 4

Stop using the network share. Have users clone the repository on their own machines and do changes that way. No files should be changed directly in staging environment, not even through network shares.

Users can have local Apache installations if they want to see their changes immediately and experiment based on that.

In addition, you could consider hosting your repositories in a separate server and do deployments from those repositories to your staging environment using a deployment tool, as git is not really a deployment tool. This could be automated. However, the biggest thing you should do is to stop using the network share and use git push to initiate the changes instead of changing files on the server yourself.

If You Enjoyed This, Take 5 Seconds To Share It

0 comments:

Post a Comment