Książka „Linux w akcji”

Książka „Linux w akcji” Witam mieszkańców Khabro! W książce David Clinton opisuje 12 rzeczywistych projektów, w tym automatyzację systemu tworzenia kopii zapasowych i odzyskiwania, konfigurowanie osobistej chmury plików w stylu Dropbox i tworzenie własnego serwera MediaWiki. Poznasz wirtualizację, odzyskiwanie po awarii, bezpieczeństwo, tworzenie kopii zapasowych, DevOps i rozwiązywanie problemów z systemami poprzez ciekawe studia przypadków. Każdy rozdział kończy się przeglądem najlepszych praktyk, słownikiem nowych terminów i ćwiczeniami.

Fragment „10.1. Tworzenie tunelu OpenVPN”

W tej książce mówiłem już dużo o szyfrowaniu. SSH i SCP mogą chronić dane przesyłane za pośrednictwem połączeń zdalnych (rozdział 3), szyfrowanie plików może chronić dane przechowywane na serwerze (rozdział 8), a certyfikaty TLS/SSL mogą chronić dane przesyłane między witrynami a przeglądarkami klientów (rozdział 9) . Czasami jednak Twoje dane muszą być chronione w szerszym zakresie połączeń. Na przykład być może niektórzy członkowie Twojego zespołu pracują w drodze, łącząc się z Wi-Fi za pośrednictwem publicznych hotspotów. Zdecydowanie nie powinieneś zakładać, że wszystkie takie punkty dostępu są bezpieczne, ale Twoi pracownicy rzeczywiście potrzebują sposobu na połączenie się z zasobami firmy — i w tym może pomóc VPN.

Odpowiednio zaprojektowany tunel VPN zapewnia bezpośrednie połączenie pomiędzy zdalnymi klientami a serwerem w sposób ukrywający dane przesyłane przez niezabezpieczoną sieć. Więc co? Widziałeś już wiele narzędzi, które mogą to zrobić za pomocą szyfrowania. Prawdziwą wartością VPN jest to, że otwierając tunel, możesz łączyć odległe sieci tak, jakby były lokalne. W pewnym sensie używasz obejścia.

Korzystając z tej rozszerzonej sieci, administratorzy mogą wykonywać swoją pracę na swoich serwerach z dowolnego miejsca. Ale co ważniejsze, firma posiadająca zasoby rozproszone w wielu lokalizacjach może sprawić, że będą one widoczne i dostępne dla wszystkich grup, które ich potrzebują, niezależnie od tego, gdzie się znajdują (rysunek 10.1).

Sam tunel nie gwarantuje bezpieczeństwa. Można jednak uwzględnić w strukturze sieci jeden ze standardów szyfrowania, co znacząco podnosi poziom bezpieczeństwa. Tunele utworzone przy użyciu pakietu OpenVPN typu open source korzystają z tego samego szyfrowania TLS/SSL, o którym już czytałeś. OpenVPN nie jest jedyną dostępną opcją tunelowania, ale jest jedną z najbardziej znanych. Uważa się, że jest nieco szybszy i bezpieczniejszy niż alternatywny protokół tunelowy warstwy 2, który wykorzystuje szyfrowanie IPsec.

Czy chcesz, aby wszyscy w Twoim zespole mogli bezpiecznie komunikować się ze sobą podczas podróży lub pracy w różnych budynkach? Aby to zrobić, musisz utworzyć serwer OpenVPN, aby umożliwić udostępnianie aplikacji i dostęp do lokalnego środowiska sieciowego serwera. Aby to zadziałało, wystarczy uruchomić dwie maszyny wirtualne lub dwa kontenery: jeden pełniący rolę serwera/hosta i drugi pełniący rolę klienta. Budowa sieci VPN nie jest prostym procesem, dlatego prawdopodobnie warto poświęcić kilka minut, aby uzyskać ogólny obraz sytuacji.

Książka „Linux w akcji”

10.1.1. Konfiguracja serwera OpenVPN

Zanim zaczniesz, dam ci kilka przydatnych rad. Jeśli zamierzasz to zrobić samodzielnie (co gorąco polecam), prawdopodobnie będziesz pracować z wieloma oknami terminali otwartymi na pulpicie, każde podłączone do innego komputera. Istnieje ryzyko, że w pewnym momencie wprowadzisz do okna błędne polecenie. Aby tego uniknąć, możesz użyć polecenia nazwa hosta, aby zmienić nazwę komputera wyświetlaną w wierszu poleceń na taką, która wyraźnie informuje, gdzie jesteś. Gdy to zrobisz, będziesz musiał wylogować się z serwera i zalogować ponownie, aby nowe ustawienia zaczęły obowiązywać. Oto jak to wygląda:

Książka „Linux w akcji”
Stosując się do tego podejścia i nadając odpowiednie nazwy każdej maszynie, na której pracujesz, możesz łatwo śledzić, gdzie jesteś.

Po użyciu nazwy hosta możesz napotkać irytujące komunikaty „Nie można rozwiązać hosta OpenVPN-Server” podczas wykonywania kolejnych poleceń. Aktualizacja pliku /etc/hosts o odpowiednią nową nazwę hosta powinna rozwiązać problem.

Przygotowanie serwera do OpenVPN

Aby zainstalować OpenVPN na swoim serwerze, potrzebujesz dwóch pakietów: openvpn i easy-rsa (do zarządzania procesem generowania klucza szyfrującego). Użytkownicy CentOS powinni najpierw zainstalować repozytorium epel-release, jeśli to konieczne, tak jak to zrobiłeś w rozdziale 2. Aby móc przetestować dostęp do aplikacji serwera, możesz także zainstalować serwer WWW Apache (Apache2 na Ubuntu i httpd na CentOS).

Podczas konfigurowania serwera zalecam aktywację zapory sieciowej, która blokuje wszystkie porty z wyjątkiem 22 (SSH) i 1194 (domyślny port OpenVPN). Ten przykład ilustruje, jak ufw będzie działać na Ubuntu, ale jestem pewien, że nadal pamiętasz program firewall CentOS z rozdziału 9:

# ufw enable
# ufw allow 22
# ufw allow 1194

Aby włączyć routing wewnętrzny pomiędzy interfejsami sieciowymi na serwerze, należy odkomentować jedną linię (net.ipv4.ip_forward = 1) w pliku /etc/sysctl.conf. Umożliwi to przekierowanie klientów zdalnych w razie potrzeby po ich podłączeniu. Aby nowa opcja działała, uruchom sysctl -p:

# nano /etc/sysctl.conf
# sysctl -p

Twoje środowisko serwerowe jest teraz w pełni skonfigurowane, ale zanim będziesz gotowy, pozostaje jeszcze jedna rzecz do zrobienia: musisz wykonać następujące kroki (omówimy je szczegółowo poniżej).

  1. Utwórz na serwerze zestaw kluczy szyfrujących infrastrukturę kluczy publicznych (PKI), korzystając ze skryptów dostarczonych w pakiecie easy-rsa. Zasadniczo serwer OpenVPN działa również jako własny urząd certyfikacji (CA).
  2. Przygotuj odpowiednie klucze dla klienta
  3. Skonfiguruj plik server.conf dla serwera
  4. Skonfiguruj klienta OpenVPN
  5. Sprawdź swoją sieć VPN

Generowanie kluczy szyfrujących

Aby wszystko było proste, możesz skonfigurować kluczową infrastrukturę na tym samym komputerze, na którym działa serwer OpenVPN. Jednak najlepsze praktyki w zakresie bezpieczeństwa zazwyczaj sugerują użycie oddzielnego serwera urzędu certyfikacji do wdrożeń produkcyjnych. Proces generowania i dystrybucji zasobów kluczy szyfrujących do wykorzystania w OpenVPN ilustruje ryc. 10.2.

Książka „Linux w akcji”
Kiedy instalowałeś OpenVPN, katalog /etc/openvpn/ został automatycznie utworzony, ale jeszcze nic w nim nie było. Pakiety openvpn i easy-rsa zawierają przykładowe pliki szablonów, których możesz użyć jako podstawy swojej konfiguracji. Aby rozpocząć proces certyfikacji, skopiuj katalog szablonów easy-rsa z /usr/share/ do /etc/openvpn i przejdź do katalogu easy-rsa/:

# cp -r /usr/share/easy-rsa/ /etc/openvpn
$ cd /etc/openvpn/easy-rsa

Katalog easy-rsa będzie teraz zawierał sporo skryptów. W tabeli 10.1 zawiera listę narzędzi, których będziesz używać do tworzenia kluczy.

Książka „Linux w akcji”

Powyższe operacje wymagają uprawnień roota, więc musisz zostać rootem poprzez sudo su.

Pierwszy plik, z którym będziesz pracować, nazywa się vars i zawiera zmienne środowiskowe, których easy-rsa używa podczas generowania kluczy. Musisz edytować plik, aby używać własnych wartości zamiast wartości domyślnych, które już tam są. Tak będzie wyglądał mój plik (Listing 10.1).

Lista 10.1. Główne fragmenty pliku /etc/openvpn/easy-rsa/vars

export KEY_COUNTRY="CA"
export KEY_PROVINCE="ON"
export KEY_CITY="Toronto"
export KEY_ORG="Bootstrap IT"
export KEY_EMAIL="[email protected]"
export KEY_OU="IT"

Uruchomienie pliku vars spowoduje przekazanie jego wartości do środowiska powłoki, gdzie zostaną one uwzględnione w zawartości Twoich nowych kluczy. Dlaczego samo polecenie sudo nie działa? Ponieważ w pierwszym kroku edytujemy skrypt o nazwie vars, a następnie go stosujemy. Zastosowanie i oznacza, że ​​plik vars przekazuje swoje wartości do środowiska powłoki, gdzie zostaną one uwzględnione w zawartości Twoich nowych kluczy.

Pamiętaj, aby ponownie uruchomić plik przy użyciu nowej powłoki, aby zakończyć niedokończony proces. Po wykonaniu tej czynności skrypt poprosi Cię o uruchomienie innego skryptu, clean-all, w celu usunięcia całej zawartości z katalogu /etc/openvpn/easy-rsa/keys/:

Książka „Linux w akcji”
Naturalnie następnym krokiem jest uruchomienie skryptu clean-all, a następnie build-ca, który wykorzystuje skrypt pkitool do utworzenia certyfikatu głównego. Zostaniesz poproszony o potwierdzenie ustawień tożsamości dostarczonych przez vars:

# ./clean-all
# ./build-ca
Generating a 2048 bit RSA private key

Następny jest skrypt build-key-server. Ponieważ używa tego samego skryptu pkitool wraz z nowym certyfikatem głównym, zobaczysz te same pytania, aby potwierdzić utworzenie pary kluczy. Klucze zostaną nazwane na podstawie przekazanych argumentów, którymi, jeśli nie uruchomisz wielu VPN na tym komputerze, będzie zazwyczaj serwer, jak w przykładzie:

# ./build-key-server server
[...]
Certificate is to be certified until Aug 15 23:52:34 2027 GMT (3650 days)
Sign the certificate? [y/n]:y
1 out of 1 certificate requests certified, commit? [y/n]y
Write out database with 1 new entries
Data Base Updated

OpenVPN wykorzystuje parametry wygenerowane przez algorytm Diffiego-Hellmana (przy użyciu build-dh) do negocjowania uwierzytelnienia dla nowych połączeń. Utworzony tutaj plik nie musi być tajny, ale musi zostać wygenerowany przy użyciu skryptu build-dh dla aktualnie aktywnych kluczy RSA. Jeśli w przyszłości utworzysz nowe klucze RSA, będziesz musiał także zaktualizować plik Diffiego-Hellmana:

# ./build-dh

Twoje klucze po stronie serwera znajdą się teraz w katalogu /etc/openvpn/easy-rsa/keys/, ale OpenVPN o tym nie wie. Domyślnie OpenVPN będzie szukać kluczy w /etc/openvpn/, więc skopiuj je:

# cp /etc/openvpn/easy-rsa/keys/server* /etc/openvpn
# cp /etc/openvpn/easy-rsa/keys/dh2048.pem /etc/openvpn
# cp /etc/openvpn/easy-rsa/keys/ca.crt /etc/openvpn

Przygotowanie kluczy szyfrujących klienta

Jak już widziałeś, szyfrowanie TLS wykorzystuje pary pasujących kluczy: jeden zainstalowany na serwerze i jeden zainstalowany na zdalnym kliencie. Oznacza to, że będziesz potrzebować kluczy klienta. Nasz stary przyjaciel pkitool jest dokładnie tym, czego potrzebujesz. W tym przykładzie, uruchamiając program w katalogu /etc/openvpn/easy-rsa/, przekazujemy mu argument klienta w celu wygenerowania plików o nazwach klient.crt i klient.key:

# ./pkitool client

Dwa pliki klienta wraz z oryginalnym plikiem ca.crt, który nadal znajduje się w katalogu klucze/, powinny teraz zostać bezpiecznie przesłane do klienta. Ze względu na ich własność i prawa dostępu może to nie być takie proste. Najprostszym podejściem jest ręczne skopiowanie zawartości pliku źródłowego (i tylko tej zawartości) do terminala działającego na pulpicie komputera (zaznacz tekst, kliknij go prawym przyciskiem myszy i wybierz z menu opcję Kopiuj). Następnie wklej to do nowego pliku o tej samej nazwie, który utworzysz w drugim terminalu podłączonym do klienta.

Ale każdy może wycinać i wklejać. Zamiast tego myśl jak administrator, ponieważ nie zawsze będziesz mieć dostęp do GUI, w którym możliwe są operacje wycinania/wklejania. Skopiuj pliki do katalogu domowego użytkownika (aby zdalna operacja scp mogła uzyskać do nich dostęp), a następnie użyj chown, aby zmienić własność plików z root na zwykłego użytkownika innego niż root, aby można było wykonać zdalną akcję scp. Upewnij się, że wszystkie pliki są aktualnie zainstalowane i dostępne. Przeniesiesz je do klienta nieco później:

# cp /etc/openvpn/easy-rsa/keys/client.key /home/ubuntu/
# cp /etc/openvpn/easy-rsa/keys/ca.crt /home/ubuntu/
# cp /etc/openvpn/easy-rsa/keys/client.crt /home/ubuntu/
# chown ubuntu:ubuntu /home/ubuntu/client.key
# chown ubuntu:ubuntu /home/ubuntu/client.crt
# chown ubuntu:ubuntu /home/ubuntu/ca.crt

Mając gotowy zestaw kluczy szyfrujących, musisz poinformować serwer, w jaki sposób chcesz utworzyć VPN. Odbywa się to za pomocą pliku server.conf.

Zmniejszenie liczby naciśnięć klawiszy

Czy jest za dużo pisania? Rozszerzenie za pomocą nawiasów pomoże zredukować te sześć poleceń do dwóch. Jestem pewien, że możesz przestudiować te dwa przykłady i zrozumieć, co się dzieje. Co ważniejsze, będziesz w stanie zrozumieć, jak zastosować te zasady do operacji obejmujących dziesiątki, a nawet setki elementów:

# cp /etc/openvpn/easy-rsa/keys/{ca.crt,client.{key,crt}} /home/ubuntu/
# chown ubuntu:ubuntu /home/ubuntu/{ca.crt,client.{key,crt}}

Konfigurowanie pliku server.conf

Skąd możesz wiedzieć, jak powinien wyglądać plik server.conf? Pamiętasz szablon katalogu easy-rsa, który skopiowałeś z /usr/share/? Po zainstalowaniu OpenVPN pozostał skompresowany plik szablonu konfiguracji, który możesz skopiować do /etc/openvpn/. Wykorzystam fakt, że szablon jest zarchiwizowany i przedstawię przydatne narzędzie: zcat.

Wiesz już o drukowaniu zawartości tekstowej pliku na ekranie za pomocą polecenia cat, ale co się stanie, jeśli plik zostanie skompresowany za pomocą programu gzip? Zawsze możesz rozpakować plik, a wtedy cat z radością go wyśle, ale to jeden lub dwa kroki więcej niż to konieczne. Zamiast tego, jak można się domyślić, możesz wydać polecenie zcat, aby w jednym kroku załadować rozpakowany tekst do pamięci. W poniższym przykładzie zamiast drukować tekst na ekranie, przekierujesz go do nowego pliku o nazwie server.conf:

# zcat 
  /usr/share/doc/openvpn/examples/sample-config-files/server.conf.gz 
  > /etc/openvpn/server.conf
$ cd /etc/openvpn

Odłóżmy na bok obszerną i pomocną dokumentację dołączoną do pliku i zobaczmy, jak może wyglądać po zakończeniu edycji. Zwróć uwagę, że średnik (;) mówi OpenVPN, aby nie czytał ani nie wykonywał następnej linii (Listing 10.2).

Książka „Linux w akcji”
Przejrzyjmy niektóre z tych ustawień.

  • Domyślnie OpenVPN działa na porcie 1194. Możesz to zmienić, na przykład, aby jeszcze bardziej ukryć swoje działania lub uniknąć konfliktów z innymi aktywnymi tunelami. Ponieważ 1194 wymaga minimalnej koordynacji z klientami, najlepiej zrobić to w ten sposób.
  • OpenVPN do przesyłania danych wykorzystuje protokół kontroli transmisji (TCP) lub protokół datagramów użytkownika (UDP). TCP może być nieco wolniejszy, ale jest bardziej niezawodny i z większym prawdopodobieństwem zrozumiały dla aplikacji działających na obu końcach tunelu.
  • Możesz określić dev tun, jeśli chcesz utworzyć prostszy, bardziej wydajny tunel IP, który będzie przenosił zawartość danych i nic więcej. Jeśli natomiast zajdzie potrzeba połączenia wielu interfejsów sieciowych (i sieci, które one reprezentują), tworząc most Ethernet, będziesz musiał wybrać opcję dev tap. Jeśli nie rozumiesz, co to wszystko oznacza, użyj argumentu tun.
  • Kolejne cztery linie podają OpenVPN nazwy trzech plików uwierzytelniających na serwerze oraz utworzonego wcześniej pliku opcji dh2048.
  • Linia serwera ustawia zakres i maskę podsieci, które będą używane do przypisywania adresów IP klientom po zalogowaniu.
  • Opcjonalny parametr push „route 10.0.3.0 255.255.255.0” umożliwia zdalnym klientom dostęp do prywatnych podsieci za serwerem. Aby to zadziałało, wymagane jest również skonfigurowanie sieci na samym serwerze, tak aby podsieć prywatna wiedziała o podsieci OpenVPN (10.8.0.0).
  • Linia port-share localhost 80 umożliwia przekierowanie ruchu klienta przychodzącego na porcie 1194 do lokalnego serwera WWW nasłuchującego na porcie 80. (Będzie to przydatne, jeśli zamierzasz używać serwera WWW do testowania sieci VPN). To działa tylko następnie po wybraniu protokołu TCP.
  • Linie użytkownika nikt i grupa nogroup muszą zostać włączone poprzez usunięcie średników (;). Zmuszanie zdalnych klientów do działania w trybie nikt i nogroup gwarantuje, że sesje na serwerze będą nieuprzywilejowane.
  • log określa, że ​​bieżące wpisy dziennika będą zastępować stare wpisy przy każdym uruchomieniu OpenVPN, podczas gdy log-append dołącza nowe wpisy do istniejącego pliku dziennika. Sam plik openvpn.log jest zapisywany w katalogu /etc/openvpn/.

Ponadto do pliku konfiguracyjnego często dodawana jest również wartość klient-klient, dzięki czemu wielu klientów może widzieć się nawzajem oprócz serwera OpenVPN. Jeśli jesteś zadowolony ze swojej konfiguracji, możesz uruchomić serwer OpenVPN:

# systemctl start openvpn

Ze względu na zmieniający się charakter relacji pomiędzy OpenVPN i systemd, czasami do uruchomienia usługi może być wymagana następująca składnia: systemctl start openvpn@server.

Uruchomienie adresu IP w celu wyświetlenia listy interfejsów sieciowych serwera powinno teraz spowodować wyświetlenie łącza do nowego interfejsu o nazwie tun0. OpenVPN utworzy go do obsługi przychodzących klientów:

$ ip addr
[...]
4: tun0: mtu 1500 qdisc [...]
      link/none
      inet 10.8.0.1 peer 10.8.0.2/32 scope global tun0
          valid_lft forever preferred_lft forever

Może być konieczne ponowne uruchomienie serwera, zanim wszystko zacznie działać w pełni. Następnym przystankiem jest komputer kliencki.

10.1.2. Konfiguracja klienta OpenVPN

Tradycyjnie buduje się tunele z co najmniej dwoma wyjściami (w przeciwnym razie nazwalibyśmy je jaskiniami). Prawidłowo skonfigurowany OpenVPN na serwerze kieruje ruch do i z tunelu po jednej stronie. Ale będziesz potrzebować także oprogramowania działającego po stronie klienta, to znaczy po drugiej stronie tunelu.

W tej sekcji skupię się na ręcznym skonfigurowaniu komputera z systemem Linux, aby działał jako klient OpenVPN. Ale to nie jedyny sposób, w jaki dostępna jest taka możliwość. OpenVPN obsługuje aplikacje klienckie, które można zainstalować i używać na komputerach stacjonarnych i laptopach z systemem Windows lub macOS, a także na smartfonach i tabletach z Androidem i iOS. Szczegóły znajdziesz na openvpn.net.

Pakiet OpenVPN będzie musiał zostać zainstalowany na komputerze klienckim tak samo, jak został zainstalowany na serwerze, chociaż nie ma tutaj potrzeby korzystania z easy-rsa, ponieważ klucze, których używasz, już istnieją. Musisz skopiować plik szablonu client.conf do katalogu /etc/openvpn/, który właśnie utworzyłeś. Tym razem plik nie zostanie spakowany, więc zwykłe polecenie cp wykona to zadanie dobrze:

# apt install openvpn
# cp /usr/share/doc/openvpn/examples/sample-config-files/client.conf 
  /etc/openvpn/

Większość ustawień w pliku client.conf będzie dość oczywista: powinny odpowiadać wartościom na serwerze. Jak widać z poniższego przykładowego pliku, unikalnym parametrem jest zdalny 192.168.1.23 1194, który informuje klienta o adresie IP serwera. Ponownie upewnij się, że jest to adres Twojego serwera. Należy także wymusić na komputerze klienckim weryfikację autentyczności certyfikatu serwera, aby zapobiec ewentualnemu atakowi typu man-in-the-middle. Jednym ze sposobów, aby to zrobić, jest dodanie linii Remote-cert-tls server (Listing 10.3).

Książka „Linux w akcji”
Możesz teraz przejść do katalogu /etc/openvpn/ i wyodrębnić klucze certyfikacyjne z serwera. Zastąp adres IP serwera lub nazwę domeny w przykładzie swoimi wartościami:

Książka „Linux w akcji”
Nic ekscytującego prawdopodobnie się nie wydarzy, dopóki nie uruchomisz OpenVPN na kliencie. Ponieważ musisz przekazać kilka argumentów, zrobisz to z wiersza poleceń. Argument --tls-client mówi OpenVPN, że będziesz działać jako klient i łączyć się poprzez szyfrowanie TLS, a --config wskazuje na Twój plik konfiguracyjny:

# openvpn --tls-client --config /etc/openvpn/client.conf

Przeczytaj uważnie wynik polecenia, aby upewnić się, że jesteś prawidłowo podłączony. Jeśli coś pójdzie nie tak za pierwszym razem, może to być spowodowane niezgodnością ustawień między plikami konfiguracyjnymi serwera i klienta lub problemem z połączeniem sieciowym/zaporą sieciową. Oto kilka wskazówek dotyczących rozwiązywania problemów.

  • Uważnie przeczytaj wynik operacji OpenVPN na kliencie. Często zawiera cenne rady, czego dokładnie nie można zrobić i dlaczego.
  • Sprawdź komunikaty o błędach w plikach openvpn.log i openvpn-status.log w katalogu /etc/openvpn/ na serwerze.
  • Sprawdź dzienniki systemowe na serwerze i kliencie pod kątem komunikatów związanych z OpenVPN i komunikatów czasowych. (journalctl -ce wyświetli najnowsze wpisy.)
  • Upewnij się, że masz aktywne połączenie sieciowe pomiędzy serwerem a klientem (więcej na ten temat w rozdziale 14).

O autorze

Davida Clintona - administrator systemu, nauczyciel i pisarz. Administrował, pisał i tworzył materiały edukacyjne dla wielu ważnych dyscyplin technicznych, w tym systemów Linux, przetwarzania w chmurze (w szczególności AWS) i technologii kontenerowych, takich jak Docker. Napisał książkę Poznaj usługi internetowe Amazon w ciągu miesiąca lunchu (Manning, 2017). Wiele jego kursów szkoleniowych wideo można znaleźć na stronie Pluralsight.com, a linki do innych jego książek (na temat administracji Linuksem i wirtualizacji serwerów) są dostępne pod adresem bootstrap-it.com.

» Więcej szczegółów na temat książki można znaleźć na stronie strona wydawcy
» Spis treści
» Fragment

Dla Khabrozhiteley 25% zniżki przy użyciu kuponu - Linux
Po opłaceniu papierowej wersji książki, e-mailem zostanie wysłana książka elektroniczna.

Źródło: www.habr.com

Dodaj komentarz