Salta al contenuto

Documentazione

Agenti

L’agente è l’unica parte di Provibr che gira sul suo hardware. Custodisce le credenziali dei sistemi che collega, esegue il lavoro richiesto dal pannello e riferisce ciò che vede. La piattaforma non si collega mai dall’esterno.

Che cos’è un agente

Un agente copre una rete: il punto da cui sono raggiungibili i suoi hypervisor e i suoi pannelli. Tutto ciò che Provibr fa alla sua infrastruttura passa da lì.

  • La connessione è in uscita. È l’agente ad aprirla verso la piattaforma e a mantenerla aperta; nulla deve essere raggiungibile da internet e non serve inoltrargli alcuna porta.
  • Le credenziali restano dalla sua parte. Vengono inviate all’agente una sola volta e sigillate in una cassaforte cifrata su quell’host; la piattaforma memorizza soltanto i nomi dei campi compilati.
  • Riferisce ciò che osserva. Le macchine vengono interrogate a ritmo serrato e ogni pochi minuti viene eseguito un confronto completo: è così che il pannello si accorge di una macchina arrestata al di fuori di Provibr.
  • Tocca soltanto ciò che gli ha affidato. Una risorsa senza una voce in Provibr viene ignorata, conteggiata e di nuovo dimenticata. L’agente non adotta mai una macchina di propria iniziativa.

L’host di cui ha bisogno

Nulla di esotico e nulla da installare prima. L’agente è un unico binario collegato staticamente: porta con sé la propria libreria C, quindi non c’è alcun runtime, alcun interprete né alcun pacchetto della distribuzione che debba corrispondere. Basta una piccola macchina virtuale: le dimensioni che abbiamo misurato si trovano più avanti in questa pagina.

Che cosa deve offrire un host per l’agente
RequisitoChe cosa servePerché
Processorex86 a 64 bit (x86_64, detto anche amd64)Pubblichiamo una sola build per Linux ed è compilata per questa architettura. L’installer controlla uname -m prima di scaricare qualsiasi cosa e su qualunque altra architettura si ferma, invece di installare qualcosa che non potrebbe funzionare. Una build per ARM al momento non esiste.
Sistema operativoQualsiasi distribuzione LinuxNulla viene collegato alle librerie del suo sistema, quindi la distribuzione, la sua versione e il suo gestore di pacchetti non fanno alcuna differenza. La sezione successiva elenca quelle su cui questa build è stata effettivamente avviata.
Gestore dei servizisystemd, o il suoL’installer scrive un’unità systemd e la attiva. Su un host senza systemd installa e registra comunque tutto, e poi stampa il comando per avviare l’agente con ciò che usa al suo posto. Una mezza installazione sarebbe peggio di un’installazione onesta.
Permessi durante l’installazioneroot, una voltaL’installazione crea l’utente di sistema provibr-agent, due directory e l’unità. Da quel momento l’agente gira con quell’utente senza privilegi, ed è lo script di aggiornamento, su cui non ha il permesso di scrivere, a mantenere le cose così.
Connessioni in uscitaTCP 50051 e 50052 verso gw.provibr.netLa porta 50051 viene usata una sola volta, durante la registrazione. La porta 50052 trasporta la sessione e resta aperta. Entrambe usano TLS. Se il suo firewall filtra il traffico in uscita, queste due regole sono tutto ciò che deve consentire.
Connessioni in entrataNessunaÈ l’agente ad aprire la connessione, quindi nulla deve essere raggiungibile da internet e non serve inoltrargli alcuna porta. L’unica eccezione è l’installazione delle macchine da rete, e anche in quel caso resta in ascolto soltanto sul segmento locale.
Accesso alla sua reteVerso i sistemi che collegaIl suo hypervisor, il suo pannello di controllo o il suo pannello di gioco viene interrogato tramite la propria API, da questo host. Ciò che l’agente non riesce a raggiungere, Provibr non può gestirlo.
Memoria512 MB bastano ampiamenteAbbiamo misurato 18 MB con mille macchine distribuite su cinque sistemi, ovvero il caso più pesante della tabella qui sotto. Il picco è altrove: sbloccare il file di licenza durante la registrazione richiede per un attimo circa 70 MB, perché quel passaggio è deliberatamente costoso da attaccare a forza bruta.
Disco1 GB basta ampiamenteIl binario occupa 17 MB e tutto ciò che l’agente conserva accanto a esso è rimasto sotto i 40 kB nelle nostre misurazioni. L’eccezione è l’installazione delle macchine da rete: mette in cache i supporti di avvio, fino a 20 GB per impostazione predefinita.
OrologioAll’incirca correttoI certificati e i file di licenza portano date di validità che entrambe le parti controllano, quindi un host il cui orologio sbaglia di giorni non riesce a registrarsi. Va bene qualsiasi client NTP.
L’agente gira con un proprio utente senza privilegi, all’interno di un’unità systemd irrobustita. Riceve esattamente due capability aggiuntive, e solo per poter servire le installazioni da rete; rimuovendole continua a funzionare tutto tranne quello.

Quale Linux

Non c’è alcun pacchetto da installare e nessuna distribuzione a cui corrispondere, quindi quello che segue non è un elenco di sistemi supportati: è l’elenco dei sistemi su cui questa build è stata effettivamente avviata. Qualsiasi altro sistema con un kernel Linux x86 a 64 bit dovrebbe comportarsi allo stesso modo e, se così non fosse, ci farebbe piacere saperlo.

Le distribuzioni Linux su cui l’agente è stato eseguito
SistemaVersioneNote
Debian13 (Trixie)
Debian12 (Bookworm)La distribuzione su cui è stato eseguito il test completo: agente installato, registrato, connesso e in polling.
Ubuntu24.04 LTSUbuntu 24.04 è la versione con cui il kernel ha iniziato a limitare esattamente gli spazi dei nomi di cui hanno bisogno le automazioni. L’agente include un profilo AppArmor apposito; tutto il resto funziona senza modifiche.
Ubuntu22.04 LTS
AlmaLinux9
AlmaLinux8Una libreria C molto più vecchia di quella con cui è stata prodotta questa build. Non fa alcuna differenza, perché l’agente porta con sé la propria.
Fedora42
openSUSE Leap15
Amazon Linux2023
Alpine Linux3.21musl e nessuna glibc: la prova più severa che esista per un binario collegato staticamente.
Due limiti da dichiarare onestamente su quell’elenco. Mostra che il binario si avvia e svolge il proprio lavoro crittografico su ciascuno di questi userland; la sessione completa, con certificati e polling, è stata misurata su uno solo di essi. E ogni verifica è stata eseguita su macchine che condividono un unico kernel, quindi ciò che dimostra è l’indipendenza dalla distribuzione, non dalla versione del kernel.

Quanta macchina serve

L’agente in sé è piccolo e resta piccolo. A crescere è l’inventario che sorveglia: ogni venti secondi legge da ciascun sistema collegato l’elenco completo delle macchine, lo confronta con il giro precedente e inoltra soltanto ciò che è cambiato. Ogni cinque minuti inoltra invece il quadro completo, che fa da rete di sicurezza per tutto ciò che un messaggio perduto sarebbe altrimenti costato, ed è anche il giro in cui chiede ai suoi nodi quanta capacità hanno.

Misurato su un solo agente; ogni riga è una finestra di cinque minuti
SituazioneMemoriaProcessore
Connesso, ancora nulla di collegato8 MB0,02% di un core
Un sistema, 10 macchine12 MB0,03%
Un sistema, 250 macchine14 MB0,06%
Un sistema, 1.000 macchine15 MB0,14%
Cinque sistemi, 200 macchine ciascuno18 MB0,19%
I passaggi tra una riga e l’altra dicono più delle righe stesse. Collegare il primo sistema costa circa 4 MB e ogni sistema successivo circa 0,75 MB, ed è esattamente lì che differiscono le ultime due righe. Più macchine ci sono, meno costa ognuna: passare da 10 a 1.000 ha aggiunto 3 MB in tutto, circa 3 kB ciascuna, e il passo da 250 a 1.000 è stato ancora meno costoso. Misurato ad agosto 2026 su sistemi simulati esattamente di quelle dimensioni, così che i numeri dicano qualcosa sull’agente e non sul cluster di qualcuno.

Il traffico è l’altro dato che vale la pena avere. Quelle mille macchine costano circa 250 kB ogni venti secondi verso i suoi sistemi, all’incirca un gigabyte al giorno sulla sua rete, mentre dieci macchine costano 2,5 kB a giro. Di quel dato relativo alle mille macchine, circa 17 kB a giro proseguono verso di noi, circa 70 MB al giorno, perché viene inoltrato soltanto ciò che è cambiato.

Il tipo di sistema conta? Quasi per nulla. Che l’agente dialoghi con un hypervisor, con un pannello di controllo per l’hosting o con un pannello di gioco, il lavoro è lo stesso: una chiamata per sistema a ogni giro, un confronto, un messaggio. Due differenze esistono davvero, ma sono piccole: a un hypervisor vengono chiesti anche gli indirizzi all’interno dei suoi sistemi guest, al massimo una volta ogni cinque minuti per ciascuna macchina in esecuzione, e un pannello che non misura nulla non invia alcun dato di utilizzo. Ciò che cambia enormemente è la macchina dall’altra parte, e quella si dimensiona in base a ciò che esegue, non in base all’agente.

Le dimensioni qui sotto non sono quindi minimi che abbiamo misurato: l’agente sta in molto meno di così. Sono ciò che ordineremmo noi, perché un sistema operativo con i suoi log, e gli strumenti che usa lei, hanno bisogno di più spazio di quanto ne serva all’agente.

Che cosa daremmo a un host per l’agente
Che cosa deve gestirevCPUMemoriaDisco
Fino a qualche centinaio di macchine11 GB10 GB
Fino a qualche migliaio di macchine22 GB10 GB
Anche installazione delle macchine da rete22 GB40 GB

Che cosa chiedono le funzioni aggiuntive

Tutto ciò che precede è quanto serve a un agente per sorvegliare e controllare i suoi sistemi. Quattro delle sue capacità meritano una parola in più, e ognuna di esse si degrada in modo onesto: se un requisito non è soddisfatto, l’agente continua a fare tutto il resto e le indica quale capacità non sta offrendo.

Installare le macchine da rete
Questa richiede che l’agente si trovi sullo stesso segmento di rete della macchina da installare, perché risponde direttamente alla richiesta di avvio di quella macchina. La sua unità porta già le due capability che gli servono per le porte di avvio; sono concesse in modo circoscritto e vengono usate soltanto mentre un’installazione è in corso. Ciò che aggiunge davvero è disco: i supporti di avvio vengono messi in cache sull’host, fino a 20 GB per impostazione predefinita, scartando per primi i più vecchi.
Eseguire le automazioni
Le automazioni sono TypeScript scritto da lei, quindi vengono eseguite dentro una sandbox costruita sugli spazi dei nomi del kernel. Il runtime che serve non fa parte dell’agente: una versione fissata di Bun deve trovarsi nella directory dell’agente che appartiene a root, dove l’utente con cui gira l’agente non può sostituirla. Ubuntu 24.04 e versioni successive limitano esattamente lo spazio dei nomi che qui serve; l’agente include un profilo AppArmor che restituisce quell’unico permesso a quell’unico binario. Se manca uno dei due pezzi, l’agente lo dichiara all’avvio, non offre quella capacità e prosegue. Ogni esecuzione ottiene almeno 4 GB di spazio di indirizzamento, che è virtuale e non memoria che debba esistere davvero. Quanto costi realmente un’automazione in esecuzione sul suo host è una cosa che non abbiamo misurato.
Riavviare l’host dal pannello
Il riavvio è una questione di autorizzazione più che di privilegi: l’agente lo chiede a logind, logind lo chiede a polkit, e l’installer lascia una regola che consente esattamente due azioni per esattamente questo utente. Gli host installati prima che quella regola esistesse richiedono di eseguire l’installer ancora una volta, perché aggiornare l’agente dal pannello sostituisce il binario e nulla in /etc. Fino ad allora il pannello rifiuta e ne spiega il motivo.
Console e diagnostica
Niente da installare. Ping e traceroute usano un socket ICMP che non richiede privilegi, purché l’host lo consenta al gruppo dell’utente di sistema, come avveniva su ogni host che abbiamo misurato; dove non è così, l’agente misura invece con TCP e le indica quale metodo ha usato. La console viene ritrasmessa sulla connessione già aperta.

Installare con il comando in una riga

Nella scheda delle impostazioni dell’agente il pannello costruisce un unico comando che contiene un link di installazione. Lo esegua come root sull’host. Il link funziona una sola volta: smette di funzionare appena l’agente è registrato, e al massimo dopo 72 ore. Può crearne uno nuovo in qualsiasi momento.

shell
curl -fsSL https://app.provibr.com/api/install/<token> | sh
Tratti il link come una credenziale. Chi lo possiede può registrare questo singolo agente, esattamente come glielo permetterebbe una chiave di autenticazione. Non viene mai conservato nel pannello: ne generi uno nuovo invece di andare a cercare il precedente.
I comandi e l’output del terminale sono in inglese ovunque, nel pannello e in queste pagine. Un solo testo, una sola verità: quello che legge qui è letteralmente ciò che il suo host le restituirà.

Che cosa fa il comando, nell’ordine:

  • Controlla che sia in esecuzione come root su un host x86 a 64 bit e sceglie curl o wget, a seconda di quale sia presente.
  • Scarica binario e checksum e li verifica. Se sull’host non esiste alcuno strumento per calcolare un checksum, si ferma invece di saltare il controllo.
  • Crea l’utente di sistema e le due directory, installa il binario e colloca lo script di aggiornamento e la sua chiave pubblica, entrambi di proprietà di root, così l’utente con cui gira l’agente non può sostituirli.
  • Chiede il codice di attivazione sul terminale, recupera il file di licenza inviando quel codice in un’intestazione della richiesta e lo installa.
  • Esegue la registrazione come utente di sistema, poi installa l’unità systemd e avvia il servizio.

Verifichi il risultato sull’host:

shell
systemctl status provibr-agent
journalctl -u provibr-agent -f
Eseguire di nuovo il comando non comporta rischi, con un nuovo link: quello vecchio non funziona più da quando l’agente è registrato. Se l’host è già registrato, vengono rinnovati soltanto il binario e l’unità; l’identità, il certificato e la cassaforte delle credenziali restano intatti. Su un host senza systemd tutto viene comunque installato e registrato, e il comando indica come avviare l’agente a mano.

Installare a mano

Tutto ciò che fa il comando in una riga si riduce anche a una manciata di comandi, e il pannello li stampa già compilati con i valori attuali. Ecco come si presenta.

  1. Preparare l’host: l’utente di sistema, le directory e il binario.

    shell
    # System user without a shell and without a home directory
    id -u provibr-agent >/dev/null 2>&1 || \
      sudo useradd --system --no-create-home --shell /usr/sbin/nologin provibr-agent
    
    # Directories for configuration (license, key) and state (identity, vault)
    sudo install -d -o provibr-agent -g provibr-agent -m 0750 /etc/provibr-agent
    sudo install -d -o provibr-agent -g provibr-agent -m 0700 /var/lib/provibr-agent
    
    # The binary from the download
    sudo install -o root -g root -m 0755 ./provibr-agent-linux-x86_64 /usr/local/bin/provibr-agent
    /usr/local/bin/provibr-agent version
  2. Collocare il file di licenza. Si scarica dalla pagina dell’agente; è cifrato con il codice di attivazione e può essere scaricato una sola volta per token.

    shell
    sudo install -o root -g provibr-agent -m 0640 \
      ./provibr-<agent>.plic /etc/provibr-agent/license.plic
  3. Eseguire la registrazione come utente di sistema, con il codice di attivazione.

    shell
    sudo -u provibr-agent /usr/local/bin/provibr-agent enroll \
      --license /etc/provibr-agent/license.plic --code XXXXX-XXXXX
  4. Installare l’unità e avviare il servizio.

    shell
    sudo install -o root -g root -m 0644 provibr-agent.service /etc/systemd/system/provibr-agent.service
    sudo systemctl daemon-reload
    sudo systemctl enable --now provibr-agent
Il download del binario offre anche il suo checksum e la sua firma. Verificare la firma è il passaggio che un semplice download non dà, ed è lo stesso controllo che il passaggio di aggiornamento esegue su ogni nuova versione.

Docker e Kubernetes

L’agente funziona anche come container. L’immagine contiene lo stesso binario firmato del download per Linux e nient’altro: nessuna shell, nessun gestore di pacchetti, e gira come utente senza privilegi di root. Si connette solo in uscita, quindi non servono porte pubblicate né un service.

La registrazione avviene al primo avvio. Fornisca al container il link di installazione del pannello e il codice di attivazione; recupera il proprio file di licenza, si registra e conserva la propria identità sul volume. A ogni avvio successivo usa quell’identità e i due valori non vengono più letti.

Il link di installazione e il codice si trovano nell’ambiente del container, e quindi in docker inspect, nella cronologia della shell o nel secret di Kubernetes. È la stessa esposizione del comando su una riga, e dura finché l’agente non è registrato: da quel momento il link non funziona più. Lo tolga comunque dal secret; l’agente non lo rilegge.
Tutto ciò che l’agente conserva si trova sul volume in /var/lib/provibr-agent: il suo certificato, il vault cifrato con le Sue credenziali e la chiave di quel vault. Senza volume ogni nuovo container è un agente nuovo e non registrato. Con un volume, quel volume è l’intero segreto: tratti una sua copia come una copia dell’agente.

Docker

Il pannello mostra questo comando con il link e il codice del Suo agente già compilati. Il volume con nome prende il proprietario dall’immagine, quindi l’agente può scriverci senza chown.

shell
docker run -d --name provibr-agent --restart unless-stopped \
  --hostname provibr-agent \
  --read-only --cap-drop ALL --security-opt no-new-privileges \
  -v provibr-agent-data:/var/lib/provibr-agent \
  -e PROVIBR_INSTALL_URL='https://app.provibr.com/api/install/<token>' \
  -e PROVIBR_ACTIVATION_CODE='XXXXX-XXXXX' \
  ghcr.io/provibr/provibr-agent:latest

Oppure lo stesso come file Docker Compose:

docker-compose.yml
# Provibr agent — Docker Compose.
#
# The agent connects outbound to the platform; it needs no published ports.
# On the first start it enrolls itself with the installation link and the
# activation code below, and stores its identity on the volume. After that the
# two values are no longer read; the link expires 72 hours after it was made.
#
# Update: change the image tag, then `docker compose up -d`. The volume keeps
# identity and credentials.
services:
  provibr-agent:
    image: ghcr.io/provibr/provibr-agent:latest
    container_name: provibr-agent
    hostname: provibr-agent
    restart: unless-stopped
    read_only: true
    cap_drop:
      - ALL
    security_opt:
      - no-new-privileges:true
    environment:
      PROVIBR_INSTALL_URL: "https://app.provibr.com/api/install/<token>"
      PROVIBR_ACTIVATION_CODE: "XXXXX-XXXXX"
    volumes:
      - provibr-agent-data:/var/lib/provibr-agent

volumes:
  provibr-agent-data:
Preferisce un bind mount a un volume con nome? Crei prima la cartella e la assegni all’utente dell’agente: mkdir -p /srv/provibr-agent && chown 65532:65532 /srv/provibr-agent. Un bind mount parte di proprietà di root, e l’agente, che non gira come root, non potrebbe scriverci.

Kubernetes

Manifesti semplici, senza Helm: un namespace, un secret con il link e il codice, un volume claim e un deployment. Il pannello compila il secret per il Suo agente.

provibr-agent.yaml
# Provibr agent — Kubernetes (plain manifests, no Helm).
#
# kubectl apply -f provibr-agent.yaml
#
# The agent connects outbound to the platform; there is no Service and no port.
# On the first start it enrolls itself with the installation link and the
# activation code from the Secret, and stores its identity on the volume. After
# that the Secret is no longer read; the link expires 72 hours after it was made.
#
# Exactly one replica, and Recreate: one agent is one identity (one
# certificate). Two pods would be two agents fighting over it.
apiVersion: v1
kind: Namespace
metadata:
  name: provibr-agent
---
apiVersion: v1
kind: Secret
metadata:
  name: provibr-agent-enrollment
  namespace: provibr-agent
type: Opaque
stringData:
  PROVIBR_INSTALL_URL: "https://app.provibr.com/api/install/<token>"
  PROVIBR_ACTIVATION_CODE: "XXXXX-XXXXX"
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: provibr-agent-data
  namespace: provibr-agent
spec:
  # One pod, and only one, may mount this volume. On clusters older than
  # Kubernetes 1.29 (or storage without it), use ReadWriteOnce instead; the
  # agent also refuses to start a second time on the same data directory.
  accessModes:
    - ReadWriteOncePod
  resources:
    requests:
      storage: 1Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: provibr-agent
  namespace: provibr-agent
  labels:
    app.kubernetes.io/name: provibr-agent
spec:
  replicas: 1
  strategy:
    type: Recreate
  selector:
    matchLabels:
      app.kubernetes.io/name: provibr-agent
  template:
    metadata:
      labels:
        app.kubernetes.io/name: provibr-agent
    spec:
      hostname: provibr-agent
      automountServiceAccountToken: false
      enableServiceLinks: false
      securityContext:
        runAsNonRoot: true
        runAsUser: 65532
        runAsGroup: 65532
        fsGroup: 65532
        # Only when the volume root does not match yet; otherwise the kubelet
        # widens the modes on the private keys at every mount.
        fsGroupChangePolicy: OnRootMismatch
        seccompProfile:
          type: RuntimeDefault
        # The ping check uses an unprivileged ICMP socket. Some container
        # runtimes keep those closed, and then the check reports a source
        # error. This safe sysctl opens them for the agent's group only.
        sysctls:
          - name: net.ipv4.ping_group_range
            value: "65532 65532"
      containers:
        - name: agent
          image: ghcr.io/provibr/provibr-agent:latest
          args: ["run"]
          envFrom:
            # optional: the agent reads it on its first start only, so you can
            # delete the Secret once it is enrolled.
            - secretRef:
                name: provibr-agent-enrollment
                optional: true
          securityContext:
            allowPrivilegeEscalation: false
            readOnlyRootFilesystem: true
            capabilities:
              drop: ["ALL"]
          resources:
            requests:
              cpu: 50m
              memory: 32Mi
            limits:
              memory: 256Mi
          volumeMounts:
            - name: data
              mountPath: /var/lib/provibr-agent
      volumes:
        - name: data
          persistentVolumeClaim:
            claimName: provibr-agent-data
Mantenga replicas a 1 e la strategia su Recreate. Un agente è un’identità: due pod sullo stesso volume condividono un certificato e si escludono a vicenda dalla piattaforma, e due pod con un volume ciascuno si registrano entrambi, dopodiché solo l’ultimo è valido. Un aggiornamento progressivo farebbe proprio questo per un momento.
Il controllo ping usa un socket ICMP senza privilegi, non la capability NET_RAW. Alcuni runtime di container tengono chiusi quei socket; misurato con i socket chiusi, un ping diagnostico è ripiegato su TCP e il controllo di monitoraggio ha segnalato un errore di origine. Il manifesto li apre per il gruppo dell’agente con il sysctl sicuro net.ipv4.ping_group_range. Aggiungere NET_RAW non aiuta: l’agente non gira come root, quindi non ottiene capability effettive.

Per Kubernetes il file di licenza è la via migliore: metta nel secret il file di licenza e il codice al posto del link di installazione, monti il file nel pod e imposti PROVIBR_LICENSE_FILE sul suo percorso. Il token nel file funziona esattamente una volta, quindi il secret non contiene nulla con cui registrare l’agente una seconda volta.

Aggiornare un container

Per impostazione predefinita un container non si aggiorna da solo: l’immagine è la versione rilasciata. Il pannello indica quale immagine è attuale; la scarichi e ricrei il container, oppure imposti il nuovo tag sul deployment. Il volume conserva identità e credenziali. Gli aggiornamenti automatici saltano questi agenti; l’opzione qui sotto lo cambia.

shell
docker pull ghcr.io/provibr/provibr-agent:latest
docker stop provibr-agent && docker rm provibr-agent
# then run the same docker run command again with the new image
shell
kubectl -n provibr-agent set image deployment/provibr-agent agent=ghcr.io/provibr/provibr-agent:latest

Opzione: aggiornarsi dal volume

Imposti PROVIBR_SELF_UPDATE=volume, oppure spunti l’opzione nel pannello. Il container partecipa allora al pulsante di aggiornamento e agli aggiornamenti notturni: una nuova versione viene scaricata, la sua firma viene verificata con la chiave della piattaforma incorporata nell’immagine e viene salvata sul volume. Il container si ferma con codice di uscita 0, Docker o il kubelet lo riavvia, e il binario nell’immagine avvia la nuova versione dal volume dopo averne verificato ancora una volta la firma.

shell
docker run -d --name provibr-agent --restart unless-stopped \
  --hostname provibr-agent \
  --read-only --cap-drop ALL --security-opt no-new-privileges \
  -v provibr-agent-data:/var/lib/provibr-agent \
  -e PROVIBR_INSTALL_URL='https://app.provibr.com/api/install/<token>' \
  -e PROVIBR_ACTIVATION_CODE='XXXXX-XXXXX' \
  -e PROVIBR_SELF_UPDATE=volume \
  ghcr.io/provibr/provibr-agent:latest

Una nuova versione ha 60 secondi per raggiungere la piattaforma. Se non ci riesce, viene segnata come difettosa sul volume e non viene più avviata, e il container torna con la versione precedente. Quando distribuisce un’immagine più recente, prevale l’immagine: ciò che era salvato sul volume per la vecchia immagine non viene più usato.

Che cosa si cede: il tag dell’immagine non dice più quale versione è in esecuzione (il pannello sì), gli scanner di immagini controllano il binario nell’immagine e non quello sul volume, e uno strumento GitOps vede un pod che esegue qualcosa di diverso da quanto dice il suo manifest. Non lo consigliamo per i cluster gestiti da uno strumento GitOps.

Opzione: l’immagine tools per le automazioni

L’immagine standard non contiene il runtime delle automazioni. L’immagine tools, con il suffisso di tag -tools, contiene lo stesso agente firmato su Debian con in più il runtime delle automazioni fissato, così le automazioni possono girare nel container. Gli esempi Docker gli danno un piccolo /tmp e un limite di pids (--pids-limit); su Kubernetes /tmp è un emptyDir in memoria, e un limite di pids si imposta sul kubelet (podPidsLimit), non nel manifest. Il filesystem radice resta in sola lettura.

shell
docker run -d --name provibr-agent --restart unless-stopped \
  --hostname provibr-agent \
  --read-only --cap-drop ALL --security-opt no-new-privileges \
  --tmpfs /tmp:rw,noexec,nosuid,size=16m --pids-limit 256 \
  -v provibr-agent-data:/var/lib/provibr-agent \
  -e PROVIBR_INSTALL_URL='https://app.provibr.com/api/install/<token>' \
  -e PROVIBR_ACTIVATION_CODE='XXXXX-XXXXX' \
  ghcr.io/provibr/provibr-agent:latest-tools
Su un host Linux un’automazione gira in una sandbox con namespace propri: niente rete, nessun file dell’host, un proprio albero di processi. Un container non può creare quei namespace, quindi nell’immagine tools l’agente limita il runtime in altri due modi. Un filtro seccomp, che funziona su qualsiasi kernel, blocca ogni socket di rete (TCP e UDP), i segnali ad altri processi, l’avvio di nuovi processi e la lettura della memoria di un altro processo. Landlock (Linux 5.13 o successivo) limita i file: uno script può leggere solo il runtime e le librerie di sistema e non può scrivere nulla. L’agente verifica entrambi all’avvio; se il kernel o il profilo seccomp del runtime dei container non li consentono, le automazioni restano spente nel container. Ciò che rimane è più debole che su un host: uno script può ancora vedere quali processi girano nel container. Scegliere l’immagine tools significa scegliere questo.

Cosa non funziona in un container

Alcune funzioni hanno bisogno dell’host stesso, e un container isolato non lo ha:

  • Installazioni di rete (PXE): richiedono la rete dell’host e porte inferiori a 1024.
  • Riavviare l’host dal pannello: il container non ha accesso al sistema di init dell’host.
  • Automazioni nell’immagine standard: non contiene il runtime delle automazioni, e la sandbox che ricevono su un host richiede gli user namespace, che un container isolato non concede. L’immagine tools le esegue invece dentro il container.
  • Metriche dell’host: CPU, memoria e uptime descrivono la macchina su cui gira il container, non i limiti del container stesso.

Il codice di attivazione e il file di licenza

Quando crea un agente, il pannello mostra un codice di attivazione una sola volta. Non viene mai annotato da nessuna parte, nemmeno come hash. È il codice a cifrare il file di licenza, quindi il file che si trova sul suo host è cifrato con qualcosa che su quell’host non c’è.

Ha perso il codice o il file? Generi un nuovo token nella pagina dell’agente. Questo invalida il precedente, compreso un file di licenza già in viaggio, e le fornisce un codice nuovo e un nuovo download.

Il file di licenza in sé non contiene alcun segreto sulla sua infrastruttura. Indica all’agente a quale piattaforma appartiene, di quale autorità di certificazione fidarsi e con quale token monouso registrarsi.

Gestire gli agenti nel pannello

L’elenco degli agenti mostra tutto insieme: stato, versione, se è connesso in questo momento, quando è stato visto l’ultima volta e quando scade il suo certificato.

  • Il nome e la descrizione si possono cambiare e sono soltanto etichette. Un nome già presente in un file di licenza consegnato resta com’era: è un dettaglio estetico e non è un motivo per registrare di nuovo l’agente.
  • Lo stato di connessione viene letto da un segnale in tempo reale con una scadenza breve, non dall’ultima volta che è stata scritta una riga. Se non è possibile stabilirlo, il pannello lo dice invece di sostenere che il suo agente è fuori servizio.
  • La versione e il sistema operativo sono quelli riferiti dall’agente all’ultima connessione. Un agente che non si è mai connesso mostra un trattino invece di un’ipotesi.
  • Selezioni più agenti nell’elenco per aggiornarli in un’unica volta. Il pulsante conta soltanto gli agenti che possono essere davvero aggiornati in questo momento, così prima di fare clic si vede già che dodici selezionati significano tre aggiornati.
  • L’avanzamento viene letto dal comando in esecuzione, non da qualcosa che il browser ha memorizzato, così un ricaricamento lo riprende da dove era arrivato.

I tre stati

In attesa di registrazione
Creato nel pannello, non ancora registrato. Ha un token e un codice di attivazione che lo aspettano, e non ha ancora un certificato.
Registrato
Ha un proprio certificato e può connettersi. È l’unico stato in cui gli si può assegnare del lavoro, e l’unico in cui non può essere eliminato.
Revocato
Ritirato da lei. La sua sessione viene chiusa, i token ancora in sospeso decadono e non può più rientrare.

Revocare un agente

La revoca è l’interruttore a cui ricorrere quando un host è compromesso, viene dismesso o semplicemente non è più suo. Ha effetto senza che debba toccare l’host.

  • Una sessione in corso viene chiusa entro un minuto. All’agente viene comunicato il motivo, invece di essere semplicemente scollegato.
  • L’agente cancella poi la propria identità, il proprio certificato e la cassaforte delle credenziali. Le credenziali del suo hypervisor spariscono da quell’host.
  • I token di registrazione ancora in sospeso decadono, quindi un file di licenza già scaricato non serve per rientrare.
Non si può annullare. Un agente revocato per errore va creato di nuovo, installato di nuovo e dotato di nuovo delle sue credenziali.

Eliminare un agente

L’eliminazione rimuove l’agente dal pannello. È un passaggio distinto dalla revoca ed è volutamente esigente su quando è consentita.

  • Un agente registrato non può essere eliminato. Lo revochi prima, perché altrimenti cancellerebbe la traccia di qualcosa che è ancora connesso.
  • Nemmeno un agente con integrazioni o server collegati può essere eliminato, e il rifiuto indica entrambi i conteggi. Quelle righe dipendono dall’agente, quindi un solo clic si porterebbe via il suo parco macchine.
  • Se l’agente non ha mai eseguito un comando e non ha mai avuto un server, la riga sparisce davvero. Altrimenti viene nascosto ovunque, ma la cronologia dei suoi comandi viene conservata, perché un registro di audit che si può cancellare eliminandone il soggetto non è un registro di audit.

Rimuoverlo dall’host

La revoca cancella i segreti dell’agente ma lascia i file. Per ripulire l’host il pannello offre un secondo comando in una riga e gli stessi quattro passaggi a mano.

Questo script non revoca nulla. Prima la revoca nel pannello, poi la pulizia dell’host, in quest’ordine. Al contrario resta un host che conserva ancora un’identità valida.
  1. Arrestare il servizio, disattivarlo e rimuovere l’unità.

    shell
    # Stop the service and forget the unit
    sudo systemctl disable --now provibr-agent
    sudo rm -f /etc/systemd/system/provibr-agent.service
    sudo systemctl daemon-reload
    sudo systemctl reset-failed provibr-agent 2>/dev/null || true
  2. Rimuovere il binario, lo script di aggiornamento e la chiave di aggiornamento.

    shell
    # Binary, update script and the trust anchor of the update step
    sudo rm -f /usr/local/bin/provibr-agent
    sudo rm -f /usr/local/lib/provibr-agent/apply-update.sh /usr/local/lib/provibr-agent/license-signing.pub
    
    # The polkit rule that allowed host.reboot (and its 0.105 fallback)
    sudo rm -f /etc/polkit-1/rules.d/49-provibr-agent-reboot.rules /etc/polkit-1/localauthority/50-local.d/49-provibr-agent-reboot.pkla
    
    # Only if nothing else lives in there
    sudo rmdir /usr/local/lib/provibr-agent 2>/dev/null || true
  3. Rimuovere i dati. È il passaggio che conta: qui si trovano la licenza, la chiave della cassaforte e le credenziali cifrate.

    shell
    # License and vault key
    sudo rm -rf /etc/provibr-agent
    
    # Identity, encrypted credential vault, outbox and staged updates
    sudo rm -rf /var/lib/provibr-agent
  4. Rimuovere l’utente di sistema.

    shell
    sudo userdel provibr-agent
    sudo groupdel provibr-agent 2>/dev/null || true

Dopodiché entrambi questi comandi non devono stampare proprio nulla:

shell
ls -d /etc/provibr-agent /var/lib/provibr-agent /usr/local/lib/provibr-agent /usr/local/bin/provibr-agent /etc/systemd/system/provibr-agent.service /etc/polkit-1/rules.d/49-provibr-agent-reboot.rules /etc/polkit-1/localauthority/50-local.d/49-provibr-agent-reboot.pkla 2>/dev/null
id provibr-agent 2>/dev/null
# Both commands should print nothing at all