Организация многопользовательского доступа на сервер GIT

При установке и конфигурировании Git-сервера встаёт вопрос об организации доступа нескольких пользователей к нескольким проектам. Я провёл исследование вопроса и нашёл решение, удовлетворяющее всем моим требованиям: простое, безопасное, надёжное.

Мои пожелания таковы:

  • каждый пользователь подключается со своим собственным аккаунтом
  • над одним проектом может работать несколько пользователей
  • один и тот же пользователь может работать над несколькими проектами
  • каждый пользователь имеет доступ только в те проекты, над которыми он работает
  • должна быть возможность подключения через командую строку, а не только через какой-то веб-интерфейс

Также было бы здорово:

  • предоставлять права только для чтения для контролирующих лиц
  • удобно администрировать права доступа пользователей в Git

Обзор возможных вариантов доступа на GIT сервер

Прежде всего, нужно знать, из чего выбирать, поэтому краткий обзор протоколов Git.

  • ssh — для доступа к серверу используется специально заведённый аккаунт пользователя.
    • странно, что Git не исключает из рекомендаций использование одного аккаунта для доступа ко всем репозиториям. Это никак не отвечает моим требованиям.
    • можно использовать несколько аккаунтов, но как ограничить доступ пользователя только определёнными директориями?
      • Закрытие в домашнюю директорию не подходит, потому что сложно организовать туда доступ на запись для других пользователей
      • Использование символических ссылок из домашней директории тоже сложно из-за того что Git не интерпретирует их как ссылки
      • Ограничить доступ к интерпретатору, ну можно, но нет полной гарантии, что это будет работать всегда
        • Можно вообще подключить для таких пользователей свой интерпретатор команд, но,
          • во 1-х, это уже какое-то сложное решение,
          • а во 2-х, это можно обойти.

    Но, может быть, это не является проблемой, что пользователь сможет выполнять любые команды?.. В общем, нельзя исключать этот метод, если придумать, как именно его использовать. Вернёмся к этому способу позже, а пока кратко рассмотрим остальные альтернативы, может быть там будет что-то проще.

  • git local протокол может быть использован в сочетании с sshfs, можно использовать несколько пользователей, но по сути, это то же самое, что предыдущий случай
  • http — только для чтения
  • git — только для чтения
  • https — сложно устанавливать, нужно дополнительное программное обеспечение, какая-то панель управления для организации доступа пользователей… выглядит реализуемым, но как-то всё сложно.

Использование протокола ssh для организации многопользовательского доступа к серверу Git

Вернёмся к протоколу ssh.

Поскольку используется доступ по ssh для git, нужно обеспечить безопасность данных сервера. Пользователь, который подключается по ssh, использует собственный логин на сервере Linux, поэтому он может подключиться через ssh клиент и получить доступ к командной строке сервера.
Полной защиты от получения такого доступа нет.

Но для пользователя не должны быть интересны файлы Linux. Значимая информация хранится только в репозитории git. Поэтому можно не ограничивать доступ через командную строку, но средствами Linux запретить пользователю смотреть проекты, исключая те, в которых он участвует.
Очевидно использовать систему прав доступа Linux.

Как уже говорилось, возможно использование только одного аккаунта для ssh доступа. Такая конфигурация является небезопасной для нескольких пользователей, хотя этот способ и включён в список рекомендуемых git вариантов.

Для реализации приведённых в начале статьи требований, создаётся следующая структура директорий с назначением прав и владельцев:

1) директории проектов

dir1(proj1:proj1,0770)
dir2(proj2:proj2,0770)
dir3(proj3:proj3,0770)

где
dir1, dir2, dir3 — директории проектов: проект 1, проект 2, проект 3.

proj1:proj1, proj2:proj2, proj3:proj3 — специально созданные Linux пользователи, которые назначаются владельцами директорий соответствующих проектов.

права на все директории выставляются в 0770 — полный доступ владельца и его группы и полный запрет для всех остальных.

2) аккаунты разработчиков

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

Ключевой момент состоит в том, что разработчикам назначается дополнительная группа системного пользователя-владельца соответствующего проекта. Это делается администратором Linux сервера одной командой.

В этом примере, «Разработчик 1» работает над проектом proj1 и proj2, а «Разработчик 2» работает надо проектами proj2 и proj3.

Если любой из Разработчиков подключится по ssh через командную строку, то его прав будет недостаточно даже на просмотр содержимого директорий проектов, в которых он не участвует. Изменить он это сам никак не может.

Поскольку основа этого принципа — базовая безопасность прав Linux, эта схема является надёжной. Кроме этого, схема очень легко администрируется.

Перейдём к практике.

Создание Git репозиториев на Linux сервере

Проверяем.

[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

надоело вводить руками…

[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

Убеждаемся, из командной строки невозможно получить доступ к чужим репозиториям и даже посмотреть их содержимое.

[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

Совместная работа в Git нескольких разработчиков над одним проектом

Остаётся один вопрос, если один разработчик вводит новый файл, то остальные разработчики не могут его менять, потому что он сам является его владельцем (например, dev1), а не пользователь-владелец проекта (например, proj1). Поскольку у нас серверный репозиторий, прежде всего, нужно знать, как устроена директория «.git» и создаются ли новые файлы.

Создание локального Git репозитория и push на Git сервер

Перейдём на клиентскую машину.

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>

В это же время на сервере образуются новые файлы, и они принадлежат пользователю который провёл 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]$

При загрузке изменений на сервер Git создаются дополнительные файлы и директории, и при этом их владельцем действительно является тот пользователь, который делает загрузку. Но тогда и группа этих файлов и директорий также соответствует основной группе этого пользователя, то есть, группа dev1 для пользователя dev1 и группа dev2 для пользователя dev2 (смена основной группы пользователя-разработчика не поможет, поскольку как тогда работать над несколькими проектами?). В таком случае, пользователь dev2 не сможет изменять файлы, созданные пользователем dev1, а это чревато нарушением функциональности.

Linux chown — изменение владельца файла обычным пользователем

Владелец файла не может изменять его принадлежность. Но он может изменить группу файла, который ему принадлежит, и тогда этот файл может быть доступен для изменения другим пользователям, которые находятся в этой же группе. Это то, что нам нужно.

Использование Git hook

Рабочей директорией для hook является корневая директория проекта. hook является исполняемым файлом, который выполняется под пользователем, который делает push. зная это, мы можем осуществить задуманное.

[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

либо просто

vi hooks/post-update

Вернёмся на клиентскую машину.

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

На Git сервере проверяем после коммита работу hook post-update скрипта

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

— пусто, всё нормально.

Подключение второго разработчика в Git

Сымитируем работу второго разработчика.

На клиенте

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

А в то же самое время, на сервере…

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

— опять пусто, всё работает.

Удаление проекта Git и загрузка проекта из Git сервера

Ну можно ещё раз убедиться в том, что все изменения сохранились.

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

— для удаления Git проекта, просто очищаем директорию полностью. Смиримся с выдаваемой ошибкой, поскольку невозможно удалить текущую директорию по этой команде, но нам как раз такое поведение и нужно.

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"

Разделение доступа в Git

Теперь убедимся, что и через Git второй разработчик не может получить доступ к проекту Proj1, надо которым он не работает.

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.

Теперь разрешаем доступ

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

и после этого всё работает.

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

Дополнительные сведения

В добавление, если есть проблема с правами по умолчанию при создании файлов и директорий, в CentOS можно воспользоваться командой

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

Также а статье вы можете наткнуться на мелкие полезные вещи:

  • как в Linux построить дерево каталогов
  • как в sed передать диапазон адресов с определённой строки до конца файла, то есть, сделать в sed замену во всех строках кроме первой строки
  • Как в Linux find инвертировать условие поиска
  • как в Linux shell передать в цикл несколько строк через однострочник
  • как в bash экранировать одиночные кавычки
  • как в командной строке windows удалить директорию со всем содержимым
  • Как с помощью bash mv переименовать файл, не переписывая его заново

Спасибо за внимание.

Источник: habr.com