fix: --ssh flag for running containers (issue #1189)#1214
fix: --ssh flag for running containers (issue #1189)#1214chavezMac wants to merge 3 commits intoapple:mainfrom
Conversation
| /// 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 |
There was a problem hiding this comment.
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!
There was a problem hiding this comment.
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
left a comment
There was a problem hiding this comment.
@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
--sshvariable - Client - update bootstrap to pass the current value of SSH_AUTH_SOCKET to the API server using an
.sshAuthSocketPathXPC key with value typeString?(for API compatibility with older clients, we're starting to consider this) - API server - extract
sshAuthSocketPathfrom the XPC request if present and pass it through to the bootstrap call to the sandbox - sandbox - extract
sshAuthSocketPathfrom the XPC request, and if present and--sshis 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 |
There was a problem hiding this comment.
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?
|
Rather than special casing The alternative approach would be for the user to be able to specify a path statically that refers to a symlink they create. |
| /// 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 { |
There was a problem hiding this comment.
@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?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
The problem with not doing it now is that it guarantees there will be a breaking change.
There was a problem hiding this comment.
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!
|
|
||
| /// 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). |
There was a problem hiding this comment.
Why are we not using the default one?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
085b1a1 to
c396115
Compare
Type of Change
Motivation and Context
[Why is this change needed?]
-Issue pointed out by @vibbix, when attempting to use the
--sshflag in thecontainer run ...command the value ofSSH_AUTH_SOCKwas 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’sSSH_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_SOCKwhen--sshis 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