Skip to content
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

add SSH agent postStart event only if SSH key has a passphrase & experimental features enabled #1341

Merged
merged 3 commits into from
Nov 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions controllers/workspace/devworkspace_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -281,9 +281,14 @@ func (r *DevWorkspaceReconciler) Reconcile(ctx context.Context, req ctrl.Request
}
workspace.Spec.Template = *flattenedWorkspace

err = ssh.AddSshAgentPostStartEvent(&workspace.Spec.Template)
if err != nil {
return r.failWorkspace(workspace, "Failed to add ssh-agent post start event", metrics.ReasonWorkspaceEngineFailure, reqLogger, &reconcileStatus), nil
if workspace.Config.EnableExperimentalFeatures != nil && *workspace.Config.EnableExperimentalFeatures {
if needsSSHAgentPostStartEvent, err := ssh.NeedsSSHPostStartEvent(clusterAPI, workspace.Namespace); err != nil {
reqLogger.Error(err, "Error retrieving SSH secret")
} else if needsSSHAgentPostStartEvent {
if err = ssh.AddSshAgentPostStartEvent(&workspace.Spec.Template); err != nil {
return r.failWorkspace(workspace, "Failed to add ssh-agent initialization postStart event", metrics.ReasonWorkspaceEngineFailure, reqLogger, &reconcileStatus), nil
}
}
}

reconcileStatus.setConditionTrue(conditions.DevWorkspaceResolved, "Resolved plugins and parents from DevWorkspace")
Expand Down
4 changes: 3 additions & 1 deletion docs/additional-configuration.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ Prerequisites:
** The steps below assume the following environment variables are set:
*** `$SSH_KEY`: path on disk to private key for SSH keypair (e.g. `~/.ssh/id_ed25519`)
*** `$SSH_PUB_KEY`: path on disk to public key for SSH keypair (e.g. `~/.ssh/id_ed25519.pub`)
*** `$PASSPHRASE`: SSH keypair passphrase (optional)
*** `$PASSPHRASE`: SSH keypair passphrase (optional). *Note:* requires setting `config.enableExperimentalFeatures: true` in the DevWorkspaceOperatorConfig.
*** `$NAMESPACE`: namespace where workspaces using the SSH keypair will be started.

Process:
Expand Down Expand Up @@ -225,6 +225,8 @@ you must add the following in your `~/.bashrc`:
----
[ -f $HOME/ssh-environment ] && source $HOME/ssh-environment
----
+
*Note:* Specifying a passphrase for an SSH key is an experimental feature and is controlled by the DevWorkspaceOperatorConfig's `config.enableExperimentalFeatures` field.

3. Annotate the secret to configure automatic mounting to DevWorkspaces
+
Expand Down
9 changes: 9 additions & 0 deletions pkg/constants/metadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,15 @@ const (
// in a given namespace. It is used when e.g. adding Git credentials via secret
GitCredentialsConfigMapName = "devworkspace-gitconfig"

// SSHSecretName is the name used for the secret that stores the SSH key data for workspaces in a given namespace.
// TODO: This is a workaround for https://github.com/devfile/devworkspace-operator/issues/1340.
// We do not enforce the SSH secret to have this name, but it is used by the Che Dashboard and this allows us
// to detect if the user has provided an SSH key with a passhprase.
SSHSecretName = "git-ssh-key"

// SSHSecretPassphraseKey is the key used to retrieve the optional passphrase stored inside the SSH secret.
SSHSecretPassphraseKey = "passphrase"

SshAskPassConfigMapName = "devworkspace-ssh-askpass"

// GitCredentialsMergedSecretName is the name for the merged Git credentials secret that is mounted to workspaces
Expand Down
30 changes: 30 additions & 0 deletions pkg/library/ssh/event.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ 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"
"github.com/devfile/devworkspace-operator/pkg/provision/sync"
corev1 "k8s.io/api/core/v1"
k8sErrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/types"
)

const commandLine = `SSH_ENV_PATH=$HOME/ssh-environment \
Expand Down Expand Up @@ -58,3 +62,29 @@ func AddSshAgentPostStartEvent(spec *v1alpha2.DevWorkspaceTemplateSpec) error {
}
return err
}

// Determines whether an SSH key with a passphrase is provided for the namespace where the workspace exists.
// If an SSH key with a passphrase is used, then the SSH Agent Post Start event is needed for it to automatically
// be used by the workspace's SSH agent.
// If no SSH key is provided, or the SSH key does not provide a passphrase, then the SSH Agent Post Start event is not required.
func NeedsSSHPostStartEvent(api sync.ClusterAPI, namespace string) (bool, error) {
secretNN := types.NamespacedName{
Name: constants.SSHSecretName,
Namespace: namespace,
}
sshSecret := &corev1.Secret{}
if err := api.Client.Get(api.Ctx, secretNN, sshSecret); err != nil {
if k8sErrors.IsNotFound(err) {
// No SSH secret found
return false, nil
}
return false, err
}

if _, ok := sshSecret.Data[constants.SSHSecretPassphraseKey]; ok {
// SSH secret exists and has a passphrase set
return true, nil
}

return false, nil
}
Loading