1
0
Fork 0

OCSP stapling

This commit is contained in:
Alessandro Chitolina 2025-06-06 17:44:04 +02:00 committed by GitHub
parent 2949995abc
commit b39ee8ede5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
30 changed files with 1576 additions and 178 deletions

View file

@ -3,7 +3,9 @@ package integration
import (
"bufio"
"bytes"
"crypto"
"crypto/rand"
"crypto/tls"
"encoding/json"
"fmt"
"io"
@ -24,6 +26,7 @@ import (
"github.com/stretchr/testify/suite"
"github.com/traefik/traefik/v3/integration/try"
"github.com/traefik/traefik/v3/pkg/config/dynamic"
"golang.org/x/crypto/ocsp"
)
// SimpleSuite tests suite.
@ -1598,6 +1601,132 @@ func (s *SimpleSuite) TestMaxHeaderBytes() {
}
}
func (s *SimpleSuite) TestSimpleOCSP() {
defaultCert, err := tls.LoadX509KeyPair("fixtures/ocsp/default.crt", "fixtures/ocsp/default.key")
require.NoError(s.T(), err)
serverCert, err := tls.LoadX509KeyPair("fixtures/ocsp/server.crt", "fixtures/ocsp/server.key")
require.NoError(s.T(), err)
defaultOCSPResponseTmpl := ocsp.Response{
SerialNumber: defaultCert.Leaf.SerialNumber,
Status: ocsp.Good,
ThisUpdate: defaultCert.Leaf.NotBefore,
NextUpdate: defaultCert.Leaf.NotAfter,
}
defaultOCSPResponse, err := ocsp.CreateResponse(defaultCert.Leaf, defaultCert.Leaf, defaultOCSPResponseTmpl, defaultCert.PrivateKey.(crypto.Signer))
require.NoError(s.T(), err)
serverOCSPResponseTmpl := ocsp.Response{
SerialNumber: serverCert.Leaf.SerialNumber,
Status: ocsp.Good,
ThisUpdate: serverCert.Leaf.NotBefore,
NextUpdate: serverCert.Leaf.NotAfter,
}
serverOCSPResponse, err := ocsp.CreateResponse(serverCert.Leaf, serverCert.Leaf, serverOCSPResponseTmpl, serverCert.PrivateKey.(crypto.Signer))
require.NoError(s.T(), err)
responderCalled := make(chan struct{})
responder := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
ct := req.Header.Get("Content-Type")
assert.Equal(s.T(), "application/ocsp-request", ct)
reqBytes, err := io.ReadAll(req.Body)
require.NoError(s.T(), err)
ocspReq, err := ocsp.ParseRequest(reqBytes)
require.NoError(s.T(), err)
var ocspResponse []byte
switch ocspReq.SerialNumber.String() {
case defaultCert.Leaf.SerialNumber.String():
ocspResponse = defaultOCSPResponse
case serverCert.Leaf.SerialNumber.String():
ocspResponse = serverOCSPResponse
default:
s.T().Fatalf("Unexpected OCSP request for serial number: %s", ocspReq.SerialNumber)
}
rw.Header().Set("Content-Type", "application/ocsp-response")
_, err = rw.Write(ocspResponse)
require.NoError(s.T(), err)
responderCalled <- struct{}{}
}))
s.T().Cleanup(responder.Close)
file := s.adaptFile("fixtures/ocsp/simple.toml", struct {
ResponderURL string
}{responder.URL})
s.traefikCmd(withConfigFile(file))
select {
case <-responderCalled:
case <-time.After(5 * time.Second):
s.T().Fatal("OCSP responder was not called")
}
select {
case <-responderCalled:
case <-time.After(5 * time.Second):
s.T().Fatal("OCSP responder was not called")
}
// Check that the response is stapled.
// Create a TLS client configuration that checks for OCSP stapling for the default cert.
var verifyCallCount int
clientConfig := &tls.Config{
InsecureSkipVerify: true,
ServerName: "unknown",
VerifyConnection: func(state tls.ConnectionState) error {
s.T().Helper()
verifyCallCount++
assert.Equal(s.T(), "default.local", state.PeerCertificates[0].Subject.CommonName)
assert.Equal(s.T(), defaultOCSPResponse, state.OCSPResponse)
return nil
},
}
// Connect to the server and verify OCSP stapling.
conn, err := tls.DialWithDialer(&net.Dialer{Timeout: 5 * time.Second}, "tcp", "127.0.0.1:8000", clientConfig)
require.NoError(s.T(), err)
s.T().Cleanup(func() {
_ = conn.Close()
})
assert.Equal(s.T(), 1, verifyCallCount)
// Create a TLS client configuration that checks for OCSP stapling for a cert in the store.
verifyCallCount = 0
clientConfig = &tls.Config{
InsecureSkipVerify: true,
ServerName: "server.local",
VerifyConnection: func(state tls.ConnectionState) error {
s.T().Helper()
verifyCallCount++
assert.Equal(s.T(), "server.local", state.PeerCertificates[0].Subject.CommonName)
assert.Equal(s.T(), serverOCSPResponse, state.OCSPResponse)
return nil
},
}
// Connect to the server and verify OCSP stapling.
conn, err = tls.DialWithDialer(&net.Dialer{Timeout: 5 * time.Second}, "tcp", "127.0.0.1:8000", clientConfig)
require.NoError(s.T(), err)
s.T().Cleanup(func() {
_ = conn.Close()
})
assert.Equal(s.T(), 1, verifyCallCount)
}
func (s *SimpleSuite) TestSanitizePath() {
s.createComposeProject("base")