Category: Security

Debian (lub Ubuntu) i SVN (Submin, WebSVN) – instalacja i konfiguracja

Ostatnio stawiając serwer deweloperski, spotkałem się z zadaniem postawienia na nim Subversion. Proces stawiania i konfiguracji SVNa miałem przygotować też na zajęcia, więc skoro i tak muszę zebrać to i spisać, postanowiłem zrobić z tego wpis.

Co to jest SVN i z czym to się je?

Subversion (znany również jako SVN) – system kontroli wersji, który powstał w celu zastąpienia CVS. SVN jest wolnym i otwartym oprogramowaniem na licencji Apache.

System kontroli wersji sam w sobie służy do śledzenia zmian głównie w kodzie źródłowym oraz pomocy programistom w łączeniu i modyfikacji zmian dokonanych przez wiele osób w różnych momentach. Mówiąc obrazowo, system kontroli wersji pozwala nam  "upilnować" swój kod. O ile pisząc w pojedynkę małe projekty, system wersjonowania nie przyda się zbytnio, o tyle pisząc coś większego (niezależnie czy samemu czy też w grupie), system kontroli wersji staje się niezbędny.

SVN posiada szereg zalet z których główne to:

  1. Pilnowanie zmian w nazwach plików i katalogów,
  2. Możliwość użycia serwera Apache lub dedykowanego serwera uruchamianego jako inetd albo osobny demon,
  3. Atomowość transakcji, dzięki czemu jesteśmy pewni, że każda zmiana jest zapamiętana w całości i żadna awaria w komunikacji nie jest w stanie zepsuć nam naszej pracy,
  4. Efektywne zarządzaniu repozytorium poprzez stosowanie aliasów, a nie kopiowanie kodu,
  5. Przywracanie dowolnej wersji repozytorium, bądź poszczególnych plików,
  6. Kontrola dostępu do poszczególnych plików (wiadomo kto, kiedy i jakich dokonał zmian),
  7. Możliwość jednoczesnej pracy wielu osób nad różnymi fragmentami kodu, bez ryzyka powstawania konflików.

SVN jest systemem wersjonowania scentralizowanym. Oznacza to że oparty jest on na architekturze klient-serwer, gdzie istnieje jedno repozytorium (źródło kodu) z którym wszyscy użytkownicy systemu synchronizują swoje zmiany. Istnieją także systemy typu P2P lecz nimi w tym artykule zajmować się nie będziemy.

Dlaczego warto używać właśnie SVNa? Oprócz plusów wymienionych parę linijek wyżej, SVN posiada jeszcze jedną bardzo ważną zaletę. Mianowicie jest sprawdzony i popularny. Dzięki temu że jest on popularny, istnieją programy wspomagające korzystanie z tego systemu, zarówno na systemie Linux jak i Windows. Można oczywiście korzystać z SVNa z pomocą wiersza poleceń, lecz na dłuższą metę nie jest to wygodne. Zanim przejdziemy jednak do opisu narzędzi wspomagających, skupmy się może na samym procesie instalacji.

Wymagania wstępne

Aby postawić SVNa i wszystkie związane z nim "bajery", musimy posiadać serwer. Fajnie by było jakby był na Debianie lub Ubuntu, ponieważ proces instalacji opiszę właśnie pod te systemy. Na początek ustalmy sobie co już mamy mieć:

  • Serwer i prąd
  • Podłączenie do internetu
  • Zainstalowany OS (Debian/Ubuntu)
  • Platformę LAMP (Linux, Apache, MySQL, PHP)
  • Trochę czasu ;)

Jakie są wymagania względem naszego systemu:

  • Możliwość zarządzania użytkownikami (prawa dostępu R i RW)
  • Możliwość tworzenia repozytoriów
  • Możliwość tworzenia grup użytkowników
  • Możliwość przeglądania kodu repozytorium z poziomu przeglądarki internetowej
  • Wszystko co wyżej tylko że wygodnie ;)

Aby osiągnąć powyższe cele, musimy zainstalować i skonfigurować następujące rzeczy:

  • Subversion
  • libapache2-svn (dodatek do Apache umożliwiający korzystanie z SVNa)
  • Submina
  • WebSVN

Instalacja

Proces instalacji wyżej wymienionych komponentów jest bardzo prosty. Aby zainstalować SVNa, wystarczy wpisać to:

sudo apt-get install subversion

Po tej operacji, mamy już dostęp do obsługi SVNa z linii poleceń. Jednak obsługę wiersza poleceń póki co pominiemy. Zajmijmy się dostępem przez http:

sudo apt-get install libapache2-svn

po instalacji musimy zresetować Apache:

sudo /etc/init.d/apache2 restart

Po tej operacji otrzymamy dostęp do naszych repozytoriów (o ile będą widoczne dla Apache) przez http.

Mając już SVNa i wsparcie dla niego z poziomu Apache, przejdźmy dalej. Zainstalujmy i skonfigurujmy sobie Submina. Submin jest przyjaznym interfejsem do zarządzania SVNem z poziomu przeglądarki internetowej. Umożliwia łatwe tworzenie repozytoriów, grup i użytkowników. Aby go zainstalować, musimy do pliku:

/etc/apt/sources.list

dodać następujące repozytorium oprogramowania:

deb http://debian.supermind.nl/ current main

Następnie aktualizujemy bazę oprogramowania oraz instalujemy submina (po każdej linijce zatwierdzamy naciskając enter):

sudo apt-get update
sudo apt-get install submin

Przy instalacji submina, możemy dostać info że pakiety nie są podpisane. Nie jest to nic groźnego (w tym przypadku), zatwierdzamy więc i kontynuujemy. Po instalacji paczek, musimy jeszcze utworzyć całą konfigurację. Zostanie ona utworzona w katalogu /etc/submin, a tworzymy ją następującym poleceniem:

sudo submin-admin create default

Po wygenerowaniu plików, musimy powiadomić Apache, że ma nowy plik konfiguracyjny do uwzględnienia. Najprościej zrobić to, tworząc link symboliczny:

sudo ln -s /etc/submin/default-apache-cgi.conf /etc/apache2/conf.d/submin.conf
# I reset apache
sudo /etc/init.d/apache2 restart

Aby móc korzystać z Submina, musimy jeszcze wyedytować plik konfiguracyjny /etc/submin/default.conf. Ma on wyglądać tak:

[svn]
authz_file = /var/lib/submin/authz
userprop_file = /var/lib/submin/userproperties.conf
access_file = /var/lib/submin/htpasswd
repositories = /var/lib/submin/svn

[www]
base_url = /submin
svn_base_url = /svn
trac_base_url = https://example.com/trac

[backend]
bindir = /usr/share/submin/bin

[generated]
session_salt = SÓL_GENEROWANA_AUTOMATYCZNIE

[trac]
enabled = False
basedir = /tmp

Obsługa tracka jest wyłączona. W sekcji [generated] klucz będzie wygenerowany automatycznie. Podaję cały plik konfiguracyjny, ponieważ nie pamiętam, które wiersze są generowane domyślnie, a które trzeba dopisać samemu. Po wszystkim resetujemy raz jeszcze Apache:

/etc/init.d/apache2 restart

Teraz wchodzimy pod adres http://adres_naszego_serwera/submin, logujemy się korzystając z domyślnego loginu: admin oraz hasła: admin i korzystamy :)

Pozostało nam zainstalowanie WebSVNa. Służyć on będzie administratorowi do przeglądania wszystkich repozytoriów. Dostęp do niego będzie miał główny administrator, z tego względu skorzystamy z najprostszej metody autoryzacji jaką jest AuthType Basic. Zanim jednak do tego przystąpimy, zainstalujmy WebSVNa:

# To nie jest wymagana - koloruje w websvnie składnię
sudo apt-get install enscript
# To już jest wymagane :)
sudo apt-get install websvn

W czasie instalacji, zostaniemy zapytani o kilka rzeczy. Pierwsze pytanie dotyczyć będzie tego, czy WebSVN ma się skonfigurować:

Websvn1

Akceptujemy konfigurację zaznaczając Yes i naciskając Enter. Na następnym ekranie także naciskamy Enter:

Websvn2

Po zatwierdzeniu przechodzimy do trzeciego ekranu, w którym musimy podać miejsce gdzie znajdują się nasze repozytoria:

Websvn3

W tym miejscu wpisujemy adres, gdzie Submin trzyma repozytoria. W naszym wypadku jest to: /var/lib/submin/svn

Zatwierdzamy naciskając Enter. Następne okno zostawiamy puste. Od tego momentu mamy dostęp do WebSVNa pod adresem: http://adres_naszego_serwera/websvn/.

Po wejściu pod powyższy adres ukaże się nam panel. Póki co mamy z nim dwa problemy:

  1. Wyświetla się nieistniejące repozytorium o nazwie "Repos 1"
  2. System nie pyta ani o login ani o hasło

Rozwiązanie pierwszego problemu opisałem w osobnym poście (link).

Jeśli chodzi o drugi problemy, rozwiązaniem jest dodanie autoryzacji. Z pomocą przyjdzie nam sam Apache. Otwieramy plik /etc/websvn/apache.conf i edytujemy go aby wyglądał tak:

# Configuration for websvn using php4.

Alias /websvn /usr/share/websvn

<Directory /usr/share/websvn>
  DirectoryIndex index.php
  Options FollowSymLinks
  Order allow,deny
  allow from all
  AuthType Basic
  AuthName "Subversion Repository"
  AuthUserFile /etc/apache2/dav_svn.passwd
  Require valid-user
  <IfModule mod_php4.c>
    php_flag magic_quotes_gpc Off
    php_flag track_vars On
  </IfModule>
</Directory>

Włączyliśmy w ten sposób autentykację. Dane do weryfikacji brane są z pliku /etc/apache2/dav_svn.passwd, którego póki co nie ma. Aby go utworzyć przechodzimy do katalogu /etc/apache2/, po czym wydajemy następujące polecenie:

# Podstaw za username własny login
sudo htpasswd -c dav_svn.passwd username

Dostaniemy monit o podanie hasła oraz jego potwierdzenie. Po tym plik zostanie utworzony a my będziemy cieszyć się autoryzacją do naszego WebSVNa.

To by było na tyle. Nie pozostaje nam nic innego jak cieszyć się całkiem sprawnie działającym systemem wersjonowania projektów.

Rails + MySQLDump czyli robienie kopii zapasowej bazy danych z poziomu Railsów

Zabezpieczenie systemu jest (jak nie muszę przekonywać) bardzo ważną sprawą. Jedną z podstawowych metod zapewnienia bezpieczeństwa systemu jest regularne wykonywanie kopii zapasowych bazy danych. Wprawdzie w Ruby on Rails nie istnieje ujednolicony interfejs do wykonywania tej czynności, jednak bardzo prosto można napisać własny model wykonujący dumpa.

Model jest bardzo prosty. Jeśli chodzi o migrację to zawiera ona tylko timestampy (created_at, updated_at). Jeśli chodzi o klasę, to jej szkielet wygląda tak:

# coding: utf-8

require 'fileutils'

# Służy do wykonywania kopii zapasowych plików oraz bazy danych
# Działa dla MySQLa - robi dumpa bazy
class Backup < ActiveRecord::Base
  # Zawartość
end

Dalej nie jest trudniej :) Musimy ustalić prefiks dla nazwy pliku, który będzie przechowywał nasz zrzut:

  # Prefix do nazwy backupu bazy danych
  DB_NAME_PREFIX = 'db_'

Oraz nadpisać metodę create, która oprócz utworzenia wpisu w bazie danych, utworzy nam także nasz zrzut.

Aby wykonać zrzut, musimy mieć dostęp do kilku informacji:

  • Nazwa bazy
  • Login
  • Hasło
  • Host
  • Możliwość wykonywania poleceń zewnętrznych
  • Ścieżka do backupu

Tak naprawdę, nie potrzebujemy niczego szczególnego. Zanim jednak przystąpimy do tworzenia naszej metody create, omówmy sobie jeszcze jak ma wyglądać przechowywanie kopii zapasowych.

Proponowałbym utworzyć logiczną hierarchię dla plików i katalogów. W głównym katalogu aplikacji, tworzony będzie katalog backup/. W nim będą subkatalogi, których nazwy składać się będą z daty oraz czasu wykonania kopii. W środku takiego subkatalogu będzie spakowany zrzut bazy.

Idąc za ciosem, zdefiniujmy sobie metodę prywatną, która sprawdzi nam czy katalog backup/ oraz jego subkatalog istnieją. Metoda ta jest na tyle prosta, że nie ma co jej opisywać. Komentarze powinny wystarczyć:

  # Sprawdzamy czy ścieżka gdzie ma być backup istnieje i jeśli nie to ją
  # utworzymy
  # Jako parametr podajemy czas - to jest nazwa folderu jako sygnatura czasowa
  # momentu rozpoczecia backupowania, czas w formacie Time.now.strftime("%Y\-%m\-%d_%H\-%M\-%S")
  def check_path(time)
    # Sprawdzajmy czy struktura katalogow jest cala - od poczatku (wiem że to
    # narzut tych kilku sprawdzen, ale tak jest prościej)
    full_backup_path = "#{Rails.root}/backup/#{time}/"
    check_path = ''
    full_backup_path.split('/').each { |el|
      check_path += "#{el}/"
      Dir.mkdir(check_path) unless File.directory?(check_path)
    }
  end

Mając już metodę, która zapewni nam, że będzie gdzie przechowywać plik ze zrzutem, możemy przystąpić do pisania metody create.

Pierwszą rzeczą jaką musimy zrobić, jest zapamiętanie czasu w którym przystąpiliśmy do tworzenia backupu. Jest to bardzo istotne, ponieważ jeśli operowalibyśmy na Time.now mógłby on różnić się dla subkatalogu oraz nazwy pliku (i ew. wpisu w bazie), co prowadziłoby do wielu problemów. Ominiemy je jednak wykonując na początku metody taki fragment kodu:

  # Nadpisujemy domyślne create żeby wykonywało kopię
  def create
    # Potrzebujemy jeden czas dla plików i dla utworzenia obiektu modelu Backup
    time = Time.now
    super
    self.created_at = time
    self.save
    # Musimy mieć czas w takim formacie aby dało się tym nazwać pliki
    file_time = time.strftime("%Y\-%m\-%d_%H\-%M\-%S")

Zapamiętaliśmy w zmiennej time aktualny czas, zapisaliśmy go także jako wartość created_at dla backupu. Na końcu konwertujemy czas do formatu który będzie stanowił nazwę dla naszego subkatalogu (np 2010-09-20_19-12-56).

Mając już "globalny" czas, możemy sprawdzić (i jeśli trzeba - a trzeba na pewno ;)) i utworzyć strukturę katalogów dla kopii zapasowej. W tym celu wywołamy następujący kod (który napisaliśmy wcześniej):

     # Jeśli nie ma katalogu na backupy to go utwórz
    check_path file_time

Ustalmy teraz jak ma nazywać się plik z kopią zapasową bazy i gdzie ma się znajdować:

    # Ścieżka gdzie ma zapisać plik bazy danych
    backup_db = "#{Rails.root}/backup/#{file_time}/#{DB_NAME_PREFIX}#{file_time}.sql.bz2"

Teraz pozostało nam tylko wyciągnąć parametry dostępowe do bazy oraz wykonać dump:

    db = self.configurations[Rails.env]['database']
    db_user = self.configurations[Rails.env]['username']
    db_pass = self.configurations[Rails.env]['password']
    db_host = self.configurations[Rails.env]['host']

    # Zrzut bazy danych
    exec "mysqldump --add-drop-table -u #{db_user} -p#{db_pass} -h #{db_host} #{db} | bzip2 -c > #{backup_db}" if fork.nil?

Baza po zrzucie zostanie spakowana.

Mamy już kopię zapasową oraz odpowiadający jej model. Musimy jednak zapewnić sobie możliwość usuwania powstałych kopii. Aby tego dokonać nadpiszemy metodę destroy w taki sposób aby usuwając instancję modelu, usuwała także korespondujący katalog z kopią zapasową:

  # Usuwa stary backup oraz zadane pliki do niego
  def destroy
    return if self.new_record?
    dir = "#{Rails.root}/backup/#{self.created_at.strftime("%Y\-%m\-%d_%H\-%M\-%S")}"
    # Usuwamy katalog z backupem
    FileUtils.rm_rf(dir) if File.directory?(dir)
    super
  end

To by było na tyle. Zachęcam Was do utworzenia w analogiczny sposób, kodu odpowiedzialnego za backupowanie plików userów - np katalogu /public/uploads/.

Copyright © 2025 Closer to Code

Theme by Anders NorenUp ↑