diff --git a/README b/README index 1b7f719..84fcebd 100644 --- a/README +++ b/README @@ -226,13 +226,38 @@ DESCRIPTION $ mkdir -p ~/.ssh/identities/work $ mkdir -p ~/.ssh/identities/secret - 4) Generate (or copy) keys for those identities: + 4) Generate (or copy or symlink) keys for those identities: # Default keys are for my personal account $ cp ~/.ssh/id_rsa* ~/.ssh/identities/personal + $ ln -s ~/.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) If you use hardware-token like eToken: + + # Make a symlink from the shared library for the token to the identity-dir + $ ln -s /usr/lib64/p11-kit-proxy.so ~/.ssh/identities/work/ + + General informations: + *) ssh-certificate: + ssh-certificates can also be used with ssh-ident. Place the *-cert.pub file + also in the identity-directory. It will be picked up from ssh-ident an put + into the ssh-agent. So you can use the cert also on remote-machine. + *) pkcs11 and ssh-certificate: + It is not now possible to add a certificate for a pkcs11 key. This version adds + the private-key from a smartcard or hardware-token to the agent. A coresponding + certificate can not be loaded in ssh-agent. + (Bugreport exists https://bugzilla.mindrot.org/show_bug.cgi?id=2472) + To use local hardware-token to authentificate on a server behind a bastion-host or jumpserver + just copy your PublicKey to the remote-machine, from where you want to connect to the 3rd + server. Add the Pubkey as IdentityFile to your ssh-connection, and ssh will find the + coresponding private-key in the forwarded ssh-agent. + Do the same with CertificateFile on remote-machine. + + + ... @@ -264,7 +289,7 @@ DESCRIPTION The default is: - PATTERN_KEYS = r"/(id_.*|identity.*|ssh[0-9]-.*)" + PATTERN_KEYS = r"/(id_.*|identity.*|ssh[0-9]-.*|.*\.so)" You can also redefine: diff --git a/ssh-ident b/ssh-ident index 320aa6b..9ac42fc 100755 --- a/ssh-ident +++ b/ssh-ident @@ -409,7 +409,7 @@ class Config(object): "DIR_AGENTS": "$HOME/.ssh/agents", # How to identify key files in the identities directory. - "PATTERN_KEYS": r"/(id_.*|identity.*|ssh[0-9]-.*)", + "PATTERN_KEYS": r"/(id_.*|identity.*|ssh[0-9]-.*|.*\.so)", # How to identify ssh config files. "PATTERN_CONFIG": r"/config$", @@ -450,6 +450,11 @@ class Config(object): # valid values are: LOG_ERROR, LOG_WARN, LOG_INFO, LOG_DEBUG # use 0 to disable ALL output (not recommended!) "VERBOSITY": LOG_INFO, + + # When using a hardware-Token, smartcard with pkcs11, it will be only + # loaded, when not loaded (ask for PIN) in ssh-agent. + # Set FORCE_RELOAD_HWTOKEN: True, if you wand to login to token every time + "FORCE_RELOAD_HWTOKEN": True, } def __init__(self): @@ -568,6 +573,7 @@ def FindKeys(identity, config): kinds = ( ("private", "priv"), ("public", "pub"), + (".so", "pkcs11"), (".pub", "pub"), ("", "priv"), ) @@ -692,7 +698,7 @@ class AgentManager(object): """ toload = self.FindUnloadedKeys(keys) if toload: - print("Loading keys:\n {0}".format( "\n ".join(toload)), + print("Loading keys:\n {0}\nLoading pkcs11:\n {1}".format( "\n ".join(toload["toload"]),"\n ".join(toload["pkcs11"])), file=sys.stderr, loglevel=LOG_INFO) self.LoadKeyFiles(toload) else: @@ -708,19 +714,36 @@ class AgentManager(object): iterable of strings, paths to private key files to load. """ loaded = set(self.GetLoadedKeys()) + ret = collections.defaultdict(dict) toload = set() + pkcs11 = set() + ret["toload"] = toload + ret["pkcs11"] = pkcs11 for key, config in keys.items(): - if "pub" not in config: - continue - if "priv" not in config: - continue + if "pkcs11" not in config: + if "pub" not in config: + continue + if "priv" not in config: + continue + if key.find("so.pub") > -1 or key.find(".so") > -1: + continue - fingerprint = self.GetPublicKeyFingerprint(config["pub"]) - if fingerprint in loaded: - continue + fingerprint = self.GetPublicKeyFingerprint(config["pub"]) + if fingerprint in loaded: + continue + + ret["toload"].add(config["priv"]) + else: + if key.find("cert") > -1 or key.find("pub") > -1: + continue + + fingerprint = self.GetPublicKeyFingerprintPKCS11(config["pkcs11"]) + if fingerprint in loaded: + continue - toload.add(config["priv"]) - return toload + ret["pkcs11"].add(config["pkcs11"]) + + return ret def LoadKeyFiles(self, keys): """Load all specified keys. @@ -728,13 +751,23 @@ class AgentManager(object): Args: keys: iterable of strings, each string a path to a key to load. """ - keys = " ".join(keys) options = self.config.Get("SSH_ADD_OPTIONS").get( self.identity, self.config.Get("SSH_ADD_DEFAULT_OPTIONS")) console = GetSessionTty() - self.RunShellCommandInAgent( - self.agent_file, "ssh-add {0} {1}".format(options, keys), - stdout=console, stdin=console) + if len(keys["toload"]) > 0: + privkeys = " ".join(keys["toload"]) + self.RunShellCommandInAgent( + self.agent_file, "ssh-add {0} {1}".format(options, privkeys), + stdout=console, stdin=console) + for key in keys["pkcs11"]: + if FORCE_RELOAD_HWTOKEN: + self.RunShellCommandInAgent( + self.agent_file, "ssh-add {0} -e {1}".format(options, key), + stdout=console, stdin=console) + self.RunShellCommandInAgent( + self.agent_file, "ssh-add {0} -s {1}".format(options, key), + stdout=console, stdin=console) + def GetLoadedKeys(self): """Returns an iterable of strings, each the fingerprint of a loaded key.""" @@ -765,6 +798,20 @@ class AgentManager(object): return None return fingerprint + @staticmethod + def GetPublicKeyFingerprintPKCS11(key): + """Returns the fingerprint of a public key as a string.""" + retval, stdout = AgentManager.RunShellCommand( + "ssh-keygen -l -D {0}|tr -s ' '".format(key)) + if retval: + return None + + try: + _, fingerprint, _ = stdout.decode("utf-8").split(" ", 2) + except ValueError: + return None + return fingerprint + @staticmethod def GetAgentFile(path, identity): """Returns the path to an agent config file. @@ -1004,8 +1051,8 @@ def main(argv): message = textwrap.dedent("""\ ssh-ident found '{0}' as the next command to run. Based on argv[0] ({1}), it seems like this will create a - loop. - + loop. + Please use BINARY_SSH, BINARY_DIR, or change the way ssh-ident is invoked (eg, a different argv[0]) to make it work correctly.""")