Organization of multi-user access to the GIT server

When installing and configuring a Git server, the question arises of organizing access for several users to several projects. I did some research on the issue and found a solution that meets all my requirements: simple, safe, reliable.

My wishes are:

  • each user connects with their own account
  • Multiple users can work on the same project
  • the same user can work on multiple projects
  • each user has access only to those projects on which he works
  • it should be possible to connect through the command line, and not just through some kind of web interface

It would also be great:

  • grant read-only rights to controlling persons
  • conveniently administer user permissions in Git

Overview of possible options for accessing the GIT server

First of all, you need to know what to choose from, so a brief overview of the Git protocols.

  • ssh - a specially created user account is used to access the server.
    • it's strange that Git doesn't recommend against using one account to access all repositories. This doesn't meet my requirements at all.
    • You can use multiple accounts, but how can you limit a user's access to only certain directories?
      • Closing to the home directory is not suitable, because it is difficult to organize write access for other users there
      • Using symbolic links from the home directory is also tricky because Git doesn't interpret them as links.
      • Restrict access to the interpreter, well, you can, but there is no full guarantee that this will always work
        • You can generally connect your own command interpreter for such users, but,
          • firstly, this is already some kind of difficult decision,
          • and 2, it can be bypassed.

    But maybe it's not a problem that the user will be able to execute any commands? .. In general, this method cannot be ruled out if you figure out how to use it. We will return to this method later, but for now we will briefly consider the remaining alternatives, maybe there will be something simpler.

  • git local protocol can be used in combination with sshfs, multiple users can be used, but it's essentially the same as the previous case
  • http - read only
  • git is read-only
  • https is difficult to install, you need additional software, some kind of control panel to organize user access ... it looks feasible, but somehow everything is complicated.

Using the ssh protocol to organize multi-user access to the Git server

Let's get back to the ssh protocol.

Since ssh access is used for git, the server data needs to be secure. The user who connects via ssh uses their own login on the Linux server, so they can connect via the ssh client and access the server's command line.
There is no complete protection against gaining such access.

But the user should not be interested in Linux files. Meaningful information is stored only in the git repository. Therefore, you can not restrict access through the command line, but by means of Linux, prohibit the user from viewing projects, excluding those in which he participates.
It is obvious to use the Linux permissions system.

As already mentioned, it is possible to use only one account for ssh access. This configuration is unsafe for several users, although it is included in git's list of recommended options.

To implement the requirements given at the beginning of the article, the following directory structure is created with the assignment of rights and owners:

1) project directories

dir1(proj1:proj1,0770)
dir2(proj2:proj2,0770)
dir3(proj3:proj3,0770)
...
where
dir1, dir2, dir3 - project directories: project 1, project 2, project 3.

proj1:proj1, proj2:proj2, proj3:proj3 are specially created Linux users who are assigned as the owners of the respective project directories.

the rights to all directories are set to 0770 - full access for the owner and his group, and a complete ban for everyone else.

2) developer accounts

Разработчик 1: dev1:dev1,proj1,proj2
Разработчик 2: dev2:dev2,proj2,proj3

The key point is that the developers are assigned an additional group of the system user who owns the corresponding project. This is done by the Linux server administrator with one command.

In this example, Developer 1 is working on projects proj1 and proj2, and Developer 2 is working on projects proj2 and proj3.

If any of the Developers connect via ssh via the command line, then his rights will not be enough even to view the contents of the directories of projects in which he does not participate. He cannot change it himself.

Since the basis of this principle is the basic security of Linux rights, this scheme is reliable. In addition, the scheme is very easy to administer.

Let's move on to practice.

Creating Git Repositories on a Linux Server

We check.

[root@server ~]# cd /var/
[root@server var]# useradd gitowner
[root@server var]# mkdir gitservertest
[root@server var]# chown gitowner:gitowner gitservertest
[root@server var]# adduser proj1
[root@server var]# adduser proj2
[root@server var]# adduser proj3
[root@server var]# adduser dev1
[root@server var]# adduser dev2
[root@server var]# passwd dev1
[root@server var]# passwd dev2

tired of typing...

[root@server gitservertest]# sed "s/ /n/g" <<< "proj1 proj2 proj3" | while read u; do mkdir $u; chown $u:$u $u; chmod 0770 $u; done

[root@server gitservertest]# usermod -aG proj1 dev1
[root@server gitservertest]# usermod -aG proj2 dev1
[root@server gitservertest]# usermod -aG proj2 dev2
[root@server gitservertest]# usermod -aG proj3 dev2

We are convinced that it is impossible to access other people's repositories from the command line and even view their contents.

[dev1@server ~]$ cd /var/gitservertest/proj3
-bash: cd: /var/gitservertest/proj3: Permission denied
[dev1@server ~]$ ls /var/gitservertest/proj3
ls: cannot open directory /var/gitservertest/proj3: Permission denied

Collaboration in Git of several developers on one project

One question remains, if one developer introduces a new file, then other developers cannot change it, because he himself is its owner (for example, dev1), and not the user who owns the project (for example, proj1). Since we have a server repository, first of all, we need to know how the “.git” directory is arranged and whether new files are created.

Create a local Git repository and push to a Git server

Let's move on to the client machine.

Microsoft Windows [Version 6.1.7601]
(c) Корпорация Майкрософт (Microsoft Corp.), 2009. Все права защищены.

C:gittest>git init .
Initialized empty Git repository in C:/gittest/.git/

C:gittest>echo "test dev1 to proj2" > test1.txt

C:gittest>git add .

C:gittest>git status
On branch master
No commits yet
Changes to be committed:
  (use "git rm --cached <file>..." to unstage)
        new file:   test1.txt

C:gittest>git commit -am "new test file added"
[master (root-commit) a7ac614] new test file added
 1 file changed, 1 insertion(+)
 create mode 100644 test1.txt
 
C:gittest>git remote add origin "ssh://[email protected]/var/gitservertest/proj2"

C:gittest>git push origin master
dev1:[email protected]'s password:
Counting objects: 3, done.
Writing objects: 100% (3/3), 243 bytes | 243.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To ssh://10.1.1.11/var/gitservertest/proj2
 * [new branch]      master -> master

C:gittest>

At the same time, new files are generated on the server, and they belong to the user who performed the push

[dev1@server proj2]$ tree
.
├── 1.txt
├── branches
├── config
├── description
├── HEAD
├── hooks
│   ├── applypatch-msg.sample
│   ├── commit-msg.sample
│   ├── post-update.sample
│   ├── pre-applypatch.sample
│   ├── pre-commit.sample
│   ├── prepare-commit-msg.sample
│   ├── pre-push.sample
│   ├── pre-rebase.sample
│   └── update.sample
├── info
│   └── exclude
├── objects
│   ├── 75
│   │   └── dcd269e04852ce2f683b9eb41ecd6030c8c841
│   ├── a7
│   │   └── ac6148611e69b9a074f59a80f356e1e0c8be67
│   ├── f0
│   │   └── 82ea1186a491cd063925d0c2c4f1c056e32ac3
│   ├── info
│   └── pack
└── refs
    ├── heads
    │   └── master
    └── tags

12 directories, 18 files
[dev1@server proj2]$ ls -l objects/75/dcd269e04852ce2f683b9eb41ecd6030c8c841
-r--r--r--. 1 dev1 dev1 54 Jun 20 14:34 objects/75/dcd269e04852ce2f683b9eb41ecd6030c8c841
[dev1@server proj2]$

When the changes are uploaded to the Git server, additional files and directories are created and are actually owned by the uploader. But then the group of these files and directories also corresponds to the main group of this user, that is, the dev1 group for the dev1 user and the dev2 group for the dev2 user (changing the developer user's main group will not help, because then how to work on multiple projects?). In this case, the dev2 user will not be able to modify the files created by the dev1 user, and this is fraught with a violation of functionality.

Linux chown - changing the owner of a file by a normal user

The owner of a file cannot change its ownership. But he can change the group of a file that belongs to him, and then this file can be changed by other users who are in the same group. That's what we need.

Using the Git Hook

The working directory for hook is the root directory of the project. hook is an executable that runs under the user who is doing the push. knowing this, we can carry out our plans.

[dev1@server proj2]$ mv hooks/post-update{.sample,}
[dev1@server proj2]$ sed -i '2,$ s/^/#/' hooks/post-update
[dev1@server proj2]$ cat <<< 'find . -group $(whoami) -exec chgrp proj2 '"'"'{}'"'"' ;' >> hooks/post-update

either just

vi hooks/post-update

Let's go back to the client machine.

C:gittest>echo "dev1 3rd line" >> test1.txt

C:gittest>git commit -am "3rd from dev1, testing server hook"
[master b045e22] 3rd from dev1, testing server hook
 1 file changed, 1 insertion(+)

C:gittest>git push origin master
dev1:[email protected]'s password:
   d22c66e..b045e22  master -> master

On the Git server, check the work of the hook post-update script after the commit

[dev1@server proj2]$ find . ! -group proj2

- empty, everything is fine.

Connecting a Second Developer to Git

Let's simulate the work of the second developer.

On the client

C:gittest>git remote remove origin

C:gittest>git remote add origin "ssh://[email protected]/var/gitservertest/proj2"

C:gittest>echo "!!! dev2 added this" >> test1.txt

C:gittest>echo "!!! dev2 wrote" > test2.txt

C:gittest>git add test2.txt

C:gittest>git commit -am "dev2 added to test1 and created test2"
[master 55d49a6] dev2 added to test1 and created test2
 2 files changed, 2 insertions(+)
 create mode 100644 test2.txt

C:gittest>git push origin master
[email protected]'s password:
   b045e22..55d49a6  master -> master

And at the same time, on the server...

[dev1@server proj2]$ find . ! -group proj2

- again empty, everything works.

Deleting a Git Project and Loading a Project from a Git Server

Well, you can once again make sure that all the changes have been saved.

C:gittest>rd /S /Q .
Процесс не может получить доступ к файлу, так как этот файл занят другим процессом.

- to remove a Git project, simply clear the directory completely. Let's put up with the error given, since it is impossible to delete the current directory with this command, but this is exactly the behavior we need.

C:gittest>dir
 Содержимое папки C:gittest

21.06.2019  08:43    <DIR>          .
21.06.2019  08:43    <DIR>          ..

C:gittest>git clone ssh://[email protected]/var/gitservertest/proj2
Cloning into 'proj2'...
[email protected]'s password:

C:gittest>cd proj2

C:gittestproj2>dir
 Содержимое папки C:gittestproj2

21.06.2019  08:46    <DIR>          .
21.06.2019  08:46    <DIR>          ..
21.06.2019  08:46               114 test1.txt
21.06.2019  08:46                19 test2.txt
C:gittestproj2>type test1.txt
"test dev1 to proj2"
"dev1 added some omre"
"dev1 3rd line"
"!!! dev2 added this"

C:gittestproj2>type test2.txt
"!!! dev2 wrote"

Sharing Access in Git

Now let's make sure that the second developer cannot access the Proj1 project through Git, on which he is not working.

C:gittestproj2>git remote remove origin

C:gittestproj2>git remote add origin "ssh://[email protected]/var/gitservertest/proj1"

C:gittestproj2>git push origin master
[email protected]'s password:
fatal: '/var/gitservertest/proj1' does not appear to be a git repository
fatal: Could not read from remote repository.

Please make sure you have the correct access rights
and the repository exists.

Now allow access

[root@server ~]# usermod -aG proj1 dev2

and after that everything works.

C:gittestproj2>git push origin master
[email protected]'s password:
To ssh://10.1.1.11/var/gitservertest/proj1
 * [new branch]      master -> master

additional information

In addition, if there is a problem with the default permissions when creating files and directories, on CentOS you can use the command

setfacl -Rd -m o::5 -m g::7 /var/gitservertest

Also in the article you can stumble upon small useful things:

  • how to build a directory tree in linux
  • how to pass a range of addresses from a certain line to the end of the file in sed, that is, to make a replacement in sed in all lines except the first line
  • How to invert search condition in Linux find
  • how to pass multiple lines through a one-liner in a linux shell
  • how to escape single quotes in bash
  • how to delete a directory with all contents in windows command line
  • How to rename a file using bash mv without rewriting it

Thank you for attention.

Source: habr.com

Add a comment