refactor(apigateway): configure api & dashboard listeneres individually
This commit is contained in:
parent
0014927ca9
commit
c0cbd22bb0
7 changed files with 143 additions and 44 deletions
|
@ -48,12 +48,23 @@ type EnvoySpec struct {
|
||||||
WorkloadTemplate *WorkloadTemplate `json:"workloadTemplate,omitempty"`
|
WorkloadTemplate *WorkloadTemplate `json:"workloadTemplate,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ApiEndpointSpec struct {
|
||||||
|
// JWKSSelector - selector where the JWKS can be retrieved from to enable the API gateway to validate JWTs
|
||||||
|
JWKSSelector *corev1.SecretKeySelector `json:"jwks"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type DashboardEndpointSpec struct{}
|
||||||
|
|
||||||
// APIGatewaySpec defines the desired state of APIGateway.
|
// APIGatewaySpec defines the desired state of APIGateway.
|
||||||
type APIGatewaySpec struct {
|
type APIGatewaySpec struct {
|
||||||
// Envoy - configure the envoy instance and most importantly the control-plane
|
// Envoy - configure the envoy instance and most importantly the control-plane
|
||||||
Envoy *EnvoySpec `json:"envoy"`
|
Envoy *EnvoySpec `json:"envoy"`
|
||||||
// JWKSSelector - selector where the JWKS can be retrieved from to enable the API gateway to validate JWTs
|
// ApiEndpoint - Configure the endpoint for all API routes
|
||||||
JWKSSelector *corev1.SecretKeySelector `json:"jwks"`
|
// this includes the JWT configuration
|
||||||
|
ApiEndpoint *ApiEndpointSpec `json:"apiEndpoint,omitempty"`
|
||||||
|
// DashboardEndpoint - Configure the endpoint for the Supabase dashboard (studio)
|
||||||
|
// this includes optional authentication (basic or Oauth2) for the dashboard
|
||||||
|
DashboardEndpoint *DashboardEndpointSpec `json:"dashboardEndpoint,omitempty"`
|
||||||
// ServiceSelector - selector to match all Supabase services (or in fact EndpointSlices) that should be considered for this APIGateway
|
// ServiceSelector - selector to match all Supabase services (or in fact EndpointSlices) that should be considered for this APIGateway
|
||||||
// +kubebuilder:default={"matchExpressions":{{"key": "app.kubernetes.io/part-of", "operator":"In", "values":{"supabase"}},{"key":"supabase.k8s.icb4dc0.de/api-gateway-target","operator":"Exists"}}}
|
// +kubebuilder:default={"matchExpressions":{{"key": "app.kubernetes.io/part-of", "operator":"In", "values":{"supabase"}},{"key":"supabase.k8s.icb4dc0.de/api-gateway-target","operator":"Exists"}}}
|
||||||
ServiceSelector *metav1.LabelSelector `json:"serviceSelector"`
|
ServiceSelector *metav1.LabelSelector `json:"serviceSelector"`
|
||||||
|
@ -88,7 +99,7 @@ type APIGateway struct {
|
||||||
|
|
||||||
func (g APIGateway) JwksSecretMeta() metav1.ObjectMeta {
|
func (g APIGateway) JwksSecretMeta() metav1.ObjectMeta {
|
||||||
return metav1.ObjectMeta{
|
return metav1.ObjectMeta{
|
||||||
Name: g.Spec.JWKSSelector.Name,
|
Name: g.Spec.ApiEndpoint.JWKSSelector.Name,
|
||||||
Namespace: g.Namespace,
|
Namespace: g.Namespace,
|
||||||
Labels: maps.Clone(g.Labels),
|
Labels: maps.Clone(g.Labels),
|
||||||
}
|
}
|
||||||
|
|
|
@ -93,11 +93,16 @@ func (in *APIGatewaySpec) DeepCopyInto(out *APIGatewaySpec) {
|
||||||
*out = new(EnvoySpec)
|
*out = new(EnvoySpec)
|
||||||
(*in).DeepCopyInto(*out)
|
(*in).DeepCopyInto(*out)
|
||||||
}
|
}
|
||||||
if in.JWKSSelector != nil {
|
if in.ApiEndpoint != nil {
|
||||||
in, out := &in.JWKSSelector, &out.JWKSSelector
|
in, out := &in.ApiEndpoint, &out.ApiEndpoint
|
||||||
*out = new(v1.SecretKeySelector)
|
*out = new(ApiEndpointSpec)
|
||||||
(*in).DeepCopyInto(*out)
|
(*in).DeepCopyInto(*out)
|
||||||
}
|
}
|
||||||
|
if in.DashboardEndpoint != nil {
|
||||||
|
in, out := &in.DashboardEndpoint, &out.DashboardEndpoint
|
||||||
|
*out = new(DashboardEndpointSpec)
|
||||||
|
**out = **in
|
||||||
|
}
|
||||||
if in.ServiceSelector != nil {
|
if in.ServiceSelector != nil {
|
||||||
in, out := &in.ServiceSelector, &out.ServiceSelector
|
in, out := &in.ServiceSelector, &out.ServiceSelector
|
||||||
*out = new(metav1.LabelSelector)
|
*out = new(metav1.LabelSelector)
|
||||||
|
@ -147,6 +152,26 @@ func (in *APIGatewayStatus) DeepCopy() *APIGatewayStatus {
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *ApiEndpointSpec) DeepCopyInto(out *ApiEndpointSpec) {
|
||||||
|
*out = *in
|
||||||
|
if in.JWKSSelector != nil {
|
||||||
|
in, out := &in.JWKSSelector, &out.JWKSSelector
|
||||||
|
*out = new(v1.SecretKeySelector)
|
||||||
|
(*in).DeepCopyInto(*out)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ApiEndpointSpec.
|
||||||
|
func (in *ApiEndpointSpec) DeepCopy() *ApiEndpointSpec {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(ApiEndpointSpec)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
func (in *AuthProviderMeta) DeepCopyInto(out *AuthProviderMeta) {
|
func (in *AuthProviderMeta) DeepCopyInto(out *AuthProviderMeta) {
|
||||||
*out = *in
|
*out = *in
|
||||||
|
@ -485,6 +510,21 @@ func (in *DashboardDbSpec) DeepCopy() *DashboardDbSpec {
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *DashboardEndpointSpec) DeepCopyInto(out *DashboardEndpointSpec) {
|
||||||
|
*out = *in
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DashboardEndpointSpec.
|
||||||
|
func (in *DashboardEndpointSpec) DeepCopy() *DashboardEndpointSpec {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(DashboardEndpointSpec)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
func (in *DashboardList) DeepCopyInto(out *DashboardList) {
|
func (in *DashboardList) DeepCopyInto(out *DashboardList) {
|
||||||
*out = *in
|
*out = *in
|
||||||
|
|
|
@ -43,11 +43,49 @@ spec:
|
||||||
spec:
|
spec:
|
||||||
description: APIGatewaySpec defines the desired state of APIGateway.
|
description: APIGatewaySpec defines the desired state of APIGateway.
|
||||||
properties:
|
properties:
|
||||||
|
apiEndpoint:
|
||||||
|
description: |-
|
||||||
|
ApiEndpoint - Configure the endpoint for all API routes
|
||||||
|
this includes the JWT configuration
|
||||||
|
properties:
|
||||||
|
jwks:
|
||||||
|
description: JWKSSelector - selector where the JWKS can be retrieved
|
||||||
|
from to enable the API gateway to validate JWTs
|
||||||
|
properties:
|
||||||
|
key:
|
||||||
|
description: The key of the secret to select from. Must be
|
||||||
|
a valid secret key.
|
||||||
|
type: string
|
||||||
|
name:
|
||||||
|
default: ""
|
||||||
|
description: |-
|
||||||
|
Name of the referent.
|
||||||
|
This field is effectively required, but due to backwards compatibility is
|
||||||
|
allowed to be empty. Instances of this type with an empty value here are
|
||||||
|
almost certainly wrong.
|
||||||
|
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
|
||||||
|
type: string
|
||||||
|
optional:
|
||||||
|
description: Specify whether the Secret or its key must be
|
||||||
|
defined
|
||||||
|
type: boolean
|
||||||
|
required:
|
||||||
|
- key
|
||||||
|
type: object
|
||||||
|
x-kubernetes-map-type: atomic
|
||||||
|
required:
|
||||||
|
- jwks
|
||||||
|
type: object
|
||||||
componentTypeLabel:
|
componentTypeLabel:
|
||||||
default: app.kubernetes.io/name
|
default: app.kubernetes.io/name
|
||||||
description: ComponentTypeLabel - Label to identify which Supabase
|
description: ComponentTypeLabel - Label to identify which Supabase
|
||||||
component a Service represents (e.g. auth, postgrest, ...)
|
component a Service represents (e.g. auth, postgrest, ...)
|
||||||
type: string
|
type: string
|
||||||
|
dashboardEndpoint:
|
||||||
|
description: |-
|
||||||
|
DashboardEndpoint - Configure the endpoint for the Supabase dashboard (studio)
|
||||||
|
this includes optional authentication (basic or Oauth2) for the dashboard
|
||||||
|
type: object
|
||||||
envoy:
|
envoy:
|
||||||
description: Envoy - configure the envoy instance and most importantly
|
description: Envoy - configure the envoy instance and most importantly
|
||||||
the control-plane
|
the control-plane
|
||||||
|
@ -2593,30 +2631,6 @@ spec:
|
||||||
required:
|
required:
|
||||||
- controlPlane
|
- controlPlane
|
||||||
type: object
|
type: object
|
||||||
jwks:
|
|
||||||
description: JWKSSelector - selector where the JWKS can be retrieved
|
|
||||||
from to enable the API gateway to validate JWTs
|
|
||||||
properties:
|
|
||||||
key:
|
|
||||||
description: The key of the secret to select from. Must be a
|
|
||||||
valid secret key.
|
|
||||||
type: string
|
|
||||||
name:
|
|
||||||
default: ""
|
|
||||||
description: |-
|
|
||||||
Name of the referent.
|
|
||||||
This field is effectively required, but due to backwards compatibility is
|
|
||||||
allowed to be empty. Instances of this type with an empty value here are
|
|
||||||
almost certainly wrong.
|
|
||||||
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
|
|
||||||
type: string
|
|
||||||
optional:
|
|
||||||
description: Specify whether the Secret or its key must be defined
|
|
||||||
type: boolean
|
|
||||||
required:
|
|
||||||
- key
|
|
||||||
type: object
|
|
||||||
x-kubernetes-map-type: atomic
|
|
||||||
serviceSelector:
|
serviceSelector:
|
||||||
default:
|
default:
|
||||||
matchExpressions:
|
matchExpressions:
|
||||||
|
@ -2674,7 +2688,6 @@ spec:
|
||||||
x-kubernetes-map-type: atomic
|
x-kubernetes-map-type: atomic
|
||||||
required:
|
required:
|
||||||
- envoy
|
- envoy
|
||||||
- jwks
|
|
||||||
- serviceSelector
|
- serviceSelector
|
||||||
type: object
|
type: object
|
||||||
status:
|
status:
|
||||||
|
|
|
@ -6,8 +6,9 @@ metadata:
|
||||||
app.kubernetes.io/managed-by: kustomize
|
app.kubernetes.io/managed-by: kustomize
|
||||||
name: gateway-sample
|
name: gateway-sample
|
||||||
spec:
|
spec:
|
||||||
jwks:
|
apiEndpoint:
|
||||||
# will be created by Core resource operator if not present
|
jwks:
|
||||||
# just make sure the secret name is either based on the name of the core resource or explicitly set
|
# will be created by Core resource operator if not present
|
||||||
name: core-sample-jwt
|
# just make sure the secret name is either based on the name of the core resource or explicitly set
|
||||||
key: jwks.json
|
name: core-sample-jwt
|
||||||
|
key: jwks.json
|
||||||
|
|
|
@ -71,13 +71,30 @@ _Appears in:_
|
||||||
| Field | Description | Default | Validation |
|
| Field | Description | Default | Validation |
|
||||||
| --- | --- | --- | --- |
|
| --- | --- | --- | --- |
|
||||||
| `envoy` _[EnvoySpec](#envoyspec)_ | Envoy - configure the envoy instance and most importantly the control-plane | | |
|
| `envoy` _[EnvoySpec](#envoyspec)_ | Envoy - configure the envoy instance and most importantly the control-plane | | |
|
||||||
| `jwks` _[SecretKeySelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#secretkeyselector-v1-core)_ | JWKSSelector - selector where the JWKS can be retrieved from to enable the API gateway to validate JWTs | | |
|
| `apiEndpoint` _[ApiEndpointSpec](#apiendpointspec)_ | ApiEndpoint - Configure the endpoint for all API routes<br />this includes the JWT configuration | | |
|
||||||
|
| `dashboardEndpoint` _[DashboardEndpointSpec](#dashboardendpointspec)_ | DashboardEndpoint - Configure the endpoint for the Supabase dashboard (studio)<br />this includes optional authentication (basic or Oauth2) for the dashboard | | |
|
||||||
| `serviceSelector` _[LabelSelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#labelselector-v1-meta)_ | ServiceSelector - selector to match all Supabase services (or in fact EndpointSlices) that should be considered for this APIGateway | \{ matchExpressions:[map[key:app.kubernetes.io/part-of operator:In values:[supabase]] map[key:supabase.k8s.icb4dc0.de/api-gateway-target operator:Exists]] \} | |
|
| `serviceSelector` _[LabelSelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#labelselector-v1-meta)_ | ServiceSelector - selector to match all Supabase services (or in fact EndpointSlices) that should be considered for this APIGateway | \{ matchExpressions:[map[key:app.kubernetes.io/part-of operator:In values:[supabase]] map[key:supabase.k8s.icb4dc0.de/api-gateway-target operator:Exists]] \} | |
|
||||||
| `componentTypeLabel` _string_ | ComponentTypeLabel - Label to identify which Supabase component a Service represents (e.g. auth, postgrest, ...) | app.kubernetes.io/name | |
|
| `componentTypeLabel` _string_ | ComponentTypeLabel - Label to identify which Supabase component a Service represents (e.g. auth, postgrest, ...) | app.kubernetes.io/name | |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#### ApiEndpointSpec
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
_Appears in:_
|
||||||
|
- [APIGatewaySpec](#apigatewayspec)
|
||||||
|
|
||||||
|
| Field | Description | Default | Validation |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `jwks` _[SecretKeySelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#secretkeyselector-v1-core)_ | JWKSSelector - selector where the JWKS can be retrieved from to enable the API gateway to validate JWTs | | |
|
||||||
|
|
||||||
|
|
||||||
#### AuthProviderMeta
|
#### AuthProviderMeta
|
||||||
|
|
||||||
|
|
||||||
|
@ -319,6 +336,19 @@ _Appears in:_
|
||||||
| `dbCredentialsRef` _[DbCredentialsReference](#dbcredentialsreference)_ | DBCredentialsRef - reference to a Secret key where the DB credentials can be retrieved from<br />Credentials need to be stored in basic auth form | | |
|
| `dbCredentialsRef` _[DbCredentialsReference](#dbcredentialsreference)_ | DBCredentialsRef - reference to a Secret key where the DB credentials can be retrieved from<br />Credentials need to be stored in basic auth form | | |
|
||||||
|
|
||||||
|
|
||||||
|
#### DashboardEndpointSpec
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
_Appears in:_
|
||||||
|
- [APIGatewaySpec](#apigatewayspec)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### DashboardList
|
#### DashboardList
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -53,7 +53,7 @@ var (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
jwksSecretNameField = ".spec.jwks.name"
|
jwksSecretNameField = ".spec.apiEndpoint.jwks.name"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
@ -116,7 +116,7 @@ func (r *APIGatewayReconciler) SetupWithManager(ctx context.Context, mgr ctrl.Ma
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return []string{gw.Spec.JWKSSelector.Name}
|
return []string{gw.Spec.ApiEndpoint.JWKSSelector.Name}
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("setting up field index for JWKS secret name: %w", err)
|
return fmt.Errorf("setting up field index for JWKS secret name: %w", err)
|
||||||
|
@ -211,7 +211,7 @@ func (r *APIGatewayReconciler) reconcileJwksSecret(
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
jwksRaw, ok := jwksSecret.Data[gateway.Spec.JWKSSelector.Key]
|
jwksRaw, ok := jwksSecret.Data[gateway.Spec.ApiEndpoint.JWKSSelector.Key]
|
||||||
if !ok {
|
if !ok {
|
||||||
return "", fmt.Errorf("%w in secret %s", ErrNoJwksConfigured, jwksSecret.Name)
|
return "", fmt.Errorf("%w in secret %s", ErrNoJwksConfigured, jwksSecret.Name)
|
||||||
}
|
}
|
||||||
|
@ -401,10 +401,10 @@ func (r *APIGatewayReconciler) reconileEnvoyDeployment(
|
||||||
{
|
{
|
||||||
Secret: &corev1.SecretProjection{
|
Secret: &corev1.SecretProjection{
|
||||||
LocalObjectReference: corev1.LocalObjectReference{
|
LocalObjectReference: corev1.LocalObjectReference{
|
||||||
Name: gateway.Spec.JWKSSelector.Name,
|
Name: gateway.Spec.ApiEndpoint.JWKSSelector.Name,
|
||||||
},
|
},
|
||||||
Items: []corev1.KeyToPath{{
|
Items: []corev1.KeyToPath{{
|
||||||
Key: gateway.Spec.JWKSSelector.Key,
|
Key: gateway.Spec.ApiEndpoint.JWKSSelector.Key,
|
||||||
Path: "jwks.json",
|
Path: "jwks.json",
|
||||||
}},
|
}},
|
||||||
},
|
},
|
||||||
|
|
|
@ -56,8 +56,12 @@ func (d *APIGatewayCustomDefaulter) Default(ctx context.Context, obj runtime.Obj
|
||||||
}
|
}
|
||||||
apigatewaylog.Info("Defaulting for APIGateway", "name", apiGateway.GetName())
|
apigatewaylog.Info("Defaulting for APIGateway", "name", apiGateway.GetName())
|
||||||
|
|
||||||
if apiGateway.Spec.JWKSSelector == nil {
|
if apiGateway.Spec.ApiEndpoint == nil {
|
||||||
apiGateway.Spec.JWKSSelector = &corev1.SecretKeySelector{
|
apiGateway.Spec.ApiEndpoint = new(supabasev1alpha1.ApiEndpointSpec)
|
||||||
|
}
|
||||||
|
|
||||||
|
if apiGateway.Spec.ApiEndpoint.JWKSSelector == nil {
|
||||||
|
apiGateway.Spec.ApiEndpoint.JWKSSelector = &corev1.SecretKeySelector{
|
||||||
LocalObjectReference: corev1.LocalObjectReference{
|
LocalObjectReference: corev1.LocalObjectReference{
|
||||||
Name: supabase.ServiceConfig.JWT.ObjectName(apiGateway),
|
Name: supabase.ServiceConfig.JWT.ObjectName(apiGateway),
|
||||||
},
|
},
|
||||||
|
|
Loading…
Add table
Reference in a new issue