Docker – editia de primavara 2024

E februarie, dar afara e primavara din decembrie a lui 2023.

Versiunea anterioara aici.

Am ajuns la 3 dockere “reale”(in sensul ca nu prea pot sa ma lipsesc de ele) pe care am o gramada de chestii. Pe scurt:

Fata de anul trecut e de remarcat:

Legat de mailcow din ultimul post – am migrat full catre mailcow, usor de administrat, totul merge bine, foarte OK. Inca nu mi-a crapat la vreun update. Inca.

Apoi am renuntat la cateva chestii, mi se pareau poate inutile (mie) sau redundante sau le-am inlocuit cu altele:

  • observium – am renuntat la el
  • openweather – am renuntat la el, mai mult din principiu si pentru ca mi-a fost lene sa “repar” schimbarile pentru API v3
  • exporterele de mikrotik – mutat in telegraf + snmp
  • controllerul de unifi – a crapat saracia de AP si am luat un cAP in locul Unifi, vezi punctul de mai sus
  • yeelight – am renuntat, nu mai am becul ala, si acum automatizez din shortcuts in iOS
  • speedtest-tracker – schimbat “providerul”

Am adaugat cateva chestii:

  • changedetection – verifica daca o pagina web s-a modificat de la ultima accesare, aveam ceva de genul facut in bash, dar era mega-rudimentar
  • un fel de stack de *arr – radarr, sonarr, deluge, jellyfin, jacket, am inceput sa-l folosesc ca sa mai eliberez spatiu din Synology
  • nginx-proxy – mai usor de accesat chestiile interne prin proxy decat sa tii minte IP/port. Trantesti repede un RR in DNS, bagi o variabla de environment in docker si gata
  • mai am un uptime-kuma aruncat pe undeva pe un docker
  • yopass – il folosesc rar, dar e OK pentru OTP
  • plik – e wetransfer personal, fara limita de 2GB. Are multe optiuni, e mistocut.

Cam atat. Ne mai auzim prin toamna, adica in februarie 2025!

Ce rulez în docker – editia de primavara 2022

Versiunea de primavara a postului asta.

Pe scurt:

Pe lung:

  • nut-influxdb-exporter – exporter de metrici pentru NUT. Metricile se citesc dintr-un server de NUT si se trimit in InfluxDB, apoi din InfluxDB le vad in Grafana cu dashboard-ul asta.
  • grafana – Grafana. Metrici. Grafice. Chestii. O sa fac o gluma si o sa zic “lasati Grafana si invatati Cacti!” (cine stie cunoaste)
  • prometheus – time series database. Data source pentru Grafana. (vezi mai jos)
  • influxdb – time series database. Data source pentru Grafana. Colectez datele cu telegraf si le salvez in influx.
  • chronograf – dashboard pentru Influx dar poate fi folosit ca un client de Influx de unde poti gestiona datele. (eu pentru asta l-am instalat)
  • observium – Observium. Se conecteaza la observium_db si salveaza datele acolo. Grafice. Metrici. Niciodata nu sunt destule. Daca Grafana este utila si personalizabila dupa bunul plac, Observium e mai mult indreptat catre retelari. 
  • portainer_agent – agentul de Portainer.io, pentru ca lene si pentru ca imi place docker dar nu prea. Nu am chef sa stau sa invat si sa fac retete de docker cand pot sa dau 3 click-uri si sa am un container.
  • mikrotik-exporter – exporter de metrici pentru device-urile Mikrotik. Datele se scriu in Prometheus si le vad in Grafana cu dashboard-ul asta.
  • pihole-exporter – exporter de metrici pentru PiHole. Datele se scriu in Prometheus si le vad in Grafana cu dashboard-ul asta.
  • cerebro – web admin tool pentru Elasticsearch.
  • watchtower – autoupdater pentru imaginile de docker.

Se mai adaugă:

  • pihole – Pihole. Server de DNS cu adblocker. Îl rulam pe un VM dar mi-am dat seama ca e ușor de migrat în docker.
  • Nginx-proxy – proxy pentru unele containere ce trebuiesc accesate
  • piair_php și piair_web și piair_mysql – practic site-ul asta. Sursa e aicisa.
  • cupsd – cupid în docker. Îmi trebuie ca am un cron care declanșează o data la 5 zile un print color și alb negru sa nu se usuce cerneala în imprimanta. (Nu o folosesc zilnic)
  • unbound – Unbound – server DNS. Îl folosesc ca upstream pentru Pihole.
  • smokeping – un fel de observium, dar mai rudimentar și știe doar ping.
  • autoheal – dacă crapă ceva ce are health check activat, se restarteaza automat containerul.
  • yeelight – un cron care pornește un bec în funcție de apusul soarelui. Sursa aici.
  • unifi-controller – controllerul de Unifi. Îl rulam separat într-un VM, la fel ca Pihole, dar l-am mutat în docker.
  • unbound-exporter – statistici pentru unbound, sa le văd în Grafana
  • openweather-exporter – statistici pentru vreme, sa le văd în Grafana

Bonus:

Am instalat de ~o săptămână Mailcow în docker. Pare o soluție de mail destul de completa, e foarte interesanta și vine destul de OK configurată by default. Sunt câteva chestii care nu îmi convin, sunt unele care îmi plac, dar per total recomand.

Manual auto SSL pe Synology

Pentru ca sunt sărac și nu vreau sa dau o grămadă de bani pe un certificat wildcard pe un an, folosesc o struto-camila intre Cloudflare și Letsencrypt. Chiar dacă Cloudflare oferă certificat gratuit timp de un an (care se reinoieste gratuit o data la un an), nu poți să-l descarci de undeva ca să-l pui și pe alte servere, așa ca am rămas cu Letsencrypt pentru anumite servicii. Problema cu LE este ca certificatul trebuie schimbat la 3 luni, așa ca am încercat sa “automatizez” chestia asta cum am putut eu mai bine.

Disclaimer: Probabil exista și o alta metoda mult mai buna, dar pentru mine funcționează, deci I’m happy with it.

Ca sa înțelegeți putin infrastructura: 1 server web, 1 server de mail, 1 server de docker cu nginx care face reverse proxy și 1 server de “storage” pe un Synology. Singurul lucru comun între “servere”/locații e un certificat SSL.

Am început lejer, cu serverul de mail. Pe serverul de mail rulez un script care verifica atunci când certificatul a expirat și atunci când condiția se întâmplă, face un SSH pe serverul web și copiază certificatele noi, apoi da restart la servicii (postfix și dovecot). Bine, de fapt pe toate rulez același script, diferă doar ce se întâmplă în condiția logica atunci când certificatul expira. Trebuie sa menționez ca serverul web face reînnoire automata prin API-ul DNS Cloudflare cu ajutorul lui certbot. Asta e serverul principal, ca sa zic așa, de la el pornește tot, el deține adevarul.

Pe celălalt server de web/docker se întâmplă același lucru ca pe cel de mail, excepție fiind faptul ca dau restart la nginx și diferă calea unde e pus certificatul.

Iar acum vine durerea mare: NAS-ul Synology. A fost un fel de PITA sa mă prind pe unde isi arunca jegul certificatele, am căutat informații pe Google și am încercat tot felul de chestii pana m-am prins ce și cum. Se pare ca Syno nu folosește locația certificatului dintr-un loc predefinit, ci din mai multe locuri pentru diferite aplicații. Stupid, poate. Eu așa o văd. Te-ai gândi ca ai o locație de unde isi citește certificatele instalate în sistem, dar se pare ca nu, Syno e mai cu mot. Certificatul “principal” se afla în /usr/syno/etc/certificate/system. Certificatele pentru restul de aplicații sunt răspândite prin sistem. În fine, long story short, am ajuns la o varianta a scriptului așa:

#!/bin/bash

NOW=$(date +%s)
CERT_EXPIRY_DATE=$(echo | openssl s_client -servername subdomeniu.linux365.ro -connect subdom.linux365.ro:port 2>/dev/null | openssl x509 -noout -enddate | cut -d "=" -f 2)
CERT_EXPIRY_DATE_UNIX=$(date -d "$CERT_EXPIRY_DATE" +%s)

if [[ $NOW -gt $CERT_EXPIRY_DATE_UNIX ]]; then
    rsync -a --copy-links user@IP:/etc/letsencrypt/live/linux365.ro/ /tmp/certs/ && echo -e "Subject: Syno certs\n\nL365 syno certs sync'ed" | ssmtp [email protected]
	sudo cp -r /tmp/certs/* /usr/syno/etc/certificate/_archive/kVlb4z/
	sudo cp -r /tmp/certs/* /usr/local/etc/certificate/LogCenter/pkg-LogCenter/
	sudo cp -r /tmp/certs/* /usr/local/etc/certificate/ScsiTarget/pkg-scsi-plugin-server/
	sudo cp -r /tmp/certs/* /usr/local/etc/certificate/SynologyDrive/SynologyDrive/
	sudo chown -R root:root /usr/syno/etc/certificate/_archive/kVlb4z/
	sudo chown -R root:root /usr/local/etc/certificate/LogCenter/pkg-LogCenter/
	sudo chown -R root:root /usr/local/etc/certificate/ScsiTarget/pkg-scsi-plugin-server/
	sudo chown -R root:root /usr/local/etc/certificate/SynologyDrive/SynologyDrive/
	sudo chmod u=r -R /usr/syno/etc/certificate/_archive/kVlb4z/*.pem
	sudo chmod u=r -R /usr/local/etc/certificate/LogCenter/pkg-LogCenter/*.pem
	sudo chmod u=r -R /usr/local/etc/certificate/ScsiTarget/pkg-scsi-plugin-server/*.pem
	sudo chmod u=r -R /usr/local/etc/certificate/SynologyDrive/SynologyDrive/*.pem
	rm -rf /tmp/certs/
	sudo reboot
fi

Partea buna la Syno e ca folosește certificatele exact cu numele de la LE, deci nu mai trebuie redenumite. Partea proasta e ca trebuie împrăștiate prin sistem. În principiu /usr/syno/etc/certificate/_archive este calea comuna pentru toate Syno, apoi avem un folder cu un ID generat unde sunt puse efectiv certificatele. Partea de web (nginx) citește din calea aia cu ID certificatul SSL. Restul serviciilor, nu chiar, din câte vedeți. Aș fi putut sa fac symlink către certificatele din folderul _archive, dar mă gândesc ca poate o sa fie rescrise de către DSM la un moment dat, plus ca nu mă deranjează sa copiez certificatele “automat” în restul de foldere.

La final am ales sa dau reboot din mai multe motive. Nu știu toate serviciile ce necesita SSL și care trebuie sa fie restartate după schimbarea certificatului, plus ca sigur o sa rămână vreunul cu certificatul vechi. Dacă vrea cineva să-și bată capul, comanda de restart este synosystemctl restart pkgctl-Synology<MUMU>.service pe DSM 7, sau ceva de genul.

O alta chestie mai cu mot la Syno este faptul ca nu folosește comanda mail cam ca toate sistemele Linux, ci ssmtp. Mă rog, nu e un deal-braker, dar nah, a trebuit sa caut care era problema lui când nu găseam comanda mail, mai ales ca nici în Entware nu exista pachetul.

Dacă interesează pe cineva restul de scripturi o sa le postez, dar probabil no one cares.

Ah, am uitat partea cea mai importanta: cum funcționează de fapt scriptul.

  1. Se setează niște variabile care verifica timpul în Unix Time (timpul actual și cel de expirare al certificatului)
  2. Dacă timpul de acum e mai mare decât timpul de expirare al certificatului, atunci “fa chestii”
  3. ???
  4. Profit.

Încă o chestie, scriptul trebuie pus în cron, sa ruleze din minut în minut.

Q&A:
Se putea face altfel scriptul sau se putea scrie mai frumos? Probabil ca da.
Merge? Merge.
Are sens să-mi bat capul să-l modific? Nope. Dacă merge, atunci care e problema?

Referințe:

1. https://gist.github.com/catchdave/69854624a21ac75194706ec20ca61327
2. https://markvanlent.dev/2020/10/02/replacing-the-tls-certificate-on-a-synology-nas-via-the-command-line/
3. https://dokuwiki.bitaranto.ch/doku.php?id=synologyimportcertfrompfsense

TelegramBot + Instagram

Scriam in postul anterior ca am facut un mic script inutil, for fun, pentru ca se poate. Scriptul ma anunta cati followeri am pe Instagram si cati am in plus sau in minus fata de ultima data cand a fost rulat scriptul.

Pe scurt: Instagram iti pune la dispozitie un API, dar nu prea. API-ul e pentru ceva aplicatii facute prin Facebook, trebuie sa te faci Facebook developer, etc, etc, n-am prea inteles bine si nici nu mi-am batut capul. Am gasit in schimb o solutie prin care Instagram iti varsa un json pe care poti sa-l parsezi: curl 'https://www.instagram.com/{ig_username}/?__a=1'

Abuse it wisely. Bineinteles, trebuie setat un cookie in request-ul cURL si eu am lasat si UserAgent-ul. Nu cred ca e nevoie de UA, dar nu costa nimic sa fie acolo.

Json-ul respectiv poate fi parsat prin jq si numarul de followeri salvat intr-o variabila. Ceva de genul: followers=$(curl {URL} | jq .graphql.user.edge_followed_by.count).

Ce faceti pe urma cu informatia e treaba voastra, eu am ales sa compar ziua de ieri cu ziua de azi si sa trimit informatia catre Telegram. Logica din spatele if-urilor probabil nu e cea mai buna, but it works.

#!/bin/bash

follow_db=$(cat /tmp/followdb)
TOKEN=aivreatu
CHID=nope
CURL=$(curl 'https://www.instagram.com/{username}/?__a=1' -X 'GET' -H 'Cookie: haha' -H 'User-Agent: ceva' -s | jq .graphql.user.edge_followed_by.count)

if [[ $CURL == '' ]]; then
    curl -s "https://api.telegram.org/$TOKEN/sendMessage?chat_id=$CHID&text=Eroare in cURL Instagram"
    exit 1
fi

cur_followers=$CURL
yest_followers=$follow_db
amount=$(echo $(($cur_followers-$yest_followers)))


if [[ $yest_followers < $cur_followers ]]; then
    curl -s "https://api.telegram.org/$TOKEN/sendMessage?chat_id=$CHID&text=Instagram report ?:%0AYou have gained $amount new followers, you now have a total of $cur_followers"
elif [[ $yest_followers > $cur_followers ]]; then
    curl -s "https://api.telegram.org/$TOKEN/sendMessage?chat_id=$CHID&text=Instagram report ?:%0AYou have lost $amount followers, you now have a total of $cur_followers"
elif [[ $yest_followers == $cur_followers ]]; then
    curl -s "https://api.telegram.org/$TOKEN/sendMessage?chat_id=$CHID&text=Instagram report ?:%0ANo followers change, you still have $cur_followers"
fi

echo $cur_followers > /tmp/followdb

exit 0

Probabil scriptul putea fi scris mai frumos, probabil nu. Probabil putea fi scris mai eficient, probabil nu. Probabil putea sa fie un oneliner, probabil… nu. La mine atat s-a putut. If it looks stupid but it works, then it’s not stupid. Pentru cei care au citit “codul” si au vazut ceva dubios acolo, puteti sa-mi ziceti in comentarii.
PS: %0A inseamna newline(enter) pentru TelegramBot, am aflat asta cand am facut scriptul.

Ne vedem in postul urmator.