Etykiety

linux (14) php (14) Laravel (9) mysql (9) Hardware (8) Windows (6) sieci (5) PowerShell (4) software (4) MariaDB (3) apache (3) html (3) Microsoft (2) bezpieczeństwo LAN (2) cygwin (2) Akcesoria (1) CMS (1) Laptop (1) Open Office (1) drupal 7 (1) gpg (1) hosting (1) jquery (1) sieci LAN (1) xml (1) zabezpieczenie sieci LAN (1)

wtorek, 15 września 2015

Apache mod_authn_dbd: uwierzytelnianie HTTP z wykorzystaniem bazy danych MySql

W niniejszym artykule opiszę jak skonfigurować i wymusić uwierzytelnianie HTTP z poziomu serwera Apache, z użytkownikami i hasłami zapisanymi w tabeli bazy danych MySql. Pomimo iż zadanie wygląda na dość skomplikowane, poradzenie sobie z nim nie wymaga dużej ilości pracy.

Opisane tutaj zagadnienia opracowałem, przetestowałem i zademonstruję w następującym środowisku Linux:

System operacyjny Scientific Linux 7;
Kernel 3.10.0-229.11.1.el7.x86_64;
Serwer www Apache v. 2.4.6;
Server MySql mariadb v. 5.5.44;
apr-util-mysql v. 1.5.2 – pakiet sterownika MySql dla abstrakcyjnego interfejsu DBD bazy danych

Aby zrozumieć i prawidłowo zastosować opisywane tutaj rozwiązania, wymagana jest od czytelników następująca wiedza:

dobra znajomość podstawowych zagadnień Linux;
dobra znajomość konfiguracji serwera Apache;
dobra znajomość serwera i klienta MySql.

Należy zwrócić uwagę, iż w celu przeprowadzenia opisanej tutaj konfiguracji, serwer Apache wymaga obecności modułów mod_auth_basic, mod_authn_core, mod_authn_file, mod_authn_socache, mod_dbd oraz mod_authn_dbd. W celu wdrożenia uwierzytelniania HTTP z wykorzystaniem tabeli MySql pakiet apr-util-mysql powinien zostać zainstalowany.

Jeżeli nie mamy pewności co do obecności wymaganych modułów należy się upewnić czy są one dostępne dla serwera, np. za pomocą narzędzia httpd z parametrem -M.

Aby wyjaśnić zagadnienie w dość przejrzysty i łatwy do zrozumienia sposób, podzielę całą procedurę na dwa etapy. W pierwszym kroku skonfiguruję proste uwierzytelnianie HTTP oparte na funkcjonalności modułu Apache mod_auth_basic, w drugim zaś kroku zmodyfikuję nieco pierwotną konfigurację, dodając do niej funkcjonalność odczytu informacji o użytkownikach z tabeli MySql.

Apache Web Server
Apache Web Server

Krok 1 - Apache mod_auth_basic: podstawowe uwierzytelnianie HTTP Apache


W tym kroku zademonstruję jak przykładowo skonfigurować podstawowe uwierzytelnianie HTTP z poziomu serwera Apache, za pomocą modułu mod_auth_basic. Należy zwrócić uwagę, że zasady bezpieczeństwa wymagałyby jednoczesnego stosowania szyfrowania SSL, co w połączeniu z uwierzytelnianiem mod_auth_basic przyniosłoby naprawdę wysoki poziom bezpieczeństwa jeśli chodzi o przesyłanie nazw użytkowników i haseł w sieci. Zależy zwrócić uwagę, że stosowanie modułu mod_auth_basic wraz z szyfrowaniem SSL jest o wiele bezpieczniejsze niż stosowanie uwierzytelniania opartego o sam moduł mod_auth_digest, który niegdyś był jednak o wiele bezpieczniejszy niż mod_auth_basic, ale dziś już tak nie jest.

Przytoczę przykład podstawowego uwierzytelniania HTTP dla ścieżki /var/www/html.

Aby rozpocząć należy dodać do pliku httpd.conf następujące linie:
<Directory "/var/www/html/zabezpieczony">
    AllowOverride None
    AuthType Basic
    AuthName "Strefa zabezpieczona"
    AuthBasicProvider file
    AuthUserFile /etc/httpd/users_zabezpieczony
    Require user michael
</Directory>
Co oznaczają te wszystkie dyrektywy? Otóż ich znaczenie jest następujące:

Directory: w sekcji <Directory>, która powinna być ograniczona znacznikami <Directory "katalog"></Directory>, umieszcza się ustawienia dotyczące określonego katalogu. 

AllowOverride: ta ważna dyrektywa z parametrem None zabezpiecza konfigurację katalogu przed nadpisaniem jej ustawień wpisami w pliku .htaccess, umieszczonym bezpośrednio w danym katalogu. Warto tutaj zwrócić uwagę, że w przypadku konieczności stosowania ustawień dotyczących uwierzytelniania bezpośrednio w pliku .htaccess, na przykład z powodu ograniczonego dostępu do plików konfiguracyjnych serwera Apache, wówczas w pliku httpd.conf, w sekcji dotyczącej danego katalogu, powinna się znajdować dyrektywa AllowOverride z parametrem AuthConfig.

AuthType Basic: określa metodę uwierzytelniania HTTP. W tym przypadku jest to podstawowe uwierzytelnianie.

AuthName: dyrektywa określa nazwę, która ma się wyświetlać w okienku uwierzytelniania. W naszej przykładowej konfiguracji jest to „Strefa zabezpieczona”.

AuthBasicProvider: dyrektywa określa metodę przechowywania nazw użytkowników i haseł dla skonfigurowanego dla danego katalogu uwierzytelniania HTTP. W naszej przykładowej konfiguracji ustawiamy metodę "file", co oznacza, że nazwy użytkowników i ich hasła mają być przechowywane w specjalnym pliku, który za chwilę utworzymy.

AuthUserFile: określa lokalizację pliku z nazwami użytkowników i ich zaszyfrowanymi hasłami. W naszej przykładowej konfiguracji jest to /etc/httpd/users_zabezpieczony

Require: dyrektywa określająca uprawnionych użytkowników, którzy mają mieć dostęp do katalogu. W naszej przykładowej konfiguracji dostęp do katalogu ma mieć wyłącznie użytkownik o nazwie michael.

Jeżeli katalog /var/www/html/zabezpieczony jeszcze nie istnieje to należy go teraz utworzyć. Własność i uprawnienia do katalogu powinny wyglądać następująco:
drwxr-xr-x 2 apache apache    6 Sep 14 09:00 zabezpieczony
Następnie należy utworzyć plik przechowujący nazwy użytkowników i ich zaszyfrowane hasła. Należy to zrobić za pomocą polecenia htpasswd:
[michael@sphera3]$ htpasswd -c /etc/httpd/users_zabezpieczony michael
New password:
Re-type new password:
Adding password for user michael
Dla wygenerowanego pliku należy następnie ustawić odpowiedniego właściciela, grupę, oraz uprawnienia:
-rw-r-----   1 apache apache   46 Sep 14 09:04 users_zabezpieczony
Nowe ustawienia, zapisane w pliku konfiguracyjnym serwera Apache, zaczną obowiązywać po ponownym uruchomieniu serwera, a więc:
[michael@sphera3]$ systemctl restart httpd
Zgodnie z przeprowadzoną konfiguracją próba dostępu do zabezpieczonego katalogu www uruchamia proces uwierzytelniania HTTP. Aby się o tym przekonać wystarczy wpisać do paska adresów przeglądarki adres serwera i ścieżkę prowadzącą do zabezpieczonego katalogu.

Krok 2 – Apache mod_authn_dbd: uwierzytelnianie HTTP Apache z użytkownikami w tabeli MySql


W tym kroku nauczymy się jak skonfigurować serwer Apache do uwierzytelniania HTTP, tak aby nazwy i zaszyfrowane hasła użytkowników mogły być przechowywane w odpowiedniej tabeli bazy danych MySql. Tego typu rozwiązanie jest nieco bardziej złożone niż to, które zostało opisane w poprzednim kroku.

Uwaga: w sieci można znaleźć obfitą ilość instruktarzy dotyczących tego zagadnienia. Wiele z nich, jak udało mi się ustalić, prowadzi do różnego rodzaju błędów, które mogą powodować. Przykładowo, najczęściej powodują one generowanie następujących komunikatach o błędach w logach serwera Apache:
[dbd:crit] [pid 9253] (20014)Internal error: AH00636: child init failed! [dbd:error] [pid 9250] (20014)Internal error: AH00629: Can't connect to mysql: Access denied for user 'apache'@'localhost' (using password: NO)
Aby prawidłowo skonfigurować uwierzytelnianie HTTP Apache z wykorzystaniem bazy danych MySql, poprzednią konfigurację uwierzytelniania HTTP dotyczącą naszego katalogu należy zmienić w następujący sposób:
DBDriver mysql
DBDParams "host=localhost,dbname=apache,user=apache,pass=tajne_haslo_apacha"
DBDMin  1
DBDKeep 1
DBDMax 10
DBDExptime 300
DBDPersist On

<Directory "/var/www/html/zabezpieczony">
  AuthType Basic
  AuthName "Strefa zabezpieczona"
  AuthBasicProvider socache dbd
  AuthnCacheProvideFor dbd
  AuthnCacheContext strefa_zabezpieczona
  Require valid-user
  AuthDBDUserPWQuery "SELECT password FROM authn WHERE user = %s"
</Directory>
Powyżej przedstawione dyrektywy DBD powinny znajdować się w pliku httpd.conf i nie mogą być zamknięte w jakichkolwiek konkretnych sekcjach, np. sekcjach <Directory>. Dyrektywy DBD określają sterownik bazy danych, nazwę użytkownika i hasło oraz kilka innych parametrów związanych z serwerem bazodanowym.

Co oznaczają dyrektywy z powyższej sekcji <Directory>, zwłaszcza, że jest ich o wiele więcej niż w przykładzie z pierwszego kroku?

AuthBasicProvider: dyrektywa ta, wraz z parametrami socache i dbd instruuje serwer Apache do odczytywania nazw użytkowników i ich haseł z serwera bazy danych oraz, w miarę możliwości, ze specjalnej pamięci podręcznej (mod_authn_socache) w celu zmniejszenia częstotliwości odczytu tabeli bazy danych.

AuthnCacheProvideFor oraz  AuthnCacheContext: te dyrektywy związane są z konfiguracją specjalnej pamięci podręcznej dla zabezpieczanego katalogu, jak wyjaśniłem powyżej.

AuthDBDUserPWQuery: ta dyrektywa określa zapytanie SQL stosowane do odczytania hasła dla nazwy użytkownika wprowadzonej w trakcie procesu uwierzytelniania.

Znaczenie pozostałych dyrektyw konfiguracji opisywanej w tym kroku przedstawiłem w wyjaśnieniach do pierwszego kroku naszej konfiguracji.

Po zapisaniu powyżej przedstawionej konfiguracji należy ponownie uruchomić serwer Apache.

Należy teraz utworzyć bazę danych i tabelę MySql, które będą wykorzystywane w procesie uwierzytelniania HTTP, zgodnie z ustawionymi w powyższej konfiguracji parametrami dyrektyw DBDParams oraz AuthDBDUserPWQuery. W tym celu należy uruchomić klienta MySql jako użytkownik root i wydać następujące polecenia:
MariaDB [(none)]> create database apache;
Query OK, 1 row affected (0.00 sec)

MariaDB [(none)]> use apache;
Database changed
MariaDB [apache]> create table authn (
    -> user varchar(20) not null,
    -> password char(38) not null ) engine = InnoDB charset utf8;
Query OK, 0 rows affected (0.06 sec)
W tym przykładzie zastosuje szyfrowanie haseł metodą MD5. Należy zwrócić szczególną uwagę na fakt, iż przechowywane w tabeli bazy danych hasła użytkowników muszą być zakodowane w jednym z rozpoznawanych przez Apache formatów haseł! Dlatego pole password ma mieć długość 38 znaków, zamiast typowej dla MD5 długości 32 znaków. Jeśli hasła mają być zakodowane algorytmem MD5, nie wystarczy zakodować ich tym algorytmem z wiersza poleceń MySql! Apache obsługuje swój specyficzny format MD5. Aby dowiedzieć się więcej na ten temat warto przeczytać ten dokument.

Dla celów niniejszego przykładu zakoduję hasła w specyficznym dla Apache formacie algorytmu MD5, posługując się w tym celu narzędziem htpasswd:
[michael@sphera3]$ htpasswd -nbm HTTPUser1 HTTPPassword1
HTTPUser1:$apr1$IWNLcKto$RkkBvlQ.cAS85wjHi8mUu/

[michael@sphera3]$ htpasswd -nbm HTTPUser2 HTTPPassword2
HTTPUser2:$apr1$mYBsnAnx$of8v/VS.ja1i0I2dh0j8n1
Następnie należy wprowadzić kilka przykładowych użytkowników z zaszyfrowanymi w formacie Apache-md5 hasłami do nowo utworzonej tabeli:
MariaDB [apache]> insert into authn values('HTTPUser1',”$apr1$IWNLcKto$RkkBvlQ.cAS85wjHi8mUu/”),('HTTPUser2',”$apr1$mYBsnAnx$of8v/VS.ja1i0I2dh0j8n1”);
Query OK, 2 rows affected (0.01 sec)
Records: 2  Duplicates: 0  Warnings: 0
Nasza przykładowa tabela powinna teraz zawierać 2 wiersze danych:
MariaDB [apache]> select * from authn;
+---------------+---------------------------------------------------------------+
| user               | password                                                                     |
+---------------+---------------------------------------------------------------+
| HTTPUser1  | $apr1$IWNLcKto$RkkBvlQ.cAS85wjHi8mUu/      |
| HTTPUser2  | $apr1$mYBsnAnx$of8v/VS.ja1i0I2dh0j8n1             |
+---------------+---------------------------------------------------------------+
2 rows in set (0.00 sec)
Po pomyślnym wprowadzeniu nazw użytkowników i ich zaszyfrowanych haseł do tabeli authn, należy utworzyć użytkownika apache@localhost dla serwera MySql, nadać mu hasło zgodnie z dyrektywą DBDParams naszej przykładowej konfiguracji i uprawnić użytkownika apache@localhost do odczytywania danych z tabeli authn:
MariaDB [apache]> grant usage on *.* to apache@localhost identified by "tajne_haslo_apacha";
Query OK, 0 rows affected (0.00 sec)

MariaDB [apache]> grant select on apache.authn to apache@localhost;
Query OK, 0 rows affected (0.00 sec)
Wszystko jest już gotowe i można sprawdzić czy rzeczywiście konfiguracja działa. Utwórzmy testowy plik index.html zawierający jakąkolwiek treść, np. „HELLO” i wrzućmy go do zabezpieczonego katalogu:
-rw-r--r-- 1 apache apache  7 Sep 15 12:19 index.html
Po wywołaniu pliku w przeglądarce, powinno się pojawić okienko uwierzytelniania, do którego należy wprowadzić jednego z utworzonych użytkowników oraz jego niezaszyfrowane hasło, czyli np. HTTPUser1 i HTTPPassword1:

Apache - Uwierzytelnianie HTTP z wykorzystaniem bazy danych MySql
Apache - Uwierzytelnianie HTTP z wykorzystaniem bazy danych MySql


W przypadku jakichkolwiek pytań służę pomocą. Proszę o komentarze!














sobota, 5 września 2015

PHP: systemy szablonów HTML

PHP i HTML w jednym pliku


Język programowania php jest głównie stosowany w aplikacjach web jako mechanizm wykonujący zaprogramowane działania po stronie serwera. Za wizualizację wyników takowych działań odpowiedzialny jest zaś przeważnie sam kod HTML, który jest odpowiednio interpretowany po stronie przeglądarki. Programiści php mają swobodę wyboru kilku odmiennych metod generowania tagów HTML i ewentualnej dodatkowej treści, która ma być wyświetlana użytkownikom.

Tagi HTML mogą być generowane za pomocą samego php wraz z ewentualną dodatkową treścią:

<?php
class Kontrahent {
    public $name;
    public $country;

    public function __construct($name, $country) {
        $this->name = $name;
        $this->country = $country;
  }
}
$adam = new Kontrahent('Adam','Polska');
print "<div><b>Imię:</b> {$adam->name}</div><div><b>Kraj:</b> {$adam->country}</div>";
?>

Można w skryptach php również stosować bezpośrednio tagi HTML, a pomiędzy nimi umieszczać kod php:

<html>
  <head></head>
  <body>
   <div><b>Imię:</b> <?php echo $adam->name?></div>
   <div><b>Kraj:</b> <?php echo $adam->country?></div>
  </body>
</html>

W obu przedstawionych powyżej podejściach, odpowiedzialny za funkcjonalność aplikacji kod php koegzystuje w jednym pliku z tagami HTML, które odpowiadają za wyświetlanie treści. W złożonych skryptach php jest to rozwiązanie na tyle niewygodne, iż sprawia ono, że długoterminowe utrzymywanie takich skryptów pod kątem wdrażania ewentualnych zmian, zarówno do kodu php jak i tagów HTML, staje się dosyć uciążliwe. Podobnie, w przypadku konieczności zlokalizowania ewentualnych błędów, praca staje się trudna.

Szablony HTML


W nowoczesnych projektach systemów aplikacji web, wyraźne oddzielenie warstwy aplikacji od warstwy prezentacji liczy coraz to większe grona zwolenników. Szablony HTML to rozwiązanie, które umożliwia zupełne oddzielenie kodu php od tagów HTML i dodatkowej statycznej treści, która ma być wyświetlana użytkownikom aplikacji web. Generalnie rzecz ujmując tagi HTML oraz ewentualna dodatkowa treść statyczna - warstwa prezentacji - , przechowywane są w samym pliku .html, natomiast skrypt php - warstwa aplikacji - znajduje się w pliku .php.

Pro i kontra systemów szablonów HTML dla php


Odnośnie stosowania szablonów HTML dla kodu php programiści aplikacji web dzielą się na zwolenników szablonów oraz tych, którzy szablonów za żadną cenę nigdy by nie użyli we własnych projektach. Debata na temat zalet i wad szablonów HTML w rozwiązaniach web bazujących na języku php jest praktycznie tak stara, jak sam język php. Zwolennicy stosowania szablonów HTML przytaczają wiele solidnych argumentów na korzyść takiego podejścia. Odizolowanie kodu php od tagów HTML rzeczywiście idzie naprzeciw dużej przejrzystości kodu i łatwości utrzymywania złożonych systemów. W aplikacjach web, w których kod php jest zupełnie odizolowany od tagów HTML generalnie jest nie tylko o wiele łatwiej wprowadzać jakiekolwiek zmiany do kodu php, lecz także wprowadzanie zmian do kodu HTML staje się o wiele prostsze. Jakimi zatem argumentami bronią się przeciwnicy szablonów? Otóż jedynym ich argumentem jest fakt, iż stosowanie szablonów HTML może znacznie spowolnić działanie aplikacji php. Ten argument można jednak łatwo podważyć, bo jak się okazuje, wiele odmiennych czynników może mieć wpływ na szybkość działania danej aplikacji php, a w wielu przypadkach rozwiązania php bazujące na szablonach HTML okazują się działać lepiej i szybciej niż podobne systemy, w których zupełnie zrezygnowano z szablonów.

Systemy szablonowe


Aby używać szablonów HTML z poziomu skryptów php można skorzystać z któregoś z wielu powszechnie dostępnych systemów szablonowych, bądź napisać od zera własny system szablonowy. Tą drugą opcję, w świetle bogatego wyboru darmowego oprogramowania tworzonego przez naprawdę zaangażowane społeczności programistów, zostawiłbym jednak prawdziwym pasjonatom tworzenia systemów szablonowych, a nie programistom poszukującym takowego systemu dla własnych projektów. Tak czy inaczej, wachlarz powszechnie dostępnych systemów szablonowych jest dziś bardzo szeroki.

Oto krótka lista najpopularniejszych na dzień dzisiejszy systemów szablonowych dla php:


Każdy z powyższych systemów szablonów ma swoje zalety i wady, które często zależą od charakterystyki projektu w jakim dany system jest stosowany.

System szablonów HTML PEAR


Aby zaprezentować funkcjonalność systemu szablonów HTML dla skryptów php posłużę się przykładem systemu szablonów z repozytorium PEAR - PHP Extension and Application Repository, który miałem okazję dokładnie przetestować stosując go w kilku dużych projektach systemów aplikacji web. Ten system szablonów dla PHP jest powszechnie używany przez programistów aplikacji web od ponad 10 lat i zyskał sławę sprawdzonego i stabilnego rozwiązania, choć nie  oferuje on ponadprzeciętnych możliwości. System może być stosowany zarówno z plikami HTML jak i XML.

PEAR System szablonów HTML
PEAR HTML_Temaplate_IT

HTML_Template_IT to w sumie bardzo przyjazne programistom rozwiązanie swojej klasy, które pozwala na wyraźne oddzielenie warstwy prezentacyjnej danej aplikacji od warstwy zawierającej kod php. Działanie systemu opiera się na obecności bloków i wypełniaczy w kodzie HTML. Wypełniacze umożliwiają ich zastępowanie wartościami ze skryptu php. System stosuje hierarchię bloków, która określa sposób prezentacji treści w przeglądarce. HTML_Template_IT świetnie się sprawuje, choć jego funkcjonalność można określić jako podstawową. Bardziej wymagający programiści mogą skorzystać z pokrewnego systemu HTML_Template_ITX, który umożliwia dynamiczne generowanie bloków, a także stosowanie swego rodzaju wywołań zwrotnych.

Aby rozpocząć należy zainstalować managera pakietów PEAR. Sposób w jaki należy to zrobić zależy od stosowanego systemu operacyjnego. W niniejszym artykule opiszę procedurę instalacji managera pakietów oraz samych pakietów PEAR w środowisku Scientific Linux 7.

Instalacja managera pakietów PEAR rozpoczyna się po wydaniu polecenia:

[root@sphera2 ~]# yum install pear-php

Po udanej instalacji managera pakietów PEAR należy zainstalować pakiet HTML_Template_IT w następujący sposób:

[root@sphera2 ~]# pear install HTML_Template_IT
downloading HTML_Template_IT-1.3.0.tgz ...
Starting to download HTML_Template_IT-1.3.0.tgz (23,309 bytes)
........done: 23,309 bytes
install ok: channel://pear.php.net/HTML_Template_IT-1.3.0

Po udanej instalacji można rozpocząć stosowanie szablonów w skryptach php.

Utworzymy więc prosty szablon HTML dla zaprezentowania szablonowej wersji naszego uproszczonego przykładowego skryptu php. Należy zauważyć, że ciało bloków zawierających znaczniki zmiennych, które skrypt php ma za zadanie wypełnić wartościami zmiennych, zamknięte jest w znacznikach <!-- BEGIN NAZWA_BLOKU --> i <!-- END NAZWA_BLOKU -->. W pojedynczym pliku HTML takich bloków można oczywiście zdefiniować ilość większą niż jeden.

A oto nasz przykładowy szablon:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">

<html>
  <head>
   <title>Przykładowy szablon HTML</title>
   <meta http-equiv="Content-Type" content="text/html;charset=utf-8">
  </head>
  <body>
   <!-- BEGIN KONTRAHENT -->
   <div>Imię: {IMIE}</div>
   <div>Kraj: {KRAJ}</div>
   <!-- END KONTRAHENT -->
  </body>
</html>

Zakładając, że szablon – względem ścieżki naszego skryptu - zapisujemy jako templates/kontrahenci.html, nasz przykładowy skrypt php powinien teraz wyglądać następująco:

<?php
class Kontrahent {
    public $name;
    public $country;

    public function __construct($name, $country) {
        $this->name = $name;
        $this->country = $country;
  }
}

// PEAR HTML_template_IT
require_once("/usr/share/pear/HTML/Template/IT.php");
$kontrahenci = new HTML_template_IT("templates");
$kontrahenci->loadTemplatefile("kontrahenci.html",true,true);

$adam = new Kontrahent('Adam','Polska');

$kontrahenci->setCurrentBlock('KONTRAHENT'); // Ustawiamy bieżący blok
$kontrahenci->setVariable('IMIE',$adam->name); // Ustawiamy wartość zmiennej IMIE
$kontrahenci->setVariable('KRAJ',$adam->country); // Ustawiamy wartość zmiennej KRAJ
$kontrahenci->parseCurrentBlock(); // Parsowanie bieżącego bloku
$kontrahenci->show(); // Wyświetlanie treści
?>

W efekcie działania skryptu otrzymujemy:

Imię: Adam
Kraj: Polska
Załóżmy teraz, że dane kontrahentów mają być pobierane z tabeli bazy danych. Pobrane w ten sposób dane kilku kontrahentów mają być wyświetlone użytkownikowi aplikacji web. W takiej sytuacji nasz szablon HTML może jak najbardziej pozostać bez zmian, a jedyne co należałoby zmienić to skrypt php, w którym należałoby dodać odpowiednie mechanizmy połączenia z bazą danych, odczytu danych z tabeli, utworzenia obiektów oraz ustawienia wartości w wypełniaczach w szablonie. Po każdorazowej podmianie wypełniaczy danymi dotyczącymi danego wiersza tabeli bazy danych należałoby parsować blok za pomocą metody HTML_Template_IT::parseCurrentBlock().

W przypadku konieczności dostępu do szerszej gamy metod, warto korzystać z klasy HTML_Template_ITX, a można do zrobić zmieniając 2 linijki powyższego przykładowego skryptu:

require_once("/usr/share/pear/HTML/Template/ITX.php");
$kontrahenci = new HTML_template_ITX("templates");

Czy warto stosować szablony w aplikacjach php? Generalnie jest tak, iż warto je stosować w każdej rozbudowanej aplikacji web, bądź w systemach, które mają być utrzymywane i rozwijane długoterminowo. W krótkich i prostych skryptach web, których dalszego rozwoju nie przewidujemy, nie ma sensu stosować szablonów, bowiem w takich przypadkach dodatkowy nakład pracy, z jakim wiąże się praca z systemami szablonów, nigdy się nie przełoży na naszą korzyść.

piątek, 28 sierpnia 2015

Wytwórnia obiektów MySql w php

Wstęp


Jakiś czas temu pisałem o tym, jak w bezpieczny sposób korzystać z bazy danych MySql w skryptach php. Dziś pragnę podzielić się z Wami wiedzą dotyczącą tak zwanych wytwórni obiektów z danych MySql. Temat przeznaczony jest dla czytelników z dobrą znajomością języka php i serwera MySql, którzy pragną korzystać z dobrodziejstw obiektowego podejścia php do zagadnień bazodanowych.

Posłużę się w tym miejscu przykładem kartoteki kontrahentów, w której znajdują się zarówno osoby fizyczne, jak i przedsiębiorstwa oraz skryptem php, którego zadaniem jest wytwarzanie obiektów z danych obecnych w kartotece.

Aby rozpocząć należy utworzyć bazę danych MySql o nazwie kartoteka, a w niej tabelę o nazwie kontrahenci. O tym jak utworzyć bazę danych i prostą tabelę MySql pisałem w artykule PHP a bezpiecznie połączenie z bazą danych MySQL. Jest w nim również mowa o tworzeniu użytkownika MySql i nadawaniu uprawnień dostępowych.

Aby utworzyć naszą przykładową tabelę kontrahenci można posłużyć się poleceniem CREATE TABLE z wiersza poleceń MySQL w następujący sposób:

CREATE TABLE `kontrahenci` (
`id` smallint(5) unsigned NOT NULL AUTO_INCREMENT,
`imie` varchar(15) DEFAULT NULL,
`nazwisko` varchar(20) DEFAULT NULL,
`adres` varchar(25) NOT NULL,
`nazwa` varchar(20) DEFAULT NULL,
`miasto` varchar(20) NOT NULL,
`kodPocztowy` char(5) NOT NULL,
`typ` enum('O','F') DEFAULT NULL,
`nip` char(13) DEFAULT NULL,
`pesel` char(11) DEFAULT NULL,
`regon` char(14) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

Tabelę można oczywiście utworzyć także korzystając z interfejsu phpMyAdmin, bądź innym dowolnym sposobem. Osobiście preferuję pracować z wierszem poleceń MySql.

Utworzona tabela MySql
Utworzona tabela MySql


Po utworzeniu tabeli warto do niej wrzucić kilka przykładowych wierszy, bowiem pusta tabela do niczego się nam nie przyda. Dla celów niniejszego artykułu tabela powinna zawierać wiersze zarówno dotyczące osób fizycznych jak i te dotyczące firm. Pole typ określa rodzaj zawartości danego wiersza danych ('O' = osoba fizyczna, 'F' = firma).

Dodajemy zatem kilka przykładowych wierszy danych do nowo utworzonej tabeli kontrahenci:

MariaDB [kartoteka]> insert into kontrahenci (`imie`,`nazwisko`,`adres`,`miasto`,`kodPocztowy`,`pesel`,`typ`) values('Jan','Kowalski','Plac Wolności 1','Warszawa','00001','01010101010','O');
Query OK, 1 row affected (0.02 sec)

MariaDB [kartoteka]> insert into kontrahenci (`imie`,`nazwisko`,`adres`,`miasto`,`kodPocztowy`,`pesel`,`typ`) values('Janusz','Kowal','Plac Wolności 2','Warszawa','00001','02020202020','O');
Query OK, 1 row affected (0.03 sec)

MariaDB [kartoteka]> insert into kontrahenci (`nazwa`,`adres`,`miasto`,`kodPocztowy`,`nip`,`regon`,`typ`) values('Jakaś Firma','Plac Wolności 3','Warszawa','00002','1231231231231','12121212121212','F');
Query OK, 1 row affected (0.00 sec)

MariaDB [kartoteka]> insert into kontrahenci (`nazwa`,`adres`,`miasto`,`kodPocztowy`,`nip`,`regon`,`typ`) values('Jakaś Inna Firma','Plac Wolności 4','Warszawa','00002','1241241241241','14141414141414','F');
Query OK, 1 row affected (0.02 sec)

Przykładowe dane można oczywiście wprowadzić dowolną metodą, nie koniecznie z wiersza poleceń MySql.

Wprowadzanie przykładowych danych do tabeli MySql
Wprowadzanie przykładowych danych do tabeli MySql z wiersza poleceń
 
Zawartość tabeli MySql po wprowadzeniu przykładowych danych
Zawartość tabeli po wprowadzeniu przykładowych danych

 

Wytwórnia obiektów MySQL w php


Ze strony MySql jest już wszystko gotowe. Przejdźmy teraz do skryptu php, który umożliwia tworzenie obiektów na podstawie danych zawartych w tabeli MySql. Od czego zacząć? Przede wszystkim będzie nam potrzebne połączenie z bazą MySql, oraz kilka prostych klas.

Plik db_ini.php zawiera wartości umożliwiające nawiązanie połączenia z serwerem MySql, takie jak nazwa hosta, nazwa użytkownika i hasło. Należy pamiętać, że nazwa użytkownika i hasło muszą być zgodne z rzeczywiście utworzonym użytkownikiem, a także o nadaniu użytkownikowi odpowiednich uprawnień do bazy danych i tabeli. O tym jak się to robi pisałem w artykule PHP a bezpiecznie połączenie z bazą danych MySQL. Oto zawartość pliku db_ini.php:

<?php
// Dostęp do bazy danych
$host = 'localhost';
$db = 'kartoteka';
$user = 'mike';
$password = 'kot-dachowiec';
?>

Skrypt php zawiera kilka klas, zdefiniowanych w bardzo prosty sposó i kilka linijek prostego kodu, tak aby skupić się na ogólnym jego działaniu i ewentualnym zastosowaniu wytwórni obiektów MySql. Na wszelkie pomysły związane z udoskonaleniami i rozbudową skryptu pozostawiam wolne pole czytelnikom.

Klasy OsobaFizyczna i Firma dziedziczą składowe i przysłaniają metody abstrakcyjnej klasy nadrzędnej Kontrahent. Uzupełniają one klasę Kontrahent ważnymi dla konkretnego celu składowymi i metodami, co oznacza, że klasy OsobaFizyczna i Firma są swojego rodzaju specjalizacjami nadrzędnej klasy Kontrahent. Firma przeważnie posiada nip i regon, podczas gdy dla identyfikacji osoby fizycznej ważny jest identyfikator PESEL. W takich przypadkach nie ma sensu tworzyć jednej rozbudowanej klasy ze wszystkimi możliwymi składowymi dla firm i osób prywatnych, ponieważ dawało by to miejsce pewnej nadmiarowości: powstałe w ten sposób obiekty zawierałyby puste i niewykorzystywane zmienne. Zarówno firma, jak i osoba fizyczna, posiadają pewne wspólne cechy, takie jak adres, kod pocztowy, miasto itp., ale jednocześnie różnią się dodatkowymi informacjami. Warto jest więc w tym przypadku utworzyć jedną abstrakcyjną klasę nadrzędną oraz 2 wyspecjalizowane klasy pochodne.

Klasy OsobaFizyczna i Firma umożliwiają tworzenie egzemplarzy odpowiadających im obiektów i zawierają podstawowe metody umożliwiające pozyskiwanie związanych z utworzonymi obiektami informacji. Konstruktor klasy Kontrahent dostępny jest tylko ze swoich klas pochodnych. Natomiast statyczna metoda getInstance() klasy Kontrahent dostępna jest publicznie. Dla zwiększenia bezpieczeństwa wszystkie składowe klas zdefiniowałem są jako prywatne.

Najistotniejsza dla niniejszego przykładu jest statyczna funkcja getInstance() klasy Kontrahent. Funkcja ta pobiera, za pomocą nawiązanego uprzednio połączenia z MySql z wykorzystaniem klasy PDO, wybrane wiersze danych z tabeli kontrahenci, a w zależności od tego, czy dany wiersz dotyczy osoby fizycznej czy firmy, funkcja tworzy nowy egzemplarz odpowiedniego obiektu. Tego typu rozwiązanie nazywamy wytwórnią obiektową.

Klasa KontrahentInfo zawiera metody umożliwiające wyświetlanie danych dotyczących obiektów klasy Kontrahent i jej podrzędnych klas.

Oto skrypt:

<?php

// Dostęp do bazy danych
include("db_ini.php");

abstract class Kontrahent {
    private $id;
    private $adres;
    private $kodPocztowy;
    private $miasto;

    protected function __construct($id, $adres, $kodPocztowy, $miasto) {
        $this->id = $id;
        $this->adres = $adres;
        $this->kodPocztowy = $kodPocztowy;
        $this->miasto = $miasto;
    }

    public static function getInstance($id, PDO $pdo) {
        $stm = $pdo->prepare("select * from kontrahenci where id=?");
        $stm->execute( array( $id ) );
        $row = $stm->fetch(PDO::FETCH_ASSOC);
      
        if (empty($row)) { return null; }
      
        if ($row['typ'] == "O") {
      
        // Osoba Fizyczna
        $obj = new OsobaFizyczna ($row['id'], $row['adres'], $row['kodPocztowy'], $row['miasto'], $row['imie'], $row['nazwisko'], $row['pesel']);
              
        } elseif ($row['typ'] == "F") {
      
        // Firma
        $obj = new Firma ($row['id'], $row['adres'], $row['kodPocztowy'], $row['miasto'], $row['nazwa'], $row['nip'], $row['regon']);
      
        } else return null;

        return $obj;  
    }
  
    protected function getId() {
        return "ID: {$this->id}";
    }
  
    protected function getInfo() {
        $base = "Adres: {$this->adres}\n".
                "Kod pocztowy: {$this->kodPocztowy}\n".
                "Miasto: {$this->miasto}";
              
        return $base;
    }
}

class OsobaFizyczna extends Kontrahent {
    private $imie;
    private $nazwisko;
    private $pesel;
  
    public function __construct ($id, $adres, $kodPocztowy, $miasto, $imie, $nazwisko, $pesel) {
        parent::__construct($id, $adres, $kodPocztowy, $miasto);
        $this->imie = $imie;
        $this->nazwisko = $nazwisko;
        $this->pesel = $pesel;
    }
  
    public function getInfo() {
        $base = parent::getId()."\n";
        $base.= "Imie: {$this->imie}\n";
        $base.= "Nazwisko: {$this->nazwisko}\n";
        $base.= parent::getInfo()."\n";
        $base.= "PESEL: {$this->pesel}";
        return $base;
    }
}

class Firma extends Kontrahent {
    private $nazwa;
    private $nip;
    private $regon;
  
    public function __construct($id, $adres, $kodPocztowy, $miasto, $nazwa, $nip, $regon) {
        parent::__construct($id, $adres, $kodPocztowy, $miasto);
        $this->nazwa = $nazwa;
        $this->nip = $nip;
        $this->regon = $regon;
    }
  
    public function getInfo() {
        $base = parent::getId()."\n";
        $base.= "Nazwa: {$this->nazwa}\n";
        $base.= parent::getInfo()."\n";
        $base.= "NIP: {$this->nip}\n";
        $base.= "REGON: {$this->regon}";
        return $base;
    }
  
}

class KontrahenciInfo {
        public function showInfo(Kontrahent $kontrahent) {
            print "<pre>{$kontrahent->getInfo()}</pre><br>---------<br>";
        }
}

try {
    // Nawiązanie połączenia z bazą danych MySQL
    $conn = new PDO("mysql:host={$host};dbname={$db}", $user, $password,array(PDO::MYSQL_ATTR_INIT_COMMAND=>"SET NAMES utf8",PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION));
     
    for ($id=1;$id<=4;$id++) {
        $kontrahent = Kontrahent::getInstance($id,$conn);
        $info = new KontrahenciInfo;
        $info->showInfo($kontrahent);
    }
          
}    catch (PDOException $e) {
        // Obsługa wyjątków
        print "Błąd PDO: " . $e->getMessage() . "<br/>";
        die();
}

?>

Jak działa ten skrypt? Na początku skryptu występują definicje niezbędnych do jego działania klas. W następnym etapie nawiązujemy połączenie z bazą danych korzystając z popularnej bazodanowej klasy PDO. Dla celów niniejszego artykułu skrypt wyszukuje konkretne wiersze z tabeli, zdefiniowane pętlą for-next. Po nawiązaniu połączenia z bazą danych wywołujemy – w pętli for-next - zdefiniowaną w klasie Kontrahent statyczną metodę Kontrahent::getInstance(), do której przekazujemy obiekt PDO połączenia z bazą oraz identyfikator wiersza tabeli MySql kontrahenci i która w ten sposób odczytuje wybrane dane z tabeli i tworzy na ich podstawie zróżnicowane obiekty, odpowiednio klas OsobaFizyczna bądź Firma, w zależności od wartości pola typ w napotkanym wierszu MySql. W tej samej pętli tworzony jest także obiekt klasy KontrahenciInfo, a za pomocą metody KontrahenciInfo::ShowInfo() wyświetlane są informacje o każdym utworzonym obiekcie z podrzędnej hierarchii klasy Kontrahent. Nawiązanie połączenia MySql, odczyt wierszy danych z tabeli, tworzenie obiektów z odczytanych danych, a także korzystanie z metody wyświetlającej informacje o utworzonych obiektach, to czynności opatrzone w skrypcie w podstawowy mechanizm przechwytywania wyjątków try-catch.

A oto efekt działania skryptu:

ID: 1
Imie: Jan
Nazwisko: Kowalski
Adres: Plac Wolności 1
Kod pocztowy: 00001
Miasto: Warszawa
PESEL: 01010101010

---------
ID: 2
Imie: Janusz
Nazwisko: Kowal
Adres: Plac Wolności 2
Kod pocztowy: 00001
Miasto: Warszawa
PESEL: 02020202020

---------
ID: 3
Nazwa: Jakaś Firma
Adres: Plac Wolności 3
Kod pocztowy: 00002
Miasto: Warszawa
NIP: 1231231231231
REGON: 12121212121212

---------
ID: 4
Nazwa: Jakaś Inna Firma
Adres: Plac Wolności 4
Kod pocztowy: 00002
Miasto: Warszawa
NIP: 1241241241241
REGON: 14141414141414

---------

poniedziałek, 24 sierpnia 2015

PHP a bezpiecznie połączenie z bazą danych MySQL

Wstęp


W sieci web można dziś łatwo znaleźć obfitą ilość instruktaży dotyczących metod łączenia się z bazą danych MySQL z poziomu przeglądarki internetowej za pomocą języka php. Większość z nich to jednak metody bardzo ogólne i podstawowe, generalnie bazujące na oprogramowaniu strukturalnym, bez odpowiedniego podejścia do kwestii bezpieczeństwa kodu. Tego typu tutoriale są często źródłem złych nawyków programistycznych, które stają się barierą w rozwoju dalszych umiejętności. Ponieważ kod przedstawiony w danym instruktarzu działa, większość początkujących programistów jest przekonana o jego zupełniej prawidłowości i pełnej efektywności. Wiele z przykładów przedstawianych w popularnych instruktażach związanych z tematem, to metody, które w środowisku produkcyjnym raczej by się długoterminowo nie sprawdziły i na pewno w końcu by zawiodły. W niniejszym artykule postaram się przedstawić temat korzystania z bazy danych MySQL za pomocą języka php z perspektywy bezpieczeństwa i efektywności kodu, skupiając się na metodzie OOP (programowanie obiektowe) z zastosowaniem klasy PDO. W celu ułatwienia czytelnikom zrozumienia omawianej tutaj tematyki, przytoczę konkretne i sprawdzone przeze mnie przykłady. Artykuł jest skierowany do czytelników z dobrą znajomością php i przynajmniej podstawową wiedzą dotyczącą MySQL oraz programowania obiektowego.

Klasa PDO


Klasa PDO oferuje programistom php szeroki wachlarz możliwości jeśli chodzi o bazy danych, stanowiąc swego rodzaju abstrakcyjną powłokę dostępu do danych. PDO zapewnia dość wysoki poziom bezpieczeństwa przy minimalnej ilości kodu, a jednocześnie skrypty wykonywane są bardzo szybko. Stosowanie odpowiednich sterowników PDO umożliwia współpracę klasy z rozmaitymi systemami baz danych. Na dzień dzisiejszy wspierane są następujące rozwiązania bazodanowe:

IBM DB2
ODBC v3 (IBM DB2, unixODBC and win32 ODBC)
PostgreSQL
Cubrid
FreeTDS / Microsoft SQL Server / Sybase
Firebird
SQLite 3 and SQLite 2
Microsoft SQL Server / SQL Azure
4D IBM Informix Dynamic Server
MySQL 3.x/4.x/5.x
Oracle Call Interface

PHP PDO
PHP PDO


Niezależnie od stosowanego sterownika bazy danych, w każdym przypadku odczyt i manipulacja danymi odbywają się za pomocą identycznych funkcji tej klasy, co pozwala na szybkie opanowanie dostępnych w klasie narzędzi i stosowanie ich w pracy z różnymi rodzajami baz danych.

PHP a bezpieczne połączenie z bazą danych MySQL


Za pomocą klasy PDO można korzystać z dobrodziejstw MySQL z poziomu przeglądarki internetowej w prosty i bezpieczny sposób. Postaram się wyjaśnić jak to zrobić na podstawie prostego przykładu kartoteki kontrahentów. Aby rozpocząć, należy w dowolny sposób utworzyć bazę danych o nazwie kartoteka oraz prostą tabelę o nazwie kontrahenci. Dla jasności przykładu posłużę się w tym celu klientem MySQL MariaDB w środowisku Scientific Linux 7. Bazę i tabelę można oczywiście utworzyć w dowolny sposób, np. korzystając z interfejsu phpMyAdmin, bądź podobnego oprogramowania.
MariaDB [(none)]> create database kartoteka;
Query OK, 1 row affected (0.00 sec)

MariaDB [(none)]> use kartoteka;
Database changed

MariaDB [kartoteka]> create table kontrahenci (
-> id smallint unsigned not null auto_increment primary key,
-> imie varchar(15) not null,
-> nazwisko varchar(20) not null,
-> adres varchar(25) not null ) engine=InnoDB charset utf8;
Query OK, 0 rows affected (0.06 sec)
Po utworzeniu tabeli o nazwie kontrahenci należy utworzyć użytkownika o przykładowej nazwie mike i nadać mu uprawnienia niezbędne do odczytu i zapisu danych:
MariaDB [kartoteka]> grant usage on *.* to mike@localhost identified by 'kot-dachowiec';
Query OK, 0 rows affected (0.01 sec)

MariaDB [kartoteka]> grant select, insert, update on kartoteka.kontrahenci to mike@localhost;
Query OK, 0 rows affected (0.00 sec)
Warto sprawdzić, czy uprawnienia dla użytkownika mike są odpowiednio ustawione:
MariaDB [kartoteka]> show grants for mike@localhost;
+-------------------------------------------------------------------------------------------------------------+
| Grants for mike@localhost |
+-------------------------------------------------------------------------------------------------------------+
| GRANT USAGE ON *.* TO 'mike'@'localhost' IDENTIFIED BY PASSWORD '*391D5A941E13141F78608619467FC09552677273' |
| GRANT SELECT, INSERT, UPDATE ON `kartoteka`.`kontrahenci` TO 'mike'@'localhost' |
+-------------------------------------------------------------------------------------------------------------+
2 rows in set (0.00 sec)
Teraz można przystąpić do sporządzenia formularza html, którego kod, dla prostoty i jasności przykładu, wygląda następująco:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
  "http://www.w3.org/TR/html4/strict.dtd">
<html>
<h3>Dodawanie kontrahenta do kartoteki</h3>
<form method="post" action="dodaj.php">
Imię: <input type="text" name="imie" size="15" maxlength="15"> <br /><br />
Nazwisko: <input type="text" name="nazwisko" size="20" maxlength="20"> <br /><br />
Adres: <input type="text" name="adres" size="25" maxlength="25"> <br /><br />
<input type="submit" value="Dodaj">
</form>
</html>
Powyższy formularz ma za zadanie przesyłanie wprowadzonych do niego danych metodą POST do skryptu dodaj.php. Kod html formularza zawiera tylko te elementy, które są niezbędne dla zrozumienia przedstawianego tutaj przykładu, nie zawiera natomiast elementów takich jak style css, bądź innych elementów poprawiających wygląd czy funkcjonalność.

W pliku o przykładowej nazwie db_ini.php należy umieścić zmienne umożliwiające dostęp do bazy danych:
<?php
// Dostęp do bazy danych
$host = 'localhost';
$db = 'kartoteka';
$user = 'mike';
$password = 'kot-dachowiec';
?>

A oto kod skryptu dodaj.php:
<?php
// Dostęp do bazy danych
include('db_ini.php');
try
{
// Nawiązanie połączenia z bazą danych MySQL
$conn = new PDO("mysql:host={$host};dbname={$db}", $user, $password,
  array(PDO::MYSQL_ATTR_INIT_COMMAND=>"SET NAMES utf8",
  PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION));

// Dodawanie nowego wiersza danych
$dodaj = $conn->prepare("insert into kontrahenci (imie, nazwisko, adres) values(:imie,:nazwisko,:adres)");
$dodaj->execute(array(':imie'=>$_POST['imie'],':nazwisko'=>$_POST['nazwisko'],':adres'=>$_POST['adres']));

// Zakończenie połączenia
$conn = null;
}

catch (PDOException $e) {
// Obsługa wyjątków
print "Błąd: " . $e->getMessage() . "<br/>";
die();
}
?>
Skrypt dodaj.php ma za zadanie nie tylko wprowadzenie przesłanych za pomocą formularza html danych do tabeli MySQL, lecz także zapewnienie podstawowego poziomu obsługi wyjątków, którą można w prosty sposób dopasować do warunków typowego środowiska produkcyjnego. Skrypt powoduje dodanie nowego wiersza (rekordu) do tabeli kontrahentów. W przypadku wystąpienia wyjątku zostają wykonane polecenia z sekcji catch, którą można dowolnie rozbudować na miarę własnych potrzeb. Należy zwrócić uwagę na specyficzne atrybuty połączenia $conn. Po pierwsze, ponieważ tabela kontrahentów obsługuje system kodowania znaków utf-8, połączenie zostaje utworzone właśnie z obsługą tego standardu kodowania - MYSQL_ATTR_INIT_COMMAND=>"SET NAMES utf8" - co umożliwia prawidłową obsługę polskich czcionek. Po drugie, ponieważ skrypt dodaj.php ma zapewnić podstawową obsługę wyjątków, połączenie zostaje nawiązane z atrybutem PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION. W przypadku ustawień obsługi wyjątków jak w przykładzie powyższego skryptu dodaj.php, pojawienie się wyjątku nie tylko daje o sobie znać w zdefiniowany przez programistę sposób, czyli w postaci wyświetlonej informacji, ale także rozpoczęta przez skrypt transakcja dodawania wpisu zostaje automatycznie wycofana, co zapewnia dobre zabezpieczenie przed skutkami ewentualnych błędów. Przykładowo, jeśli programista przeliteruje w skrypcie nazwę kolumny 'imie' tabeli 'kontrahenci', to transakcja zostanie wycofana, a jednocześnie wyświetli się następujący komunikat:
Błąd: SQLSTATE[42S22]: Column not found: 1054 Unknown column 'imse' in 'field list'
Przechwytywanie wyjątków daje duże możliwości programistyczne, przy jednoczesnej minimalizacji kodu.

Co bardzo istotne dla każdego świadomego programisty php, wszelkie zapytania przekazywane do MySQL poprzez klasę PDO nie stanowią jakiejkolwiek luki bezpieczeństwa. W celu uzyskania maksymalnego poziomu bezpieczeństwa, PDO umożliwia stosowanie sparametryzowanych zapytań poprzez ich przygotowanie metodą prepare oraz powiązanie z wartościami zmiennych przekazanych w tablicy poprzez metodę execute (jak w niniejszym przykładzie). W celu zachowania wysokiego poziomu bezpieczeństwa zapytań PDO można równie dobrze korzystać z metod ::bindParam lub ::bindValue. Nie ma konieczności korzystania z dodatkowych narzędzi typu mysql_real_escape_string() w przykładowym celu zapobiegania atakom sql injection. Klasa PDO eliminuje potrzebę stosowania typowo proceduralnych zabezpieczeń przed różnego rodzaju atakami związanymi z dostępem do baz danych.

W celu uzupełnienia przykładu bezpiecznego zapisu danych, udostępniam przykład odczytu danych z bazy z wykorzystaniem klasy PDO:
<?php
// Dostęp do bazy danych
include('db_ini.php');
try
{
// Nawiązanie połączenia z bazą danych MySQL
$conn = new PDO("mysql:host={$host};dbname={$db}", $user, $password,
  array(PDO::MYSQL_ATTR_INIT_COMMAND=>"SET NAMES utf8",
  PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION));

// Odczyt i wyświetlanie wierszy danych
$dane=$conn->prepare("select id, imie, nazwisko, adres from kontrahenci");
$dane->execute();
$result = $dane->fetchAll(PDO::FETCH_ASSOC);

// Zakończenie połączenia
$conn = null;
}

catch (PDOException $e) {
// Obsługa wyjątków
print "Błąd: " . $e->getMessage() . "<br/>";
die();
}

// Wyświetlanie wyników
foreach ($result as $kontrahent)
 print "{$kontrahent['id']} - {$kontrahent['imie']} {$kontrahent['nazwisko']}
 {$kontrahent['adres']} <br /&gt";
?>

środa, 19 sierpnia 2015

Amanda Backup: Amrecover - przywracanie zarchiwizowanych danych

Wstęp


Jakiś czas temu opracowałem bardzo ogólny artykuł dedykowany systemowi archiwizacji danych Amanda, w którym przedstawiłem nie tylko ogólny zarys tego znakomitego i darmowego oprogramowania, lecz także przytoczyłem nieco udokumentowany przykład pewnego sprawdzonego wariantu podstawowej konfiguracji systemu.

Dobrze zaplanowana archiwizacja danych jest bardzo ważna, lecz kluczowe znaczenie ma również umiejętność przywracania zarchiwizowanych po stronie serwera plików. Ponieważ kwestia przywracania plików to, w przypadku systemu Amanda, bardzo obszerny temat, w niniejszym artykule mam zamiar omówić bardzo prosty przykład przywracania zarchiwizowanych danych do archiwizowanego klienta z systemem operacyjnym Linux, za pomocą oprogramowania Amrecover.

Należy pamiętać o tym, iż z oprogramowania Amrecover można korzystać tylko i wyłacznie jako użytkownik root na archiwizowanej maszynie.

Przykładowy scenariusz Amrecover


Serwer archiwizacji - z zainstalowanym i prawidłowo skonfigurowanym, dla celów niniejszego przykładu, oprogramowaniem amanda-server - o nazwie hosta amanda i adresie ip 192.168.0.2. Archiwizowany klient Linux o nazwie hosta bob3 i adresie ip 192.168.0.3, z zainstalowanym i prawidłowo skonfigurowanym, dla celów niniejszego przykładu, oprogramowaniem amanda-client.

Ustawienia wstępne


Aby oprogramowanie Amrecover działało prawidłowo kilka ważnych warunków musi zostać spełnionych.

1. Po stronie serwera archiwizacji danych należy sporządzić odpowiednią zawartość pliku /var/lib/amanda/.amandahosts. Dla niniejszego przykładu plik .amandahosts powinien koniecznie zawierać następującą linię, uprawniającą użytkownika root na maszynie adresie ip 192.168.0.3 do dostępu do systemu indeksacji zarchiwizowanych danych oraz do zarchiwizowanej zawartości:
192.168.0.3 root amindexd amidxtaped

2. Po stronie archiwizowanego klienta plik /etc/amanda/amanda-client.conf powinien zawierać następujące linie:
conf  "TestConfig1" # nazwa konfiguracji zgodnie z konfiguracją serwera
index_server "192.168.0.2" # serwer indeksacji danych
tape_server  "192.168.0.2" # serwer archiwizacji
tapedev "" # urządzenie taśmowe

Amrecover w akcji


Jako użytkownik root, po stronie archiwizowanej maszyny Linux zaprzęgamy do pracy Amrecover.
[root@bob3 tmp_test]# amrecover
AMRECOVER Version 3.3.3. Contacting server on amanda.intranet.bla.pl ...
220 amanda AMANDA index server (3.3.3) ready.
Setting restore date to today (2015-08-19)
200 Working date set to 2015-08-19.
200 Config set to TestConfig1.
501 Host bob3 is not in your disklist.
Use the sethost command to choose a host to recover
Po uruchomieniu oprogramowania Amrecover warto się upewnić, że na serwerze archiwizacji znajdują się zarchiwizowane dane z hosta bob3. Sprawdzamy więc listę zarchiwizowanych maszyn za pomocą polecenia listhost:
amrecover> listhost
200- List hosts for config TestConfig1
201- 192.168.0.3
200  List hosts for config TestConfig1
Ponieważ nasza maszyna znajduje się na liście, możemy śmiało kontynuować.
Za pomocą polecenia sethost wybieramy nazwę hosta, którego zarchiwizowane dane mamy zamiar przywrócić:
amrecover> sethost 192.168.0.3
200 Dump host set to 192.168.0.3.
Została prawidłowo wybrana nazwa hosta, którego dane nas interesują. Teraz, korzystając z polecenia listdisk uzyskujemy listę zarchiwizowanych na serwerze katalogów wybranego hosta bob3:
amrecover> listdisk
200- List of disk for host 192.168.0.3
201- /home/bob/dom/multimedia/Muzyka
200 List of disk for host 192.168.0.3
Ponieważ szybko znajdujemy katalog który nas interesuje, śmiało wybieramy go za pomocą polecenia setdisk:
mrecover> setdisk /home/bob/dom/multimedia/Muzyka
200 Disk set to /home/bob/dom/multimedia/Muzyka.
Po wybraniu interesującego nas dysku wyświetlamy jego zawartość:
amrecover> ls
2015-08-19-14-26-04 "gunnar Qmusic/"
2015-08-19-14-26-04 DreamcatcherQ/
2015-08-19-14-26-04 .
Ponieważ mamy zamiar przywrócić wyłącznie katalog DreamcatcherQ, wybieramy go za pomocą polecenia add:
amrecover> add Dreamcatcher
Added dir /Dreamcatcher/ at date 2015-08-19-14-26-04

Należy pamiętać o tym, że wybrane pliki lub katalogi są przywracane do lokalizacji ścieżki, w której obecnie się znajdujemy!

Przystępujemy do przywracania zarchiwizowanych plików za pomocą polecenia extract.
amrecover> extract

Extracting files using tape drive changer on host amanda.intranet.bla.pl.
The following tapes are needed: TestConfig1-1

Extracting files using tape drive changer on host amanda.intranet.bla.pl.
Load tape TestConfig1-1 now
Continue [?/Y/n/s/d]? y
Restoring files into directory /root/tmp_test
All existing files in /root/tmp_test can be deleted
Continue [?/Y/n]? y

./DreamcatcherQ/
./DreamcatcherQ/ab.ini
./DreamcatcherQ/dreamcatcherQ_wav.wav

amrecover>
Dane zostały skutecznie przywrócone, co w przykładowym przypadku umożliwi dalsze słuchanie ulubionej muzyki!

Amrecover oferuje cały szereg przydatnych w odzyskiwaniu danych poleceń. Aby wyświetlić całą listę poleceń, należy z poziomu wiersza poleceń amrecover wydać polecenie help.

W przypadku uwag bądź pytań proszę o komentarze. Służę pomocą!

czwartek, 13 sierpnia 2015

Amanda - sieciowa archiwizacja danych

Co to jest Amanda


Jednymi z trudniejszych zagadnień, najskuteczniej spędzających sen z powiek wielu administratorów sieci w dużych korporacjach są niewątpliwie procedury archiwizacji danych w rozległych i cross-platformowych sieciach komputerowych. Istnieje bardzo szeroki wachlarz rozwiązań umożliwiających zautomatyzowane, bardziej lub mniej skuteczne, procedury zabezpieczania danych w sieci. Amanda jest jednym z tych rozwiązań, które zasługują na więcej uwagi niż inne, ze względu na prostotę obsługi i konfiguracji, skalowalność i niezawodność. System archiwizacji danych pod mianem Amanda to zupełnie darmowe rozwiązanie typu Open Source, a co za tym idzie, jest to świetnie udokumentowane i cross-platformowe oprogramowanie, które może znacznie ułatwić administratorom rozległych sieci wiele trudnych do opanowania procedur. W niniejszym artykule przedstawię niektóre cechy systemu archiwizacji danych Amanda, zaprezentuję i udokumentuję prostą konfigurację systemu Amanda, a także przeprowadzę prosty przykład sieciowej archiwizacji danych z sieciowego folderu Windows.

Jak działa Amanda


Amanda - Sieciowa archiwizacja danych
Amanda Community Server


Amanda jest systemem o architekturze Klient-Serwer, który umożliwia - między innymi - wykonywanie kopii zapasowych danych z wielu maszyn w sieci na jednym serwerze. Amanda oferuje możliwość archiwizacji danych zarówno na dyskach twardych, jak i na taśmach, bądź nośnikach optycznych. Możliwa jest jednoczesna archiwizacja danych na różnych rodzajach nośników. Zarchiwizowane dane mogą znajdować się więc zarówno na szybko dostępnych dyskach, w celu zapewnienia możliwości ich natychmiastowego przywrócenia, jak i na taśmach - w celu ich długoterminowej archiwizacji. Dane mogą zostać dodatkowo zarchiwizowane w chmurze.

System Amanda korzysta ze standardowych sterowników systemowych - jakie są ogólnie stosowane w danym systemie operacyjnym - urządzeń wejścia/wyjścia oraz z powszechnie dostępnych aplikacji archiwizacyjnych, takich jak dump czy GNUTar, wobec czego dostęp do zarchiwizowanych danych jest zawsze możliwy, nawet w przypadku całkowitego odinstalowania systemu Amanda z serwera i z klientów sieciowych. Amanda posiada bardzo dopracowane algorytmy archiwizacji, które są w stanie optymalnie dostroić sposób wykonywania zapisu, mając na uwadze wiele różnych czynników, takich jak ruch w sieci, ilość danych do zarchiwizowania itp.

Typowa konfiguracja serwera Amanda uwzględnia przejściową archiwizację danych w katalogach podręcznych na dysku twardym (holding disk) przed ich docelowym zapisaniem na bardziej trwałych nośnikach, takich jak taśmy. Tego typu rozwiazanie daje optymalne rezultaty jeśli chodzi o wydajność i płynność archiwizacji. Zamiast prawdziwych taśm istnieje możliwość stosowania wirtualnych taśm (vtapes), które są niczym innym jak odpowiednio przygotowanymi w tym celu katalogami na dysku twardym. Katalogi danych podręcznych (holding disk) i wirtualne taśmy, ze względów wydajnościowych powinny mieścić się na odrębnych dyskach fizycznych. Serwer Amanda umożliwia jednoczesne stosowanie wielu oddzielnych zestawów ustawień archiwizacji. W pliku disklist danego zestawu ustawień określa się katalogi, które mają zostać zarchiwizowane oraz sposób ich archiwizacji, który definiowany jest uprzednio w pliku amanda.conf danego zestawu ustawień. Serwer Amanda kontaktuje się z klientami Amanda, zainstalowanymi na maszynach w sieci, a klienci wysyłają archiwa danych w żądany przez serwer sposób. Możliwe jest również wyszukiwanie i odzyskiwanie zapisanych w archiwach serwera plików bezpośrednio poprzez oprogramowanie klienckie na archiwizowanych maszynach.

System archiwizacji Amanda może być z powodzeniem stosowany zarówno w małych sieciach jak i w rozległych korporacyjnych infrastrukturach sieciowych. Amanda oferuje wiele rozmaitych możliwości, nawet takich jak zabezpieczanie danych z serwera NFS, bądź archiwizacja danych z sieciowych folderów Windows z zastosowaniem klienta Samba (smbclient), z którym Amanda idealnie się integruje. W celu wykonania archiwizacji danych w sieci za pomocą systemu Amanda, należy zainstalować i skonfigurować zarówno serwer Amanda, jak i ewentualnie klientów sieciowych na maszynach, które zamierzamy archiwizować. W przypadku konieczności archiwizacji danych z komputerów Windows można, lecz nie trzeba, stosować klienckie oprogramowanie Amanda na maszynach Windows. W przypadku konieczności archiwizacji maszyn Windows, można obejść instalację oprogramowania klienckiego korzystając z oprogramowania smbclient po stronie serwera Amanda - co właśnie zademonstruję w niniejszym artykule.

Instalacja serwera Amanda możliwa jest wyłącznie w środowiskach Linux/UNIX, natomiast oprogramowanie klienckie może zostać zainstalowane na kilku różnych platformach: Linux, UNIX, BSD, Mac OS-X oraz Microsoft Windows. Komunikacja pomiędzy serwerem a klientami Amanda odbywa się w zupełnie bezpieczny sposób, pod warunkiem sporządzenia odpowiedniej konfiguracji systemu.

Od czego zacząć?


System archiwizacji danych Amanda może być skonfigurowany na różne sposoby, w zależności od potrzeb użytkowników i umiejętności administratora. Opanowanie podstaw systemu zajmuje generalnie od kilku do kilkunastu minut. Dogłębne poznanie możliwości systemu Amanda może zająć miesiące, a nawet całe lata. Omówiona w niniejszym artykule instalacja i podstawowa konfiguracja to tylko jedno z ogromnej ilości możliwych podejść do zagadnienia. Dużo informacji można również znaleźć tutaj: http://wiki.zmanda.com/index.php/Getting_Started_with_Amanda


Wstępne uwagi do przykładowej instalacji systemu Amanda


System archiwizacji Amanda dostępny jest w dwóch odmiennych wersjach: komercyjna wersja systemu, oraz Amanda Community – bezpłatna odmiana Amandy. Obie wersje różnią się zasadniczo nie tylko zakresem funkcjonalności, ale rodzajem wsparcia technicznego. W tym artykule skupię się na oprogramowaniu Amanda Community. Pliki niezbędne do instalacji systemu Amanda Community można pozyskać na wiele sposobów. Zaawansowani użytkownicy zapewne skorzystają z plików źródłowych, które można pobrać z witryny http://amanda.org/download.php W oficjalnej witrynie Amanda można także znaleźć pakiety binarne oprogramowania, odpowiednie dla kilku najbardziej popularnych dystrybucji systemu operacyjnego Linux, takich jak Debian, Fedora, Red Hat, Suse i Ubuntu. W opracowanym przeze mnie przykładzie instalacji i konfiguracji Amanda Community skorzystam z narzędzia yum, typowego sposobu instalacji pakietów dla dystrybucji Red Hat i pokrewnych, ponieważ – o dziwo - daje w moim środowisku pracy dostęp do stosunkowo aktualnej wersji oprogramowania.

Instalacja i konfiguracja serwera Amanda Community


Instalacja serwera Amanda wymaga obecności i prawidłowego funkcjonowania pakietu xinetd, za pomocą którego są uruchamiane usługi związane z serwerem archiwizacji. Xinetd, w porównaniu z Inetd zapewnia o wiele większe bezpieczeństwo pracy uruchamianych za jego pomocą usług. Aby zainstalować serwer Amanda i przeprowadzić jego podstawową konfigurację należy wykonać kilka prostych czynności. Aby zademonstrować prostotę i funkcjonalność systemu archiwizacji danych Amanda przygotowałem przykład prostej konfiguracji serwera do archiwizacji danych z klienta sieciowego Windows. Cała procedura instalacji i tego typu konfiguracji nie powinna czasowo przekroczyć 5-10 minut. Wiele jednak zależy od wersji instalowanego oprogramowania i od wersji systemu operacyjnego. W opracowanym przeze mnie przykładzie instalacji pakietów Amanda posługuję się wersjami pakietów 3.3.3, instalowanych w środowisku Scientific Linux 7.

Aby rozpocząć, jako użytkownik root należy zainstalować pakiety amanda oraz amanda-server za pomocą narzędzia yum:
[root@amanda1 ~]# yum install amanda amanda-server
W trakcie instalacji zostaje automatycznie utworzony użytkownik amandabackup, co można zweryfikować zaglądając do /etc/passwd:
amandabackup:x:33:6:Amanda user:/var/lib/amanda:/bin/bash
Jako użytkownik root należy ustawić hasło dla uzytkownika amandabackup!

Następnie należy utworzyć plik konfiguracyjny xinetd /etc/xinetd.d/amandaserver dla serwera Amanda:
# default: on
#
# opis: Usługi serwera i klienta Amanda uruchamiane przez xinetd
#
service amanda
{
disable = no
socket_type = stream
protocol = tcp
wait = no
user = amandabackup
group = disk
groups = yes
server = /usr/lib64/amanda/amandad
server_args = -auth=bsdtcp amdump amindexd amidxtaped
}
Po zapisaniu pliku /etc/xinetd.d/amandaserver można przeładować xinetd. Warto wówczas także sprawdzić czy xinetd i usługa amanda działają prawidłowo:
[root@amanda1 ~]# systemctl reload xinetd
[root@amanda1 ~]# systemctl status xinetd xinetd.service - Xinetd A Powerful Replacement For Inetd
Loaded: loaded (/usr/lib/systemd/system/xinetd.service; enabled)
Active: active (running) since Wed 2015-08-12 23:00:56 CEST; 9min ago
Process: 18344 ExecReload=/usr/bin/kill -HUP $MAINPID (code=exited, status=0/SUCCESS)
Process: 18216 ExecStart=/usr/sbin/xinetd -stayalive -pidfile /var/run/xinetd.pid $EXTRAOPTIONS (code=exited, status=0/SUCCESS)
Main PID: 18217 (xinetd)
CGroup: /system.slice/xinetd.service
└─18217 /usr/sbin/xinetd -stayalive -pidfile /var/run/xinetd.pid

Aug 12 23:08:25 amanda1 xinetd[18217]: readjusting service amanda
Aug 12 23:08:25 amanda1 xinetd[18217]: Reconfigured: new=0 old=1 dropped=0 (services)
Aug 12 23:08:29 amanda1 systemd[1]: Started Xinetd A Powerful Replacement For Inetd.
Przejdźmy teraz do ustawień koniecznych do archiwizowania danych z sieciowego klienta Windows o przykładowej nazwie sphinx. Aby archiwizacja danych z klienta Windows o nazwie sphinx była możliwa, po stronie serwera Amanda należy wykonać następujące czynności jako użytkownik root:
[root@amanda1 ~]# echo //sphinx/DoArchiwizacji amandabackup%tajnehasloamandy BIURO.LAN >> /etc/amandapass
[root@amanda1 ~]# chown amandabackup:disk /etc/amandapass
[root@amanda1 ~]# chmod 700 /etc/amandapass
Po stronie klienta Windows, należy odpowiednio udostępnić wybrany folder, w tym przypadku jest to folder o udostępnionej nazwie DoArchiwizacji. W niniejszym przykładzie folder jest udostępniany w grupie roboczej BIURO.LAN użytkownikowi systemu Windows (którego należy ewentualnie utworzyć) o nazwie Amandabackup, z hasłem użytkownika tajnehasloamandy.

Należy utworzyć konfigurację serwera Amanda jako użytkownik amandabackup, np. o nazwie Codziennie1.
bash-4.2$  amserverconfig Codziennie1
Logging to /var/log/amanda/amserverconfig.20150813112215.debug
mkdir /etc/amanda/Codziennie1
/var/lib/amanda/gnutar-lists directory exists
/etc/amanda/Codziennie1/advanced.conf created and updated
mkdir /etc/amanda/Codziennie1/curinfo
mkdir /etc/amanda/Codziennie1/index
curinfo and index directory created
tapelist file created
disklist file created
custom amanda.conf created
DONE.
Po udanym utworzeniu konfiguracji Codziennie1 należy edytować zawartość pliku konfiguracyjnego /etc/amanda/Codziennie1/amanda.conf. Plik zawiera mnóstwo opcji, których przeważnie nie trzeba zmieniać, chyba że się dokładnie wie co się robi. Jak widać z poniżej przedstawionej zawartości, plik zawiera załączoną zawartość kolejnego pliku - advanced.conf.

Poniżej zawartość przykładowego pliku amanda.conf z niektórymi typowymi opcjami, ważnymi dla niniejszego przykładu:

org "Przykładowa nazwa" # nazwa Organizacji
dumpuser "amandabackup" # użytkownik uprawniony do wykonywania archiwizacji
mailto "admin@przykladowyadres" # lista operatorów (adresy email oddzielone spacją)
dumpcycle 1 week # długość cyklu archiwizacji
runspercycle 5 # ilość uruchomień amdump w każdym cyklu archiwizacji
tapecycle 10 tapes # ilość taśm w rotacji
runtapes 1 # maks. taśm stosowanych w każdym uruchomieniu amdump

tpchanger "chg-disk://backup/vtapes/Codziennie1/slots"
tapetype HARDDISK
labelstr "^Codziennie1-[0-9][0-9]*$"
dtimeout 1800
ctimeout 30
etimeout 300

define dumptype global {
comment "Global definitions"
auth "bsdtcp"
}

define dumptype gui-base {
global
program "GNUTAR"
comment "gui base dumptype dumped with tar"
compress none
index yes
}

define tapetype HARDDISK {
comment "Virtual Tapes"
length 32768 mbytes
}

define dumptype encrypt-simple {
program "GNUTAR"
comment "szyfrowanie symetryczne - zrzut tar"
encrypt client
compress fast
client_encrypt "/usr/sbin/amcryptsimple"
client_decrypt_option "-d"
}


define holdingdisk Archiwizacja1 { 
directory /amandabackup/dumps/amanda 
use 300 Gb 
}

includefile "advanced.conf"
includefile "/etc/amanda/template.d/dumptypes"
includefile "/etc/amanda/template.d/tapetypes"

Prawidłowe ustawienia dotyczące taśm i rotacji taśm w pliku amanda.conf mają kluczowe znaczenie.

dumpcykle - Długość cyklu archiwizacji. Wartość parametru określa przedział czasowy, w którym wymienione w odpowiednim pliku disklist "dyski" są objęte co najmniej 1 pełną archiwizacją danych.

runspercykle - Wartość tego parametru określa ilość uruchomień programu archiwizacyjnego w przedziale czasowym określonym w dumpcykle. 

runtapes - Maksymalna ilość taśm stosowana przy każdy uruchomieniu programu archiwizacji.

tapecykle -  Ten parametr określa ilość taśm w rotacji, a jego błędne ustawienie może być przyczyną nieprawidłowo wykonywanej archiwizacji. Wartość tego parametru powinna zawsze przewyższać (np. 2x, 3x) ilość taśm stosowanych w danym cyklu archiwizacji, którą można łatwo obliczyć mnożąc wartość parametrów runspercykle x runtapes. Przykładowo, jeśli runspercykle wynosi 7, a runtapes jest 1, to Amanda nadpisuje zestaw 7 taśm w danym cyklu. Jeśli wówczas w rotacji jest 14 taśm (tapecykle = 14) to oznacza, że dane z każdego poprzedniego pełnego cyklu archiwizacji przechowywane są na pozostałym zestawie 7 taśm. Dobrym zwyczajem jest ustawienie parametru tapecykle:

               tapecykle >= 2 x runspercykle x runtapes

Następnie, jako użytkownik root należy utworzyć po stronie serwera Amanda katalogi danych archiwizacji przechowywanych tymczasowo na dysku twardym (holding disk). W tym celu należy wykonać następujące czynności:
[root@amanda1 ~]# mkdir -p /amandabackup/dumps/amanda
[root@amanda1 ~]# chown amandabackup:disk /amandabackup/dumps/amanda
[root@amanda1 ~]# chmod 750 /amandabackup/dumps/amanda

Kolejne kroki polegają na utworzeniu katalogów do przechowywania wirtualnych taśm zarchiwizowanych danych. Polecenia, które należy wydać jako użytkownik root:
[root@amanda1 ~]# mkdir -p /backup/vtapes
[root@amanda1 ~]# chown amandabackup:disk /backup/vtapes
[root@amanda1 ~]# chmod 750 /backup/vtapes

Następnie, jako użytkownik amandabackup, należy wydać następujące polecenia w celu utworzenia katalogów wirtualnych taśm:
bash-4.2$ mkdir -p /backup/vtapes/Codziennie1/slots
bash-4.2$ cd /backup/vtapes/Codziennie1/slots
bash-4.2$ for ((i=1; $i<=10; i++)); do mkdir slot$i;done
bash-4.2$ ln -s slot1 data

Warto teraz sprawdzić, również jako użytkownik amandabackup, czy wirtualne taśmy są w porządku:
bash-4.2$ amtape Codziennie1 show
amtape: scanning all 10 slots in changer:
slot 1: unlabeled volume
slot 2: unlabeled volume
slot 3: unlabeled volume
slot 4: unlabeled volume
slot 5: unlabeled volume
slot 6: unlabeled volume
slot 7: unlabeled volume
slot 8: unlabeled volume
slot 9: unlabeled volume
slot 10: unlabeled volume

Błąd: amtape: running as user "amandabackup" instead of "root"


Ten błąd pojawia się w przypadku gdy następuje próba uruchomienia aplikacji archiwizujących z pakietu Amanda przez użytkownika innego, niż użytkownik określony jako „dumpuser” w pliku amanda.conf dla danej konfiguracji serwera. Jeżeli po uruchomieniu aplikacji amtape nie wyświetlają się jakiekolwiek błędy, to można kontynuować konfigurację serwera Amanda.

Jako użytkownik amandabackup, pozostając w /backup/vtapes/Codziennie1/slots, należy nadać uprzednio utworzonym wirtualnym taśmom odpowiednie etykietki - w następujący sposób:
bash-4.2$ for ((i=1; $i<=10;i++)); do amlabel Codziennie1 Codziennie1-0$i slot $i; done
Reading label...
Found an empty tape.
Writing label 'Codziennie1-01'...
Checking label...
Success!
...
Po nadaniu etykiet warto się upewnić czy wszystko jest w porządku:
bash-4.2$ amtape Codziennie1 show
amtape: scanning all 10 slots in changer:
slot 10: date X label Codziennie1-010
slot 1: date X label Codziennie1-01
slot 2: date X label Codziennie1-02
slot 3: date X label Codziennie1-03
slot 4: date X label Codziennie1-04
slot 5: date X label Codziennie1-05
slot 6: date X label Codziennie1-06
slot 7: date X label Codziennie1-07
slot 8: date X label Codziennie1-08
slot 9: date X label Codziennie1-09
Następnie należy zresetować wirtualny streamer:
bash-4.2$ amtape Codziennie1 reset
changer is reset
Jako użytkownik amandabackup należy dodać następującą zawartość do pliku /etc/amanda/Codziennie1/disklist:
amanda1 //sphinx/DoArchiwizacji comp-user-tar
Kolejna czynność polega na skonfigurowaniu, jako użytkownik amandabackup, pliku /var/lib/amanda/.amandahosts, którego zawartość powinna wyglądać następująco:
localhost amandabackup admump amanda1 root amindexd amidxtaped amanda1 amandabackup admump
W tej chwili warto sprawdzić za pomocą narzędzia amcheck, czy wszystkie ustawienia systemu archiwizacji zostały dokonane prawidłowo. Należy pamiętać o uprzednim udostępnieniu odpowiedniego folderu użytkownikowi amandabackup w systemie Windows, w przeciwnym razie program amcheck poinformuje o problemie z dostępem do sieciowego folderu. Jak widać poniżej wszelkie ustawienia zostały dokonane prawidłowo i serwer Amanda jest w stanie poprawnie wykonywać ustawione archiwizacje danych.
bash-4.2$ amcheck Codziennie1
Amanda Tape Server Host Check
-----------------------------
slot 1: volume 'Codziennie1-01'
Will write to volume 'Codziennie1-01' in slot 1.
NOTE: skipping tape-writable test
NOTE: host info dir /etc/amanda/Codziennie1/curinfo/amanda1 does not exist
NOTE: it will be created on the next run.
NOTE: index dir /etc/amanda/Codziennie1/index/amanda1 does not exist
NOTE: it will be created on the next run.
Server check took 0.767 seconds
Amanda Backup Client Hosts Check
--------------------------------
Client check: 1 host checked in 0.130 seconds. 0 problems found.

(brought to you by Amanda 3.3.3)
Aby sprawdzić, czy na pewno ustawiona archiwizacja klienta Windows działa prawidłowo, wystarczy uruchomić działanie ręcznie, po uprzednim skopiowaniu przykładowych plików do udostępnianego użytkownikowi amandabackup folderze w systemie Windows.
bash-4.2$ amdump Codziennie1
W przypadku problemów z dostępem systemu Amanda do udostępnionego folderu sieciowego, warto sprawdzić jak sobie z tym radzi smbclient:
bash-4.2$ smbclient //sphinx/DoArchiwizacji -U amandabackup -W BIURO.LAN
Enter amandabackup's password:
Domain=[SPHINX] OS=[Windows 10 Pro 10243] Server=[Windows 10 Pro 6.4]
Po uruchomieniu narzędzia amdump zostaje wykonana archiwizacja Codziennie1 i serwer wysyła wiadomość email z odpowiednim powiadomieniem na adres pocztowy wskazany w pliku amanda.conf odpowiadającemu wykonanej archiwizacji. Wiadomość zawiera wiele informacji związanych z wykonaną archiwizacją.

Aby sprawdzić co zostało zarchiwizowane:

bash-4.2$ amadmin Codziennie1 find

date host disk lv tape or file file part status
2015-08-13 22:48:00 amanda1 //sphinx/DoArchiwizacji 0 Codziennie1-08 1 1/1 OK

Aby jednak archiwizacje miały większy sens, warto określić odpowiedni harmonogram działań za pomocą narzędzia crontab.

Dodawanie klienta Linux do bieżącej konfiguracji Amanda


Aby do aktualnej - przykładowej -  konfiguracji systemu archiwizacji Amanda dodać nowego klienta, tym razem Linux, wystarczy wykonać kilka prostych czynności:

1. Na maszynie Linux, która ma być archiwizowana na serwerze Amanda należy zainstalować oprogramowanie amanda-client;

2. Należy utworzyć plik konfiguracyjny xinetd dla zainstalowanego oprogramowania amanda-client - analogicznie do czynności wykonanej po stronie serwera archiwizacji;

3. Nazwa serwera archiwizacji powinna zostać dodana do pliku .amandahosts na maszynie klienckiej, a nazwa klienta powinna zostać dodana do pliku .amandahosts po stronie serwera. Należy posługiwać się odpowiednią składnią zawierającą nazwę hosta, użytkownika i oprogramowania:
amandaserver.przyklad.com użytkownik-archiwizator usługa(i)
4. Po stronie serwera należy uwzględnić katalogi klienta Linux, które mają być archiwizowane, w odpowiednim pliku .disklist, np:
kot-filemon.dom.net /home/kicia1 comp-user-tar
5. Po stronie klienta należy utworzyć odpowiedni plik amanda-client.conf, co umożliwi przywracanie danych z serwera Amanda, przykładowo:
# /etc/amanda/amanda-client.conf
#
conf "Codziennie1"
index_server "amanda"
tape_server "amanda"
auth "bsdtcp"

środa, 12 sierpnia 2015

Aktualizacja BIOSU

W niniejszym artykule opisuję przykładową procedurę aktualizacji oprogramowania BIOS. Do celów związanych z tematem wybrałem płytę główną ASUS P6T Deluxe, która pomimo swoich kilku ładnych lat, jest nadal bardzo popularnym i lubianym modelem wśród pasjonatów sprzętu IT. Płytę, którą znalazłem dziś przypadkowo na strychu, po przeprowadzonej i udanej aktualizacji BIOSu, postanowiłem zastosować w domowym serwerze plików z bardzo wydajnym, ale na dzisiejsze czasy już nie tak drogim jak niegdyś, procesorem Intel Core i7.

Uwaga! Aktualizacja BIOSu jest operacją krytyczną i nie należy jej przeprowadzać jeśli nie jesteśmy pewni co robimy! W trakcie aktualizacji BIOSU nie wolno wyłączać sprzętu, ponieważ grozi to nieodwracalnym uszkodzeniem układów płyty głównej. Aktualizację BIOSu przeprowadzamy zawsze na własne ryzyko!

Oprogramowanie RUFUS


Aktualizacja BIOSu wymaga uprzedniego uruchomienia komputera z bootowalnego nośnika MS-DOS. Kiedyś w tym celu stosowało się dyskietki, ale dziś trudno jest o dyskietki i odpowiednie dla nich napędy. Obecnie istnieje kilka popularnych aplikacji nadających się do tworzenia bootowalnych nośników USB z oprogramowaniem emulującym MS-DOS, lecz moim ulubionym rozwiązaniem jest od pewnego czasu Rufus. Aplikacja Rufus, oferująca wiele ciekawych opcji, świetnie nadaje się nie tylko do tworzenia bootowalnych nośników USB z systemem Free Dos, ale także można jej użyć do przygotowania bootowalnych nośników z wybranymi obrazami .iso, np. systemu Linux. Rufus obsługuje zarówno system partycjonowania MBR, jak również UEFI. W niniejszym przykładzie, w celu aktualizacji BIOSu dość popularnej płyty głównej, zastosowałem aplikację Rufus w wersji 2.2, a pobrałem ją z witryny http://rufus.akeo.ie/

Aktualne pliki BIOSu


Do przeprowadzenia aktualizacji BIOSu niezbędny jest plik z najnowszą wersją BIOSu oraz aplikacja zapisująca program w układzie płyty głównej. Plik z aktualną wersją BIOSu danej płyty głównej można zawsze pobrać z witryny producenta sprzętu, gdzie można przeważnie znaleźć także aplikację służącą do przeprogramowania układów płyty. W niniejszym przykładzie pliki związane z aktualizacją BIOSu płyty głównej ASUS P6T Deluxe pochodzą z oficjalnej witryny producenta.

Tworzenie bootowalnego nośnika USB z systemem operacyjnym FREE DOS


Aby utworzyć bootowalny nośnik USB z oprogramowaniem Free Dos, należy w poziomu Windows uruchomić program Rufus i wybrać przedstawione w poniższym obrazku opcje, po czym należy przystąpić do tworzenia nośnika klikając w przycisk START.

Rufus Free Dos
Rufus 2.2 - Tworzenie bootowalnego nośnika USB Free Dos


Proszę zwrócić uwagę, iż po potwierdzeniu operacji utworzenia bootowalnego nośnika, wszelkie znajdujące się na nośniku USB dane zostaną usunięte. W efekcie potwierdzenia operacji, utworzona przez program Rufus zawartość głównego katalogu bootowalnego nośnika USB powinna wyglądać następująco:
2015-08-11 22:15 [DIR] LOCALE
2015-08-11 22:15 266 CONFIG.SYS
2015-08-11 22:15 202 AUTOEXEC.BAT
2015-08-11 22:15 206 autorun.inf
2015-08-11 22:15 34 494 autorun.ico
4 File(s) 35 168 bytes
1 Dir(s) 4 033 347 584 bytes free

Kopiowanie plików BIOSu na bootowalny nośnik USB


Po utworzeniu bootowalnego nośnika USB należy do niego wrzucić wszelkie pliki niezbędne do aktualizacji BIOSu. Po skopiowaniu na nośnik USB plików związanych z aktualizacją BIOSu, czyli w naszym przypadku pliku z rozszerzeniem .ROM zawierającego nowy firmware oraz aplikacji AFU241, służącej do aktualizacji oprogramowania płyty głównej, zawartość dysku F:\ powinna wyglądać następująco:

2015-08-11 22:15 [DIR] LOCALE
2015-08-11 22:15 266 CONFIG.SYS
2015-08-11 22:15 202 AUTOEXEC.BAT
2015-08-11 22:15 206 autorun.inf
2015-08-11 22:15 34 494 autorun.ico
2010-11-08 16:59 2 097 152 P6T-AS~1.ROM
2009-04-14 18:32 53 345 AFUDOS241.exe
6 File(s) 2 185 665 bytes
1 Dir(s) 4 031 184 896 bytes free
W tym momencie wszelkie pliki niezbędne do wykonania aktualizacji BIOSu znajdują się już na bootowalnym nośniku USB i można przystąpić do aktualizacji starej wersji firmware płyty.

Aktualizacja BIOSu ASUS P6T Deluxe


Przed włożeniem nowo utworzonego bootowalnego nośnika do portu USB komputera, którego BIOS mamy zamiar aktualizować, warto się upewnić, że obecne ustawienia BIOSu umożliwiają uruchomienie sprzętu z wymiennego dysku USB. W tym celu, po wejściu do ustawień BIOSu (wciśnięty przycisk DEL podczas startu komputera) należy się upewnić, że obsługa USB jest włączona, a także ustawić prawidłową kolejność napędów bootowania, tak aby wymienny dysk (Removable Dev.) znajdował się w pierwszej kolejności.

Po udanym uruchomieniu komputera z bootowalnego nośnika USB Free Dos, na ekranie powinny pojawić się informacje związane z systemem operacyjnym Free Dos.

Free Dos
Free Dos

Aby kontynuować należy dokonać wyboru klawiatury.

Po dokonaniu wyboru klawiatury można rozpocząć aktualizację BIOSu.

Free Dos
Free Dos


Uwaga! Jak już pisałem wcześniej, aktualizacja BIOSu jest operacją krytyczną. Nie należy do niej podchodzić, jeśli nie jest się pewnym o co w tym chodzi!

Aby uruchomić aplikację służącą do aktualizacji BIOSu i przeprogramować układ płyty do wybranej wersji oprogramowania, należy wydać polecenie AFUDOS.EXE z przedstawionymi poniżej parametrami:

Asus AFUDOS.EXE
Asus AFUDOS.EXE



 
Programowanie BIOSu
Programowanie BIOSu

Asus P6T Deluxe
Aktualna wersja BIOSu ASUS P6T Deluxe
 To wszystko na dziś. Powodzenia!