Skip to content

Commit

Permalink
[NET-5346] Expose JWKCluster fields in jwt-provider config entry
Browse files Browse the repository at this point in the history
  • Loading branch information
roncodingenthusiast committed Sep 5, 2023
1 parent 8c44e1d commit 545c52a
Show file tree
Hide file tree
Showing 10 changed files with 635 additions and 8 deletions.
3 changes: 3 additions & 0 deletions .changelog/2881.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:improvement
helm: Add `JWKSCluster` field to `JWTProvider` CRD.
```
1 change: 1 addition & 0 deletions charts/consul/templates/crd-gatewayclassconfigs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
# Copyright (c) HashiCorp, Inc.
# SPDX-License-Identifier: MPL-2.0

---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
Expand Down
60 changes: 60 additions & 0 deletions charts/consul/templates/crd-jwtproviders.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,66 @@ spec:
the proxy listener will wait for the JWKS to be fetched
before being activated. \n Default value is false."
type: boolean
jwksCluster:
description: "JWKSCluster defines how the specified Remote JWKS
URI is to be fetched."
properties:
connectTimeout:
description: "The timeout for new network connections to hosts
in the cluster. \n If not set, a default value of 5s will be
used."
format: int64
type: integer
discoveryType:
description: "DiscoveryType refers to the service discovery type
to use for resolving the cluster. \n Defaults to STRICT_DNS."
type: string
tlsCertificates:
description: "TLSCertificates refers to the data containing
certificate authority certificates to use in verifying a presented
peer certificate."
properties:
caCertificateProviderInstance:
description: "CaCertificateProviderInstance Certificate provider
instance for fetching TLS certificates."
properties:
instanceName:
description: "InstanceName refers to the certificate provider
instance name. \n The default value is 'default'."
type: string
certificateName:
description: "CertificateName is used to specify certificate
instances or types. For example, \"ROOTCA\" to specify a
root-certificate (validation context) or \"example.com\"
to specify a certificate for a particular domain. \n
The default value is the empty string."
type: string
type: object
trustedCA:
description: "TrustedCA defines TLS certificate data containing
certificate authority certificates to use in verifying a presented
peer certificate. \n Exactly one of Filename, EnvironmentVariable,
InlineString or InlineBytes must be specified."
properties:
filename:
description: "The name of the file on the local system to use a
data source for trusted CA certificates."
type: string
environmentVariable:
description: "The environment variable on the local system to use
a data source for trusted CA certificates."
type: string
inlineString:
description: "A string to inline in the configuration for use as
a data source for trusted CA certificates."
type: string
inlineBytes:
description: "A sequence of bytes to inline in the configuration
for use as a data source for trusted CA certificates."
type: string
type: object
type: object
type: object
requestTimeoutMs:
description: RequestTimeoutMs is the number of milliseconds
to time out when making a request for the JWKS.
Expand Down
1 change: 1 addition & 0 deletions charts/consul/templates/crd-meshservices.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
# Copyright (c) HashiCorp, Inc.
# SPDX-License-Identifier: MPL-2.0

---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
Expand Down
9 changes: 6 additions & 3 deletions charts/consul/templates/crd-routeretryfilters.yaml
Original file line number Diff line number Diff line change
@@ -1,18 +1,21 @@
{{- if .Values.connectInject.enabled }}
# Copyright (c) HashiCorp, Inc.
# SPDX-License-Identifier: MPL-2.0

---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.8.0
creationTimestamp: null
name: routeretryfilters.consul.hashicorp.com
labels:
app: {{ template "consul.name" . }}
chart: {{ template "consul.chart" . }}
heritage: {{ .Release.Service }}
release: {{ .Release.Name }}
component: crd
controller-gen.kubebuilder.io/version: v0.8.0
creationTimestamp: null
name: routeretryfilters.consul.hashicorp.com
spec:
group: consul.hashicorp.com
names:
Expand Down
9 changes: 6 additions & 3 deletions charts/consul/templates/crd-routetimeoutfilters.yaml
Original file line number Diff line number Diff line change
@@ -1,18 +1,21 @@
{{- if .Values.connectInject.enabled }}
# Copyright (c) HashiCorp, Inc.
# SPDX-License-Identifier: MPL-2.0

---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.8.0
creationTimestamp: null
name: routetimeoutfilters.consul.hashicorp.com
labels:
app: {{ template "consul.name" . }}
chart: {{ template "consul.chart" . }}
heritage: {{ .Release.Service }}
release: {{ .Release.Name }}
component: crd
controller-gen.kubebuilder.io/version: v0.8.0
creationTimestamp: null
name: routetimeoutfilters.consul.hashicorp.com
spec:
group: consul.hashicorp.com
names:
Expand Down
185 changes: 184 additions & 1 deletion control-plane/api/v1alpha1/jwtprovider_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,12 @@ import (
)

const (
JWTProviderKubeKind string = "jwtprovider"
JWTProviderKubeKind string = "jwtprovider"
DiscoveryTypeStrictDNS ClusterDiscoveryType = "STRICT_DNS"
DiscoveryTypeStatic ClusterDiscoveryType = "STATIC"
DiscoveryTypeLogicalDNS ClusterDiscoveryType = "LOGICAL_DNS"
DiscoveryTypeEDS ClusterDiscoveryType = "EDS"
DiscoveryTypeOriginalDST ClusterDiscoveryType = "ORIGINAL_DST"
)

func init() {
Expand Down Expand Up @@ -404,6 +409,9 @@ type RemoteJWKS struct {
//
// There is no retry by default.
RetryPolicy *JWKSRetryPolicy `json:"retryPolicy,omitempty"`

// JWKSCluster defines how the specified Remote JWKS URI is to be fetched.
JWKSCluster *JWKSCluster `json:",omitempty" alias:"jwks_cluster"`
}

func (r *RemoteJWKS) toConsul() *capi.RemoteJWKS {
Expand All @@ -416,6 +424,7 @@ func (r *RemoteJWKS) toConsul() *capi.RemoteJWKS {
CacheDuration: r.CacheDuration,
FetchAsynchronously: r.FetchAsynchronously,
RetryPolicy: r.RetryPolicy.toConsul(),
JWKSCluster: r.JWKSCluster.toConsul(),
}
}

Expand All @@ -432,6 +441,180 @@ func (r *RemoteJWKS) validate(path *field.Path) field.ErrorList {
}

errs = append(errs, r.RetryPolicy.validate(path.Child("retryPolicy"))...)
errs = append(errs, r.JWKSCluster.validate(path.Child("jwksCluster"))...)
return errs
}

type JWKSCluster struct {
// DiscoveryType refers to the service discovery type to use for resolving the cluster.
//
// This defaults to STRICT_DNS.
// Other options include STATIC, LOGICAL_DNS, EDS or ORIGINAL_DST.
DiscoveryType ClusterDiscoveryType `json:",omitempty" alias:"discovery_type"`

// TLSCertificates refers to the data containing certificate authority certificates to use
// in verifying a presented peer certificate.
// If not specified and a peer certificate is presented it will not be verified.
//
// Must be either CaCertificateProviderInstance or TrustedCA.
TLSCertificates *JWKSTLSCertificate `json:",omitempty" alias:"tls_certificates"`

// The timeout for new network connections to hosts in the cluster.
// If not set, a default value of 5s will be used.
ConnectTimeout time.Duration `json:",omitempty" alias:"connect_timeout"`
}

func (c *JWKSCluster) toConsul() *capi.JWKSCluster {
if c == nil {
return nil
}
return &capi.JWKSCluster{
DiscoveryType: c.DiscoveryType.toConsul(),
TLSCertificates: c.TLSCertificates.toConsul(),
ConnectTimeout: c.ConnectTimeout,
}
}

func (c *JWKSCluster) validate(path *field.Path) field.ErrorList {
var errs field.ErrorList
if c == nil {
return errs
}

errs = append(errs, c.DiscoveryType.Validate(path.Child("discoveryType"))...)
errs = append(errs, c.TLSCertificates.validate(path.Child("tlsCertificates"))...)

return errs
}

type ClusterDiscoveryType string

func (d ClusterDiscoveryType) Validate(path *field.Path) field.ErrorList {
var errs field.ErrorList

switch d {
case DiscoveryTypeStatic, DiscoveryTypeStrictDNS, DiscoveryTypeLogicalDNS, DiscoveryTypeEDS, DiscoveryTypeOriginalDST:
return errs
default:
errs = append(errs, field.Invalid(path, string(d), "unsupported jwks cluster discovery type."))
}
return errs
}

func (d ClusterDiscoveryType) toConsul() capi.ClusterDiscoveryType {
return capi.ClusterDiscoveryType(string(d))
}

// JWKSTLSCertificate refers to the data containing certificate authority certificates to use
// in verifying a presented peer certificate.
// If not specified and a peer certificate is presented it will not be verified.
//
// Must be either CaCertificateProviderInstance or TrustedCA.
type JWKSTLSCertificate struct {
// CaCertificateProviderInstance Certificate provider instance for fetching TLS certificates.
CaCertificateProviderInstance *JWKSTLSCertProviderInstance `json:",omitempty" alias:"ca_certificate_provider_instance"`

// TrustedCA defines TLS certificate data containing certificate authority certificates
// to use in verifying a presented peer certificate.
//
// Exactly one of Filename, EnvironmentVariable, InlineString or InlineBytes must be specified.
TrustedCA *JWKSTLSCertTrustedCA `json:",omitempty" alias:"trusted_ca"`
}

func (c *JWKSTLSCertificate) toConsul() *capi.JWKSTLSCertificate {
if c == nil {
return nil
}

return &capi.JWKSTLSCertificate{
TrustedCA: c.TrustedCA.toConsul(),
CaCertificateProviderInstance: c.CaCertificateProviderInstance.toConsul(),
}
}

func (c *JWKSTLSCertificate) validate(path *field.Path) field.ErrorList {
var errs field.ErrorList
if c == nil {
return errs
}

hasProviderInstance := c.CaCertificateProviderInstance != nil
hasTrustedCA := c.TrustedCA != nil

if countTrue(hasTrustedCA, hasProviderInstance) != 1 {
asJSON, _ := json.Marshal(c)
errs = append(errs, field.Invalid(path, string(asJSON), "exactly one of 'trustedCa' or 'caCertificateProviderInstance' is required"))
}

errs = append(errs, c.TrustedCA.validate(path.Child("trustedCa"))...)

return errs
}

type JWKSTLSCertProviderInstance struct {
// InstanceName refers to the certificate provider instance name
//
// The default value is "default".
InstanceName string `json:",omitempty" alias:"instance_name"`

// CertificateName is used to specify certificate instances or types. For example, "ROOTCA" to specify
// a root-certificate (validation context) or "example.com" to specify a certificate for a
// particular domain.
//
// The default value is the empty string.
CertificateName string `json:",omitempty" alias:"certificate_name"`
}

func (c *JWKSTLSCertProviderInstance) toConsul() *capi.JWKSTLSCertProviderInstance {
if c == nil {
return nil
}

return &capi.JWKSTLSCertProviderInstance{
InstanceName: c.InstanceName,
CertificateName: c.CertificateName,
}
}

// JWKSTLSCertTrustedCA defines TLS certificate data containing certificate authority certificates
// to use in verifying a presented peer certificate.
//
// Exactly one of Filename, EnvironmentVariable, InlineString or InlineBytes must be specified.
type JWKSTLSCertTrustedCA struct {
Filename string `json:",omitempty" alias:"filename"`
EnvironmentVariable string `json:",omitempty" alias:"environment_variable"`
InlineString string `json:",omitempty" alias:"inline_string"`
InlineBytes []byte `json:",omitempty" alias:"inline_bytes"`
}

func (c *JWKSTLSCertTrustedCA) toConsul() *capi.JWKSTLSCertTrustedCA {
if c == nil {
return nil
}

return &capi.JWKSTLSCertTrustedCA{
Filename: c.Filename,
EnvironmentVariable: c.EnvironmentVariable,
InlineBytes: c.InlineBytes,
InlineString: c.InlineString,
}
}

func (c *JWKSTLSCertTrustedCA) validate(path *field.Path) field.ErrorList {
var errs field.ErrorList
if c == nil {
return errs
}

hasFilename := c.Filename != ""
hasEnv := c.EnvironmentVariable != ""
hasInlineBytes := len(c.InlineBytes) > 0
hasInlineString := c.InlineString != ""

if countTrue(hasFilename, hasEnv, hasInlineString, hasInlineBytes) != 1 {
asJSON, _ := json.Marshal(c)
errs = append(errs, field.Invalid(path, string(asJSON), "exactly one of 'filename', 'environmentVariable', 'inlineString' or 'inlineBytes' is required"))
}
return errs
}

Expand Down
Loading

0 comments on commit 545c52a

Please sign in to comment.