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 RouterAccess and custom certificates - Manually define the RouterAccess CR with your own certificate (linkAccess is not enabled)
  • Using Link resources and custom certificates - Override the default skupper-site-server certificate before linkAccess is enabled

Key differences between approaches

Expand
 linkAccessRouterAccess

Who creates RouterAccess

Skupper controller (auto)

You (manually)

generateTlsCredentials

true

false

Secret name

Must be skupper-site-server

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

  • RouterAccess is a listening site concern - it controls how your site accepts incoming link connections
  • Certificate CR 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
Note

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.

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
  • jq and yq (mikefarah/yq, the Go-based implementation) installed if using the kubectl method to generate the Link resource

Procedure

  1. On the listening site, create a Site CR without spec.linkAccess (or with spec.linkAccess: none):

    apiVersion: skupper.io/v2alpha1
    kind: Site
    metadata:
      name: my-site
    spec: {}

    Apply it:

    kubectl apply -f site.yaml

    Because spec.linkAccess is not set, the controller will not auto-create any RouterAccess.

  2. 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...redacted

    Apply it:

    kubectl apply -f my-server-cert.yaml

    Make sure the certificate in tls.crt is valid for the hostname or IP address that will be referenced in your Link resource. In this example, consider it valid for skupper.public.host.

  3. On the listening site, create a RouterAccess CR that references your Secret and sets generateTlsCredentials: 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: 45671

    Apply it:

    kubectl apply -f my-router-access.yaml

    Because generateTlsCredentials: false, Skupper will use your Secret as-is and will never overwrite it.

  4. Determine the hostname or IP address for the listening site.

    Check the RouterAccess status 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.host
  5. On 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-ca signing Certificate resource, you can use it to generate a client secret automatically. Create a Certificate resource:

    apiVersion: skupper.io/v2alpha1
    kind: Certificate
    metadata:
      name: skupper-link
    spec:
      ca: skupper-site-ca
      client: true
      subject: skupper-client

    Apply it:

    kubectl apply -f skupper-link-certificate.yaml

    Save 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.yaml

    b) If your server certificate was signed by a different CA:

    You must issue a client certificate yourself and create a Secret named skupper-link directly. 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 the ca.crt field contains the same CA certificate. Save it as client-secret.yaml.

  6. From the listening site, create a Link resource 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.yaml

    Option 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...redacted
  7. Securely transfer the Link resource YAML file to the connecting site.

    📌 NOTE Access to this file provides access to the application network. Protect it appropriately.

  8. 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
Red Hat logoGithubredditYoutubeTwitter

Learn

Try, buy, & sell

Communities

About Red Hat

We deliver hardened solutions that make it easier for enterprises to work across platforms and environments, from the core datacenter to the network edge.

Making open source more inclusive

Red Hat is committed to replacing problematic language in our code, documentation, and web properties. For more details, see the Red Hat Blog.

About Red Hat Documentation

Legal Notice

Theme

© 2026 Red Hat
Back to top