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 viatls.LoadX509KeyPairou 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.Cachepara 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
- Documentação oficial crypto/tls — Referência completa do pacote TLS da biblioteca padrão Go
- Pacote autocert (golang.org/x/crypto) — Documentação oficial do gerenciador automático de certificados ACME
- Let's Encrypt - Como funciona o ACME — Explicação detalhada do protocolo ACME e desafios de validação
- Guia de segurança TLS do Mozilla — Recomendações atualizadas de cifras e configurações seguras
- Go by Example: TLS — Exemplos práticos de configuração TLS em servidores Go
- Blog Cloudflare: TLS 1.3 no Go — Artigo técnico sobre implementação de TLS 1.3 no ecossistema Go