Un aggiornamento di Windows danneggia GRUB


Nel mio ultimo computer ho voluto tenere un dual boot con GRUB essendo obbligato in alcune situazioni ad utilizzare una macchina Windows genuina per connettermi ad alcune VPN. Stamattina, eseguendo gli aggiornamenti del sistema operativo Windows 11, qualcosa è andato storto ed è stato danneggiato il GRUB che mi permette all’avvio della macchina di avviare Ubuntu 22.04 oppure Windows11

Unified Extensible Firmware Interface
Unified Extensible Firmware Interface

Cos’è GRUB

GRUB (Grand Unified Bootloader) è un bootloader, ovvero un programma che viene eseguito all’avvio di un computer per caricare il sistema operativo. È particolarmente comune nei sistemi basati su Linux, ma può essere utilizzato anche per avviare altri sistemi operativi come Windows.

Funzioni principali di GRUB:

  1. Selezione del sistema operativo:
    • GRUB permette di scegliere quale sistema operativo avviare se ne hai installato più di uno sullo stesso computer (dual-boot). Ad esempio, se hai sia Ubuntu che Windows installati, GRUB ti presenterà un menu all’avvio dove puoi scegliere quale sistema operativo avviare.
  2. Caricamento del kernel:
    • GRUB carica il kernel del sistema operativo scelto in memoria e lo avvia. Il kernel è il cuore del sistema operativo e gestisce tutte le interazioni tra l’hardware e il software.
  3. Configurabilità:
    • GRUB è altamente configurabile. Puoi modificarne le impostazioni per cambiare l’aspetto del menu, aggiungere o rimuovere opzioni di avvio, impostare quale sistema operativo deve essere avviato di default, e molto altro.
  4. Compatibilità con diversi file system:
    • GRUB supporta una vasta gamma di file system, il che significa che può leggere i file necessari per avviare un sistema operativo indipendentemente dal file system utilizzato.
  5. Modalità di recupero:
    • Se un sistema operativo non riesce ad avviarsi correttamente, GRUB può fornire opzioni per avviare il sistema in modalità di recupero (recovery mode) o per passare parametri speciali al kernel per aiutare a risolvere problemi.

GRUB e UEFI

  • UEFI e BIOS: GRUB funziona sia con sistemi UEFI (che è il firmware moderno presente nei computer più recenti) sia con sistemi BIOS (che è il firmware più vecchio).
  • Secure Boot: In sistemi UEFI con Secure Boot abilitato, GRUB deve essere firmato digitalmente per poter essere avviato. Questo garantisce che solo software approvato possa essere eseguito all’avvio, migliorando la sicurezza.

Versioni di GRUB

  • GRUB Legacy: La versione più vecchia di GRUB, che è meno usata oggi.
  • GRUB 2: La versione più recente e quella più comunemente utilizzata oggi. GRUB 2 offre molte più funzionalità rispetto a GRUB Legacy, incluso un sistema di configurazione più potente e un supporto esteso per diversi tipi di file system e dispositivi di avvio.

Tentativo di soluzione manuale

La prima operazione da fare è creare una chiavetta USB di avvio contenente la stessa versione di Ubuntu che ho a bordo del PC, la 22.04 Jammy Jellyfish.

Creazione di un USB disk di avvio

Per fare questo occorre scaricare dal sito di Ubuntu l’immagine ISO di questa versione; una volta scaricata l’ISO utilizzare l’utility di creazione dischi:

Crea disco di avvio
Crea disco di avvio

Selezionare quindi l’immagine scaricata e avviare la creazione del disco

Installazione dell'USB di avvio
Installazione dell’USB di avvio

Alla fine verrà mostrato l’avviso di creazione eseguita

Creazione USB disk completata
Creazione USB disk completata

Come è stato partizionato il disco?

Occorre innanzitutto chiarire come è stato partizionato il disco quando è stato installato Ubuntu.

Per fare questo esistono due comandi di ispezione che sono lsblk e fdisk.

Questo è l’output di lsblk per il mio disco

$ sudo lsblk
NAME        MAJ:MIN RM   SIZE RO TYPE MOUNTPOINTS
loop0         7:0    0     4K  1 loop /snap/bare/5
loop1         7:1    0  10,1M  1 loop /snap/canonical-livepatch/278
...
loop54        7:54   0 447,3M  1 loop /snap/telegram-desktop/6117
nvme0n1     259:0    0 953,9G  0 disk 
├─nvme0n1p1 259:1    0   260M  0 part /boot/efi
├─nvme0n1p2 259:2    0    16M  0 part 
├─nvme0n1p3 259:3    0 464,3G  0 part 
├─nvme0n1p4 259:4    0     1G  0 part 
└─nvme0n1p5 259:5    0 488,3G  0 part /var/snap/firefox/common/host-hunspell

E questo è l’output per fdisk

$ sudo fdisk -l
[sudo] password di marco: 
Disk /dev/loop0: 4 KiB, 4096 bytes, 8 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
...
Disk /dev/nvme0n1: 953,87 GiB, 1024209543168 bytes, 2000409264 sectors
Disk model: WD PC SN740 SDDQNQD-1T00-1014           
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: gpt
Disk identifier: 8C2BBF42-DC93-4D6C-A305-9C00B93C5816
Dispositivo         Start       Fine    Settori   Size Tipo
/dev/nvme0n1p1       2048     534527     532480   260M EFI System
/dev/nvme0n1p2     534528     567295      32768    16M Microsoft reserved
/dev/nvme0n1p3     567296  974311423  973744128 464,3G Microsoft basic data
/dev/nvme0n1p4 1998311424 2000408575    2097152     1G Windows recovery environment
/dev/nvme0n1p5  974311424 1998311423 1024000000 488,3G Linux filesystem
Partition table entries are not in disk order.

Interpretazione di lsblk e fdisk -l

  1. Dispositivi loop:
    • I dispositivi loop che vengono mostrati (loop0loop1, ecc.) sono immagini montate temporaneamente dal sistema live che si sta utilizzando. Non hanno a che fare con il mio disco fisso, quindi li possiamo ignorare.
  2. Disco sda:
    • Questo sembra essere il supporto USB o il CD/DVD da cui ho avviato il sistema live. Anche questo possiamo ignorarlo per ora.
  3. Disco nvme0n1:
    • Questo è il mio SSD principale da 953,87 GiB (1 Terabayte), suddiviso in diverse partizioni:
PartizioneTipoDimensione
/dev/nvme0n1p1EFI System (Partizione EFI)260M
/dev/nvme0n1p2Microsoft Reserved16M
/dev/nvme0n1p3Microsoft basic data (Windows)464,3G
/dev/nvme0n1p4Windows recovery environment1G
/dev/nvme0n1p5Linux filesystem (Ubuntu)488,3G

Quindi le partizioni più importanti sono

  • /dev/nvme0n1p1 che è quello dall’UEFI (l’ex BIOS)
  • /dev/nvme0n1p3 la partizione Windows
  • /dev/nvme0n1p5 la partizione Linux

Procedura per ripristinare GRUB

Ora che abbiamo identificato le partizioni, si può procedere con il ripristino di GRUB montando manualmente dal sistema di avvio USB la partizione di Ubuntu e reinstallando GRUB.

1. Montare la partizione di Ubuntu

  • Monto la partizione di Ubuntu, che nel mio caso è /dev/nvme0n1p5, con il seguente comando:

sudo mount /dev/nvme0n1p5 /mnt

2. Montare le directory necessarie

  • Dopo aver montato la partizione di Ubuntu, devo montare anche le altre directory necessarie:

sudo mount –bind /dev /mnt/dev

sudo mount –bind /proc /mnt/proc

sudo mount –bind /sys /mnt/sys

sudo mount /dev/nvme0n1p1 /mnt/boot/efi

Il comando per montare /dev/nvme0n1p1 nella directory /mnt/boot/efi è necessario perché questa partizione contiene i file di avvio EFI.

3. Accedere alla mia installazione con chroot

  • Ora, entro nel sistema Ubuntu installato utilizzando chroot (in questo momento sono nella chiavetta USB!):
sudo chroot /mnt

4. Reinstallare GRUB

  • Reinstallo GRUB sul SSD principale (che è /dev/nvme0n1):
grub-install /dev/nvme0n1
  • Aggiorno la configurazione di GRUB:
update-grub

5. Uscire e riavviare

  • Uscire dall’ambiente chroot:
exit
  • Smonto tutte le partizioni montate:
sudo umount /mnt/dev
sudo umount /mnt/proc
sudo umount /mnt/sys
sudo umount /mnt/boot/efi
sudo umount /mnt
  • Riavvio il sistema:
sudo reboot

Dopo il riavvio, dovrei vedere il menu di GRUB e poter scegliere tra Ubuntu e Windows.

Ma non funziona

Però quanto fatto non funziona: si avvia sempre automaticamente la partizione Windows e non vedo più Linux.

In realtà la partizione c’è: avviando UEFI (premendo F2 all’avvio) vedo correttamente le due partizioni e modificando l’ordine a mano parte sia Windows che Linux ma non parte più il GRUB.

Tra le varie opzioni trovate da ChatGPT 4, preferisco andare direttamente all’opzione che mi consiglia di installare boot-repair.

Ripristino del bootloader con Boot-Repair

Avvio nuovamente il sistema con un live CD/USB di Ubuntu.

Quindi installo Boot-Repair:

sudo add-apt-repository ppa:yannubuntu/boot-repair
sudo apt update
sudo apt install -y boot-repair

Avvio Boot-Repair:

boot-repair

Un estratto della procedura di riparazione dell’UEFI è questa (mi viene generato un pastebin che verrà conservato per un mese. Me lo trascrivo in locale):

============================= Boot Repair Summary ==============================
modprobe: FATAL: Module efivars not found in directory /lib/modules/6.5.0-18-generic
Recommended repair: ____________________________________________________________
The default repair of the Boot-Repair utility will reinstall the grub-efi-amd64-signed of
nvme0n1p5,
using the following options:  nvme0n1p1/boot/efi
Additional repair will be performed:  unhide-bootmenu-10s use-standard-efi-file
Mount /dev/nvme0n1p1 on /mnt/boot-sav/nvme0n1p5/boot/efi
Unhide GRUB boot menu in nvme0n1p5/etc/default/grub
============ Reinstall the grub-efi-amd64-signed of /dev/nvme0n1p5 =============

In sostanza credo che l’aggiornamento di Windows abbia in qualche modo causato una corruzione del file efi nella partizione destinata a EFI.

Tuttavia questa operazione fatta da boot-repair risolve completamente il problema e il dual boot ricomincia a funzionare, lo provo per un po’ di volte per essere sicuro.

Laravel: Impossibilità di cancellare la cache

Laravel logo
Laravel logo

Ho risolto un problema che mi ha disturbato per un po’ di tempo: volendo pulire la cache di Laravel con il comando

$ php artisan cache:clear

ne risultava immancabilmente un errore

   ERROR  Failed to clear cache. Make sure you have the appropriate permissions.

Apparentemente tutte le assegnazioni sulla proprietà e sull’operatività (R/W) sulle varie cartelle erano corrette. Anche le interazioni con ChatGpt non davano i suggerimenti sperati.

Ma una cosa mancava. La faccio breve, mancava questa directory che ho quindi creato a mano con il mio user marcob:

$ cd <APP-ROOT>
$ mkdir storage/framework/cache/data
$ php artisan cache:clear
INFO Application cache cleared successfully.

Grazie al buon vecchio Stackoverflow.

That’s all folks!!!

Riferimenti

Madhava: la serie che produce Pi greco

Un piccolo esercizio che studia la serie di Madhava, o Madhavan, trovato nel capitolo sui cicli del bel libro di Christian Hill, Learning Scientific Programming with Python, second edition, 2020, Cambridge University Press, ha risvegliato la mia passione per la matematica.

Mi diverto ancora a ristudiare i fondamentali della programmazione, un po’ come un buon pianista che non smette di esercitarsi con l’Hanon.

È un algoritmo eccezionale che converge con velocità esponenziale per trovare le cifre di \pi.

L’algoritmo di Madhava, noto anche come serie di Madhava-Leibniz (anche se quella di Leibniz è posteriore e peggiorativa rispetto a questa, per cui mi pare più corretto chiamarla di Madhava e basta), è uno dei metodi storici per calcolare il valore di π.

Prende il nome dal matematico indiano Madhava di Sangamagrama, che visse tra il XIV e il XV secolo, – un periodo intermedio tra i nostri Leonardo Pisano “Fibonacci” e Scipione del Ferro – ed è considerato uno dei precursori del calcolo infinitesimale.

Nell’articolo che segue, c’è la descrizione dell’algortimo, la dimostrazione della convergenza, l’analisi della velocità convergenza e l’implementazione in Python con Matplotlib.

Articolo sulla serie di Madhava

Collegamento

Pillole Oracle: creazione user

Ho incontrato questo problema tentando di creare un nuovo utente. La versione di Oracle che sto usando è la 21c.

CREATE USER scott IDENTIFIED BY tiger135
DEFAULT TABLESPACE users
TEMPORARY TABLESPACE temp;

Risultato

Report error -
ORA-65096: nome utente o ruolo comune non valido
65096. 00000 - "invalid common user or role name"

Ho verificato innanzitutto che lo user “scott” esistesse già e non esisteva.

Il problema era un altro. La sessione non aveva caricato il corretto pluggable database che stavo utilizzando in precedenza:

show con_name;

CON_NAME 
------------------------------
CDB$ROOT

Ho quindi commutato la sessione sul database che avevo creato:

alter session set container=XEPDB1;

Session modificato.

Rilanciando l’istruzione di creazione utente stavolta ha funzionato:

Creato user SCOTT.

Configurazione SSL per Apache

Piccolo tutorial su come configurare un accesso cifrato con SSL per un sito web. Sia myserver il nome sia dell’host che del dominio: ovviamente in questo tutorial non utilizzeremo il DNS, stiamo solo facendo esperimenti in rete locale per cui basta specificare il nome dell’host.

csfr hacking technique ssl
SSL

Ingredienti

  • una chiave privata
  • una richiesta di certificato
  • un certificato SSL
  • un virtualhost Apache
  • molta pazienza

Chiave SSL

Utilizzando OpenSSL generiamo una chiave privata RSA. Dalla directory in cui si desidera salvare la chiave privata (tipicamente /etc/ssl/private/) si esegue il seguente comando:

$ sudo openssl genrsa -out myserver.key 2048

Richiesta di firma

Utilizzando la chiave privata appena generata, creiamo una richiesta di firma del certificato (CSR).

Importante: Assicuriamoci di inserire correttamente il nome del dominio locale quando richiesto.

Muoviamoci adesso in un directory standard che contiene tutti i certificati SSL: /etc/ssl/certs/

Il comando è il seguente:

$ sudo openssl req -new -key /etc/ssl/private/myserver.key -out myserver.csr

Generazione del certificato

Desiderando creare un certificato autofirmato per soli scopi di sviluppo locale o di sollazzo, si può auto-firmare il CSR utilizzando il comando:

$ sudo openssl x509 -req -days 365 -in myserver.csr -signkey /etc/ssl/private/myserver.key -out myserver.crt

Configurazione del virtualhost Apache

Configuriamo ora Apache per utilizzare il certificato SSL per il sito “myserver”.

Apriamo il file di configurazione di Apache (apache2.conf, lavorando con una distribuzine Ubuntu) , o un file specifico per il sito: io per esempio ho un file in cui configuro tutti i virtualhost per le mie applicazioni Laravel che si chiama laravel.conf) e aggiungiamo il seguente blocco di configurazione:

<VirtualHost *:443>
    ServerName myserver
    DocumentRoot /var/www/myserver

    SSLEngine on
    SSLCertificateFile /etc/ssl/certs/js.crt
    SSLCertificateKeyFile /etc/ssl/private/js.key

    # Opzionale: Specifica il file di catena dei certificati (CA bundle) se necessario
    # SSLCertificateChainFile /percorso/verso/ca-bundle.crt

    # Altri settaggi del virtual host...
    ErrorLog ${APACHE_LOG_DIR}/error_ssl.log
    CustomLog ${APACHE_LOG_DIR}/access_ssl.log combined
</VirtualHost>

Abbiamo configurato anche i file di log che conterranno accessi ed errori al virtual host https://myserver.

Non abbiamo invece specificato alcuna catena di autorità (CA bundle) proprio perché è un certificato autofirmato che non fa riferimento ad alcuna Autorità di certificazione standard.

Non resta che (ri)avviare Apache:

$ sudo apache2ctl restart
$ sudo systemctl status apache2
● apache2.service - The Apache HTTP Server
     Loaded: loaded (/lib/systemd/system/apache2.service; enabled; vendor preset: enabled)
     Active: active (running) since Mon 2024-04-15 10:34:26 CEST; 29min ago
       Docs: https://httpd.apache.org/docs/2.4/
    Process: 45590 ExecStart=/usr/sbin/apachectl start (code=exited, status=0/SUCCESS)
   Main PID: 45593 (/usr/sbin/apach)
      Tasks: 9 (limit: 9309)
     Memory: 48.0M
        CPU: 511ms
     CGroup: /system.slice/apache2.service
             ├─45593 /usr/sbin/apache2 -k start
             ├─45594 /usr/sbin/apache2 -k start
             ├─45595 /usr/sbin/apache2 -k start
             ├─45596 /usr/sbin/apache2 -k start
             ├─45597 /usr/sbin/apache2 -k start
             ├─45598 /usr/sbin/apache2 -k start
             ├─45599 /usr/sbin/apache2 -k start
             ├─45600 /usr/sbin/apache2 -k start
             └─45996 /usr/sbin/apache2 -k start

apr 15 10:34:26 jsbach systemd[1]: Starting The Apache HTTP Server...
apr 15 10:34:26 jsbach systemd[1]: Started The Apache HTTP Server.

Conflitto con altri Virtualhost

Se una stessa porta è utilizzata da altri virtualhost il sistema non funzionerà. Capire che è questo il problema può comportare un po’ di tempo.

I sintomi per questo tipo di errata configurazione sono parecchi:

  • Il browser lamenta genericamente un ERR_SSL_PROTOCOL_ERROR
  • Client e server abilitati antrambi a versioni di TLS compatibili
  • Porta crittografica non correttemente impostatata
  • analizzando il traffico anche con Wireshark si nota che non si ha nessun Server Hello in conseguenza al Client Hello e la risposta del server contiene un errore 400 Bad Request

Un controllo fatto con openssl da qualche informazione:

40F7C74FFA7F0000:error:0A00010B:SSL routines:ssl3_get_record:wrong version number:../ssl/record/ssl3_record.c:354:
---
no peer certificate available
---
No client certificate CA names sent
---
SSL handshake has read 5 bytes and written 316 bytes
Verification: OK
---
New, (NONE), Cipher is (NONE)
Secure Renegotiation IS NOT supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
Early data was not sent
Verify return code: 0 (ok)
---

Per sistemare queste cose ho impostato:

  • Nella configurazione del Virtualhost bisogna scegliere porte diverse per Virtualhost diversi: replicare una stessa porta per virtualhost diversi porta a errori.
  • Scegliere porte che non siano già utilizzate da altri software oltre Apache (ad esempio nel mio muletto Netxgen si è già occupato la porta 8443)
  • Nella configurazione generale di Apache vanno aggiunte tutte le porte che si vuole ascolti; per fare questo editare il file ports.conf:
<IfModule ssl_module>
        Listen 443
        Listen 9443 <--- ho aggiunto questa
</IfModule>
  • Nel file di configurazione del virtualhost scegliere la porta scelta per ports.conf
<VirtualHost *:9443>
  • In questo stesso blocco abilitare le versioni di TLS supporate da Apache che verranno comunicate al clinet:
        SSLEngine on
        SSLCertificateFile /etc/ssl/certs/myserver.crt
        SSLCertificateKeyFile /etc/ssl/private/myserver.key
        SSLProtocol -all +TLSv1.2 +TLSv1.3 <------ aggiunto

Chiaramente i percorsi dei certificati devono essere corretti e con i permessi corretti:

$ sudo ls -l /etc/ssl/certs/myserver.crt
-rw-r--r-- 1 root root 1334 mag 14 19:00 /etc/ssl/certs/myserver.crt
$ sudo ls -l /etc/ssl/private/myserver.key
-rw------- 1 www-data root 1704 mag 14 18:59 /etc/ssl/private/myserver.key

Nota l’abilitazione a RW della chiave privata al solo utente www-data.

Si può fare un controllo del funzionamento con il client s_client di openssl che stavolta da una risposta molto più prolissa di prima:

$ openssl s_client -connect mytech.local:9443
CONNECTED(00000003)
depth=0 C = IT, ST = ITALY, L = VENICE, O = MYTECH, OU = R&D, CN = mytech.local
verify error:num=18:self-signed certificate
verify return:1
depth=0 C = IT, ST = ITALY, L = VENICE, O = MYTECH, OU = R&D, CN = mytech.local
verify return:1
---
Certificate chain
0 s:C = IT, ST = ITALY, L = VENICE, O = MYTECH, OU = R&D, CN = mytech.local
i:C = IT, ST = ITALY, L = VENICE, O = MYTECH, OU = R&D, CN = mytech.local
a:PKEY: rsaEncryption, 2048 (bit); sigalg: RSA-SHA256
v:NotBefore: May 14 17:00:17 2024 GMT; NotAfter: May 14 17:00:17 2025 GMT
---
Server certificate
-----BEGIN CERTIFICATE-----
MIIDqzCCApOgAwIBAgIUPutXcLp1H8o4J5qtNEWJSB7pwoEwDQYJKoZIhvcNAQEL
BQAwZTELMAkGA1UEBhMCSVQxDjAMBgNVBAgMBUlUQUxZMQ8wDQYDVQQHDAZWRU5J
Q0UxDjAMBgNVBAoMBUNBTU9OMQwwCgYDVQQLDANSJkQxFzAVBgNVBAMMDnlhY2F0
ZWNoLmxvY2FsMB4XDTI0MDUxNDE3MDAxN1oXDTI1MDUxNDE3MDAxN1owZTELMAkG
A1UEBhMCSVQxDjAMBgNVBAgMBUlUQUxZMQ8wDQYDVQQHDAZWRU5JQ0UxDjAMBgNV
BAoMBUNBTU9OMQwwCgYDVQQLDANSJkQxFzAVBgNVBAMMDnlhY2F0ZWNoLmxvY2Fs
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArzga2ElQ5Se/E1OKDODE
t2bLF707kjFqYTc5m4LXu1zPmXF/VZTbE80QN9/oB5OVOlYeRlE/hsjGuOTIYaI4
IWa4FvWgb1QGw5aHn4rKZ7Nm/QgG2yRagNoGQIgDE6S/G+WOrhNr9m3dgme0C+7j
uB2sYsBkCGgeqpEiTEKMSMTspElNeE8emCcTzKgnTQJkA6jpp65SKkuw6HBUC4x+
6x0prAzlUJeMLvuKlsT8MtLpj35kNWxsHxfwzhKKv0H7Wj8+kGTc8QYepFnHbpmc
qlRPNqcrXMLiYMUgkNr/aeXUIeDqpJSa1q2vbnXO1d+ToIYgt8uTpXz/yDqiVlfC
6wIDAQABo1MwUTAdBgNVHQ4EFgQUFIRqn2kn2o0KPCoM9qgITYHijiowHwYDVR0j
BBgwFoAUFIRqn2kn2o0KPCoM9qgITYHijiowDwYDVR0TAQH/BAUwAwEB/zANBgkq
hkiG9w0BAQsFAAOCAQEAJFIP8OnTFTHpFGdD6KKEmQbvidiNhDmimSqXCde6nK3B
q1R9wGdWu+zP+0ER7YmozTYJZmvUeYApArJM3Y42SLNj9RBP1RPeYi25MsUjI4JY
fKuhtwUvwy8H2G2zg6h0ObwsBoELMt2s3pP6aX9aVvpg8IIKADJO+ux182zYiK/F
1IEKW5u7YEYIS31VQ/e82QQAe2IK+EmWpnLHteVaOSpDRWd6xxkUtXwXQdSOi66q
oq6fA6t1xobngdVi7RHMLoDyzs6iuMK/naN6B/QbflRP5GNz2xjwOYuTUh7Uv3lZ
DHeV3glkgILZIYBnOsKX5SWb8mXbkId3GONkN3toUQ==
-----END CERTIFICATE-----
subject=C = IT, ST = ITALY, L = VENICE, O = MYTECH, OU = R&D, CN = mytech.local
issuer=C = IT, ST = ITALY, L = VENICE, O = MYTECH, OU = R&D, CN = mytech.local
---
No client certificate CA names sent
Peer signing digest: SHA256
Peer signature type: RSA-PSS
Server Temp Key: X25519, 253 bits
---
SSL handshake has read 1503 bytes and written 396 bytes
Verification error: self-signed certificate
---
New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384
Server public key is 2048 bit
Secure Renegotiation IS NOT supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
Early data was not sent
Verify return code: 18 (self-signed certificate)
---
---
Post-Handshake New Session Ticket arrived:
SSL-Session:
Protocol : TLSv1.3
Cipher : TLS_AES_256_GCM_SHA384
Session-ID: E66BC218BE742F264887D733676F053EA14CF057764032A6FCBD3ED3530738C5
Session-ID-ctx:
Resumption PSK: B42299493ED95287FF6DD7A28AE53EDF626F051A02F8A0262E51965356C2AFAA8C256000C8C965F61FB6BC57EA338917
PSK identity: None
PSK identity hint: None
SRP username: None
TLS session ticket lifetime hint: 300 (seconds)
TLS session ticket:
0000 - 4c 7a 89 1a d0 c9 e9 e9-b0 95 db de 19 12 a5 87 Lz..............
0010 - 1c 2e 32 d3 bf f9 39 c1-5c 3e 35 3f 78 b2 13 6e ..2...9.\>5?x..n
0020 - c1 cb 36 df 46 9b 64 fe-d0 f9 91 e9 72 3b dd 50 ..6.F.d.....r;.P
0030 - 4e 05 ab e5 6d e1 15 cc-5a f2 e1 09 c4 17 28 c8 N...m...Z.....(.
0040 - 2d d3 ba 98 1f 40 2a 19-46 9e 01 41 70 66 b3 f1 -....@*.F..Apf..
0050 - 65 38 8e 0d d0 5b 99 83-c5 d5 64 8f 48 8e ed 0f e8...[....d.H...
0060 - aa e9 7b 42 8e 97 06 69-52 ea d9 eb a5 94 f6 92 ..{B...iR.......
0070 - 75 73 e0 60 ba f9 29 c9-2d 72 e2 d9 b7 3d d8 65 us.`..).-r...=.e
0080 - 6b 4e 55 a3 2f 62 aa 3d-42 14 0c 36 bd 46 f0 1e kNU./b.=B..6.F..
0090 - 75 14 e7 2b fd ab 9f d0-d3 af 09 42 5b c2 e9 88 u..+.......B[...
00a0 - ca 5f dd 94 f6 20 c0 03-64 81 95 d0 04 a4 16 a9 ._... ..d.......
00b0 - cb 5e d0 f9 10 26 26 e7-4a bc 1b 6a 5a 1e 81 a4 .^...&&.J..jZ...
00c0 - bd 46 52 a8 79 38 17 d9-64 e6 5a fb cc b1 14 3d .FR.y8..d.Z....=
00d0 - e3 76 01 70 27 29 46 d2-bf e3 ca 8c 3f a9 bc 83 .v.p')F.....?...
00e0 - b2 9d ff 0a ff 68 89 43-1f 19 e7 27 9b 7c dd ff .....h.C...'.|..
00f0 - dd 64 ad 37 e7 0a 00 f3-f3 d3 33 e9 88 80 72 a1 .d.7......3...r.

Start Time: 1716285423
Timeout : 7200 (sec)
Verify return code: 18 (self-signed certificate)
Extended master secret: no
Max Early Data: 0
---
read R BLOCK
---
Post-Handshake New Session Ticket arrived:
SSL-Session:
Protocol : TLSv1.3
Cipher : TLS_AES_256_GCM_SHA384
Session-ID: 9571C915486E6A0BBD0B2716E35EAAD6D0121EEFAA5FEE27D6966CA8C504F28B
Session-ID-ctx:
Resumption PSK: AB6C7FE7DEB60CF747ED96D6F1E51CF76A190B4F74C8BB71BDEE655FBD814AB56E4C23992B1D17F293328F6FF23DFB97
PSK identity: None
PSK identity hint: None
SRP username: None
TLS session ticket lifetime hint: 300 (seconds)
TLS session ticket:
0000 - 4c 7a 89 1a d0 c9 e9 e9-b0 95 db de 19 12 a5 87 Lz..............
0010 - 9a b8 e7 33 71 8c 49 5f-ac a4 ba 22 f0 89 e4 78 ...3q.I_..."...x
0020 - 6a f3 08 4a 0e 13 1c f1-2e 1a 07 d9 11 49 8b ca j..J.........I..
0030 - b3 89 87 5f fd 51 05 25-10 81 fa 00 d0 47 eb 2f ..._.Q.%.....G./
0040 - 2e 16 e3 92 e0 48 ab 92-1d f8 3e df 28 6a 35 49 .....H....>.(j5I
0050 - 8d 9e 6b 51 30 bf ad 3c-f7 6e b8 18 db 87 97 d3 ..kQ0..<.n......
0060 - 36 9a 4d 9e d6 cc d3 19-de b1 86 08 6c 0e e4 b3 6.M.........l...
0070 - 7f 28 95 08 c9 46 e2 7f-57 be 47 28 c2 02 8f b2 .(...F..W.G(....
0080 - 9c 3d 0d af cc 75 43 42-0d 7b a1 97 b2 ec 6a 3f .=...uCB.{....j?
0090 - b9 62 ae 70 d8 0d c4 4e-e6 01 b5 74 1b 7f 90 d1 .b.p...N...t....
00a0 - 07 7e b0 23 98 dd 22 d2-3d c5 74 31 9f 83 ad 66 .~.#..".=.t1...f
00b0 - 56 6b 40 6a 04 19 7e 1b-26 5d 6b 72 bb 35 5e f6 Vk@j..~.&]kr.5^.
00c0 - e5 3a a1 a3 d0 57 99 49-d5 cc b5 4f 94 1e d5 f2 .:...W.I...O....
00d0 - c0 28 c6 38 6b 28 41 d7-57 63 c0 da 8d 71 11 09 .(.8k(A.Wc...q..
00e0 - 96 09 67 1c 3d 0f 70 99-61 43 c7 7b a0 5c 1b 01 ..g.=.p.aC.{.\..
00f0 - 0d cf a9 1c 84 62 c5 50-8b 00 31 ba d3 a4 36 94 .....b.P..1...6.

Start Time: 1716285423
Timeout : 7200 (sec)
Verify return code: 18 (self-signed certificate)
Extended master secret: no
Max Early Data: 0
---
read R BLOCK
closed

Ultima cosa: il certificato è autofirmato e quindi non è collegato a nessuna Certification Authority, dunque in ogni caso il browser ci avverte:

errore scambio certificato https
errore scambio certificato https

Una volta data l’autorizzazione a scaricare il certificato autofirmato, posso vedere in Wireshsrk tutte le transazioni:

  • Client Hello
  • Server Hello
  • Change Cipher Spec

È istruttivo vedere il frammento di conversazione TCP/IP in cui il server comunica al client al versione di TLS che intende usare, il session ID, l’algoritmo di cifratura simmetrica per stabilire la chiave simmetrica DH con i parametri lunghezza e chiave:

Frammento dell'handshake (Server hello) con lo scambio dei parametri DH
Frammento dell’handshake (Server hello) con lo scambio dei parametri DH

Adozione di un certificato Let’s Encrypt

Un certificato Let’s Encrypt risolve ottimamente e senza costi il problema della verifica del certificato da parte dei browser, ma in questa fase mi interessava sapere come funzionano le cose, essendo queste delle prove eseguite in un ambiente isolato.

Riferimenti

Oracle 21 su Docker

Mi serve una installazione Oracle sul mio Linux Ubuntu 22.04. Dai vari link vedo che c’è qualche difficoltà a trovare pacchetti di installazionde diversi da distribuzioni Suse e RedHat. Per cui decido di utlizzare un container Docker al fine di sorpassare questi problemi di compatibilità, per lo sviluppo può andare più che bene.

Ho trovato un aiuto nel link citato nei Riferimenti

Download dell’immagine

Scarico l’express edition:

$ docker pull container-registry.oracle.com/database/express:21.3.0-xe

21.3.0-xe: Pulling from database/express
2318ff572021: Pull complete 
c6250726c822: Pull complete 
33ac5ea7f7dd: Pull complete 
753e0fae7e64: Pull complete 
Digest: sha256:dcf137aab02d5644aaf9299aae736e4429f9bfdf860676ff398a1458ab8d23f2
Status: Downloaded newer image for container-registry.oracle.com/database/express:21.3.0-xe

Sono all’incirca 3 GB.

Creo il container e lo avvio (attenzione che con l’istruzione di creazione definisco la password di system!):

$ docker container create -it --name oracle-devel -p 1521:1521 -e ORACLE_PWD=welcome123 container-registry.oracle.com/database/express:21.3.0-xe 
5640b42ae4387deffad2594fb0f9a391e13a53ed8e9bc8cc87cf17e41e445be2

$ docker ps -a
CONTAINER ID   IMAGE                                                      COMMAND                  CREATED              STATUS    PORTS     NAMES
5640b42ae438   container-registry.oracle.com/database/express:21.3.0-xe   "/bin/bash -c $ORACL…"   About a minute ago   Created             oracle-devel

$ docker start oracle-devel
oracle-devel

$ docker ps -a
CONTAINER ID   IMAGE                                                      COMMAND                  CREATED         STATUS                            PORTS                                       NAMES
5640b42ae438   container-registry.oracle.com/database/express:21.3.0-xe   "/bin/bash -c $ORACL…"   2 minutes ago   Up 2 seconds (health: starting)   0.0.0.0:1521->1521/tcp, :::1521->1521/tcp   oracle-devel

Alla fine mi connetto con Sqldeveloper in localhost specificando

  • nome utente: system
  • password
  • nome host: localhost
  • porta host: 1521
  • SID: xe

La connessione funziona (Stato: operazione riuscita):

Riferimenti

Algoritmi di crittografia con SSH

ssh
ssh

Da tempo non accedevo via ssh ad un server di un mio cliente, è successo questo:

$ ssh user@myhost.com
Unable to negotiate with <some IP adddress> port 22: no matching key exchange method found. Their offer: diffie-hellman-group-exchange-sha1,diffie-hellman-group14-sha1,diffie-hellman-group1-sha1

Il problema si chiama key exchange method, metodo di scambio delle chiavi. Ho controllato quali erano i metodi disponibili in locale con:

$ ssh -Q kex
diffie-hellman-group1-sha1
diffie-hellman-group14-sha1
diffie-hellman-group14-sha256
diffie-hellman-group16-sha512
diffie-hellman-group18-sha512
diffie-hellman-group-exchange-sha1
diffie-hellman-group-exchange-sha256
ecdh-sha2-nistp256
ecdh-sha2-nistp384
ecdh-sha2-nistp521
curve25519-sha256
curve25519-sha256@libssh.org

Mi pare ce ne siano parecchi. Ne scelgo uno:

$ ssh -oKexAlgorithms=diffie-hellman-group1-sha1 user@myhost.com
Unable to negotiate with <some IP adddress> port 22: no matching host key type found. Their offer: ssh-rsa,ssh-dss

Il problema si sposta, adesso si chiama host key type (tipo di chiave host), allora aggiungo anche l’altro parametro:

$ ssh -oKexAlgorithms=diffie-hellman-group1-sha1 -oHostKeyAlgorithms=ssh-rsa user@myhost.com
The authenticity of host 'myhost.com (<some IP adddress>)' can't be established.
RSA key fingerprint is SHA256:QrIQ15WsEr48UvcEE9cLJhoj7ilRCTL3ghaRMYI2Ewg.
This key is not known by any other names
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added 'myhost.com' (RSA) to the list of known hosts.
user@myhost.com's password: 
Last login: Tue Jan  9 11:54:48 2024 from <some other IP address>
-bash-3.2$

L’ultimo problema (server authenticity) era la firma RSA dell’host che non era tra i miei host conosciuti (con questo PC in effetti era la prima volta che mi connettevo).

Cos’è successo al mio client SSH?

Al client SSH ovviamente non è successo nulla: i tre problemi sono di natura diversa. L’ultimo (il problema della firma o fingerprint) l’ho già spiegato.

Vediamo cosa sono gli altri due.

I parametri HostKeyAlgorithms e KexAlgorithms sono entrambi utilizzati per specificare gli algoritmi crittografici utilizzati durante la fase di negoziazione di una connessione SSH. Tuttavia, hanno scopi leggermente diversi:

  1. HostKeyAlgorithms:
    Questo parametro specifica gli algoritmi utilizzati per generare e verificare le chiavi pubbliche degli host. Quando un client si connette a un server SSH per la prima volta, il server invia la sua chiave pubblica al client. Il client verifica quindi questa chiave pubblica confrontandola con una copia locale. Se le chiavi pubbliche non corrispondono, potrebbe esserci un attacco man-in-the-middle in corso. Gli algoritmi elencati in HostKeyAlgorithms specificano quali algoritmi il client accetterà per la chiave pubblica del server.
  2. KexAlgorithms:
    Questo parametro specifica gli algoritmi utilizzati per lo scambio delle chiavi segrete durante la fase di negoziazione della chiave di sessione. Questa fase è cruciale per garantire che le comunicazioni tra client e server siano protette da crittografia. Gli algoritmi elencati in KexAlgorithms specificano quali algoritmi il client è disposto ad utilizzare per lo scambio delle chiavi durante la connessione SSH.

Entrambi i parametri sono importanti per garantire la sicurezza e la compatibilità delle connessioni SSH. È possibile configurarli in modo da includere solo gli algoritmi più sicuri e recenti supportati da entrambe le parti, evitando quelli considerati obsoleti o vulnerabili. Tuttavia, è importante anche assicurarsi che l’algoritmo specificato sia supportato sia dal client che dal server per evitare errori di compatibilità, come quelli che ho riscontrato.

È infatti importante che il client e il server abbiano almeno un algoritmo in comune per poter stabilire una connessione sicura.

Da quanto detto è chiaro che esistono due fasi nella transazione:

  • l’invio della chiave pubblica dell’host
  • lo scambio di una chiave segreta per cifrare la sessione

Vediamo con un po’ più di dettaglio la fase di negoziazione.

Come si stabilisce una connessione sicura tra client e server con SSH

Una connessione SSH (Secure Shell) coinvolge diverse fasi per stabilire una comunicazione sicura e autenticata tra un client e un server remoto. Ecco le fasi principali di una connessione SSH:

  1. Inizializzazione della connessione: Il client SSH inizia la connessione al server SSH specificando l’host a cui desidera connettersi e autenticandosi, se necessario.
  2. Negoziazione delle chiavi crittografiche: Il client e il server SSH si scambiano informazioni sulle chiavi crittografiche supportate e selezionano una chiave per la sessione corrente.
  3. Scambio delle chiavi pubbliche: Il server invia la sua chiave pubblica al client per consentire al client di autenticare l’identità del server.
  4. Verifica dell’identità del server: Il client verifica che la chiave pubblica ricevuta dal server corrisponda a quella memorizzata localmente per il server a cui si sta connettendo.
  5. Generazione della chiave di sessione: Una volta che l’identità del server è stata verificata, client e server generano una chiave di sessione condivisa per crittografare i dati scambiati durante la connessione.
  6. Autenticazione: Se richiesta, il client si autentica con il server utilizzando password, chiavi SSH o altri metodi di autenticazione supportati.
  7. Stabilimento della connessione sicura: Una volta completate le fasi precedenti con successo, la connessione SSH è stabilita in modo sicuro e i dati trasmessi tra client e server sono crittografati per garantire la riservatezza e l’integrità.
  8. Interazione con il server: Una volta stabilita la connessione, il client può interagire con il server SSH per eseguire comandi remoti, trasferire file in modo sicuro o utilizzare altre funzionalità offerte dal protocollo SSH.

Il punto cruciale è il punto 5. Qui si decide la chiave da utilizzare da qui in avanti per cifrare il contenuto dei messaggi per questa sessione. Fino al punto 5 infatti la trasmissione avviene in chiaro. Del resto lo scambio della chiave pubblica dal server al client non è un segreto: è pubblica! Per il calcolo della chiave di sessione, che è una chiave simmetrica, di solito vengono utilizzati algoritmi di scambio di chiavi come il Diffie-Hellman o il suo derivato Elliptic Curve Diffie-Hellman (ECDH). Questi algoritmi consentono a client e server di concordare in modo sicuro una chiave di sessione condivisa senza dover trasmettere in chiaro la chiave attraverso la rete, garantendo così la riservatezza della chiave simmetrica. Una volta che la chiave di sessione è generata utilizzando l’algoritmo di scambio di chiavi concordato, viene utilizzata per crittografare i dati scambiati durante la connessione SSH. L’algortimo da usare per calcolare la chiave è esattamente quello indicato con i due parametri visti sopra.

il parametro KexAlgorithms viene utilizzato per specificare gli algoritmi di scambio di chiavi (key exchange algorithms) che possono essere utilizzati per stabilire una connessione sicura tra il client e il server. Questi algoritmi sono utilizzati per generare la chiave di sessione condivisa, che è una chiave simmetrica utilizzata per crittografare i dati scambiati durante la connessione SSH.

D’altra parte, l’opzione HostKeyAlgorithms specifica gli algoritmi di chiave pubblica che il server accetta per autenticarsi durante la procedura di handshake iniziale. Questi algoritmi sono utilizzati per verificare l’identità del server e stabilire una connessione sicura con esso.

Quindi, per generare la chiave simmetrica durante una connessione SSH, sono importanti sia gli algoritmi di scambio di chiavi specificati con KexAlgorithms che gli algoritmi di chiave pubblica specificati con HostKeyAlgorithms. Entrambi giocano un ruolo cruciale nella sicurezza e nell’efficienza della connessione SSH.

Diciamo, in ultima analisi, che

  • HostKeyAlgorithms serve per stabilire quale algoritmo di chiave pubblica asimmetrica verrà usato per scambiarsi la chiave simmetrica di sessione.
  • KexAlgorithms serve per stabilire quale algoritmo di chiave simmetrica verrà usato per generare la chiave (simmetrica) di sessione.

Dunque

  1. prima si calcola la chiave simmetrica con l’algoritmo concordato con KexAlgorithms (nel mio caso: diffie-hellman-group1-sha) e
  2. poi il client invia al server la chiave di sessione, dopo averla cifrata con l’agoritmo di chiave asimmetrica concordato con HostKeyAlgorithms (nel mio caso ssh-rsa) che utilizza la chiave pubblica del server.
  3. Alla fine il server è in grado di confontare che la chiave simmetrica calcolata da lui è uguale a quella calcolata dal client e cifrata con la sua chiave pubblica. Questo consente al server di autenticare il client.

Fonti

È stato divertente cercare di capire l’argomento utilizzando tre sistemi di AI: ChatGpt, Aria (Opera) e Skype/Bing.

Ci sono arrivato per interpolazione e verificando su siti attendibili come ssh.com, nessuno spiegava veramente bene come funziona il tutto, ma si avvicinava comunque alla realtà.

Poi… sono cose studiate tanti anni fa e un ripassino è sempre utile.

gRPC e Protocol Buffers: una introduzione.

gRPC è una tecnologia nata in Google che astrae il concetto di classe immergendolo in quello di cloud: supponiamo che il nostro progetto locale sfrutti una classe Figura che realizza una figura piana della geometria.

Chi l’ha detto che la classe debba trovarsi sullo stesso PC in cui stiamo sviluppando il nostro progetto?

Protocol Buffers è una tecnologia open source di Google che implementa questa architettura: istanziare nel nostro ambiente local un oggetto (stub) definito altrove:

gRPC architecture
gRPC architecture

gRPC è stato inizialmente creato da Google, che da oltre un decennio utilizza un’unica infrastruttura RPC generica chiamata Stubby per connettere il gran numero di microservizi in esecuzione all’interno e attraverso i suoi data center. Nel marzo 2015, Google ha deciso di creare una nuova versione di Stubby rendendola open source.

In gRPC, un’applicazione client può chiamare direttamente un metodo su un’applicazione server su un computer diverso come se fosse un oggetto locale, semplificando la creazione di applicazioni e servizi distribuiti. Come in molti sistemi RPC, gRPC si basa sull’idea di definire un servizio, specificando i metodi che possono essere chiamati da remoto con i relativi parametri e tipi di ritorno. Sul lato server, il server implementa questa interfaccia ed esegue un server gRPC per gestire le chiamate client. Dal lato client, il client ha uno stub (indicato semplicemente come client in alcuni linguaggi) che fornisce gli stessi metodi del server.

I client e i server gRPC possono essere eseguiti e comunicare tra loro in una varietà di ambienti, dai server interni a Google al desktop, e possono essere scritti in qualsiasi linguaggio supportato da gRPC. Quindi, ad esempio, puoi creare facilmente un server gRPC in Java con client in Go, Python o Ruby. Inoltre, le ultime API di Google disporranno di versioni gRPC delle rispettive interfacce, consentendoti di integrare facilmente le funzionalità Google nelle tue applicazioni.

Lavorare con i protocol buffer (PB) di gRPC

Si parte dalla definizione del messaggio che può essere un file JSON o più convenientemente un file di tipo proto (per protocol), per esempio questo potrebbe essere il contenuto del file message.proto:

message Person {
  string name = 1;
  int32 id = 2;
  bool has_ponycopter = 3;
}

Nota che 1, 2 e 3 sono i segnaposto, non sono assegnazioni.

Quindi, una volta specificate le strutture dati, si utilizza il compilatore del protocol buffer protocper generare classi di accesso ai dati nel linguaggio di programmazione scelto nella definizione del prototipo. Queste classi forniscono semplici metodi di accesso (get/set) per ciascun campo, come name()set_name(), nonché metodi per serializzare/analizzare l’intera struttura da/verso byte grezzi. Quindi, ad esempio, se il linguaggio scelto è C++, l’esecuzione del compilatore nell’esempio sopra genererà una classe chiamata Person. Puoi quindi utilizzare questa classe nella tua applicazione per popolare, serializzare e recuperare Personi messaggi del protocol buffer.

Oltre ai dati, nei file proto vengono definiti anche i servizi:

// The greeter service definition.
service Greeter {
  // Sends a greeting
  rpc SayHello (HelloRequest) returns (HelloReply) {}
}

// The request message containing the user's name.
message HelloRequest {
  string name = 1;
}

// The response message containing the greetings
message HelloReply {
  string message = 1;
}

Il comando protoc (compilatore) poi esegue la magia, convertendo i file .proto in classi nel linguaggio scelto per il progetto.

La versione 3 di PB (proto3) è disponibile nei seguenti linguaggi di programmazione:  Java, C++, Dart, Python, Objective-C, C#, un runtime lite (Android Java), Ruby e JavaScript. Anche PHP con l’adozione di un plugin.

Riferimenti

Port forwarding con Docker

La mappatura delle porte Docker, nota anche come port forwarding , è una tecnica utilizzata per esporre i servizi di rete – che sono in esecuzione all’interno di un contenitore Docker – verso l’host, verso altri contenitori Docker dello stesso host o verso altri host o dispositivi di rete.

Il port forwarding consente di mappare una porta specifica dal sistema host a una porta sul contenitore, rendendo il servizio accessibile dall’esterno del contenitore.

Quando si esegue un contenitore, in genere esiste un proprio spazio dei nomi di rete isolato con il proprio indirizzo IP (per questo, si veda il comando docker network). Per impostazione predefinita, i contenitori possono comunicare tra loro e con il sistema host, ma l’accesso alla rete esterna non è abilitato automaticamente. La mappatura delle porte viene utilizzata per creare la comunicazione tra la rete isolata del contenitore e la rete del sistema host.

La mappatura delle porte

Possiamo eseguire la mappatura delle porte in Docker in due modi: usando il Dockerfile e i comandi di docker, oppure con docker-compose.

Quando si esegue un contenitore con docker run.

Quando si utilizza il comando docker run, è possibile utilizzare il flag -p--publishper specificare la mappatura delle porte. La sintassi è la seguente: -p host_port:container_port. Ad esempio, per mappare:

  • la porta 3307 sull’host
  • alla porta 3306 all’interno del contenitore,

Quindi: -p 3307:3306. È possibile specificare più mappature di porte ripetendo il flag -p. Questo per esempio ci serve se nello stesso container vogliamo pubblicare un database, un web server Apache o un Tomcat.

# docker run - p 8080:80 myapp

Quando si utilizza docker compose:

Se si utilizza Docker Compose per gestire i contenitori, si può puoi definire la mappatura delle porte nel file di configuazione docker-compose.yml utilizzando la sezione portssezione. La sintassi è la stessa del comando docker run.

Nel mio caso ho un container che contiene un’applicazione PHP – Laravel e una che contiene il DBMS MySQL:

version: '3'

services:
  app:
    image: ap2mylaravel
    container_name: app
    networks:
      - my_network

  mysql_db_container:
    image: mysql_db
    container_name: app_db
    ports:
      - "3307:3306" # Mappa la porta 3306 del container sulla porta 3307 dell'host
    networks:
      - my_network

networks:
  my_network:
     driver: bridge

Il container che ospita MySQL si intende pubblicato sulla porta interna 3306 che, all’esterno dell’host, viene mappata sulla 3307 (per non confliggere con quella del MySQL che gira sull’host che è pubblicato sulla 3306).

Il comando

# docker-compose up

avvierà il contenitore con la mappatura della porta specificata.

Ho disegnato questo schema funzionale completo in cui si vede come vanno usati i comandi per collegarsi al container del db a partire dall’altro container, dall’host e anche da un computer remoto: si vedono i due container che giurano all’interno di Host, il container dedicato al dbms ascolta sulla porta 3306 e internamente a Host ed è questa la porta che va usata nei client (sia che si utilizzi il calinet in Host che nel container app.

Posso altresì accedere allo stesso db anche dall’esterno del host – un pc remoto – puntando alla porta 3307 e utilizzando l’indirizzo del netowk virtuale creato da docker che posso trovare digitanto docker network ls (posso verificare che l’interfaccia è proprio quella che si vede digitando il comando ifconfig).

È possibile configurare la rete (sia agendo a livello di configurazione docker che sulla configurazione di mysql) in modo tale da avere il livello di sicurezza che si vuole: per esempio ho fatto in modo che l’accesso al dbms come root sia possibile esclusivamente dall’interno del container app_db e che laravel possa accedere soltanto dal suo container (app) con un utente applicativo a cui sono stati concessi i soli privilegi di crud.

docker network layout
docker network layout

Riferimenti

Matematici della storia

Con GPT-3.5, Octave e un po’ di lavoro

matematici della storia
Matematici della storia (formato SVG)