Skip to content

Commit

Permalink
Wire up support for ignoring unhandled critical extensions
Browse files Browse the repository at this point in the history
  • Loading branch information
sgmiller committed Aug 1, 2024
1 parent 52af34e commit 20e5d05
Show file tree
Hide file tree
Showing 4 changed files with 215 additions and 160 deletions.
3 changes: 2 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ go 1.21
require (
github.com/go-jose/go-jose/v3 v3.0.3
github.com/go-test/deep v1.1.0
github.com/hashicorp/cap v0.6.0
github.com/hashicorp/cap v0.6.1-0.20240801183234-3dae6e2fed81
github.com/hashicorp/errwrap v1.1.0
github.com/hashicorp/go-cleanhttp v0.5.2
github.com/hashicorp/go-hclog v1.6.2
Expand Down Expand Up @@ -58,6 +58,7 @@ require (
github.com/hashicorp/go-plugin v1.6.0 // indirect
github.com/hashicorp/go-retryablehttp v0.7.1 // indirect
github.com/hashicorp/go-rootcerts v1.0.2 // indirect
github.com/hashicorp/go-secure-stdlib v0.1.1-0.20240801184542-be66c7f88420 // indirect
github.com/hashicorp/go-secure-stdlib/mlock v0.1.2 // indirect
github.com/hashicorp/go-secure-stdlib/parseutil v0.1.7 // indirect
github.com/hashicorp/go-secure-stdlib/plugincontainer v0.3.0 // indirect
Expand Down
6 changes: 6 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,8 @@ github.com/googleapis/gax-go/v2 v2.12.0 h1:A+gCJKdRfqXkr+BIRGtZLibNXf0m1f9E4HG56
github.com/googleapis/gax-go/v2 v2.12.0/go.mod h1:y+aIqrI5eb1YGMVJfuV3185Ts/D7qKpsEkdD5+I6QGU=
github.com/hashicorp/cap v0.6.0 h1:uOSdbtXu8zsbRyjwpiTy6QiuX3+5paAbNkYlop7QexM=
github.com/hashicorp/cap v0.6.0/go.mod h1:DwzHkoG6pxSARiqwvAgxmCPUpTTCCw2wVuPrIFOzpe0=
github.com/hashicorp/cap v0.6.1-0.20240801183234-3dae6e2fed81 h1:EujCSV7+oCGbo9qvRRwA6FuRDUd7tljhi6zHB1fBhls=
github.com/hashicorp/cap v0.6.1-0.20240801183234-3dae6e2fed81/go.mod h1:HT0J5oqbyWOVTRRwNbdjvmYCdEhMz+OqgIjd2kkbv2w=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
Expand Down Expand Up @@ -151,6 +153,10 @@ github.com/hashicorp/go-retryablehttp v0.7.1 h1:sUiuQAnLlbvmExtFQs72iFW/HXeUn8Z1
github.com/hashicorp/go-retryablehttp v0.7.1/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc=
github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=
github.com/hashicorp/go-secure-stdlib v0.1.1-0.20240801184315-43e42ec42f9f h1:O9NQm16w3kx1n1ww245u3/g3uzLtcn2Rl71nFNkkanI=
github.com/hashicorp/go-secure-stdlib v0.1.1-0.20240801184315-43e42ec42f9f/go.mod h1:FWbaGK8VHvb9GD7G/FwTwfFoKsLv2cf5wsBmbZUcKUQ=
github.com/hashicorp/go-secure-stdlib v0.1.1-0.20240801184542-be66c7f88420 h1:FlITwC9BVBSXFnP9CHhegs/JELyGmoSqWIuT6eK+uAU=
github.com/hashicorp/go-secure-stdlib v0.1.1-0.20240801184542-be66c7f88420/go.mod h1:FWbaGK8VHvb9GD7G/FwTwfFoKsLv2cf5wsBmbZUcKUQ=
github.com/hashicorp/go-secure-stdlib/base62 v0.1.2 h1:ET4pqyjiGmY09R5y+rSd70J2w45CtbWDNvGqWp/R3Ng=
github.com/hashicorp/go-secure-stdlib/base62 v0.1.2/go.mod h1:EdWO6czbmthiwZ3/PUsDV+UD1D5IRU4ActiaWGwt0Yw=
github.com/hashicorp/go-secure-stdlib/mlock v0.1.2 h1:p4AKXPPS24tO8Wc8i1gLvSKdmkiSY5xuju57czJ/IJQ=
Expand Down
117 changes: 72 additions & 45 deletions path_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"crypto"
"crypto/tls"
"crypto/x509"
"encoding/asn1"
"errors"
"fmt"
"net/http"
Expand All @@ -17,6 +18,7 @@ import (
"github.com/hashicorp/cap/oidc"
"github.com/hashicorp/errwrap"
"github.com/hashicorp/go-cleanhttp"
httputil "github.com/hashicorp/go-secure-stdlib/httputil"
"github.com/hashicorp/go-secure-stdlib/strutil"
"github.com/hashicorp/vault/sdk/framework"
"github.com/hashicorp/vault/sdk/helper/certutil"
Expand Down Expand Up @@ -108,6 +110,10 @@ func pathConfig(b *jwtAuthBackend) *framework.Path {
Value: true,
},
},
"unsupported_critical_cert_extensions": {
Type: framework.TypeCommaStringSlice,
Description: `A list of ASN1 OIDs of certificate extensions marked Critical that are unsupported by Vault and should be ignored. This option should very rarely be needed except in specialized PKI environments.`,
},
},

Operations: map[logical.Operation]framework.OperationHandler{
Expand Down Expand Up @@ -196,20 +202,21 @@ func (b *jwtAuthBackend) pathConfigRead(ctx context.Context, req *logical.Reques

resp := &logical.Response{
Data: map[string]interface{}{
"oidc_discovery_url": config.OIDCDiscoveryURL,
"oidc_discovery_ca_pem": config.OIDCDiscoveryCAPEM,
"oidc_client_id": config.OIDCClientID,
"oidc_response_mode": config.OIDCResponseMode,
"oidc_response_types": config.OIDCResponseTypes,
"default_role": config.DefaultRole,
"jwt_validation_pubkeys": config.JWTValidationPubKeys,
"jwt_supported_algs": config.JWTSupportedAlgs,
"jwks_url": config.JWKSURL,
"jwks_pairs": config.JWKSPairs,
"jwks_ca_pem": config.JWKSCAPEM,
"bound_issuer": config.BoundIssuer,
"provider_config": providerConfig,
"namespace_in_state": config.NamespaceInState,
"oidc_discovery_url": config.OIDCDiscoveryURL,
"oidc_discovery_ca_pem": config.OIDCDiscoveryCAPEM,
"oidc_client_id": config.OIDCClientID,
"oidc_response_mode": config.OIDCResponseMode,
"oidc_response_types": config.OIDCResponseTypes,
"default_role": config.DefaultRole,
"jwt_validation_pubkeys": config.JWTValidationPubKeys,
"jwt_supported_algs": config.JWTSupportedAlgs,
"jwks_url": config.JWKSURL,
"jwks_pairs": config.JWKSPairs,
"jwks_ca_pem": config.JWKSCAPEM,
"bound_issuer": config.BoundIssuer,
"provider_config": providerConfig,
"namespace_in_state": config.NamespaceInState,
"unsupported_critical_cert_extensions": config.UnsupportedCriticalCertExtensions,
},
}

Expand All @@ -218,20 +225,21 @@ func (b *jwtAuthBackend) pathConfigRead(ctx context.Context, req *logical.Reques

func (b *jwtAuthBackend) pathConfigWrite(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
config := &jwtConfig{
OIDCDiscoveryURL: d.Get("oidc_discovery_url").(string),
OIDCDiscoveryCAPEM: d.Get("oidc_discovery_ca_pem").(string),
OIDCClientID: d.Get("oidc_client_id").(string),
OIDCClientSecret: d.Get("oidc_client_secret").(string),
OIDCResponseMode: d.Get("oidc_response_mode").(string),
OIDCResponseTypes: d.Get("oidc_response_types").([]string),
JWKSURL: d.Get("jwks_url").(string),
JWKSPairs: d.Get("jwks_pairs").([]interface{}),
JWKSCAPEM: d.Get("jwks_ca_pem").(string),
DefaultRole: d.Get("default_role").(string),
JWTValidationPubKeys: d.Get("jwt_validation_pubkeys").([]string),
JWTSupportedAlgs: d.Get("jwt_supported_algs").([]string),
BoundIssuer: d.Get("bound_issuer").(string),
ProviderConfig: d.Get("provider_config").(map[string]interface{}),
OIDCDiscoveryURL: d.Get("oidc_discovery_url").(string),
OIDCDiscoveryCAPEM: d.Get("oidc_discovery_ca_pem").(string),
OIDCClientID: d.Get("oidc_client_id").(string),
OIDCClientSecret: d.Get("oidc_client_secret").(string),
OIDCResponseMode: d.Get("oidc_response_mode").(string),
OIDCResponseTypes: d.Get("oidc_response_types").([]string),
JWKSURL: d.Get("jwks_url").(string),
JWKSPairs: d.Get("jwks_pairs").([]interface{}),
JWKSCAPEM: d.Get("jwks_ca_pem").(string),
DefaultRole: d.Get("default_role").(string),
JWTValidationPubKeys: d.Get("jwt_validation_pubkeys").([]string),
JWTSupportedAlgs: d.Get("jwt_supported_algs").([]string),
BoundIssuer: d.Get("bound_issuer").(string),
ProviderConfig: d.Get("provider_config").(map[string]interface{}),
UnsupportedCriticalCertExtensions: d.Get("unsupported_critical_cert_extensions").([]string),
}

// Check if the config already exists, to determine if this is a create or
Expand Down Expand Up @@ -286,7 +294,6 @@ func (b *jwtAuthBackend) pathConfigWrite(ctx context.Context, req *logical.Reque
b.Logger().Error("error checking oidc discovery URL", "error", err)
return logical.ErrorResponse("error checking oidc discovery URL"), nil
}

case config.OIDCClientID != "" && config.OIDCDiscoveryURL == "":
return logical.ErrorResponse("'oidc_discovery_url' must be set for OIDC"), nil

Expand Down Expand Up @@ -318,6 +325,12 @@ func (b *jwtAuthBackend) pathConfigWrite(ctx context.Context, req *logical.Reque
return logical.ErrorResponse(fmt.Errorf("error parsing public key: %w", err).Error()), nil
}
}
case len(config.UnsupportedCriticalCertExtensions) > 0:
for _, v := range config.UnsupportedCriticalCertExtensions {
if _, err := certutil.StringToOid(v); err != nil {
return logical.ErrorResponse(fmt.Errorf("error parsing extension OID: %w", err).Error()), nil
}
}

default:
return nil, errors.New("unknown condition")
Expand Down Expand Up @@ -374,9 +387,22 @@ func (b *jwtAuthBackend) createProvider(config *jwtConfig) (*oidc.Provider, erro
supportedSigAlgs = []oidc.Alg{oidc.RS256}
}

opts := []oidc.Option{oidc.WithProviderCA(config.OIDCDiscoveryCAPEM)}
if len(config.UnsupportedCriticalCertExtensions) > 0 {
var oids []asn1.ObjectIdentifier
for _, v := range config.UnsupportedCriticalCertExtensions {
oid, err := certutil.StringToOid(v)
if err != nil {
return nil, errwrap.Wrapf("error creating provider: {{err}}", err)
}
oids = append(oids, oid)
}
ietripper := httputil.NewIgnoreUnsupportedExtensionsRoundTripper(nil, oids)
opts = append(opts, oidc.WithRoundTripper(ietripper))
}
c, err := oidc.NewConfig(config.OIDCDiscoveryURL, config.OIDCClientID,
oidc.ClientSecret(config.OIDCClientSecret), supportedSigAlgs, []string{},
oidc.WithProviderCA(config.OIDCDiscoveryCAPEM))
opts...)
if err != nil {
return nil, errwrap.Wrapf("error creating provider: {{err}}", err)
}
Expand Down Expand Up @@ -440,21 +466,22 @@ func (b *jwtAuthBackend) validateJWKSURL(ctx context.Context, JWKSURL, JWKSCAPEM
}

type jwtConfig struct {
OIDCDiscoveryURL string `json:"oidc_discovery_url"`
OIDCDiscoveryCAPEM string `json:"oidc_discovery_ca_pem"`
OIDCClientID string `json:"oidc_client_id"`
OIDCClientSecret string `json:"oidc_client_secret"`
OIDCResponseMode string `json:"oidc_response_mode"`
OIDCResponseTypes []string `json:"oidc_response_types"`
JWKSURL string `json:"jwks_url"`
JWKSCAPEM string `json:"jwks_ca_pem"`
JWKSPairs []interface{} `json:"jwks_pairs"`
JWTValidationPubKeys []string `json:"jwt_validation_pubkeys"`
JWTSupportedAlgs []string `json:"jwt_supported_algs"`
BoundIssuer string `json:"bound_issuer"`
DefaultRole string `json:"default_role"`
ProviderConfig map[string]interface{} `json:"provider_config"`
NamespaceInState bool `json:"namespace_in_state"`
OIDCDiscoveryURL string `json:"oidc_discovery_url"`
OIDCDiscoveryCAPEM string `json:"oidc_discovery_ca_pem"`
OIDCClientID string `json:"oidc_client_id"`
OIDCClientSecret string `json:"oidc_client_secret"`
OIDCResponseMode string `json:"oidc_response_mode"`
OIDCResponseTypes []string `json:"oidc_response_types"`
JWKSURL string `json:"jwks_url"`
JWKSCAPEM string `json:"jwks_ca_pem"`
JWKSPairs []interface{} `json:"jwks_pairs"`
JWTValidationPubKeys []string `json:"jwt_validation_pubkeys"`
JWTSupportedAlgs []string `json:"jwt_supported_algs"`
BoundIssuer string `json:"bound_issuer"`
DefaultRole string `json:"default_role"`
ProviderConfig map[string]interface{} `json:"provider_config"`
NamespaceInState bool `json:"namespace_in_state"`
UnsupportedCriticalCertExtensions []string `json:"unsupported_critical_cert_extensions"`

ParsedJWTPubKeys []crypto.PublicKey `json:"-"`
}
Expand Down
Loading

0 comments on commit 20e5d05

Please sign in to comment.