-
Notifications
You must be signed in to change notification settings - Fork 55
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
Apply ssh askpass flow for the workspace container #1307
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
// Copyright (c) 2019-2024 Red Hat, Inc. | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
package ssh | ||
|
||
import ( | ||
"github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2" | ||
"github.com/devfile/devworkspace-operator/pkg/constants" | ||
"github.com/devfile/devworkspace-operator/pkg/library/lifecycle" | ||
) | ||
|
||
const commandLine = `SSH_ENV_PATH=$HOME/ssh-environment \ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If we keep the current approach of modifying the .bashrc, it's probably worth making the ssh-environment file hidden with a |
||
&& if [ -f /etc/ssh/passphrase ] && command -v ssh-add >/dev/null; \ | ||
then ssh-agent | sed 's/^echo/#echo/' > $SSH_ENV_PATH \ | ||
&& chmod 600 $SSH_ENV_PATH && source $SSH_ENV_PATH \ | ||
&& ssh-add /etc/ssh/dwo_ssh_key < /etc/ssh/passphrase \ | ||
&& if [ -f $HOME/.bashrc ] && [ -w $HOME/.bashrc ]; then echo "source ${SSH_ENV_PATH}" >> $HOME/.bashrc; fi; fi` | ||
|
||
// AddSshAgentPostStartEvent Start ssh-agent and add the default ssh key to it, if the ssh key has a passphrase. | ||
// Initialise the ssh-agent session env variables in the user .bashrc file. | ||
func AddSshAgentPostStartEvent(spec *v1alpha2.DevWorkspaceTemplateSpec) error { | ||
if spec.Commands == nil { | ||
spec.Commands = []v1alpha2.Command{} | ||
} | ||
|
||
if spec.Events == nil { | ||
spec.Events = &v1alpha2.Events{} | ||
} | ||
|
||
_, mainComponents, err := lifecycle.GetInitContainers(spec.DevWorkspaceTemplateSpecContent) | ||
for _, component := range mainComponents { | ||
if component.Container == nil { | ||
continue | ||
} | ||
spec.Commands = append(spec.Commands, v1alpha2.Command{ | ||
Id: constants.SshAgentStartEventId, | ||
CommandUnion: v1alpha2.CommandUnion{ | ||
Exec: &v1alpha2.ExecCommand{ | ||
CommandLine: commandLine, | ||
Component: component.Name, | ||
}, | ||
}, | ||
}) | ||
} | ||
spec.Events.PostStart = append(spec.Events.PostStart, constants.SshAgentStartEventId) | ||
return err | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
// | ||
// Copyright (c) 2019-2024 Red Hat, Inc. | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
// | ||
|
||
package workspace | ||
|
||
import ( | ||
_ "embed" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there a reason why the blank identifier import (
"embed"
...
) I've never used the embed directive, but examples of it don't seem to be using the blank identifier. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Importing the |
||
"path" | ||
|
||
"github.com/devfile/devworkspace-operator/apis/controller/v1alpha1" | ||
"github.com/devfile/devworkspace-operator/pkg/constants" | ||
"github.com/devfile/devworkspace-operator/pkg/dwerrors" | ||
"github.com/devfile/devworkspace-operator/pkg/provision/sync" | ||
corev1 "k8s.io/api/core/v1" | ||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
"k8s.io/utils/pointer" | ||
) | ||
|
||
const SshAskPassMountPath = "/.ssh-askpass/" | ||
const SshAskPassScriptFileName = "ssh-askpass.sh" | ||
|
||
//go:embed ssh-askpass.sh | ||
var data string | ||
|
||
func ProvisionSshAskPass(api sync.ClusterAPI, namespace string, podAdditions *v1alpha1.PodAdditions) error { | ||
sshAskPassConfigMap := constructSshAskPassCM(namespace) | ||
if _, err := sync.SyncObjectWithCluster(sshAskPassConfigMap, api); err != nil { | ||
switch err.(type) { | ||
case *sync.NotInSyncError: // Ignore the object created error | ||
default: | ||
return dwerrors.WrapSyncError(err) | ||
} | ||
} | ||
|
||
sshAskPassVolumeMounts, sshAskPassVolumes, err := getSshAskPassVolumesAndVolumeMounts() | ||
if err != nil { | ||
return err | ||
} | ||
podAdditions.VolumeMounts = append(podAdditions.VolumeMounts, sshAskPassVolumeMounts...) | ||
podAdditions.Volumes = append(podAdditions.Volumes, sshAskPassVolumes...) | ||
return nil | ||
} | ||
|
||
func constructSshAskPassCM(namespace string) *corev1.ConfigMap { | ||
askPassConfigMap := &corev1.ConfigMap{ | ||
ObjectMeta: metav1.ObjectMeta{ | ||
Name: constants.SshAskPassConfigMapName, | ||
Namespace: namespace, | ||
Labels: map[string]string{ | ||
"app.kubernetes.io/defaultName": "ssh-askpass-secret", | ||
"app.kubernetes.io/part-of": "devworkspace-operator", | ||
"controller.devfile.io/watch-configmap": "true", | ||
}, | ||
}, | ||
Data: map[string]string{ | ||
SshAskPassScriptFileName: data, | ||
}, | ||
} | ||
return askPassConfigMap | ||
} | ||
|
||
func getSshAskPassVolumesAndVolumeMounts() ([]corev1.VolumeMount, []corev1.Volume, error) { | ||
name := "ssh-askpass" | ||
volume := corev1.Volume{ | ||
Name: name, | ||
VolumeSource: corev1.VolumeSource{ | ||
ConfigMap: &corev1.ConfigMapVolumeSource{ | ||
LocalObjectReference: corev1.LocalObjectReference{ | ||
Name: constants.SshAskPassConfigMapName, | ||
}, | ||
DefaultMode: pointer.Int32(0755), | ||
}, | ||
}, | ||
} | ||
volumeMount := corev1.VolumeMount{ | ||
Name: name, | ||
ReadOnly: true, | ||
MountPath: path.Join(SshAskPassMountPath, SshAskPassScriptFileName), | ||
SubPath: SshAskPassScriptFileName, | ||
} | ||
return []corev1.VolumeMount{volumeMount}, []corev1.Volume{volume}, nil | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since the project clone container also gets environment variables from the
commonEnvironmentVariables()
function, we no longer have to setup the SSH askpass related environment variables in the project clone container's Dockerfile.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Awesome thank you! Just leaving a comment here as a reminder to myself: it's worth re-testing the functionality of #1291 since we're modifying how it's implemented now.