Costruiremo un sistema di log di Certificate Transparency per PKI interne utilizzando Kafka come coda di messaggi e Trillian come albero di Merkle a sola aggiunta. Questa configurazione consente un audit efficiente delle emissioni di certificati, la rilevazione di certificati emessi erroneamente e controlli di coerenza basati su gossip.

Perché preoccuparsi dei log CT interni?

Prima di addentrarci nei dettagli tecnici, affrontiamo la questione principale: perché dovremmo preoccuparci di implementare i log CT per le PKI interne?

  • Rilevare emissioni di certificati non autorizzate
  • Garantire la conformità con le politiche interne
  • Migliorare le capacità di risposta agli incidenti
  • Rafforzare la postura complessiva di sicurezza

Pensatelo come un approccio di fiducia ma verifica per la vostra CA interna. Vi fidate della vostra CA, ma volete anche mantenerla onesta.

I Mattoni: Kafka e Trillian

Per implementare il nostro sistema di log CT interno, utilizzeremo due potenti strumenti:

1. Apache Kafka

Kafka servirà come coda di messaggi, gestendo l'ingestione ad alta velocità dei dati dei certificati. È come un nastro trasportatore per i vostri certificati, assicurando che vengano elaborati in ordine e con alta affidabilità.

2. Trillian

Trillian, sviluppato da Google, è la nostra implementazione di un albero di Merkle a sola aggiunta. È la spina dorsale del nostro log CT, fornendo garanzie crittografiche di integrità del log e consentendo prove di inclusione efficienti.

Panoramica dell'Architettura

Analizziamo l'architettura del nostro sistema:


+----------------+     +--------+     +----------+     +---------+
|  Internal CA   | --> | Kafka  | --> | Trillian | --> | Auditor |
+----------------+     +--------+     +----------+     +---------+
        |                                 |
        |                                 |
        v                                 v
+----------------+              +--------------------+
| Monitor/Alert  |              | Gossip Participants|
+----------------+              +--------------------+

1. La CA interna invia i certificati appena emessi a Kafka.

2. Kafka garantisce una consegna ordinata e affidabile a Trillian.

3. Trillian aggiunge i certificati al suo albero di Merkle.

4. Gli auditor possono verificare la coerenza del log e controllare i certificati sospetti.

5. I sistemi di monitoraggio avvisano su eventuali anomalie.

6. I partecipanti al gossip garantiscono la coerenza del log su più istanze.

Implementazione del Sistema

Passo 1: Configurazione di Kafka

Per prima cosa, configuriamo il nostro cluster Kafka. Useremo Docker per semplicità:


version: '3'
services:
  zookeeper:
    image: confluentinc/cp-zookeeper:latest
    environment:
      ZOOKEEPER_CLIENT_PORT: 2181
      ZOOKEEPER_TICK_TIME: 2000

  kafka:
    image: confluentinc/cp-kafka:latest
    depends_on:
      - zookeeper
    ports:
      - 9092:9092
    environment:
      KAFKA_BROKER_ID: 1
      KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
      KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:29092,PLAINTEXT_HOST://localhost:9092
      KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT
      KAFKA_INTER_BROKER_LISTENER_NAME: PLAINTEXT
      KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1

Esegui questo comando con docker-compose up -d, e avrai un cluster Kafka pronto per l'ingestione dei certificati.

Passo 2: Configurazione di Trillian

Ora, configuriamo Trillian. Dovremo compilarlo dal sorgente:


git clone https://github.com/google/trillian.git
cd trillian
go build ./cmd/trillian_log_server
go build ./cmd/trillian_log_signer

Crea un database MySQL per Trillian:


CREATE DATABASE trillian;

Inizializza lo schema del database:


mysql -u root -p trillian < storage/mysql/schema/storage.sql

Ora, avvia il server di log Trillian e il signer:


./trillian_log_server --logtostderr ...
./trillian_log_signer --logtostderr ...

Passo 3: Implementazione del Sottomettitore di Certificati

Abbiamo bisogno di un componente per inviare i certificati dalla nostra CA interna a Kafka. Ecco una semplice implementazione in Go:


package main

import (
	"context"
	"crypto/x509"
	"encoding/pem"
	"github.com/segmentio/kafka-go"
)

func submitCertificate(cert *x509.Certificate) error {
	w := kafka.NewWriter(kafka.WriterConfig{
		Brokers: []string{"localhost:9092"},
		Topic:   "ct-log-entries",
	})

	pemCert := pem.EncodeToMemory(&pem.Block{
		Type:  "CERTIFICATE",
		Bytes: cert.Raw,
	})

	return w.WriteMessages(context.Background(),
		kafka.Message{
			Key:   []byte(cert.SerialNumber.String()),
			Value: pemCert,
		},
	)
}

Passo 4: Elaborazione dei Certificati con Trillian

Ora, dobbiamo consumare i certificati da Kafka e aggiungerli a Trillian:


package main

import (
	"context"
	"github.com/google/trillian"
	"github.com/segmentio/kafka-go"
)

func processCertificates(logID int64) {
	r := kafka.NewReader(kafka.ReaderConfig{
		Brokers: []string{"localhost:9092"},
		Topic:   "ct-log-entries",
		GroupID: "trillian-processor",
	})

	client, err := trillian.NewTrillianLogClient(...)
	if err != nil {
		// Gestisci errore
	}

	for {
		msg, err := r.ReadMessage(context.Background())
		if err != nil {
			// Gestisci errore
			continue
		}

		leaf := &trillian.LogLeaf{
			LeafValue: msg.Value,
		}

		_, err = client.QueueLeaf(context.Background(), &trillian.QueueLeafRequest{
			LogId: logID,
			Leaf:  leaf,
		})
		if err != nil {
			// Gestisci errore
		}
	}
}

Implementazione della Coerenza Basata su Gossip

Per garantire la coerenza del nostro log CT su più istanze, implementeremo un protocollo gossip. Questo consente alle diverse istanze del log di confrontare le loro visioni del log e rilevare eventuali discrepanze.

Panoramica del Protocollo Gossip

  1. Ogni istanza del log invia periodicamente il suo ultimo Signed Tree Head (STH) a un insieme di peer.
  2. I peer confrontano l'STH ricevuto con il proprio.
  3. Se vengono rilevate differenze, i peer richiedono e verificano le prove di coerenza.
  4. Eventuali incoerenze attivano avvisi per ulteriori indagini.

Ecco una semplice implementazione del protocollo gossip:


package main

import (
	"context"
	"github.com/google/trillian"
	"github.com/google/trillian/client"
	"time"
)

type GossipParticipant struct {
	LogID     int64
	Client    trillian.TrillianLogClient
	Verifier  *client.LogVerifier
	Peers     []string
}

func (g *GossipParticipant) RunGossip() {
	ticker := time.NewTicker(5 * time.Minute)
	for range ticker.C {
		g.gossipRound()
	}
}

func (g *GossipParticipant) gossipRound() {
	ctx := context.Background()
	sth, err := g.Client.GetLatestSignedLogRoot(ctx, &trillian.GetLatestSignedLogRootRequest{LogId: g.LogID})
	if err != nil {
		// Gestisci errore
		return
	}

	for _, peer := range g.Peers {
		peerSTH := getPeerSTH(peer) // Implementa questa funzione per ottenere l'STH da un peer
		if !g.Verifier.VerifyRoot(sth.SignedLogRoot, peerSTH.SignedLogRoot) {
			// Gli STH non corrispondono, richiedi la prova di coerenza
			proof, err := g.Client.GetConsistencyProof(ctx, &trillian.GetConsistencyProofRequest{
				LogId:          g.LogID,
				FirstTreeSize:  peerSTH.TreeSize,
				SecondTreeSize: sth.TreeSize,
			})
			if err != nil {
				// Gestisci errore
				continue
			}

			// Verifica la prova di coerenza
			if !g.Verifier.VerifyConsistencyProof(proof) {
				// Incoerenza rilevata! Genera un avviso
				raiseInconsistencyAlert(g.LogID, peer)
			}
		}
	}
}

func raiseInconsistencyAlert(logID int64, peer string) {
	// Implementa il meccanismo di avviso (ad esempio, invia email, attiva la risposta agli incidenti)
}

Audit e Monitoraggio

Con il nostro sistema di log CT in atto, dobbiamo implementare audit e monitoraggio per rilevare eventuali attività sospette o incoerenze.

Implementazione di un Auditor

Il compito dell'auditor è controllare periodicamente il log per eventuali certificati che violano le politiche o appaiono sospetti. Ecco una semplice implementazione:


package main

import (
	"context"
	"crypto/x509"
	"encoding/pem"
	"github.com/google/trillian"
	"time"
)

type Auditor struct {
	LogID  int64
	Client trillian.TrillianLogClient
}

func (a *Auditor) AuditLog() {
	ticker := time.NewTicker(1 * time ora)
	for range ticker.C {
		a.auditRound()
	}
}

func (a *Auditor) auditRound() {
	ctx := context.Background()
	leaves, err := a.Client.GetLeavesByRange(ctx, &trillian.GetLeavesByRangeRequest{
		LogId: a.LogID,
		StartIndex: 0,
		Count: 1000, // Regola secondo necessità
	})
	if err != nil {
		// Gestisci errore
		return
	}

	for _, leaf := range leaves.Leaves {
		block, _ := pem.Decode(leaf.LeafValue)
		if block == nil {
			// Gestisci errore
			continue
		}

		cert, err := x509.ParseCertificate(block.Bytes)
		if err != nil {
			// Gestisci errore
			continue
		}

		if isSuspiciousCertificate(cert) {
			raiseSuspiciousCertificateAlert(cert)
		}
	}
}

func isSuspiciousCertificate(cert *x509.Certificate) bool {
	// Implementa controlli per certificati sospetti
	// Ad esempio:
	// - Emittenti inaspettati
	// - Periodi di validità insoliti
	// - Usi delle chiavi proibiti
	// - SAN inaspettati
	return false
}

func raiseSuspiciousCertificateAlert(cert *x509.Certificate) {
	// Implementa il meccanismo di avviso per certificati sospetti
}

Monitoraggio e Avvisi

Per tenere d'occhio la salute e le prestazioni del nostro sistema di log CT, dovremmo implementare un monitoraggio e avvisi completi. Ecco alcune metriche chiave da monitorare:

  • Dimensione del log e tasso di crescita
  • Latenza delle sottomissioni di certificati
  • Tassi di errore nell'elaborazione dei certificati
  • Controlli di coerenza del protocollo gossip
  • Risultati e avvisi dell'auditor

Puoi utilizzare strumenti come Prometheus e Grafana per raccogliere e visualizzare queste metriche. Ecco un esempio di come esporre alcune metriche di base utilizzando la libreria client di Prometheus per Go:


package main

import (
	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promauto"
)

var (
	certificatesProcessed = promauto.NewCounter(prometheus.CounterOpts{
		Name: "ct_log_certificates_processed_total",
		Help: "Il numero totale di certificati elaborati",
	})

	processingLatency = promauto.NewHistogram(prometheus.HistogramOpts{
		Name: "ct_log_processing_latency_seconds",
		Help: "La latenza dell'elaborazione dei certificati",
		Buckets: prometheus.DefBuckets,
	})

	gossipInconsistencies = promauto.NewCounter(prometheus.CounterOpts{
		Name: "ct_log_gossip_inconsistencies_total",
		Help: "Il numero totale di incoerenze gossip rilevate",
	})
)

// Usa queste metriche nel tuo codice:
// certificatesProcessed.Inc()
// processingLatency.Observe(duration.Seconds())
// gossipInconsistencies.Inc()

Conclusione: Fidati, ma Verifica (e Registra)

Implementare un log di Certificate Transparency per la tua PKI interna potrebbe sembrare eccessivo a prima vista. Ma nel mondo della cybersecurity, dove la fiducia è fondamentale e le conseguenze di una violazione possono essere catastrofiche, è un piccolo prezzo da pagare per la tranquillità.

Sfruttando la potenza di Kafka per l'elaborazione di messaggi ad alta velocità e Trillian per l'integrità crittografica, abbiamo creato un sistema robusto che può:

  • Rilevare rapidamente certificati non autorizzati o emessi erroneamente
  • Fornire una traccia di audit immutabile di tutte le emissioni di certificati
  • Garantire la coerenza tra più istanze di log tramite protocolli gossip
  • Abilitare il monitoraggio proattivo e l'allerta per attività sospette

Ricorda, nel regno delle PKI, la fiducia è buona, ma la verifica è migliore. Implementando questo sistema di log CT interno, non stai solo migliorando la tua postura di sicurezza; stai costruendo una base di fiducia verificabile che può resistere al controllo degli audit e alla prova del tempo.

"In God we trust. All others must bring data." - W. Edwards Deming

Ora vai avanti e registra quei certificati! Il tuo futuro te stesso (e i tuoi auditor) ti ringrazieranno.

Ulteriori Letture