Skip to content

Commit ae7ec92

Browse files
committed
implementation
Signed-off-by: Lukas Kral <lukywill16@gmail.com> finish implementation Signed-off-by: Lukas Kral <lukywill16@gmail.com> fix tests Signed-off-by: Lukas Kral <lukywill16@gmail.com> add changelog Signed-off-by: Lukas Kral <lukywill16@gmail.com> same value of validityDays and renewalDays in KafkaUserModelCertificateHandlingTest Signed-off-by: Lukas Kral <lukywill16@gmail.com> crds 🤦 Signed-off-by: Lukas Kral <lukywill16@gmail.com> update API docs and add ST for this change Signed-off-by: Lukas Kral <lukywill16@gmail.com>
1 parent 42522ab commit ae7ec92

21 files changed

Lines changed: 600 additions & 107 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
## 1.1.0
44

5-
* _Nothing here yet, but we will surely develop something new pretty soon_ 😉
5+
* Add possibility to configure mTLS `validityDays` and `renewalDays` for each `KafkaUser`
66

77
### Major changes, deprecations, and removals
88

@@ -42,14 +42,6 @@
4242
* Update HTTP bridge to 1.0.0.
4343
* `/metrics` endpoint is no longer available on the regular HTTP interface (port 8080 by default). It is now available on the HTTP management interface, 8081.
4444
Users upgrading to Strimzi 1.0.0+ should check all monitoring configurations that scrape Kafka Bridge metrics and update them to use port 8081 instead of 8080 or any other non-default port before or immediately after the upgrade to avoid metrics collection failures.
45-
* Standalone Topic Operator now reads certificates directly from the Kubernetes Secrets in PEM format instead of using JKS/P12 keystore and truststore files.
46-
If you use the standalone Topic Operator and you have any custom configuration related to TLS certificates, you might need to update it during the upgrade to Strimzi 1.0.0.
47-
* Make sure the Topic Operator has the Kubernetes RBAC rights to read the certificate Secrets
48-
* Use the environment variable `STRIMZI_TLS_TRUSTED_CERTS_SECRET_NAME` to configure the CA certificates for TLS encryption when connecting to the Apache Kafka cluster.
49-
* Use the environment variables `STRIMZI_TLS_SECRET_NAME`, `STRIMZI_TLS_KEY_NAME`, and `STRIMZI_TLS_CERT_NAME` to configure client certificate for the mTLS authentication when connecting to the Apache Kafka cluster.
50-
* Use the environment variable `STRIMZI_CLUSTER_NAMESPACE` to define the namespace where the TLS Secrets are.
51-
* If you want to use TLS encryption with an Apache Kafka cluster using server certificates signed by a public CA, you just need to use the `STRIMZI_SSL_ENDPOINT_IDENTIFICATION_ALGORITHM` variable and set it to `TLS`.
52-
* The `STRIMZI_TLS_ENABLED`, `STRIMZI_TLS_AUTH_ENABLED`, `STRIMZI_PUBLIC_CA`, `STRIMZI_TRUSTSTORE_LOCATION`, `STRIMZI_TRUSTSTORE_PASSWORD`, `STRIMZI_KEYSTORE_LOCATION`, and `STRIMZI_KEYSTORE_PASSWORD` environment variables are not used anymore and will be ignored if set.
5345

5446
## 0.51.0
5547

api/src/main/java/io/strimzi/api/kafka/model/user/KafkaUserAuthentication.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,20 @@
88
import com.fasterxml.jackson.annotation.JsonSubTypes;
99
import com.fasterxml.jackson.annotation.JsonTypeInfo;
1010
import io.strimzi.api.kafka.model.common.UnknownPropertyPreserving;
11+
import io.strimzi.crdgenerator.annotations.CelValidation;
1112
import io.strimzi.crdgenerator.annotations.Description;
1213
import lombok.EqualsAndHashCode;
1314
import lombok.ToString;
1415

1516
import java.util.HashMap;
1617
import java.util.Map;
1718

19+
@CelValidation(rules = {
20+
@CelValidation.CelValidationRule(
21+
rule = "self.type == 'tls' || (!has(self.validityDays) && !has(self.renewalDays))",
22+
message = "'validityDays' and 'renewalDays' can be configured only with 'type: tls'"
23+
)
24+
})
1825
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME,
1926
include = JsonTypeInfo.As.EXISTING_PROPERTY,
2027
property = "type")

api/src/main/java/io/strimzi/api/kafka/model/user/KafkaUserTlsClientAuthentication.java

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import com.fasterxml.jackson.annotation.JsonInclude;
88
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
99
import io.strimzi.api.kafka.model.common.Constants;
10+
import io.strimzi.crdgenerator.annotations.CelValidation;
1011
import io.strimzi.crdgenerator.annotations.Description;
1112
import io.sundr.builder.annotations.Buildable;
1213
import lombok.EqualsAndHashCode;
@@ -17,18 +18,60 @@
1718
builderPackage = Constants.FABRIC8_KUBERNETES_API
1819
)
1920
@JsonInclude(JsonInclude.Include.NON_NULL)
20-
@JsonPropertyOrder({"type"})
21+
@JsonPropertyOrder({"type", "validityDays", "renewalDays"})
2122
@EqualsAndHashCode(callSuper = true)
2223
@ToString(callSuper = true)
2324
public class KafkaUserTlsClientAuthentication extends KafkaUserAuthentication {
2425
public static final String TYPE_TLS = "tls";
2526

27+
private Integer validityDays;
28+
private Integer renewalDays;
29+
2630
@Description("Must be `" + TYPE_TLS + "`")
2731
@JsonInclude(JsonInclude.Include.NON_NULL)
2832
@Override
2933
public String getType() {
3034
return TYPE_TLS;
3135
}
3236

37+
@CelValidation(rules = {
38+
@CelValidation.CelValidationRule(
39+
rule = "self > 0",
40+
message = "'validityDays' has to be higher than 0."
41+
)
42+
})
43+
@Description(
44+
"Number of days for which the user certificate should be valid. " +
45+
"If not configured, default User Operator value is used. " +
46+
"If new validity policy would make the current certificate expired or current certificate's validity period would exceed new policy, " +
47+
"the certificate is immediately renewed, without waiting for maintenance window. "
48+
)
49+
@JsonInclude(value = JsonInclude.Include.NON_NULL)
50+
public Integer getValidityDays() {
51+
return this.validityDays;
52+
}
53+
54+
public void setValidityDays(Integer validityDays) {
55+
this.validityDays = validityDays;
56+
}
57+
58+
@CelValidation(rules = {
59+
@CelValidation.CelValidationRule(
60+
rule = "self > 0",
61+
message = "'renewalDays' has to be higher than 0."
62+
)
63+
})
64+
@Description(
65+
"Configures how many days before the certificate expiration should be the user certificate renewed. " +
66+
"If not configured, default User Operator value is used."
67+
)
68+
@JsonInclude(value = JsonInclude.Include.NON_NULL)
69+
public Integer getRenewalDays() {
70+
return renewalDays;
71+
}
72+
73+
public void setRenewalDays(Integer renewalDays) {
74+
this.renewalDays = renewalDays;
75+
}
3376

3477
}

api/src/test/resources/crds/v1/044-Crd-kafkauser.yaml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,16 +80,31 @@ spec:
8080
required:
8181
- valueFrom
8282
description: "Specify the password for the user. If not set, a new password is generated by the User Operator."
83+
renewalDays:
84+
type: integer
85+
description: "Configures how many days before the certificate expiration should be the user certificate renewed. If not configured, default User Operator value is used."
86+
x-kubernetes-validations:
87+
- rule: self > 0
88+
message: '''renewalDays'' has to be higher than 0.'
8389
type:
8490
type: string
8591
enum:
8692
- tls
8793
- tls-external
8894
- scram-sha-512
8995
description: Authentication type.
96+
validityDays:
97+
type: integer
98+
description: "Number of days for which the user certificate should be valid. If not configured, default User Operator value is used. If new validity policy would make the current certificate expired or current certificate's validity period would exceed new policy, the certificate is immediately renewed, without waiting for maintenance window. "
99+
x-kubernetes-validations:
100+
- rule: self > 0
101+
message: '''validityDays'' has to be higher than 0.'
90102
required:
91103
- type
92104
description: "Authentication mechanism enabled for this Kafka user. The supported authentication mechanisms are `scram-sha-512`, `tls`, and `tls-external`. \n\n* `scram-sha-512` generates a secret with SASL SCRAM-SHA-512 credentials.\n* `tls` generates a secret with user certificate for mutual TLS authentication.\n* `tls-external` does not generate a user certificate. But prepares the user for using mutual TLS authentication using a user certificate generated outside the User Operator.\n ACLs and quotas set for this user are configured in the `CN=<username>` format.\n\nAuthentication is optional. If authentication is not configured, no credentials are generated. ACLs and quotas set for the user are configured in the `<username>` format suitable for SASL authentication."
105+
x-kubernetes-validations:
106+
- rule: self.type == 'tls' || (!has(self.validityDays) && !has(self.renewalDays))
107+
message: "'validityDays' and 'renewalDays' can be configured only with 'type: tls'"
93108
authorization:
94109
type: object
95110
properties:

api/src/test/resources/crds/v1beta2/044-Crd-kafkauser.yaml

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,16 +80,31 @@ spec:
8080
required:
8181
- valueFrom
8282
description: "Specify the password for the user. If not set, a new password is generated by the User Operator."
83+
renewalDays:
84+
type: integer
85+
description: "Configures how many days before the certificate expiration should be the user certificate renewed. If not configured, default User Operator value is used."
86+
x-kubernetes-validations:
87+
- rule: self > 0
88+
message: '''renewalDays'' has to be higher than 0.'
8389
type:
8490
type: string
8591
enum:
8692
- tls
8793
- tls-external
8894
- scram-sha-512
8995
description: Authentication type.
96+
validityDays:
97+
type: integer
98+
description: "Number of days for which the user certificate should be valid. If not configured, default User Operator value is used. If new validity policy would make the current certificate expired or current certificate's validity period would exceed new policy, the certificate is immediately renewed, without waiting for maintenance window. "
99+
x-kubernetes-validations:
100+
- rule: self > 0
101+
message: '''validityDays'' has to be higher than 0.'
90102
required:
91103
- type
92104
description: "Authentication mechanism enabled for this Kafka user. The supported authentication mechanisms are `scram-sha-512`, `tls`, and `tls-external`. \n\n* `scram-sha-512` generates a secret with SASL SCRAM-SHA-512 credentials.\n* `tls` generates a secret with user certificate for mutual TLS authentication.\n* `tls-external` does not generate a user certificate. But prepares the user for using mutual TLS authentication using a user certificate generated outside the User Operator.\n ACLs and quotas set for this user are configured in the `CN=<username>` format.\n\nAuthentication is optional. If authentication is not configured, no credentials are generated. ACLs and quotas set for the user are configured in the `<username>` format suitable for SASL authentication."
105+
x-kubernetes-validations:
106+
- rule: self.type == 'tls' || (!has(self.validityDays) && !has(self.renewalDays))
107+
message: "'validityDays' and 'renewalDays' can be configured only with 'type: tls'"
93108
authorization:
94109
type: object
95110
properties:
@@ -298,14 +313,27 @@ spec:
298313
type: boolean
299314
required:
300315
- valueFrom
316+
renewalDays:
317+
type: integer
318+
x-kubernetes-validations:
319+
- rule: self > 0
320+
message: '''renewalDays'' has to be higher than 0.'
301321
type:
302322
type: string
303323
enum:
304324
- tls
305325
- tls-external
306326
- scram-sha-512
327+
validityDays:
328+
type: integer
329+
x-kubernetes-validations:
330+
- rule: self > 0
331+
message: '''validityDays'' has to be higher than 0.'
307332
required:
308333
- type
334+
x-kubernetes-validations:
335+
- rule: self.type == 'tls' || (!has(self.validityDays) && !has(self.renewalDays))
336+
message: "'validityDays' and 'renewalDays' can be configured only with 'type: tls'"
309337
authorization:
310338
type: object
311339
properties:
@@ -494,14 +522,27 @@ spec:
494522
type: boolean
495523
required:
496524
- valueFrom
525+
renewalDays:
526+
type: integer
527+
x-kubernetes-validations:
528+
- rule: self > 0
529+
message: '''renewalDays'' has to be higher than 0.'
497530
type:
498531
type: string
499532
enum:
500533
- tls
501534
- tls-external
502535
- scram-sha-512
536+
validityDays:
537+
type: integer
538+
x-kubernetes-validations:
539+
- rule: self > 0
540+
message: '''validityDays'' has to be higher than 0.'
503541
required:
504542
- type
543+
x-kubernetes-validations:
544+
- rule: self.type == 'tls' || (!has(self.validityDays) && !has(self.renewalDays))
545+
message: "'validityDays' and 'renewalDays' can be configured only with 'type: tls'"
505546
authorization:
506547
type: object
507548
properties:
@@ -690,14 +731,27 @@ spec:
690731
type: boolean
691732
required:
692733
- valueFrom
734+
renewalDays:
735+
type: integer
736+
x-kubernetes-validations:
737+
- rule: self > 0
738+
message: '''renewalDays'' has to be higher than 0.'
693739
type:
694740
type: string
695741
enum:
696742
- tls
697743
- tls-external
698744
- scram-sha-512
745+
validityDays:
746+
type: integer
747+
x-kubernetes-validations:
748+
- rule: self > 0
749+
message: '''validityDays'' has to be higher than 0.'
699750
required:
700751
- type
752+
x-kubernetes-validations:
753+
- rule: self.type == 'tls' || (!has(self.validityDays) && !has(self.renewalDays))
754+
message: "'validityDays' and 'renewalDays' can be configured only with 'type: tls'"
701755
authorization:
702756
type: object
703757
properties:

development-docs/systemtests/io.strimzi.systemtest.operators.user.UserST.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,28 @@
9797
* [user-operator](labels/user-operator.md)
9898

9999

100+
## testTlsValidityDays
101+
102+
**Description:** Verifies functionality of the mTLS `validityDays` and `renewalDays` configured inside each KafkaUser.
103+
104+
**Steps:**
105+
106+
| Step | Action | Result |
107+
| - | - | - |
108+
| 1. | Create `KafkaTopic` to which we will send (and from which we will receive) messages - created in existing Kafka cluster. | `KafkaTopic` is created. |
109+
| 2. | Create `KafkaUser` with TLS authentication; together with default `validityDays` (200 days) and `renewalDays` (20 days) - configured in User operator. | `KafkaUser` is created with defaults. |
110+
| 3. | Obtain the `KafkaUser`'s `Secret` and check validity period of the user certificate. | Validity period should be default - 200 days. |
111+
| 4. | Do message transmission to verify, that we are able to connect to Kafka cluster with the TLS `KafkaUser`. | Messages are successfully sent and received. |
112+
| 5. | Change the `validityDays` and `renewalDays` in the `KafkaUser` `.spec.authentication` to 60 and 10. | The `validityDays` and `renewalDays` should be changed in the `KafkaUser`. |
113+
| 6. | Because the current certificate would exceed the new validity period, `KafkaUser`'s `Secret` and user certificate should be renewed - we are waiting for the certificate change. | The user certificate was changed. |
114+
| 7. | Obtain the `KafkaUser`'s `Secret` again and check the validity period of the user certificate. | Validity period should be 60 days. |
115+
| 8. | Do message transmission again to verify, that we are able to connect to Kafka cluster with the new user's certificate. | Messages are successfully sent and received using new certificate. |
116+
117+
**Labels:**
118+
119+
* [user-operator](labels/user-operator.md)
120+
121+
100122
## testUpdateUser
101123

102124
**Description:** Verifies updating a Kafka user from TLS to SCRAM-SHA-512 authentication and validates user secret contents.

development-docs/systemtests/labels/user-operator.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ They verify user authentication mechanisms (TLS, SCRAM-SHA-512, external TLS), a
1515
- [testTlsExternalUser](../io.strimzi.systemtest.operators.user.UserST.md)
1616
- [testTlsExternalUserWithQuotas](../io.strimzi.systemtest.operators.user.UserST.md)
1717
- [testTlsUserWithQuotas](../io.strimzi.systemtest.operators.user.UserST.md)
18+
- [testTlsValidityDays](../io.strimzi.systemtest.operators.user.UserST.md)
1819
- [testUpdateUser](../io.strimzi.systemtest.operators.user.UserST.md)
1920
- [testUserWithNameMoreThan64Chars](../io.strimzi.systemtest.operators.user.UserST.md)
2021
- [testUserWithQuotas](../io.strimzi.systemtest.operators.user.UserST.md)

documentation/modules/appendix_crds.adoc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2675,6 +2675,12 @@ It must have the value `tls` for the type `KafkaUserTlsClientAuthentication`.
26752675
|type
26762676
|string
26772677
|Must be `tls`.
2678+
|validityDays
2679+
|integer
2680+
|Number of days for which the user certificate should be valid. If not configured, default User Operator value is used. If new validity policy would make the current certificate expired or current certificate's validity period would exceed new policy, the certificate is immediately renewed, without waiting for maintenance window.
2681+
|renewalDays
2682+
|integer
2683+
|Configures how many days before the certificate expiration should be the user certificate renewed. If not configured, default User Operator value is used.
26782684
|====
26792685

26802686
[id='type-KafkaUserTlsExternalClientAuthentication-{context}']

operator-common/src/main/java/io/strimzi/operator/common/model/Ca.java

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -542,6 +542,41 @@ public boolean isExpiring(Secret secret, String certKey) {
542542
return certNeedsRenewal(currentCert);
543543
}
544544

545+
/**
546+
* Checks if the validityDays configuration is different from previous state.
547+
* If yes, the method checks if the new configuration of validityDays would make the certificate expired
548+
* or if the current Certificate's validity period exceeds the new policy.
549+
*
550+
* @param secret Secret with the certificate
551+
* @param certKey Key under which is the certificate stored
552+
*
553+
* @return True if the certificate should be renewed due to new validity period. False otherwise.
554+
*/
555+
public boolean requiresImmediateRenewalDueToValidityChange(Secret secret, String certKey) {
556+
X509Certificate currentCert = cert(secret, certKey);
557+
558+
Instant notBefore = currentCert.getNotBefore().toInstant();
559+
Instant notAfter = currentCert.getNotAfter().toInstant();
560+
int currentValidityDays = (int) ChronoUnit.DAYS.between(notBefore, notAfter);
561+
562+
if (currentValidityDays != validityDays) {
563+
Instant wouldExpireUnderNewPolicy = notBefore.plus(validityDays, ChronoUnit.DAYS);
564+
565+
if (!this.clock.instant().isBefore(wouldExpireUnderNewPolicy)) {
566+
LOGGER.infoCr(reconciliation, "Certificate of Secret {}/{} would be expired under new validity policy, it will be renewed",
567+
secret.getMetadata().getNamespace(), secret.getMetadata().getName());
568+
return true;
569+
}
570+
if (currentValidityDays > validityDays) {
571+
LOGGER.infoCr(reconciliation, "Certificate's current validity period of Secret {}/{} exceeds new policy, it will be renewed",
572+
secret.getMetadata().getNamespace(), secret.getMetadata().getName());
573+
return true;
574+
}
575+
}
576+
577+
return false;
578+
}
579+
545580
/**
546581
* Returns whether the certificate is expiring or not
547582
*

0 commit comments

Comments
 (0)