Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Document how to use the OpenShift certificate manager #42104

Merged
merged 1 commit into from
Jul 25, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
230 changes: 227 additions & 3 deletions docs/src/main/asciidoc/tls-registry-reference.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -102,9 +102,10 @@
quarkus.grpc.server.plain-text=false
----

[#referencing-a-tls-configuration]
== Referencing a TLS configuration

Once you have configured a _named_ configuration using `quarkus.tls.<name>`, you need to reference it.

Check warning on line 108 in docs/src/main/asciidoc/tls-registry-reference.adoc

View workflow job for this annotation

GitHub Actions / Linting with Vale

[vale] reported by reviewdog 🐶 [Quarkus.Fluff] Depending on the context, consider using 'Rewrite the sentence, or use 'must', instead of' rather than 'need to'. Raw Output: {"message": "[Quarkus.Fluff] Depending on the context, consider using 'Rewrite the sentence, or use 'must', instead of' rather than 'need to'.", "location": {"path": "docs/src/main/asciidoc/tls-registry-reference.adoc", "range": {"start": {"line": 108, "column": 82}}}, "severity": "INFO"}
This is done using the `tls-configuration-name` property:

[source,properties]
Expand Down Expand Up @@ -348,7 +349,7 @@
quarkus.tls.cipher-suites=TLS_AES_128_GCM_SHA256,TLS_AES_256_GCM_SHA384
----

==== TLS Protocol versions
==== TLS protocol versions

Check warning on line 352 in docs/src/main/asciidoc/tls-registry-reference.adoc

View workflow job for this annotation

GitHub Actions / Linting with Vale

[vale] reported by reviewdog 🐶 [Quarkus.HeadingPunctuation] Do not use end punctuation in headings. Raw Output: {"message": "[Quarkus.HeadingPunctuation] Do not use end punctuation in headings.", "location": {"path": "docs/src/main/asciidoc/tls-registry-reference.adoc", "range": {"start": {"line": 352, "column": 1}}}, "severity": "INFO"}

Check warning on line 352 in docs/src/main/asciidoc/tls-registry-reference.adoc

View workflow job for this annotation

GitHub Actions / Linting with Vale

[vale] reported by reviewdog 🐶 [Quarkus.Headings] Use sentence-style capitalization in '3.3.2. TLS protocol versions'. Raw Output: {"message": "[Quarkus.Headings] Use sentence-style capitalization in '3.3.2. TLS protocol versions'.", "location": {"path": "docs/src/main/asciidoc/tls-registry-reference.adoc", "range": {"start": {"line": 352, "column": 1}}}, "severity": "INFO"}

The TLS protocol versions are the list of protocols that can be used during the TLS handshake.
You can configure the ordered list of enabled TLS protocols.
Expand Down Expand Up @@ -520,12 +521,14 @@

If any of these checks fail, the application will fail to start.

== Reloading Certificates
[#reloading-certificates]
== Reloading certificates

The `TlsConfiguration` obtained from the `TLSConfigurationRegistry` includes a mechanism for reloading certificates.
The `reload` method refreshes the key stores and trust stores, typically by reloading them from the file system.

NOTE: The reload operation is not automatic and must be triggered manually. Additionally, the `TlsConfiguration` implementation must support reloading (which is the case for the configured certificate).
NOTE: The reload operation is not automatic and must be triggered manually.
Additionally, the `TlsConfiguration` implementation must support reloading (which is the case for the configured certificate).

The `reload` method returns a `boolean` indicating whether the reload was successful.
A value of `true` means the reload operation was successful, not necessarily that there were updates to the certificates.
Expand Down Expand Up @@ -693,6 +696,227 @@
%prod.quarkus.http.insecure-requests=disabled
----

== Utilizing OpenShift serving certificates

When running your application in OpenShift, you can leverage the https://docs.openshift.com/container-platform/4.16/security/certificates/service-serving-certificate.html[OpenShift serving certificates] to automatically generate and renew TLS certificates.

Check warning on line 701 in docs/src/main/asciidoc/tls-registry-reference.adoc

View workflow job for this annotation

GitHub Actions / Linting with Vale

[vale] reported by reviewdog 🐶 [Quarkus.TermsWarnings] Consider using 'use' rather than 'leverage' unless updating existing content that uses the term. Raw Output: {"message": "[Quarkus.TermsWarnings] Consider using 'use' rather than 'leverage' unless updating existing content that uses the term.", "location": {"path": "docs/src/main/asciidoc/tls-registry-reference.adoc", "range": {"start": {"line": 701, "column": 53}}}, "severity": "WARNING"}
The Quarkus TLS registry can use these certificates and Certificate Authority (CA) files to handle HTTPS traffic securely and to validate certificates.

=== Acquiring a certificate

To have OpenShift generate a certificate, you need to annotate an existing _Service_ object.

Check warning on line 706 in docs/src/main/asciidoc/tls-registry-reference.adoc

View workflow job for this annotation

GitHub Actions / Linting with Vale

[vale] reported by reviewdog 🐶 [Quarkus.Fluff] Depending on the context, consider using 'Rewrite the sentence, or use 'must', instead of' rather than 'need to'. Raw Output: {"message": "[Quarkus.Fluff] Depending on the context, consider using 'Rewrite the sentence, or use 'must', instead of' rather than 'need to'.", "location": {"path": "docs/src/main/asciidoc/tls-registry-reference.adoc", "range": {"start": {"line": 706, "column": 47}}}, "severity": "INFO"}
The generated certificate will be stored in a secret, which you can then mount in your pod.

Consider the following _Service_ example:

[source, yaml]
----
---
apiVersion: v1
kind: Service
metadata:
annotations:
service.beta.openshift.io/serving-cert-secret-name: my-tls-secret
labels:
app.kubernetes.io/name: ...
app.kubernetes.io/version: ...
app.kubernetes.io/managed-by: quarkus
name: hero-service
spec:
ports:
- name: http
port: 443
protocol: TCP
targetPort: 8443
selector:
app.kubernetes.io/name: ...
app.kubernetes.io/version: ...
type: ClusterIP
----

The annotation `service.beta.openshift.io/serving-cert-secret-name` instructs OpenShift to generate a certificate and store it in a secret named `my-tls-secret`. If your service is already running, you can add this annotation with the following command:

[source, shell]
----
oc annotate service hero-service \
service.beta.openshift.io/serving-cert-secret-name=my-tls-secret
----

Next, mount the secret in your pod by updating your _Deployment_ configuration to include a volume and mount the secret:

[source, yaml]
----
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app.kubernetes.io/name: ...
app.kubernetes.io/version: ...
name: my-service
spec:
replicas: 1
selector:
matchLabels:
app.kubernetes.io/name: ...
app.kubernetes.io/version: ...
template:
metadata:
labels:
app.kubernetes.io/name: ...
app.kubernetes.io/version: ...
spec:
volumes:
- name: my-tls-secret # <1>
secret:
secretName: my-tls-secret
containers:
- env:
- name: KUBERNETES_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: QUARKUS_TLS_KEY_STORE_PEM_ACME_CERT # <2>
value: /etc/tls/tls.crt
- name: QUARKUS_TLS_KEY_STORE_PEM_ACME_KEY
value: /etc/tls/tls.key
image: ...
imagePullPolicy: Always
name: my-service
volumeMounts: # <3>
- name: my-tls-secret
mountPath: /etc/tls
readOnly: true
ports:
- containerPort: 8443 # <4>
name: https
protocol: TCP
----
<1> Define a volume to mount the secret. Use the same name as the secret declared above.

Check warning on line 793 in docs/src/main/asciidoc/tls-registry-reference.adoc

View workflow job for this annotation

GitHub Actions / Linting with Vale

[vale] reported by reviewdog 🐶 [Quarkus.TermsSuggestions] Depending on the context, consider using 'because' or 'while' rather than 'as'. Raw Output: {"message": "[Quarkus.TermsSuggestions] Depending on the context, consider using 'because' or 'while' rather than 'as'.", "location": {"path": "docs/src/main/asciidoc/tls-registry-reference.adoc", "range": {"start": {"line": 793, "column": 60}}}, "severity": "INFO"}
<2> Set up the key store with the paths to the certificate and private key. This can be configured using environment variables or configuration files. Here, we use environment variables. OpenShift serving certificates always create the `tls.crt` and `tls.key` files.

Check warning on line 794 in docs/src/main/asciidoc/tls-registry-reference.adoc

View workflow job for this annotation

GitHub Actions / Linting with Vale

[vale] reported by reviewdog 🐶 [Quarkus.CaseSensitiveTerms] Use 'keystore' rather than 'key store'. Raw Output: {"message": "[Quarkus.CaseSensitiveTerms] Use 'keystore' rather than 'key store'.", "location": {"path": "docs/src/main/asciidoc/tls-registry-reference.adoc", "range": {"start": {"line": 794, "column": 16}}}, "severity": "INFO"}
<3> Mount the secret in the container. Ensure the path matches the one used in the configuration (here `/etc/tls`).
<4> Configure the port to serve HTTPS.

By setting the `quarkus.tls.key-store.pem.acme.cert` and `quarkus.tls.key-store.pem.acme.key` variables (or their environment variable variant as done above), the TLS registry will use the certificate and private key from the secret. This configures the default key store for the Quarkus HTTP server, allowing it to use the certificate.

Check warning on line 798 in docs/src/main/asciidoc/tls-registry-reference.adoc

View workflow job for this annotation

GitHub Actions / Linting with Vale

[vale] reported by reviewdog 🐶 [Quarkus.TermsSuggestions] Depending on the context, consider using 'because' or 'while' rather than 'as'. Raw Output: {"message": "[Quarkus.TermsSuggestions] Depending on the context, consider using 'because' or 'while' rather than 'as'.", "location": {"path": "docs/src/main/asciidoc/tls-registry-reference.adoc", "range": {"start": {"line": 798, "column": 144}}}, "severity": "INFO"}

Check warning on line 798 in docs/src/main/asciidoc/tls-registry-reference.adoc

View workflow job for this annotation

GitHub Actions / Linting with Vale

[vale] reported by reviewdog 🐶 [Quarkus.CaseSensitiveTerms] Use 'keystore' rather than 'key store'. Raw Output: {"message": "[Quarkus.CaseSensitiveTerms] Use 'keystore' rather than 'key store'.", "location": {"path": "docs/src/main/asciidoc/tls-registry-reference.adoc", "range": {"start": {"line": 798, "column": 263}}}, "severity": "INFO"}
For using this certificate in a named configuration, refer to <<referencing-a-tls-configuration>>.

Deploy your application, and it will utilize the certificate generated by OpenShift, making the service available over HTTPS.

Check warning on line 801 in docs/src/main/asciidoc/tls-registry-reference.adoc

View workflow job for this annotation

GitHub Actions / Linting with Vale

[vale] reported by reviewdog 🐶 [Quarkus.TermsWarnings] Consider using 'use' rather than 'utilize' unless updating existing content that uses the term. Raw Output: {"message": "[Quarkus.TermsWarnings] Consider using 'use' rather than 'utilize' unless updating existing content that uses the term.", "location": {"path": "docs/src/main/asciidoc/tls-registry-reference.adoc", "range": {"start": {"line": 801, "column": 38}}}, "severity": "WARNING"}

=== Trusting the Certificate Authority (CA)

Now that your service uses a certificate issued by OpenShift, you might need to configure your client applications to trust this certificate.
To accomplish this, create a _ConfigMap_ that holds the CA certificate and mount it in the application's pod.

In this example, we'll use a Quarkus REST client, but the same principle applies to any client.

First, create a _ConfigMap_ for the CA certificate.
Start by defining an _empty_ ConfigMap, which will be populated with the CA certificate:

[source, yaml]
----
apiVersion: v1
kind: ConfigMap
metadata:
name: client-tls-config
annotations:
service.beta.openshift.io/inject-cabundle: "true"
----

The `service.beta.openshift.io/inject-cabundle` annotation is used to inject the CA certificate into the ConfigMap.
Note that the ConfigMap initially has no data — it is empty.
During its processing, OpenShift injects the CA certificate into the ConfigMap in the `service-ca.crt` file.

Next, mount the ConfigMap by adding a volume and mount it in your _Deployment_ configuration:

[source, yaml]
----
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-service-client
labels:
app.kubernetes.io/name: ...
app.kubernetes.io/version: ...
spec:
replicas: 1
selector:
matchLabels:
app.kubernetes.io/name: ...
app.kubernetes.io/version: ...
template:
metadata:
labels:
app.kubernetes.io/name: ...
app.kubernetes.io/version: ...
spec:
containers:
- name: my-service-client
image: ...
ports:
- name: http
containerPort: 8080
protocol: TCP
volumeMounts: # <1>
- name: my-client-volume
mountPath: /deployments/tls
volumes: # <2>
- name: my-client-volume
configMap:
name: client-tls-config
----
<1> Mount the ConfigMap in the container. Ensure the path matches the one used in the configuration (here `/deployments/tls`).
<2> Define a volume to mount the ConfigMap and reference the ConfigMap that receives the CA certificate.

Finally, configure the REST client to use this CA certificate. Consider the following REST client interface:

[source, java]
----
package org.acme;

import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;

@RegisterRestClient(baseUri = "https://hero-service.cescoffi-dev.svc", configKey = "hero") // <1>
public interface HeroClient {

record Hero (Long id, String name, String otherName, int level, String picture, String powers) {
}

@GET
@Path("/api/heroes/random")
Hero getRandomHero();

}
----
<1> Configure the base URI and the configuration key. The name must be in the format `<service-name>.<namespace>.svc`, otherwise the certificate will not be trusted. Ensure to also configure the `configKey`.

Next, configure the REST client to trust the CA certificate:

[source, properties]
----
quarkus.rest-client.hero.tls-configuration-name=my-service-tls # <1>
quarkus.tls.my-service-tls.trust-store.pem.certs=/deployments/tls/service-ca.crt # <2>
----
<1> Configure the `hero` REST client with the TLS configuration named `my-service-tls`.
<2> Set up the `my-service-tls` TLS configuration, specifically the trust store with the CA certificate. Ensure the path matches the one used in the Kubernetes _Deployment_ configuration. The file is always named `service-ca.crt`.

Now, the REST client is configured to trust the certificate generated by OpenShift.

=== Certificate renewal

OpenShift automatically renews the certificates it generates.
When the certificate is renewed, the secret is updated with the new certificate and private key.

To ensure your application uses the new certificate, you can leverage the Quarkus TLS registry's periodic reloading feature.
By setting the `reload-period` property, the TLS registry will periodically check the key stores and trust stores for changes and reload them if needed:

[source, properties]
----
quarkus.tls.reload-period=24h
----

You can also implement a custom mechanism to reload the certificates when the secret is updated.
See <<reloading-certificates>> for more information.

== Quarkus CLI commands and development CA (Certificate Authority)

The TLS registry provides CLI commands to generate a development CA and trusted certificates.
Expand Down
Loading