Ależ ja się płodny zrobiłem ;) Dziś znów mam dla Was wpis z gatunku technicznych. Tym razem zabrałem się za serwer kontroli wersji. CVS jest już stare i praktycznie nieużywane, gita i mercuriala jeszcze nie ogarniam, więc skupiłem się na tym co choć trochę liznąłem – Subversion. Co prawda też już do najmłodszych nie należy i pewnie w niedalekiej przyszłości podzieli los CVSa, ale ma to co lubię najbardziej  – prostotę obsługi. Żeby nie było nie jest też pozbawiony wad a najważniejsza to brak natywnej obsługi SSL. Ale jak to powiadają jak się chce to można. Jest trochę kombinowania ale da się zmusić subversion do obsługi SSL.

Żeby SVN działał z SSL potrzebujemy do tego serwera www. Niestety musi być to Apache ponieważ ani NGiNX ani Lighttpd nie doczekały się jeszcze odpowiednika apacheowego mod_dav_svn. Trochę mi to nie na rękę ponieważ u siebie używam NGiNX ale skoro mus to mus – może kiedys NGiNX doczeka się obsługi SVN tak niedawno jak doczekał się mod_security.

Jak to więc ugryźć i co będzie potrzebe? A no to zależy od rozwiązania na które się zdecydujemy. Lista możliwych kombinacji w przypadku gdy głownym serwerem WWW jest NGiNX:

  • nowa domena + nowy certyfikat + osobne IP, na którym będzie nasłuchiwał tylko Apache (jak wiadomo nie da się jednego portu zbindować do 2 różnych usług)
  • subdomena + nowy certyfikat + osobne IP
  • subdomena + certyfikat typu wildcard + osobne IP
  • subdomena + certyfikat typu wildcard + apache nasłuchujący na wysokim, wolnym porcie
  • Apache skonfigurowany dla domeny dla której już mamy certyfikat i nasłuchujący na wysokim, wolnym porcie
  • Apache skonfigurowany dla domeny dla której już mamy certyfikat i nasłuchujący na lokalnym IP (np 127.0.0.1) i dowolnym, niezajętym porcie, NGiNX jako reverse-proxy

Wspólną wadą czterech pierwszych rozwiązań jest cena – nawet jak mamy darmowy certyfikat to pozostaje kwestia domeny (od kilkunastu do kilkuset złotych rocznie) oraz koszt adresu IP – a z tym to różnie bywa, nie którzy dostawcy nie są skorzy do rozdawnictwa, inni krzyczą sobie kolejne “grosiki” do comiesięcznego rachunku. A nawet jak uda się nam dopaść dostawcę który da nam drugiego/kolejngo IPka za free to (w przypadku 3 i 4) zostanie kwestia ceny certyfikatu WildcardSSL (tych jeszcze za free nie rozdają a ceny zaczynają się od ~60$ rocznie jeśli kupimy “zagramanicą” lub od ~200-250pln). 4 i 5 opcja są nieestetyczne, jakoś nie lubię odwołań typu http://domena.pl:PORT/costam. Metodą eliminacji doszliśmy do tego “jak to jest ma być zrobione”, więc do dzieła.

Instalacja

Ustawiamy flagi i instalujemy wymagane oprogramowanie podstawowe:

echo "dev-vcs/subversion -berkdb" >> /etc/portage/package.use
echo "app-portage/layman subversion" >> /etc/portage/package.use
echo "www-apache/mod_dav_svn -berkdb" >> /etc/portage/package.use
emerge subversion layman www-servers/apache

Mod_dav_svn nie jest dostępny w standardowym drzewie portage, stąd konieczność instalacji laymana. Będziemy musieli dodać overlaya sabayona. Mamy zainstalowane subversion i laymana więc dodajmy overlaya i instalujemy moduł do svna.

layman -a sabayon-distro
eix-sync
echo "source /var/lib/layman/make.conf" >> /etc/portage/make.conf
emerge www-apache/mod_dav_svn

Konfiguracja

Przygotowanie katalogów dla SVN

W naszym przykładzie subversion będzie urzędował w katalogu /home/svn. Repozytoria będą w /home/svn/repos/NAZWA-REPO. Plik /home/svn/.svn-auth-file będzie zawierał pary login:hasło użytkowników z uprawnieniami do zapisu w rpozytoriach. W pliku /home/svn/.svn-policy-file będą skonfigurowane “ACLe” dla poszczególnych repozytoriów.

mkdir -p /home/svn/repos
htpasswd -cs /home/svn/.svn-auth-file LOGIN

W miejsce login wpisujemy nazwę użytkownika, który ma mieć pełny dostęp do repozytorium(ów). Zostaniemy poproszeni o podanie hasła (dwa razy). Polecenie htpasswd automatycznie utworzy plik /home/svn/.svn-auth-file (opcja -c) oraz zapisze hasło jako skrót SHA1 (opcja -s) zamiast domyślnego MD5. Do pliku /home/svn/.svn-policy-file dodajemy:

[/]
* = r

[REPO1:/]
LOGIN = rw

Pierwsza sekcja “[/]” pozwala wszsytkim użytkownikom na odczytanie dowolnego repozytorium. Kolejne sekcje, “[REPO:/]”, to określenie dokładnych praw poszczególnych użytkowników do wybranych repozytoriów. Jeśli chcemy zabronić komuś dostępu do wskazanego repozytorium korzystamy z wpisu

LOGIN =

w sekcji wybranego repozytorium. Po zaawansowane ustawienia ACLi odsyłam do dokumentacji.

Katalogi utworzone, ACLe i hasła przygotowane więc czas utworzyć nasze pierwsze repozytorium

svnadmin create /home/svn/repos/NAZWA

I na tym chwilowo poprzestaniemy jeśli chodzi o svn. Później do niego wrócimy.

Przygotowanie i uruchomienie Apache

Otwieramy plik /etc/conf.d/apache2 i linijkę

APACHE2_OPTS="-D DEFAULT_VHOST -D INFO -D SSL -D SSL_DEFAULT_VHOST -D LANGUAGE"

zamieniamy na

APACHE2_OPTS="-D SSL -D SVN -D SVN_AUTHZ -D DAV -D DAV_FS"

Zapisujemy zmiany i wychodzimy. Następny do edycji jest plik /etc/apache2/modules.d/00_default_settings.conf. Zamieniamy 2 wpisy. Pierwszy

ServerTokens Full

na

ServerTokens Prod

. Drugi

ServerSignature On

na

ServerSignature Off

.

Opcjonalnie modyfikujemy plik /etc/apache2/modules.d/00_mod_log_config.conf zamieniając linijkę

LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined

na

LogFormat "%{X-Forwarded-For}i %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined

. Zmiana jest potrzeba tylko jeśli chcemy mieć dodatkowe logi vhosta. Można pominąć ten krok wtedy wszystko będzie w logach NGiNXa. Jeśli dodamy logowanie to w dodatkowym logu będą wpisy dotyczące tylko svna. Dzięki tej zmianie w logach Apache będziemy mieć rzeczywisty adres IP, z którego łączono się z SVNem. Bez tej zmiany w logu zamiast IP użytkownika będzie 127.0.0.1 – czyli IP serwera proxy.

Konfiguracja Apache już prawie zakończona. Zostało tylko ustawienie vhosta. SVN będzie dostępny u mnie pod adresem https://valhalla.org.pl/svn. Jak wspominałem wcześniej Apache będzie działał na lokalnym IP (u mnie akurat 127.0.0.1 ale może być dowolny inny dodany do interfejsu sieciowego np przez ip a a 10.0.0.10/16 dev eth0) i jakimś wysokim porcie (u mnie 2233). “Oczywistą oczywistością” jest włączenie SSL w konfiguracji vhosta – w końcu po to tyle rzeźbimy ;) Nazwę serwera ustawiamy na domenę, dla której mamy wykupiony certyfikat SSL!

Listen 2233

<VirtualHost 127.0.0.1:2233>;
ServerName valhalla.org.pl

SSLEngine on

SSLProtocol ALL -SSLv2 -SSLv3
SSLCipherSuite ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128:AES256:HIGH:!RC4:!aNULL:!eNULL:!EXPORT:!DES:!3DES:!MD5:!PSK
SSLHonorCipherOrder On
SSLCertificateFile /etc/ssl/my_certs/valhalla.org.pl/valhalla.crt
SSLCertificateKeyFile /etc/ssl/my_certs/valhalla.org.pl/valhalla.key
SSLCertificateChainFile /etc/ssl/my_certs/valhalla.org.pl/rootCA.pem

# następne 2 linijki (ErrorLog i CustomLog) są opcjonalne
ErrorLog /var/log/apachesvn.err
CustomLog /var/log/apachesvn.log combined

<Location /svn/>;
DAV svn
SVNParentPath /home/svn/repos
AuthzSVNAccessFile /home/svn/.svn-policy-file
AuthName "SVN Repositories"
AuthType Basic
AuthUserFile /home/svn/.svn-auth-file
# 3 następne linijki pozwolą na odczytanie repo bez konieczności logowania
# za to wszystkie commity będą wymagały podania poprawnego loginu i hasła
<LimitExcept GET PROPFIND OPTIONS REPORT>
Require valid-user
</LimitExcept>
</Location>
</VirtualHost>

Apacz gotowy więc można go z czystym sumieniem uruchomić /etc/init.d/apache2 start

Konfiguracja NGiNX

Apache działa na lokalnym IP wiec z zewnątrz się do niego nie dostaniemy potrzebujemy więc jakiegoś serwera reverse-proxy. Na szczęście NGiNX przychodzi nam z pomocą. Jak już wcześniej wspominałem dostęp do SVNa chce mieć po wpisaniu adresu https://valhalla.org.pl/svn. Musimy więc ustawić odpowiedni kontekst w konfiguracji vhosta domeny.

Uwaga! Achtung! внимание! Kolejna “oczywista oczywistość”: vhost w nginx dla naszej domeny również musi być ustawiony z obsługą SSL!

W dowolnym miejscu w konfigu vhosta (byle przed zamykającą sekcje server klamerką “}”) dodajemy:

 location /svn {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass https://127.0.0.1:2233/svn/;
}

Zapisujemy zmiany i resetujemy NGiNX /etc/init.d/nginx restart

Najistotniejszą linijką jest tutaj proxy_pass https://127.0.0.1:2233/svn/;. Dzięki czemu po wpisaniu adresu https://valhalla.org.pl/svn dostaniemy to co serwuje Apache pod adresem https://127.0.0.1:2233/svn/. Sprawdzamy czy wszytko działa otwierając w przeglądarce adres https://nasza-domena/svn. Naszym oczom ukaże się piękny błąd 403 - Frobidden You don't have permission to access /svn/repos on this server. Bez paniki, wszytko działa jak powinno. Błąd spowodowany jest brakiem pliku index.html/index.php oraz uprawnieinami do katalogu /home/svn/repos. Jak zapewne pamiętasz, drogi Czytelniku, utworzyliśmy je z konta root i takiego też mają ownera, a Apache działa na użytkowniku apache:apache.

“Powrót do przeszłości” – subversion

No dobra mamy dostęp do SVNa przez HTTPS. Teraz trzeba by utworzyć jakieś repozytorium i sprawdzić jak to działa w środowisku “produkcyjnym”. Tworzymy repo:

svnadmin create /home/svn/repos/NAZWA

Zmieniamy uprawnienia katalogu tak aby Apache mógł się do niego dostać:

chown -R apache:apache /home/svn/repos/NAZWA

. Teraz możemy sprawdzić czy nasze repo jest dostępne, otwieramy więc w przeglądarce adres https://nazwa-domeny/svn/NAZWA-REPO. Powinniśmy zobaczyć tekst

NAZWA-REPO - Revision 0:

. Skoro działa, to sprawdźmy czy przez klienta SVN też się dostaniemy do repo. Na lokalnym kompie wydajemy polecenie

cd /tmp
svn co https://nazwa-domeny/svn/NAZWA-REPO

I wszystko działa… No to teraz spróbujmy coś zaimportować do tego repozytorium.

cd /tmp
mkdir -p nowy-projetk/{trunk,branches,tags}
echo "moje pierwsze repozytorium" > nowy-projekt/trunk/projekt.txt
svn import nowy-projekt https://nazwa-domeny/svn/NAZWA-REPO -m 'komentarz'

Powita nas taki oto komunikat:

Authentication realm: <https://DOMENA:443> SVN Repositories
Password for 'LOKALNY_UŻYTKOWNIK':

Jeśli lokalny użytkownik zgadza się z tym, który ma dostęp RW do repozytorium to wpisujemy hasło, jeśli na lokalnym kompie mamy innego użytkownika niż ten z prawami zapisu do repozytorium to wciskamy [Enter] a potem podajemy nazwę użytkownika i hasło.

Voilà

SVN działa, szyfrowane połączenie z nim działa – ot mamy wszystko o co nam chodziło. W powyższym how-to nie uwzględniłem możliwości połączeń przez protokół SVN:// – co zresztą było zamierzone. Jeśli spróbujemy zrobić checkout przez protokół svn zostaniemy powitani komunikatem błędu:

winnetou@hordeum-vulgare /tmp $ svn co svn://domena.pl/svn/repo
svn: E000111: Unable to connect to a repository at URL 'svn://domena/svn/repo'
svn: E000111: Can't connect to host 'domena.pl': Połączenie odrzucone

 

Standardowo wszelkie pytania/sugestie/poprawki proszę kierować mailowo lub w komentarzach.

Do następnego wpisu.

WykopShare