Organizing Multi-user Access on a GIT Server

When setting up and configuring a Git server, the question arises about organizing access for multiple users to several projects. I conducted research on the matter and found a solution that meets all my requirements: simple, secure, and reliable.

My wishes are as follows:

  • 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 the projects they are working on
  • there should be the ability to connect via the command line, not just through some web interface

It would also be great to:

  • provide read-only rights for supervising individuals
  • conveniently manage user access rights in Git

Overview of possible access options on the GIT server

First of all, it's important to know what options are available, so here’s a brief overview of Git protocols.

  • ssh - a specifically created user account is used to access the server.
    • It's strange that Git does not discourage the recommendation to use one account for access to all repositories. This does not meet my requirements at all.
    • You can use multiple accounts, but how do you restrict a user's access to only certain directories?
      • Restricting to the home directory doesn’t work because it's difficult to organize write access for other users there.
      • Using symbolic links from the home directory is also complicated because Git does not interpret them as links.
      • Restricting access to the interpreter is possible, but there is no guarantee that it will always work.
        • You could even connect a separate command interpreter for such users, but,
          • firstly, that’s already a complicated solution,
          • and secondly, it can be bypassed.

    But perhaps it’s not a problem if a user can execute any commands? In general, this method shouldn’t be ruled out if a way to use it can be devised. Let’s revisit this approach later; for now, let’s briefly consider other alternatives, as there may be simpler options.

  • The git local protocol can be used in conjunction with sshfs, allowing multiple users, but essentially, it's the same as the previous case.
  • HTTP β€” read-only.
  • Git β€” read-only.
  • HTTPS β€” complex to set up, requires additional software, some kind of. control panel For organizing user access... it seems feasible, but everything is somewhat complicated.

Using the ssh protocol to set up multi-user access to the Git server.

Let's go back to the ssh protocol.

Since ssh access is used for git, it is essential to ensure the security of the server's data. The user connecting via ssh uses their own login on server Linux, so they can connect through an ssh client and access the server's command line.
There is no complete protection against such access.

However, the user should not be interested in Linux files. Significant information is only stored in the git repository. Therefore, command line access can be unrestricted, while using Linux tools to prevent the user from viewing projects they are not involved in.
It is evidently necessary to use the Linux permissions system.

As previously mentioned, it is possible to use only one account for ssh access. This configuration is unsafe for multiple users, although this method is included in the recommended git options.

To implement the requirements outlined at the beginning of the article, the following directory structure is created with designated permissions 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 β€” specially created Linux users assigned as owners of the corresponding project directories.

Permissions for all directories are set to 0770 β€” full access for the owner and their group, and complete denial for everyone else.

2) developer accounts

Developer 1: dev1:dev1,proj1,proj2
Developer 2: dev2:dev2,proj2,proj3

The key point is that 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, while 'Developer 2' is working on projects proj2 and proj3.

If any of the Developers connects via SSH through the command line, their permissions will not be sufficient even to view the contents of the project directories they are not involved in. They cannot change this themselves.

Since the basis of this principle is the fundamental security of Linux permissions, this scheme is reliable. Moreover, the scheme is very easy to administer.

Let's move on to practice.

Creating Git repositories on a Linux server

Checking.

[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 manually...

[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 confirm that it is impossible to access other people's repositories and even view their contents from the command line.

[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

Collaborative work in Git by multiple developers on a single project

One question remains: if one developer adds a new file, the other developers cannot change it because they are not its owner (for example, dev1), but rather a project owner (for example, proj1). Since we have a server repository, we must first understand how the '.git' directory is structured and whether new files are created.

Creating a local Git repository and pushing to the Git server

Let's switch to the client machine.

Microsoft Windows [Version 6.1.7601]
(c) Microsoft Corporation, 2009. All rights reserved.

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://dev1@10.1.1.11/var/gitservertest/proj2"

C:gittest>git push origin master
dev1:dev1@10.1.1.11'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 created 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 uploading changes to the Git server, additional files and directories are created, and their owner is indeed the user who uploads them. However, the group of these files and directories also corresponds to this user's primary group, meaning, group dev1 for user dev1 and group dev2 for user dev2 (changing the primary group of the developer user won't help, as they need to work on several projects). In this case, user dev2 will not be able to modify files created by user dev1, which jeopardizes functionality.

Linux chown β€” change file owner by a regular user

The file owner cannot change its ownership. However, they can change the group of the file they own, allowing access for modification to other users in the same group. This is exactly what we need.

Using Git hook

The working directory for the hook is the root directory of the project. The hook is an executable file that runs under the user who performs the push. Knowing this, we can proceed with our plans.

[dev1@server proj2]$ mv hooks/post-update{.sample,}
[dev1@server proj2]$ sed -i '2,$ s/^/#/' hooks/post-update
[dev1@server proj2]$ cat <> hooks/post-update

or simply

vi hooks/post-update

Let's return 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:dev1@10.1.1.11's password:
   d22c66e..b045e22  master -> master

On the Git server, we check the functioning of the post-update hook script after the commit.

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

β€” empty, everything is fine.

Connecting the 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://dev2@10.1.1.11/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
dev2@10.1.1.11's password:
   b045e22..55d49a6  master -> master

And at the same time, on the server…

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

β€” again empty, everything is working.

Deleting the Git project and downloading the project from the Git server

Well, we can check again that all changes have been saved.

C:gittest>rd /S /Q .
The process cannot access the file because it is being used by another process.

β€” to delete the Git project, we simply clear the directory completely. We will accept the error shown since it is impossible to remove the current directory with this command, but this is exactly the behavior we need.

C:gittest>dir
Contents of the folder C:gittest

21.06.2019  08:43              .
21.06.2019  08:43              ..

C:gittest>git clone ssh://dev2@10.1.1.11/var/gitservertest/proj2
Cloning into 'proj2'...
dev2@10.1.1.11's password:

C:gittest>cd proj2

C:gittestproj2>dir
Contents of the folder C:gittestproj2

21.06.2019  08:46              .
21.06.2019  08:46              ..
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 more"
"dev1 3rd line"
"!!! dev2 added this"

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

Access control in Git

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

C:gittestproj2>git remote remove origin

C:gittestproj2>git remote add origin "ssh://dev2@10.1.1.11/var/gitservertest/proj1"

C:gittestproj2>git push origin master
dev2@10.1.1.11'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 we allow access

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

and after that everything works.

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

More information

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

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

You may also come across small useful tips in the article:

  • how to build a directory tree in Linux
  • how to pass a range of addresses to sed from a specific line to the end of the file, i.e., perform a replacement in sed for all lines except the first line
  • How to invert the search condition with find in Linux
  • how to pass multiple lines to a loop through a one-liner in Linux shell
  • how to escape single quotes in bash
  • how to delete a directory with all its contents from the Windows command line
  • How to rename a file using bash mv without rewriting it

Thank you for your attention.

Source: habr.com

Buy reliable website hosting with DDoS protection, VPS VDS servers πŸ”₯ Buy reliable website hosting with DDoS protection, VPS VDS servers | ProHoster