Principi e criteri sistemistici.

FIXME

In questo documento illustriamo i principi generali e i criteri sistemistici per la progettazione, l'implementazione e la gestione delle nostre infrastrutture informatiche.

Introduzione

Questi i principi generali che adottiamo:

  • l'infrastruttura e i servizi erogati devono garantire sicurezza a ogni livello dello "stack", a partire dal bare metal 1
  • l'intera infrastruttura di SW e i relativi servizi sono basati solo su software libero
  • non profiliamo i nostri clienti, siamo totalmente contrari al Surveillance Capitalism
  • non usiamo pratiche di lock-in, vogliamo che i nostri clienti siano liberi di rimanere con noi oppure di rivolgersi ad altri fornitori se lo ritengono opportuno: se richiesto forniremo loro tutte le risorse necessarie per riprodurre i servizi su altre infrastrutture.

Supply Chain e Infrastructure stack

Il principio fondamentale che adottiamo per gestire la nostra infrastruttura - e quelle gestite on-premises per conto dei clienti - è quello di tenere strettamente sotto controllo l'intera Supply Chain del software.

Il concetto è illustrato molto bene nell'acticolo Developer Supply Chain Management (in inglese), che riassumeremmo in questo estratto:

All of these are the kinds of questions that any manufacturing company has had to wrestle with, under the larger term “Supply Chain Management”. If you currently run a software development department, and you currently use libraries that are developed out-of-house (which is to say, everybody), then you owe it to your customers and consumers and operations staff and executive management to read up on this subject and find some ideas for how to manage your software supply chain.

Alla luce di questo principio abbiamo deciso di utilizzare uno stack misto nella infrastruttura, valutando per ciascuno strumento nello stack la propria adeguatezza a tenere sotto controllo la Supply Chain del software utilizzato nell'infrastruttura e per l'erogazione dei servizi. FIXME: migliorare la scorrevolezza di questa frase

Reproducible builds, non binari prefconfezionati.

Adottiamo solo software che rispetta i principi di Reproducible Builds, questo significa che evitiamo di utilizzare software binario precompilato che non provenga da un progetto che non si impegna a fornire software riproducibile e che possa quindi essere riprodotto e verificato localmente anche da chiunque voglia verificarne l'autenticità.

I problemi del build non riproducibile

Il 26 Marzo 2019 è stata pubblicata nel repository ufficiale RubyGems la versione 3.2.0.3 della libreria bootstrap-sass, che conteneva una backdoor che consentiva l'esecuzione remota di codice sul server.

Il nocciolo della questione è che il codice upstream di bootstrap-sass non ha mai incluso la backdoor ma è stata inserita da un attaccante che ha ottenuto le credenziali di accesso al repository RubyGem di uno degli sviluppatori upstream e le ha usate per caricare il formato già pacchettizzato della versione con la backdoor. In altre parole il repository RubyGem non si occupa di effettuare direttamente la pacchettizzazione del codice upstream ma solo di distribuire la versione già pacchettizzata caricata direttamente dagli sviluppatori.

In un articolo intitolato Subversion of bootstrap-sass (in inglese) David Wheeler elenca alcuni strumenti per evitare simili situazioni 2, tra cui quello fondamentale è a nostro giudizio questo:

Require a reproducible build. The attackers subverted the distributed package on RubyGems without first posting the corresponding source code in its official source code repository on GitHub. That’s a big red flag. I believe package repositories should verify that code distributed can be reproducibly regenerated from its putative source. Such an approach would have prevented this attack, and also made the previous “event-stream” incident far more visible than it was. They don't need to change their inputs; accept a build, and check that the regenerating the build will produce the same thing.

Noi ritieniamo che simili problemi possano riguardare anche altri sistemi di distribuzione analoghi a RubyGems (ad esempio npmjs, pypi, ecc.) e che non è detto che questi tipi di attacchi siano scoperti in tempi brevi come nel caso di bootstrap-sass, soprattutto quando si tratta di librerie o pacchetti meno famosi. FIXME: tipi di attacchi? Mi pare meglio il singolare: tipo di attacchi, perché alla fine tutti sarebbero riconducibili alla stessa tecnica: sostituzione del binario legittimo (derivato dal sorgente) con uno contraffatto.

Infrastructure as code, code as data

FIXME: semplificare e rielaborare questo https://en.wikipedia.org/wiki/Infrastructure_as_code

FIXME: concetto di stateless e stateful

Tutto ciò che nell'infrastruttura è definito in Guile Scheme può essere trattato sia come codice, quindi implementato via Guix System, che come dati, quindi elaborato per diventare per esempio: documentazione dell'infrastruttura, template per l'istanziazione automatica.

A regime utilizzeremo questa caratteristica per sviluppare una interfaccia self-service attraverso la quale i clienti non solo potranno istanziare in autonomia i servizi, ma anche (ri)configurarli, attraverso una combinazione di template per ciascun servizio e macro per la generazione del codice. Un interessante articolo in merito è Lisp for the Web, che usa Common Lisp per gli esempi ma può essere implementato in qualsiasi dialetto Lisp moderno.

Guix

Ogni volta che è disponibile un servizio in Guix System noi implementiamo quello.

Guix è il package manager, Guix System è il sistema operativo basato su Guix e configurabile in modo dichiarativo attraverso uno speciale EDSL (Embedded Domain Specific Language) che estende il linguaggio Guile Scheme.

Per la descrizione delle caratteristiche principali di Guix, trovo questo un interessante executive summary: https://web.archive.org/web/20190921020738/https://github.com/malcook/sce/blob/master/MakingTheCase.org

Per una descrizione alternativa ad "alto livello" (sempre un po' tecnica) di Guix è da tenere in considerazione What Is Guix Really? (del 2021-01-17) di Ryan Prior, uno sviluppatore piuttosto attivo nella comunità Guix.

Coinvolgimento nella comunità Guix

Considerato l'alto valore strategico di Guix System per SWWS, nel medio periodo uno degli obiettivi di SWWS è quello di essere parte attiva all'interno della comunità Guix contribuendo con codice per la definizione di nuovi pacchetti ma soprattutto di nuovi servizi, dando priorità a quelli strategici per la nostra offerta.

NixOS https://nixos.org/

Il secondo sistema operativo nello stack di SWWS è NixOS, il sistema operativo basato su Nix con una configurazione di tipo dichiarativo 3.

Nix possiede le caratteristiche di: configurazione dichiarativa, roll-back delle istanze, build dei pacchetti in un processo isolato dall'ambiente utente.

Le caratteristiche principali di NixOS sono descritte qui: https://nixos.org/nixos/about.html

Problemi di Nix rispetto a Guix System

NixOS utilizza un DSL funzionale sviluppato ad-hoc, non estende altri linguaggi esistenti: questo lo rende meno versatile rispetto all'EDSL utilizzato da Guix System.

L'approccio nella creazione e inclusione dei pacchetti in Nix a volte è un po' sbrigativo e per aggirare alcuni problemi non esitano a includere binari precompilati anzichè fare il build da sorgente; questo approccio è illustrato bene nell'articolo Let's Package jQuery: A Javascript Packaging Dystopian Novella, del quale citiamo questo estratto:

And let's face it, "fuck it, I'm out" seems to be the mantra of web application packaging these days. Our deployment and build setups have gotten so complicated that I doubt anyone really has a decent understanding of what is going on, really.

Per questi motivi non ritieniamo opportuno investire nello sviluppo di nuovi pacchetti o servizi che non siano già disponibili in NixOS, preferendo per questo Guix.

Debian

Ove non siano disponibili servizi in Guix System o NixOS, utilizziamo i servizi disponibili in Debian (stable), possibilmente installati in un container LXC definito e gestito via libvirt (virsh) per una configurazione il più possibile dichiarativa (in questo caso il formato XML di configurazione di libvirt).

Problemi di Debian

In Debian la configurazione (solitamente in /etc) è stateful, ovvero fa parte integrante dello stato della macchina e non è per nulla semplice adottare un sistema di configurazione stateless.

La totalità dei sistemi di configuration management disponibili, da Ansible a Puppet, non risolvono questo problema (FIXME: punto da sviluppare).

Essendo l'intero sistema operativo stateful, l'unico modo per garantire la riproducibilità dell'implementazione è quello di effettuare snapshot periodici (backup) dell'intero sistema. Il fatto che il sistema sia un container rende più semplice la procedura. Utilizzare il filesystem btrfs per le root dei container rende ancora più semplice effettuare snapshots ed eventualmente effettuare il roll-back a versioni precedenti, in caso di problemi durante un upgrade di sistema.

Docker

Ove non siano disponibili servizi per i sistemi operativi che ufficialmente supportiamo, ricorriamo in ultima istanza all'utilizzo di container Docker (in questo contesto si intende immagini di applicazioni create per mezzo di un Dockerfile), messi solitamente a disposizione upstream dagli sviluppatori del software come mezzo ufficiale di distribuzione o - ove ragionevole da un punto di vista della sicurezza - da terze parti che rielaborano i Dockerfile iniziali.

Nell'adottare queste immagini di applicazioni stabiliamo questi ulteriori criteri per tenere sotto controllo la sicurezza e la gestione dei container:

  1. tutti i servizi devono essere implementati attraverso Docker compose

(https://docs.docker.com/compose/) con relativo file di configurazione docker-compose.yml; eventuali deroghe a questa regola possono essere date solo a fronte documentata impossibilità di utilizzo dell'immagine con Docker compose e un piano di integrazione dell'app in Docker compose deve essere improntato e implementato in tempi ragionevoli FIXME: rendere più scorrevole.

  1. tutte le immagini utilizzate devono essere compilate in locale

(ovvero all'interno dell'infrastruttura SWWS) per mezzo di un Dockerfile valutato e approvato dal responsabile IT di SWWS; eventuali deroghe a questa regola possono essere date solo a fronte di una documentata impossibilità di ricompilare localmente le immagini e un piano di compilazione locale deve essere improntato e implementato con urgenza

Problemi di Docker

Docker, come sistema di istanziazione di container, non ha assolutamente nessun problema di sicurezza e non pone nessun particolare allarme il suo utilizzo dell'infrastruttura SWWS.

Il fondamentale (e troppo spesso sottovalutato) problema di Docker sta nell'utilizzo dei Dockerfiles, che purtroppo in moltissimi casi sono mal progettati e installano nelle immagini software potenzialmente insicuro, a volte direttamente in formato binario. L'utilizzo massiccio della direttiva FROM, molto diffuso, rende l'analisi del build delle immagini un'operazione estremamente complessa e dispendiosa, che in SWWS preferiremmo non essere costretti a fare per poterci concentrare su cose più produttive.

Anche dal punto di vista legale l'utilizzo di Dockerfiles con molteplici overlay rende la situazione più complicata rispetto all'adozione dei sistemi elencati sopra.

In un articolo intitolato Containers and license compliance il responsabile open source di VMware illustra bene sia i problemi di sicurezza che quelli legali dovuto ad un uso spregiudicato, purtroppo piuttosto diffuso, dei Dockerfiles. Questi i punti fondamentali della sua analisi:

  1. People do "incredibly dumb stuff" in their Dockerfiles, including adding

new repositories with higher priorities than the standard distribution repositories, then doing an update. That means the standard packages might be replaced with others from elsewhere. Once again, that is a security nightmare, but it may also mean that there is no source code available and/or that the license information is missing. This is not something he made up, he said, if you look at the Docker repositories, you will see this kind of thing all over; many will just copy their Dockerfiles from elsewhere.

  1. There is a "rabbit hole" that you need to follow, Dockerfile to

Dockerfile, to figure out what you are actually shipping. He has done a search of official Docker images and did not find a single one that follows compliance best practices. All of the Dockerfiles grab other Dockerfiles—on and on.

  1. Containers need to be built starting from a base that has known-good

package versions, corresponding source code, and licenses. The anti-pattern of installing stuff from random internet locations needs to be avoided. And software developers need to be trained about the pitfalls of the container build systems, which should not be hard, but is.

Tutte le attività sopra elencate sono piuttosto dispendiose da effettuare internamente in SWWS, per questo motivo per ciascun servizio implementato attraverso un Dockerfile SWWS deve progettare e implementare una soluzione alternativa che eviti quelle incombenze.

Altri riferimenti

Message-ID: <871s3v6e9p.fsf@roquette.mug.biscuolo.net>

Message-ID: <87o94pma6j.fsf@roquette.mug.biscuolo.net> (e relativo thread)

Message-ID: <87zhgjgdtb.fsf@roquette.mug.biscuolo.net> (guix-devel)

Message-ID: <878su3b4qq.fsf@roquette.mug.biscuolo.net> (guix-devel)

Note a piè di pagina:

1

per quanto ragionevolmente possibile considerato lo stato dell'arte, vedi ad es. il problema con Intel Management Engine

2

interessante anche tutta la serie di saggi Learning from Disaster.

3

Guix si ispira a Nix e in un certo senso ne è l'evoluzione.

Mappa del sito

Sorgente del documento: commit 268c2ff @ master (file history).