Skip to content

Commit

Permalink
SSH Auth for Git Init #69
Browse files Browse the repository at this point in the history
  • Loading branch information
jfaltermeier committed May 17, 2023
1 parent 6d368df commit f988e95
Show file tree
Hide file tree
Showing 5 changed files with 114 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -342,7 +342,7 @@ public static void addInitContainers(String correlationId, TheiaCloudClient clie
}
}

public static Volume createVolume(String pvcName) {
public static Volume createUserDataVolume(String pvcName) {
Volume volume = new Volume();
volume.setName(USER_DATA);
PersistentVolumeClaimVolumeSource persistentVolumeClaim = new PersistentVolumeClaimVolumeSource();
Expand All @@ -351,7 +351,7 @@ public static Volume createVolume(String pvcName) {
return volume;
}

public static VolumeMount createVolumeMount(AppDefinitionSpec appDefinition) {
public static VolumeMount createUserDataVolumeMount(AppDefinitionSpec appDefinition) {
VolumeMount volumeMount = new VolumeMount();
volumeMount.setName(AddedHandlerUtil.USER_DATA);
volumeMount.setMountPath(TheiaCloudPersistentVolumeUtil.getMountPath(appDefinition));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,18 +35,26 @@
import io.fabric8.kubernetes.api.model.Container;
import io.fabric8.kubernetes.api.model.EnvVar;
import io.fabric8.kubernetes.api.model.EnvVarSource;
import io.fabric8.kubernetes.api.model.KeyToPath;
import io.fabric8.kubernetes.api.model.Secret;
import io.fabric8.kubernetes.api.model.SecretKeySelector;
import io.fabric8.kubernetes.api.model.SecretVolumeSource;
import io.fabric8.kubernetes.api.model.SecurityContext;
import io.fabric8.kubernetes.api.model.Volume;
import io.fabric8.kubernetes.api.model.VolumeMount;
import io.fabric8.kubernetes.api.model.apps.Deployment;

public class GitInitOperationHandler implements InitOperationHandler {

protected static final String ETC_THEIA_CLOUD_SSH = "/etc/theia-cloud-ssh";
protected static final String ID_THEIACLOUD = "id_theiacloud";
protected static final String SSH_PRIVATEKEY = "ssh-privatekey";
protected static final String SSH_KEY = "ssh-key";
protected static final String PASSWORD = "password";
protected static final String USERNAME = "username";
protected static final String GIT_PROMPT1 = "GIT_PROMPT1";
protected static final String GIT_PROMPT2 = "GIT_PROMPT2";
protected static final String KUBERNETES_IO_SSH_AUTH = "kubernetes.io/ssh-auth";
protected static final String KUBERNETES_IO_BASIC_AUTH = "kubernetes.io/basic-auth";
protected static final String HTTPS = "https://";
protected static final String HTTP = "http://";
Expand Down Expand Up @@ -80,6 +88,7 @@ public void addInitContainer(String correlationId, TheiaCloudClient client, Depl
}

List<Container> initContainers = deployment.getSpec().getTemplate().getSpec().getInitContainers();
List<Volume> volumes = deployment.getSpec().getTemplate().getSpec().getVolumes();

Container gitInitContainer = new Container();
initContainers.add(gitInitContainer);
Expand All @@ -100,7 +109,7 @@ public void addInitContainer(String correlationId, TheiaCloudClient client, Depl
securityContext.setRunAsUser(Long.valueOf(appDefinition.getSpec().getUid()));
securityContext.setRunAsGroup(Long.valueOf(appDefinition.getSpec().getUid()));

VolumeMount volumeMount = AddedHandlerUtil.createVolumeMount(appDefinition.getSpec());
VolumeMount volumeMount = AddedHandlerUtil.createUserDataVolumeMount(appDefinition.getSpec());
gitInitContainer.getVolumeMounts().add(volumeMount);

String secretName = args.get(2);
Expand Down Expand Up @@ -131,8 +140,9 @@ public void addInitContainer(String correlationId, TheiaCloudClient client, Depl
return;
}
} else {
/* get SSH Key and password from secret */
// TODO JF
if (!injectSSHRepoCredentials(correlationId, secret, secretName, repository, gitInitContainer, volumes)) {
return;
}
}

}
Expand Down Expand Up @@ -195,6 +205,45 @@ protected boolean injectHTTPRepoCredentials(String correlationId, Secret secret,
return true;
}

protected boolean injectSSHRepoCredentials(String correlationId, Secret secret, String secretName,
String repository, Container gitInitContainer, List<Volume> volumes) {

if (!KUBERNETES_IO_SSH_AUTH.equals(secret.getType())) {
LOGGER.warn(LogMessageUtil.formatLogMessage(correlationId, MessageFormat
.format("Secret with name {0} is not of type {1}.", secretName, KUBERNETES_IO_SSH_AUTH)));
return false;
}

/* inject password */
EnvVar envVar = new EnvVar();
gitInitContainer.getEnv().add(envVar);
envVar.setName(GIT_PROMPT1);

EnvVarSource envVarSource = new EnvVarSource();
envVar.setValueFrom(envVarSource);
envVarSource.setSecretKeyRef(new SecretKeySelector(PASSWORD, secretName, false));

/* inject ssh key */
Volume volume = new Volume();
volumes.add(volume);
volume.setName(SSH_KEY);
SecretVolumeSource secretVolumeSource = new SecretVolumeSource();
volume.setSecret(secretVolumeSource);
secretVolumeSource.setSecretName(secretName);
KeyToPath keyToPath = new KeyToPath();
secretVolumeSource.getItems().add(keyToPath);
keyToPath.setKey(SSH_PRIVATEKEY);
keyToPath.setPath(ID_THEIACLOUD);

VolumeMount volumeMount = new VolumeMount();
gitInitContainer.getVolumeMounts().add(volumeMount);
volumeMount.setName(SSH_KEY);
volumeMount.setMountPath(ETC_THEIA_CLOUD_SSH);
volumeMount.setReadOnly(true);

return true;
}

protected static boolean isHTTP(String repository) {
String lowerCasedRepo = repository.toLowerCase(Locale.US);
return (lowerCasedRepo.startsWith(HTTP) || lowerCasedRepo.startsWith(HTTPS));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -335,10 +335,10 @@ protected void createAndApplyDeployment(String correlationId, String sessionReso

protected void addVolumeClaim(Deployment deployment, String pvcName, AppDefinitionSpec appDefinition) {
PodSpec podSpec = deployment.getSpec().getTemplate().getSpec();
Volume volume = AddedHandlerUtil.createVolume(pvcName);
Volume volume = AddedHandlerUtil.createUserDataVolume(pvcName);
podSpec.getVolumes().add(volume);
Container container = TheiaCloudPersistentVolumeUtil.getTheiaContainer(podSpec, appDefinition);
VolumeMount volumeMount = AddedHandlerUtil.createVolumeMount(appDefinition);
VolumeMount volumeMount = AddedHandlerUtil.createUserDataVolumeMount(appDefinition);
container.getVolumeMounts().add(volumeMount);
}

Expand Down
49 changes: 49 additions & 0 deletions python/git-init/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ docker run --env GIT_PROMPT1=$SSH_PASSWORD -v ~/tmp/ssh/:/etc/theia-cloud-ssh --

### Create Kubernetes Resources

#### Secret for HTTP(S) auth

```yaml
apiVersion: v1
kind: Secret
Expand All @@ -74,6 +76,8 @@ stringData:
password: pat
```
#### Example Session for HTTP(S) auth
```yaml
apiVersion: theia.cloud/v5beta
kind: Session
Expand All @@ -96,3 +100,48 @@ spec:
- maintenance_1_1_x
- foo-theiacloud-io-basic-auth
```
#### Secrets for SSH auth
```yaml
apiVersion: v1
kind: Secret
metadata:
name: foo-theiacloud-io-ssh-auth
namespace: theiacloud
labels:
theiaCloudInit: git
annotations:
theiaCloudUser: [email protected]
type: kubernetes.io/ssh-auth
stringData:
ssh-privatekey: |
-----BEGIN OPENSSH PRIVATE KEY-----
b3B...
password: sshpw
```
#### Example Session for SSH auth
```yaml
apiVersion: theia.cloud/v5beta
kind: Session
metadata:
name: ws-asdfghjkl-theia-cloud-demo-foo-theia-cloud-io-session
namespace: theiacloud
spec:
appDefinition: theia-cloud-demo
envVars: {}
envVarsFromConfigMaps: []
envVarsFromSecrets: []
name: ws-asdfghjkl-theia-cloud-demo-foo-theia-cloud-io-session
user: [email protected]
workspace: ws-asdfghjkl-theia-cloud-demo-foo-theia-cloud-io
sessionSecret: 3e68605f-0c6d-4ae5-9816-738f15d34fc9
initOperations:
- id: git
arguments:
- [email protected]:username/my.repository.git
- maintenance_1_1_x
- foo-theiacloud-io-ssh-auth
```
9 changes: 9 additions & 0 deletions terraform/terraform.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,15 @@ terraform apply

Point your browser to the `try_now` output value URL printed to the console at the end.

If you want to use non ephemeral workspaces with minikube you have to mount a directory with the expected user id. Please check the existing persisted volume in Minikube for the path.\
You might have to configure the firewall for mounting.

```bash
# This mounts the ~/tmp/minikube on the machine running minikube into minkube.
# Check the persisted volume to find the exact /tmp/hostpath-provisioner/theia-cloud/id path
minikube mount ~/tmp/minikube:/tmp/hostpath-provisioner/theia-cloud/a36c30cee-4d97-4097-826a-31ba72734fd0-pvc-ws-asdfghjkl-theia-c/
```

#### Destroy Minikube Cluster

First remove the persistent volume from the terraform state:
Expand Down

0 comments on commit f988e95

Please sign in to comment.