New post about ssh known_hosts merging master
authorJoerg Jaspert <joerg@debian.org>
Thu, 11 Apr 2019 06:50:35 +0000 (08:50 +0200)
committerJoerg Jaspert <joerg@debian.org>
Thu, 11 Apr 2019 06:51:51 +0000 (08:51 +0200)
_posts/2019-04-11-ssh-known_hosts-merge-by-key.md [new file with mode: 0644]

diff --git a/_posts/2019-04-11-ssh-known_hosts-merge-by-key.md b/_posts/2019-04-11-ssh-known_hosts-merge-by-key.md
new file mode 100644 (file)
index 0000000..5027385
--- /dev/null
@@ -0,0 +1,97 @@
+---
+layout: post
+title: SSH known_hosts merge by key
+categories:
+- personal
+- tech
+date: '2019-04-11 08:39:23 +0200'
+---
+
+So I just ran again into sshs annoying behaviour of storing the same
+host key a trillionth time in my _.ssh/known_hosts_ file. And then
+later on, when it changes (for whatever reason), complaining over and
+over until one manually fixed all those tons of lines.
+
+**Annoying**.
+
+So I came up with a little hacky python script that takes one or more
+files in the known_hosts format and merges them by key. So you end up
+with one line per key, and as many hostnames, IP addresses and whatnot
+in front.
+
+Note: Does not work with that annoying _HashKnownHosts_ format, and I
+have no idea what ssh will say if you use one of those @ tags in
+there. The first one is think is crap, so I don't use it anywhere,
+the second I never had to use, so no idea if it breaks or not.
+
+{% highlight python %}
+#!/usr/bin/python
+
+# Copyright (C) 2019 Joerg Jaspert <joerg@debian.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# .
+# 1. Redistributions of source code must retain the above copyright
+#    notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+#    notice, this list of conditions and the following disclaimer in the
+#    documentation and/or other materials provided with the distribution.
+# .
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+import argparse
+
+parser = argparse.ArgumentParser(
+    description='Merge ssh known host entries by key',
+    epilog="""
+Merges entries in given ssh known_hosts file based on the key. One can also merge from multiple files.
+The file should NOT use the HashKnownHosts feature.
+""")
+
+parser.add_argument('files', type=str, nargs='+', help='files that should be merged')
+parser.add_argument('-o', '--output', type=str, nargs='?', help='output file (defaults is STDOUT). Only opened after merge is complete, so can be used for inplace merge.')
+args = parser.parse_args()
+
+if args.output:
+  import StringIO
+  output = StringIO.StringIO()
+else:
+  import sys
+  output = sys.stdout
+
+hostkeys = {}
+for kfile in args.files:
+  with open(kfile) as kf:
+    for line in kf:
+      line_splitted = line.rstrip().split(' ')
+      hosts = line_splitted.pop(0).split(',')
+      key_type = line_splitted.pop(0)
+      key = line_splitted[0]
+      if not key in hostkeys:
+        hostkeys[key]={}
+        hostkeys[key]["hosts"] = {}
+      hostkeys[key]["key_type"]=key_type
+      # Store the host entries, uniquify them
+      for entry in hosts:
+        hostkeys[key]["hosts"][entry]=1
+
+# And now output it all
+for key in hostkeys:
+  output.write('%s %s %s\n' %
+  (','.join(hostkeys[key]["hosts"]), hostkeys[key]["key_type"], key))
+
+if args.output:
+  with open(args.output,'w') as f:
+    f.write(output.getvalue())
+{% endhighlight %}