Skip to content

Commit

Permalink
Merge pull request #174 from gardener/gateway-sources
Browse files Browse the repository at this point in the history
Istio and Gateway API gateway resources as source objects
  • Loading branch information
MartinWeindel authored Apr 23, 2024
2 parents 4e1f48e + 8c5d952 commit 9d5d03f
Show file tree
Hide file tree
Showing 36 changed files with 2,818 additions and 262 deletions.
108 changes: 108 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ Currently, the `cert-controller-manager` supports certificate authorities via:
- [Requesting a Certificate for Ingress](#requesting-a-certificate-for-ingress)
- [Process](#process)
- [Requesting a Certificate for Service](#requesting-a-certificate-for-service)
- [Requesting a Certificate for Gateways](#requesting-a-certificate-for-gateways)
- [Istio gateways](#istio-gateways)
- [Gateway API gateways](#gateway-api-gateways)
- [Demo quick start](#demo-quick-start)
- [Using the cert-controller-manager](#using-the-cert-controller-manager)
- [Usage](#usage)
Expand Down Expand Up @@ -664,6 +667,111 @@ if it has exactly the same common name and DNS names.
NAME COMMON NAME ISSUER STATUS EXPIRATION_DATE DNS_NAMES AGE
cert-simple cert1.mydomain.com issuer-staging Ready 2019-11-10T09:48:17Z [cert1.my-domain.com] 34s
```
## Requesting a Certificate for Gateways

There are source controllers for `Gateways` from [Istio](https://github.com/istio/istio) or the new
Kubernetes [Gateway API](https://gateway-api.sigs.k8s.io/).
By annotating the `Gateway` resource with the `cert.gardener.cloud/purpose=managed` annotation,
certificates are managed automatically for the hosts.

### Istio gateways

For Istio, gateways for API versions `networking.istio.io/v1beta1` and `networking.istio.io/v1alpha3` are supported.

To enable automatic management of `Certificate` resources, annotate the Istio `Gateway` resource with `cert.gardener.cloud/purpose=managed`.
The domain names are extracted from the `spec.servers.hosts` field and from the field `spec.hosts` of related `VirtualService` resources.

```yaml
apiVersion: networking.istio.io/v1beta1
kind: Gateway
metadata:
annotations:
cert.gardener.cloud/purpose: managed
#cert.gardener.cloud/commonname: "*.demo.mydomain.com" # optional, if not specified the first name from spec.tls[].hosts is used as common name
#cert.gardener.cloud/dnsnames: "" # optional, if not specified the names from spec.tls[].hosts are used
#cert.gardener.cloud/follow-cname: "true" # optional, to activate CNAME following for the DNS challenge
#cert.gardener.cloud/secret-labels: "key1=value1,key2=value2" # optional labels for the certificate secret
#cert.gardener.cloud/issuer: issuer-name # optional to specify custom issuer (use namespace/name for shoot issuers)
#cert.gardener.cloud/preferred-chain: "chain name" # optional to specify preferred-chain (value is the Subject Common Name of the root issuer)
#cert.gardener.cloud/private-key-algorithm: ECDSA # optional to specify algorithm for private key, allowed values are 'RSA' or 'ECDSA'
#cert.gardener.cloud/private-key-size: "384" # optional to specify size of private key, allowed values for RSA are "2048", "3072", "4096" and for ECDSA "256" and "384"
name: my-gateway
namespace: default
spec:
selector:
istio: ingressgateway
servers:
- hosts:
- uk.example.com
- eu.example.com
port:
name: https-443
number: 443
protocol: HTTPS
tls: # this server is ignored by the cert-controller-manager, as `tls.credentialName` is not set
mode: SIMPLE
privateKey: /etc/certs/privatekey.pem
serverCertificate: /etc/certs/servercert.pem
- hosts:
- bookinfo-namespace/*.example2.com
port:
name: https-9443
number: 9443
protocol: HTTPS
tls:
credentialName: my-secret # only servers with credentialName will be considered
mode: SIMPLE
```
In this case, only a `Certificate` resource would be created with domain name `*.example2.com`, as the first server item
specifies no `tls.credentialName` field.

See the [Istio tutorial](docs/usage/tutorials/istio-gateways.md) for a more detailed example.

### Gateway API gateways

The Gateway API versions `gateway.networking.k8s.io/v1`, `gateway.networking.k8s.io/v1beta1` and `gateway.networking.k8s.io/v1alpha2` are supported.

To enable automatic management of `Certificate` resources, annotate the Gateway API `Gateway` resource with `cert.gardener.cloud/purpose=managed`.
The domain names are extracted from the `spec.listeners.hostnames` field and from the field `spec.hostnames` of related `HTTPRoute` resources.

```yaml
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
annotations:
cert.gardener.cloud/purpose: managed
#cert.gardener.cloud/commonname: "*.demo.mydomain.com" # optional, if not specified the first name from spec.tls[].hosts is used as common name
#cert.gardener.cloud/dnsnames: "" # optional, if not specified the names from spec.tls[].hosts are used
#cert.gardener.cloud/follow-cname: "true" # optional, to activate CNAME following for the DNS challenge
#cert.gardener.cloud/secret-labels: "key1=value1,key2=value2" # optional labels for the certificate secret
#cert.gardener.cloud/issuer: issuer-name # optional to specify custom issuer (use namespace/name for shoot issuers)
#cert.gardener.cloud/preferred-chain: "chain name" # optional to specify preferred-chain (value is the Subject Common Name of the root issuer)
#cert.gardener.cloud/private-key-algorithm: ECDSA # optional to specify algorithm for private key, allowed values are 'RSA' or 'ECDSA'
#cert.gardener.cloud/private-key-size: "384" # optional to specify size of private key, allowed values for RSA are "2048", "3072", "4096" and for ECDSA "256" and "384"
name: my-gateway
namespace: default
spec:
gatewayClassName: my-gateway-class
listeners:
- allowedRoutes:
namespaces:
from: Selector
selector:
matchLabels:
shared-gateway-access: "true"
hostname: foo.example.com
name: https
port: 443
protocol: HTTPS
tls:
certificateRefs:
- name: my-tls-secret # note: listeners are only considered if they have exactly one certificateRefs item
```

In this case, a single `Certificate` resource with domain name `foo.example.com` would be created.

See the [Gateway API tutorial](docs/usage/tutorials/gateway-api-gateways.md) for a more detailed example.

## Using the cert-controller-manager

Expand Down
21 changes: 21 additions & 0 deletions charts/cert-management/templates/clusterrole.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,26 @@ rules:
- list
- update
- watch
- apiGroups:
- "gateway.networking.k8s.io"
resources:
- gateways
- httproutes
verbs:
- get
- list
- update
- watch
- apiGroups:
- "networking.istio.io"
resources:
- gateways
- virtualservices
verbs:
- get
- list
- update
- watch
- apiGroups:
- ""
resources:
Expand Down Expand Up @@ -74,6 +94,7 @@ rules:
- list
- update
- create
- watch
- apiGroups:
- dns.gardener.cloud
resources:
Expand Down
26 changes: 20 additions & 6 deletions cmd/cert-controller-manager/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,16 @@ import (
"fmt"
"os"

"github.com/gardener/controller-manager-library/pkg/utils"
istionetworkingv1alpha3 "istio.io/client-go/pkg/apis/networking/v1alpha3"
istionetworkingv1beta1 "istio.io/client-go/pkg/apis/networking/v1beta1"
coordinationv1 "k8s.io/api/coordination/v1"
corev1 "k8s.io/api/core/v1"
networkingv1 "k8s.io/api/networking/v1"
networkingv1beta1 "k8s.io/api/networking/v1beta1"
gatewayapisv1 "sigs.k8s.io/gateway-api/apis/v1"
gatewayapisv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2"
gatewayapisv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1"

"github.com/gardener/controller-manager-library/pkg/controllermanager"
"github.com/gardener/controller-manager-library/pkg/controllermanager/cluster"
Expand All @@ -25,6 +31,9 @@ import (
"github.com/gardener/cert-management/pkg/apis/cert/v1alpha1"
ctrl "github.com/gardener/cert-management/pkg/controller"
_ "github.com/gardener/cert-management/pkg/controller/issuer"
_ "github.com/gardener/cert-management/pkg/controller/source/gateways/crdwatch"
_ "github.com/gardener/cert-management/pkg/controller/source/gateways/gatewayapi"
_ "github.com/gardener/cert-management/pkg/controller/source/gateways/istio"
_ "github.com/gardener/cert-management/pkg/controller/source/ingress"
_ "github.com/gardener/cert-management/pkg/controller/source/service"
)
Expand Down Expand Up @@ -56,12 +65,17 @@ func init() {
mappings.ForControllerGroup(ctrl.ControllerGroupSource).
MustRegister()

resources.Register(networkingv1beta1.SchemeBuilder)
resources.Register(networkingv1.SchemeBuilder)
resources.Register(corev1.SchemeBuilder)
resources.Register(dnsapi.SchemeBuilder)
resources.Register(v1alpha1.SchemeBuilder)
resources.Register(coordinationv1.SchemeBuilder)
utils.Must(resources.Register(networkingv1beta1.SchemeBuilder))
utils.Must(resources.Register(networkingv1.SchemeBuilder))
utils.Must(resources.Register(corev1.SchemeBuilder))
utils.Must(resources.Register(dnsapi.SchemeBuilder))
utils.Must(resources.Register(v1alpha1.SchemeBuilder))
utils.Must(resources.Register(coordinationv1.SchemeBuilder))
utils.Must(resources.Register(istionetworkingv1alpha3.SchemeBuilder))
utils.Must(resources.Register(istionetworkingv1beta1.SchemeBuilder))
utils.Must(resources.Register(gatewayapisv1alpha2.SchemeBuilder))
utils.Must(resources.Register(gatewayapisv1beta1.SchemeBuilder))
utils.Must(resources.Register(gatewayapisv1.SchemeBuilder))
}

func migrateExtensionsIngress(c controllermanager.Configuration) controllermanager.Configuration {
Expand Down
170 changes: 170 additions & 0 deletions docs/usage/tutorials/gateway-api-gateways.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
# Using annotated Gateway API Gateway and/or HTTPRoutes as Source
This tutorial describes how to use annotated Gateway API resources as source for `Certificate` in the cert-controller-manager.

The cert-controller-manager supports the resources `Gateway` and `HTTPRoute`.

*Note: The cert-management is used together with the [external-dns-management](https://github.com/gardener/external-dns-management), typically.
See [its own tutorial](https://github.com/gardener/external-dns-management/blob/master/docs/usage/tutorials/gateway-api-gateways.md)
for how to automatically create DNS records by annotations.*

## Install cert-controller-manager
First, install the cert-controller-manager using the Helm charts.

## Install Istio on your cluster

Follow the Istio [Kubernetes Gateway API](https://istio.io/latest/docs/tasks/traffic-management/ingress/gateway-api/) to
install the Gateway API and to install Istio.

These are the typical commands for the Istio installation with the Kubernetes Gateway API:

```bash
export KUEBCONFIG=...
curl -L https://istio.io/downloadIstio | sh -
kubectl get crd gateways.gateway.networking.k8s.io &> /dev/null || \
{ kubectl kustomize "github.com/kubernetes-sigs/gateway-api/config/crd?ref=v1.0.0" | kubectl apply -f -; }
istioctl install --set profile=minimal -y
kubectl label namespace default istio-injection=enabled
```

## Verify that Gateway Source works

### Install a sample service
With automatic sidecar injection:
```bash
$ kubectl apply -f https://raw.githubusercontent.com/istio/istio/release-1.20/samples/httpbin/httpbin.yaml
```

Note: The sample service is not used in the following steps. It is deployed for illustration purposes only.
To use it with certificates, you have to add an HTTPS port for it.

### Using a Gateway as a source

Deploy the Gateway API configuration including a single exposed route (i.e., /get):
```bash
kubectl create namespace istio-ingress
kubectl apply -f - <<EOF
apiVersion: gateway.networking.k8s.io/v1beta1
kind: Gateway
metadata:
name: gateway
namespace: istio-ingress
annotations:
#cert.gardener.cloud/dnsnames: "*.example.com" # alternative if you want to control the dns names explicitly.
cert.gardener.cloud/purpose: managed
spec:
gatewayClassName: istio
listeners:
- name: default
hostname: "*.example.com" # this is used by cert-controller-manager to extract DNS names
port: 443
protocol: HTTPS
allowedRoutes:
namespaces:
from: All
tls: # important: tls section must be defined with exactly one certificateRefs item
certificateRefs:
- name: foo-example-com
---
apiVersion: gateway.networking.k8s.io/v1beta1
kind: HTTPRoute
metadata:
name: http
namespace: default
spec:
parentRefs:
- name: gateway
namespace: istio-ingress
hostnames: ["httpbin.example.com"] # this is used by cert-controller-manager to extract DNS names too
rules:
- matches:
- path:
type: PathPrefix
value: /get
backendRefs:
- name: httpbin
port: 8000
EOF
```

You should now see a created `Certificate` resource similar to:

```bash
$ kubectl -n istio-ingress get cert -oyaml
apiVersion: v1
items:
- apiVersion: cert.gardener.cloud/v1alpha1
kind: Certificate
metadata:
generateName: gateway-gateway-
name: gateway-gateway-kdw6h
namespace: istio-ingress
ownerReferences:
- apiVersion: gateway.networking.k8s.io/v1
blockOwnerDeletion: true
controller: true
kind: Gateway
name: gateway
spec:
commonName: '*.example.com'
secretName: foo-example-com
status:
...
kind: List
metadata:
resourceVersion: ""
```

#### Using a HTTPRoute as a source

If the `Gateway` resource is annotated with `cert.gardener.cloud/purpose: managed`,
hostnames from all referencing `HTTPRoute` resources are automatically extracted.
These resources don't need an additional annotation.

Deploy the Gateway API configuration including a single exposed route (i.e., /get):

```bash
kubectl create namespace istio-ingress
kubectl apply -f - <<EOF
apiVersion: gateway.networking.k8s.io/v1beta1
kind: Gateway
metadata:
name: gateway
namespace: istio-ingress
annotations:
cert.gardener.cloud/purpose: managed
spec:
gatewayClassName: istio
listeners:
- name: default
hostname: null # not set
port: 443
protocol: HTTPS
allowedRoutes:
namespaces:
from: All
tls: # important: tls section must be defined with exactly one certificateRefs item
certificateRefs:
- name: foo-example-com
---
apiVersion: gateway.networking.k8s.io/v1beta1
kind: HTTPRoute
metadata:
name: http
namespace: default
spec:
parentRefs:
- name: gateway
namespace: istio-ingress
hostnames: ["httpbin.example.com"] # this is used by dns-controller-manager to extract DNS names too
rules:
- matches:
- path:
type: PathPrefix
value: /get
backendRefs:
- name: httpbin
port: 8000
EOF
```

This should show a similar `Certificate` resource as above.
Loading

0 comments on commit 9d5d03f

Please sign in to comment.