Собственный сервер svnserve

Программа svnserve легкий сервер, способный общаться с клиентами через TCP/IP, используя собственный простой протокол. Клиенты обращаются к серверу, svnserve используя URL, который начинается с svn:// или svn+ssh://. В этом разделе рсказывается о различных способах запуска svnserve, о том как клиенты аутентифицируются на сервере и о настройке соответствующих правил доступа к хранилищу.

Запуск Сервера

Существует несколько различных способов запустить программу svnserve. При запуске без параметров, ничего кроме справочного сообщения вы не увидите. Если вы планируете запускать процесс через inetd, необходимо указывать параметр -i (--inetd):

$ svnserve -i
( success ( 1 2 ( ANONYMOUS ) ( edit-pipeline ) ) )

При запуске с параметром --inetd, svnserve будет пытаться общаться с клиентом Subversion через stdin и stdout используя собственный протокол. Это стандартное поведение для программ, запускаемых через inetd. IANA зарезервировал порт 3690 для протокола Subversion, поэтому на unix-подобных системах вы можете добавить в /etc/services строки подобные этим (возможно, они там уже есть):

svn           3690/tcp   # Subversion
svn           3690/udp   # Subversion

Если система использует классический Unix-подобный демон inetd, в /etc/inetd.conf можно добавить такую строку:

svn stream tcp nowait svnowner /usr/bin/svnserve svnserve -i

Убедитесь, что пользователь «svnowner» имеет необходимые права для доступа к хранилищу. После этого, когда от клиента к серверу на порт 3690 придет запрос на соединение, inetd запустит процесс svnserve для обслуживания этого запроса.

На Windows системах, существуют средства от сторонних производителей, которые запускают svnserve как сервис. Список этих инструментов можно посмотреть на веб-сайте Subversion.

Второй параметр запускает svnserve как автономный процесс—«демон». Для этого используется параметр -d:

$ svnserve -d
$               # svnserve is now running, listening on port 3690

Если svnserve запускается в режиме демона, можно использовать опции --listen-port= и --listen-host=, чтобы назначить нужное значение порта и имени хоста, к которому «привязан» svnserve.

Существует еще один, третий способ запуска svnserve, с параметром -t — это так называемый «туннельный режим». Этот режим предполагает, что программы удаленного доступа такие как RSH или SSH уже успешно аутентифицировали пользователя и запускают частный процесс svnserve от имени этого пользователя. Программа svnserve работает в обычном режиме (работая через stdin и stdout) и предполагает, что трафик автоматически перенаправится по некоторому тунелю к клиенту. Когда svnserve запущен подобным туннельным агентом убедитесь в том что аутентифицированый пользователь имеет полный доступ на чтение и запись к файлам базы данных хранилища. По сути, это соответствует обращению к хранилищу локального пользователя через URL вида file:///.

После запуска svnserve все хранилища на вашей системе будут доступны через сеть. Клиент должен указывать абсолютный путь в URL хранилища. Например, если хранилище расположено в /usr/local/repositories/project1, обращаться к нему клиент сможет по адресу svn://host.example.com/usr/local/repositories/project1. Чтобы увеличить безопасность, можно передать svnserve параметр -r, который разрешит обращаться только к тем хранилищам, которые расположены ниже указанного пути:

$ svnserve -d -r /usr/local/repositories
…

Применение параметра -r эффективно изменяет местоположение рассматриваемое программой как корень файловой системы. Клиенты используют URL, из которого удален этот путь, в результате чего URL получается более коротким (и менее разоблачительным):

$ svn checkout svn://host.example.com/project1
…

Встроенная аутентификация и авторизация

Когда клиент подключается к svnserve, происходит следующее:

  • Клиент указывает требуемое хранилище.

  • Сервер обрабатывает файл conf/svnserve.conf хранилища, и начинает выполнять правила аутентификации и авторизации, указанные в нем.

  • В зависимости от ситуации и правил авторизации,

    • клиенту может быть разрешено делать анонимные обращения, без запроса на идентификацию, ИЛИ

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

    • при работе в «туннельном режиме», клиент объявит себя уже идентифицированным.

Во время написания данного материала сервер знал только об вызове авторизации через CRAM-MD5 [41]. В сущности, сервер отправляет некоторые данные клиенту. Клиент, используя хэш алгоритма MD5, создает отпечаток (fingerprint) из совмещенных вместе этих данных и своего пароля, после чего отправляет этот отпечаток серверу как ответ. Сервер производит подобные вычисления со своей версией пользовательского пароля и проверяет идентичность результатов. Таким образом пароль никогда не передается в открытую по сети.

Также возможно, что клиент уже был идентифицирован через внешнего туннельного агента, такого как SSH. В таком случае, сервер просто проверяет запустившего его пользователя и в дальнейшем использует идентифицированное имя пользователя. Более подробно об этом смотри в «SSH идентификация и авторизация».

Как вы уже догадались, файл хранилища svnserve.conf — центральный механизм контроля правил идентификации и авторизации. Этот файл имеет такой же формат, как и другие конфигурационные файлы (см. «Параметры времени выполнения»): имена секций помечены квадратными скобками (([ и ]), комментарии начинаются с #, а каждая секция содержит определенные переменные, которые могут быть использованы для конфигурирования (переменная = значение). Давайте посмотрим на этот файл и изучим как им пользоваться.

Создание файла пользователей и область хранилища

Сейчас, секция [general] вsvnserve.conf имеет все необходимые вам переменные. Начнем с определения файла, содержащего имена пользователей и пароли, а также с области хранилища:

[general]
password-db = userfile
realm = example realm

realm — это определяемое вами имя. Оно сообщает клиентам, к какой «области идентификации» они подсоединяются; клиенту Subversion она выводится в приглашении к аутентификации, и используется как ключ (вместе с именем сервера и портом) для кэширования клиентской идентификационной информации на диск (см. «Кэширование клиентской идентификационной информации»). Переменная password-db указывает на отдельный файл, который содержит список пользователей и пароли в таком же простом формате. Например:

[users]
harry = foopassword
sally = barpassword

Значение password-db может быть абсолютным или относительным путем к файлу пользователей. Для большинства администраторов, его легче держать в conf/ области хранилища, рядом с svnserve.conf. С другой стороны, возможно, вы захотите разделять один и тот же файл пользователей для двух или более хранилищ, в этом случае этот файл стоит поместить в более доступное место. Хранилища разделяющие файл пользователей, должны быть также сконфигурированы с одинаковыми областями, так как список пользователей по существу определяет область аутентификации. Где бы в итоге файл не находился, убедитесь, что у него выставлены соответствующие права на чтение/запись. Если вы знаете от имени какого пользователя(-ей) будет запускаться svnserve, то ограничьте доступ на чтение только тем пользователем, которым это нужно.

Установка контроля доступа

Есть еще две дополнительных переменные в svnserve.conf: они определяют, что будут допущены делать не идентифицированные (анонимные) и идентифицированные пользователи. Переменные anon-access и auth-access могут иметь значения: none, read, или write. Установка значения в none запрещает доступ любого рода; read — доступ к хранилищу только на чтение, а write — позволяет полный доступ к хранилищу на чтение/запись. Например:

[general]
password-db = userfile
realm = example realm

# anonymous users can only read the repository
anon-access = read

# authenticated users can both read and write
auth-access = write

Значения установленные в примере — фактически значения по умолчанию, они будут установлены если вы не захотите определить их. Если вы хотите быть более консервативным, то можете заблокировать полностью анонимный доступ:

[general]
password-db = userfile
realm = example realm

# anonymous users aren't allowed
anon-access = none

# authenticated users can both read and write
auth-access = write

Сервер понимает не только такие «поверхностные» ограничения доступа к хранилищу, но также и более детальные, связанные с конкретными файлами и каталогами в хранилище. Чтобы использовать эту возможность, вам необходимо создать файл, содержащий эти специальные правила и указать к нему путь с помощью переменной authz-db:

[general]
password-db = userfile
realm = example realm

# Specific access rules for specific locations
authz-db = authzfile

Формат файла authzfile детально описан в «Path-Based Authorization». Заметьте, что переменная authz-db и пара anon-access, auth-access допускают совместное использование; если все переменные определены одновременно, то для получения доступа должны быть удовлетворены все правила.

SSH идентификация и авторизация

Встроенная в svnserve идентификация может быть более удобной, так как избегает необходимости создавать реальные системные учетные записи. С другой стороны, некоторые администраторы уже имеют хорошо настроенные SSH оболочки в своих системах. В этой ситуации все пользователи проектов уже имеют системные учетные записи и способность к «SSH into» на серверную машину.

Проще всего использовать SSH в связке с svnserve. Клиенты просто используют для коннекта схему svn+ssh://:

$ whoami
harry

$ svn list svn+ssh://host.example.com/repos/project
harry@host.example.com's password:  *****

foo
bar
baz
…

В этом примере клиент Subversion вовлекает локальный процесс ssh, соединяется с host.example.com, идентифицируется как пользователь harry, затем порождает личный процесс svnserve на удаленной машине запускаемый от имени пользователя harry. Команда svnserve была вовлечена в режим тунелирования (-t) и ее сетевой протокол был «тунелирован» через зашифрованное соединение через тунельного-агента ssh. svnserve знает что он запущен пользователем harry, и если клиент выполняет фиксацию, идентификационное имя пользователя будет использовано как имя автора новой ревизии.

Важная вещь для понимания здесь, это то что клиент Subversion не соединяется с запущенным демоном svnserve. Этот метод доступа не требует демона, ни делает уведомления даже если он присутствует. Он использует в целом способность ssh запускать временный процесс svnserve, которые завершается когда сетевое соединение закроется.

Когда для доступа к хранилищу используется URL вида svn+ssh://, помните что это программа ssh запрашивает идентификацию, а не клиентская программа svn. Это означает что нет автоматического кеширования паролей (см. «Кэширование клиентской идентификационной информации»). Клиенты Subversion часто делают несколько соединений к хранилищу, хотя пользователи обычно не знают об этом из-за возможности кеширования паролей. Однако когда используют URL вида svn+ssh:// URLs, пользователи могут быть раздражены ssh из-за повторяющихся запросов пароля для каждого исходящего соединения. Решение этой проблемы заключается в использовании отдельного инструмента для кеширования паролей SSH, подобных ssh-agent на Unix-подобных системах, или pageant в Windows.

Когда выполняется через тунелирование, идентификация первоначально управляется правами операционной системы на файлы базы данных хранилища; это очень похоже на то как если бы Harry получала доступ к хранилищу напрямую через URL file:///. Если несколько пользователей системы получают доступ к хранилищу напрямую, вы можете захотеть поместить их в общую группу, и вы должны будете быть очень осторожным при разрешении (umasks). (Прочитайте «Supporting Multiple Repository Access Methods».) Но в каждом случае тунелирования файл svnserve.conf может продолжать использоваться для блокирования доступа, простой установкой auth-access = read или auth-access = none. [42]

Вы не должны думать что рассказ о SSH тунелирование будет закончен здесь. Subversion позволяет вам создавать заказное поведение тунеля в файле config (смотри «Параметры времени выполнения»). Например, предположим что вы хотите использовать RSH вместо SSH. В разделе [tunnels] файла [tunnels] просто укажите подобно этому:

[tunnels]
rsh = rsh

И сейчас вы можете использовать новое определение туннеля используя схему URL которая соответствует имени вашей новой переменной: svn+rsh://host/path. Затем используя новую схему URL, клиент Subversion будет выполнять команду rsh host svnserve -t за кулисами. Если вы включите имя пользователя в URL (например, svn+rsh://username@host/path) клиент также будет включать его в эту команду (rsh username@host svnserve -t). Но вы может определить новую схему туннелирования которая будет более умная чем эта:

[tunnels]
joessh = $JOESSH /opt/alternate/ssh -p 29934

Этот пример демонстрирует связанные вещи. С начала он показывает как можно сделать чтобы клиент Subversion запускал очень специфическую программу тунелирования (она расположена в /opt/alternate/ssh) с некоторым параметром. В данном случае, доступ к svn+joessh:// будет вовлекать особую программу SSH с аргументами -p 29934 — полезно если вы хотите соединить программу тунелирования на нестандартный порт.

Затем он показывает как определить пользовательскую переменную окружения, которая может перекрыть имя программы тунелирования. Установка переменной окружения SVN_SSH это удобный путь для перекрытия агента тунелирования SSH по умолчанию. Но если вы нуждаетесь в нескольких различных перекрытиях, для разных серверов, каждый возможно взаимодействует с разными портами или передача различных наборов параметров в SSH, вы можете использовать механизм показанный в этом примере. Сейчас, если вы установите переменную окружения JOESSH, ее значение будет перекрывать содержимое переменной тунелирования —$JOESSH будет выполняться вместо /opt/alternate/ssh -p 29934.

Трюки конфигурирования SSH

Возможно не только управлять как клиент выполняет ssh, но также управлять поведением sshd на машине вашего сервера. В этом разделе, мы покажем как управлять тем, какие именно команды svnserve вызываются sshd, а также о том, как несколько пользователей могут использовать одну системную учетную запись.

Начальная настройка

Для начала, перейдите в домашнюю папку учетной записи которую вы используете для запуска svnserve. Убедитесь что учетная запись имеет установленную пару ключей SSH (публичную/приватную), и что пользователь может зайти через идентификацию с использованием публичного ключа. Парольная идентификация не работает, так как все следующие SSH трюки вращаются вокруг использования файла SSH authorized_keys.

Если он не существует, создайте файл authorized_keys (на Unix, обычно ~/.ssh/authorized_keys). Каждая строка этого файла описывает публичный ключ, который разрешен для соединения. Строки обычно в следующей форме:

  ssh-dsa AAAABtce9euch… user@example.com

Первое поле описывает тип ключа, второе поле uu-кодированный (uuencoded) ключ и третье поле это комментарий. Однако, менее известный факт что всей строке может предшествовать поле command:

  command="program" ssh-dsa AAAABtce9euch… user@example.com

Когда поле command установлено, демон SSH будет выполнять указанную программу, вместо обычной svnserve -t моля что клиент знает об этом. Это открывает двери для многих трюков на стороне сервера. В следующем примерах мы сокращаем строки в файле так :

  command="program" TYPE KEY COMMENT

Controlling the invoked command

Так как мы можем указать выполняемую на сервере команду, проще назвать специфическую программу svnserve и передать дополнительные параметры:

  command="/path/to/svnserve -t -r /virtual/root" TYPE KEY COMMENT

В этом примере, /path/to/svnserve может быть пользовательским скриптом, оберткой вокруг svnserve которая устанавливает umask (смотри «Supporting Multiple Repository Access Methods»). Также показано как привязать svnserve к виртуальному корневому каталогу, который часто делается когда запускается svnserve как процесс-демон. Это может быть сделано либо ограничением доступа к части системы, или просто заставляя пользователя указывать абсолютный путь в svn+ssh:// URL.

Так же возможно иметь нескольких пользовательских разделяемых ресурсов под одной учетной записью. Вместо создания различных учетных записей для каждого пользователя, сгенерируйте пару публичный/частный ключ для каждого человека. Затем поместите каждый публичный ключ в файл authorized_users, по одному ключу в строке, и используйте параметр --tunnel-user:

  command="svnserve -t --tunnel-user=harry" TYPE1 KEY1 harry@example.com
  command="svnserve -t --tunnel-user=sally" TYPE2 KEY2 sally@example.com

Этот пример позволяет обоим, Гарии и Салли подключаться к одной учетной записи через идентификацию через публичный ключ. Каждый из них имеет собственную команду, которая будет выполняться, параметр --tunnel-user говорит svnserve -t какой из названных аргументов идентифицирует пользователя. Без --tunnel-user они будут появляться не смотря на все фиксации которые сделаны из одной разделяемой учетной записи.

В заключение предостережение: предоставление доступа пользователям к серверу через публичные ключи в разделяемой учетной записи может оставлять доступным другие формы доступа через SSH, даже если вы установили значение command в authorized_keys. Например, пользователи могут получить доступ через SSH, или иметь возможность выполнять X11 или общий форвардинг портов через ваш сервер. Для предоставления пользователям как можно меньше привилегий, можно указать несколько ограничивающих опций сразу же после command:

  command="svnserve -t --tunnel-user=harry",no-port-forwarding,\
           no-agent-forwarding,no-X11-forwarding,no-pty \
           TYPE1 KEY1 harry@example.com


[41] Смотри RFC 2195.

[42] Note that using any sort of svnserve-enforced access control at all is a bit pointless; the user already has direct access to the repository database.