Configure TLS for Spinnaker Services

Spinnaker services communicate with each other and can exchange potentially sensitive data. Enabling TLS between services ensures that this data is encrypted and that a service will only communicate with another service that has a valid certificate.

Switching from plain HTTP to HTTPS will cause some short disruption to the services as they become healthy at different times.

Overview of TLS

When a client attempts to communicate with a server over SSL:

  • the server must present a certificate to the user.
  • the client must validate that certificate by checking it against its known valid certificate authorities (CA).

To properly set up TLS between services, we need to provide each service with:

  1. a certificate signed by a CA
  2. the CA certificate to verify these certificates

Note that distributing a CA public key is only needed if you sign certificates with a CA that is not bundled in most systems. In this document, we assume that you are using a self-signed CA.

Java

Java services can present #1 as a keystore and #2 as a trust store in PKCS12 (preferred) or JKS format.

Golang

Golang services need a X509 certificate (PEM format) and a private key for #1 as well as the X509 certificate of the CA for #2.

What you need

The following table lists the Armory and Spinnaker services, their type (Java or Golang), and which certificates they need:

ServiceTypeServerClient
ClouddriverJavaYesYes
DeckN/A--
Dinghy*GolangYesYes
EchoJavaYesYes
FiatJavaYesYes
Front50JavaYesYes
GateJavaMaybeYes
KayentaJavaYesYes
IgorJavaYesYes
OrcaJavaYesYes
RoscoJavaYesYes
Terraformer*GolangYesYes
  • Dinghy is the service for Pipelines-as-Code.
  • Terraformer is the service for the Armory Terraform Integration.

Note: Gate may be handled differently if you already terminating SSL at Gate. If not, make sure the load balancer and ingress you are using supports self-signed certificates.

In the following sections, you need to have the following information available:

  • ca.pem (all Golang servers): the CA certificate in PEM format
  • [service].crt (each Golang server): the certificate and (optionally) the private key of the Golang server in PEM format
  • [service].key (each Golang server): the private key of the Golang server if not bundled with the certificate you’re using
  • [GOSERVICE]_KEY_PASS (each Golang server): the password to the private key of the server
  • truststore.p12 (all Java clients): a PKCS12 truststore with CA certificate imported
  • TRUSTSTORE_PASS (all Java clients): the password to the truststore you’re using
  • [service].p12 (each Java server): a PKCS12 keystore containing the certificate and private key of the server
  • [SERVICE]_KEY_PASS (each Java server): the password to the keystore you’re using

The server certificate will serve as its client certificate to other services. You can generate different certificates and use them in ok-http-client.key-store* (Java) and http.key* (Golang).

To learn how to generate these files, see the Generate Certificates for Spinnaker guide.

Configuring Java services

Add the following to each Java service profile under profiles in the SpinnakerService’s profiles:

# Only needed for "server" role
server:
  ssl:
    enabled: true
    key-store: <reference to [service].p12>
    key-store-type: PKCS12
    key-store-password: <[SERVICE]_KEY_PASS>

# Needed for all Java services
ok-http-client:
  key-store: <reference to truststore.p12>
  key-store-type: PKCS12
  key-store-password: <TRUSTSTORE_PASS>
  trust-store: <reference to truststore.p12>
  trust-store-type: PKCS12
  trust-store-password: <TRUSTSTORE_PASS>

Currently, ok-http-client.key-store is required even though it is not used in a simple TLS setup.

Configuring Golang services

server:
  ssl:
    enabled: true
    certFile: <reference to [service].crt>
    keyFile: <reference to [service].key if not included in the certFile's PEM>
    keyPassword: <[GOSERVICE]_KEY_PASS if required>

http:
  cacertFile: <reference to ca.pem>

Changing service endpoints

Use the Operator to change Spinnaker’s service endpoints.

Change the SpinnakerService custom resource:

kind: SpinnakerService
...
spec:
  spinnakerConfig:
    service-settings:
      clouddriver:
        baseUrl: https://spin-clouddriver.<NAMESPACE>:7002
      dinghy:
        baseUrl: https://spin-dinghy.<NAMESPACE>:8081
      echo:
        baseUrl: https://spin-echo.<NAMESPACE>:8089
      fiat:
        baseUrl: https://spin-fiat.<NAMESPACE>:7003
      front50:
        baseUrl: https://spin-front50.<NAMESPACE>:8080
      gate:
        baseUrl: https://spin-gate.<NAMESPACE>:8084
      kayenta:
        baseUrl: https://spin-kayenta.<NAMESPACE>:8090
      orca:
        baseUrl: https://spin-orca.<NAMESPACE>:8083
      igor:
        baseUrl: https://spin-igor.<NAMESPACE>:8088
      rosco:
        baseUrl: https://spin-rosco.<NAMESPACE>:8087
      terraformer:
        baseUrl: https://spin-terraformer.<NAMESPACE>:7088

Deploying Spinnaker

After you finish modifying the service YAML files, run kubectl -n <spinnaker namespace> apply -f <SpinnakerService manifest> to apply your changes to your Spinnaker deployment.

Switching from plain HTTP to HTTPS will cause some short disruption to the services as they become healthy at different times.

Providing certificates and passwords to services

Secret engines

You can store secrets (and non secrets) in supported secret stores as well as in Kubernetes secrets if using the Armory Operator. This is the simplest route.

For instance, assuming all the information is stored in a bucket named mybucket on s3 that all services have access to, SpinnakerService manifest might look like:

apiVersion: spinnaker.armory.io/v1alpha2
kind: SpinnakerService
metadata:
  name: spinnaker
spec:
  spinnakerConfig:
    profiles:
      echo:
        server:
          ssl:
            enabled: true
            key-store: encryptedFile:s3!b:mybucket!r:us-west-2!f:echo.jks
            key-store-type: JKS
            key-alias: echo
            key-store-password: encrypted:s3!b:mybucket!r:us-west-2!f:passwords.yml!k:echo.keyPassword

        ok-http-client:
          key-store: encryptedFile:s3!b:mybucket!r:us-west-2!f:truststore.jks
          key-store-type: JKS
          key-store-password: encrypted:s3!b:mybucket!r:us-west-2!f:passwords.yml!k:truststorePassword
          trust-store: encryptedFile:s3!b:mybucket!r:us-west-2!f:truststore.jks
          trust-store-type: JKS
          trust-store-password: encrypted:s3!b:mybucket!r:us-west-2!f:passwords.yml!k:truststorePassword

Run kubectl -n <spinnaker namespace> apply -f <SpinnakerService manifest> after you make your changes.

Manually providing information

An alternative if you cannot use one of the supported secret engine is to store the information in Kubernetes secrets and manually provide the information. Files are added through a volumeMount and passwords through an environment variable.

For instance, assuming mysecrets Kubernetes Secret is available in the same namespace as Spinnaker with the following keys:

data:
  echo.jks: <base64 encoded keystore>

We’ll now provide the following in service-settings/echo.yml:

kubernetes:
  volume:
  - name: secretvol
    mountPath: /var/mysecrets

And a reference would then be:

server:
  ssl:
    key-store: /var/mysecrets/echo.jks

Apply your changes.


Last modified December 9, 2022: (77a2e500)