<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Maciej[cc] &#187; Linux</title><atom:link href="https://nabozny.io/category/linux/index.xml" rel="self" type="application/rss+xml"/><link>https://nabozny.io/category/linux/</link><description>Blog o programowaniu i nie tylko</description><lastBuildDate>Tue, 12 Mar 2024 10:03:44 +0000</lastBuildDate><language>pl-PL</language><generator>Hugo 0.167.0</generator><item><title>Debugowanie kontenerów w kubernetesie – część 2</title><link>https://nabozny.io/debugowanie-kontenerow-w-kubernetesie-czesc-2/</link><dc:creator>maciej</dc:creator><pubDate>Tue, 12 Mar 2024 10:02:11 +0000</pubDate><category>Cloud</category><category>Linux</category><category>docker</category><category>linux</category><category>k8s</category><category>kubernetes</category><category>sieci</category><guid isPermaLink="true">https://nabozny.io/debugowanie-kontenerow-w-kubernetesie-czesc-2/</guid><description>Po poprzedniej części zapraszam na kolejną, pokazującą jak poradzić sobie z ograniczeniami które wprowadza Kubernetes podczas debugowania aplikacji sieciowych.
Wykonanie pojedynczego polecenia/shelll kubectl -n my_namespace exec -it pod -- /bin/some_command Gdzie zwykle /bin/some_command można …</description><content:encoded>&lt;p&gt;Po poprzedniej &lt;a href="https://nabozny.io/debugowanie-kontenerow-w-chmurze/"&gt;części&lt;/a&gt; zapraszam na kolejną, pokazującą jak poradzić sobie z ograniczeniami które wprowadza Kubernetes podczas debugowania aplikacji sieciowych.&lt;/p&gt;&#10;&lt;h3 id="wykonanie-pojedynczego-poleceniashelll"&gt;Wykonanie pojedynczego polecenia/shelll&lt;/h3&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;kubectl -n my_namespace exec -it pod -- /bin/some_command&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Gdzie zwykle &lt;em&gt;/bin/some_command&lt;/em&gt; można zastąpić &lt;em&gt;/bin/bash&lt;/em&gt;&lt;/p&gt;&#10;&lt;h3 id="kontener-do-debugowania"&gt;Kontener do debugowania&lt;/h3&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;kubectl debug my_pod_name -n my_namespace -it --image=ubuntu:latest /bin/bash&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Powyższe spowoduje uruchomienie nowego kontenera w tym samym podzie. Dzięki temu możemy połączyć się do localhost (127.0.0.1) i sprawdzić jak działają nasze serwisy za pomocą curl/wget. W trybie debug mamy też dostęp do roota, dzięki czemu hardening właściwej aplikacji nie jest aż tak straszny i nie ogranicza nas, np. w zainstalowaniu nowych paczek. Taki kontener nie współdzieli też systemu plików z oryginalnymi kontenerami z poda, dzięki temu nie namieszamy zbyt dużo.&lt;/p&gt;&#10;&lt;h3 id="lepszy-kontener-do-debugowania"&gt;Lepszy kontener do debugowania&lt;/h3&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;kubectl debug my_pod_name -n my_namespace -it --image=ubuntu:latest --share-processes -c container_name --copy-to=mg-debug&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Jest to trochę bardziej rozbudowana wersja poprzedniego polecenia. Odpalając taki kontener do debugowania możemy wybrać jeden z istniejących kontenerów i kubectl skopiuje jego konfiguracje (zamontowane &lt;em&gt;volumes&lt;/em&gt;, sieć i przestrzeń procesów). Zabawa w takim środowisku daje już dość duże możliwości interakcji z innymi kontenerami. Można wysłać przyjacieski sygnał -KILL do procesów, pogrzebać w podmontowanych wolumenach lub sprawdzić sieć, również bez ograniczeń hardeningu.&lt;/p&gt;&#10;&lt;h3 id="shell-noda"&gt;Shell noda&lt;/h3&gt;&#10;&lt;p&gt;Link dla zdesperowanych: &lt;a href="https://github.com/kvaps/kubectl-node-shell"&gt;https://github.com/kvaps/kubectl-node-shell&lt;/a&gt;. Skrypt potrafi uruchomić shell na nodzie kubernetesa. Dzięki niemu możemy dostać się jako root na dowolnego noda z klastra jako root i wprost pogrzebać w systemie. Chyba najbardziej &amp;ldquo;wszechmocna&amp;rdquo; opcja do zabawy w szukanie dziury w całym.&lt;/p&gt;&#10;&lt;p&gt;Miłego debugowania!&lt;/p&gt;&#10;</content:encoded></item><item><title>Co zrobić gdy nie mamy w kontenerze roota i trzeba debugować sieć</title><link>https://nabozny.io/co-zrobic-gdy-nie-ma-dostepu-netstat-ani-root/</link><dc:creator>maciej</dc:creator><pubDate>Wed, 27 Dec 2023 08:24:13 +0000</pubDate><category>Linux</category><guid isPermaLink="true">https://nabozny.io/co-zrobic-gdy-nie-ma-dostepu-netstat-ani-root/</guid><description>Często debugowanie kontenerów w kubernetesie wymaga przeanalizowania ruchu, jaki wchodzi lub wychodzi do systemu. Nie ma z tym problemu jeśli mamy dostęp do konta administratora u możemy uruchomić netstat lub tcpdump aby popatrzeć w pakiety. Co jeśli nie ma do tego dostępu?</description><content:encoded>&lt;p&gt;Często debugowanie kontenerów w kubernetesie wymaga przeanalizowania ruchu, jaki wchodzi lub wychodzi do systemu. Nie ma z tym problemu jeśli mamy dostęp do konta administratora u możemy uruchomić netstat lub tcpdump aby popatrzeć w pakiety. Co jeśli nie ma do tego dostępu?&lt;/p&gt;&#10;&lt;h3 id="znajdź-proces"&gt;Znajdź proces&lt;/h3&gt;&#10;&lt;p&gt;Na początek zacznijmy od znalezienia procesu, który obsługuje ruch na porcie 80:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;$ ps&#10;bash: ps: command not found&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Będzie trudniej&amp;hellip; przejdźmy to katalogu &lt;em&gt;/proc&lt;/em&gt;. Część z podkatalogów odpowiada procesom. Zwykle w kontenerach oraz w Linuksie proces z PID o numerze 1 odpowiada pierwszemu procesowi w systemie/kontenerze (np. &lt;em&gt;/bin/init&lt;/em&gt;). Mamy zatem sporo różnych wpisów w proc:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;$ ls -l /proc&#10;total 0&#10;dr-xr-xr-x 9 root root 0 Dec 19 11:59 1&#10;dr-xr-xr-x 9 nginx nginx 0 Dec 27 08:09 29&#10;dr-xr-xr-x 9 nginx nginx 0 Dec 27 08:09 30&#10;dr-xr-xr-x 9 nginx nginx 0 Dec 27 08:09 31&#10;dr-xr-xr-x 9 nginx nginx 0 Dec 27 08:09 32&#10;dr-xr-xr-x 9 root root 0 Dec 27 08:05 33&#10;dr-xr-xr-x 9 root root 0 Dec 27 08:06 55&#10;dr-xr-xr-x 9 nginx nginx 0 Dec 27 08:09 56&#10;dr-xr-xr-x 9 nginx nginx 0 Dec 27 08:09 63&#10;drwxrwxrwt 2 root root 40 Dec 19 11:59 acpi&#10;dr-xr-xr-x 13 root root 0 Dec 19 11:59 asound&#10;...&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Każdy z nich odpowiada osobnemu procesowi. Po nazwie właściciela można stwierdzić co to za proces, ale aby upewnić się, sprawdźmy wpis &lt;em&gt;exe&lt;/em&gt; w takim katalogu:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;$ ls -lh /proc/29/exe&#10;lrwxrwxrwx 1 nginx nginx 0 Dec 27 08:10 /proc/29/exe -&amp;gt; /usr/sbin/nginx&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;em&gt;exe&lt;/em&gt; to link symboliczny do pliku wykonywalnego w systemie plików. Mamy zatem ustalonego podejrzanego oraz jego proces.&lt;/p&gt;&#10;&lt;h3 id="jakie-porty-są-otwarte-przez-proces"&gt;Jakie porty są otwarte przez proces&lt;/h3&gt;&#10;&lt;p&gt;Aby sprawdzić z czego korzysta dany proces zwykle można urzyć &lt;em&gt;lsof&lt;/em&gt; lub &lt;em&gt;netstat&lt;/em&gt;. W dobrze zabezpieczonych systemach nie mamy takiej możliwości. Zamiast tego możemy podglądnąć listę otwartych prze nginx plików:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;$ ls -lh /proc/29/fd/&#10;total 0&#10;lrwx------ 1 nginx nginx 64 Dec 27 08:13 0 -&amp;gt; /dev/null&#10;l-wx------ 1 nginx nginx 64 Dec 27 08:13 1 -&amp;gt; &amp;#39;pipe:[37844]&amp;#39;&#10;lrwx------ 1 nginx nginx 64 Dec 27 08:13 10 -&amp;gt; &amp;#39;anon_inode:[eventfd]&amp;#39;&#10;lrwx------ 1 nginx nginx 64 Dec 27 08:13 11 -&amp;gt; &amp;#39;anon_inode:[eventfd]&amp;#39;&#10;lrwx------ 1 nginx nginx 64 Dec 27 08:13 12 -&amp;gt; &amp;#39;socket:[40105]&amp;#39;&#10;lrwx------ 1 nginx nginx 64 Dec 27 08:13 13 -&amp;gt; &amp;#39;socket:[40107]&amp;#39;&#10;l-wx------ 1 nginx nginx 64 Dec 27 08:13 2 -&amp;gt; &amp;#39;pipe:[37845]&amp;#39;&#10;lrwx------ 1 nginx nginx 64 Dec 27 08:13 3 -&amp;gt; &amp;#39;socket:[40103]&amp;#39;&#10;l-wx------ 1 nginx nginx 64 Dec 27 08:13 4 -&amp;gt; &amp;#39;pipe:[37845]&amp;#39;&#10;l-wx------ 1 nginx nginx 64 Dec 27 08:13 5 -&amp;gt; &amp;#39;pipe:[37844]&amp;#39;&#10;lrwx------ 1 nginx nginx 64 Dec 27 08:13 6 -&amp;gt; &amp;#39;socket:[40099]&amp;#39;&#10;lrwx------ 1 nginx nginx 64 Dec 27 08:13 7 -&amp;gt; &amp;#39;socket:[40100]&amp;#39;&#10;lrwx------ 1 nginx nginx 64 Dec 27 08:13 8 -&amp;gt; &amp;#39;socket:[40102]&amp;#39;&#10;lrwx------ 1 nginx nginx 64 Dec 27 08:13 9 -&amp;gt; &amp;#39;anon_inode:[eventpoll]&amp;#39;&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Pliki z numerami (dokładniej deskryptorami, możliwe, że kojarzysz to z funkcji &lt;em&gt;open&lt;/em&gt; w C) 0 i 1 to standardowe wejście i wyjście. Za ich pomocą program komunikuje się z użytkownikiem w konsoli. Tutaj jest wejście jest ustawione na &lt;em&gt;/dev/null&lt;/em&gt; a wyjście przekierowane do pipe. To, co nas interesuje, to pliki opisane jako &lt;em&gt;socket:[numer]&lt;/em&gt;. Są to połączenia sieciowe. Nazwa symlinka to deskryptor pliku/socketu widziany po stronie aplikacji. Numer widoczny po &lt;em&gt;socket:&amp;hellip;&lt;/em&gt; to identyfikator obiektu po stronie jądra systemu, tzw. inode.&lt;/p&gt;&#10;&lt;p&gt;Jak sprawdzić który inode odpowiada socketowi, nasłuchującemu na porcie 80? Popatrzmy w plik &lt;em&gt;/proc/net/tcp&lt;/em&gt;:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;$ cat /proc/net/tcp&#10; sl local_address rem_address st tx_queue rx_queue tr tm-&amp;gt;when retrnsmt uid timeout inode &#10; 0: 00000000:0050 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 40099 1 0000000000000000 100 0 0 10 0&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Pokazuje on otwarte połączenia sieciowe. Ostatnia kolumna to inode połączenia (socketu). Jeśli poszukasz jej w &lt;em&gt;/proc/29/fd&lt;/em&gt;, to znajdziesz podobny wpis, dla deskryptora pliku nr &lt;em&gt;6&lt;/em&gt;. Teraz wystarczy popatrzeć na &lt;em&gt;local_address&lt;/em&gt; i zamienić numer portu &lt;em&gt;0050&lt;/em&gt; na wartość dziesiętną i wyjdzie port &lt;em&gt;80&lt;/em&gt;, na którym słucha nasza aplikacja.&lt;/p&gt;&#10;&lt;p&gt;Wszystko to używając jedynie &lt;em&gt;ls&lt;/em&gt; oraz &lt;em&gt;cat&lt;/em&gt; z konta zwykłego użytkownika, które zwykle nie są usuwane podczas zabezpieczania kontenerów&lt;/p&gt;</content:encoded></item><item><title>Debugowanie kontenerów w chmurze</title><link>https://nabozny.io/debugowanie-kontenerow-w-chmurze/</link><dc:creator>maciej</dc:creator><pubDate>Fri, 24 Sep 2021 08:29:21 +0000</pubDate><category>Cloud</category><category>Debugowanie</category><category>Linux</category><guid isPermaLink="true">https://nabozny.io/debugowanie-kontenerow-w-chmurze/</guid><description>Czasami coś nie działa i przyczyna tego za diabła nie chce dać się znaleźć. Poniżej kilka pomocnych rad w debugowaniu w środowisku chmury i kontenerów, czyli “u mnie działa, a na cudzym komputerze nie”</description><content:encoded>&lt;p&gt;Czasami coś nie działa i przyczyna tego za diabła nie chce dać się znaleźć. Poniżej kilka pomocnych rad w debugowaniu w środowisku chmury i kontenerów, czyli &amp;ldquo;u mnie działa, a na cudzym komputerze nie&amp;rdquo;&lt;/p&gt;&#10;&lt;h3 id="problem"&gt;Problem&lt;/h3&gt;&#10;&lt;p&gt;Moja strona nie działa, a devops skomplikował sprawę. Na wejściu do naszego serwisu stoi ELB, ALB lub inny load balancer, który przekierowuje ruch do kontenera zarządzanego przez ECS lub kubernetes. Żeby było trudniej, nie mamy dostępu do czeluści load balancera i wejście na noda klastra też jest ograniczone. Możemy jedynie dostać się do kontenera.&lt;/p&gt;&#10;&lt;p&gt;Porblem pojawia się gdy frontend (UI) wysyła żądanie do backendu. Nie dostajemy poprawnej odpowiedzi, a jedynie błąd 503 Service Unavailable.&lt;/p&gt;&#10;&lt;h3 id="rozwiązanie"&gt;Rozwiązanie&lt;/h3&gt;&#10;&lt;p&gt;Znajdź problem i napraw :)&lt;/p&gt;&#10;&lt;h3 id="rozwiązanie-2"&gt;Rozwiązanie 2&lt;/h3&gt;&#10;&lt;p&gt;Zanim zaczniesz grzebać, upewnij się, że zepsucie danego kontenera nie pociągnie za sobą negatywnych konsekwencji. W przypadku Kubernetesa, ECS lub innych orchestratorów (jakkolwiek to się tłumaczy) usunięcie zepsutego kontenera powinno spowodować automatyczne utworzenie nowego, takiego samego. Oprócz tego dobrą rzeczą będzie kawałek papieru lub gedit do notowania co robimy, aby łatwo dało się zrobić rollback zmian, które są trwałe lub odtworzyć całą ścieżkę.&lt;/p&gt;&#10;&lt;p&gt;Ponadto pomyśl czy żądanie/request, które będziemy naprawiać nie niszczy czegoś po stronie backendu. Jeśli problem sprawia guzik &amp;ldquo;skasuj konto&amp;rdquo; to dwukrotne debugowanie tego samego requestu będzie problematyczne&amp;hellip;&lt;/p&gt;&#10;&lt;h5 id="sprawdź-co-nie-bangla"&gt;Sprawdź co nie bangla&lt;/h5&gt;&#10;&lt;p&gt;Na początek znajdź z użyciem developer tools jakie żądanie sprawia problem. Otwórz &lt;em&gt;developer tools&lt;/em&gt; w przeglądarce i popatrz co się dzieje gdy strona wysyła do backendu request. W dalszej części bardzo pomocne będzie skopiowanie polecenia cURL z dokładnymi parametrami żądania, które wysyła przeglądarka. W zakładce z listą połączeń sieciowych z &lt;em&gt;developer tools&lt;/em&gt; możesz wybrać skopiowanie requestu m.in. jako polecenie programu cURL.&lt;/p&gt;&#10;&lt;p&gt;Ponadto, w ulubionym edytorze tekstu, jakim jest oczywiście &lt;em&gt;pico&lt;/em&gt; albo &lt;em&gt;gedit&lt;/em&gt;, zapisz powyższe polecenie. Często bedzie ono zawierało całą masę nagłówków, id sesji, ciasteczek i innych rzeczy, które weryfikuje backend w celu uwierzytelnienia człowieka. Jako użyteczny dodatek możesz zamienić w takim poleceniu nagłówek &lt;em&gt;User Agent&lt;/em&gt; na własny string. W większości logów user agent pojawia się w każdej linijce logowanego requestu i dzięki temu będzie nam łatwiej przeszukać logi tylko z naszego curla.&lt;/p&gt;&#10;&lt;p&gt;Mając powyższe sprawdź czy curl dostaje tak samo niepoprawną odpowiedź od serwera. Możliwe że już na tym etapie otrzymasz komunikat z błędem i dalsze debugowanie stanie się zbędne.&lt;/p&gt;&#10;&lt;h5 id="logi"&gt;Logi&lt;/h5&gt;&#10;&lt;p&gt;Chyba podstawowa rzecz, dużo grepa, a jeśli to AWS to dużo scrollowania i szukania w cloudwatch. Jeśli nie ma w logach aplikacji z backendu, to możliwe że jest w logach load balancera. Jeśli nie, to&amp;hellip;&lt;/p&gt;&#10;&lt;h3 id="czas-na-grubszy-kaliber"&gt;Czas na grubszy kaliber&lt;/h3&gt;&#10;&lt;p&gt;Czasami błąd jest tak uciążliwy, że ani w logach ani w odpowiedzi backendu nie da się znaleźć jego przyczyny. Pozostaje więc kilka cięższych dział:&lt;/p&gt;&#10;&lt;h5 id="wewnątrz-kontenera--netstat"&gt;Wewnątrz kontenera – netstat&lt;/h5&gt;&#10;&lt;p&gt;Odpal &lt;em&gt;netstat&lt;/em&gt; z pakietu &lt;em&gt;net-tools&lt;/em&gt; w Ubuntu/Debianie. Polecenie &lt;em&gt;netstat -ntap&lt;/em&gt; powinno wypisać wszystkie nawiązane połączenia i otwarte porty. Sprawdź czy wszystko jest tak jak powinno, tj. czy serwer nasłuchuje na odpowiednim porcie, czy widać połączenie z bazą etc.&lt;/p&gt;&#10;&lt;p&gt;Jeśli wszystko jest ok, to spróbuj wykonać to samo podczas wykonywania żądania przez cURL. Jeśli takie żądanie wykonuje się za szybko i nie nadążasz klikać, to wrzuć gdzieś &lt;em&gt;sleep 10&lt;/em&gt;, tylko nie zapomnij go usunąć :) Ponieważ często po zmodyfikowaniu kodu potrzebny jest restart serwisu, a ubicie głównego procesu spowoduje restart kontenera, spróbuj &lt;em&gt;kill -HUP [proces]&lt;/em&gt;. Dawno dawno temu, gdy komputery obsługiwało się bez myszki (i nie przez touch screen) zdarzało się, że coś zerwało połączenie z terminalem, na którym mieliśmy uruchomiony interaktywny proces. Przez &lt;em&gt;SIGHUP&lt;/em&gt; można było poinformować taką odłączoną aplikacje, że straciliśmy z nią połączenie i chcemy aby się zakończyła. Serwery zwykle nie reagowały na to, jednak przez lata &lt;em&gt;SIGHUP&lt;/em&gt; stał się sygnałem do reloadu konfiguracji i często również skryptów, które są wykonywane przez serwer. Dzięki temu prawdopodobnie nie zrestartujesz kontenera, w którym pracujesz.&lt;/p&gt;&#10;&lt;p&gt;Wracając do tematu – mając już wrzuconego gdzieś &lt;em&gt;sleep 10&lt;/em&gt; spróbuj netstat ponownie i zobacz czy nic nie wygląda podejrzanie&lt;/p&gt;&#10;&lt;h5 id="curl-do-kontenera"&gt;cURL do kontenera&lt;/h5&gt;&#10;&lt;p&gt;Kolejnym punktem do odhaczenia jest wysłanie żądania przez cURL bezpośrenio z kontenera do niego samego. Możemy wykorzystać wcześniejsze polecenie cURL. Możemy też dodać opcje wymuszającą na cURL&amp;rsquo;u wyświetlenie nagłówków. Jeśli się powiedzie, to problem prawdopodobnie pojawia się gdzieś wcześniej, np. między load balancerem a kontenerem. Jeśli masz taką możliwość, wykonaj to samo z load balancera.&lt;/p&gt;&#10;&lt;h5 id="nc-do-innych-usług"&gt;nc do innych usług&lt;/h5&gt;&#10;&lt;p&gt;Jeśli to zadziała, dobrze jest sprawdzić połączenie z bazą lub innymi wymaganymi serwisami bezpośrednio z kontenera: &lt;em&gt;nc nazwa_hosta port&lt;/em&gt; ( &lt;em&gt;apt install netcat&lt;/em&gt;). W przypadku bazy można doinstalować jej klienta i spróbować się połączyć, przy okazji wykonując jakiś prosty select na tabelach, do których aplikacja ma mieć dostęp.&lt;/p&gt;&#10;&lt;h5 id="udawanie-innych-serwisów"&gt;Udawanie innych serwisów&lt;/h5&gt;&#10;&lt;p&gt;Kolejnym trikiem, który może się przydać jest odpalenie czegoś, co udaje bazę lub inny serwis. Pozwoli to poznać nam jakie żądania są wysyłane do sąsiednich usług. Może to być przydatne gdy nie mamy możliwości zainstalowania &lt;em&gt;tcpdump&lt;/em&gt; lub jest to zabronione.&lt;/p&gt;&#10;&lt;p&gt;Jak to zrobić aby stało się to tylko lokalnie, bez psucia całej infrastruktury? Sprawdź jaki host jest ustawiony jako np. mikroserwis, który chcesz podsłuchać (jeśli używasz po IP, to sorry&amp;hellip;). Następnie wpisz w &lt;em&gt;/etc/hosts&lt;/em&gt; nowy wpis: &lt;em&gt;127.0.0.1 [nazwa hosta]&lt;/em&gt; i odpal w kontenerze &lt;em&gt;nc -l [port usługi]&lt;/em&gt;. W Linuksie pierwszeństwo w ustalaniu adresów przypisanych do hostów ma plik /etc/hosts, dzięki czemu dodając do niego nowy wpis wymusimy aby nasz kontener połączył się z localhost zamiast właściwego hosta. Uruchomiony program &lt;em&gt;nc&lt;/em&gt; pozwoli wypisać to, co miało zostać wysłane przez aplikację z naszego kontenera do serwisu. Pamiętaj, aby usunąć później wpis z &lt;em&gt;/etc/hosts&lt;/em&gt; :)&lt;/p&gt;&#10;&lt;h5 id="podglądnięcie-zmiennych-środowiskowych"&gt;Podglądnięcie zmiennych środowiskowych&lt;/h5&gt;&#10;&lt;p&gt;Często zanim połączymy się z bazą lub podglądniemy ruch sieciowy do danego hosta warto sprawdzić jakie zmienne znają aplikacje, które są już uruchomione w kontenerze. Czasami przez błąd w konfiguracji aplikacja może nie mieć możliwości uzyskania tych zmiennych, bo np. zabrakło uprawnienia IAM do odpytania Secret Parameter Store. Może też to wynikać ze zmienionego API lub biblioteki – zamiast jednego requestu o listę wszystkich parametrów odpytuje o każdy z osobna, do czego IAM już nie uprawnia.&lt;/p&gt;&#10;&lt;p&gt;Wykonaj &lt;em&gt;ps aux&lt;/em&gt;. Zwykle pierwszy proces będzie znał tylko te parametry, które są przekazywane bezpośrednio przez dockera. Wszelkie inne, uzyskiwane z chmury są znane późniejszym procesom, po pełnej inicjalizacji środowiska. Możesz podglądnąć jeden z wyższych PID&amp;rsquo;ów (ale nie z naszej sesji!) przez wpis w &lt;em&gt;/proc/[pid]/environ&lt;/em&gt;:&lt;/p&gt;&#10;&lt;p&gt;&lt;em&gt;sudo cat /proc/123/environ | tr &amp;lsquo;\0&amp;rsquo; &amp;lsquo;\n&amp;rsquo;&lt;/em&gt;&lt;/p&gt;&#10;&lt;p&gt;Poszczególne zmienne są rozdzielone zerami (znakami o kodzie zero), stąd &lt;em&gt;tr&lt;/em&gt;, który zmieni zera na nowe linie. Jeśli wygląda ok, to pijemy kolejną kawę i szukamy dalej&lt;/p&gt;&#10;&lt;h5 id="ponownie-netstat-lsof"&gt;Ponownie netstat, lsof&lt;/h5&gt;&#10;&lt;p&gt;Sprawdź ulimit i porównaj z ilością połączeń i otwartych plików w kontenerze. W zależności od konfiguracji ten parametr przy dużej ilości połączeń może stwarzać problemy&lt;/p&gt;&#10;&lt;h5 id="podglądanie-z-czym-się-łączy-aplikacja"&gt;Podglądanie z czym się łączy aplikacja&lt;/h5&gt;&#10;&lt;p&gt;Sprawdzisz to przez &lt;em&gt;tcpdump udp port 53&lt;/em&gt;. W trakcie działania sniffera możesz wykonać żądanie przez cURL i sprawdzić co się dzieje i z czym się łączy aplikacja. Jeśli używasz IP, to zapytanie tcpdump będzie trochę inne.&lt;/p&gt;&#10;&lt;h5 id="strace-do-czytania-logów"&gt;strace do czytania logów&lt;/h5&gt;&#10;&lt;p&gt;Jeśli cloudwatch szczególnie uwiera tu i ówdzie, przez co nie możesz znaleźć logów, warto sprawdzić co aplikacja wypisuje na standardowe wyjście. Nightmare w kontenerze? Nie :) Spróbuj:&lt;br&gt;&#10;&lt;em&gt;strace --attach=[pid serwera] -e write=1,2&lt;/em&gt;&lt;br&gt;&#10;Powyższe podłączy &amp;ldquo;debuger&amp;rdquo; strace do procesu i będzie wyłapywało jedynie wywołania systemowe funkcji write do plików o fd=1 i 2. Oznaczają one odpowiednio &lt;em&gt;stdout&lt;/em&gt; i &lt;em&gt;stderr&lt;/em&gt;.&lt;/p&gt;&#10;&lt;p&gt;Nie jest to najwygodniejsza metoda oglądania logów, ale pozwala użyć ukochanego grepa i awk&amp;rsquo;a, co w przypadku cloudwatch nie zawsze jest najwygodniejsze i najszybsze.&lt;/p&gt;&#10;&lt;p&gt;Dla przykładowego &lt;em&gt;ls&lt;/em&gt; wykonanego przez ssh:&lt;/p&gt;&#10;&lt;p&gt;&lt;img src="https://nabozny.io/uploads/2021/09/strace-ssh.png" alt="" width="635" height="61" class="alignnone size-full" loading="lazy" decoding="async"&gt;&lt;/p&gt;&#10;&lt;p&gt;otrzymamy wynik ze strace:&lt;br&gt;&#10;&lt;img src="https://nabozny.io/uploads/2021/09/strace-out.png" srcset="https://nabozny.io/uploads/2021/09/strace-out_hu_ff789cf743115bfc.png 600w, https://nabozny.io/uploads/2021/09/strace-out.png 1004w" sizes="(max-width: 767px) 89vw, (max-width: 1000px) 54vw, (max-width: 1071px) 543px, 580px" alt="" width="1004" height="690" class="alignnone size-full" loading="lazy" decoding="async"&gt;&lt;br&gt;&#10;Nie jest to najczytelniejsze, ale odpowiednio okraszając to grep&amp;rsquo;em i wycinając wszystkie znaki sterowania terminalem (kolory, znaki specjalne etc.) możemy uzyskać dość czytelny wgląd w to, co się dzieje. Powyższa metoda zadziała też na połączeniach sieciowych, w tym na tych, któ©e są już odszyfrowane i idą np. do fcgi po systemowym sockecie.&lt;/p&gt;&#10;&lt;h3 id="na-koniec"&gt;Na koniec&lt;/h3&gt;&#10;&lt;p&gt;Jeśli nie udało się nic znaleźć, a aplikacja dalej się sypie, to pozostaje stara dobra metoda&amp;hellip; wyłącz i włącz kontener, później instancje, a na koniec pogadaj z frontendowcami i backendowcami którzy muszą wysyłać to problematyczne żądanie. Chociaż pogadać i zrozumieć jest dobrze przed całym powyższym szukaniem dziury w całym ;)&lt;/p&gt;</content:encoded></item><item><title>Szyfrowany dysk sieciowy dla potrzeb serwerowych</title><link>https://nabozny.io/szyfrowany-dysk-sieciowy-dla-potrzeb-serwerowych/</link><dc:creator>maciej</dc:creator><pubDate>Mon, 26 Oct 2020 07:05:44 +0000</pubDate><category>Linux</category><category>Kryptografia</category><guid isPermaLink="true">https://nabozny.io/szyfrowany-dysk-sieciowy-dla-potrzeb-serwerowych/</guid><description>Poniżej przedstawiam kilka sposobów na wykorzystanie przestrzeni dyskowej zdalnej maszyny w sposób zabezpieczający w pełni nasze dane. Założenie konfiguracji jest jedno – maszyna udostępniająca swój dysk nie może mieć wglądu w nasze dane, które przechowujemy ani w klucze, którymi dane są szyfrowane. …</description><content:encoded>&lt;p&gt;Poniżej przedstawiam kilka sposobów na wykorzystanie przestrzeni dyskowej zdalnej maszyny w sposób zabezpieczający w pełni nasze dane. Założenie konfiguracji jest jedno – maszyna udostępniająca swój dysk nie może mieć wglądu w nasze dane, które przechowujemy ani w klucze, którymi dane są szyfrowane. Całość komunikacji ma być w pełni bezpieczna, a jedyny dostęp do danych ma być możliwy jedynie po ich odszyfrowaniu w locie, już na komputerze klienta.&lt;/p&gt;&#10;&lt;p&gt;Dzięki temu przejęcie docelowej maszyny nie będzie oznaczało od razu wycieku naszych danych. Ma to być zapewnione począwszy od dostępu do samego jej dysku, aż po grzebanie w RAM&amp;rsquo;ie, gdzie mogą być przechowywane klucze szyfrujące, gdyby szyfrowanie odbywało się po zdalnej stronie. Kolejnym wymogiem jest aby sposób przechowywania danych był możliwy w środowisku serwerowym, gdzie chcemy zautomatyzować cały proces montowania i dostępu do danych, a więc bez żadnych UI oraz webowych interfejsów.&lt;/p&gt;&#10;&lt;h3 id="duplicity"&gt;Duplicity&lt;/h3&gt;&#10;&lt;p&gt;Najprostszym i zarazem najmniej funkcjonalnym na codzień rozwiązaniem jest zaprzęgnięcie narzędzia do backupów &lt;em&gt;duplicity&lt;/em&gt;. Instalujemy go za pomocą:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;sudo apt install duplicity&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;i od razu możemy korzystać. Jest to jeden z wygodniejszych sposobów na tworzenie backupów:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;export PASSPHRASE=&amp;#34;ala ma kota&amp;#34;&#10;duplicity full /home/shrek rsync://shrek@remote-host//backups/shrek&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Plusy:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Wsparcie dla przeróżnych backendów, od ssh, S3 aż po różne egzotyczne chmury&lt;/li&gt;&#10;&lt;li&gt;Pozwala tworzyć pełne kopie jak również przyrostowe, co może ograniczyć ilość używanego miejsca&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;Wady:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Działa jedynie jako backup na żądanie, nie da się wykorzystywać do bieżących operacji, tak jak system plików&lt;/li&gt;&#10;&lt;li&gt;Potrafi się wysypać przy dużych ilościach plików&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h3 id="ecryptfs-po-nfs"&gt;Ecryptfs po NFS&lt;/h3&gt;&#10;&lt;p&gt;Kolejnym, trochę wygodniejszym do codziennego użytku narzędziem jest połączenie NFS i ecryptfs. Z racji, że jest to połączenie dwóch różnych systemów plików, potrafi sprawiać problemy podczas usypiania komputera lub gdy wystąpią problemy z siecią.&lt;/p&gt;&#10;&lt;p&gt;Instalacja i uruchomienie po stronie klienta (zakładam, że gdzieś stoi serwer NFS):&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;sudo apt install ecryptfs-utils nfs-utils&#10;&#10;mkdir /.backup&#10;mkdir /backup&#10;&#10;sudo mount -t nfs zdalny_host:/zasob_nfs /.backup&#10;sudo mount -t ecryptfs /.backup /backup&#10;# Tutaj ecryptfs poprosi o podanie nowego hasła, metody szyfrowania i kilku innych detali&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Korzystając z takiego połączenia systemów plików nasz lokalny komputer pokaże w katalogu &lt;em&gt;/backup&lt;/em&gt; odszyfrowaną zawartość katalogu &lt;em&gt;/.backup&lt;/em&gt;. Każdy plik lub katalog w &lt;em&gt;/backup&lt;/em&gt; odpowiada w ten sposób zaszyfrowanemu plikowi lub katalogowi w &lt;em&gt;/.backup&lt;/em&gt;. Dzięki zastosowaniu NFS wszelkie dane, które zapisujemy w /backup są w formie zaszyfrowanej przesyłane przez sieć do hosta, który udostępnia swój katalog przez NFS.&lt;/p&gt;&#10;&lt;p&gt;Plusy:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Działa na żywo, jak zwykły system plików&lt;/li&gt;&#10;&lt;li&gt;Szyfrowanie jest wykonywane w całości po stronie klienta&lt;/li&gt;&#10;&lt;li&gt;Nie angażuje sterownika plików, dzięki czemu trudniej o uszkodzenie plików na poziomie systemu plików&lt;/li&gt;&#10;&lt;li&gt;Łatwe wykonywanie bezpiecznego backupu zaszyfrowanych danych – wystarczy rsync&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;Wady:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Czasami potrafi mocno zwolnić, zwłaszcza gdy mamy duży ping lub powolne łącze&lt;/li&gt;&#10;&lt;li&gt;Zawieszenie się lub zerwanie połączenia NFS skutecznie blokuje szyfrowany system plików, przez co procesy utykają na dostępie do plików w przypadku problemów sieciowych&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h3 id="nbd-i-luks"&gt;NBD i LUKS&lt;/h3&gt;&#10;&lt;p&gt;Ostatnim rozwiązaniem jest wykorzystanie serwera Network Block Device, który udostępni przez sieć całe urządzenie blokowe oraz systemu LUKS, który potrafi szyfrować całe urządzenia blokowe, ponad systemem plików. Dla zestawienia powyższych narzędzi nasz serwer będzie jedynie udostępniać pojedynczą partycję przez NBD, a my jako klient wykorzystamy mapowanie i odszyfrowanie jej przez driver LUKS, a następnie zamontujemy tak odszyfrowane urządzenie jako partycję ext4.&lt;/p&gt;&#10;&lt;p&gt;Instalacja po stronie klienta:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;sudo apt install cryptsetup nbd-client&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Instalacja po stronie serwera:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;sudo apt install nbd-server&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Następnie trzeba dodać plik eksportujący urządzenie (np. partycja /dev/sda4) do pliku /etc/nbd-server/conf.d/backup:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;[backup]&#10; exportname = /dev/sda4&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Pierwsze montowanie i formatowanie partycji po stronie klienta:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;sudo nbd-client -N backup 10.11.22.33 /dev/nbd0&#10;&#10;# Sformatowanie dysku do luks i odszyfrowanie go. Po obu poleceniach cryptsetup zapyta o hasło&#10;sudo cryptsetup luksFormat /dev/nbd0&#10;sudo cryptsetup luksOpen /dev/nbd0 backup&#10;&#10;# cryptsetup stworzy plik backup, który reprezentuje odszyfrowane urządzenie /dev/nbd0:&#10;sudo mkfs.ext4 /dev/mapper/backup&#10;&#10;# Na koniec stwórzmy katalog, gdzie będziemy montować naszą partycję&#10;mkdir /backup&#10;&#10;sudo cryptsetup luksClose backup&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Montowanie i odszyfrowanie za każdym kolejnym razem:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;sudo nbd-client -N backup 10.11.22.33 /dev/nbd0&#10;sudo cryptsetup luksOpen /dev/nbd0 backup&#10;sudo mount /dev/mapper/backup /backup&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;i gotowe!&lt;/p&gt;&#10;&lt;p&gt;Zalety:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;W mojej opini najszybsze i najbardziej odporne na problemy sieciowe rozwiązanie&lt;/li&gt;&#10;&lt;li&gt;Pozwala w pełni wykorzystać dowolny system plików&lt;/li&gt;&#10;&lt;li&gt;W razie problemów dane można również odszyfrować na hoście lub dowolnym innym komputerze&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;Wady:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Duże ryzyko uszkodzenia systemu plików w razie problemu z siecią&lt;/li&gt;&#10;&lt;li&gt;Utrudniony backup całego dysku&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h3 id="podsumowanie"&gt;Podsumowanie&lt;/h3&gt;&#10;&lt;p&gt;Kombinacji i różnych narzędzi umożliwiających podobne zabezpieczenie jest cała masa, zwłaszcza, jeśli nie ograniczymy się do tych typowo konsolowych. Powyższe jednak pozwolą w miarę dobrze zabezpieczyć się na wypadek przejęcia maszyny hostującej nasze dane i to w sposób dość &amp;ldquo;paranoiczny&amp;rdquo;. Zarówno dane jak i klucze deszyfrujące nie są znane po stronie serwera. Może to być zetem sposób na ochronę nawet w najbardziej ekstremalnych przypadkach – zapobiega wyciągnięciu kluczy np. z pamięci wirtualnej maszyny po uśpieniu jej. Natomiast z racji tego, że nasz klient nie posiada samych zaszyfrowanych danych bezpośrednio u siebie, to w przypadku przejęcia fizycznej maszyny, która te dane przetwarza pozostają one dalej bezpieczne, w formie zaszyfrowanej.&lt;/p&gt;&#10;&lt;p&gt;Do przechowywania dużych ilości danych można w tym przypadku wykorzystać na przykład tanie serwery z oferty kimsufi (nie, nie płacą mi za to :), z dyskami do 2TB w parze z raspberry pi hostowanym u siebie pod biurkiem lub wirtualną maszyną w innej chmurze.&lt;/p&gt;</content:encoded></item><item><title>Kubernetes – jak działa cluster IP</title><link>https://nabozny.io/kubernetes-jak-dziala-cluster-ip/</link><dc:creator>maciej</dc:creator><pubDate>Sun, 29 Dec 2019 13:27:08 +0000</pubDate><category>Cloud</category><category>Linux</category><category>linux</category><category>kubernetes</category><category>iptables</category><guid isPermaLink="true">https://nabozny.io/kubernetes-jak-dziala-cluster-ip/</guid><description>Niedawno pisałem o implementacji sieci opartej o Flannel. Zabrakło tam jednak jednej istotnej rzeczy – clusterIP. W tym poście przeczytasz jak dokładnie jest to zaimplementowane.</description><content:encoded>&lt;p&gt;Niedawno &lt;a href="https://nabozny.io/kubernetes-co-w-sieci-piszczy/"&gt;pisałem&lt;/a&gt; o implementacji sieci opartej o Flannel. Zabrakło tam jednak jednej istotnej rzeczy – clusterIP. W tym poście przeczytasz jak dokładnie jest to zaimplementowane.&lt;/p&gt;&#10;&lt;p&gt;Jeśli patrzyłeś na organizację interfejsów, adresów itd., to pewnie rzuciło Ci się w oczy, że nigdzie nie było routingu ani żadnego innego śladu po adresach IP przydzielonych do serwisów. Jeśli korzystasz z domyślnej konfiguracji, jaką Kubernetes daje, to jest to pula 10.96.0.0/16. Z niej można wybrać dowolny adres IP który będzie dostępny w całym klastrze Kubernetesa i routowany do konkretnego serwisu:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;apiVersion: v1&#10;kind: Service&#10;metadata:&#10; name: dinemic-io&#10; labels:&#10; app: dinemic-io&#10;spec:&#10; ports:&#10; - port: 80&#10; selector:&#10; app: mnabozny-pl&#10; clusterIP: 10.96.0.123&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Serwis za pomocą definicji z pola &lt;em&gt;selector&lt;/em&gt; wyszukuje odpowiednie aplikacje, do których będzie przekierowany ruch na port 80. Będąc w dowolnym miejscu klastra (node lub K8s master) zawsze będziemy mogli połączyć się z tym adresem, na port 80. Patrząc natomaist w tablicę routingu, nie znajdziemy ani jednego wpisu wskazującego na taką pulę adresów:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;maciek&amp;gt; ip route show&#10;default via 192.168.1.1 dev eth0 onlink &#10;10.0.0.0/24 dev eth1 proto kernel scope link src 10.0.0.1 &#10;10.244.0.0/24 dev cni0 proto kernel scope link src 10.244.0.1 &#10;10.244.1.0/24 via 10.244.1.0 dev flannel.1 onlink &#10;10.244.2.0/24 via 10.244.2.0 dev flannel.1 onlink &#10;10.244.3.0/24 via 10.244.3.0 dev flannel.1 onlink &#10;10.244.4.0/24 via 10.244.4.0 dev flannel.1 onlink &#10;...&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Pula adresów &lt;em&gt;10.0.0.0/24&lt;/em&gt; to fizyczna sieć klastra, w którym działa Kubernetes. &lt;em&gt;10.244.X.0/24&lt;/em&gt; to pula adresów, które Kubernetes przydziela poszczególnym podom na nodach (gdzie X odpowiada nodowi, a konkretny numerek podowi).&lt;/p&gt;&#10;&lt;p&gt;Traceroute do adresu powyższego serwisu wychodzi zawsze poza moją sieć, zgodnie z tym, co mówi tablica routingu. Wget na http://10.96.0.123:80 zawsze magicznie dociera do celu. Gdzie więc czai się ta magia?&lt;/p&gt;&#10;&lt;h3 id="iptables-dla-clusterip"&gt;Iptables dla clusterIP&lt;/h3&gt;&#10;&lt;p&gt;Popatrzmy w ipsables. Za pomocą &lt;em&gt;sudo iptables-save&lt;/em&gt; można wylistować wszystkie regułki, które są aktualnie załadowane do jądra. Jest tego dość sporo, dla tego można wspomóc się grepem:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;maciek&amp;gt; sudo iptables-save | grep 10.96.0.123&#10;-A KUBE-SERVICES ! -s 10.244.0.0/16 -d 10.96.0.123/32 -p tcp -m comment --comment &amp;#34;cloudover/dinemic-io: cluster IP&amp;#34; -m tcp --dport 80 -j KUBE-MARK-MASQ&#10;-A KUBE-SERVICES -d 10.96.0.123/32 -p tcp -m comment --comment &amp;#34;cloudover/dinemic-io: cluster IP&amp;#34; -m tcp --dport 80 -j KUBE-SVC-AA7UXNSLITSSEFAY&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Do łańcucha &lt;em&gt;KUBE-SERVICES&lt;/em&gt; jest przekierowywany cały ruch z łańcucha &lt;em&gt;INPUT&lt;/em&gt;. Pierwsza z powyższych reułek oznacza pakiety, które mają być oznaczone do MASQUERADE. Druga natomiast przekazuje je do konkretnego łańcucha – KUBE-SVC-AA7UXNSLITSSEFAY. Obie reguły działają dla docelowego adresu 10.96.0.123, na docelowym porcie 80.&lt;/p&gt;&#10;&lt;h3 id="łańcuch-dla-konkretnego-serwisu"&gt;Łańcuch dla konkretnego serwisu&lt;/h3&gt;&#10;&lt;p&gt;W powyższym outpucie z iptables-save można zobaczyć, że druga regułka przekierowuje druch do konkretnego łańcucha, odpowiadającego serwisowi – KUBE-SVC-AA7UXNSLITSSEFAY. Przyjżyjmy się mu bliżej:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;maciek&amp;gt; sudo iptables -nL KUBE-SVC-AA7UXNSLITSSEFAY -t nat&#10;Chain KUBE-SVC-AA7UXNSLITSSEFAY (1 references)&#10;target prot opt source destination &#10;KUBE-SEP-Q3TQA7ZS25JCTF3F all -- 0.0.0.0/0 0.0.0.0/0&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Powyższy serwis przekierowuje cały ruch (100%) do łańcucha odpowiadającemu konkretnemu podowi, na port 80, robiąc przy okazji DNAT:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;maciek&amp;gt; sudo iptables -nL KUBE-SEP-Q3TQA7ZS25JCTF3F -t nat&#10;Chain KUBE-SEP-Q3TQA7ZS25JCTF3F (1 references)&#10;target prot opt source destination &#10;KUBE-MARK-MASQ all -- 10.244.3.115 0.0.0.0/0 &#10;DNAT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp to:10.244.3.115:80&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Czyli, podsumowując, dowolny pakiet wchodzący do noda (INPUT) lub wychodzący z niego (OUTPUT) jest procesowany przez łańcuch KUBE-SERVICES. W nim natomiast K8S dodaje pojedyncze regułki, które wyłapują ruch kierowany do poszczególnych clusterIP przydzielonych do serwisów. Każdy serwis i clusterIP posiada swój osobny łańcuch KUBE-SVC-&amp;hellip;, w którym jest rozdzielany ruch do poszczególnych podów i ich łańcuchów, w których jest realizowany DNAT.&lt;/p&gt;&#10;&lt;h3 id="serwis-dla-wielu-podów"&gt;Serwis dla wielu podów&lt;/h3&gt;&#10;&lt;p&gt;Powyższy przykład pokazywał wpisy dla serwisu, który przekazuje ruch do jednego poda. Co jeśli przeskalujemy go do dwóch instancji? Szybkie &lt;em&gt;kubectl apply -k .&lt;/em&gt; i po chwili mamy kilka zmian! Chwila oznacza dokładnie tyle, ile trzeba aby nowy pod się uruchomił i zaczął działać. Popatrzmy jeszcze raz w całe iptables. KUBE-SYSTEM pozostaje bez zmian, dalej kieruje ruch dla clusterIP 10.96.0.123 do naszego serwisu, w łańcuchu &lt;em&gt;KUBE-SVC-AA7UXNSLITSSEFAY&lt;/em&gt;.&lt;/p&gt;&#10;&lt;p&gt;Natomiast sam łańcuch &lt;em&gt;KUBE-SVC-AA7UXNSLITSSEFAY&lt;/em&gt; ma już nowe wpisy:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;&amp;gt; sudo iptables -nL KUBE-SVC-AA7UXNSLITSSEFAY -t nat&#10;Chain KUBE-SVC-AA7UXNSLITSSEFAY (1 references)&#10;target prot opt source destination &#10;KUBE-SEP-Q3TQA7ZS25JCTF3F all -- 0.0.0.0/0 0.0.0.0/0 statistic mode random probability 0.50000000000&#10;KUBE-SEP-OCALVIKCSC2PZKNJ all -- 0.0.0.0/0 0.0.0.0/0&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Dodana została na początku nowa regułka kierująca 50% ruchu (popatrz na komentarz – statistic mode, z probability 0.5) do nowego poda, którego ruch jest kształtowany przez łancuch &lt;em&gt;KUBE-SEP-Q3TQA7ZS25JCTF3F&lt;/em&gt;. Pozostałe 50% ruchu wpada w ostatnią regułkę, która jest tam od początku. Jeśli przeskalujemy nasz deployment do większej ilości podów, to udział procentowy odpowiednio spadnie do 100/N.&lt;/p&gt;&#10;&lt;h3 id="skalowalność-samego-cluster-ip"&gt;Skalowalność samego cluster IP&lt;/h3&gt;&#10;&lt;p&gt;Można mieć wrażenie, że każdy pod w klastrze K8S jest w pewnym stopniu single point of failure i jest to prawdą. Natomiast dołożenie nad podami i naszymi aplikacjami abstrakcyjnej warstwy serwisów, które są czymś w rodzaju load balancera, pozwala bardzo łatwo ograniczyć ten słaby punkt. Jeśli nasze pody z aplikacją będą odpowiednio rozproszone po klastrze, w dużej ilości instancji, to awaria dowolnego poda nie wpłynie na dostępność naszego serwisu.&lt;/p&gt;&#10;&lt;p&gt;A co z samym clusterIP? Zauważ, że to konkretne IP, na które tu patrzyliśmy nie jest nigdzie &amp;ldquo;fizycznie&amp;rdquo; widoczne. Każdy host w klastrze K8S posiada swój własny zestaw regułek, zwykle spójny z innymi, przynajmniej pod kątem clusterIP. Próbując dostać się do konkretnego serwisu, właśnie przez clusterIP tak na prawdę iptables losuje nam IP poda, na który będziemy przekierowani i DNAT&amp;rsquo;owani.&lt;/p&gt;&#10;&lt;p&gt;Również dzięki temu można przekierować/przeroutować ruch np. z VPN lub zewnętrznej sieci do dowolnego noda, który stanie się wtedy gateway&amp;rsquo;em. Taki node po otrzymaniu pakietu do jednego z clusterIP przekieruje takie zapytanie dalej, do odpowiedniego poda.&lt;/p&gt;&#10;&lt;h3 id="troubleshooting"&gt;Troubleshooting&lt;/h3&gt;&#10;&lt;p&gt;A wszystko to, bo ostatnio pomieszałem labelki w selektorze aplikacji www i mysql&amp;rsquo;a :) około połowa połączeń była odrzucana, przez co strona częściej nie działała niż działała. Jeśli doświadczasz losowego connection refused, to prawdopodobnie właśnie przy load balancingu/losowaniu poda K8S przekierowuje Twoje zapytanie do innego poda, który w danej chwili ma uruchomioną inną usługę. Po sprawdzeniu, zmianie labelek i przeładowaniu całego serwisu wszystko zaczyna działać.&lt;/p&gt;</content:encoded></item><item><title>Kubernetes – co w sieci piszczy</title><link>https://nabozny.io/kubernetes-co-w-sieci-piszczy/</link><dc:creator>maciej</dc:creator><pubDate>Sun, 24 Nov 2019 10:44:57 +0000</pubDate><category>Cloud</category><category>Linux</category><category>cloud</category><category>linux</category><category>k8s</category><category>kubernetes</category><category>sieci</category><guid isPermaLink="true">https://nabozny.io/kubernetes-co-w-sieci-piszczy/</guid><description>Zróbmy wycieczkę pakietem od poda, aż po default gateway całego klastra Kubernetesa. Post ma na celu ułatwienie debugowania ewentualych problemów sieciowych Kubernetesa z Flannelem.</description><content:encoded>&lt;p&gt;Zróbmy wycieczkę pakietem od poda, aż po default gateway całego klastra Kubernetesa. Post ma na celu ułatwienie debugowania ewentualych problemów sieciowych Kubernetesa z Flannelem.&lt;/p&gt;&#10;&lt;p&gt;Skoro to czytasz, to zakładam, że masz jako takie pojęcie czym się je Kubernetesa, czym są pody i interfejsy sieciowe. Dobrze, gdybyś też wiedział conieco o kontenerach Dockera. Na tapetę weźmiemy (nie)działającą instalację, która składa się z kilku kontenerów i sieci obsługiwanej przez Flannel. Zaczynajmy!&lt;/p&gt;&#10;&lt;h5 id="wejdźmy-do-poda"&gt;Wejdźmy do poda&lt;/h5&gt;&#10;&lt;p&gt;A zanim wejdziemy, zobaczmy jakie mamy pody:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;master# kubectl get pods -n cloudover&#10;NAME READY STATUS RESTARTS AGE&#10;cloudover-io-8xxxyyy886-6c86k 1/1 Running 2 10d&#10;cloudover-io-8xxxyyy886-jx479 1/1 Running 2 10d&#10;cloudover-org-xxxyyyc48-4j4j4 1/1 Running 2 10d&#10;cloudover-org-xxxyyyc48-jv59p 1/1 Running 2 10d&#10;dinemic-io-599xxxyyy8-86d49 1/1 Running 2 20d&#10;dinemic-io-599xxxyyy8-t4pq6 1/1 Running 2 20d&#10;dinemic-io-backup-15xxxyyy20-4t2xr 0/1 Completed 0 2d17h&#10;dinemic-io-backup-15xxxyyy20-2x7pp 0/1 Completed 0 41h&#10;dinemic-io-backup-15xxxyyy20-7k8zs 0/1 Completed 0 17h&#10;dinemic-io-mysql-79yyyxxx6d-wfdrh 1/1 Running 2 20d&#10;mnabozny-pl-56xxxyyy45-p9jp2 1/1 Running 2 20d&#10;mnabozny-pl-56xxxyyy45-plzfq 1/1 Running 2 20d&#10;mnabozny-pl-backup-157xxxyyy0-q76s7 0/1 Completed 0 125m&#10;mnabozny-pl-backup-157xxxyyy0-vrzgt 0/1 Completed 0 65m&#10;mnabozny-pl-backup-157xxxyyy0-nt5mm 0/1 Completed 0 5m20s&#10;mnabozny-pl-mysql-86xxxyyy55-q7nhn 1/1 Running 2 20d&#10;pkg-cloudover-org-84xxxyyy78-dxzxr 1/1 Running 2 10d&#10;pkg-cloudover-org-84xxxyyy78-tqb7w 1/1 Running 2 10d&#10;pkg-dinemic-io-xxxyyyfc77-2729k 1/1 Running 2 10d&#10;pkg-dinemic-io-xxxyyyfc77-4vjsq 1/1 Running 2 10d&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Mamy więc kilka podów z różnych moich serwisów oraz zakończone cron joby backupu danych (Completed). Weźmy na warsztat poda &lt;em&gt;pkg-dinemic-io-xxxyyyfc77-2729k&lt;/em&gt;, który sprawia problemy i zobaczmy jak wygląda jego sieć. Żeby było czym popatrzeć, musimy doinstalować potrzebne narzędzia (polecenie ip). Domyślnie, obraz dockera, z którego odpalony jest ten serwis, jest dość mocno okrojony:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;master# kubectl exec -n cloudover -it pkg-dinemic-io-... bash&#10;apt update&#10;apt install -yqq iproute2 traceroute&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Kolejną rzeczą jest sprawdzenie na którym nodzie uruchomiony jest pod, który chcemy zbadać i doinstalowanie tcpdumpa.&lt;/p&gt;&#10;&lt;p&gt;Mając (prawie) wszystko co potrzeba, możemy zobaczyć jak połączone są interfejsy poda i noda. Linux (docker i cała masa innych narzędzi) wykorzystuje mechanizm veth oraz sieciowe namespace (przestrzenie nazw), netns do oddzielania od siebie ruchu sieciowego kontenerów, wirtualnych maszyn itd. Namespace to grupa procesów (dotyczy to też dowolnego innego zasobu), która współdzieli te same interfejsy, tablice routingu, firewall i trochę innych rzeczy. Działa to prawie tak, jak osobny host w sieci lub wirtualna maszyna, z tą różnicą, że mamy doczynienia z jednym systemem operacyjnym. Jądro Linuksa odseparowuje te zasoby, aby można było używać np. nachodzących na siebie adresacji lub regułek iptables bez żadnych dodatkowych komplikacji.&lt;/p&gt;&#10;&lt;p&gt;Druga z przydatnych rzeczy, o której wspomniałem to interfejs typu veth. Najprościej jest porównać je do wirtualnego kabelka ethernetowego, który spina dwa interfejsy. Jeśli pakiet pojawi się na jego jednym końcu, to jest widoczny również na drugim (peer interface). Sparowane interfejsy veth można przekładać pomiędzy różnymi namespaces i przy nieco bardziej zaawansowanej konfiguracji, kształtować ruch sieciowy pomiędzy nimi. Korzysta z tego m.in. openstackowy neuron, flannel, corecluster i prawdopodobnie sporo innych narzędzi.&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;# dodaj nowa namespace&#10;ip netns add test-ns&#10;&#10;# dodaj pare interfejsow veth - veth1 i veth1p, ktore sa wzajemnie sparowane&#10;ip link add veth1 type veth peer name veth1p&#10;&#10;# ustaw namespace interfejsu peer na nasza nowa namespace&#10;ip link set veth1p netns test-ns&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Powyższy przykład tworzy nazwaną namespace, którą możemy zobaczyć przez &lt;em&gt;ip netns show&lt;/em&gt; i usunąć przez &lt;em&gt;ip netns del test-ns&lt;/em&gt;.&lt;/p&gt;&#10;&lt;p&gt;My za pomocą polecenia ip chcemy zobaczyć jak poukładane są interfejsy sieciowe na nodzie, gdzie jest uruchomiony pod. Sprawdźmy najpierw w samym podzie jakie mamy interfejsy:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;pod# ip link show&#10;1: lo: &amp;lt;LOOPBACK,UP,LOWER_UP&amp;gt; mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000&#10; link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00&#10; inet 127.0.0.1/8 scope host lo&#10; valid_lft forever preferred_lft forever&#10;3: eth0@if12: &amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;gt; mtu 1450 qdisc noqueue state UP group default &#10; link/ether a2:62:74:d8:6e:1c brd ff:ff:ff:ff:ff:ff link-netnsid 0&#10; inet 10.244.1.80/24 scope global eth0&#10; valid_lft forever preferred_lft forever&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Jak zawsze &lt;em&gt;lo&lt;/em&gt;, które jest po prostu lo. Eth0 za to prezentuje się nieco inaczej niż na &amp;ldquo;zwykłym&amp;rdquo; systemie. Po nazwie eth0 widzimy &lt;em&gt;@if12&lt;/em&gt;. Dwunastka po małpie oznacza id interfejsu peer, który jest sparowany z naszym eth0. Eth0 w tym przypadku veth w przestrzeni namespace, której tutaj nie widzimy, z netnsid 0.&lt;/p&gt;&#10;&lt;p&gt;Patrząc po drugiej stronie, czyli na nodzie mamy taką listę interfejsów:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;node# ip link show&#10;1: lo: &amp;lt;LOOPBACK,UP,LOWER_UP&amp;gt; mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000&#10; link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00&#10;2: eth0: &amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;gt; mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000&#10; link/ether aa:bb:cc:xx:yy:zz brd ff:ff:ff:ff:ff:ff&#10;3: wlan0: &amp;lt;BROADCAST,MULTICAST&amp;gt; mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000&#10; link/ether aa:bb:cc:xx:yy:zz brd ff:ff:ff:ff:ff:ff&#10;...&#10;12: veth7b2f355b@if3: &amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;gt; mtu 1450 qdisc noqueue master cni0 state UP mode DEFAULT group default &#10; link/ether 92:04:f7:a2:90:f0 brd ff:ff:ff:ff:ff:ff link-netnsid 5&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Ostatni z nich to dźwięcznie brzmiący &lt;em&gt;veth7b2f355b&lt;/em&gt; z dopiskiem &lt;em&gt;@if3&lt;/em&gt; Patrząc na pierwszy listing, który zrobiliśmy w podzie, z &lt;em&gt;eth0@if12&lt;/em&gt; widać, że eth0 poda miało id 3. Wszystko wię zatem zgadza. Ostatnią rzeczą, na którą warto rzucić okiem to parametr &lt;em&gt;link-netnsid&lt;/em&gt; oznaczający ID namespace, do którego przypisany jest peerowany interfejs. Tak więc, w &lt;em&gt;ip link show&lt;/em&gt; bezpośrednio z noda widzimy, że interfejs &lt;em&gt;veth7b2f355b&lt;/em&gt; jest speerowany (sparowany? może pożeniony? :) z interfejsem o id 3 z namespace o id 5. W &lt;em&gt;ip link show&lt;/em&gt; uruchomionym na podzie, widzimy, że interfejs &lt;em&gt;eth0&lt;/em&gt; jest speerowany z interfejsem o id 12, z namespace 0.&lt;/p&gt;&#10;&lt;p&gt;Wszystko się zgadza. Żeby sprawdzić możemy puścić pinga lub traceroute z poda i nasłuchiwać na odpowiadającym mu interfejsie sieciowym, na nodzie. Pakiety powinny się pojawić.&lt;/p&gt;&#10;&lt;h3 id="dalej-w-świat-po-opuszczeniu-poda"&gt;Dalej, w świat po opuszczeniu poda&lt;/h3&gt;&#10;&lt;p&gt;Pakiety wychodzące z poda są adresowane przez &amp;ldquo;system operacyjny&amp;rdquo; poda z adresu interfejsu eth0 (src):&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;pod# ip route show&#10;default via 10.244.1.1 dev eth0 &#10;10.244.0.0/16 via 10.244.1.1 dev eth0 &#10;10.244.1.0/24 dev eth0 proto kernel scope link src 10.244.1.80&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;W tym przypadku będzie to 10.244.x.y/24, który skonfigurował automatycznie Kubernetes. Po stworzeniu takiego pakietu i zaadresowaniu, jego warstwa IP nie powinna się zmieniać (chyba, że przejdzie przez magiczne regułki iptables, o czym będzie później). Zatem sam pod odpowiada za przygotowanie naszego pakietu w warstwie trzeciej.&lt;/p&gt;&#10;&lt;p&gt;Po przejściu na drugą stronę, czyli do &amp;ldquo;systemu&amp;rdquo; noda, pakiet taki pojawia się na interfejsie speerowanym z podem. Patrząc na adresacje tego interfejsu po stronie noda, nie zobaczymy już na nim adresu IP:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;node# ip a s&#10;1: lo: &amp;lt;LOOPBACK,UP,LOWER_UP&amp;gt; mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000&#10; link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00&#10; inet 127.0.0.1/8 scope host lo&#10; valid_lft forever preferred_lft forever&#10; inet6 ::1/128 scope host &#10; valid_lft forever preferred_lft forever&#10;2: eth0: &amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;gt; mtu 1500 qdisc pfifo_fast state UP group default qlen 1000&#10; link/ether b8:27:eb:46:06:69 brd ff:ff:ff:ff:ff:ff&#10; inet 10.0.0.103/24 brd 10.0.0.255 scope global eth0&#10; valid_lft forever preferred_lft forever&#10; inet6 fe80::ba27:ebff:fe46:669/64 scope link &#10; valid_lft forever preferred_lft forever&#10;...&#10;5: flannel.1: &amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;gt; mtu 1450 qdisc noqueue state UNKNOWN group default &#10; link/ether 9e:2b:2b:16:e2:73 brd ff:ff:ff:ff:ff:ff&#10; inet 10.244.1.0/32 scope global flannel.1&#10; valid_lft forever preferred_lft forever&#10; inet6 fe80::9c2b:2bff:fe16:e273/64 scope link &#10; valid_lft forever preferred_lft forever&#10;6: cni0: &amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;gt; mtu 1450 qdisc noqueue state UP group default qlen 1000&#10; link/ether 5a:a8:5d:84:d7:ee brd ff:ff:ff:ff:ff:ff&#10; inet 10.244.1.1/24 scope global cni0&#10; valid_lft forever preferred_lft forever&#10; inet6 fe80::58a8:5dff:fe84:d7ee/64 scope link &#10; valid_lft forever preferred_lft forever&#10;...&#10;12: veth7b2f355b@if3: &amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;gt; mtu 1450 qdisc noqueue master cni0 state UP group default &#10; link/ether 92:04:f7:a2:90:f0 brd ff:ff:ff:ff:ff:ff link-netnsid 5&#10; inet6 fe80::9004:f7ff:fea2:90f0/64 scope link &#10; valid_lft forever preferred_lft forever&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;W zasadzie żaden z interfejsów speerowanych z podami nie ma przypisanego adresu. Jak jest zatem organizowany routing i przekazywanie pakietów? Tu zaczyna się nieco trudniejsza zabawa.&lt;/p&gt;&#10;&lt;p&gt;Każdy z interfejsów przyłączonych do podów jest zmostkowany z cni0 (żeby wyświetlić, doinstaluj pakiet br-utils):&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;node# brctl show&#10;bridge name&#9;bridge id&#9;&#9;STP enabled&#9;interfaces&#10;cni0&#9;&#9;8000.5aa85d84d7ee&#9;no&#9;&#9;veth67f9f314&#10;&#9;&#9;&#9;&#9;&#9;&#9;&#9;veth7b2f355b&#10;&#9;&#9;&#9;&#9;&#9;&#9;&#9;veth96d5a04b&#10;&#9;&#9;&#9;&#9;&#9;&#9;&#9;vethe25b43a7&#10;&#9;&#9;&#9;&#9;&#9;&#9;&#9;vethed432234&#10;docker0&#9;&#9;8000.02421143bd1a&#9;no&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Nasz interfejs &lt;em&gt;veth7b2f355b&lt;/em&gt; również tam jest. Zatem pakiety, o ile nie zostaną wycięte przez iptables, są przekazywane przez mostek pomiędzy interfejsami z tego samego noda. Jeśli nie możesz sobie tego wyobrazić, to pomyśl, że cni0 jest switchem, a każdy zmostkowany interfejs jest kabelkiem (veth) prowadzącym do innego komputera (tutaj to namespace).&lt;/p&gt;&#10;&lt;p&gt;Dokładnie to samo można sprawdzić przez polecenie &lt;em&gt;ip&lt;/em&gt; z pakietu &lt;em&gt;iproute&lt;/em&gt;:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;node# ip link show&#10;...&#10;6: cni0: &amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;gt; mtu 1450 qdisc noqueue state UP mode DEFAULT group default qlen 1000&#10; link/ether 5a:a8:5d:84:d7:ee brd ff:ff:ff:ff:ff:ff&#10;7: veth67f9f314@if3: &amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;gt; mtu 1450 qdisc noqueue master cni0 state UP mode DEFAULT group default &#10; link/ether ce:53:d6:38:84:5c brd ff:ff:ff:ff:ff:ff link-netnsid 0&#10;8: vethe25b43a7@if3: &amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;gt; mtu 1450 qdisc noqueue master cni0 state UP mode DEFAULT group default &#10; link/ether a6:16:c1:f9:ae:02 brd ff:ff:ff:ff:ff:ff link-netnsid 1&#10;10: vethed432234@if3: &amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;gt; mtu 1450 qdisc noqueue master cni0 state UP mode DEFAULT group default &#10; link/ether 82:a5:3c:69:61:39 brd ff:ff:ff:ff:ff:ff link-netnsid 3&#10;11: veth96d5a04b@if3: &amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;gt; mtu 1450 qdisc noqueue master cni0 state UP mode DEFAULT group default &#10; link/ether 7e:af:3a:8a:29:91 brd ff:ff:ff:ff:ff:ff link-netnsid 4&#10;12: veth7b2f355b@if3: &amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;gt; mtu 1450 qdisc noqueue master cni0 state UP mode DEFAULT group default &#10; link/ether 92:04:f7:a2:90:f0 brd ff:ff:ff:ff:ff:ff link-netnsid 5&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Każdy z interfejsów zmostkowanych z cni0 (lub innym mostkiem) ma parametr &lt;em&gt;master cni0&lt;/em&gt;, który odpowiada za &amp;ldquo;bycie w bridgu&amp;rdquo; z cni0.&lt;/p&gt;&#10;&lt;p&gt;Ok, mamy zatem interfejsy, które są ze sobą połączone warstwą drugą, natomiast nie mamy jeszcze interfejsu, który odpowiadałby za wypchnięcie pakietu z noda lub przekazanie do innego poda. O ile w podzie wszystko jest jasne i routing mówi: wszystko co nie do mnie, idzie przez default gw, to na nodzie tak różowo nie jest.&lt;/p&gt;&#10;&lt;p&gt;Pakiety w obrębie jednego noda są routowane pomiędzy nimi, przez cni0 – każdy pod na nodzie ma swój IP z sieci przypisanej do noda – w tym przypadku 10.244.1.0/24. Jeśli pakiet ma być dostarczony do innego noda, to zaczynają się schodki.&lt;/p&gt;&#10;&lt;p&gt;Tablica rouringu noda wygląda następująco:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;node# ip route show&#10;default via 10.0.0.1 dev eth0 onlink &#10;10.0.0.0/24 dev eth0 proto kernel scope link src 10.0.0.103 &#10;10.244.0.0/24 via 10.244.0.0 dev flannel.1 onlink &#10;10.244.1.0/24 dev cni0 proto kernel scope link src 10.244.1.1 &#10;10.244.2.0/24 via 10.244.2.0 dev flannel.1 onlink &#10;10.244.3.0/24 via 10.244.3.0 dev flannel.1 onlink &#10;10.244.4.0/24 via 10.244.4.0 dev flannel.1 onlink &#10;10.244.5.0/24 via 10.244.5.0 dev flannel.1 onlink &#10;10.244.6.0/24 via 10.244.6.0 dev flannel.1 onlink &#10;172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1 linkdown&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Cała sieć 10.244.1.0/24 jest routowana do naszego noda przez cni0. Natomiast sieci z innych nodów (10.244.2-6.0/24) są przesyłane przez interfejs flannel.1. Dopisek onlink mówi kernelowi, aby przesyłał ruch do tych sieci, nawet nie mając na tym interfejsie przypisanego adresu ip (inaczej musiałby mieć trasę przez konkretny host).&lt;/p&gt;&#10;&lt;p&gt;Interfejs flannel.1 jest tunelem vxlan (&lt;em&gt;ip -d link show&lt;/em&gt; pokaże przy nim ID vlanu 1, mówi o tym też dopisek .1 w nazwie). Zatem adresując pakiet z naszego poda z adresem 10.244.1.68, przechodzi on przez interfejsy veth do domyślnego namespace, i pojawia się jako pakiet przychodzący przez port cni0 (łapie się na regułki z łancucha FORWARD z iptables), ale nie adresowany do naszego noda (zatem nie będzie to iptables INPUT). Ponieważ kernel w domyślnym namespace zna regułki mówiące o tym jak dostać się do innych sieci na innych nodach, przesyła taki pakiet przez interfejs flannel.1. Tam już na podstawie zapytań arp odnajnowany jest host z sieci innego noda, obsługującego danego poda, np. 10.244.6.0/32:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;node# arp -an | grep flannel.1&#10;? (10.244.4.0) at 2e:ed:ad:yy:aa:xx [ether] PERM on flannel.1&#10;? (10.244.0.0) at d2:79:b7:yy:aa:xx [ether] PERM on flannel.1&#10;? (10.244.3.0) at 1e:42:c2:yy:aa:xx [ether] PERM on flannel.1&#10;? (10.244.6.0) at 5a:2d:9a:yy:aa:xx [ether] PERM on flannel.1&#10;? (10.244.2.0) at c6:52:d3:yy:aa:xx [ether] PERM on flannel.1&#10;? (10.244.5.0) at ce:ec:b4:yy:aa:xx [ether] PERM on flannel.1&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Sieć stworzona z interfejsów flannel.1 różnych nodów można znowu wyobrazić sobie jako switcha spinającego po jednym kabelku z różnych nodów. Jednak zamiast par veth mamy tutaj tunele VXLAN, które tworzą wirtualną warstwę 2, niezależną od fizycznej sieci. W powyższym przypadku vxlan stworzony przez flannel.1 jest wypuszczany do fizycznej sieci przez eth0 noda. W vxlanie node za pomocą pakietów ARP poznaje inne hosty w sieci i na ich podstawie jest w stanie poprawnie zaadresować mac adres karty odpowiadającej docelowej sieci, w której będzie pod. Flaga onlink w tablicy routingu trochę to zadanie ułatwia, gdyż normalnie kernel musiałby mieć podanego hosta we wspólnej sieci, łączącej wszystkie nody. Tutaj ten etap jest pominięty i przeniesiony na vxlan i odkrywanie hostów poniekąd przez arp.&lt;/p&gt;&#10;&lt;p&gt;Po osiągnięciu docelowego hosta przez vxlan, pakiet przebywa analogiczną drogę do poda.&lt;/p&gt;&#10;&lt;h3 id="iptables"&gt;iptables&lt;/h3&gt;&#10;&lt;p&gt;Po wpisaniu &lt;em&gt;iptables-save&lt;/em&gt; dostajemy całkiem sporo regułek i łańcuchów, które odpowiadają za filtrowanie ruchu i natowanie go. Z ważniejszych rzeczy, na które warto zwrócić uwagę, to na wszystkich nodach mamy mimo wszystko ustawioną politykę FORWARD na DROP z dopuszczeniem przekazywania pakietów z sieci flanella:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;:FORWARD DROP [0:0]&#10;...&#10;-A FORWARD -s 10.244.0.0/16 -j ACCEPT&#10;-A FORWARD -d 10.244.0.0/16 -j ACCEPT&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Do debugowania ruchu może też się przydać poniższy zestaw regułek:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;-A POSTROUTING -s 10.244.0.0/16 -d 10.244.0.0/16 -j RETURN&#10;-A POSTROUTING -s 10.244.0.0/16 ! -d 224.0.0.0/4 -j MASQUERADE --random-fully&#10;-A POSTROUTING ! -s 10.244.0.0/16 -d 10.244.1.0/24 -j RETURN&#10;-A POSTROUTING ! -s 10.244.0.0/16 -d 10.244.0.0/16 -j MASQUERADE --random-fully&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Określa on skąd i gdzie pakiety mają być natowane oraz jaki ruch powinien zostać zablokowany.&lt;/p&gt;&#10;&lt;p&gt;Dodatkowo w dumpie z iptables-save znajdziemy też całą listę (dość długą) reguł, określających dostęp do klastra k8s, jego serwisów i dns&amp;rsquo;a.&lt;/p&gt;&#10;&lt;h3 id="uff"&gt;Uff&amp;hellip;&lt;/h3&gt;&#10;&lt;p&gt;Podsumowując, inżynierowie integrujący flanella zrobili kawał dobrej roboty, który niekoniecznie dobrze się debuguje w razie problemów :) Natomiast ostatnie kilka lub kilkanaście lat pracy nad wirtualizacją i kontenerami daje coraz lepsze narzędzia pozwalające coraz lepiej kontrolować ruch sieciowy w tak zagmatwanych środowiskach jakim jest na przykład kubernetes.&lt;/p&gt;</content:encoded></item><item><title>Dlaczego zawód informatyka zawsze będzie potrzebny?</title><link>https://nabozny.io/dlaczego-zawod-informatyka-zawsze-bedzie-potrzebny/</link><dc:creator>maciej</dc:creator><pubDate>Sun, 08 Sep 2019 17:55:27 +0000</pubDate><category>Inne</category><category>Linux</category><guid isPermaLink="true">https://nabozny.io/dlaczego-zawod-informatyka-zawsze-bedzie-potrzebny/</guid><description>Kilka lat temu usłyszałem ciekawe stwierdzenie. Jakieś 40 lat temu wszyscy szewcy chórem mówili, że ich zawód nigdy nie wymrze, ponieważ ludzie zawsze będą potrzebowali butów, a ktoś będzie musiał je przecież naprawiać. Jak wyszło? Masz dziurę w bucie, to idziesz po nową parę. Niecałe trzydzieści …</description><content:encoded>&lt;p&gt;Kilka lat temu usłyszałem ciekawe stwierdzenie. Jakieś 40 lat temu wszyscy szewcy chórem mówili, że ich zawód nigdy nie wymrze, ponieważ ludzie zawsze będą potrzebowali butów, a ktoś będzie musiał je przecież naprawiać. Jak wyszło? Masz dziurę w bucie, to idziesz po nową parę. Niecałe trzydzieści lat temu (tak, to już lata 90-te) tą samą pewność siebie mieli elektronicy. Coraz więcej sprzętu elektronicznego, w każdym sklepie z elektroniką części zapasowe, ktoś to przecież musiał naprawiać.&lt;/p&gt;&#10;&lt;p&gt;Około 10 – 15 lat temu to samo myślałem o informatykach i adminach. Coraz więcej serwerów, ktoś to musi ogarniać. Nowe instalacje Apache + PHP + MySQL, nowe aktulizacje Linuksa, ktoś to musi ogarniać. Do tego wykuty prawie na pamięć config Apache, jakieś fajne zmiany w configu i&amp;hellip; Dzisiaj okazało się, że taki admin potrafiący zainstalować LAMP to w sumie przeżytek. Admina zastąpiło&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;docker run --name moja_piekna_strona -p 80:80 \&#10;&#9;-v /home/maciej/nicciekawego:/srv \&#10;&#9;-d wordpress:latest&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Przepraszam. To było 5 lat temu. 4 lata temu ewoluowało do docker-compose:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;version: &amp;#39;3.3&amp;#39;&#10;&#10;services:&#10; db:&#10; image: mysql:5.7&#10; volumes:&#10; - db_data:/var/lib/mysql&#10; restart: always&#10; environment:&#10; MYSQL_ROOT_PASSWORD: somewordpress&#10; MYSQL_DATABASE: wordpress&#10; MYSQL_USER: wordpress&#10; MYSQL_PASSWORD: wordpress&#10;&#10; wordpress:&#10; depends_on:&#10; - db&#10; image: wordpress:latest&#10; ports:&#10; - &amp;#34;8000:80&amp;#34;&#10; restart: always&#10; environment:&#10; WORDPRESS_DB_HOST: db:3306&#10; WORDPRESS_DB_USER: wordpress&#10; WORDPRESS_DB_PASSWORD: wordpress&#10; WORDPRESS_DB_NAME: wordpress&#10;volumes:&#10; db_data: {}&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;i sieciowca można w sumie zwolnić. A trzy lata temu przeobraziło się w CloudFormation lub, u bardziej ambitnych, w Kubernetes:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;apiVersion: v1&#10;kind: Service&#10;metadata:&#10; name: wordpress-mysql&#10; labels:&#10; app: wordpress&#10;spec:&#10; ports:&#10; - port: 3306&#10; selector:&#10; app: wordpress&#10; tier: mysql&#10; clusterIP: None&#10;---&#10;apiVersion: v1&#10;kind: PersistentVolumeClaim&#10;metadata:&#10; name: mysql-pv-claim&#10; labels:&#10; app: wordpress&#10;spec:&#10; accessModes:&#10; - ReadWriteOnce&#10; resources:&#10; requests:&#10; storage: 20Gi&#10;---&#10;apiVersion: apps/v1 # for versions before 1.9.0 use apps/v1beta2&#10;kind: Deployment&#10;metadata:&#10; name: wordpress-mysql&#10; labels:&#10; app: wordpress&#10;spec:&#10; selector:&#10; matchLabels:&#10; app: wordpress&#10; tier: mysql&#10; strategy:&#10; type: Recreate&#10; template:&#10; metadata:&#10; labels:&#10; app: wordpress&#10; tier: mysql&#10; spec:&#10; containers:&#10; - image: mysql:5.6&#10; name: mysql&#10; env:&#10; - name: MYSQL_ROOT_PASSWORD&#10; valueFrom:&#10; secretKeyRef:&#10; name: mysql-pass&#10; key: password&#10; ports:&#10; - containerPort: 3306&#10; name: mysql&#10; volumeMounts:&#10; - name: mysql-persistent-storage&#10; mountPath: /var/lib/mysql&#10; volumes:&#10; - name: mysql-persistent-storage&#10; persistentVolumeClaim:&#10; claimName: mysql-pv-claim&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Możliwe, że nawet ciut wcześniej. Firmy, które były trochę bardziej do przodu z technologią, po drodze zaadaptowały jeszcze prawdopodobnie Puppet, Ansible lub inne.&lt;/p&gt;&#10;&lt;p&gt;Obecnie, aby postawić wordpressa, wystarczy średnioogarnięty devops, który pozmienia w powyższym pliku co ważniejsze parametry i doczyta jak to wgrać w klaster. Zastąpi on admina od baz danych, systemu, LAMPA, sieciowca i po części programistę. Znajomości Apache nikt nie potrzebuje, bo wszystko ogarnia dość dobrze prekonfigurowany Nginx lub load balancer w chmurze, a baza danych w małych instalacjach daje radę. Jeszcze pozostaje kwestia tego, co zrobić z pomieszczeniem po starym WC i zaadaptowanym kiedyś na serwerownie. Jest dobra klima i media, więc możnaby zrobić relax room lub spa. Wróć, spa w biurze średnio pasuje. Więc relax room. A z piwnicy, w której kiedyś trzymało się admina można zrobić garaż dla rowerów.&lt;/p&gt;&#10;&lt;p&gt;Wszystko to tylko dla tego, że całą serwerownie i obowiązki admina zastąpiła chmura AWS lub GC ze swoimi usługami baz danych, load balancera, API gateway&amp;rsquo;ów i całej masy innego fajnego bajeru, który da się prawieże wyklikać. Do tego jeszcze był devops, który spisał to kiedyś w formie Infrastructure As A Code. Już nie pracuje dla nas, ale yaml po nim został :)&lt;/p&gt;&#10;&lt;h5 id="co-z-tym-programistą"&gt;Co z tym programistą?&lt;/h5&gt;&#10;&lt;p&gt;Tak, z programistą pewnie będzie podobnie, ale jeszcze nie tak szybko. Co chwilę gdzieś można usłyszeć, że zawód informatyka nigdy nie wyginie, bo kto będzie pisał te aplikacje, strony, bugi i pił kawę w kuchni. Czy na pewno? Można popatrzeć na ostatnie 10 lat rozwoju oprogramowania, na przykładzie Pythona. W 2009 roku dla tego języka było niewiele ORM&amp;rsquo;ów dla baz danych, kilka fajnych frameworków pozwalających postawić stronę i dość sporo systemowych bibliotek do wszystkiego. Patrząc na to jak rozwinęło się Django, programista dostaje prawie gotowe klocki, z których musi poukładać logiczną strukturę strony, a nie mozolnie pisać logikę, konfigurować renderowanie stron HTML i łączyć wszystko ze sobą. Najpopularniejsze zadania zostały tak zautomatyzowane, żeby programiście pozostało właśnie tylko układanie klocków. No czasami może integracja z czymś innym.&lt;/p&gt;&#10;&lt;p&gt;Składając to z zewnętrznymi narzędziami, lub całymi serwisami oferującymi swoje funkcjonalności, programista nie musi nawet konfigurować mailerki do rozsyłania spamu ze swojego serwisu. Maile wyśle za niego sendgrid. Dużo maili wyśle sendgrid za odpowiednią opłatą.&lt;/p&gt;&#10;&lt;p&gt;Patrząc na to jak rozwija się świat IT oraz dostępne narzędzia, programistów powoli zastąpią osoby pokroju DevOps, które będą potrafiły szybko poskładać gotowe klocki w całość. Na studiach uczyłem się, że architekt dostarcza UML programistom, z których oni mają wyczytać to, co ma robić aplikacja. Dzisiaj to programista dostaje opis aplikacji, sugeruje klientowi co może być lepiej zaimplementowane i to po prostu robi.&lt;/p&gt;&#10;&lt;p&gt;Nie jest to oczywiście reguła, ponieważ jest cała pula aplikacji, które wymagają indywidualnego podejścia, porządnego zaprojektowania i implementacji nieszablonowej logoki. Natomiast ilość aplikacji, które wymagają Javascriptowego frontendu (np. reacta), REST API i bazy danych w chmurze systematycznie rośnie.&lt;/p&gt;&#10;&lt;p&gt;Czy zatem programiści to gatunek na wymarciu? Pewnie jeszcze długo nie, tak samo długo jak &amp;ldquo;zwykli&amp;rdquo; admini znający system do spodu będą potrzebni. Natomiast same narzędzia, które stają się coraz lepsze i prostsze sprawiają, że coraz mniej aplikacji wymaga programisty z krwi i kości, a coraz więcej akcji dzieje się magicznie i może być mocno uproszczonych. Sukcesywnie dąży się do tego, że opis aplikacji, który kiedyś tworzyło się w UML&amp;rsquo;u dzisiaj zastępuje dokument lub seria calli z klientem, na podstawie których wiadomo z czego poskładać daną aplikację. Na pewno jest to dość duże uproszczenie i spłycenie tego, co aktualnie robią programiści, natomiast koniec końców ktoś zawsze będzie musiał przetłumaczyć klientowe &amp;ldquo;ja chcę to&amp;rdquo; na coś bardziej formalnego i dopytać tego klienta o całą masę szczegółów. Czy sztuczna inteligencja? Myślę, że jeszcze nie prędko&lt;/p&gt;</content:encoded></item><item><title>Na co dzień na raspberry</title><link>https://nabozny.io/na-co-dzien-na-raspberry/</link><dc:creator>maciej</dc:creator><pubDate>Fri, 02 Aug 2019 19:39:16 +0000</pubDate><category>Inne</category><category>Linux</category><guid isPermaLink="true">https://nabozny.io/na-co-dzien-na-raspberry/</guid><description>Raspberry PI jako desktop sprawdzało się do tej pory średnio – I/O dysku na poziomie 30MB/s bez perspektywy poprawienia jakimikolwiek rozszerzeniami przekreślała. Kilka tygodni temu sytuacja się zmieniła i mamy PI 4. Jak się na nim pracuje?</description><content:encoded>&lt;p&gt;Raspberry PI jako desktop sprawdzało się do tej pory średnio – I/O dysku na poziomie 30MB/s bez perspektywy poprawienia jakimikolwiek rozszerzeniami przekreślała. Kilka tygodni temu sytuacja się zmieniła i mamy PI 4. Jak się na nim pracuje?&lt;/p&gt;&#10;&lt;p&gt;Od kilku tygodni jestem szczęśliwym posiadaczem dwóch malin w wersji 4. Pierwsza, z 1GB ramu sprawuje się bardzo dobrze hostując storage m.in. tego bloga. W zestawie z kilkoma innymi Raspberry PI 3, dedykowanym zasilaczem usb 40W od Tp-linka i dyskiem 2 TB daje sobie radę dość dobrze. Albo po prostu mi nie przeszkadza trochę dłuższe ładowanie strony :) Udało się na to wepchnąć kilka wordpressów, NAS, build server i jeszcze pare innych rzeczy.&lt;/p&gt;&#10;&lt;p&gt;&lt;img src="https://nabozny.io/uploads/2019/08/rpi-small.jpg" srcset="https://nabozny.io/uploads/2019/08/rpi-small_hu_e338b569aeee9fe6.jpg 600w, https://nabozny.io/uploads/2019/08/rpi-small_hu_56bfcc9580b421e1.jpg 1200w, https://nabozny.io/uploads/2019/08/rpi-small.jpg 1853w" sizes="(max-width: 767px) 89vw, (max-width: 1000px) 54vw, (max-width: 1071px) 543px, 580px" alt="RPi cluster" width="1853" height="1390" class="alignnone size-full" loading="lazy" decoding="async"&gt;&lt;/p&gt;&#10;&lt;p&gt;Drugie PI4 czekało na lepsze czasy. Miało posłużyć jako hosting do bardziej wymagających stron, jednak między czasie plan się zmienił i stronki czekają na lepsze czasy. Pozostało zatem skorzystać z okazji i podpiąć je w miejsce starego, popsutego już macbooka i zobaczyć czy da się na tym pracować.&lt;/p&gt;&#10;&lt;h5 id="da-się-"&gt;Da się :)&lt;/h5&gt;&#10;&lt;p&gt;Oprócz kilku drobnych problemów, m.in. ze starą wersją firefoxa, brakiem ładnego środowiskai brakiem niektórych sterowników, m.in. do drukarki, RPI4 jako desktop sprawuje się całkiem znośnie. Dobra, może ktoś lubi MATE lub Xfce4, wtedy ma jeden problem mniej.&lt;/p&gt;&#10;&lt;p&gt;Bardzo duży zysk na wydajności daje włożenie dysku SSD. O ile w RPI 3 nie miało to większego sensu (bo USB 2.0 i tak obcinało transfer do 30MB/s), to malina w wersji 4 daje radę wyciągać 200-330MB/s. Do tego dochodzi płynnie działająca sieć, która nie współdzieli transferu z dyskiem i mamy całkiem przyjemnie pracujący desktop. Uruchomienie SSD wymagało co prawda dopisanie następującego stringa w /boot/cmdline.txt:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;usb-storage.quirks=125f:a68a:u&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;i zabawę w kopiowanie systemu plików z karty SD na dysk, ale zwróciło się szybko w wydajności. Co więcej, karty SD nie dają takich transferów, więc zainwestowanie w dysk SSD dla maliny jest moim zdaniem dobrym krokiem.&lt;/p&gt;&#10;&lt;h5 id="a-po-instalacji"&gt;A po instalacji&amp;hellip;&lt;/h5&gt;&#10;&lt;p&gt;Po zainstalowaniu Raspbiana Buster pozostaje doinstalować dosłownie kilka niezbędnych programów, m.in. &lt;em&gt;ssh, openvpn, git, gcc, make, cmake, qtcreator, mate-desktop, mate-desktop-environment, libreoffice-writer, chromium-browser, mate-terminal, whois, imagemagick, htop, screen, nmap, tcpdump, fish&lt;/em&gt; oraz skonfigurować wifi w &lt;em&gt;/etc/wpa_supplicant&lt;/em&gt; i &lt;em&gt;/etc/network/interfaces&lt;/em&gt; aby cieszyć się gołym desktopem. Aby cieszyć się jeszcze bardziej i poczuć jak na początku tego tysiąclecia, wypadało jeszcze pobrać z mate-look.org kilka zestawów ikon oraz theme dla środowiska Mate. Następnie rozpakować je, do odpowiednio: &lt;em&gt;/usr/share/icons&lt;/em&gt; i &lt;em&gt;/usr/share/themes&lt;/em&gt;.&lt;/p&gt;&#10;&lt;p&gt;Wisienką na torcie jest oczywiście wywalenie z /etc/passwd domyślnego basha, wsadzenie tam fisha dla swojego konta (no tak, jeszcze konto pi trzeba usunąć i dodać swoje) i skonfigurowanie mate-terma ze swoimi ulubionymi skrótami.&lt;/p&gt;&#10;&lt;p&gt;Uf. Jak 15 lat temu, pół dnia roboty i desktop wygląda, nie wstyd się zalogować przy znajomych.&lt;/p&gt;&#10;&lt;h5 id="wydajność"&gt;Wydajność&lt;/h5&gt;&#10;&lt;p&gt;Jak wspomniałem, SSD daje kopa wydajności jeśli chodzi o dostęp do plików, uruchamianie programów itd. w stosunku do RPi 3. Natomiast po chwili użytkowania zaczynają wkurzać niewielkie lagi przy zmianach i włączaniu aplikacji, które trwają ok. pół sekundy, czasami ciut dłużej. Nie ma tego na zwykłym desktopie, nawet z włączonymi efektami pulpitu. Skąd tu się bierze – nie wiem. Może to brak cache w cpu, a może brak wsparcia grafiki w wyświetlaniu okienek?&lt;/p&gt;&#10;&lt;p&gt;Koniec końców da się jednak żyć z tym komuterkiem, jeśli tylko małę lagowanie i brak aktualnych wersji niektórych programów nie przeszkadza. Co więcej, kompilacja frameworka &lt;a href="https://dinemic.io"&gt;dinemic&lt;/a&gt; i &lt;a href="https://gitlab.com/dinemic/pydinemic"&gt;innych aplikacji&lt;/a&gt;, nad którymi ostatnio pracuję przebiega tu dość sprawnie, nawet szybciej niż na moim zwykłym desktopie. Oczywiście kompilator wypluwa binarki w wersji ARM, ale to już inna bajka. Nie przeszkadza to również w normalnej pracy w przeglądarce i oglądaniu głupich obrazków, gdy kompilator zjada 400% CPU.&lt;/p&gt;&#10;&lt;p&gt;&lt;img src="https://nabozny.io/uploads/2019/08/htop.jpg" srcset="https://nabozny.io/uploads/2019/08/htop_hu_1d7c1a7fb2a6d443.jpg 600w, https://nabozny.io/uploads/2019/08/htop_hu_bfae66eb8b833216.jpg 1200w, https://nabozny.io/uploads/2019/08/htop.jpg 1536w" sizes="(max-width: 767px) 89vw, (max-width: 1000px) 54vw, (max-width: 1071px) 543px, 580px" alt="htop" width="1536" height="919" class="alignnone size-full" loading="lazy" decoding="async"&gt;&lt;/p&gt;&#10;&lt;p&gt;Podsumowując, jeśli ktoś nie ma wymagań co do grania i oglądania wideo (od biedy też się da), to idzie przeżyć z samym RPi 4. Jeśli chodzi o hosting, to jest znacznie lepiej i daje nieporównywalnego kopa w stosunku do RPi 3 zarówno jako sam hosting dynamicznych stron jak i NAS.&lt;/p&gt;&#10;&lt;h5 id="na-koniec-dorzućmy-upsa"&gt;Na koniec dorzućmy UPS&amp;rsquo;a&lt;/h5&gt;&#10;&lt;p&gt;Wspomniany na początku klaster RPi zastąpił starego dobrego HP G6, który robił za build server, hosting i całą resztę rzeczy na potrzeby projektów z cloudover. Po drodze przyplętał się UPS, 1500VA / 900W, który mógł podtrzymać serwer HP przez około 50 minut. Po wymianie na kilka raspberry PI i przekonfigurowaniu całego środowiska testowania i budowania czas wydłużył się&amp;hellip; do 15 godzin :)&lt;/p&gt;</content:encoded></item><item><title>Jak trzymać hasła w pamięci i dlaczego cstringi są czasem lepsze od std::stringów?</title><link>https://nabozny.io/jak-trzymac-hasla-w-pamieci-i-dlaczego-cstringi-sa-czasem-lepsze-od-stdstringow/</link><dc:creator>maciej</dc:creator><pubDate>Sat, 16 Feb 2019 08:32:37 +0000</pubDate><category>Programowanie</category><category>Cloud</category><category>Linux</category><category>kryptografia</category><category>programowanie</category><category>linux</category><guid isPermaLink="true">https://nabozny.io/jak-trzymac-hasla-w-pamieci-i-dlaczego-cstringi-sa-czasem-lepsze-od-stdstringow/</guid><description>“Poznaj sekret cstringów. Std::stringi ich za to nienawidzą”! A na serio, nie zgapiając od pomponików, będzie trochę na temat trzymania wrażliwych danych w pamięci RAM i o zapobieganiu ich zapisowi na dysk.</description><content:encoded>&lt;p&gt;&amp;ldquo;Poznaj sekret cstringów. Std::stringi ich za to nienawidzą&amp;rdquo;! A na serio, nie zgapiając od pomponików, będzie trochę na temat trzymania wrażliwych danych w pamięci RAM i o zapobieganiu ich zapisowi na dysk.&lt;/p&gt;&#10;&lt;p&gt;Na początek krótkie ćwiczenie: napisałeś program do przechowywania haseł. Taki keepass w wersji DIY. Po uruchomieniu wczytuje zaszyfrowany plik do pamięci, pyta Cię o hasło, odszyfrowuje i pokazuje wybrane hasło. Działa dopóki go nie zamkniesz, na wszelki wypadek, jakbyś potrzebował jeszcze innego hasła. Niby bezpieczne, bo zaszyfrowane, na hasło&amp;hellip; ale nie do końca.&lt;/p&gt;&#10;&lt;h3 id="co-może-pójść-nie-tak"&gt;Co może pójść nie tak?&lt;/h3&gt;&#10;&lt;p&gt;Trzymasz odszyfrowany pęk kluczy w pamięci – to pierwszy problem. Dowolne nadpisanie/odczytanie takiej pamięci, np. przez /dev/mem albo /proc/X/mem może skompromitować wszystkie hasła z Twojego pęku – dowolna osoba z uprawnieniami root&amp;rsquo;a będzie mogła wczytać treść z Twojej pamięci. To może odszyfrowywać tylko gdy tego potrzebujemy? Ok, wtedy trzymamy w pamięci jedynie nasze główne hasło i zaszyfrowane przez prawie cały czas dane.&lt;/p&gt;&#10;&lt;p&gt;Osoba atakująca będzie mogła wtedy dość łatwo poznać hasło, którego możesz też przecież używać gdzieś indziej (tak, są tacy ludzie i jest ich dość dużo :) ). Popatrzmy na inny przykład: Linux Kernel posiada wbudowany system szyfrowania dysków – LUKS. Do odszyfrowania jednego dysku można przypisać kilka różnych haseł dla różnych osób. Dzięki temu można uniknąć przekazywania ich i zapisywania na karteczkach. Każdy może użyć wtedy swojego ulubionego hasła :) Jeśli natomiast je zgubi, można zmienić tylko to jedno hasło do dysku, bez zmiany innych haseł. Co więcej zmiana dowolnego hasła nie wymusza re-szyfrowania całego dysku. Jak to działa? Nie ma za tym jakiejś specjalnej magii i matematyki. Każdy z użytkowników, którzy mają swoje hasła tak na prawdę deszyfruje główny klucz do dysku. Dopiero za pomocą tego klucza jest przeprowadzane pełne (de)szyfrowanie.&lt;/p&gt;&#10;&lt;p&gt;Podobny mechanizm możesz też zastosować w Twoim pęku kluczy lub podobnej aplikacji. Za pomocą hasła użytkownik może odszyfrować tak na prawdę klucz symetryczny, który posłuży do odczytania pozostałej części danych. Przechowywanie go w pamięci, jeśli to konieczne, jest wtedy nieco bardziej bezpiecznym rozwiązaniem niż ciągłe przechowywanie hasła, lub co gorsze pełnego pęku kluczy.&lt;/p&gt;&#10;&lt;h3 id="gdzie-zapisać-tymczasowe-hasło"&gt;Gdzie zapisać tymczasowe hasło?&lt;/h3&gt;&#10;&lt;p&gt;Mimo wszystko Twoje hasło lub klucz deszyfrujący musi być tak czy tak chociaż na chwilę wczytane do pamięci. Jak zabezpieczyć się przed jego wyciekiem? Oprócz możliwości odczytania hasła bezpośrednio z pamięci jest jeszcze jedna możliwość. Atakujący może zaalokować tak dużo pamięci w systemie, że w końcu Twój DIY Keepass zostanie przeniesiony do swapu. A stąd już prosta droga do wyciągnięcia i przeanalizowania danych. Jeśli nie jako dump pamięci z poziomu roota, to śrubokrętem, przenosząc dysk do innego komputera lub bootując komputer z live cd.&lt;/p&gt;&#10;&lt;p&gt;Jak się przed tym obronić? Linux posiada trzy funkcje pozwalające Twojej aplikacji określić jak ma się zachowywać jej wirtualna pamięć. Pierwsza z nich to &lt;a href="https://linux.die.net/man/2/mprotect"&gt;&lt;em&gt;mprotect&lt;/em&gt;&lt;/a&gt;. Podając adres pamięci (np. blok zwrócony przez &lt;em&gt;malloc&lt;/em&gt;) możesz sprawić, że dany obszar będzie dostępny tylko do odczytu, do zapisu lub w cale. Flaga &lt;em&gt;PROT_WRITE&lt;/em&gt; jest zwykle stosowana do zabezpieczenia obszaru pamięci, w którym znajduje się Twój plik wykonywalny, aby przypadkowe (lub złośliwe) nadpisanie kodu instrukcji Twojej aplikacji nie było możliwe. &lt;em&gt;PROT_EXEC&lt;/em&gt; jest natomiast stosowane do zabezpieczenia pamięci przed wykonywaniem jej kodu, np. w obszarach pamięci przechwujących zmienne. Zapobiega do przypadkowemu uruchomieniu takiego kodu. Wbrew pozorom bardzo użyteczną flagą może być &lt;em&gt;PROT_NONE&lt;/em&gt;. Takiego obszaru nie da się ani odczytać, ani zmodyfikować ani wykonać. Jedną stronę takiej pamięci możemy ustawić np. za obszarem, do którego będą zapisywane dane od użytkownika, o nieznanym rozmiarze. Jeśli nasze programowe zabezpieczenia zawiodą i na przykład zaczniemy zapisywać do takiej tablicy bez opamiętania (bez if&amp;rsquo;a :) to finalnie program spróbuje zapisać coś na stronie pamięci oznaczonym &lt;em&gt;PROT_NONE&lt;/em&gt;. W takiej sytuacji zadziała już sprzętowe zabezpieczenie i nasza aplikacja otrzyma stosowny, zwykle bolesny sygnał od systemu operacyjnego.&lt;/p&gt;&#10;&lt;p&gt;Drugą funkcją służącą do modyfikowania zachowania pamięci jest &lt;a href="http://man7.org/linux/man-pages/man2/madvise.2.html"&gt;&lt;em&gt;madvise&lt;/em&gt;&lt;/a&gt;. Podobnie jak &lt;em&gt;mprotect&lt;/em&gt; przyjmuje ona adres i rozmiar obszaru pamięci, który chcemy zmodyfikować. Za jej pomocą możemy powiedzieć systemowi (doradzić) jak będziemy korzystać z tego obszaru. Czy na przykład będą to losowe odczyty i cache&amp;rsquo;owanie na wyrost nie ma sensu, czy też podczas forka procesu ten obszar pamięci ma pozostać nieskopiowany. Flag jest sporo, warto przeglądnąć je w przyjaznym manualu.&lt;/p&gt;&#10;&lt;p&gt;Ostatnia funkcja, warta wspomnienia to &lt;em&gt;mlock&lt;/em&gt;. Ponownie, przyjmuje jako pierwsze parametry adres i rozmiar obszaru pamięci. Zapobiega ona przenoszeniu stron pamięci na swap. Oznaczając w ten sposób obszar gdzie trzymamy klucz deszyfrujący oraz sam keychain, możemy zabezpieczyć się przed wspomnianym wcześniej wymuszeniem zrzutu pamięci na swap.&lt;/p&gt;&#10;&lt;p&gt;Korzystając z wszystkich trzech powyższych funkcji możemy wymusić na systemie operacyjnym nieco lepsze potraktowanie naszych danych przesiadujących w pamięci. Za pomocą &lt;em&gt;mprotect&lt;/em&gt; możemy przez większość czasu zapobiegać dostępowi do pamięci, ustawiając jej flagę &lt;em&gt;PROT_NONE&lt;/em&gt;, i &amp;ldquo;odbezpieczając&amp;rdquo; tylko gdy użytkownik potrzebuje hasła. Za pomocą &lt;em&gt;madvise&lt;/em&gt; mona zapobiec skopiowaniu tej pamięci przez fork (i prawdopodobnie zabezpieczyć się też na kilka innych okoliczności), a z &lt;em&gt;mlock&lt;/em&gt; zapobiegniemy wyrzuceniu na swap.&lt;/p&gt;&#10;&lt;h3 id="cstring-vs-stdstring"&gt;Cstring vs. std::string&lt;/h3&gt;&#10;&lt;p&gt;Przy okazji wychodzi czemu czasami zarządzanie pamięcią przez &lt;em&gt;malloc&lt;/em&gt; i standardowe stringi z C są bardziej użyteczne. Korzystając z &lt;em&gt;std::string&lt;/em&gt; nie mamy tak dokładnej kontroli nad tym, gdzie będą trzymane dane ze stringa, jak również nie mamy gwarancji że po dopisaniu kolejnych znaków nie zostanie on w całości przeniesiony w inne miejsce. Trudniejsze staje się też zabezpieczenie strony tuż za stringiem. Nie jest to jednak niemożliwe, jednak wymaga dodatkowej pracy i dogłębnej wiedzy jak jest alokowana w tym przypadku pamięć. Dodatkowo, gdy std::string zwalnia pamięć, musimy też ręcznie posprzątać po nim, aby inny obiekt nie dostał obszaru z &lt;em&gt;PROT_NONE&lt;/em&gt;.&lt;/p&gt;&#10;&lt;p&gt;W przypadku c stringów, wszystkie operacje przenoszenia, zabezpieczania i odbezpieczania musimy zrobić tak czy tak ręcznie, jednak z punktu widzenia czytelności kodu i jego obsługi jest to znacznie czytelniejsze.&lt;/p&gt;&#10;&lt;h3 id="swap-też-jest-szyfrowany"&gt;Swap też jest szyfrowany&lt;/h3&gt;&#10;&lt;p&gt;Tak, od jakiegoś czasu dystrybucje wspierają to domyślnie. Strony pamięci zrzucane do swapu są w locie szyfrowane, także odczytanie ich po wyłaczeniu komputera nieco się komplikuje. Przykładowy wpis z Arch wiki:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;swap /dev/sdX# /dev/urandom swap,cipher=aes-xts-plain64,size=256&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Przy każdym starcie system generuje losowy klucz do zaszyfrowania swapu z /dev/urandom. Nawet jeśli coś się na swapie pojawi, to będą to tylko losowe znaki dla osoby, która nie zna klucza. Problem pojawia się jednak przy wsparciu dla disk suspend – uśpienia komputera i zrzucenia stanu pamięci na dysk. W tym przypadku sprawy się nieco komplikują, ale to historia na inny wpis.&lt;/p&gt;&#10;&lt;h3 id="wirtualne-maszyny"&gt;Wirtualne maszyny&lt;/h3&gt;&#10;&lt;p&gt;O ile wszystkie powyższe sposoby mają się całkiem dobrze na &amp;ldquo;gołym&amp;rdquo; PC, to już korzystając z wirtualizacji nie mamy gwarancji, że ktoś nie zrobi zrzutu pamięci oraz dysku w locie i nie będzie próbował analizować tego offline. W takim wypadku jedynym rozwiązaniem minimalizującym ryzyko przechwycenia cennych danych jest tylko chwilowe odszyfrowywanie ich. Nie gwarantuje to jednak pełnego bezpieczeństwa. Na własnych systemach można zapobiegać temu odpowiednimi opcjami dla wirtualnych maszyn, dzięki którym gospodarz nie będzie przenosił ich stron na swap. Służy do tego m.in. opcja &lt;a href="https://libvirt.org/formatdomain.html#elementsMemoryBacking"&gt;&lt;em&gt;locked&lt;/em&gt;&lt;/a&gt; w libvircie. Jednak w publicznych chmurach nie mamy takiej gwarancji.&lt;/p&gt;&#10;&lt;h3 id="bezpieczeństwo-różnych-algorytmów"&gt;Bezpieczeństwo różnych algorytmów&lt;/h3&gt;&#10;&lt;p&gt;Na koniec warto wspomnieć o bezpieczeństwie samych algorytmów szyfrujących. Co, jeśli któryś okaże się podatny na ataki? Często aplikacje oferujące szyfrowanie pozwalają na złożenie kilku algorytmów na raz. O ile Twoje hasło pozostaje prawdopodobnie najsłabszym punktem, to szansa na skompromitowanie dwóch algorytmów szyfrujących na raz znacznie spada. Pomyśl, że lata temu używałeś MD5 do potwierdzania integralności plików. Algorytm się &amp;ldquo;zepsuł&amp;rdquo;. Wykonanie SHA1(MD5(&amp;hellip;)) nieco ratuje sprawę. W ten sposób zabezpieczają się np. adresy portfeli z Ethereum. Skompromitowanie jednego algorytmu (co powodowałoby że łatwo wygenerować inny portfel z tym samym adresem) znacznie maleje.&lt;/p&gt;</content:encoded></item><item><title>Pobieranie czegokolwiek z youtuba w fullhd z konsoli</title><link>https://nabozny.io/pobieranie-czegokolwiek-z-youtuba-w-fullhd-z-konsoli/</link><dc:creator>maciej</dc:creator><pubDate>Mon, 11 Feb 2019 22:41:12 +0000</pubDate><category>Inne</category><category>Linux</category><category>linux</category><category>ffmpeg</category><category>media</category><guid isPermaLink="true">https://nabozny.io/pobieranie-czegokolwiek-z-youtuba-w-fullhd-z-konsoli/</guid><description>Niektórzy lubią filmy, inni muzykę, a jeszcze inni po prostu potrzebują zapisać na dysk filmiki z kotami. Powód wymyślcie sobie sami :)</description><content:encoded>&lt;p&gt;Niektórzy lubią filmy, inni muzykę, a jeszcze inni po prostu potrzebują zapisać na dysk filmiki z kotami. Powód wymyślcie sobie sami :)&lt;/p&gt;&#10;&lt;p&gt;Od niedawna wszelkiego rodzaju pobieraczki youtubowe przestały oferować jakość full HD z dźwiękiem. No cóż, trzeba sobie radzić inaczej. Za pomocą youtube-dl można pobrać osobno dowolną ścieżkę audio z youtube oraz dowolną ścieżkę video, o wybranym bitrate, codecu oraz rozdzielczości.&lt;/p&gt;&#10;&lt;h5 id="co-będzie-potrzebne"&gt;Co będzie potrzebne?&lt;/h5&gt;&#10;&lt;p&gt;Potrzeba nam dwóch narzędzi. A w zasadzie trzech – pipem pobierzemy downloader:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;sudo apt install ffmpeg python3-pip&#10;sudo pip3 install youtube_dl&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Pakiet &lt;em&gt;pip&lt;/em&gt; będzie nam służył do pobrania właściwego narzędzia w pythonie – youtube-dl. Za jego pomocą można zobaczyć co dany film oferuje do pobrania – wszystkie wersje audio i video oraz umożliwia pobranie ich. Ffmpeg posłuży do połączenia osobnych ścieżek audio oraz video w jeden plik.&lt;/p&gt;&#10;&lt;h5 id="pobranie-audio-i-video"&gt;Pobranie audio i video&lt;/h5&gt;&#10;&lt;p&gt;Po pierwsze wybierzmy jakieś interesujące video. Na przykład to: &lt;a href="https://www.youtube.com/watch?v=G1IbRujko-A"&gt;https://www.youtube.com/watch?v=G1IbRujko-A&lt;/a&gt;.&lt;/p&gt;&#10;&lt;p&gt;Za pomocą youtube-dl można wyświetlić listę dostępnych ścieżek audio i video:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;~&amp;gt; youtube-dl -F &amp;#39;https://www.youtube.com/watch?v=G1IbRujko-A&amp;#39;&#10;[youtube] G1IbRujko-A: Downloading webpage&#10;[youtube] G1IbRujko-A: Downloading video info webpage&#10;[youtube] G1IbRujko-A: Downloading js player vflYv1bWD&#10;[info] Available formats for G1IbRujko-A:&#10;format code extension resolution note&#10;249 webm audio only DASH audio 117k , opus @ 50k, 272.51MiB&#10;250 webm audio only DASH audio 136k , opus @ 70k, 351.10MiB&#10;140 m4a audio only DASH audio 162k , m4a_dash container, mp4a.40.2@128k, 545.84MiB&#10;171 webm audio only DASH audio 191k , vorbis@128k, 577.59MiB&#10;251 webm audio only DASH audio 207k , opus @160k, 667.00MiB&#10;160 mp4 256x144 144p 170k , avc1.4d400c, 24fps, video only, 205.76MiB&#10;133 mp4 426x240 240p 237k , avc1.4d4015, 24fps, video only, 543.85MiB&#10;278 webm 256x144 144p 281k , webm container, vp9, 24fps, video only, 401.04MiB&#10;242 webm 426x240 240p 302k , vp9, 24fps, video only, 467.08MiB&#10;134 mp4 640x360 360p 338k , avc1.4d401e, 24fps, video only, 968.71MiB&#10;243 webm 640x360 360p 528k , vp9, 24fps, video only, 1.41GiB&#10;135 mp4 854x480 480p 690k , avc1.4d401e, 24fps, video only, 2.24GiB&#10;244 webm 854x480 480p 739k , vp9, 24fps, video only, 2.37GiB&#10;247 webm 1280x720 720p 1090k , vp9, 24fps, video only, 3.61GiB&#10;136 mp4 1280x720 720p 1154k , avc1.4d401f, 24fps, video only, 4.41GiB&#10;137 mp4 1920x1080 1080p 2373k , avc1.640028, 24fps, video only, 9.32GiB&#10;248 webm 1920x1080 1080p 2647k , vp9, 24fps, video only, 5.18GiB&#10;43 webm 640x360 medium , vp8.0, vorbis@128k, 3.06GiB&#10;18 mp4 640x360 medium 485k , avc1.42001E, mp4a.40.2@ 96k (44100Hz), 2.04GiB&#10;22 mp4 1280x720 hd720 1179k , avc1.64001F, mp4a.40.2@192k (44100Hz) (best&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;W tym przypadku można pobrać gotowy plik – ścieżkę 22. W niektórych filmach, np. w teledyskach nie ma połączonej wersji audio + video i jest możliwość pobrania tylko osobnego audio i osobnego video. Zróbmy więc tak:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;youtube-dl --format 171 &amp;#39;https://www.youtube.com/watch?v=G1IbRujko-A&amp;#39;&#10;youtube-dl --format 136 &amp;#39;https://www.youtube.com/watch?v=G1IbRujko-A&amp;#39;&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Ścieżka o id formatu 171 to audio w kontenerze webm, z kodekiem vorbis i bitrate 191k (powyższa tabelka). Ścieżka 136 to video 1280x720 mp4. W bieżącym katalogu powinieneś widzieć dwa nowe pliki – jeden z rozszerzeniem webm, a drugi mp4. Po otworzeniu w dowolnym odtwarzaczu pierwszy z nich będzie grał, a drugi wyglądał.&lt;/p&gt;&#10;&lt;h5 id="łączenie-w-całość"&gt;Łączenie w całość&lt;/h5&gt;&#10;&lt;p&gt;Na koniec pozostaje połączyć jedno i drugie w jeden plik MP4:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;ffmpeg -i [plik].webm -i [plik].mp4 -acodec copy -vcodec copy plik-calosc.mp4&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Powyższe polecenie przekopiuje ścieżkę audio (-acodec copy) oraz ścieżkę video (-vcodec copy) bez ponownej kompresji. Obie ścieżki zostaną zapisane w pliku &lt;em&gt;plik-calosc.mp4&lt;/em&gt;. I to wszystko.&lt;/p&gt;</content:encoded></item></channel></rss>