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
- Ogni istanza del log invia periodicamente il suo ultimo Signed Tree Head (STH) a un insieme di peer.
- I peer confrontano l'STH ricevuto con il proprio.
- Se vengono rilevate differenze, i peer richiedono e verificano le prove di coerenza.
- 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.