Skip to content

Commit c7c4b15

Browse files
committed
add ca-certs to gitsync set-up
1 parent 2ba637e commit c7c4b15

3 files changed

Lines changed: 247 additions & 1 deletion

File tree

crates/stackable-operator/crds/DummyCluster.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,12 @@ spec:
8484
8585
Since git-sync v4.x.x this field is mapped to the flag `--ref`.
8686
type: string
87+
caCertSecretName:
88+
description: |-
89+
An optional secret used for holding CA certificates that will be used to verify the git server's TLS certificate by passing it to the git config option `http.sslCAInfo` passed with the gitsync command. The secret must have a key named `ca.crt` whose value is the PEM-encoded certificate bundle.
90+
If `http.sslCAInfo` is also set via `gitSyncConf` (the `--git-config` option) then a warning will logged and the explicit field value will take precendence.
91+
nullable: true
92+
type: string
8793
credentials:
8894
description: An optional secret used for git access.
8995
nullable: true

crates/stackable-operator/src/crd/git_sync/mod.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,11 @@ pub mod versioned {
6868
downgrade_with = credentials_to_secret
6969
))]
7070
pub credentials: Option<Credentials>,
71+
72+
/// An optional secret used for holding CA certificates that will be used to verify the git server's TLS certificate by passing it to the git config option `http.sslCAInfo` passed with the gitsync command. The secret must have a key named `ca.crt` whose value is the PEM-encoded certificate bundle.
73+
/// If `http.sslCAInfo` is also set via `gitSyncConf` (the `--git-config` option) then a warning will logged and the explicit field value will take precendence.
74+
#[versioned(added(since = "v1alpha2"))]
75+
pub ca_cert_secret_name: Option<String>,
7176
}
7277

7378
#[derive(strum::Display, Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]

crates/stackable-operator/src/crd/git_sync/v1alpha2_impl.rs

Lines changed: 236 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ pub const SSH_MOUNT_PATH_PREFIX: &str = "/stackable/gitssh";
3030
pub const GIT_SYNC_SAFE_DIR_OPTION: &str = "safe.directory";
3131
pub const GIT_SYNC_ROOT_DIR: &str = "/tmp/git";
3232
pub const GIT_SYNC_LINK: &str = "current";
33+
pub const CA_CERT_VOLUME_NAME_PREFIX: &str = "ca-cert";
34+
pub const CA_CERT_MOUNT_PATH_PREFIX: &str = "/stackable/gitca";
35+
pub const GIT_SSL_CA_INFO_CONFIG_KEY: &str = "http.sslCAInfo";
3336

3437
#[derive(Snafu, Debug, EnumDiscriminants)]
3538
#[strum_discriminants(derive(IntoStaticStr))]
@@ -65,6 +68,9 @@ pub struct GitSyncResources {
6568

6669
/// GitSync volumes containing the synchronized repository
6770
pub git_ssh_volumes: Vec<Volume>,
71+
72+
// GitSync volumes containing Ca certificates
73+
pub git_ca_cert_volumes: Vec<Volume>,
6874
}
6975

7076
impl GitSyncResources {
@@ -155,6 +161,21 @@ impl GitSyncResources {
155161
git_sync_container_volume_mounts.push(ssh_volume_mount);
156162
}
157163

164+
if git_sync.ca_cert_secret_name.is_some() {
165+
let ca_cert_secret_mount_path = format!("{CA_CERT_MOUNT_PATH_PREFIX}-{i}");
166+
let ca_cert_secret_volume_name = format!("{CA_CERT_VOLUME_NAME_PREFIX}-{i}");
167+
168+
let ca_cert_volume_mount =
169+
VolumeMountBuilder::new(ca_cert_secret_volume_name, ca_cert_secret_mount_path)
170+
.build();
171+
git_sync_container_volume_mounts.push(ca_cert_volume_mount);
172+
}
173+
174+
let ca_cert_path: Option<String> = git_sync
175+
.ca_cert_secret_name
176+
.as_ref()
177+
.map(|_| format!("{CA_CERT_MOUNT_PATH_PREFIX}-{i}/ca.crt"));
178+
158179
let container = Self::create_git_sync_container(
159180
&format!("{CONTAINER_NAME_PREFIX}-{i}"),
160181
resolved_product_image,
@@ -163,6 +184,7 @@ impl GitSyncResources {
163184
&env_vars,
164185
&git_sync_container_volume_mounts,
165186
container_log_config,
187+
ca_cert_path.as_deref(),
166188
)?;
167189

168190
let init_container = Self::create_git_sync_container(
@@ -173,6 +195,7 @@ impl GitSyncResources {
173195
&env_vars,
174196
&git_sync_container_volume_mounts,
175197
container_log_config,
198+
ca_cert_path.as_deref(),
176199
)?;
177200

178201
let volume = VolumeBuilder::new(volume_name.clone())
@@ -211,11 +234,20 @@ impl GitSyncResources {
211234
.build();
212235
resources.git_ssh_volumes.push(ssh_secret_volume);
213236
}
237+
238+
if let Some(ca_cert_secret_name) = &git_sync.ca_cert_secret_name {
239+
let ca_cert_secret_volume_name = format!("{CA_CERT_VOLUME_NAME_PREFIX}-{i}");
240+
let ca_cert_secret_volume = VolumeBuilder::new(&ca_cert_secret_volume_name)
241+
.with_secret(ca_cert_secret_name, false)
242+
.build();
243+
resources.git_ca_cert_volumes.push(ca_cert_secret_volume);
244+
}
214245
}
215246

216247
Ok(resources)
217248
}
218249

250+
#[allow(clippy::too_many_arguments)]
219251
fn create_git_sync_container(
220252
container_name: &str,
221253
resolved_product_image: &ResolvedProductImage,
@@ -224,6 +256,7 @@ impl GitSyncResources {
224256
env_vars: &[EnvVar],
225257
volume_mounts: &[VolumeMount],
226258
container_log_config: &ContainerLogConfig,
259+
ca_cert_path: Option<&str>,
227260
) -> Result<k8s_openapi::api::core::v1::Container, Error> {
228261
let container = ContainerBuilder::new(container_name)
229262
.context(InvalidContainerNameSnafu)?
@@ -240,6 +273,7 @@ impl GitSyncResources {
240273
git_sync,
241274
one_time,
242275
container_log_config,
276+
ca_cert_path,
243277
)])
244278
.add_env_vars(env_vars.into())
245279
.add_volume_mounts(volume_mounts.to_vec())
@@ -261,6 +295,7 @@ impl GitSyncResources {
261295
git_sync: &GitSync,
262296
one_time: bool,
263297
container_log_config: &ContainerLogConfig,
298+
ca_cert_path: Option<&str>,
264299
) -> String {
265300
let internal_args = BTreeMap::from([
266301
("--repo".to_string(), git_sync.repo.as_str().to_owned()),
@@ -275,11 +310,15 @@ impl GitSyncResources {
275310
("--one-time".to_string(), one_time.to_string()),
276311
]);
277312

278-
let internal_git_config = BTreeMap::from([(
313+
let mut internal_git_config = BTreeMap::from([(
279314
GIT_SYNC_SAFE_DIR_OPTION.to_owned(),
280315
GIT_SYNC_ROOT_DIR.to_owned(),
281316
)]);
282317

318+
if let Some(path) = ca_cert_path {
319+
internal_git_config.insert(GIT_SSL_CA_INFO_CONFIG_KEY.to_owned(), path.to_owned());
320+
}
321+
283322
let mut git_sync_config = git_sync.git_sync_conf.clone();
284323

285324
// The key and value in Git configs are separated by a colon, but both can contain either
@@ -1108,4 +1147,200 @@ secret:
11081147
serde_yaml::to_string(&git_sync_resources.git_ssh_volumes.first()).unwrap()
11091148
);
11101149
}
1150+
1151+
#[test]
1152+
fn test_git_sync_ca_cert() {
1153+
let git_sync_spec = r#"
1154+
# GitSync using SSH
1155+
- repo: ssh://git@github.com/stackabletech/repo.git
1156+
branch: trunk
1157+
gitFolder: ""
1158+
depth: 3
1159+
wait: 1m
1160+
caCertSecretName: git-ca-cert
1161+
gitSyncConf:
1162+
--rev: HEAD
1163+
"#;
1164+
1165+
let git_syncs: Vec<GitSync> = yaml_from_str_singleton_map(git_sync_spec).unwrap();
1166+
1167+
let resolved_product_image = ResolvedProductImage {
1168+
image: "oci.stackable.tech/sdp/product:latest".to_string(),
1169+
app_version_label_value: "1.0.0-latest"
1170+
.parse()
1171+
.expect("static app version label is always valid"),
1172+
product_version: "1.0.0".to_string(),
1173+
image_pull_policy: "Always".to_string(),
1174+
pull_secrets: None,
1175+
};
1176+
1177+
let extra_env_vars = env_vars_from([("VAR1", "value1")]);
1178+
1179+
let extra_volume_mounts = [VolumeMount {
1180+
name: "extra-volume".to_string(),
1181+
mount_path: "/mnt/extra-volume".to_string(),
1182+
..VolumeMount::default()
1183+
}];
1184+
1185+
let git_sync_resources = GitSyncResources::new(
1186+
&git_syncs,
1187+
&resolved_product_image,
1188+
&extra_env_vars,
1189+
&extra_volume_mounts,
1190+
"log-volume",
1191+
&validate(default_container_log_config()).unwrap(),
1192+
)
1193+
.unwrap();
1194+
1195+
assert!(git_sync_resources.is_git_sync_enabled());
1196+
1197+
assert_eq!(1, git_sync_resources.git_sync_containers.len());
1198+
1199+
assert_eq!(
1200+
r#"args:
1201+
- |-
1202+
mkdir --parents /stackable/log/git-sync-0 && exec > >(tee /stackable/log/git-sync-0/container.stdout.log) 2> >(tee /stackable/log/git-sync-0/container.stderr.log >&2)
1203+
1204+
prepare_signal_handlers()
1205+
{
1206+
unset term_child_pid
1207+
unset term_kill_needed
1208+
trap 'handle_term_signal' TERM
1209+
}
1210+
1211+
handle_term_signal()
1212+
{
1213+
if [ "${term_child_pid}" ]; then
1214+
kill -TERM "${term_child_pid}" 2>/dev/null
1215+
else
1216+
term_kill_needed="yes"
1217+
fi
1218+
}
1219+
1220+
wait_for_termination()
1221+
{
1222+
set +e
1223+
term_child_pid=$1
1224+
if [[ -v term_kill_needed ]]; then
1225+
kill -TERM "${term_child_pid}" 2>/dev/null
1226+
fi
1227+
wait ${term_child_pid} 2>/dev/null
1228+
trap - TERM
1229+
wait ${term_child_pid} 2>/dev/null
1230+
set -e
1231+
}
1232+
1233+
prepare_signal_handlers
1234+
/stackable/git-sync --depth=3 --git-config='http.sslCAInfo:/stackable/gitca-0/ca.crt,safe.directory:/tmp/git' --link=current --one-time=false --period=60s --ref=trunk --repo=ssh://git@github.com/stackabletech/repo.git --rev=HEAD --root=/tmp/git &
1235+
wait_for_termination $!
1236+
command:
1237+
- /bin/bash
1238+
- -x
1239+
- -euo
1240+
- pipefail
1241+
- -c
1242+
env:
1243+
- name: VAR1
1244+
value: value1
1245+
image: oci.stackable.tech/sdp/product:latest
1246+
imagePullPolicy: Always
1247+
name: git-sync-0
1248+
resources:
1249+
limits:
1250+
cpu: 200m
1251+
memory: 64Mi
1252+
requests:
1253+
cpu: 100m
1254+
memory: 64Mi
1255+
volumeMounts:
1256+
- mountPath: /tmp/git
1257+
name: content-from-git-0
1258+
- mountPath: /stackable/log
1259+
name: log-volume
1260+
- mountPath: /mnt/extra-volume
1261+
name: extra-volume
1262+
- mountPath: /stackable/gitca-0
1263+
name: ca-cert-0
1264+
"#,
1265+
serde_yaml::to_string(&git_sync_resources.git_sync_containers.first()).unwrap()
1266+
);
1267+
1268+
assert_eq!(1, git_sync_resources.git_sync_init_containers.len());
1269+
1270+
assert_eq!(
1271+
r#"args:
1272+
- |-
1273+
mkdir --parents /stackable/log/git-sync-0-init && exec > >(tee /stackable/log/git-sync-0-init/container.stdout.log) 2> >(tee /stackable/log/git-sync-0-init/container.stderr.log >&2)
1274+
/stackable/git-sync --depth=3 --git-config='http.sslCAInfo:/stackable/gitca-0/ca.crt,safe.directory:/tmp/git' --link=current --one-time=true --period=60s --ref=trunk --repo=ssh://git@github.com/stackabletech/repo.git --rev=HEAD --root=/tmp/git
1275+
command:
1276+
- /bin/bash
1277+
- -x
1278+
- -euo
1279+
- pipefail
1280+
- -c
1281+
env:
1282+
- name: VAR1
1283+
value: value1
1284+
image: oci.stackable.tech/sdp/product:latest
1285+
imagePullPolicy: Always
1286+
name: git-sync-0-init
1287+
resources:
1288+
limits:
1289+
cpu: 200m
1290+
memory: 64Mi
1291+
requests:
1292+
cpu: 100m
1293+
memory: 64Mi
1294+
volumeMounts:
1295+
- mountPath: /tmp/git
1296+
name: content-from-git-0
1297+
- mountPath: /stackable/log
1298+
name: log-volume
1299+
- mountPath: /mnt/extra-volume
1300+
name: extra-volume
1301+
- mountPath: /stackable/gitca-0
1302+
name: ca-cert-0
1303+
"#,
1304+
serde_yaml::to_string(&git_sync_resources.git_sync_init_containers.first()).unwrap()
1305+
);
1306+
1307+
assert_eq!(1, git_sync_resources.git_content_volumes.len());
1308+
1309+
assert_eq!(
1310+
"emptyDir: {}
1311+
name: content-from-git-0
1312+
",
1313+
serde_yaml::to_string(&git_sync_resources.git_content_volumes.first()).unwrap()
1314+
);
1315+
1316+
assert_eq!(1, git_sync_resources.git_content_volume_mounts.len());
1317+
1318+
assert_eq!(
1319+
"mountPath: /stackable/app/git-0
1320+
name: content-from-git-0
1321+
",
1322+
serde_yaml::to_string(&git_sync_resources.git_content_volume_mounts.first()).unwrap()
1323+
);
1324+
1325+
assert_eq!(1, git_sync_resources.git_content_folders.len());
1326+
1327+
assert_eq!(
1328+
"/stackable/app/git-0/current/",
1329+
git_sync_resources
1330+
.git_content_folders_as_string()
1331+
.first()
1332+
.unwrap()
1333+
);
1334+
1335+
assert_eq!(1, git_sync_resources.git_ca_cert_volumes.len());
1336+
1337+
assert_eq!(
1338+
"name: ca-cert-0
1339+
secret:
1340+
optional: false
1341+
secretName: git-ca-cert
1342+
",
1343+
serde_yaml::to_string(&git_sync_resources.git_ca_cert_volumes.first()).unwrap()
1344+
);
1345+
}
11111346
}

0 commit comments

Comments
 (0)