Skip to content

Repository files navigation

Overview

This collection of shamefully scrappy scripts do two main things:

  1. Run different SSH agents for different keys, so that a compromised agent has only limited use (eg: root on client A's hosts can't use it access client B's hosts).

  2. Require ssh-agent to prompt for confirmation before it uses a key, so that a compromised agent stands less chance of being exploited (if the user is away or declines the request then nothing happens).

The scripts are mostly Bourne-shell pure, although it's possible some BASH-isms might've crept in.

terminal-notifier create macOS notifications. reattach-to-user-namespace and setsid are macOS session process tools. They are available through Homebrew

There's doubtless room for improvement here. This grew over time to scratch successive itches, and trades grace and style for getting stuff done!

Operation

Shells source ssh-agent-env.rc in their profile. This defines two functions:

  1. load_agent_env

For a named key, this will either create a new agent for it, loading the key as it goes, or merely recover the environment of an existing agent.

The create/load work is pushed to a script, create-ssh-agent-for-key

  1. kill_agent

Kills the agent of a named key

It also defines some shell aliases * la for load_agent_env

And only if load-agent-inline config exists: * ssh for load-agent-inline ssh * scp for load-agent-inline scp * rsync for load-agent-inline rsync

More on load-agent-inline later.

The main scripts are:

  1. create-ssh-agent-for-key

This script goes off to look for a SSH key whose path is derived from its name. For me, this is /Volumes/key/ssh/id_ed2-${KEYNAME}.

It then creates a new SSH agent and stores its environment details in a dotfile specific to this key, ~/.ssh/.ssh-agent-${KEYNAME}.

Whilst doing so it uses the ${SSH_ASKPASS} variable to indicate to ssh-agent that it should run this variable's command before passing a key's deatils along. Keys that want such behaviour are then loaded into the agent with ssh-add -c.

  1. ssh-askpass

This is the program run by ssh-agent when it's asked to provide a key. It alerts the user to an incoming agent request and waits for confirmation.

To decouple the user interaction from the SSH process it compares two files' timestamps: one from when the request came in, and one updated by the out-of-band confirmation process.

The script exits zero to indicate confirmation, or non-zero to decline the request.

  1. ssh-agent-auth

This is other side of the confirmation process. The script merely update the timestamp of one of the files ssh-askpass is looking at. Typically it's run by a keyboard shortcut.

  1. ssh-decline

This script drops a file marker to indicate to ssh-askpass that its should not agree to the agent request. Again, can be run from a keyboard shortcut.

  1. load-agent-inline

Python script that looks at the commandline and tries to detect a hostname, and then tries to look that hostname, and its parents, up in config file to determine which agent to load. It then execs the remaining commandline.

It looks for ~/.config/ssh-bits/agents.json which is expected to contain

  {
  "domains": {
      "zikomo.xyz":       "jon-zikomo",
      "example.com":      "user-example"
      }
  }

This loads the jon-zikomo key for foo.bar.zikomo.xyz, baz.zikomo.xyz, zikomo.xyz ...

  1. clear-agent

Removes the dotfiles relating to managed agents and any timestamp files. It doesn't actually kill the agents. It probably should.

  1. ssh-defeat

Drop off a marker file that disables confirmation prompting for a given key.

  1. ssh-agent-lock

Not part of this stuff, but a proof-of-concept agent lock/unlock script. See inside for more details.

Example

Quick demo to unabstractificate this stuff:

  1. Create a new agent for the jon-zikomo key:
$ load_agent_env jon-zikomo
No agent loaded for jon-zikomo
Agent pid 23919
Enter passphrase for /Volumes/key/ssh/id_ed2-jon-zikomo:
Identity added: /Volumes/key/ssh/id_ed2-jon-zikomo (/Volumes/key/ssh/id_ed2-jon-zikomo)
The user has to confirm each use of the key
Agent pid 23919

$ ssh-add -l
256 SHA256:ueHHHX877aWrcnO19bh/Qg1A1hX6GoxdJxQ38HHHykg jon-zikomo (ED25519)
  1. Create another agent for another key, using the shell alias la:
$ la 732
No agent loaded for 732
Agent pid 25129
Enter passphrase for /Volumes/key/ssh/id_ed2-732:
Identity added: /Volumes/key/ssh/id_ed2-732 (/Volumes/key/ssh/id_ed2-732)
The user has to confirm each use of the key
Agent pid 25129

$ ssh-add -l
256 SHA256:udfgo973l4t00sflfkh/Qg1A1hX6GoxdJxQ94LLCVmn 732 (ED25519)
  1. In a new shell, use load-agent-inline to wrap ssh to somewhere:
$ ssh-add -l
The agent has no identities. # This is OS X's default agent

$ lookup-agent-inline ssh --debug ssh hq.zikomo.xyz
DEBUG:load-agent-inline:agent: jon-zikomo
DEBUG:load-agent-inline:setting SSH_AUTH_SOCK to ...
DEBUG:load-agent-inline:setting SSH_AGENT_PID to ...
DEBUG:load-agent-inline:exec ssh (ssh hq.zikomo.xyz)
Last login: Wed Aug 11 13:12:33 2010 from somewhere.example.com
  1. Quality of life

    Alias lookup-agent-inline into ssh, scp, rsync:

    alias ssh="lookup-agent-line ssh"
    ...
    

    Define domain rewrites in agents.json:

    "rewrites": {
        "zik": "zikomo.xyz"
    }
    
    $ ssh hq.zik
    

    `` Various things now happen:

  • load-agent-inline determined the hostname needed the jon-zikomo agent
  • it loaded the environment for that agent from the ~/.ssh-agent-jon-zikomo
  • it execd the remaining commandline (`ssh
  • the agent was asked to key the connection
  • the agent ran ssh-askpass
  • ssh-askpass didn't exit until I confirmed it
  • which I did by pressing a keyboard shortcut that ran ssh-agent-auth

Disclaimer

As suggested this is all quick and dirty hacks. No claims made about its fitness for use, no warranty or anything like that. It may indeed eat children and melt ice caps. Good luck.

License

Relesed under MIT license, see LICENSE. Non-warranty in there too.

About

Collection of scripts for managing SSH agents

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages