Archiwum kategorii: Linux

Logo projektu Let's Encypt i firmy OVH

SSL/TLS z Let’s Encrypt dla domen w OVH.

Let’s Encrypt ma plugin dla OVH, skorzystamy więc z niego w poniższym tutorialu. Opisuje w nim krok po kroku jak skonfigurować automatyczne generowanie i odświeżanie certyfikatów z wykorzystaniem dns-01 challenge.

Instrukcja ta ma zastosowanie tylko gdy Twoja domena hostowana jest przez OVH, wykorzystywać bowiem będziemy API tej firmy.

Pokazane rozwiązanie pozwala na generowanie certyfikatów dla pojedynczych domen, oraz wildcardów.

Żeby było jeszcze ciekawiej, skorzystamy z oficjalnego obrazu certbot/dns-ovh, także tutorial ten można zastosować praktycznie na każdym linuxie z dockerem lub podmanem. No i przy okazji jest szansa, że będzie przez jakiś czas aktualny. 😉

1. Generowanie credentiali w OVH

Na stronie https://api.ovh.com/createToken/ musimy wypełnić formularz w celu wygenerowania danych używanych później do tworzenia i kasowania wpisów dns. Zgodnie z dokumentacją pluginu certbot-dns-ovh, by móc potwierdzić bycie właścicielem domeny na potrzeby uzyskania certyfikatu Let’s Encrypt, będziemy potrzebować dostępu do następujących metod udostępnionych przez API OVH:

  • GET /domain/zone/*
  • PUT /domain/zone/*
  • POST /domain/zone/*
  • DELETE /domain/zone/*

Tak wygląda formularz, który wypełniłem dla swojego rozwiązania:

OVH API, Creating API Keys for your script

Klikamy na 'Create keys’, jeżeli macie skonfigurowane 2FA OVH poprosi o zatwierdzenie akcji, a rezulatem będzie tabelka zawierająca:

  1. Script name
  2. Script Description
  3. Application Key
  4. Application Secret
  5. Consumer Key

Notujemy z boku te wartości, będą za chwilkę potrzebne.

2. Konfiguracja certbota

Kolejny krok polega na stworzeniu pliku zawierającego dane pozwalające na autentykację w api OVH. Muszą się w nim znaleźć cztery kluczowe informacje – dns_ovh_endpoint, dns_ovh_application_key, dns_ovh_application_secret, dns_ovh_consumer_key.

Ja założyłem ten plik w /etc/certbot/ovh-secrets.conf. Pamiętajcie, że raczej nie chcecie, żeby ktoś mógł tworzyć, czy kasować subdomeny w waszym imieniu, także zabezpieczcie ten pliczek. Powiedzmy, że wystarczy chmod 0600 /etc/certbot/ovh-secrets.conf.

Przykładowy plik konfiguracyjny:

dns_ovh_endpoint = ovh-eu
dns_ovh_application_key = 7rPawafweAJYWNvhH
dns_ovh_application_secret = 4SFtGD012npocan90nc0qkTWBflirSEf8oo
dns_ovh_consumer_key = Et3lLNe2fthx3MeMFaHe3ssoS71qQZHZ
Jeżeli przygotowujesz konfigurację dla infrastruktury zarządzanej przez OVH w Ameryce Północnej, parametr dns_ovh_endpoint powinien mieć wartość ovh-ca. 

3. Katalogi, uprawnienia, selinux

Przygotuj potrzebne do działania katalogi i ustaw odpowiednie uprawnienia. W tym tutorialu przyjmuję, że certbot będzie uruchamiany jako root i chcemy trzymać certyfikaty w głównej strukturze systemu (/etc, /var).

mkdir /etc/letsencrypt /var/lib/letsencrypt /var/log/letsencrypt
chmod 0700 /etc/letsencrypt /var/lib/letsencrypt /var/log/letsencrypt

3.1 SELinux?

Na Fedorze SELinux zablokował mi certbota przy próbie zapisu do powyższych katalogów. Jako, że będzie do nich pisać tylko ta aplikacja, ustawiłem/dodałem im kontekst container_file_t.

# semanage fcontext -a -t container_file_t '/etc/letsencrypt'
# restorecon -v /etc/letsencrypt/
# semanage fcontext -a -t container_var_lib_t /var/lib/letsencrypt
# restorecon -v /var/lib/letsencrypt
# semanage fcontext -a -t container_file_t /var/log/letsencrypt
# restorecon -v /var/log/letsencrypt

3.2 Logrotate

Jak można się domyślić po tym, że zakładamy dedykowany katalog w /var/log, będziemy zbierać logi. Logi zbyt długo trzymane to zło, także dodaj sobie do katalogu /etc/logrotate.d pliczek certbot, którego zawartość to:


Dzięki temu będziemy trzymać logi przez 60 dni, no i log będzie 'łamany’ codziennie. Co oczywiste chcemy też starsze logi pakować, a użyjemy do tego komendy xz.

4. Pierwsze uruchomienie

Podman czy Docker?

To zależy… Ja pracuję na Fedorze, lubię podmana i w tutorialu będę używał komendy podman.

Co gdy używasz Dockera?
Składnia jest identyczna, także zamień słówko podman na docker i wszystko będzie działać bez problemu. 🙂

podman run \
 --rm \
 --name letsencrypt \
 -v /etc/certbot/ovh-secrets.conf:/ovh-secrets.conf \
 -v /etc/letsencrypt:/etc/letsencrypt \
 -v /var/lib/letsencrypt:/var/lib/letsencrypt \
 -v /var/log/letsencrypt:/var/log/letsencrypt \ 
 certbot/dns-ovh:latest certonly \
   --non-interactive \
   --agree-tos -m twój@email.pl \ 
   --dns-ovh
   --dns-ovh-credentials /ovh-secrets.conf \
   --dns-ovh-propagation-seconds 10 \
   -d example.com -d *.example.com -d innadomena.pl

Ło panie, a dłuższej tej komendy się nie dało? Przeanalizujmy linia po linii co tu się dzieje:

  1. podman run – uruchom kontener (możesz zmienić na docker run, jeżeli używasz dockera)
  2. –rm – posprzątaj po zakończeniu życia kontenera
  3. –name letsencrypt – kontener ma się nazywać letsencrypt
  4. -v ścieżka_na_serwerze:ścieżka_w_kontenerze – te pliki i katalogi z serwera będą dostępne w kontenerze. Tam gdzie sobie zażyczyliśmy.
  5. certbot/dns-ovh:latest certonly – uruchamiamy najnowszą wersję kontenera dns-ovh. To w nim zainstalowany jest certbot, wraz z naszym pluginem do ovh. Instruujemy certbota, że ma działać w trybie 'certonly’.
  6. –non-interactive – nie chcemy być zbyt rozmowni i odpowiadać na pytania
  7. –agree-tos -m twój@email.pl – zgadzamy się na licencję i podajemy swojego maila, żeby Let’s Encrypt wiedziało z kim gadać na temat domen, którym wystawi certyfikat. W praktyce mail jest używany do powiadomienia, że certyfikat niedługo wygaśnie.
  8. –dns-ovh – odpal challenge dns01 z wykorzystaniem pluginu dns-ovh
  9. –dns-ovh-credentials /ovh-secrets.conf – plugin musi wiedzieć jak się zalogować do OVH, także pokazujemy mu gdzie leży przygotowany przez nas config.
  10. –dns-ovh-propagation-seconds 10 – halt! Dajmy ovh 10 sekund, żeby się zdążyła dodać domena.
  11. -d, -d, -d, -d itd. – Lista domen dla których chcemy dostać certyfikaty.

Przykładowy log:

# podman run --rm --name letsencrypt -v /etc/certbot/ovh-secrets.conf:/ovh-secrets.conf -v /etc/letsencrypt:/etc/letsencrypt -v /var/lib/letsencrypt:/var/lib/letsencrypt certbot/dns-ovh:latest certonly --agree-tos -m mój@email.pl  --dns-ovh --dns-ovh-credentials /ovh-secrets.conf --dns-ovh-propagation-seconds 10 -d example.com -d *.example.com

Requesting a certificate for example.com and example.com
Waiting 10 seconds for DNS changes to propagate

Successfully received certificate.
Certificate is saved at: /etc/letsencrypt/live/example.com/fullchain.pem
Key is saved at:         /etc/letsencrypt/live/example.com/privkey.pem
This certificate expires on 2021-10-28.
These files will be updated when the certificate renews.

NEXT STEPS:
- The certificate will need to be renewed before it expires. Certbot can automatically renew the certificate in the background, but you may need to take steps to enable that functionality. See https://certbot.org/renewal-setup for instructions.
Saving debug log to /var/log/letsencrypt/letsencrypt.log

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
If you like Certbot, please consider supporting our work by:
 * Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
 * Donating to EFF:                    https://eff.org/donate-le
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

5. Mamy certyfikat, co dalej?

Zapewne po coś ten certyfikat był ci potrzebny. Apache httpd? Nginx? Haproxy? W tym tutorialu nic o nich nie będzie, ale tak. To są programy w których możesz użyć uzyskane właśnie certy.

5.1 Odnawianie

Po przeczytaniu loga na 100% (jak nie na 100000!) rzuciła ci się w oczy informacja, że certyfikaty trzeba odnawiać, i są raczej dość krótko ważne. Co z tym zrobić?

Stwórz sobie pliczek /usr/local/bin/certbot_renew.sh (albo o innej nazwie!) i nadaj mu prawa do wykonywania (chmod +x /usr/local/bin/certbot_renew.sh).
Do środka wrzuć wywołanie certbota w trybie 'renew’, oczywiście w stylu dns-ovh. Żeby mieć pewność, że korzystam z najnowszej wersji kontenera dns-ovh, dorzuciłem jeszcze podman pull na początku:


Odpalamy testowo:

# vi /usr/local/bin/certbot_renew.sh
# chmod +x /usr/local/bin/certbot_renew.sh
# /usr/local/bin/certbot_renew.sh
Trying to pull docker.io/certbot/dns-ovh:latest...
Getting image source signatures
...
Copying blob 0b99e4c5dcd1 [--------------------------------------] 0.0b / 0.0b
Copying config be6f9c73b8 done  
Writing manifest to image destination
Storing signatures
be6f9c73b8127bdd5983728deba3644efffb6826ff4d9be6757b2cf60c4aef5f

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Processing /etc/letsencrypt/renewal/example.com.conf
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Certificate not yet due for renewal

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
The following certificates are not due for renewal yet:
  /etc/letsencrypt/live/example.com/fullchain.pem expires on 2021-10-28 (skipped)
No renewals were attempted.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Saving debug log to /var/log/letsencrypt/letsencrypt.log

5.2 systemd service i timer

Mamy już działający skrypcik, który gdy będzie czas odnowi certyfikaty. Wypadałoby teraz go uruchamiać cyklicznie. Cron jest trochę passe, także treningowo zróbmy to przy użyciu systemd.

Stwórz plik /etc/systemd/system/certbot_renew.service:


Następnie timer, który będzie go uruchamiać (/etc/systemd/system/certbot_renew.timer):


Pozostaje nam już tylko przeładować ustawienia i włączyć timer:

# systemctl daemon-reload
# systemctl enable certbot_renew.timer 
Created symlink /etc/systemd/system/timers.target.wants/certbot_renew.timer → /etc/systemd/system/certbot_renew.timer.

Podsumowanie i aftercare Let’s Encrypt + OVH

Raz zdefiniowany proces odświeżania certyfikatów powinien być bezobsługowy, pamiętajmy jednak, że dobrze jest czasem zajrzeć do logów:

  • journalctl -u certbot_renew.service
  • cat /var/log/letsencrypt/letsencrypt.log

No i na szczęście Let’s encrypt jest o tyle miłe, że w razie czego ostrzeże mailowo, gdy odnowienie certa nie wykona się o czasie.

Ściana z czerwonej cegły, a za nią monitor. Obok napis firewalld

firewalld – przekierowywanie portów

Lubię firewalld. Nie zawsze jednak pamiętam składnię firewall-cmd.
Szybka notatka jak zestawić przekierowanie portu na inne ip i port.

Uruchom jako root:

# firewall-cmd --permanent --zone=public --add-forward-port=port=80:proto=tcp:toport=8080:toaddr=10.1.1.10

Rezultatem tej komendy będzie ustanowienie przekierowania z portu 80(port) twojej maszyny, na port 8080(toport) maszyny o adresie 10.1.1.10(toaddr). Jeżeli chcesz przekierować porty wewnątrz jednego hosta, pomiń część 'toaddr’.

Co dalej? Standardowo:

# firewall-cmd --reload
# firewall-cmd --list-all

Pamiętaj, że samo przekierowanie portu może nie wystarczyć. Musisz go mieć otwartego. W tym celu wykonaj dodatkowo komendę:

# firewall-cmd --add-port=80/tcp --permanent
# firewall-cmd --reload

I zweryfikuj czy pojawia się na liście otwartych portów:

#firewall-cmd --list-all

Przekierowanie oczywiście można też usunąć.
Wystarczy zamienić w komendzie dodawania słówko add na remove:

# firewall-cmd --permanent --zone=public --remove-forward-port=port=80:proto=tcp:toport=8080:toaddr=10.1.1.10

Firewalld powinien ogłosić sukces, a nam pozostaje tylko załadować zmiany do pamięci komendą firewall-cmd –reload.

Logo Linux Fedora

Jak zaktualizować Fedorę?

Kolejnych kilka komend, których notorycznie zapominam. Jak zaktualizować Linux Fedora?

Chyba najprościej z terminala. Co prawda jest też jakiś kreator graficzny, ale nie jestem fanem.

# dnf upgrade --refresh
# dnf install dnf-plugin-system-upgrade
# dnf system-upgrade download --releasever=XX*
# dnf system-upgrade reboot
  • W miejsce XX wpisujemy numerek wersji. Supportowany jest upgrade o 1 lub 2 oczka w górę. Szczerze mówiąc na skok o dwa się jeszcze nie odważyłem, ale o 1 idzie bez problemów.
Napis ssh na czarnym tle

SSH długi czas logowania

GSS – Przy próbie połączenia się po ssh, dodaj -vvv do polecenia. Odpalisz w ten sposób szczegółowy debug. Jeżeli w logu zatrzymujesz się przy wpisach podobnych do tych:

debug1: Unspecified GSS failure.  Minor code may provide more information 
Ticket expired

Rozwiązaniem jest zedytowanie pliku /etc/ssh/sshd_config i ustawienie:

GSSAPIAuthentication no

Jeżeli pracujesz na Fedorze/RHEL/CentOS’ie, to ustawienie to bywa zdublowane w pliku /etc/ssh/sshd_config.d/50-redhat.conf. Również w nim zmień wartość na no.

RevDNS – Zdarza się, że opóźnienie powodowane jest sprawdzaniem nazwy hosta, z którego się łączysz. Żeby wyłączyć to sprawdzanie ustaw:

UseDNS no

W celu zatwierdzenia zmian, zrestartuj serwer sshd. Na Fedorze zrobisz to bezpiecznie komendą:

# systemctl restart sshd

btmp – powyższe nie pomogło? Sprawdź jak duży jest twój plik /var/log/btmp.
Czemu? Przykładowo na Fedorach, w /etc/pam.d/postlogin skonfigurowane jest:

session     optional      pam_lastlog.so silent noupdate showfailed

Opcja showfailed, sprawdza w btmp ile było nieprawidłowych prób zalogowania się na Twój login. Jeżeli serwer jest publiczny, to zapewne nie było ich mało. Rozwiązanie?
Skasuj btmp, zainstaluj logrotate i skonfiguruj częstsze czyszczenie tego logu.
Na Fedorze będzie wyglądać to tak.

# dnf install logrotate
# systemctl enable logrotate.timer

Logrotate standardowo czyści btmp. Definicję znajdziesz w pliku /etc/logrotate.d/btmp. Dzięki włączeniu timera, logrotate uruchomi się raz dziennie.

Chroot w rescue mode (np. OVH)

Miałem ostatnio sytuację z niedziałającym VPS’em w OVH. Jedyna opcja to – rescue mode i dostanie się 'z boku’ do systemu.

Jak to zrobić dobrze?

# mkdir -p /mnt/myos
# mount /dev/sdXX /mnt/myos
# mount -t proc proc /mnt/myos/proc
# mount --rbind /dev /mnt/myos/dev
# mount --rbind /sys /mnt/myos/sys
# chroot /mnt/myos /bin/bash

W moim przypadku dyskiem vps’a jest /dev/sdb1. Jeżeli nie wiesz jaki dysk powinieneś podać, użyj komend 'lsblk’ i 'blkid’.

Możemy teraz wprowadzać zmiany mniej więcej tak samo jakby nasz system działał.

Jeżeli używamy selinuxa i wprowadzaliśmy jakieś zmiany, dobrze jest na koniec prac wykonać:

# touch /.autorelabel

wymusi to przebudowanie etykiet selinuxa dla wszystkich plików.

Na koniec ctrl-d, żeby się wylogować z chroota i restart do normalnego trybu. Trzymam kciuki za poprawność wprowadzonych zmian. 🙂

Ustawienie strefy czasowej (systemd).

W paczce systemd możemy znaleźć narzędzie do zarządzania ustawieniami czasu w Linuksie. Narzędzie pokazuje obecny status, pozwala też w prosty status ustawić TZ.

Weryfikacja:

$ timedatectl
Local time: Sat 2020-05-23 15:39:25 CEST
Universal time: Sat 2020-05-23 13:39:25 UTC
RTC time: Sat 2020-05-23 13:39:25
Time zone: Europe/Paris (CEST, +0200)
System clock synchronized: yes
NTP service: active
RTC in local TZ: no

Chcę, żeby serwer pracował zgodnie z czasem polskim. Komenda 'timedatectl list-timezones’ pozwala znaleźć odpowiadającą nam strefę czasową, czyli w tym przypadku 'Europe/Warsaw’.


No to ustawiamy i weryfikujemy:

$ sudo timedatectl set-timezone 'Europe/Warsaw'
$ timedatectl
Local time: Sat 2020-05-23 15:41:38 CEST
Universal time: Sat 2020-05-23 13:41:38 UTC
RTC time: Sat 2020-05-23 13:41:38
Time zone: Europe/Warsaw (CEST, +0200)
System clock synchronized: yes
NTP service: active
RTC in local TZ: no

Chcesz automatycznie synchronizować czas na swoim serwerze? Rzuć okiem na artykuł opisujący jak skonfigurować ntp z użyciem serwisu systemd-timesyncd.

Synchro czasu ntp Linux (systemd)

Kolejny już wpis o synchronizacji czasu ntp. O dziwo pozmieniało się od 2014 roku, kiedy cały dumny opisałem jak w sposób nieprzesadzony synchronizować czas na Linuxach. 😉

Ktoś mądrzejszy ode mnie wdrożył to o czym pisałem – debilizmem jest stawianie serwera ntpd, jeżeli nasz Linux ma być tylko klientem. Wdrożył w systemd, także dystrybucje które go używają, powinny mieć to rozwiązanie out of box (na pewno mają Fedora, Arch i HypriotOS).

No to po kolei. Sprawdzamy czy faktycznie posiadamy taki serwis:

[szydell@laPtak ~]$ systemctl status systemd-timesyncd.service
● systemd-timesyncd.service - Network Time Synchronization
Loaded: loaded (/usr/lib/systemd/system/systemd-timesyncd.service; disabled; vendor preset: disabled)
Active: inactive (dead)
Docs: man:systemd-timesyncd.service(8)

Jest. Sprawdzamy czy nasz system nie korzysta już czasem z jakiegoś dostawcy czasu. Jeżeli korzysta, to wyłączamy.

Chronyd (Fedora):

$ sudo systemctl status chronyd
● chronyd.service - NTP client/server
Loaded: loaded (/usr/lib/systemd/system/chronyd.service; enabled; vendor preset: enabled)
Active: active (running) since Fri 2020-05-22 12:39:12 CEST; 59min ago
...

$ sudo systemctl disable chronyd.service --now
Removed /etc/systemd/system/multi-user.target.wants/chronyd.service.

Ntpd (HypriotOS/Debian):

$ sudo systemctl status ntp
● ntp.service - Network Time Service
Loaded: loaded (/lib/systemd/system/ntp.service; enabled; vendor preset: enabled)
Active: active (running) since Tue 2020-05-12 09:17:07 UTC; 1 weeks 3 days ago
...

$ sudo systemctl disable ntp --now
Synchronizing state of ntp.service with SysV service script with /lib/systemd/systemd-sysv-install.
Executing: /lib/systemd/systemd-sysv-install disable ntp
Removed /etc/systemd/system/multi-user.target.wants/ntp.service.

$ sudo apt purge ntp
Reading package lists… Done
Building dependency tree
Reading state information… Done
The following packages will be REMOVED:
ntp*
0 upgraded, 0 newly installed, 1 to remove and 0 not upgraded.
After this operation, 1,848 kB disk space will be freed.
Do you want to continue? [Y/n]
(Reading database … 28459 files and directories currently installed.)
Removing ntp (1:4.2.8p12+dfsg-4) …
Processing triggers for man-db (2.8.5-2) …
(Reading database … 28404 files and directories currently installed.)
Purging configuration files for ntp (1:4.2.8p12+dfsg-4) …
Processing triggers for systemd (241-7~deb10u3+rpi1) …

Konfiguracja serwisu systemd-timesyncd leży w pliku /etc/systemd/timesyncd.conf.
Ustaw NTP i FallbackNTP. Resztę parametrów można skasować. Wartości domyślne są ok.

Przykładowy config (główne serwery ntp z polski. Awaryjne z zasobów Fedory i Debiana):

# cat systemd/timesyncd.conf
[Time]
NTP=0.pl.pool.ntp.org 1.pl.pool.ntp.org 2.pl.pool.ntp.org 3.pl.pool.ntp.org
FallbackNTP=0.fedora.pool.ntp.org 1.fedora.pool.ntp.org 2.fedora.pool.ntp.org 3.fedora.pool.ntp.org 0.debian.pool.ntp.org 1.debian.pool.ntp.org 2.debian.pool.ntp.org 3.debian.pool.ntp.org

Uruchomienie:

[szydell@laPtak ~]$ sudo systemctl enable systemd-timesyncd.service --now
Created symlink /etc/systemd/system/dbus-org.freedesktop.timesync1.service → /usr/lib/systemd/system/systemd-timesyncd.service.
Created symlink /etc/systemd/system/sysinit.target.wants/systemd-timesyncd.service → /usr/lib/systemd/system/systemd-timesyncd.service.

[szydell@laPtak ~]$ sudo timedatectl set-ntp true

Efekty zmian można szybko zweryfikować:

[szydell@laPtak ~]$ timedatectl
Local time: pią 2020-05-22 14:48:08 CEST
Universal time: pią 2020-05-22 12:48:08 UTC
RTC time: pią 2020-05-22 12:48:08
Time zone: Europe/Warsaw (CEST, +0200)
System clock synchronized: yes
NTP service: active
RTC in local TZ: no

[szydell@laPtak ~]$ timedatectl timesync-status
Server: 162.159.200.123 (0.pl.pool.ntp.org)
Poll interval: 1min 4s (min: 32s; max 34min 8s)
Leap: normal
Version: 4
Stratum: 3
Reference: A490853
Precision: 1us (-25)
Root distance: 11.786ms (max: 5s)
Offset: +2.133ms
Delay: 12.974ms
Jitter: 0
Packet count: 1
Frequency: -13,327ppm
VueScan logo

VueScan Linux, problem z wykrywaniem skanera sieciowego Epson XP-640

Problem niestety jest szerszy, bo dotyczy sposobu w jaki działa protokół wykrywania urządzeń w sieci, który to dopuszcza aby urządzenie samo nawiązywało połączenie do komputera, np. w celu dosłania dodatkowych szczegółów dotyczących ich konfiguracji. FirewallD takie połączenia traktuje jak 'niezamówiony ruch’, nie potrafi ich śledzić i dropuje.

Testy z wykorzystaniem avahi-browse i tcpdump’a pokazały, że problemem jest blokowanie odpowiedzi zwrotnej generowanej przez skaner. Nie chciałem całkowicie wyłączać firewalld (a takie rozwiązanie krążą głównie po sieci), ale niestety nie udało mi się jak na razie znaleźć eleganckiego rozwiązania.

Nieeleganckie rozwiązanie:

# firewall-cmd --zone=public --add-rich-rule='rule family="ipv4" source address="<tutaj-wpisz-ip-skanera>" accept'
# firewall-cmd --runtime-to-permanent

Zrzuty z analizy:

Start VueScan z poziomu tcpdump’a

[szydell@laPtak ~]$ sudo tcpdump src or dst 192.168.88.247
dropped privs to tcpdump
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on wlp3s0, link-type EN10MB (Ethernet), capture size 262144 bytes
23:42:09.219064 IP 192.168.88.247.mdns > laPtak.53278: 0- [4q],,, 4/0/10 PTR EPSON XP-640 Series._printer._tcp.local., PTR EPSON XP-640 Series._pdl-datastream._tcp.local., PTR EPSON XP-640 Series._scanner._tcp.local., PTR EPSON XP-640 Series._uscan._tcp.local. (1306) 23:42:09.219160 ARP, Request who-has 192.168.88.247 tell laPtak, length 28 23:42:09.317428 ARP, Reply 192.168.88.247 is-at 9c:ae:d3:12:68:97 (oui Unknown), length 28 23:42:09.317454 IP laPtak > 192.168.88.247: ICMP host laPtak unreachable - admin prohibited, length 556 23:42:09.420803 IP 192.168.88.247.mdns > laPtak.53278: 0- [4q],,, 4/0/10 PTR EPSON XP-640 Series._printer._tcp.local., PTR EPSON XP-640 Series._pdl-datastream._tcp.local., PTR EPSON XP-640 Series._scanner._tcp.local., PTR EPSON XP-640 Series._uscan._tcp.local. (1306)
23:42:09.420892 IP laPtak > 192.168.88.247: ICMP host laPtak unreachable - admin prohibited, length 556
23:42:09.731152 IP 192.168.88.247.mdns > laPtak.53278: 0- [4q],,, 4/0/10 PTR EPSON XP-640 Series._printer._tcp.local., PTR EPSON XP-640 Series._pdl-datastream._tcp.local., PTR EPSON XP-640 Series._scanner._tcp.local., PTR EPSON XP-640 Series._uscan._tcp.local. (1306) 23:42:09.731245 IP laPtak > 192.168.88.247: ICMP host laPtak unreachable - admin prohibited, length 556 23:42:10.046472 IP 192.168.88.247.mdns > laPtak.53278: 0- [4q],,, 4/0/10 PTR EPSON XP-640 Series._printer._tcp.local., PTR EPSON XP-640 Series._pdl-datastream._tcp.local., PTR EPSON XP-640 Series._scanner._tcp.local., PTR EPSON XP-640 Series._uscan._tcp.local. (1306)
23:42:10.046562 IP laPtak > 192.168.88.247: ICMP host laPtak unreachable - admin prohibited, length 556
23:42:10.344299 IP 192.168.88.247.mdns > laPtak.53278: 0- [4q],,, 4/0/10 PTR EPSON XP-640 Series._printer._tcp.local., PTR EPSON XP-640 Series._pdl-datastream._tcp.local., PTR EPSON XP-640 Series._scanner._tcp.local., PTR EPSON XP-640 Series._uscan._tcp.local. (1306) 23:42:10.344386 IP laPtak > 192.168.88.247: ICMP host laPtak unreachable - admin prohibited, length 556 23:42:10.649270 IP 192.168.88.247.mdns > laPtak.53278: 0- [4q],,, 4/0/10 PTR EPSON XP-640 Series._printer._tcp.local., PTR EPSON XP-640 Series._pdl-datastream._tcp.local., PTR EPSON XP-640 Series._scanner._tcp.local., PTR EPSON XP-640 Series._uscan._tcp.local. (1306)
23:42:10.649358 IP laPtak > 192.168.88.247: ICMP host laPtak unreachable - admin prohibited, length 556
23:42:10.956049 IP 192.168.88.247.mdns > laPtak.53278: 0- [4q],,, 4/0/10 PTR EPSON XP-640 Series._printer._tcp.local., PTR EPSON XP-640 Series._pdl-datastream._tcp.local., PTR EPSON XP-640 Series._scanner._tcp.local., PTR EPSON XP-640 Series._uscan._tcp.local. (1306) 23:42:10.956131 IP laPtak > 192.168.88.247: ICMP host laPtak unreachable - admin prohibited, length 556 23:42:11.263326 IP 192.168.88.247.mdns > laPtak.53278: 0- [4q],,, 4/0/10 PTR EPSON XP-640 Series._printer._tcp.local., PTR EPSON XP-640 Series._pdl-datastream._tcp.local., PTR EPSON XP-640 Series._scanner._tcp.local., PTR EPSON XP-640 Series._uscan._tcp.local. (1306)
23:42:11.263416 IP laPtak > 192.168.88.247: ICMP host laPtak unreachable - admin prohibited, length 556
23:42:11.569615 IP 192.168.88.247.mdns > laPtak.53278: 0*- [4q],,, 4/0/10 PTR EPSON XP-640 Series._printer._tcp.local., PTR EPSON XP-640 Series._pdl-datastream._tcp.local., PTR EPSON XP-640 Series._scanner._tcp.local., PTR EPSON XP-640 Series._uscan._tcp.local. (1306)
23:42:14.288668 ARP, Request who-has laPtak tell 192.168.88.247, length 28
23:42:14.288694 ARP, Reply laPtak is-at 34:e1:2d:ee:21:18 (oui Unknown), length 28

avahi-browse

[root@laPtak ~]#  avahi-browse -rt _scanner._tcp
+ wlp3s0 IPv6 EPSON XP-640 Series                           _scanner._tcp        local
+ wlp3s0 IPv4 EPSON XP-640 Series                           _scanner._tcp        local
= wlp3s0 IPv4 EPSON XP-640 Series                           _scanner._tcp        local
   hostname = [EPSON126897.local]
   address = [192.168.88.247]
   port = [1865]
   txt = ["note=" "scannerAvailable=1" "UUID=cfe92100-67c4-11d4-a45f-9caed3126897" "mdl=XP-640 Series" "mfg=EPSON" "adminurl=http://EPSON126897.local.:80/PRESENTATION/BONJOUR" "ty=EPSON XP-640 Series" "txtvers=1"]
Failed to resolve service 'EPSON XP-640 Series' of type '_scanner._tcp' in domain 'local': Timeout reached
postfix logo

postfix, brak public/pickup

Do lokalnego dostarczania poczty miałem zainstalowane estmp. Chyba jest to rozwiązanie 'standardowe’ w Fedorze. Dziwnie działało, potrafiło mi zjeść całego core’a, także beż żalu zmieniłem na lepiej mi znanego postfixa.

W logach odkryłem, że z postfixem też coś nie halo:

laPtak postfix/postdrop[7937]: warning: unable to look up public/pickup: No such file or directory

Rozwiązanie:

sudo mkfifo /var/spool/postfix/public/pickup
sudo systemctl restart postfix
logo kubernetes

Kubernetes na Raspberry Pi 4b

Notatki krok po kroku – jak na jednym i więcej Raspbery Pi 4b zainstalować HypriotOS, skonfigurować Dockera i ostatecznie uruchomić klaster Kubernetes.

Do flashowania kart będę używał komputera działającego pod Fedorą, ale kroki są na tyle uniwersalne, że można je spokojnie wykonać na większości innych dystrybucji.

1. HypriotOS

Czemu akurat HypriotOS, a nie Raspbian/Ubuntu/Windows IoT/etc? Z lenistwa 😉
HypriotOS to Debian okrojony gdzie trzeba, a rozbudowany w kierunku bycia hostem dla kontenerów. Dodatkowo jest optymalizowany pod RPi.
Czyli jest dokładnie tym, czego potrzebuję.

1a. Program do flashowania

HypriotOS stworzyło skrypt, który ułatwia flashowanie systemu na kartę sd. Wygodne to to i działa.

https://github.com/hypriot/flash/releases.

Skrypt wykorzystuje hdparm, pv, unzip i curla, także dla świętego spokoju można zacząć od próby doinstalowania braków. Na Fedorze wygląda to tak:

sudo dnf install pv curl unzip hdparm

Teraz ściągamy skrypt. Link do wersji 2.7.0 – przetestowanej i działającej.
Dajemy prawa zapisu i opcjonalnie wrzucamy sobie do /usr/local/bin.

curl -LO https://github.com/hypriot/flash/releases/download/2.7.0/flash
chmod +x flash
sudo mv flash /usr/local/bin/flash

1b. Wstępna konfiguracja

Skrypt 'flash’ daje możliwość sparametryzowania HypriotOS według własnych potrzeb. Część ustawień można podać używając parametrów, część wymaga stworzenia pliku konfiguracyjnego. Można go stworzyć na podstawie dostępnych sampli.

Mój plik konfiguracyjny (myhypriot.yml) wygląda następująco:

Uwaga! Jeżeli skorzystasz z konfiguracji powyżej Malinka dostanie adres z DHCP. Jeżeli adres powinien być statyczny, sprawdź składnię w przykładowych konfigach i wprowadź poprawki w myhypriot.yml.

1c. Flashowanie

Posiadam trzy Raspbery Pi 4. Każde chcę mieć z innym hostname, ale z tym samym plikiem konfiguracyjnym.

W chwili pisania tej notatki, HypriotOS dostępny jest w wersji 1.12.0. Nowsze wersje można znaleźć na stronie https://github.com/hypriot/image-builder-rpi/releases.

Trzykrotnie wkładam więc kartę microsd do laptopa i uruchamiam jako root (za każdym razem z innym hostname):

flash --hostname berta https://github.com/hypriot/image-builder-rpi/releases/download/v1.12.0/hypriotos-rpi-v1.12.0.img.zip
Log z przykładowego wykonania (rozwiń)
# flash --hostname test --userdata myhypriot.yml https://github.com/hypriot/image-builder-rpi/releases/download/v1.12.0/hypriotos-rpi-v1.12.0.img.zip           
Using cached image /tmp/hypriotos-rpi-v1.12.0.img

Is /dev/mmcblk0 correct? y
Unmounting /dev/mmcblk0 ...
Flashing /tmp/hypriotos-rpi-v1.12.0.img to /dev/mmcblk0 ...
1,27GiB 0:00:00 [2,79GiB/s] [============================================================================================================================================================================================>] 100%            
0+20800 records in
0+20800 records out
1363148800 bytes (1,4 GB, 1,3 GiB) copied, 640,081 s, 2,1 MB/s
Waiting for device /dev/mmcblk0

/dev/mmcblk0:
 re-reading partition table
Mounting Disk
Mounting /dev/mmcblk0 to customize...
total 52604
drwxr-xr-x. 3 root root    16384 sty  1  1970 .
drwxr-xr-x. 3 root root       60 maj  2 22:19 ..
-rwxr-xr-x. 1 root root    23966 wrz 20  2019 bcm2708-rpi-b.dtb
-rwxr-xr-x. 1 root root    24229 wrz 20  2019 bcm2708-rpi-b-plus.dtb
-rwxr-xr-x. 1 root root    23747 wrz 20  2019 bcm2708-rpi-cm.dtb
-rwxr-xr-x. 1 root root    23671 lip  8  2019 bcm2708-rpi-zero.dtb
-rwxr-xr-x. 1 root root    24407 lip  8  2019 bcm2708-rpi-zero-w.dtb
-rwxr-xr-x. 1 root root    25293 lip  8  2019 bcm2709-rpi-2-b.dtb
-rwxr-xr-x. 1 root root    25422 wrz 25  2019 bcm2710-rpi-2-b.dtb
-rwxr-xr-x. 1 root root    26463 lip  8  2019 bcm2710-rpi-3-b.dtb
-rwxr-xr-x. 1 root root    27082 lip  8  2019 bcm2710-rpi-3-b-plus.dtb
-rwxr-xr-x. 1 root root    25277 wrz 20  2019 bcm2710-rpi-cm3.dtb
-rwxr-xr-x. 1 root root    40559 wrz 17  2019 bcm2711-rpi-4-b.dtb
-rwxr-xr-x. 1 root root    52296 wrz 25  2019 bootcode.bin
-rwxr-xr-x. 1 root root      246 sty  7 19:31 cmdline.txt
-rwxr-xr-x. 1 root root      203 sty  7 19:31 config.txt
-rwxr-xr-x. 1 root root    18693 cze 24  2019 COPYING.linux
-rwxr-xr-x. 1 root root       20 sty  7 19:30 fake-hwclock.data
-rwxr-xr-x. 1 root root     3073 wrz 25  2019 fixup4cd.dat
-rwxr-xr-x. 1 root root     6167 wrz 25  2019 fixup4.dat
-rwxr-xr-x. 1 root root     9247 wrz 25  2019 fixup4db.dat
-rwxr-xr-x. 1 root root     9249 wrz 25  2019 fixup4x.dat
-rwxr-xr-x. 1 root root     2657 wrz 25  2019 fixup_cd.dat
-rwxr-xr-x. 1 root root     6736 wrz 25  2019 fixup.dat
-rwxr-xr-x. 1 root root     9808 wrz 25  2019 fixup_db.dat
-rwxr-xr-x. 1 root root     9810 wrz 25  2019 fixup_x.dat
-rwxr-xr-x. 1 root root  5310624 wrz 25  2019 kernel7.img
-rwxr-xr-x. 1 root root  5628040 wrz 25  2019 kernel7l.img
-rwxr-xr-x. 1 root root 13230592 wrz 25  2019 kernel8.img
-rwxr-xr-x. 1 root root  5029176 wrz 25  2019 kernel.img
-rwxr-xr-x. 1 root root     1494 cze 24  2019 LICENCE.broadcom
-rwxr-xr-x. 1 root root       23 sty  7 19:25 meta-data
-rwxr-xr-x. 1 root root      365 sty  7 19:35 os-release
drwxr-xr-x. 2 root root    16384 sty  7 19:30 overlays
-rwxr-xr-x. 1 root root   770816 wrz 25  2019 start4cd.elf
-rwxr-xr-x. 1 root root  4733128 wrz 25  2019 start4db.elf
-rwxr-xr-x. 1 root root  2769540 wrz 25  2019 start4.elf
-rwxr-xr-x. 1 root root  3683816 wrz 25  2019 start4x.elf
-rwxr-xr-x. 1 root root   685668 wrz 25  2019 start_cd.elf
-rwxr-xr-x. 1 root root  4854728 wrz 25  2019 start_db.elf
-rwxr-xr-x. 1 root root  2877988 wrz 25  2019 start.elf
-rwxr-xr-x. 1 root root  3792232 wrz 25  2019 start_x.elf
-rwxr-xr-x. 1 root root     1713 sty  7 19:25 user-data
Copying cloud-init myhypriot.yml to /tmp/0/mnt.477635/user-data ...
Set hostname=test
Unmounting /dev/mmcblk0 ...
Finished.

Przekładamy kartę do RPi, podpinamy do sieci i odpalamy. Po kilku minutach potrzebnych na pierwotną autokonfigurację, HypriotOS powinien być dostępny po ssh.

2. OS & Docker

Kroki do wykonania na wszystkich maszynach!

Zaktualizuj i zrestartuj system

$ sudo -i
# apt update && apt upgrade && reboot

Popraw konfigurację Dockera tak, aby:

  • do zarządzania cgroup’ami używał systemd
  • logi tworzył w formacie json
  • zajmował do 100MiB na logi
  • używał sterownika storage w wersji overlay2
$ sudo -i
# cat > /etc/docker/daemon.json <<EOF
{
  "exec-opts": ["native.cgroupdriver=systemd"],
  "log-driver": "json-file",
  "log-opts": { "max-size": "100m" },
  "storage-driver": "overlay2"
}
EOF

(opcjonalnie) Tworzymy katalog na parametry serwisu Docker i restartujemy go

# mkdir -p /etc/systemd/system/docker.service.d
# systemctl daemon-reload
# systemctl restart docker

Ustawiamy parametry kernela pozwalające iptables poprawnie widzieć ruch w bridge’ach.

# cat <<EOF | tee /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
EOF
# sysctl --system

Tworzymy katalog /etc/cnt/net.d. Wymagany jest przez kubelet.service, a niestety instalator go nie przygotowuje. Brak katalogu skutkuje błędami NetworkPluginNotReady.

# mkdir -p /etc/cnt/net.d

3. Instalacja kubernetesa

Kroki do wykonania na wszystkich maszynach!

Dodaj oficjalne repozytorium Kubernetes

# curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add -
# echo "deb http://apt.kubernetes.io/ kubernetes-xenial main" > /etc/apt/sources.list.d/kubernetes.list
# apt update

Zainstaluj!

# apt install -y kubeadm

4. To ja tu rządzę!

Wybierz jedną z maszyn do zarządzania klastrem. Wydaj na niej komendę:

# kubeadm init --pod-network-cidr 10.244.0.0/16
Log z wykonania (rozwiń)
# kubeadm init --pod-network-cidr 10.244.0.0/16
W0503 10:39:57.299688 32439 configset.go:202] WARNING: kubeadm cannot validate component configs for API groups [kubelet.config.k8s.io kubeproxy.config.k8s.io]
[init] Using Kubernetes version: v1.18.2
[preflight] Running pre-flight checks
[preflight] Pulling images required for setting up a Kubernetes cluster
[preflight] This might take a minute or two, depending on the speed of your internet connection
[preflight] You can also perform this action in beforehand using 'kubeadm config images pull'
[kubelet-start] Writing kubelet environment file with flags to file "/var/lib/kubelet/kubeadm-flags.env"
[kubelet-start] Writing kubelet configuration to file "/var/lib/kubelet/config.yaml"
[kubelet-start] Starting the kubelet
[certs] Using certificateDir folder "/etc/kubernetes/pki"
[certs] Generating "ca" certificate and key
[certs] Generating "apiserver" certificate and key
[certs] apiserver serving cert is signed for DNS names [elsa kubernetes kubernetes.default kubernetes.default.svc kubernetes.default.svc.cluster.local] and IPs [10.96.0.1 192.168.88.16]
[certs] Generating "apiserver-kubelet-client" certificate and key
[certs] Generating "front-proxy-ca" certificate and key
[certs] Generating "front-proxy-client" certificate and key
[certs] Generating "etcd/ca" certificate and key
[certs] Generating "etcd/server" certificate and key
[certs] etcd/server serving cert is signed for DNS names [elsa localhost] and IPs [192.168.88.16 127.0.0.1 ::1]
[certs] Generating "etcd/peer" certificate and key
[certs] etcd/peer serving cert is signed for DNS names [elsa localhost] and IPs [192.168.88.16 127.0.0.1 ::1]
[certs] Generating "etcd/healthcheck-client" certificate and key
[certs] Generating "apiserver-etcd-client" certificate and key
[certs] Generating "sa" key and public key
[kubeconfig] Using kubeconfig folder "/etc/kubernetes"
[kubeconfig] Writing "admin.conf" kubeconfig file
[kubeconfig] Writing "kubelet.conf" kubeconfig file
[kubeconfig] Writing "controller-manager.conf" kubeconfig file
[kubeconfig] Writing "scheduler.conf" kubeconfig file
[control-plane] Using manifest folder "/etc/kubernetes/manifests"
[control-plane] Creating static Pod manifest for "kube-apiserver"
[control-plane] Creating static Pod manifest for "kube-controller-manager"
W0503 10:42:11.343155 32439 manifests.go:225] the default kube-apiserver authorization-mode is "Node,RBAC"; using "Node,RBAC"
[control-plane] Creating static Pod manifest for "kube-scheduler"
W0503 10:42:11.346581 32439 manifests.go:225] the default kube-apiserver authorization-mode is "Node,RBAC"; using "Node,RBAC"
[etcd] Creating static Pod manifest for local etcd in "/etc/kubernetes/manifests"
[wait-control-plane] Waiting for the kubelet to boot up the control plane as static Pods from directory "/etc/kubernetes/manifests". This can take up to 4m0s
[apiclient] All control plane components are healthy after 38.015489 seconds
[upload-config] Storing the configuration used in ConfigMap "kubeadm-config" in the "kube-system" Namespace
[kubelet] Creating a ConfigMap "kubelet-config-1.18" in namespace kube-system with the configuration for the kubelets in the cluster
[upload-certs] Skipping phase. Please see --upload-certs
[mark-control-plane] Marking the node elsa as control-plane by adding the label "node-role.kubernetes.io/master=''"
[mark-control-plane] Marking the node elsa as control-plane by adding the taints [node-role.kubernetes.io/master:NoSchedule]
[bootstrap-token] Using token: zuqc0t.xyq5i6lzbf9jarlb
[bootstrap-token] Configuring bootstrap tokens, cluster-info ConfigMap, RBAC Roles
[bootstrap-token] configured RBAC rules to allow Node Bootstrap tokens to get nodes
[bootstrap-token] configured RBAC rules to allow Node Bootstrap tokens to post CSRs in order for nodes to get long term certificate credentials
[bootstrap-token] configured RBAC rules to allow the csrapprover controller automatically approve CSRs from a Node Bootstrap Token
[bootstrap-token] configured RBAC rules to allow certificate rotation for all node client certificates in the cluster
[bootstrap-token] Creating the "cluster-info" ConfigMap in the "kube-public" namespace
[kubelet-finalize] Updating "/etc/kubernetes/kubelet.conf" to point to a rotatable kubelet client certificate and key
[kubelet-check] Initial timeout of 40s passed.
[addons] Applied essential addon: CoreDNS
[addons] Applied essential addon: kube-proxy
Your Kubernetes control-plane has initialized successfully!
To start using your cluster, you need to run the following as a regular user:
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
You should now deploy a pod network to the cluster.
Run "kubectl apply -f [podnetwork].yaml" with one of the options listed at:
https://kubernetes.io/docs/concepts/cluster-administration/addons/
Then you can join any number of worker nodes by running the following on each as root:
kubeadm join 192.168.88.16:6443 --token asdfgh.12qaz2wsx3edc4rf \
--discovery-token-ca-cert-hash sha256:1qaz2wsx3edc4rfv5tgb6yhn7ujm8ikk9oll0ppp1qaz2wsx3edc4rfv5t

Na koniec komendy inicjalizacyjnej dostajemy zestaw zaleceń do zrealizowania:

  1. Wykonaj na userze na którym będziesz standardowo pracować poniższe komendy. Zarządzanie kubernetesem z roota jest mało sexi.
$ mkdir -p $HOME/.kube
$ sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
$ sudo chown $(id -u):$(id -g) $HOME/.kube/config

2. Skopiuj sobie gdzieś na bok komendę zawierającą token i klucz. Nie wykonuj jej jeszcze na workerach! Chodzi o coś wyglądającego w ten sposób:

kubeadm join 192.168.88.16:6443 --token asdfgh.12qaz2wsx3edc4rf \
--discovery-token-ca-cert-hash sha256:1qaz2wsx3edc4rfv5tgb6yhn7ujm8ikk9oll0ppp1qaz2wsx3edc4rfv5t

Na naszej głównej maszynie przenosimy się na usera 'zarządzającego’. Czyli tego dla którego przygotowaliśmy punkt 1 powyżej.

3. Flannel

Czyli wirtualna sieć, która udostępnia odpowiednie podsieci każdemu z hostów klastra.

Instalacja:

$ kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/k8s-manifests/kube-flannel-rbac.yml
Log z wykonania (rozwiń)

$ kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml
podsecuritypolicy.policy/psp.flannel.unprivileged created
clusterrole.rbac.authorization.k8s.io/flannel configured
clusterrolebinding.rbac.authorization.k8s.io/flannel unchanged
serviceaccount/flannel created
configmap/kube-flannel-cfg created
daemonset.apps/kube-flannel-ds-amd64 created
daemonset.apps/kube-flannel-ds-arm64 created
daemonset.apps/kube-flannel-ds-arm created
daemonset.apps/kube-flannel-ds-ppc64le created
daemonset.apps/kube-flannel-ds-s390x created

4. Dołącz hosty do klastra

(opcjonalnie) Zezwól na swojej głównej maszynie na uruchamianie na niej PODów.
Standardowo, z powodów bezpieczeństwa, host na którym odpalamy kubeadm init, jest wyłączony z klastra. Służy tylko do jego zarządzania. W warunkach domowego laba, to często marnotrawstwo zasobów.
Uwaga! Jeżeli robisz ten tutorial na pojedynczej maszynie, to poniższa komenda nie jest opcjonalna.

kubectl taint nodes --all node-role.kubernetes.io/master-

Nie zanotowałeś sobie tokena?
Minęło ponad 24h od uruchomienia komendy kubeadm init? To nic strasznego. Skorzystaj z instrukcji we wpisie o zarządzaniu tokenami w kubernetesie.

Uruchom na wszystkich workerach komendę wymusząjącą dołączenie do klastra:

# kubeadm join --token <token> <control-plane-host>:<control-plane-port> --discovery-token-ca-cert-hash sha256:<hash>
Log z przykładowego uruchomienia (rozwiń)
kubeadm join 192.168.88.16:6443 --token 3fs9sd.z4di8tmrxdskitl4 --discovery-token-ca-cert-hash sha256:c4d361fb1bd7a4511ab398b3e99d950dfac3b1235ae981628c79ac0c5110c156
W0516 14:24:09.006377 21168 join.go:346] [preflight] WARNING: JoinControlPane.controlPlane settings will be ignored when control-plane flag is not set.
[preflight] Running pre-flight checks
[preflight] Reading configuration from the cluster…
[preflight] FYI: You can look at this config file with 'kubectl -n kube-system get cm kubeadm-config -oyaml'
[kubelet-start] Downloading configuration for the kubelet from the "kubelet-config-1.18" ConfigMap in the kube-system namespace
[kubelet-start] Writing kubelet configuration to file "/var/lib/kubelet/config.yaml"
[kubelet-start] Writing kubelet environment file with flags to file "/var/lib/kubelet/kubeadm-flags.env"
[kubelet-start] Starting the kubelet
[kubelet-start] Waiting for the kubelet to perform the TLS Bootstrap…
This node has joined the cluster:
Certificate signing request was sent to apiserver and a response was received.
The Kubelet was informed of the new secure connection details.
Run 'kubectl get nodes' on the control-plane to see this node join the cluster.

5. Weryfikacja

Zweryfikuj na głównej maszynie listę hostów w klastrze:

$ kubectl get nodes

Przykładowo, może wyglądać to tak:

$ kubectl get nodes
NAME     STATUS     ROLES    AGE     VERSION
berta    NotReady   <none>   4s      v1.18.2
elsa     Ready      master   13d     v1.18.2
ingrid   Ready      <none>   3m13s   v1.18.2

$ kubectl get nodes
NAME     STATUS   ROLES    AGE   VERSION
berta    Ready    <none>   12m   v1.18.2
elsa     Ready    master   13d   v1.18.2
ingrid   Ready    <none>   15m   v1.18.2

Spróbujmy uruchomić na naszym klastrze mały serwis testowy

kubectl run hypriot --image=hypriot/rpi-busybox-httpd --port=80

Działa?

$ kubectl get pods
HypriotOS/armv7: szydell@elsa in ~
$ kubectl get pods
NAME      READY   STATUS    RESTARTS   AGE
hypriot   1/1     Running   0          96s

Udostępnijmy teraz port 80 i sprawdźmy pod jakim ip, oraz na którym node kubernetes serwuje nasz serwis.

$ kubectl expose po hypriot --port 80
$ kubectl get endpoints hypriot
$ kubectl describe pods/hypriot | grep Node:

Przykładowo:

$ kubectl expose po hypriot --port 80
service/hypriot exposed
$ kubectl get endpoints hypriot
NAME ENDPOINTS AGE
hypriot 10.244.1.2:80 17s
$ kubectl describe pods/hypriot | grep Node:
Node: ingrid/192.168.88.15

Wchodzimy na hosta zwróconego przez ostatnią komendę. I jeżeli wszystko jest ok, powinniśmy móc lokalnie pobrać zawartość działającej strony:

$ curl 10.244.1.2:80
<html>
<head><title>Pi armed with Docker by Hypriot</title>
  <body style="width: 100%; background-color: black;">
    <div id="main" style="margin: 100px auto 0 auto; width: 800px;">
      <img src="pi_armed_with_docker.jpg" alt="pi armed with docker" style="width: 800px">
    </div>
  </body>
</html>

Jak widać wersja minimum działa. Wywalmy jeszcze dla sportu poda hypriot i sprawdzamy czy zniknął.

$ kubectl delete pod hypriot --now
$ kubectl get pods

Kubernetes w wersji minimum jest zainstalowany i działa. Teraz dopiero zaczną się schody. 😉