README
¶
ssh-agent-multiplexer
This is a small program which multiplexes running ssh agents.
Install
Homebrew
brew tap everpeace/tap
brew install ssh-agent-multiplexer
Binary
go to Releases page and download the tarballs.
Build from Source
git clone https://github.com/everpeace/ssh-agent-multiplexer.git
cd ssh-agent-multiplexer.git
# Download dev tools to ./.dev
make setup
# Build: binaries will be built in dist/
make
Quickstart
This quickstart is available only when installed via Homebrew.
-
Hit
ssh-agent-multiplexer config edit- This command opens your default config file with
EDITORenvironment variable (default isvi). Default config file path depends on your system:- for Mac:
~/Library/Application Support/ssh-agent-multiplexer/config.toml - for Linux:
~/.config/ssh-agent-multiplexer/config.toml
- for Mac:
You can use environment variables like
${SSH_AUTH_SOCK}directly in string values. For example, to use your current SSH agent as one of the agents the multiplexer can forward to, and have the multiplexer listen on a new, dedicated socket:# Example for ~/Library/Application Support/ssh-agent-multiplexer/config.toml # or ~/.config/ssh-agent-multiplexer/config.toml # Let the multiplexer listen on a new socket, perhaps in your home directory for clarity # If not set, "<your_config_dir>/agent.sock" was used. listen = "${HOME}/mux-agent.sock" # Add your existing SSH agent as a target for adding keys by expanding SSH_AUTH_SOCK add_targets = ["${SSH_AUTH_SOCK}"] # You can also add other read-only targets if needed targets = [ # # Mac # # 1Password # "${HOME}/Library/Group Containers/2BUA8C4S2C.com.1password/t/agent.sock", # Bitwarden # "${HOME}/Library/Containers/com.bitwarden.desktop/Data/.bitwarden-ssh-agent.sock" # Secretive # "${HOME}/Library/Containers/com.maxgoedjen.Secretive.SecretAgent/Data/socket.ssh", # # Linux # # 1Password # "${HOME}/.1password/agent.sock", # Bitwarden # "${HOME}/.bitwarden-ssh-agent.sock", ]Please see config file section for config file schema details.
- This command opens your default config file with
-
Start ssh-agent-multiplexer service via homebrew services
# logs will be at "$HOMEBREW_PREFIX/var/log/ssh-agent-multiplexer.log" brew services start ssh-agent-multiplexer -
Set
SSH_AUTH_SOCKto thelistenpath you defined in your config file (e.g., the value oflistenin the example above).export SSH_AUTH_SOCK="${HOME}/mux-agent.sock" # Or whatever you set for 'listen' in your configNow,
ssh-add -lshould show keys from your original agent (forwarded through the multiplexer), and new keys added viassh-addwill go to that original agent.
Note: ssh-agent-multiplexer supports dynamic configuration reloading. You don't need to restart your service when you updated the config file. Please see Dynamic Configuration Reloading section for details.
ssh-agent-multiplexer CLI
You can run ssh-agent-multiplexer on-demand. If you would like to
- aggregate two agents
/path/to/work-agent.sockand/path/to/personal-agent.sock - use
/path/to/work-agent.sockfor target agent for adding keys (viassh-add),
just run like this:
# Note:
# --target: for agents which are read-only
# --add-target: for agents which can support adding keys via ssh-add
$ ssh-agents-multiplexer run --add-target /path/to/work-agent.sock --target /path/to/personal-agent.sock --listen=/path/to/agent.sock
2022-09-28T23:06:59+09:00 INF revision=4b48f3b version=x.x.x
2022-09-28T23:06:59+09:00 INF Agent multiplexer listening listen=/path/to/agent.sock
Then, an multiplexer agent is listening at /path/to/agent.sock/agent.sock. So, You can use the socket as an usual ssh agent.
$ export SSH_AUTH_SOCK=/path/to/agent.sock/agent.sock
# this shows all the keys in target agents (both --target and --add-target)
$ ssh-add -l
# this will add <private_key> to an agent specified by --add-target
$ ssh-add <private_key>
# this removes <public_key> from a target keeping it
$ ssh-add -d <public_key>
# forward the multiplexing agent to some.host. Thus, you can use all the key in target agents
$ ssh -A some.host
Advanced Key Management with ssh-add
ssh-agent-multiplexer provides flexible options for managing keys with ssh-add when working with multiple SSH agents.
Multiple --add-target agents
The --add-target flag can be specified multiple times. This allows you to proxy ssh-add operations (like adding a new key) to a selection of different target agents.
Example:
ssh-agent-multiplexer run \
--add-target /path/to/work-agent.sock \
--add-target /path/to/personal-agent.sock \
--target /path/to/agent.sock \
# other flags...
In this scenario, when you run ssh-add <your_key>, ssh-agent-multiplexer needs to know which of the --add-target agents (work-agent.sock or personal-agent.sock) should receive the new key. This is where --select-target-command comes in.
Selecting the target agent with --select-target-command
When more than one --add-target is specified, the --select-target-command flag can be specified ('ssh-agent-mux-select' is the default value. See the next section.). This flag specifies an external command or script that ssh-agent-multiplexer will execute each time an ssh-add operation (that adds a key) is invoked. The purpose of this command is to allow you (the user) to choose which of the specified --add-target agents will be used for the operation.
The ssh-agent-multiplexer will set the following environment variables when executing this command:
SSH_AGENT_MUX_TARGETS: A newline-separated list of the socket paths for all agents specified via--add-target.SSH_AGENT_MUX_KEY_INFO: A string providing details about the key being added. This helps the selection script/tool display more context to the user. The string is semicolon-separated, containing key-value pairs.- Format:
KEY1=value1;KEY2=value2;... - Potential pairs:
COMMENT: The comment associated with the key (e.g.,COMMENT=user@host). This will beCOMMENT=if the original comment is empty.TYPE: The type of the SSH public key (e.g.,TYPE=ssh-rsa,TYPE=ecdsa-sha2-nistp256). This will beTYPE=unknownif the type cannot be determined (e.g., if the private key type is not recognized or doesn't allow public key derivation in a standard way).FINGERPRINT_SHA256: The SHA256 fingerprint of the public key (e.g.,FINGERPRINT_SHA256=SHA256:ABC123...). This will beFINGERPRINT_SHA256=unknownif the fingerprint cannot be determined.
- Example:
SSH_AGENT_MUX_KEY_INFO="COMMENT=my-ssh-key;TYPE=ssh-ed25519;FINGERPRINT_SHA256=SHA256:abcdef12345..."
- Format:
The command is expected to print the path of the chosen agent socket to its standard output (stdout).
Example:
ssh-agent-multiplexer run \
--add-target /path/to/work-agent.sock \
--add-target /path/to/personal-agent.sock \
--select-target-command "/path/to/ssh-agent-mux-select" \
--listen /path/to/agent.sock
Now, running ssh-add <your_key> (while SSH_AUTH_SOCK points to /path/to/agent.sock) will trigger /path/to/ssh-agent-mux-select, allowing you to choose between work-agent.sock and personal-agent.sock.
The ssh-agent-mux-select helper tool
This project now bundles a helper tool named ssh-agent-mux-select. If you've installed ssh-agent-multiplexer (e.g., via go install or from a release package), this tool should be available in your path. It's designed to be a user-friendly default for the --select-target-command flag.
ssh-agent-mux-select behavior:
- On macOS: It attempts to display a native GUI dialog using AppleScript.
- On Linux: It tries to use
zenityorkdialog(if available) to show a graphical selection list. - Fallback: If GUI methods are unavailable, fail, or are canceled, it falls back to an interactive text-based prompt in the terminal where
ssh-agent-multiplexeris running.
You can, of course, write your own custom scripts or commands to use with --select-target-command if ssh-agent-mux-select doesn't fit your specific workflow or if you prefer a different UI/UX for selection. Your custom script just needs to read the SSH_AGENT_MUX_TARGETS environment variable and print the chosen agent path to stdout.
Configuration File
ssh-agent-multiplexer now supports configuration via a TOML (Tom's Obvious, Minimal Language) file. This allows you to set your preferred defaults without needing to specify them as command-line flags every time.
Specifying a Configuration File
You can specify a custom configuration file path using the --config (or -c) command-line flag:
ssh-agent-multiplexer --config /path/to/your/custom-config.toml
Default Search Paths
If the --config or -c flag is not used, ssh-agent-multiplexer will look for a configuration file in the following locations. The first file found will be loaded. The search order varies by operating system:
On macOS:
./.ssh-agent-multiplexer.toml(in the current working directory)~/Library/Application Support/ssh-agent-multiplexer/config.toml(Standard macOS user configuration path)~/.config/ssh-agent-multiplexer/config.toml(Fallback user configuration path on macOS)
On other systems (e.g., Linux):
./.ssh-agent-multiplexer.toml(in the current working directory)- Path based on
os.UserConfigDir()(e.g.,~/.config/ssh-agent-multiplexer/config.tomlifXDG_CONFIG_HOMEis not set, or$XDG_CONFIG_HOME/ssh-agent-multiplexer/config.tomlif it is).os.UserConfigDir()respects$XDG_CONFIG_HOMEon Linux.
The first file found in these ordered lists will be loaded.
Precedence of Configuration Sources
Configuration values are determined with the following precedence (highest to lowest):
- Command-line arguments: Flags passed directly when running the program (e.g.,
--debug,--listen /tmp/my.sock). - Configuration file values: Settings defined in the loaded TOML configuration file.
- Built-in default values: Default values for options if not specified by flags or a config file (e.g.,
select-target-commanddefaults tossh-agent-mux-select).
Environment Variable Expansion
String values within the configuration file for options like listen, targets, add_targets, and select_target_command can utilize environment variables. This allows for more dynamic and user-specific configurations.
The application supports expansion for variables using the ${VAR} or $VAR syntax. os.ExpandEnv is used for the substitution, which means:
${VAR}is replaced by the value of the environment variableVAR.$VARis also replaced by the value ofVAR.- If a variable is not set, it's replaced with an empty string. For example, if
UNDEFINED_VARis not set,"${UNDEFINED_VAR}/path"becomes"/path".
Example:
# In your config.toml
listen = "${HOME}/my-agent.sock" # Expands HOME to your home directory
targets = ["/var/run/another-agent.sock"]
add_targets = ["${SSH_AUTH_SOCK}"] # Expands to your current SSH_AUTH_SOCK
select_target_command = "my_script --path ${MY_CUSTOM_PATH}"
If HOME is /Users/me, SSH_AUTH_SOCK is /tmp/ssh-XXXXXX/agent.123, and MY_CUSTOM_PATH is /opt/custom, the above would effectively become:
listen = "/Users/me/my-agent.sock"
targets = ["/var/run/another-agent.sock"]
add_targets = ["/tmp/ssh-XXXXXX/agent.123"]
select_target_command = "my_script --path /opt/custom"
Example Configuration File
Here is an example of a TOML configuration file (.ssh-agent-multiplexer.toml or config.toml) showing all available options:
# Example ssh-agent-multiplexer configuration file
# Enable debug logging
debug = false
# Socket path for the multiplexer to listen on.
# If not set, "/path/to/config_dir/agent.sock" was used.
# listen = "/path/to/your/mux.sock"
# Agents to proxy for read-only operations (equivalent to --target or -t flag).
targets = [
# "/path/to/example/readonly-agent1.sock",
# "/path/to/example/readonly-agent2.sock"
]
# Agents that can handle adding keys via ssh-add (equivalent to --add-target or -a flag).
# The TOML key is "add_targets".
add_targets = [
# "/path/to/example/writable-agent1.sock",
# Consider using your existing agent socket directly via environment variable expansion:
# "${SSH_AUTH_SOCK}" # This will be expanded to the value of your SSH_AUTH_SOCK env var
]
# Command to use for selecting an agent when multiple add_targets are specified.
# (equivalent to --select-target-command flag). The TOML key is "select_target_command".
# select_target_command = "ssh-agent-mux-select" # This is the built-in default
The keys in the TOML file generally map to their respective command-line flags. For flags that can be specified multiple times (like --target and --add-target), these are represented as arrays in the TOML file.
Dynamic Configuration Reloading
ssh-agent-multiplexer supports dynamic reloading of its configuration file. If the configuration file that was loaded at startup is modified, the application will detect the changes and attempt to reload the configuration automatically.
The following configuration settings can be dynamically changed and will be applied upon a successful reload:
targets: The list of read-only agent sockets.add_targets: The list of agent sockets that can handle key additions.select_target_command: The command used to select an agent when multipleadd_targetsare specified.debug: The verbosity level of logging.
Important: The listen address (the socket path where ssh-agent-multiplexer listens) cannot be changed dynamically. If you modify the listen setting in the configuration file, a full restart of the ssh-agent-multiplexer application is required for this change to take effect.
Release
The release process is fully automated by tagpr. To release, just merge the latest release PR.
License
Apache License, Version 2.0.
See LICENSE.
Directories
¶
| Path | Synopsis |
|---|---|
|
cmd
|
|
|
ssh-agent-multiplexer
command
|
|
|
ssh-agent-mux-select
command
|
|
|
config
Package config defines the application's configuration structure.
|
Package config defines the application's configuration structure. |