backport of commit bcbd45b380d2cf776cb3cd920f03291301cee998 (#24198)
Co-authored-by: Steven Clark <steven.clark@hashicorp.com>
This commit is contained in:
parent
ff81ab8a30
commit
a73098c99e
|
@ -0,0 +1,3 @@
|
||||||
|
```release-note:bug
|
||||||
|
auth/cert: Handle errors related to expired OCSP server responses
|
||||||
|
```
|
|
@ -517,6 +517,11 @@ func (c *Client) GetRevocationStatus(ctx context.Context, subject, issuer *x509.
|
||||||
}
|
}
|
||||||
if isValidOCSPStatus(ret.code) {
|
if isValidOCSPStatus(ret.code) {
|
||||||
ocspStatuses[i] = ret
|
ocspStatuses[i] = ret
|
||||||
|
} else if ret.err != nil {
|
||||||
|
// This check needs to occur after the isValidOCSPStatus as the unknown
|
||||||
|
// status also sets an err value within ret.
|
||||||
|
errors[i] = ret.err
|
||||||
|
return ret.err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -568,8 +573,12 @@ func (c *Client) GetRevocationStatus(ctx context.Context, subject, issuer *x509.
|
||||||
if (ret == nil || ret.code == ocspStatusUnknown) && firstError != nil {
|
if (ret == nil || ret.code == ocspStatusUnknown) && firstError != nil {
|
||||||
return nil, firstError
|
return nil, firstError
|
||||||
}
|
}
|
||||||
// otherwise ret should contain a response for the overall request
|
// An extra safety in case ret and firstError are both nil
|
||||||
|
if ret == nil {
|
||||||
|
return nil, fmt.Errorf("failed to extract a known response code or error from the OCSP server")
|
||||||
|
}
|
||||||
|
|
||||||
|
// otherwise ret should contain a response for the overall request
|
||||||
if !isValidOCSPStatus(ret.code) {
|
if !isValidOCSPStatus(ret.code) {
|
||||||
return ret, nil
|
return ret, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,14 +6,20 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"crypto"
|
"crypto"
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"crypto/elliptic"
|
||||||
|
"crypto/rand"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
|
"crypto/x509/pkix"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"math/big"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
"net/url"
|
"net/url"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
@ -21,6 +27,7 @@ import (
|
||||||
"github.com/hashicorp/go-hclog"
|
"github.com/hashicorp/go-hclog"
|
||||||
"github.com/hashicorp/go-retryablehttp"
|
"github.com/hashicorp/go-retryablehttp"
|
||||||
lru "github.com/hashicorp/golang-lru"
|
lru "github.com/hashicorp/golang-lru"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
"golang.org/x/crypto/ocsp"
|
"golang.org/x/crypto/ocsp"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -201,6 +208,88 @@ func TestUnitCheckOCSPResponseCache(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestUnitExpiredOCSPResponse(t *testing.T) {
|
||||||
|
rootCaKey, rootCa, leafCert := createCaLeafCerts(t)
|
||||||
|
|
||||||
|
expiredOcspResponse := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
now := time.Now()
|
||||||
|
ocspRes := ocsp.Response{
|
||||||
|
SerialNumber: big.NewInt(2),
|
||||||
|
ThisUpdate: now.Add(-1 * time.Hour),
|
||||||
|
NextUpdate: now.Add(-30 * time.Minute),
|
||||||
|
Status: ocsp.Good,
|
||||||
|
}
|
||||||
|
response, err := ocsp.CreateResponse(rootCa, rootCa, ocspRes, rootCaKey)
|
||||||
|
if err != nil {
|
||||||
|
_, _ = w.Write(ocsp.InternalErrorErrorResponse)
|
||||||
|
t.Fatalf("failed generating OCSP response: %v", err)
|
||||||
|
}
|
||||||
|
_, _ = w.Write(response)
|
||||||
|
})
|
||||||
|
ts := httptest.NewServer(expiredOcspResponse)
|
||||||
|
defer ts.Close()
|
||||||
|
|
||||||
|
logFactory := func() hclog.Logger {
|
||||||
|
return hclog.NewNullLogger()
|
||||||
|
}
|
||||||
|
client := New(logFactory, 100)
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
config := &VerifyConfig{
|
||||||
|
OcspEnabled: true,
|
||||||
|
OcspServersOverride: []string{ts.URL},
|
||||||
|
OcspFailureMode: FailOpenFalse,
|
||||||
|
QueryAllServers: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
status, err := client.GetRevocationStatus(ctx, leafCert, rootCa, config)
|
||||||
|
require.ErrorContains(t, err, "invalid validity",
|
||||||
|
"Expected error got response: %v, %v", status, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func createCaLeafCerts(t *testing.T) (*ecdsa.PrivateKey, *x509.Certificate, *x509.Certificate) {
|
||||||
|
rootCaKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||||
|
require.NoError(t, err, "failed generated root key for CA")
|
||||||
|
|
||||||
|
// Validate we reject CSRs that contain CN that aren't in the original order
|
||||||
|
cr := &x509.Certificate{
|
||||||
|
Subject: pkix.Name{CommonName: "Root Cert"},
|
||||||
|
SerialNumber: big.NewInt(1),
|
||||||
|
IsCA: true,
|
||||||
|
BasicConstraintsValid: true,
|
||||||
|
SignatureAlgorithm: x509.ECDSAWithSHA256,
|
||||||
|
NotBefore: time.Now().Add(-1 * time.Second),
|
||||||
|
NotAfter: time.Now().AddDate(1, 0, 0),
|
||||||
|
KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign,
|
||||||
|
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageOCSPSigning},
|
||||||
|
}
|
||||||
|
rootCaBytes, err := x509.CreateCertificate(rand.Reader, cr, cr, &rootCaKey.PublicKey, rootCaKey)
|
||||||
|
require.NoError(t, err, "failed generating root ca")
|
||||||
|
|
||||||
|
rootCa, err := x509.ParseCertificate(rootCaBytes)
|
||||||
|
require.NoError(t, err, "failed parsing root ca")
|
||||||
|
|
||||||
|
leafKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||||
|
require.NoError(t, err, "failed generated leaf key")
|
||||||
|
|
||||||
|
cr = &x509.Certificate{
|
||||||
|
Subject: pkix.Name{CommonName: "Leaf Cert"},
|
||||||
|
SerialNumber: big.NewInt(2),
|
||||||
|
SignatureAlgorithm: x509.ECDSAWithSHA256,
|
||||||
|
NotBefore: time.Now().Add(-1 * time.Second),
|
||||||
|
NotAfter: time.Now().AddDate(1, 0, 0),
|
||||||
|
KeyUsage: x509.KeyUsageDigitalSignature,
|
||||||
|
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
|
||||||
|
}
|
||||||
|
leafCertBytes, err := x509.CreateCertificate(rand.Reader, cr, rootCa, &leafKey.PublicKey, rootCaKey)
|
||||||
|
require.NoError(t, err, "failed generating root ca")
|
||||||
|
|
||||||
|
leafCert, err := x509.ParseCertificate(leafCertBytes)
|
||||||
|
require.NoError(t, err, "failed parsing root ca")
|
||||||
|
return rootCaKey, rootCa, leafCert
|
||||||
|
}
|
||||||
|
|
||||||
func TestUnitValidateOCSP(t *testing.T) {
|
func TestUnitValidateOCSP(t *testing.T) {
|
||||||
ocspRes := &ocsp.Response{}
|
ocspRes := &ocsp.Response{}
|
||||||
ost, err := validateOCSP(ocspRes)
|
ost, err := validateOCSP(ocspRes)
|
||||||
|
|
Loading…
Reference in New Issue