Chapter 13. Linking sites using custom certificates
By default, the Skupper controller generates internal Certificate Authorities (CAs) and self-signed certificates.
For example, it creates certificates to authenticate incoming Skupper links from external Skupper sites.
The CA and server certificate used for this authentication are named skupper-site-ca (default signing Certificate resource for a Skupper Site) and skupper-site-server, respectively.
Although this behavior is automatic, you can override it by providing your own custom server certificate or even your own CA.
This document describes two approaches for using custom certificates:
-
Using a custom
RouterAccessand custom certificates - Manually define theRouterAccessCR with your own certificate (linkAccessis not enabled) -
Using
Linkresources and custom certificates - Override the defaultskupper-site-servercertificate beforelinkAccessis enabled
Key differences between approaches
linkAccess | RouterAccess | |
|---|---|---|
|
Who creates | Skupper controller (auto) | You (manually) |
|
|
|
|
| Secret name |
Must be | Any name you choose |
| Site delete/recreate needed | Yes, to prevent overwrite | No |
| Skupper overwrites your cert | Yes, unless pre-created | Never |
Site-specific context
-
RouterAccessis a listening site concern - it controls how your site accepts incoming link connections CertificateCR can be used on both sites, but only if the referenced CA secret exists in the namespace:- On the listening site - the Certificate CR can reference a CA secret to sign the server certificate
- On the connecting site - it generates client credentials for outgoing links, but the CA that signs those credentials must also be present in the namespace
If a RouterAccess references a custom secret signed by an external CA (where no CA secret exists in the namespace), then Certificate CRs cannot be used to generate client credentials automatically.
In both approaches, the listening site provides server certificates and the connecting site uses client certificates to establish the link.
13.1. Linking sites using a custom RouterAccess, and custom certificates Copy linkLink copied to clipboard!
By default, when you set spec.linkAccess on a Site, the Skupper controller automatically creates a RouterAccess named skupper-router with generateTlsCredentials: true and tlsCredentials: skupper-site-server.
The alternative is to define the RouterAccess CR yourself with generateTlsCredentials: false and point tlsCredentials at a Secret you supply. When generateTlsCredentials is false, the Skupper controller recognizes your custom certificate and will not modify it.
Prerequisites
- Two sites
- A server certificate and key for the listening site
-
jqandyq(mikefarah/yq, the Go-based implementation) installed if using the kubectl method to generate theLinkresource
Procedure
On the listening site, create a
SiteCR withoutspec.linkAccess(or withspec.linkAccess: none):apiVersion: skupper.io/v2alpha1 kind: Site metadata: name: my-site spec: {}Apply it:
kubectl apply -f site.yamlBecause
spec.linkAccessis not set, the controller will not auto-create anyRouterAccess.On the listening site, create a Secret containing your custom server certificate. You can name it anything — this example uses
my-server-cert:apiVersion: v1 kind: Secret type: "kubernetes.io/tls" metadata: name: my-server-cert data: ca.crt: LS0tLS1C...redacted tls.crt: LS0tLS1C...redacted tls.key: LS0tLS1C...redactedApply it:
kubectl apply -f my-server-cert.yamlMake sure the certificate in
tls.crtis valid for the hostname or IP address that will be referenced in yourLinkresource. In this example, consider it valid forskupper.public.host.On the listening site, create a
RouterAccessCR that references your Secret and setsgenerateTlsCredentials: false:apiVersion: skupper.io/v2alpha1 kind: RouterAccess metadata: name: my-router-access spec: generateTlsCredentials: false tlsCredentials: my-server-cert accessType: loadbalancer # or "route", "ingress", etc. roles: - name: inter-router port: 55671 - name: edge port: 45671Apply it:
kubectl apply -f my-router-access.yamlBecause
generateTlsCredentials: false, Skupper will use your Secret as-is and will never overwrite it.Determine the hostname or IP address for the listening site.
Check the
RouterAccessstatus for the resolved endpoints:kubectl get routeraccess my-router-access -o json | jq -r '.status.endpoints[0].host'You should see something like:
skupper.public.hostOn the listening site, create client credentials for the connecting site.
a) If your server certificate was signed by
skupper-site-ca:Since Skupper creates the
skupper-site-casigningCertificateresource, you can use it to generate a client secret automatically. Create aCertificateresource:apiVersion: skupper.io/v2alpha1 kind: Certificate metadata: name: skupper-link spec: ca: skupper-site-ca client: true subject: skupper-clientApply it:
kubectl apply -f skupper-link-certificate.yamlSave the generated Secret for use in the next step:
kubectl get secret skupper-link -o yaml | yq eval -o=yaml 'del(.metadata.namespace, .metadata.creationTimestamp, .metadata.resourceVersion, .metadata.uid, .metadata.managedFields)' - > client-secret.yamlb) If your server certificate was signed by a different CA:
You must issue a client certificate yourself and create a Secret named
skupper-linkdirectly. The client certificate must be signed by the same CA that signed your custom server certificate. Create the Secret similarly to how you created the server certificate earlier, ensuring theca.crtfield contains the same CA certificate. Save it asclient-secret.yaml.From the listening site, create a
Linkresource YAML file.Option A: Using kubectl with jq and yq
kubectl get routeraccess my-router-access -o json | jq '{ apiVersion: "skupper.io/v2alpha1", kind: "Link", metadata: {name: "skupper-link"}, spec: { cost: 1, tlsCredentials: "skupper-link", endpoints: .status.endpoints } }' | yq -P > skupper-link.yaml echo "---" >> skupper-link.yaml cat client-secret.yaml >> skupper-link.yamlOption B: Manual generation
Retrieve the endpoints:
kubectl get routeraccess my-router-access -o yaml | yq '.status.endpoints'Then compose the file manually:
--- apiVersion: skupper.io/v2alpha1 kind: Link metadata: name: skupper-link spec: cost: 1 tlsCredentials: skupper-link endpoints: - group: skupper-router host: skupper.public.host name: inter-router port: '55671' - group: skupper-router host: skupper.public.host name: edge port: '45671' --- apiVersion: v1 kind: Secret type: "kubernetes.io/tls" metadata: name: skupper-link data: ca.crt: LS0tLS1C...redacted tls.crt: LS0tLS1C...redacted tls.key: LS0tLS1C...redactedSecurely transfer the
Linkresource YAML file to the connecting site.📌 NOTE Access to this file provides access to the application network. Protect it appropriately.
On the connecting site, apply the YAML file and check status:
kubectl apply -f skupper-link.yaml kubectl get link NAME STATUS REMOTE SITE MESSAGE skupper-link Ready my-site OK
13.2. Linking sites using Link resources and custom certificates Copy linkLink copied to clipboard!
The server certificate (skupper-site-server) issued by the self-signed CA skupper-site-ca is issued for the public hostname or IP address associated with the skupper-router service. This depends on the ingress method used, for example an OpenShift Route or a Kubernetes LoadBalancer Service.
You can override this behavior by providing your own custom server certificate.
Prerequisites
- Two sites
-
The listening site must have
link-accessenabled - A server certificate and key for the listening site
-
jqandyq(mikefarah/yq, the Go-based implementation) installed if using the kubectl method to generate theLinkresource
To link sites using custom certificates, you provide a custom server certificate on the listening site and create a Link resource on the connecting site that references matching client credentials.
In this procedure you delete and recreate your site to make sure the certificate configuration is applied.
Procedure
On the listening site, create a secret named
skupper-site-server, for example:apiVersion: v1 kind: Secret type: "kubernetes.io/tls" metadata: name: skupper-site-server data: ca.crt: LS0tLS1C...redacted tls.crt: LS0tLS1C...redacted tls.key: LS0tLS1C...redactedApply the secret while deleting and recreating the site:
kubectl delete site <site-name> # delete the site kubectl apply -f skupper-site-server.yaml kubectl apply -f site.yaml # recreate the siteNoteIf you attempt to apply the secret on an existing site, the Skupper controller overwrites your changes. Make sure to create the secret before creating your site.
Make sure the certificate specified in
tls.crtis valid for the hostname or IP address that will be referenced in yourLinkresource. In this example, consider the server certificate as being valid for the hostname:skupper.public.host.Determine the hostname or IP address for the listening site.
In case your Skupper Site is already created, you can find the appropriate Hostname or IP by looking at the Endpoint element of a Site status:
kubectl get site <site-name> -o json | jq -r '.status.endpoints[0].host'And you will see something like:
skupper.public.hostNote: Make sure you specify the appropriate namespace when running the commands from this example.
Again, if your Site has already been created, Skupper will recognize your custom secret. You can confirm this with the following command:
kubectl get certificate.skupper.io skupper-site-server -o json | jq -r '.status.conditions[].message'You should see a message like:
Secret exists but is not controlled by skupperThis confirms that Skupper has detected your certificate but is not managing it.
On the listening site, create client credentials for the connecting site.
Once your Skupper site is configured to use your custom server certificate, you can create a
Linkresource and an associated clientSecret.-
If your custom server certificate was signed by the
skupper-site-caissuer or by a CA whose secret is on the listening site’s namespace, you can use the Certificate CR to generate client credentials automatically. -
If your custom server certificate was signed by a different CA, you must provide the client
Secretyourself, signed by that CA.
To use the automatically created
skupper-site-caissuer, create aCertificateresource so that Skupper generates a client secret namedskupper-link:+
apiVersion: skupper.io/v2alpha1 kind: Certificate metadata: name: skupper-link spec: ca: skupper-site-ca client: true subject: skupper-client+ Apply the resource:
+
kubectl apply -f skupper-link-certificate.yaml+ You should see an output like:
+
certificate.skupper.io/skupper-link created+ Then a Secret named
skupper-linkshould be created and it can be used to compose your link file.+ Save the generated secret to a local file for use in the next step:
+
kubectl get secret skupper-link -o yaml | yq eval -o=yaml 'del(.metadata.namespace, .metadata.creationTimestamp, .metadata.resourceVersion, .metadata.uid, .metadata.managedFields)' - > client-secret.yaml+ If you are providing the client certificate yourself, create a secret named
skupper-link, for example:+
apiVersion: v1 kind: Secret type: "kubernetes.io/tls" metadata: name: skupper-link data: ca.crt: LS0tLS1C...redacted tls.crt: LS0tLS1C...redacted tls.key: LS0tLS1C...redacted+ Save this resource locally as
client-secret.yamlif you want to combine it with aLinkresource in a single file.-
If your custom server certificate was signed by the
On the listening site, create a
Linkresource YAML file.Now, regardless of whether Skupper generated your client certificate Secret or if you generated it yourself, to define a Skupper Link, that can be shared with remote sites allowing them to initiate a secure outgoing link to your site, you will need to write a YAML file that contains both documents: a
Link, and a ClientSecret.You can generate a Link using
kubectlor manually (as long as retrieve the list of endpoints). Here are these two methods:Option A: Using kubectl with jq and yq
The following command uses
kubectl,jq, andyqto extract and compose the information needed to define a Link, storing the Link document intoskupper-link.yaml:kubectl get site <site-name> -o json | jq '{ apiVersion: "skupper.io/v2alpha1", kind: "Link", metadata: {name: "skupper-link"}, spec: { cost: 1, tlsCredentials: "skupper-link", endpoints: .status.endpoints } }' | yq -P > skupper-link.yamlThen you need to combine it in a YAML file that contains both the Link above and the client Secret. Suppose your client Secret is stored in a file named
client-secret.yaml, you could run:echo "---" >> skupper-link.yaml cat client-secret.yaml >> skupper-link.yamlOption B: Manual generation
You can retrieve the list of endpoints from the Site definition, using:
kubectl get site <site-name> -o yaml | yq '.status.endpoints'The command above uses both
kubectlandyq. And the output should be something like:- group: skupper-router host: skupper.public.host name: inter-router port: '55671' - group: skupper-router host: skupper.public.host name: edge port: '45671'Then you can create your Link file with:
--- apiVersion: skupper.io/v2alpha1 kind: Link metadata: name: skupper-link spec: cost: 1 tlsCredentials: skupper-link endpoints: - group: skupper-router host: skupper.public.host # The hostname or IP specified in your custom certificate name: inter-router port: '55671' - group: skupper-router host: skupper.public.host # The same hostname or IP name: edge port: '45671' --- apiVersion: v1 kind: Secret type: "kubernetes.io/tls" metadata: name: skupper-link data: ca.crt: LS0tLS1C...redacted # The trusted CA certificate tls.crt: LS0tLS1C...redacted # The client certificate tls.key: LS0tLS1C...redacted # The corresponding private keySecurely transfer the
Linkresource YAML file to the context of the connecting site. If you have both sites available from your terminal session, this step is not required.📌 NOTE Access to this file provides access to the application network. Protect it appropriately.
On the connecting site, apply the YAML file and check status:
kubectl apply -f skupper-link.yaml kubectl get link NAME STATUS REMOTE SITE MESSAGE skupper-link Ready my-site OK