17

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?

Sumurai8
  • 20,333
  • 11
  • 66
  • 100

5 Answers5

10

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.

Nathan Vērzemnieks
  • 5,495
  • 1
  • 11
  • 23
  • I like the concept, +1: more complete than my answer. If this work, that would allow for authentication in a shared environment. – VonC Jan 22 '18 at 09:07
6

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.

Mehdi
  • 7,204
  • 1
  • 32
  • 44
6

Part of the problem could be solved by using git config --global credential.useHttpPath true. It gets rid of the error that the repository does not exist, because we do not have people that have permission to pull a repository, but no rights to push it. Two people working with the same repository in different directories may still accidentally push as the other user instead though.

Eis' answer is solid advice when you work on projects you can easily seed. In my case a lot of the projects are not easily seedable, making it somewhat less ideal of a solution.

Nathan's answer is probably the cleanest solution, and likely to work on a broader set of systems than the following solution.


In my case I have decided to go with an approach inspired by VonC. git-credential-cache allows two parameters, namely the timeout parameter and the socket parameter. We currently use exclusively bash, which exposes the $BASHPID variable that contains the pid of the current shell. We sadly cannot use the variable directly in the config value, but using the suggestion of VonC to shadow the git command we can make an alias instead.

I added an extra line to the ~/.bashrc file of the www-data user and added:

alias git="/usr/bin/git -c credential.helper='cache --timeout=7200 --socket=/var/www/.git-credential-cache/socket_$BASHPID'"

Note: the socket needs to be an absolute path, and the home directory of the www-data user happened to be /var/www/

I have removed the global credential helper config like so:

git config --global --unset credential.helper

It now uses a seperate socket per bash shell. The nice thing of this approach is that it is still using the caching mechanism git is shipped with, which means it probably is not going to be broken anytime soon. The downside is that shadowing a command introduces magic behaviour (there is nothing set in the config, but it is working regardless... how?), which may confuse co-workers in the future.

Sumurai8
  • 20,333
  • 11
  • 66
  • 100
4

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)

VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250
2

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.

eis
  • 51,991
  • 13
  • 150
  • 199
  • 1
    As much as I would want to, I don't think this is realistic for most projects in my case. The staging environments I mentioned are mostly about Wordpress installations, where I would otherwise need to pull from git, manually or automatically copy the database, or make a manual install and enter dummy content myself, just to add a button somewhere in a theme. It would take a disproportional amount of time compared to the actual work. Two other developers also don't have a strong enough computer to run virtual machines either. We do use CI for hybrid projects with actual apis, and that does help – Sumurai8 Jan 20 '18 at 15:10