Faidon Liambotis has submitted this change and it was merged.

Change subject: ssh: fix completely broken host key collection
......................................................................


ssh: fix completely broken host key collection

SSH host key collection basically exhibits the "Naginator" problem: it's
extremely slow, due to needing to parse the whole file N+1 times. Thus,
we are only supposed to collect on every run only on bastions, and on
the rest of the fleet a few times a day (the infamous "position of the
moon").

I say "supposed" because:
- This seems to be only enabled on every run for tin and bast1001, not
  other bastions.
- It does does not in fact run at all on *any* other host (and thus the
  potm logic is unused), as the class is never included. Many hosts have
  their ssh_known_hosts with a Oct 18, 2013 mtime, which is when
  36c7f7d90c551755fee90234ad1c4abf8e32cb4e got merged and broke this.

This patch adds an "sshknowngen", copied over from "naggen2" and
modified extensively. It then replaces the ssh_known_hosts generation
with a File resource and a generate() statement. Finally, it
(re-)enables it for every host on the fleet, unconditionally (no potm,
should be fast enough by now).

A diff between bast1001's ssh_known_hosts and sshknowngen's output
showed no functional differences and the current logic is quite broken
already, so this should be safe.

Change-Id: Ieaede0c9356c46661ddb3c236da4c5f0c3ce350a
---
M manifests/site.pp
A modules/puppetmaster/files/sshknowngen
A modules/puppetmaster/manifests/generators.pp
D modules/puppetmaster/manifests/naggen2.pp
M modules/puppetmaster/manifests/scripts.pp
M modules/ssh/manifests/client.pp
D modules/ssh/manifests/hostkeys-collect.pp
7 files changed, 150 insertions(+), 34 deletions(-)

Approvals:
  Faidon Liambotis: Looks good to me, approved
  jenkins-bot: Verified



diff --git a/manifests/site.pp b/manifests/site.pp
index cb9e514..ef5e079 100644
--- a/manifests/site.pp
+++ b/manifests/site.pp
@@ -246,7 +246,6 @@
     include standard
     include subversion::client
     include dsh
-    include ssh::hostkeys-collect
     class { 'nfs::netapp::home':
         mountpoint => '/srv/home_pmtpa',
         mount_site => 'pmtpa',
@@ -2359,7 +2358,6 @@
     include role::deployment::server
     include mysql
     include role::labsdb::manager
-    include ssh::hostkeys-collect
     include role::releases::upload
 
     interface::add_ip6_mapped { 'main':
diff --git a/modules/puppetmaster/files/sshknowngen 
b/modules/puppetmaster/files/sshknowngen
new file mode 100755
index 0000000..915f0dc
--- /dev/null
+++ b/modules/puppetmaster/files/sshknowngen
@@ -0,0 +1,115 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+import os
+import sys
+# python 3 compatibility
+try:
+    import ConfigParser as configparser
+except ImportError:
+    import configparser
+
+import sqlalchemy
+from collections import defaultdict
+import argparse
+import logging
+from logging.handlers import SysLogHandler
+
+
+class SshKnownHostsGen(object):
+    query = """
+SELECT resources.title as title,
+  GROUP_CONCAT(CONCAT(param_names.name, "\t", param_values.value)
+  SEPARATOR "\n") AS service_content
+FROM param_values
+  JOIN param_names ON param_names.id = param_values.param_name_id
+  JOIN resources ON param_values.resource_id = resources.id
+WHERE restype = 'Sshkey'
+GROUP BY resources.id ORDER BY resources.title ASC"""
+
+    def load_config(self, configfile):
+        self.config = configparser.SafeConfigParser()
+        self.config.read(configfile)
+        self.dsn = "{}://{}:{}@{}:3306/puppet".format(
+            self.config.get('master', 'dbadapter'),
+            self.config.get('master', 'dbuser'),
+            self.config.get('master', 'dbpassword'),
+            self.config.get('master', 'dbserver')
+        )
+
+    def __init__(self, configfile, debug):
+        self.log = logging.getLogger('sshknowngen')
+        self.log.debug('Loading configfile %s', configfile)
+        self.load_config(configfile)
+        self.db_engine = sqlalchemy.create_engine(
+            self.dsn,
+            echo=debug
+        )
+
+    def _query(self):
+        connection = self.db_engine.connect()
+        connection.execute('set group_concat_max_len = @@max_allowed_packet')
+        res = connection.execute(self.query)
+        connection.close()
+        return res
+
+    def render(self):
+        try:
+            for entity in self._query():
+                self.log.debug('Working on resource %s', entity['title'])
+                attrs = defaultdict(list)
+                for restuple in entity['service_content'].split("\n"):
+                    (k, v) = restuple.split("\t")
+                    attrs[k].append(v)
+
+                if attrs['ensure'][0] != 'present':
+                    continue
+
+                hostname = entity['title']
+                aliases = [
+                    a
+                    for a in attrs['host_aliases']
+                    if not a.startswith('---')
+                ]
+                host_part = ",".join([hostname] + aliases)
+                (keytype, key) = (attrs['type'][0], attrs['key'][0])
+
+                line = '%s %s %s' % (host_part, keytype, key)
+                yield line
+        except Exception as e:
+            self.log.exception(
+                'Could not generate output for resource Sshkey')
+            sys.exit(30)
+
+
+def main():
+    parser = argparse.ArgumentParser()
+    parser.add_argument(
+        '--configfile', '-c', dest='configfile', 
default='/etc/puppet/puppet.conf')
+    parser.add_argument('--debug', action='store_true', default=False)
+    args = parser.parse_args()
+
+    log_format='%(name)s: %(levelname)s - %(message)s'
+    log = logging.getLogger('sshknowngen')
+
+    if not args.debug:
+        #if normal mode, log to syslog
+        log.setLevel(logging.INFO)
+        log.propagate = False
+        handler = SysLogHandler(
+                address='/dev/log',
+                facility=SysLogHandler.LOG_LOCAL3)
+        formatter = logging.Formatter(fmt=log_format)
+        handler.setFormatter(formatter)
+        log.addHandler(handler)
+    else:
+        #if debug mode, print to stderr
+        logging.basicConfig(level=logging.DEBUG, format=log_format)
+
+    log.info('Generating output for resource Sshkey')
+    n = SshKnownHostsGen(args.configfile, args.debug)
+    for entity in n.render():
+        print(entity)
+    log.info('Run completed')
+
+if __name__ == '__main__':
+    main()
diff --git a/modules/puppetmaster/manifests/generators.pp 
b/modules/puppetmaster/manifests/generators.pp
new file mode 100644
index 0000000..412b3bb
--- /dev/null
+++ b/modules/puppetmaster/manifests/generators.pp
@@ -0,0 +1,23 @@
+class puppetmaster::generators($ensure = 'present'){
+
+    $packages = ['python-jinja2', 'python-mysqldb', 'python-sqlalchemy']
+    require_package($packages)
+
+    file {'/usr/local/bin/naggen2':
+        ensure  => 'present',
+        owner   => 'root',
+        group   => 'root',
+        mode    => '0555',
+        source  => 'puppet:///modules/puppetmaster/naggen2',
+        require => Package[$packages]
+    }
+
+    file {'/usr/local/bin/sshknowngen':
+        ensure  => 'present',
+        owner   => 'root',
+        group   => 'root',
+        mode    => '0555',
+        source  => 'puppet:///modules/puppetmaster/sshknowngen',
+        require => Package[$packages]
+    }
+}
diff --git a/modules/puppetmaster/manifests/naggen2.pp 
b/modules/puppetmaster/manifests/naggen2.pp
deleted file mode 100644
index 5476009..0000000
--- a/modules/puppetmaster/manifests/naggen2.pp
+++ /dev/null
@@ -1,14 +0,0 @@
-class puppetmaster::naggen2($ensure = 'present'){
-
-    $packages = ['python-jinja2', 'python-mysqldb', 'python-sqlalchemy']
-    require_package($packages)
-
-    file {'/usr/local/bin/naggen2':
-        ensure  => 'present',
-        owner   => 'root',
-        group   => 'root',
-        mode    => '0555',
-        source  => 'puppet:///modules/puppetmaster/naggen2',
-        require => Package[$packages]
-    }
-}
diff --git a/modules/puppetmaster/manifests/scripts.pp 
b/modules/puppetmaster/manifests/scripts.pp
index 82027c0..514431b 100644
--- a/modules/puppetmaster/manifests/scripts.pp
+++ b/modules/puppetmaster/manifests/scripts.pp
@@ -13,7 +13,7 @@
     $keep_reports_minutes = 960, # 16 hours
 ) {
 
-    require puppetmaster::naggen2
+    require puppetmaster::generators
 
     file {'/usr/local/bin/uuid-generator':
         ensure  => absent,
diff --git a/modules/ssh/manifests/client.pp b/modules/ssh/manifests/client.pp
index 11c21ea..87f3582 100644
--- a/modules/ssh/manifests/client.pp
+++ b/modules/ssh/manifests/client.pp
@@ -1,5 +1,14 @@
 class ssh::client {
-    package { "openssh-client":
-        ensure => latest
+    package { 'openssh-client':
+        ensure => latest,
     }
+
+    file { '/etc/ssh/ssh_known_hosts':
+        content => generate('/usr/local/bin/sshknowngen'),
+        backup  => false,
+        owner   => 'root',
+        group   => 'root',
+        mode    => '0644',
+    }
+
 }
diff --git a/modules/ssh/manifests/hostkeys-collect.pp 
b/modules/ssh/manifests/hostkeys-collect.pp
deleted file mode 100644
index e52a1b3..0000000
--- a/modules/ssh/manifests/hostkeys-collect.pp
+++ /dev/null
@@ -1,15 +0,0 @@
-class ssh::hostkeys-collect {
-    # Do this about twice a day
-    $potm = inline_template('<%= srand ; (rand(25) == 5).to_s.capitalize -%>')
-    if $hostname == "tin" or $hostname == "bast1001" or $hostname == "mira" or 
$potm == "True" {
-        notice("Collecting SSH host keys on ${hostname}.")
-
-        # install all collected SSH host keys
-        Sshkey <<| |>>
-
-        # clean up unmanaged host keys
-        resources { 'sshkey':
-            purge => true,
-        }
-    }
-}

-- 
To view, visit https://gerrit.wikimedia.org/r/210926
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: merged
Gerrit-Change-Id: Ieaede0c9356c46661ddb3c236da4c5f0c3ce350a
Gerrit-PatchSet: 4
Gerrit-Project: operations/puppet
Gerrit-Branch: production
Gerrit-Owner: Faidon Liambotis <fai...@wikimedia.org>
Gerrit-Reviewer: Alexandros Kosiaris <akosia...@wikimedia.org>
Gerrit-Reviewer: BBlack <bbl...@wikimedia.org>
Gerrit-Reviewer: Faidon Liambotis <fai...@wikimedia.org>
Gerrit-Reviewer: Giuseppe Lavagetto <glavage...@wikimedia.org>
Gerrit-Reviewer: jenkins-bot <>

_______________________________________________
MediaWiki-commits mailing list
MediaWiki-commits@lists.wikimedia.org
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to