openssl-win-root: usare l’archivio certificati di Windows con Ruby
Se usate Ruby su Windows, prima o poi vedrete una richiesta HTTPS fallire con un errore di verifica del certificato, anche se lo stesso sito si apre senza problemi nel browser. openssl-win-root è una piccola gem che aiuta proprio in questo caso.
Innanzitutto, il merito a chi spetta: la gem è stata scritta da Stas Ukolov e il repository originale è github.com/ukoloff/openssl-win-root. La prima release risale al 2015. Il mio contributo è piccolo e lo descrivo più avanti.
Il problema
L’OpenSSL di Ruby non consulta l’archivio certificati di Windows: ha bisogno di propri certificati CA. RubyInstaller include un file cacert.pem, ma come fa notare il README della gem, questo file non viene aggiornato automaticamente. È il bundle del momento in cui è stata compilata la vostra versione di Ruby, quindi possono mancare certificati radice più recenti.
Windows invece mantiene aggiornato il proprio archivio di certificati radice attendibili. Questo archivio contiene anche i certificati installati dalla vostra azienda, ad esempio tramite Group Policy. Ruby semplicemente non li vede.
Cosa fa la gem
Quando la caricate su Windows, openssl-win-root:
- legge i certificati radice attendibili dall’archivio di sistema di Windows (tramite l’API
crypt32, usando Fiddle), - li scrive come file PEM in una cartella
pemall’interno della directory della gem, con i nomi che OpenSSL si aspetta per una directory di certificati, - aggiunge quella cartella all’archivio certificati predefinito di OpenSSL e imposta su di essa la variabile d’ambiente
SSL_CERT_DIR.
Non serve alcun accesso alla rete. Sugli altri sistemi operativi la gem non fa nulla, quindi la si può tenere senza problemi in un progetto multipiattaforma.
Come usarla
Aggiungetela al Gemfile, solo per Windows:
gem 'openssl-win-root' if Gem.win_platform?
Poi eseguite bundle, oppure installatela direttamente:
gem install openssl-win-root
Caricatela prima di effettuare richieste HTTPS:
require 'openssl/win/root'
require 'net/http'
Net::HTTP.get(URI('https://www.ruby-lang.org/')).length
Se il vostro progetto usa Bundler.require, come fa Rails, il require esplicito non serve.
I certificati esportati si possono usare anche al di fuori di Ruby. Puntate SSL_CERT_DIR, o l’opzione -CApath del comando openssl, alla cartella restituita da:
OpenSSL::Win::Root.path
Cose da tenere presenti
- I certificati vengono esportati nella directory della gem a ogni
require, quindi il processo deve avere i permessi di scrittura lì. - Su Windows Server capita che l’archivio certificati non abbia ancora attivato un certificato radice. Il rimedio descritto nel README è aprire il sito una volta in un browser (Microsoft) oppure effettuare una richiesta con
WinHttpRequesttramitewin32ole. In qualche modo questo induce Windows ad attivare il certificato radice mancante. - L’ultima release, la 1.1.2, è di gennaio 2023 e la gem richiama l’API di Windows tramite Fiddle. Provatela sulla versione di Ruby che usate effettivamente.
Il mio ruolo
Con l’arrivo di Ruby 3.2 la gem ha smesso di funzionare: chiamava File.exists?, rimosso in Ruby 3.2. È lo stesso problema di cui ho scritto nel mio articolo sulla gem file_exists. Ho aperto una pull request con la correzione di una sola parola.
Sono uno dei due proprietari della gem su RubyGems e a gennaio 2023 ho pubblicato la versione 1.1.2 dal mio fork. Quella release ha cambiato molto poco:
File.exists?è diventatoFile.exist?, così la gem funziona di nuovo su Ruby 3.2,- dal gemspec è stato rimosso il passo di build dell’estensione, inutilizzato,
- il README ha ricevuto note sul
cacert.pemdi RubyInstaller e sull’avvertenza per Windows Server descritta sopra.
Tutto il resto, l’idea e l’implementazione vere e proprie, è opera di Stas.