TLS/SSL: configuração e renovação automática

1. Fundamentos de TLS/SSL no ecossistema Go

1.1. O pacote padrão crypto/tls

O Go oferece suporte nativo a TLS através do pacote crypto/tls. Os tipos fundamentais são:

  • tls.Config: estrutura que define todas as configurações de TLS, incluindo certificados, cifras e versões permitidas.
  • tls.Certificate: representa um par certificado-chave privada, carregado via tls.LoadX509KeyPair ou gerado dinamicamente.
import (
    "crypto/tls"
    "log"
)

func loadCert() tls.Certificate {
    cert, err := tls.LoadX509KeyPair("server.crt", "server.key")
    if err != nil {
        log.Fatal(err)
    }
    return cert
}

1.2. Diferenças entre TLS 1.2 e TLS 1.3

O Go 1.22+ suporta TLS 1.3 como padrão. As principais diferenças:

  • TLS 1.2: handshake completo com 2 RTT (round trips), suporte a cifras configuráveis.
  • TLS 1.3: handshake reduzido a 1 RTT, remoção de cifras inseguras, Perfect Forward Secrecy obrigatório.

Por padrão, tls.Config{} habilita TLS 1.2 e 1.3. Para restringir versões:

config := &tls.Config{
    MinVersion: tls.VersionTLS12,
    MaxVersion: tls.VersionTLS13,
}

1.3. Geração de certificados autoassinados para desenvolvimento

Para ambientes de desenvolvimento, podemos gerar certificados programaticamente:

import (
    "crypto/rand"
    "crypto/rsa"
    "crypto/x509"
    "crypto/x509/pkix"
    "math/big"
    "time"
)

func generateSelfSignedCert() (tls.Certificate, error) {
    privateKey, _ := rsa.GenerateKey(rand.Reader, 2048)
    template := x509.Certificate{
        SerialNumber: big.NewInt(1),
        Subject: pkix.Name{CommonName: "localhost"},
        NotBefore:   time.Now(),
        NotAfter:    time.Now().Add(365 * 24 * time.Hour),
        KeyUsage:    x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
        ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
    }
    certDER, _ := x509.CreateCertificate(rand.Reader, &template, &template, &privateKey.PublicKey, privateKey)
    return tls.Certificate{
        Certificate: [][]byte{certDER},
        PrivateKey:  privateKey,
    }, nil
}

2. Configuração básica de servidor HTTPS

2.1. Servidor com TLSConfig customizado

func main() {
    mux := http.NewServeMux()
    mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        w.Write([]byte("HTTPS Server"))
    })

    server := &http.Server{
        Addr:      ":443",
        Handler:   mux,
        TLSConfig: &tls.Config{
            MinVersion: tls.VersionTLS12,
            CipherSuites: []uint16{
                tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
                tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
            },
        },
    }

    log.Fatal(server.ListenAndServeTLS("server.crt", "server.key"))
}

2.2. Redirecionamento HTTP → HTTPS

func redirectToHTTPS(w http.ResponseWriter, r *http.Request) {
    http.Redirect(w, r, "https://"+r.Host+r.URL.String(), http.StatusMovedPermanently)
}

func main() {
    go http.ListenAndServe(":80", http.HandlerFunc(redirectToHTTPS))
    // ... servidor HTTPS
}

3. Configuração avançada de TLS

3.1. Controle de cifras e curvas elípticas

config := &tls.Config{
    CurvePreferences: []tls.CurveID{
        tls.CurveP256,
        tls.X25519,
    },
    CipherSuites: []uint16{
        tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
        tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
    },
    PreferServerCipherSuites: true,
}

3.2. HSTS via middleware

func hstsMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        w.Header().Set("Strict-Transport-Security", "max-age=31536000; includeSubDomains")
        next.ServeHTTP(w, r)
    })
}

3.3. Callback GetCertificate para renovação dinâmica

config := &tls.Config{
    GetCertificate: func(hello *tls.ClientHelloInfo) (*tls.Certificate, error) {
        cert, err := loadCertificateForDomain(hello.ServerName)
        return cert, err
    },
}

4. Geração e gerenciamento com Let's Encrypt

4.1. Protocolo ACME

O ACME (Automatic Certificate Management Environment) automatiza a emissão e renovação de certificados. O Go oferece suporte através do pacote golang.org/x/crypto/acme/autocert.

4.2. Uso de autocert.Manager

import (
    "golang.org/x/crypto/acme/autocert"
    "net/http"
)

func main() {
    m := &autocert.Manager{
        Cache:      autocert.DirCache("certs"),
        Prompt:     autocert.AcceptTOS,
        HostPolicy: autocert.HostWhitelist("example.com", "www.example.com"),
    }

    server := &http.Server{
        Addr:      ":443",
        TLSConfig: &tls.Config{GetCertificate: m.GetCertificate},
    }

    // Redirecionamento HTTP
    go http.ListenAndServe(":80", m.HTTPHandler(nil))

    log.Fatal(server.ListenAndServeTLS("", ""))
}

4.3. Configuração de cache e políticas

manager := &autocert.Manager{
    Cache: autocert.DirCache("/var/lib/letsencrypt"),
    Prompt: func(tosURL string) bool {
        log.Printf("Aceitando TOS: %s", tosURL)
        return true
    },
    HostPolicy: func(ctx context.Context, host string) error {
        if host == "api.example.com" || host == "admin.example.com" {
            return nil
        }
        return fmt.Errorf("domínio não autorizado: %s", host)
    },
    Email: "admin@example.com", // Para notificações
}

5. Renovação automática em produção

5.1. Estratégia com autocert

O autocert.Manager gerencia renovação automaticamente:

m := &autocert.Manager{
    Cache:   autocert.DirCache("certs"),
    Prompt:  autocert.AcceptTOS,
    RenewBefore: 30 * 24 * time.Hour, // Renova 30 dias antes
}

5.2. Cache persistente

  • autocert.DirCache: armazena certificados em disco
  • Cache customizado: implemente a interface autocert.Cache para Redis, S3 etc.
type RedisCache struct {
    client *redis.Client
}

func (c *RedisCache) Get(ctx context.Context, key string) ([]byte, error) {
    return c.client.Get(ctx, key).Bytes()
}

func (c *RedisCache) Put(ctx context.Context, key string, data []byte) error {
    return c.client.Set(ctx, key, data, 90*24*time.Hour).Err()
}

func (c *RedisCache) Delete(ctx context.Context, key string) error {
    return c.client.Del(ctx, key).Err()
}

5.3. Monitoramento de expiração

func monitorCertExpiration(certFile string) {
    cert, err := tls.LoadX509KeyPair(certFile, certFile)
    if err != nil {
        log.Printf("Erro ao carregar certificado: %v", err)
        return
    }
    x509Cert, _ := x509.ParseCertificate(cert.Certificate[0])
    daysUntilExpiry := time.Until(x509Cert.NotAfter).Hours() / 24
    log.Printf("Certificado expira em %.0f dias", daysUntilExpiry)

    if daysUntilExpiry < 30 {
        metrics.CertExpiryWarning.WithLabelValues(x509Cert.Subject.CommonName).Set(daysUntilExpiry)
    }
}

6. Integração com frameworks

6.1. Gin e Gorilla Mux

// Com Gin
r := gin.Default()
r.Use(hstsMiddleware)
server := &http.Server{
    Addr:      ":443",
    Handler:   r,
    TLSConfig: &tls.Config{GetCertificate: autocertManager.GetCertificate},
}

// Com Gorilla Mux
r := mux.NewRouter()
r.Use(hstsMiddleware)
server.ListenAndServeTLS("", "")

6.2. WebSocket com TLS

import "github.com/gorilla/websocket"

upgrader := websocket.Upgrader{
    CheckOrigin: func(r *http.Request) bool { return true },
}

http.HandleFunc("/ws", func(w http.ResponseWriter, r *http.Request) {
    conn, _ := upgrader.Upgrade(w, r, nil)
    defer conn.Close()
    // ...
})
// Servir sobre HTTPS com autocert

7. Boas práticas, testes e troubleshooting

7.1. Testes com httptest.NewTLSServer

func TestHTTPSHandler(t *testing.T) {
    server := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        w.Write([]byte("OK"))
    }))
    defer server.Close()

    // Cliente que ignora certificado autoassinado
    client := server.Client()
    resp, _ := client.Get(server.URL)
    // ...
}

7.2. OCSP Stapling

config := &tls.Config{
    GetCertificate: func(hello *tls.ClientHelloInfo) (*tls.Certificate, error) {
        cert, err := loadCert()
        if err == nil {
            // Verificar e anexar OCSP response
            cert.OCSPStaple = fetchOCSPResponse(cert)
        }
        return cert, err
    },
}

7.3. Ferramentas de depuração

# Verificar handshake
openssl s_client -connect example.com:443 -tls1_3

# Analisar certificado com certigo
certigo connect example.com:443

Referências