Quota na katalog w XFS’ie

Większość systemów plików w linuksie pozwala na ustawienie quoty na dwóch poziomach: na użytkownika lub na grupę użytkowników. W wielu przypadkach taki podział jest sensowny i wystarczający. Ale zdarzają się scenariusze, w których to za mało.

Dobrym przykładem jest serwer FTP z wirtualnymi kontami użytkowników. Czyli usługa serwera działa jako pewien nieuprzywilejowany użytkownik systemowy (przeważnie ftp) przypisany do nieuprzywilejowanej grupy (np. nogroup). Konta użytkowników serwera FTP są zdefiniowane w bazie danych lub serwerze LDAP. Takie konta nazywa się wirtualnymi ponieważ po autoryzacji w pewnym systemie (bazie danych, LDAP’ie) działają z uprawnieniami pewnego systemowego konta (w tym przypadku ftp) – nie ma więc odzwierciedlenia pomiędzy użytkownikami korzystającymi z ftp a kontami systemowymi.
Problemem w takim układzie jest to, że nie można rozróżnić poszczególnych użytkowników wirtualnych, ponieważ wszystkie operacje są wykonywane przez systemowego użytkownika ftp i to on jest właścicielem wszystich plików utworzonych przez użytkowników wirtualnych.

Rozwiązania są dwa:
– sewer FTP ma zaimplementowaną obsługę quot dla użytkowników wirtualnych, z czego możemy skorzystać,
– tworzymy quotę na katalog domowy użytkownika wirtualnego.

Mnie bardziej odpowiadała druga metoda.

O ile mi wiadomo w ext3 nie można tworzyć quoty per katalog, na moje szczęscie taką możliwość oferuje xfs, z którego korzystałem.

Jeżeli jeszcze nie masz dysku sformatowanego jako xfs to jest to dobry moment aby to zrobić:

# sdaX zastap nazwa urzadzenia ktore TY chcesz sformatowac
mkfs.xfs /dev/sdaX

# aby dzialala quoata per katalog trzeba zamonowac
# dysk z opcja prjquota
mount /dev/sdaX /mnt/ftp -o prjquota

Logika quot per katalog w xfs’ie jest taka, że najpierw tworzymy projekt z powiązanym z nim id, a później wiążemy poprzez id projekt z katalogiem. Przez co quota nałożona na projekt ma zastosowanie do katalogu. Konfiguracja projektów i przypisanych im katalogów znajduje się w dwóch plikach: /etc/projid i /etc/projects.

Utwórzymy teraz projekt i konfigurację dla niego:

# nazwa projektu i jego id - w naszym przypadku
# konto uzytkownika romana :)
echo ftproman:10 >> /etc/projid

# projektowi z id 10 przypisujemy katalog /mnt/ftp/roman
echo 10:/mnt/ftp/roman >> /etc/projects

Pozostało uruchomić quotę wpisująć komendy:

# wiazemy projekt z punktem montowania
xfs_quota -x -c 'poject -s ftproman' /mnt/ftp

# ustawiamy wlasiwa quote 1 gigabajt dla projektu
xfs_quota -x -c 'limit -p bhard=1g ftproman' /mnt/ftp

To tyle – quota na katalog jest założona i działa. Aby zobaczyć jaka część miejsca jest już wykorzystana wystarczy uruchomić polecenie:

xfs_quota -x -c 'report -h /mnt/ftp'

Project quota on /mnt/ftp (/dev/sda3)
                        Blocks
Project ID   Used   Soft   Hard Warn/Grace
---------- ---------------------------------
ftproman     1,5M      0     1G    00 [------]

Dla kolejnych użytkowników musimy tworzyć kolejne wpisy.

Klastrowanie sesji PHP z memcached

Klastrowanie to może zbyt dumnie powiedziane. Rozwiązanie to wyszukałem gdy chcąc skonfigurować dwa serwery apache do współpracy na rzecz jednego serwisu okazało się, że sejse trzymane są tylko przez jeden serwer a drugi nic o nich nie wie. To oczywiście nie pozwalało na prawidłowe działanie jakiegokolwiek serwisu korzystającego z sesji.

Pomysł jest taki, że zastępujemy domyśny mechanizm przechowywania sesji w plikach na dysku mechanizmem memcache. Ponieważ memcached działa jako usługa sieciowa, różne serwery mogą się odwoływać do puli memcached i odczytywać zapisane w niej dane. W przypadku sesji – nie jest ważne, kto ją utworzył – bo po jej wysłaniu do puli memcached staje się dostępna dla wszystkich klientów php z niej korzystających.

Jednym z pierwszych pytań nasuwających się do takiej kofiguracji jest: a co jeśli serwer memcached padnie? W chwili gdy wiele serwerów apache zależy od jednego serwera memcached jego awaria unieruchamia kaskadowo wszystkie.

Dlatego wykorzystałem konfigurację z dwoma serwerami memcached. Gdy php zapisuje dane w puli memcached dane są wysyłane do wszystkich podanych serwerów (w tym przypadku dwóch). A odczytywanie polega na odpytaniu pierwszego podanego serwera, a jeśli to się nie uda to drugiego. Układ nie jest idealny (jak mamy dwa działające serwery to aż się prosi o loadbalancing) ale zmniejsza prawdopodobieństwo, że awaria pojedynczego elementu położy wszystko.

Z moim problemem moża było sobie poradzić też inaczej, np. zmieniając mechanizm sesji na stronie na taki, który korzysta z bazy danych. O ile w przypadku jednego serwisu nie jest to duży kłopot, to już przy kilku/kilkunastu byłoby to już spore przedsięwzięcie.

Ważną zaletą tego rozwiązanie jest fakt, że nie są wymagane żadne zmiany w istniejących serwisach. Po zmianie mechanizmu w konfiguracji php wszystko powinno działać bez zmian.

Instalacja/Konfiguracja

Najpierw trzeba zainstalować i uruchomić memcached oraz rozszerzenie php5-memcachedo php’a, które da nam możliwość korzystania z niego. U mnie robi się to tak:

apt-get install memcached php5-memcache

Teraz trzeba by wyedytować konfigurację /etc/memcached.conf. Poniżej wycinek z tego pliku z opcjami, które należy ustawić:

# Maksymalna wartość pamięci w MB jaka może być
# wykorzystana przez demona.
# Warto dostosować do swoich potrzeb.
-m 128

# Interfejs, na którym nasłuchiwać będzie usługa.
# Ja dla wygody wybiorę wszystkie :)
-l 0.0.0.0

# można też dostosować port do nasłuchiwania
-p 11211

# użytkownika nieuprzywilejowanego
# (memcached domyślnie startuje jako root)
-u nobody

Wprowadzamy i zapisujemy zmiany. Trzeba zrestartować serwer memcached uruchamiając:

invoke-rc.d memcached restart

Do tego momentu musimy powtórzyć konfigurację na drugiej maszynie (bo inaczej po co klastrować sesje).

Teraz trzeba skonfiguraować php’a aby zamiast używać sesji zapisywanych do plików, korzystał z memcached. Edytujemy php.ini, u mnie akurat w lokalizacji:/etc/php5/apache2/php.ini. Odszukujemy opcje:

session.save_handler = files
;session.save_path = /var/lib/php5

I zamieniamy na:

session.save_handler = memcache
; adresy oczywiście należy dostosować do własnych ustawień
session.save_path = "tcp://localhost:11211, tcp://remotehost:11211"

W kolejnym kroku edytujemy plik konfiguracyjny rozszerzenia php’a dla memcache w /etc/php5/conf.d/memcache.ini dodając takie ustawienia:

extension=memcache.so

[memcache]
memcache.dbpath="/var/lib/memcache"
memcache.maxreclevel=0
memcache.maxfiles=0
memcache.archivememlim=0
memcache.maxfilesize=0
memcache.maxratio=0

; to jedyna wymagana opcja - resztę można dostosować pod siebie
; albo zostawić domyślnie
memcache.allow_failover=1

memcache.max_failover_attempts=20
memcache.default_port=11211
memcache.chunk_size=8192
memcache.hash_strategy=standard
memcache.hash_function="crc32"

Więcej po poszczególnych opcjach można się dowiedzieć z dokumentacji php.

Test

Jeżeli zrobiliśmy wszystko jak trzeba to sesje powinny zapisywać się z pamięci memcachedi dystrybuować na wszystkie wpisane w polu save_path serwery. Możemy to sprawdzić wykorzystując np. taki skrypt:

<?php
session_start();

print "Opcja save_handler: "
     . ini_get("session.save_handler") . "<br>";
print "Opcja save_path: "
     . ini_get("session.save_path") . "<br>";

if(isset($_SESSION['testowa'])) {
    print "Testowa sesja jest już ustawiona: " .
        $_SESSION['testowa'] . "<br>";
} else {
    $_SESSION['testowa'] = "i wygląda, że działa dobrze";
    print "Ustawiamy testową sesją wartością: " .
        $_SESSION['testowa'] . "<br>";
}
?>

Wystarczy odświeżyć skrypt kilka razy. Za pierwszym razem sesja zostanie ustawiona a kolejne odświeżenia będą już zwracały jej wartość.

Proponuję też wyłączyć jeden z serwerów memcached aby sprawdzić czy php poprawnie odwoła się do drugiego serwera.

Włam na lokalne konto root’a

Jeżeli tu zaglądasz pewnie zdarzyło Ci się kiedyś, że przykładowo wygrzebujesz jakiś stary serwer i nie masz pojęcia co na nim było, ani do czego służyło, czy jeszcze działa… Albo jeszcze inaczej – serwer działał tak długo, że wszystkie osoby znające hasło na root’a przeszły na emeryturę lub zmarły… Nieistotne 🙂

Jest pewna prosta sztuczka, pozwalająca wbić się na konto root’a nie znając hasła – dając nam możliwość jego zmiany. Potrzebne dwa restarty ale za to nie trzeba korzystać z żadnychlive cd.

  1. Na początek zmuszamy serwer do restartu – mieć nadzieję, że maszyna obsługuje ACPI i delikatne wciśnięcie przycisku power subtelnie ją wyłączy. Jeśli to nie zadziała to kojarzą mi się tylko brzydkie rzeczy 🙂
  2. Gdy po restarcie załaduje się grub na domyślnej opcji bootowania wybieramy edycję wciskając “e“.
  3. Wybieramy linię zaczynającą się od kernel i znów wybieramy edycję wciskając “e“.
  4. Jeżeli znajduje się tam parametr ro to zastępujemy go rw i dopisujemy na końcu init=/bin/bash
  5. Wbijamy “enter” zapisując zmieniony wiersz.
  6. Bootujemy się z tak zmienionej konfiguracji wciskając “b“.
  7. Po chwili system zamiast wystartować init’a i uruchamiać usługi, ląduje w bash’u z uprawnieniami root’a. A skoro mamy root’a to możemy wpisać passwd i zmienić rootowi hasło 🙂
  8. Teraz już tylko reboot i startujemy system normalnie – hasło root’a powinno działać.

Niestety ta prosta sztuczka nie działa na wszystkich linux’ach – szczególnie tych wykorzystujących initramfs-tools. Na tych systemach trzeba ciut więcej pokombinować ale przynajmniej ma się jakiś punkt wyjścia.

MySQL – dostęp zdalny na szybko

Instalacja serwera MySQL na Debianie jest niezwykle prosta i sprowadza się do jednego polecenia:

sudo apt-get install mysql-server

Polecenie to zainstaluje i uruchomi usługę serwerową MySQL. W czasie instalacji będziemy proszeni o podanie hasła dla root’a (które oczywiście dobrze jest zapamiętać bądź zapisać).

Tak zainstalowana baza nasłuchuje na lokalnym porcie (localhost:3306) umożliwiająć dostęp wyłącznie root’owi. Jest to bardzo bezpieczna konfiguracja… Ale jeśli nie mamy zamiaru na tej samej maszynie instalować oprogramowania zarządzającego to nie zawsze jest to wygodne, tym bardziej gdy przykładowo mamy działającego phpmyadmin’a na jakimś serwerze www. W takim przypadku pierwszą rzeczą, którą robię jest udostępnienie dostępu zdalnego dla root’a. Warto zaznaczyć że uprawnienia root’a można nadać dowolnemu użytkownikowi (np. romanowi) co jest dużo bezpieczniejszą konfiguracją niż działanie bezpośrednio na koncie root’a (którego nazwa jest powszechnie znana).

Dostęp zdalny dla root’a

Aby umożliwić zdalne zalogowanie się do bazy z uprawnieniami root’a trzeba ustawić odpowiednie GRANT’y, robimy to tak:

mysql -u root -p

mysql> GRANT ALL PRIVILEGES ON *.* TO 'roman'@'%'
> IDENTIFIED BY 'haslo dla zdalnego roota' WITH GRANT OPTION;
mysql> FLUSH PRIVILEGES;
mysql> exit

Pierwsze polecenie połączy nas z bazą prosząc o hasło podane w czasie instalacji.

Kolejne to polecenia SQL’owe, które pozwalają użytkownikowi ‘roman’ łączącemu się z hosta ‘%’ (dowolnego) identyfikującemu się hasłem ‘haslo dla zdalnego roota’. Jeżeli chcemy ograniczyć dostęp do tylko jednego zdalnego adresu to zamiast znaku procenta wpisujemy ten adres IP. Tak dodany użytkownik ma też prawo nadawania uprawnień (GRANT OPTION). Polecenie FLUSH PRIVILEGES przeładowuje uprawnienia – umożliwiając logowanie z podanymi wcześniej uprawnieniami.

Pozostało nam zmienić ustawienia serwera tak aby nasłuchiwał nie tylko na localhoście. W tym celu edytujemy plik /etc/mysql/my.cnf:

sudo vim /etc/mysql/my.cnf

Odszukujemy następującą linię:

bind-address = 127.0.0.1

Linię tę możemy zakomentować co będzie skutkować nasłuchiwaniem przez serwer na wszystkich skonfigurowanych adresach IP (taki sam efekt da wpisanie w polu adresu 0.0.0.0). Można też wpisać tylko jeden adres IP w przypadku gdy na serwerze jest ich kilka i nie chcemy aby serwer był dostępny na wszystkich.

Ostatnim krokiem jest zrestartowania serwera MySQL aby zadziałały wprowadzone w pliku konfiguracyjnym zmiany. Można to zrobić tak:

invoke-rc.d mysql restart

Jeżeli właśnie założyłeś i udostępniłeś nowy serwer bazodanowy MySQL to oszczędź sobie pracy w przyszłości i od razu ustaw przechowywanie tabel InnoDB w osobnych plikach.