Skip to content

Commit 40862c0

Browse files
committed
fix: Certificate handling in case SecretClass has multiple CAs
1 parent 24658ef commit 40862c0

9 files changed

Lines changed: 131 additions & 114 deletions

File tree

_WORK/Dockerfile

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# FROM stackable/image/stackable-devel AS truststore-merger
2+
FROM registry.access.redhat.com/ubi9/ubi-minimal@sha256:383329bf9c4f968e87e85d30ba3a5cb988a3bbde28b8e4932dcd3a025fd9c98c AS truststore-merger
3+
4+
5+
RUN microdnf update -y
6+
RUN microdnf install -y gcc findutils git
7+
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
8+
9+
ENV CARGO_CYCLONEDX_CRATE_VERSION=0.5.7
10+
ENV CARGO_AUDITABLE_CRATE_VERSION=0.6.6
11+
RUN . "$HOME/.cargo/env" && cargo --locked --quiet install cargo-cyclonedx@"$CARGO_CYCLONEDX_CRATE_VERSION" cargo-auditable@"$CARGO_AUDITABLE_CRATE_VERSION" && rustup toolchain install
12+
13+
RUN microdnf install -y openssl-devel pkg-config
14+
15+
16+
17+
18+
19+
20+
21+
22+
23+
24+
25+
26+
27+
# TRASH ABOVE!!!
28+
29+
# docker build . -t oci.stackable.tech/sdp/trino:476-stackable0.0.0-dev-with-merger
30+
31+
RUN echo "invalidate cache 2 :)"
32+
33+
RUN <<EOF
34+
git clone --depth 1 --branch "feat/truststore-merger" https://github.com/stackabletech/secret-operator
35+
cd ./secret-operator
36+
. "$HOME/.cargo/env"
37+
cargo auditable --quiet build --release --package truststore-merger && cargo cyclonedx --all --spec-version 1.5 --describe binaries
38+
EOF
39+
40+
FROM oci.stackable.tech/sdp/trino:476-stackable0.0.0-dev AS final
41+
42+
COPY --from=truststore-merger --chown=stackable:0 /secret-operator/target/release/truststore-merger /stackable/truststore-merger

_WORK/README.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
2+
### Create problem
3+
4+
```bash
5+
stackablectl op in commons listener secret trino
6+
kind load docker-image oci.stackable.tech/sdp/trino:476-stackable0.0.0-dev-with-merger
7+
kubectl apply -f _WORK/trino.yaml
8+
```
9+
The Trino Pod will not came up because of `backend failed to get secret data: failed to pick a CA: no CA in Secret.v1./secret-provisioner-short-tls-ca.stackable-operators will live until at least 2025-09-04 8:03:42.030007063 +00:00:00`.
10+
That's totally expected!
11+
12+
Let's wait until the CA certificate Secrets have the desired amount of rotated certificates.
13+
14+
Afterwards increase the ca cert lifetime to start the Pod:
15+
16+
```bash
17+
kubectl patch secretclass short-tls --type=merge --patch '{"spec": {"backend": {"autoTls": {"ca": {"caCertificateLifetime": "365d"}}}}}'
18+
```
19+
20+
Congrats, your secret-op now did a certificate rotation and your Pod should start up (after some time for retries)!
21+
22+
### Debug commands
23+
24+
```bash
25+
keytool -list -storepass "" -keystore /certs/pkcs12-1/truststore.p12
26+
openssl pkcs12 -password pass: -in /certs/pkcs12-1/truststore.p12
27+
```

_WORK/trino.yaml

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
apiVersion: trino.stackable.tech/v1alpha1
2+
kind: TrinoCluster
3+
metadata:
4+
name: trino
5+
spec:
6+
image:
7+
productVersion: "476"
8+
custom: oci.stackable.tech/sdp/trino:476-stackable0.0.0-dev-with-merger
9+
pullPolicy: Never
10+
clusterConfig:
11+
tls:
12+
serverSecretClass: short-tls
13+
catalogLabelSelector:
14+
matchLabels:
15+
trino: simple-trino
16+
coordinators:
17+
roleConfig:
18+
listenerClass: external-unstable
19+
roleGroups:
20+
default:
21+
replicas: 1
22+
workers:
23+
roleGroups:
24+
default:
25+
replicas: 1
26+
---
27+
apiVersion: secrets.stackable.tech/v1alpha1
28+
kind: SecretClass
29+
metadata:
30+
name: short-tls
31+
spec:
32+
backend:
33+
autoTls:
34+
ca:
35+
secret:
36+
name: secret-provisioner-short-tls-ca
37+
namespace: stackable-operators
38+
autoGenerate: true
39+
caCertificateLifetime: 1m

rust/operator-binary/src/authentication/oidc/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,7 @@ impl TrinoOidcAuthentication {
184184
oauth2_authentication_config.add_commands(
185185
TrinoRole::Coordinator,
186186
crate::crd::Container::Prepare,
187-
command::add_cert_to_truststore(&path, STACKABLE_CLIENT_TLS_DIR, "oidc-idp"),
187+
command::add_cert_to_truststore(&path, STACKABLE_CLIENT_TLS_DIR),
188188
);
189189
}
190190
}

rust/operator-binary/src/catalog/commons.rs

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ impl ExtendCatalogConfig for s3::v1alpha1::InlineConnectionOrReference {
7777
async fn extend_catalog_config(
7878
&self,
7979
catalog_config: &mut CatalogConfig,
80-
catalog_name: &str,
80+
_catalog_name: &str,
8181
catalog_namespace: Option<String>,
8282
client: &Client,
8383
trino_version: u16,
@@ -151,11 +151,7 @@ impl ExtendCatalogConfig for s3::v1alpha1::InlineConnectionOrReference {
151151
}) => {
152152
if let Some(ca_cert) = s3.tls.tls_ca_cert_mount_path() {
153153
catalog_config.init_container_extra_start_commands.extend(
154-
command::add_cert_to_truststore(
155-
&ca_cert,
156-
STACKABLE_CLIENT_TLS_DIR,
157-
&format!("{catalog_name}-ca-cert"),
158-
),
154+
command::add_cert_to_truststore(&ca_cert, STACKABLE_CLIENT_TLS_DIR),
159155
);
160156
}
161157
}

rust/operator-binary/src/command.rs

Lines changed: 17 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,7 @@ use crate::{
1414
CONFIG_DIR_NAME, Container, LOG_PROPERTIES, RW_CONFIG_DIR_NAME, STACKABLE_CLIENT_TLS_DIR,
1515
STACKABLE_INTERNAL_TLS_DIR, STACKABLE_MOUNT_INTERNAL_TLS_DIR,
1616
STACKABLE_MOUNT_SERVER_TLS_DIR, STACKABLE_SERVER_TLS_DIR, STACKABLE_TLS_STORE_PASSWORD,
17-
SYSTEM_TRUST_STORE, SYSTEM_TRUST_STORE_PASSWORD, TrinoRole,
18-
fault_tolerant_execution::ResolvedFaultTolerantExecutionConfig, v1alpha1,
17+
TrinoRole, fault_tolerant_execution::ResolvedFaultTolerantExecutionConfig, v1alpha1,
1918
},
2019
};
2120

@@ -43,38 +42,23 @@ pub fn container_prepare_args(
4342
));
4443
}
4544

45+
// Create truststore that will be used when talking to external tools like S3
46+
// It will be populated from the system truststore so that connections against public services like AWS S3 are still possible
47+
args.push(format!("truststore-merger --pem /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem --out {STACKABLE_CLIENT_TLS_DIR}/truststore.p12 --out-password {STACKABLE_TLS_STORE_PASSWORD}"));
48+
4649
if trino.tls_enabled() {
47-
args.extend(import_truststore(
48-
STACKABLE_MOUNT_SERVER_TLS_DIR,
49-
STACKABLE_SERVER_TLS_DIR,
50-
));
51-
args.extend(import_keystore(
52-
STACKABLE_MOUNT_SERVER_TLS_DIR,
53-
STACKABLE_SERVER_TLS_DIR,
54-
));
50+
args.push(format!("cp {STACKABLE_MOUNT_SERVER_TLS_DIR}/truststore.p12 {STACKABLE_SERVER_TLS_DIR}/truststore.p12"));
51+
args.push(format!("cp {STACKABLE_MOUNT_SERVER_TLS_DIR}/keystore.p12 {STACKABLE_SERVER_TLS_DIR}/keystore.p12"));
5552
}
5653

5754
if trino.get_internal_tls().is_some() {
58-
args.extend(import_truststore(
59-
STACKABLE_MOUNT_INTERNAL_TLS_DIR,
60-
STACKABLE_INTERNAL_TLS_DIR,
61-
));
62-
args.extend(import_keystore(
63-
STACKABLE_MOUNT_INTERNAL_TLS_DIR,
64-
STACKABLE_INTERNAL_TLS_DIR,
65-
));
55+
args.push(format!("cp {STACKABLE_MOUNT_INTERNAL_TLS_DIR}/truststore.p12 {STACKABLE_INTERNAL_TLS_DIR}/truststore.p12"));
56+
args.push(format!("cp {STACKABLE_MOUNT_INTERNAL_TLS_DIR}/keystore.p12 {STACKABLE_INTERNAL_TLS_DIR}/keystore.p12"));
6657
if trino.tls_enabled() {
67-
args.extend(import_truststore(
68-
STACKABLE_MOUNT_SERVER_TLS_DIR,
69-
STACKABLE_INTERNAL_TLS_DIR,
70-
))
58+
args.push(format!("truststore-merger --pkcs12 {STACKABLE_MOUNT_SERVER_TLS_DIR}/truststore.p12:{STACKABLE_TLS_STORE_PASSWORD} --pkcs12 {STACKABLE_INTERNAL_TLS_DIR}/truststore.p12:{STACKABLE_TLS_STORE_PASSWORD} --out {STACKABLE_INTERNAL_TLS_DIR}/truststore.p12 --out-password {STACKABLE_TLS_STORE_PASSWORD}"));
7159
}
7260
}
7361

74-
// Create truststore that will be used when talking to external tools like S3
75-
// It will be populated from the system truststore so that connections against public services like AWS S3 are still possible
76-
args.extend(import_system_truststore(STACKABLE_CLIENT_TLS_DIR));
77-
7862
// Add the commands that are needed to set up the catalogs
7963
catalogs.iter().for_each(|catalog| {
8064
args.extend_from_slice(&catalog.init_container_extra_start_commands);
@@ -148,77 +132,11 @@ wait_for_termination $!
148132
args
149133
}
150134

151-
/// Adds a CA file from `cert_file` into a truststore named `truststore.p12` in `destination_directory`
152-
/// under the alias `alias_name`.
153-
pub fn add_cert_to_truststore(
154-
cert_file: &str,
155-
destination_directory: &str,
156-
alias_name: &str,
157-
) -> Vec<String> {
158-
vec![
159-
format!(
160-
"echo Adding cert from {cert_file} to truststore {destination_directory}/truststore.p12"
161-
),
162-
format!(
163-
"keytool -importcert -file {cert_file} -keystore {destination_directory}/truststore.p12 -storetype pkcs12 -noprompt -alias {alias_name} -storepass {STACKABLE_TLS_STORE_PASSWORD}"
164-
),
165-
]
166-
}
167-
168-
/// Generates the shell script to import a secret operator provided keystore without password
169-
/// into a new keystore with password in a writeable empty dir
170-
///
171-
/// # Arguments
172-
/// - `source_directory`: The directory of the source keystore. Should usually be a secret operator volume mount.
173-
/// - `destination_directory`: The directory of the destination keystore. Should usually be an empty dir.
174-
fn import_keystore(source_directory: &str, destination_directory: &str) -> Vec<String> {
175-
vec![
176-
// The source directory is a secret-op mount and we do not want to write / add anything in there
177-
// Therefore we import all the contents to a keystore in "writeable" empty dirs.
178-
// Keytool is only barking if a password is not set for the destination keystore (which we set)
179-
// and do provide an empty password for the source keystore coming from the secret-operator.
180-
// Using no password will result in a warning.
181-
format!(
182-
"echo Importing {source_directory}/keystore.p12 to {destination_directory}/keystore.p12"
183-
),
184-
format!(
185-
"keytool -importkeystore -srckeystore {source_directory}/keystore.p12 -srcstoretype PKCS12 -srcstorepass \"\" -destkeystore {destination_directory}/keystore.p12 -deststoretype PKCS12 -deststorepass {STACKABLE_TLS_STORE_PASSWORD} -noprompt"
186-
),
187-
]
188-
}
189-
190-
/// Generates the shell script to import a secret operator provided truststore without password
191-
/// into a new truststore with password in a writeable empty dir
192-
///
193-
/// # Arguments
194-
/// - `source_directory`: The directory of the source truststore. Should usually be a secret operator volume mount.
195-
/// - `destination_directory`: The directory of the destination truststore. Should usually be an empty dir.
196-
fn import_truststore(source_directory: &str, destination_directory: &str) -> Vec<String> {
197-
vec![
198-
// The source directory is a secret-op mount and we do not want to write / add anything in there
199-
// Therefore we import all the contents to a truststore in "writeable" empty dirs.
200-
// Keytool is only barking if a password is not set for the destination truststore (which we set)
201-
// and do provide an empty password for the source truststore coming from the secret-operator.
202-
// Using no password will result in a warning.
203-
// All secret-op generated truststores have one entry with alias "1". We generate a UUID for
204-
// the destination truststore to avoid conflicts when importing multiple secret-op generated
205-
// truststores. We do not use the UUID rust crate since this will continuously change the STS... and
206-
// leads to never-ending reconciles.
207-
format!(
208-
"echo Importing {source_directory}/truststore.p12 to {destination_directory}/truststore.p12"
209-
),
210-
format!(
211-
"keytool -importkeystore -srckeystore {source_directory}/truststore.p12 -srcstoretype PKCS12 -srcstorepass \"\" -srcalias 1 -destkeystore {destination_directory}/truststore.p12 -deststoretype PKCS12 -deststorepass {STACKABLE_TLS_STORE_PASSWORD} -destalias $(cat /proc/sys/kernel/random/uuid) -noprompt"
212-
),
213-
]
214-
}
215-
216-
/// Import the system truststore to a truststore named `truststore.p12` in `destination_directory`.
217-
fn import_system_truststore(destination_directory: &str) -> Vec<String> {
218-
vec![
219-
format!("echo Importing {SYSTEM_TRUST_STORE} to {destination_directory}/truststore.p12"),
220-
format!(
221-
"keytool -importkeystore -srckeystore {SYSTEM_TRUST_STORE} -srcstoretype jks -srcstorepass {SYSTEM_TRUST_STORE_PASSWORD} -destkeystore {destination_directory}/truststore.p12 -deststoretype pkcs12 -deststorepass {STACKABLE_TLS_STORE_PASSWORD} -noprompt"
222-
),
223-
]
135+
/// Adds a PEM file to configured PKCS12 truststore (using the [`STACKABLE_TLS_STORE_PASSWORD`]
136+
/// password)
137+
pub fn add_cert_to_truststore(cert_file: &str, destination_directory: &str) -> Vec<String> {
138+
let truststore = format!("{destination_directory}/truststore.p12");
139+
vec![format!(
140+
"truststore-merger --pkcs12 {truststore}:{STACKABLE_TLS_STORE_PASSWORD} --pem {cert_file} --out {truststore} --out-password {STACKABLE_TLS_STORE_PASSWORD}"
141+
)]
224142
}

rust/operator-binary/src/controller.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ use crate::{
8686
MAX_TRINO_LOG_FILES_SIZE, METRICS_PORT, METRICS_PORT_NAME, NODE_PROPERTIES,
8787
RW_CONFIG_DIR_NAME, STACKABLE_CLIENT_TLS_DIR, STACKABLE_INTERNAL_TLS_DIR,
8888
STACKABLE_MOUNT_INTERNAL_TLS_DIR, STACKABLE_MOUNT_SERVER_TLS_DIR, STACKABLE_SERVER_TLS_DIR,
89-
TrinoRole,
89+
STACKABLE_TLS_STORE_PASSWORD, TrinoRole,
9090
authentication::resolve_authentication_classes,
9191
catalog,
9292
discovery::{TrinoDiscovery, TrinoDiscoveryProtocol, TrinoPodRef},
@@ -1576,6 +1576,7 @@ fn create_tls_volume(
15761576
secret_volume_source_builder
15771577
.with_pod_scope()
15781578
.with_format(SecretFormat::TlsPkcs12)
1579+
.with_tls_pkcs12_password(STACKABLE_TLS_STORE_PASSWORD)
15791580
.with_auto_tls_cert_lifetime(*requested_secret_lifetime);
15801581

15811582
if let Some(listener_scope) = &listener_scope {

rust/operator-binary/src/crd/fault_tolerant_execution.rs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -545,11 +545,7 @@ impl ResolvedFaultTolerantExecutionConfig {
545545
}) => {
546546
if let Some(ca_cert) = s3_connection.tls.tls_ca_cert_mount_path() {
547547
self.init_container_extra_start_commands.extend(
548-
command::add_cert_to_truststore(
549-
&ca_cert,
550-
STACKABLE_CLIENT_TLS_DIR,
551-
"exchange-s3-ca-cert",
552-
),
548+
command::add_cert_to_truststore(&ca_cert, STACKABLE_CLIENT_TLS_DIR),
553549
);
554550
}
555551
}

rust/operator-binary/src/crd/mod.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -99,10 +99,8 @@ pub const STACKABLE_INTERNAL_TLS_DIR: &str = "/stackable/internal_tls";
9999
pub const STACKABLE_LOG_DIR: &str = "/stackable/log";
100100
pub const STACKABLE_MOUNT_SERVER_TLS_DIR: &str = "/stackable/mount_server_tls";
101101
pub const STACKABLE_MOUNT_INTERNAL_TLS_DIR: &str = "/stackable/mount_internal_tls";
102-
pub const SYSTEM_TRUST_STORE: &str = "/etc/pki/java/cacerts";
103102
// store pws
104103
pub const STACKABLE_TLS_STORE_PASSWORD: &str = "changeit";
105-
pub const SYSTEM_TRUST_STORE_PASSWORD: &str = "changeit";
106104
// secret vars
107105
pub const ENV_INTERNAL_SECRET: &str = "INTERNAL_SECRET";
108106
// TLS

0 commit comments

Comments
 (0)