Skip to content

Different agents and different keys for different projects, with ssh.

License

ssh-ident/ssh-ident1

 
 

Repository files navigation

Help on module ssh-ident:

NAME
    ssh-ident - Start and use ssh-agent and load identities as necessary.

DESCRIPTION
    Use this script to start ssh-agents and load ssh keys on demand,
    when they are first needed.

    All you have to do is modify your .bashrc to have:

      alias ssh='/path/to/ssh-ident'

    or add a link to ssh-ident from a directory in your PATH, for example:

      ln -s /path/to/ssh-ident ~/bin/ssh

    If you use scp or rsync regularly, you should add a few more lines described
    below.

    In any case, ssh-ident:

    - will create an ssh-agent and load the keys you need the first time you
      actually need them, once. No matter how many terminals, ssh or login
      sessions you have, no matter if your home is shared via NFS.

    - can prepare and use a different agent, different set of keys and different
      ssh config file depending on the host you are connecting to, or the
      directory you are using ssh from.
      This allows for isolating keys when using agent forwarding with different
      sites (eg, university, work, home, secret evil internet identity, ...).
      It also allows to use multiple accounts on sites like github, unfuddle
      and gitorious easily.

    - allows to specify different options for each set of keys. For example, you
      can provide a -t 60 to keep keys loaded for at most 60 seconds. Or -c to
      always ask for confirmation before using a key.


    Installation
    ============

    All you need to run ssh-ident is a standard installation of python 3 or 2.7.
    Newer versions of Python 3 may deprecate stuff and will fail.

    If your system has wget and are impatient to use it, you can install
    ssh-ident with two simple commands:

       mkdir -p ~/bin; wget -O ~/bin/ssh https://raw.githubusercontent.com/ssh-ident/ssh-ident1/refs/heads/dev/ssh-ident ; chmod 0755 ~/bin/ssh

       echo 'export PATH="${HOME}/bin:${PATH}"' >> ~/.bashrc

    Logout, login, and done. SSH should now invoke ssh-ident instead of the
    standard ssh.


    Alternatives
    ============

    In .bashrc, I have:

      alias ssh=/home/ccontavalli/scripts/ssh-ident

    all I have to do now is logout, login and then:

      $ ssh somewhere

    ssh-ident will be called instead of ssh, and it will:
    - check if an agent is running. If not, it will start one.
    - try to load all the keys in ~/.ssh, if not loaded.

    If I now ssh again, or somewhere else, ssh-ident will reuse the same agent
    and the same keys, if valid.


    About scp, rsync, and friends
    =============================

    scp, rsync, and most similar tools internally invoke ssh. If you don't tell
    them to use ssh-ident instead, key loading won't work. There are a few ways
    to solve the problem:

    1) Rename 'ssh-ident' to 'ssh' or create a symlink 'ssh' pointing to
       ssh-ident in a directory in your PATH before /usr/bin or /bin, similarly
       to what was described previously. For example, add to your .bashrc:

        export PATH="${HOME}/bin:${PATH}"

       And run:

        ln -s /path/to/ssh-ident ~/bin/ssh

       Make sure `echo ${PATH}` shows '~/bin' *before* '/usr/bin' or '/bin'. You
       can verify this is working as expected with `which ssh`, which should
       show ~/bin/ssh.

       This works for rsync and git, among others, but not for scp and sftp, as
       these do not look for ssh in your PATH but use a hard-coded path to the
       binary.

       If you want to use ssh-ident with scp or sftp, you can simply create
       symlinks for them as well:

        ln -s /path/to/ssh-ident ~/bin/scp
        ln -s /path/to/ssh-ident ~/bin/sftp

       It is not recommended to create symlinks for 'ssh-agent' and 'ssh-add'.

    2) Add a few more aliases in your .bashrc file, for example:

        alias scp="BINARY_SSH='scp' '/path/to/ssh-ident'"
        alias rsync="BINARY_SSH='rsync' '/path/to/ssh-ident'"
        alias ssh-add="BINARY_SSH='ssh-add' '/path/to/ssh-ident'"
        alias ssh-agent="BINARY_SSH='ssh-agent' '/path/to/ssh-ident'"

        ...

       The first alias will make the 'scp' command invoke 'ssh-ident' instead,
       but tell 'ssh-ident' to invoke 'scp' instead of the plain 'ssh' command
       after loading the necessary agents and keys.

       Note that aliases don't work from scripts - if you have any script that
       you expect to use with ssh-ident, you may prefer method 1), or you will
       need to update the script accordingly.

       For the ssh-agent and ssh-add binaries the first parameter can be
       '-i/-I <identity>' to use them with a non-default identity.
       Note that with '-i' (lowercase) no keys will be loaded for ssh-agent
       and ssh-add calls.

         BINARY_SSH='ssh-agent' /path/to/ssh-ident <-i|-I> <identity> ...
         BINARY_SSH='ssh-add' /path/to/ssh-ident <-i|-I> <identity> ...

       Additionally the identity can be defined via SSH_IDENT_ID='<identity>'.

    3) Use command specific methods to force them to use ssh-ident instead of
       ssh, for example:

        rsync -e '/path/to/ssh-ident' ...
        scp -S '/path/to/ssh-ident' ...

    4) Replace the real ssh on the system with ssh-ident, and set the
       BINARY_SSH configuration parameter to the original value.

       On Debian based system, you can make this change in a way that
       will survive automated upgrades and audits by running:

         dpkg-divert --divert /usr/bin/ssh.ssh-ident --rename /usr/bin/ssh

       After which, you will need to use:

         BINARY_SSH="/usr/bin/ssh.ssh-ident"

    5) Identities can also be used like a normal ssh-agent and helps in some
       special cases, where programs use an ssh library and do not call the ssh
       binaries.
       Make sure the agent for the identity is running and keys are loaded via
       ssh-add -I (uppercase). After this just source the agent environment file
       of the identity and execute the program.
       That's the same way as using pure ssh-agent, where you also have to make
       sure to have the correct environment variables set.
       Use a subshell to not polute the normal environment.

         BINARY_SSH='ssh-add' /path/to/ssh-ident -I <identity>; # uppercase -I to load all keys if necessary
         ( . ~/.ssh/agents/agent-<identity>-${HOSTNAME} >/dev/null 2>/dev/null; <program that does not use the ssh binaries>; )`


    Config file with multiple identities
    ====================================

    To have multiple identities, all I have to do is:

    1) create an ssh-ident config file.

       ssh-ident follows the XDG base dir specs (see https://specifications.freedesktop.org/basedir-spec/latest/)
       and looks for "${XDG_CONFIG_HOME}/ssh-ident/ssh-ident1" which defaults to "~/.config/ssh-ident/ssh-ident1".
       When the file at the XDG path is missing, then it looks for the legacy file "${HOME}/.ssh-ident".

       In this file, I need to tell ssh-ident which identities to use and when.
       The file should look something like:

      # Specifies which identity to use depending on the path I'm running ssh
      # from.
      # For example: ("mod-xslt", "personal") means that for any path that
      # contains the word "mod-xslt", the "personal" identity should be used.
      # This is optional - don't include any MATCH_PATH if you don't need it.
      MATCH_PATH = [
        # [directory pattern, identity]
        [r"mod-xslt", "personal"],
        [r"ssh-ident", "personal"],
        [r"opt/work", "work"],
        [r"opt/private", "secret"],
      ]

      # Specifies which identity to use depending on the arguments I'm running
      # ssh with. At first each element is separately tested against all patterns,
      # then a string of all elements is tested against all patterns.
      # This is optional - don't include any MATCH_ARGV if you don't need it.
      # For example: If any of the ssh arguments have 'cweb' in it, the 'personal'
      # identity has to be used. "ssh myhost.cweb.com" will have cweb in argv, and
      # the "personal" identity will be used.
      MATCH_ARGV = [
        [r"cweb", "personal"],
        [r"corp", "work"],
      ]
      # Another example: Choose an identity for git depending on the accessed
      # repository on GitHub (similar for GitLab, Bitbucket, etc.).
      # Recognize the elements with the host and the repository path to switch
      # between 'personal' and 'work' identity.
      MATCH_ARGV = [
        [r"\s(git@)?github\.com\s.*'company\/.+\.git'", "work"],
        [r"\s(git@)?github\.com\s.*'ccontavalli\/.+\.git'", "personal"],
        [r"\s(git@)?gist\.github\.com\s.*'abcdef01234567890fedcba912345678\.git'", "work"],
        [r"^(git@)?(gist\.)?github\.com$", "personal"],
      ]

      # Note that if no match is found, the DEFAULT_IDENTITY is used. This is
      # generally your loginname, no need to change it.
      # This is optional - don't include any DEFAULT_IDENTITY if you don't
      # need it.
      # DEFAULT_IDENTITY = "foo"

      # This is optional - don't include any SSH_ADD_OPTIONS if you don't
      # need it.
      # Otherwise, provides options to be passed to the ssh-add command for
      # specific identities and/or binaries. First fitting entry wins.
      # First element is a list of identities, second element is a list of
      # binaries and third element is the option string.
      # An empty list means for all identities or all binaries.
      SSH_ADD_OPTIONS = [
        # Regardless, ask for confirmation before using any of the
        # work keys.
        [ ["work", ], [], "-c -t 3600"],
        # Forget about secret keys after ten minutes. ssh-ident will
        # automatically ask for the passphrase again if they are needed.
        [ ["secret", ], ['other-ssh-add', ], "-t 600"],
        # As default prevent keys remaining in memory for too long (>2h) for all
        # identities and binaries. Set SSH_ADD_OPTIONS to empty to disable this.
        [ [], [], "-t 7200"],
      ]

      # This is optional - dont' include any SSH_OPTIONS if you don't
      # need it.
      # Note that a separate ssh config file per identity is possible too.
      # Otherwise, provides options to be passed to the ssh command for
      # specific identities and/or binaries. All fitting entries combined.
      # First element is a list of identities, second element is a list of
      # binaries and third element is the option string.
      # An empty list means for all identities or all binaries.
      SSH_OPTIONS = [
        # As default pass UseRoaming=no to standard ssh binaries for all identities,
        # due to CVE-2016-0777. Set SSH_OPTIONS to empty to disable this.
        [ [], ['ssh', 'scp', 'sftp', ], "-oUseRoaming=no"],

        # Disable forwarding of the agent, but enable X forwarding,
        # when using the work or personal profile.
        [["work", "personal", ], ["ssh", ], "-Xa"],

        # Always forward the agent when using the secret identity.
        [["secret", ], ["ssh", ], "-A"],

        # Make all commands verbose for "personal"
        [["personal", ], [], "-v"],
      ]

      # This is optional - don't include any SSH_AGENTS or SSH_ADDS if you
      # don't need it. Defaults are 'ssh-agent' and 'ssh-add'.
      # Otherwise, provides ssh-agent/ssh-add binary to use for key handling
      # of specific identities. First fitting entry wins.
      # Those binaries must be compatible to OpenSSH's implementation.
      # First element is a list of identities and second element is the
      # ssh-agent/ssh-add binary name.
      # An empty list means for all identities.
      "SSH_AGENTS" = [
        [ ['work', 'work2', ], "other-ssh-agent"],
        [ ['secret', ], "ssh-pageant"],
      ],
      "SSH_ADDS" = [
        [ ['work1', 'work2', ], "other-ssh-add"],
      ],

      # Output verbosity
      # Valid values are: "ERROR", "WARNING"/"WARN", "INFO", "DEBUG" (strings)
      # Backward compatibility: LOG_ERROR, LOG_WARN, LOG_INFO, LOG_DEBUG (variables)
      VERBOSITY = LOG_INFO

    2) Create the directory where all the identities and agents
       will be kept:

        $ mkdir -p ~/.ssh/identities; chmod u=rwX,go= -R ~/.ssh

    3) Create a directory for each identity, for example:

        $ mkdir -p ~/.ssh/identities/personal
        $ mkdir -p ~/.ssh/identities/work
        $ mkdir -p ~/.ssh/identities/secret

    4) Generate (or copy) keys for those identities:

        # Default keys are for my personal account
        $ cp ~/.ssh/id_rsa* ~/.ssh/identities/personal

        # Generate keys to be used for work only, rsa
        $ ssh-keygen -t rsa -b 4096 -f ~/.ssh/identities/work/id_rsa

    5) Optionally create a separate ssh config file for those identities that
       need special ssh settings in general or for specific hosts:

        $ ${EDITOR} ~/.ssh/identities/secret/config

        ...


    Now if I run:

      $ ssh corp.mywemployer.com

    ssh-ident will be invoked instead, and:

      1) check ssh argv, determine that the "work" identity has to be used.
      2) look in ~/.ssh/agents, for a "work" agent loaded. If there is no
         agent, it will prepare one.
      3) look in ~/.ssh/identities/work/* for a list of keys to load for this
         identity. It will try to load any key that is not already loaded in
         the agent.
      4) finally run ssh with the environment setup such that it will have
         access only to the agent for the identity work, and the corresponding
         keys.

    Notes about key files:
    ssh-ident needs to access both files of each key pair - private and public key.
    Both files have to reside in the same directory.
    All files in your identities subdirectories that match PATTERN_KEYS will be
    considered key files (either private or public). If a different naming scheme
    is used, then make sure that PATTERN_KEYS matches filenames for both types.
    By default ssh-ident identifies public keys by the .pub extension or
    a "public" inside the filename, while private keys have no explicit extension
    or a "private" inside the filename. To recognize a key pair these specific name
    parts are removed, the remaining filenames compared and connected if they match.
    A key is only recognized and loaded if the key pair is complete.
    The public key file is necessary to detect if a key is already loaded into
    ssh-agent to avoid adding it again and therefore asking for password again.
    If a public key file is missing check out the '-y' parameter of 'ssh-keygen'.
    All patterns to detect key files in general plus public and private keys are
    defined in lists, which can hold multiple regular expressions or simple
    compare strings. The first match is taken and no further tests done.
    Patterns are tested against the filename, not the full path.

    The defaults of PATTERN_KEYS against the filename for the general key file
    determination are:

          PATTERN_KEYS = [
            r"^id_",
            r"^identity",
            r"^ssh[0-9]-",
          ]

    The defaults of PATTERN_PUBKEYS and PATTERN_PRIVKEYS for the public and private
    key determination are:

          PATTERN_PUBKEYS = [
              [r"\.pub$", 0],
              [r"public", 0],
          ]
          PATTERN_PRIVKEYS = [
              [r"private", 0],
              # Fallback for all remaining files.
              [r"", None],
          ]

    Notes about PATTERN_PUBKEYS and PATTERN_PRIVKEYS:
    ssh-ident first checks if the file is a public key, then if it is a private key.
    The second parameter after the patterns defines which group to remove to
    recognize key pairs. A zero (0) means remove the whole match. 'None' means do
    not remove anything and leave filename as is.

    If you want to only load keys that have "mykey" in their filename, you can
    define in your ssh-ident config file:

          PATTERN_KEYS = [
            "mykey",
          ]

    If you want to also load keys that have the extension ".key" or ".pub", but no
    tilde (~) at zhe beginning, then you can define in your ssh-ident config file:

          PATTERN_KEYS = [
            r"^id_",
            r"^identity",
            r"^ssh[0-9]-",
            r"^[^~].*(\.key|\.pub)$",
          ]
          PATTERN_PRIVKEYS = [
              [r"\.key$", 0],
              [r"private", 0],
              # Fallback for all remaining files.
              [r"", None],
          ]

    Note: As the ".pub" and ".key" patterns come first, files that also have
    "public" or "private" in their names may not match.


    You can also redefine:

          DIR_IDENTITIES = "${HOME}/.ssh/identities"
          DIR_AGENTS = "${HOME}/.ssh/agents"

    To point somewhere else if you so desire.


    TROUBLESHOOTING
    ===============

    If something does not work as wanted then enable verbose mode for the
    target binary, for example '-v' for ssh.
    Additionally remove the keys for the identity and set ssh-ident's
    VERBOSITY to LOG_DEBUG.

      BINARY_SSH='ssh-add' /path/to/ssh-ident -i <identity> -D; # delete all keys from agent of identity
      VERBOSITY='LOG_DEBUG' ssh -vT git@github.com "test 'my-git-user-1/test-repo.git'"

    Check that the correct keys are available for each identity (public and
    private) and that the configuration matches the scenario (binary, parameters, etc.).


    BUILDING A DEBIAN PACKAGE
    =========================

    If you need to use ssh-ident on a debian / ubuntu / or any other
    derivate, you can now build debian packages.

      1. Make sure you have devscripts installed:

        sudo apt-get install devscripts debhelper

      2. Download ssh-ident in a directory of your choice (ssh-ident)

        git clone https://github.com/ccontavalli/ssh-ident.git ssh-ident

      3. Build the .deb package:

        cd ssh-ident && debuild -us -uc

      4. Profit:

        cd ..; dpkg -i ssh-ident*.deb


    CREDITS
    =======

    - ssh-ident Team, https://github.com/ssh-ident/, main authors.
    - Carlo Contavalli, http://www.github.com/ccontavalli, original main author.
    - Hubert depesz Lubaczewski, http://www.github.com/despez, support
      for using environment variables for configuration.
    - Flip Hess, http://www.github.com/fliphess, support for building
      a .deb out of ssh-ident.
    - Terrel Shumway, https://www.github.com/scholarly, port to python3.
    - black2754, https://www.github.com/black2754, vim modeline, support
      for verbosity settings, and BatchMode passing.
    - Michael Heap, https://www.github.com/mheap, support for per
      identities config files.
    - Carl Drougge, https://www.github.com/drougge, CVE-2016-0777 fix,
      fix for per user config files, and use /bin/env instead of python
      path.
    - Eric T. Johnson, https://github.com/yut23/ssh-ident
      adapted some stuff from his fork (logging)

About

Different agents and different keys for different projects, with ssh.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • Python 99.3%
  • Shell 0.7%