Andrew Bogott has uploaded a new change for review.

  https://gerrit.wikimedia.org/r/274167

Change subject: Support totp auth in keystone
......................................................................

Support totp auth in keystone

Bug: T105690
Change-Id: I5e067bcd326a345616139e307336e44591734aad
---
M hieradata/eqiad.yaml
A modules/openstack/files/kilo/keystone/totp/wmtotp.py
M modules/openstack/manifests/keystone/service.pp
M modules/openstack/templates/kilo/keystone/keystone.conf.erb
4 files changed, 129 insertions(+), 0 deletions(-)


  git pull ssh://gerrit.wikimedia.org:29418/operations/puppet 
refs/changes/67/274167/1

diff --git a/hieradata/eqiad.yaml b/hieradata/eqiad.yaml
index 7e82fae..3c281f6 100644
--- a/hieradata/eqiad.yaml
+++ b/hieradata/eqiad.yaml
@@ -129,6 +129,8 @@
   auth_host: 208.80.154.92
   admin_project_id: 'admin'
   admin_project_name: 'admin'
+  oath_dbname: 'labswiki'
+  oath_dbhost: 'silver.wikimedia.org'
 
 designateconfig:
   db_host:  'm5-master.eqiad.wmnet'
diff --git a/modules/openstack/files/kilo/keystone/totp/wmtotp.py 
b/modules/openstack/files/kilo/keystone/totp/wmtotp.py
new file mode 100644
index 0000000..0d499ba
--- /dev/null
+++ b/modules/openstack/files/kilo/keystone/totp/wmtotp.py
@@ -0,0 +1,109 @@
+# Copyright 2016 Wikimedia Foundation
+#
+#  (this is a custom hack local to the Wikimedia Labs deployment)
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from oslo_log import log
+from oslo_config import cfg
+
+from keystone import auth
+from keystone.auth import plugins as auth_plugins
+from keystone.common import dependency
+from keystone import exception
+from keystone.i18n import _
+
+import oath
+import base64
+import mysql.connector
+
+METHOD_NAME = 'mwtotp'
+
+LOG = log.getLogger(__name__)
+CONF = cfg.CONF
+
+oathoptions = [
+    cfg.StrOpt('dbuser',
+               default='wiki_user',
+               help='Database user for retrieving OATH secret.'),
+    cfg.StrOpt('dbpass',
+               default='12345',
+               help='Database password for retrieving OATH secret.'),
+    cfg.StrOpt('dbhost',
+               default='localhost',
+               help='Database host for retrieving OATH secret.'),
+    cfg.StrOpt('dbname',
+               default='labswiki',
+               help='Database name for retrieving OATH secret.'),
+]
+
+for option in oathoptins:
+    conf.register_opt(option, group='oath')
+
+
[email protected]('identity_api')
+class Mwtotp(auth.AuthMethodHandler):
+
+    method = METHOD_NAME
+
+    def authenticate(self, context, auth_payload, auth_context):
+        """Try to authenticate against the identity backend."""
+        user_info = auth_plugins.UserAuthInfo.create(auth_payload, self.method)
+
+        # FIXME(gyee): identity.authenticate() can use some refactoring since
+        # all we care is password matches
+        try:
+            self.identity_api.authenticate(
+                context,
+                user_id=user_info.user_id,
+                password=user_info.password)
+        except AssertionError:
+            # authentication failed because of invalid username or password
+            msg = _('Invalid username or password')
+            raise exception.Unauthorized(msg)
+
+
+        # Password auth succeeded, check two-factor
+        # LOG.debug("OATH: Doing 2FA for user_info " + ( "%s(%r)" % 
(user_info.__class__, user_info.__dict__) ) )
+        # LOG.debug("OATH: Doing 2FA for auth_payload " + ( "%s(%r)" % 
(auth_payload.__class__, auth_payload) ) )
+        cnx = mysql.connector.connect(
+            user=CONF.oath.dbuser,
+            password=CONF.oath.dbpass,
+            database=CONF.oath.dbname,
+            host=CONF.oath.dbhost)
+        cur = cnx.cursor(buffered=True)
+        sql = ('SELECT oath.secret as secret from user '
+              'left join oathauth_users as oath on oath.id = user.user_id '
+              'where user.user_name = %s LIMIT 1')
+        cur.execute(sql, (user_info.user_ref['name'], ))
+        secret = cur.fetchone()[0]
+
+        if secret:
+            if 'mwtotp' in auth_payload['user']:
+                (p, d) = oath.accept_totp(
+                    base64.b16encode(base64.b32decode(secret)),
+                    auth_payload['user']['totp'])
+                if p:
+                    LOG.debug("OATH: 2FA passed")
+                else:
+                    LOG.debug("OATH: 2FA failed")
+                    msg = _('Invalid two-factor token')
+                    raise exception.Unauthorized(msg)
+            else:
+                LOG.debug("OATH: 2FA failed, missing totp param")
+                msg = _('Missing two-factor token')
+                raise exception.Unauthorized(msg)
+        else:
+            LOG.debug("OATH: user '%s' does not have 2FA enabled.", 
user_info.user_ref['name'])
+
+        auth_context['user_id'] = user_info.user_id
diff --git a/modules/openstack/manifests/keystone/service.pp 
b/modules/openstack/manifests/keystone/service.pp
index 19d50c9..5132597 100644
--- a/modules/openstack/manifests/keystone/service.pp
+++ b/modules/openstack/manifests/keystone/service.pp
@@ -28,6 +28,12 @@
             owner   => 'root',
             group   => 'root',
             require => Package['keystone'];
+        '/usr/lib/python2.7/dist-packages/keystone/auth/plugins/wmtotp.py'
+            source  => 
"puppet:///modules/openstack/${openstack_version}/keystone/totp/wmtotp.py",
+            mode    => '0644',
+            owner   => 'root',
+            group   => 'root',
+            require => Package['keystone'];
     }
 
     if $::fqdn == hiera('labs_nova_controller') {
diff --git a/modules/openstack/templates/kilo/keystone/keystone.conf.erb 
b/modules/openstack/templates/kilo/keystone/keystone.conf.erb
index 3e16967..1dc09fe 100644
--- a/modules/openstack/templates/kilo/keystone/keystone.conf.erb
+++ b/modules/openstack/templates/kilo/keystone/keystone.conf.erb
@@ -204,3 +204,15 @@
 use = egg:Paste#urlmap
 /v2.0 = admin_api
 / = admin_version_api
+
+[auth]
+methods = external,password,token,wmtotp
+
+wmtotp=password = keystone.auth.plugins.wmtotp.Wmtotp
+
+[oath]
+
+dbuser = <%= @keystoneconfig["oath_dbuser"] %>
+dbpass = <%= @keystoneconfig["oath_dbpass"] %>
+dbname = <%= @keystoneconfig["oath_dbname"] %>
+dbhost = <%= @keystoneconfig["oath_dbhost"] %>

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

Gerrit-MessageType: newchange
Gerrit-Change-Id: I5e067bcd326a345616139e307336e44591734aad
Gerrit-PatchSet: 1
Gerrit-Project: operations/puppet
Gerrit-Branch: production
Gerrit-Owner: Andrew Bogott <[email protected]>

_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to