6 March 2011

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

System administration
Под катом описан пример конфигурации связки 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. При увеличении количества пользователей и репозиториев конфиг обильно теряет в наглядности
Tags:linuxmercurialnginxbashawkshell scriptinggentoo
Hubs: System administration
+25
5.3k 62
Comments 11