Конфигурация Mercurial+Nginx для управления большим количеством репозиториев

    Под катом описан пример конфигурации связки mercurial+nginx и приведен скрипт автоматизации всего вышеперечисленного.

    Задачи:

    Создание хранилища репозиториев на базе системы контроля версий Mercurial с возможностью безопасной передачи данных и разделением уровней доступа.
    Автоматизация вышеперечисленных действий.

    Решение:

    Проксирование встроенного http сервера (hg serve) с разделением доступа на уровне прокси.
    Для исключения перехвата данных передача ведется по протоколу HTTPS.
    Для минимизации потребляемых ресурсов в роли прокси выступает Nginx.
    Разграничение доступа произведено на уровне директивы location.

    Соглашения:

    Дистрибутив Linux в котором все будет происходить — Gentoo
    https://hg.expample.com/reponame — ссылка для доступа к репозиторию
    hg.example.com — доменное имя сервера хранилища репозиториев
    reponame — название необходимого репозитория
    /home/repos — корневая папка для репозиториев
    /etc/hg — корневая папка для конфигурационных файлов
    Читающий(ая) сие имеет минимальные навыки администрирования Linux

    Установка необходимых пакетов:

    hg ~ # emerge mercurial<br>hg ~ # emerge nginx
    Встроенный http сервер будем запускать командой
    hg ~ # /usr/bin/hg serve -d -A /var/log/hg_access.log -p 8080 -a 127.0.0.1 --pid-file /var/run/hgserver.pid --encoding utf8 --webdir-conf /etc/hg/web.config
    Директивы запуска:
    -d — запуск сервера как демона
    -A /var/log/hg_access.log — лог доступа к серверу
    -p 8080 — номер порта на котором сервер ожидает запросов
    -a 127.0.0.1 — ip адрес на котором запускается сервер
    --pid-file /var/run/hgserver.pid — файл с идентификатором процесса сервера
    --encoding utf8 — кодировка
    --webdir-conf /etc/hg/web.config — конфигурационный файл сервера
    hg ~ # cat /etc/hg/web.config<br>[web] //секция параметров веб сервера<br>allow_push = * //разрешаем всем “поднимать” изменения (контроль доступа идет на уровне прокси)<br>push_ssl = false //не используем ssl (шифрование идет на уровне прокси)<br>[paths] //секция “путей”<br>rep1=/home/repos/rep1 //выставляем соответствие: название - расположение для репозитория rep1<br>rep2=/home/repos/rep2 //выставляем соответствие: название - расположение для репозитория rep2<br>
    Прописываем директиву Include в основной конфигурационный файл Nginx
    hg ~ # cat /etc/nginx/nginx.conf |grep -i include<br> include "/etc/hg/nginx.conf";
    Пример конфигурационного файла /etc/hg/nginx.conf
    hg ~ # cat /etc/hg/nginx.conf <br>server <br> {<br> listen 443;<br> server_name hg.example.com;<br> client_max_body_size 128M;<br> ssl on;<br> ssl_certificate /etc/ssl/nginx/nginx.pem;<br> ssl_certificate_key /etc/ssl/nginx/nginx.key;<br>location /repo1<br> {<br> proxy_pass http://127.0.0.1:8080;<br> auth_basic "Restricted";<br> auth_basic_user_file /etc/hg/nginx/repo1.pass;<br> access_log /var/log/nginx/repo1.hg.example.com.ssl_access_log main;<br> error_log /var/log/nginx/repo1.hg.example.com.ssl_error_log info;<br> }<br>location /repo2<br> {<br> proxy_pass http://127.0.0.1:8080;<br> auth_basic "Restricted";<br> auth_basic_user_file /etc/hg/nginx/repo2.pass;<br> access_log /var/log/nginx/repo2.hg.example.com.ssl_access_log main;<br> error_log /var/log/nginx/repo2.hg.example.com.ssl_error_log info;<br> }<br>}<br>
    Рассмотрим секцию: location /repo1
    proxy_pass — указываем куда пересылать запросы при удачной авторизации
    auth_basic — используем HTTP Basic Authentication
    auth_basic_user_file — указываем в каком файле находится база паролей
    access_log и error_log — директивы логирования доступа к репозиторию
    Создаем файлы авторизации для каждого из репозиториев командой htpasswd
    (программа htpasswd входит в пакет apache, так что придется установить его командой emerge apache)
    hg ~ # htpasswd -bc /etc/hg/nginx/repo2.pass test2 testpass2<br>hg ~ # htpasswd -bc /etc/hg/nginx/repo1.pass test1 testpass1
    -b — использовать пароль указанный в командной строке
    — создать новый файл базы паролей
    /etc/hg/nginx/repo1.pass — название файла базы паролей
    test1 — логин добавляемого юзера
    testpass1 — пароль добавляемого юзера
    Осталось инициализировать репозитории командой hg init
    hg ~ # hg init /home/repos/repo1<br>hg ~ # hg init /home/repos/repo2
    Запускаем Nginx командой
    hg ~ # /etc/init.d/nginx start
    Теперь репозитории repo1 и repo2
    доступны по адресу
    https://hg.example.com/repo1 и https://hg.example.com/repo2 соответственно.
    С настроенным разделением уровней доступа.
    На этом можно было и закончить статью, ибо для простой конфигурации, которую обновляют раз в 3-5 месяцев этого достаточно. Но что делать когда конфигурацию приходится менять часто? Правильный системный администратор незамедлительно начнет искать способ облегчить себе жизнь.

    Вариантов решения проблемы как минимум три:

    1. Использовать готовый продукт управления репозиториями.
    2. Делегировать почетную обязанность своему подчиненному.
    3. Создать систему управления самолично.
    К сожалению мне ни первый, ни второй вариант не подходили, так что пришлось пересиливать свою лень и делать самому.
    Итогом стал скрипт полностью автоматизирующий процесс управления сервером репозиториев.

    В котором было реализовано:

    1. Создание(если необходимо) и предоставление доступа к репозиториям.
    2. Запрет доступа к неактивным репозиториям.(при удалении из конфигурационного файла репозиторий не удаляется физически).
    3. Регенерация паролей пользователей для авторизации Nginx.
    4. Распределение доступа к репозиториям на уровне пользователя.

    Синтаксис конфигурационного файла допускает:

    1. использование пустых строк для визуального разделения секций
    2. использование символа “#” для выделения комментариев

    Файл конфигурации состоит из трех секций.

    [users] - секция описывающая пары: логин-пароль.разделителем служит занк “=”<br>user1=pass1<br>user2=pass2<br>user3=pass3<br>[repos] - секция описывающая доступные репозитории<br>repo1<br>repo2<br>repo3<br>[access] - секция описывающая права доступа к репозиториям, разделителем служит символ “,”<br>repo1 = user1 , user2,user3 - к репозиторию имеют доступ user1,user2,user3<br>repo2 = user1,user2 - к репозитроию имеют доступ только user1 и user2<br>repo3 = user3 - доступ к репозиторию только для пользователя user3<br>
    Листинг скрипта /usr/local/sbin/hgmkrep.sh
    1. #!/bin/bash
    2. tmphtpass="/var/tmp/htpass"  #определяем временный файл базы паролей
    3. repohome="/home/repos/"  #определяем корневую папку для репозиториев
    4. hgservepid="/var/run/hgserver.pid" #pid hg serve 
    5. hgaccesslog="/var/log/hg_access.log" #access лог файл для hg server
    6. domain="exapmple.com" #tld имя сервера 
    7. confdir="/etc/hg/" #определяем корневую папку для конфигов
    8. confile=${confdir}"repo.cfg" #основной конфиг файл /etc/hg/repo.cfg
    9. webconfig=${confdir}"web.config" #конфиг для hg server /etc/hg/web.config
    10. nginxconfig=${confdir}"nginx.conf" #конфиг для nginx /etc/hg/nginx.conf
    11. nginxauthdir=${confdir}"nginx/" #папка баз паролей для доступа к репозиториям
    12. [ -s ${confile} ] || echo "where is config file?"  #проверяем наличие основного конфига
    13. [ -s ${confile} ] || exit 0 # огорчаемся, если нет основного конфига
    14. #парсим секцию [repos] на предмет репозиториев
    15. repos=`cat ${confile}|sed '/^$/d'|sed '/^#/d'|sed 's/\ //g'|awk '/\[repos\]/{
    16. is_repos=1;
    17. while (is_repos==1)
    18.     {if (getline <= 0 || index($0,"[")==1)
    19.         {is_repos=0;}
    20.     else
    21.         {print $0;}}}'`
    22. #Проверяем наличие папок с репозиториями и при необходимости создаем
    23. for i in ${repos}
    24. do;[ -d ${repohome}${i} ] || /usr/bin/hg init ${repohome}${i};done
    25. #генерируем конфиг для hg server
    26. echo "[web]
    27. allow_push = *
    28. push_ssl = false
    29. [paths]" > ${webconfig}
    30. #разрещаем доступ только к активным репозиториям
    31. for i in ${repos}
    32. do;echo ${i}=${repohome}${i} >> ${webconfig};done
    33. #перезагружаем hg serve
    34. [ -a ${hgservepid} ] && /bin/kill `/bin/cat ${hgservepid}` && rm ${hgservepid}
    35. /usr/bin/hg serve -d -A ${hgaccesslog} -p 8080 -a 127.0.0.1  --pid-file  ${hgservepid} --encoding utf8 --webdir-conf ${webconfig}
    36. #создаем конфиг для nginx
    37. echo "server 
    38.     {
    39.     listen              443;
    40.     server_name hg."${domain}";
    41.     client_max_body_size 128M;
    42.     ssl on;
    43.     ssl_certificate             /etc/ssl/nginx/nginx.pem;
    44.     ssl_certificate_key /etc/ssl/nginx/nginx.key;" >  ${nginxconfig}
    45. #создаем lacation для активных репозиториев
    46.     for i in ${repos}
    47.         do
    48.     echo "location /"${i}"
    49.         {
    50.         proxy_pass http://127.0.0.1:8080;
    51.         auth_basic           \"Restricted\";
    52.         auth_basic_user_file "${nginxauthdir}${i}".pass;
    53.         access_log  /var/log/nginx/"${i}".hg."${domain}".ssl_access_log main;
    54.         error_log   /var/log/nginx/"${i}".hg."${domain}".ssl_error_log info;
    55.         }" >>  ${nginxconfig}
    56.         done
    57. echo "}" >> ${nginxconfig}
    58. #создаем (обнуляем на всякий случай) временную базу паролей
    59. cat /dev/null > ${tmphtpass}
    60. #парсим секцию основного конфига [users]
    61. #генеририруем пароли для все активных пользователей
    62. cat ${confile}|sed '/^$/d'|sed '/^#/d'|sed 's/\ //g'|awk -v passfile=$tmphtpass '/\[users\]/{
    63. is_users=1;
    64. while (is_users==1)
    65.     {if (getline <= 0 || index($0,"[")==1)
    66.         {is_users=0;}
    67.     else
    68.         {split($0,userpass,"=");system("htpasswd -b "passfile" "userpass[1]" "userpass[2]);}}}'
    69. #парсим секцию [access] основного конфига
    70. #и получаем список привилегий вида repo=user1,user2
    71. access=`cat ${confile}|sed '/^$/d'|sed '/^#/d'|sed 's/\ //g'|awk '/\[access\]/{
    72. is_access=1;while (is_access==1)
    73.     {if (getline <= 0 || index($0,"[")==1)
    74.         {is_access=0;}
    75.     else
    76.         {print $0;}}}'`
    77. #проверяем есть ли папка для хранения баз паролей
    78. [ -d ${nginxauthdir} ] || mkdir -p ${nginxauthdir}
    79. #удаляем старые файлы баз паролей
    80. find ${nginxauthdir} -type f -name *.pass -delete
    81. #для каждого репозитория из секции [access] генерируем личную базу паролей
    82. for i in ${access}
    83. do;echo ${i}|sed 's/,/\|/g'|awk -v tmphtpass=${tmphtpass} -v nginxauthdir=${nginxauthdir} \
    84. 'BEGIN{FS="="}{system("cat "tmphtpass" |egrep \""$2"\" > "nginxauthdir""$1".pass")}'done
    85. #перезапускаем nginx
    86. /etc/init.d/nginx restart
    87. #удаляем временный файл базы паролей
    88. rm ${tmphtpass}
    Так же выкладываю линк на рабочий скрипт и пример конфига: hgmkrep.tar.gz

    Существующие недостатки:

    1. Отсутствие разграничения для чтения и записи, каждый пользователь допущенный к телу императора репозиторию имеет права rw по умолчанию
    2. Не реализовано использование групп для ограничения доступа
    3. Не реализован откат конфигурации при некорректном синтаксисе конфига (система впадет в коллапс)
    4. При увеличении количества пользователей и репозиториев конфиг обильно теряет в наглядности
    Поделиться публикацией
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама

    Комментарии 11

      +2
      emerge dev-vcs/mercurial-server

      ;)
        0
        В документации к mercurial-server указанно: mercurial-server gives your developers remote read/write access to centralized Mercurial repositories using SSH public key authentication
        К сожалению данная схема авторизации мне не подходила.
        0
        Еще один недостаток: нет возможности показать пользователю список репозиториев (https://hg.expample.com/) доступ к которым ему разрешен. В вашем варианте можно только показать все сразу.
          +3
          Вижу уже не первый топик про Mercurial и автоматизацию работы с ним. Без лишней критики мне кажется что авторы не особо сильно старались облегчить жизнь себе и людям. Я например просто дописал сервер для поддержки авторизации Redmine.
          А для создания написал еще один модуль. Красиво и эффективно.
            +1
            авторы не особо сильно старались облегчить жизнь себе и людям
            авторы mercurial? Прозреваю, что они и не ставили себе задачи реализовать аутентификацию.
              +4
              В меркуриале многое написано на python. Ваш к.о. И авторизация как раз есть в классе который отвечает за сервер. А авторизация прикручивается более чем просто: наследуемся от этого базового класса и немного меняем поведение отвечающее за аутентификацию.
              Идея в том что надо использовать uwsgi как единственный православный веб-сервер для питоновских апликаций.
              Даже в доках сказано не использовать hg serve.
                +1
                Рад бы ошибаться, но судя по вашему комментарию, вы ещё путаете авторизацию с аутентификацией.
                Ссылка с исходниками не открывается, кстати. От меня, во всяком случае.
                  0
                  Все верно, авторизация за Redmine. DDNS сглючил. Ну по крайней мере время от времени должно работать.
                    0
                    Для меня пока основной вопрос по чьему велению создавать репозитории. Один вариант при создании проекта в Redmine, что уже сделано. Но это как-то не кошерно местами.
                      0
                      да, не очень кошерно. например, если у проекта несколько репозиториев.
                      КМК, правильнее было бы создавать репозитории автоматически. как mercurial-server. hg clone . ssh://hrr.brr/drr/bar/ — и вуаля
                        0
                        Ну, думаю, если собственно взяться можно до ума довести, ведь на тех же хостингах меркуриала репозитории из веб-интерфейса создаются.
                        По крайней мере мне казалось такое решение наиболее ванильным.

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

            Самое читаемое