Skip to content

fix: --ssh flag for running containers (issue #1189)#1214

Open
chavezMac wants to merge 3 commits intoapple:mainfrom
chavezMac:sshFlagAuthSocketFix
Open

fix: --ssh flag for running containers (issue #1189)#1214
chavezMac wants to merge 3 commits intoapple:mainfrom
chavezMac:sshFlagAuthSocketFix

Conversation

@chavezMac
Copy link

Type of Change

  • Bug fix
  • New feature
  • Breaking change
  • Documentation update

Motivation and Context

[Why is this change needed?]
-Issue pointed out by @vibbix, when attempting to use the --ssh flag in the container run ... command the value of SSH_AUTH_SOCK was not persisting and would result in a "The agent has no identities." as noted in the original issue.

Following the how-to documentation, future users may run into this issue as well.

Root cause: The runtime helper is started by launchd (per-container plist), so it sees launchd’s environment, not the shell that ran container run. The client’s SSH_AUTH_SOCK (e.g. from 1Password) was never passed into the container config, so the wrong socket was being used or no socket was used.

Fix: The client now captures the caller’s SSH_AUTH_SOCK when --ssh is set and stores it in the container config. The sandbox service resolves the host socket with precedence: config → runtime env → launchctl, and mounts that path into the container so the correct agent is used across stop/logout/login/restart.

Testing

  • Tested locally
  • Added/updated tests
  • Added/updated docs

/// Enable SSH agent socket forwarding from host to container.
public var ssh: Bool = false
/// Optiional preferred SSH agent socket path captured from client-side environment
public var sshAuthSocketPath: String? = nil
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for the confusion. I found this much more complex.

If we simply put sshAuthSocketPath in ContainerConfiguration, then it would not work if the ssh auth socket path changes while container is stopped.

We need a way to convey current shell's $SSH_AUTH_SOCK to the API server on container bootstrap, but there seems no way today.

Let me discuss it further! Thanks!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this variable do anything that just -v ${sshAuthSocketPath}:/run/host-services/ssh-auth.sock -e SSH_AUTH_SOCK=/run/host-services/ssh-auth.sock doesn't already do?

Copy link
Contributor

@jglogan jglogan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@chavezMac @JaewonHur The analysis makes sense but not the fix. Storing it in the container configuration is no different than setting the environment in the configuration.

I think what you have is most of the way there, but what if we got rid of the the sshAuthSocketPath configuration variable and the existing grab of the environment variable in the sandbox server, and instead do something like:

  • CLI - no change to --ssh variable
  • Client - update bootstrap to pass the current value of SSH_AUTH_SOCKET to the API server using an .sshAuthSocketPath XPC key with value type String? (for API compatibility with older clients, we're starting to consider this)
  • API server - extract sshAuthSocketPath from the XPC request if present and pass it through to the bootstrap call to the sandbox
  • sandbox - extract sshAuthSocketPath from the XPC request, and if present and --ssh is true, set the environment variable (replacing the existing env lookup code which uses the launch env)

/// Enable SSH agent socket forwarding from host to container.
public var ssh: Bool = false
/// Optiional preferred SSH agent socket path captured from client-side environment
public var sshAuthSocketPath: String? = nil
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this variable do anything that just -v ${sshAuthSocketPath}:/run/host-services/ssh-auth.sock -e SSH_AUTH_SOCK=/run/host-services/ssh-auth.sock doesn't already do?

@jglogan
Copy link
Contributor

jglogan commented Feb 24, 2026

Rather than special casing sshAuthSocketPath it might be generally useful to be able to pass dynamic (start-time) environment overrides from the user. This isn't something that should get surfaced in the CLI but could be useful for these specific cases where the container really needs something from the environment.

The alternative approach would be for the user to be able to specify a path statically that refers to a symlink they create.

@chavezMac chavezMac requested a review from jglogan February 27, 2026 02:23
/// Bootstrap the init process of the container.
public func bootstrap(id: String, stdio: [FileHandle?]) async throws {
/// - Parameter sshAuthSocketPath: Optional path to the current shell's SSH agent socket, supplied by the client at bootstrap time when SSH forwarding is enabled.
public func bootstrap(id: String, stdio: [FileHandle?], sshAuthSocketPath: String? = nil) async throws {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@chavezMac I had a thought on this to future-proof the API.

What do you think about changing it to:

public func bootstrap(id: String, stdio: [FileHandle?], dynamicEnv: [String:String] = [:]) async throws {...}

We'd not expose this up to the CLI (container start) as generally, the env variables are baked in at create time. With this API we can address any other cases like SSH_AUTH_SOCK without breaking changes.

Do you think it'd work cleanly with what you have?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do like the idea of a dynamicEnv API as a future-proof idea to support other bootstrap‑time env overrides without breaking changes. If we see more use cases beyond SSH, I’d be happy to prototype that in a separate PR.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The problem with not doing it now is that it guarantees there will be a breaking change.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I apologize for the delay in this request, I have been busy with other work. I plan on getting an updated PR for this by end of day, Friday!

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated PR! @jglogan


/// Resolves the host path for SSH agent socket forwarding. Uses only the path passed by the
/// client at bootstrap time; the sandbox does not read SSH_AUTH_SOCK from its own environment
/// or launchctl (the client is responsible for passing the current value).
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are we not using the default one?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe the initial bug was stemming from the use of the default one. We only use the path the client passes at bootstrap so forwarding matches the shell that ran the container run/start command.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe it still needs to fallback to default one if there is not SSH_AUTH_SOCK provided from command line. We would not want to surprise the users relying on it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If no SSH_AUTH_SOCK is defined at the time the container is started, there is no fallback and there should be no ssh socket forwarding.

@chavezMac chavezMac force-pushed the sshFlagAuthSocketFix branch from 085b1a1 to c396115 Compare March 14, 2026 03:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants