URL: https://github.com/freeipa/freeipa/pull/559 Author: pvomacka Title: #559: WebUI: Certificate login Action: synchronized
To pull the PR as Git branch: git remote add ghfreeipa https://github.com/freeipa/freeipa git fetch ghfreeipa pull/559/head:pr559 git checkout pr559
From 41aafdf67613ce3cd98471d00d523c6c792c849d Mon Sep 17 00:00:00 2001 From: Pavel Vomacka <pvoma...@redhat.com> Date: Thu, 9 Mar 2017 12:14:21 +0100 Subject: [PATCH 1/2] Support certificate login after installation and upgrade Add necessary steps which set SSSD and set SELinux boolean during installation or upgrade. Also create new endpoint in apache for login using certificates. https://pagure.io/freeipa/issue/6225 --- freeipa.spec.in | 1 + install/conf/ipa.conf | 30 +++++++++++++++++++++++++++++- install/share/gssproxy.conf.template | 1 + ipaclient/install/client.py | 20 ++++++++++++++++++++ ipaserver/install/httpinstance.py | 1 + ipaserver/install/server/upgrade.py | 5 +++++ 6 files changed, 57 insertions(+), 1 deletion(-) diff --git a/freeipa.spec.in b/freeipa.spec.in index 6eb00ee..bc3f3fb 100644 --- a/freeipa.spec.in +++ b/freeipa.spec.in @@ -255,6 +255,7 @@ Requires: mod_wsgi Requires: mod_auth_gssapi >= 1.5.0 Requires: mod_nss >= 1.0.8-26 Requires: mod_session +Requires: mod_lookup_identity Requires: python-ldap >= 2.4.15 Requires: python-gssapi >= 1.2.0 Requires: acl diff --git a/install/conf/ipa.conf b/install/conf/ipa.conf index 419d4e3..b4f2fb9 100644 --- a/install/conf/ipa.conf +++ b/install/conf/ipa.conf @@ -4,8 +4,13 @@ # This file may be overwritten on upgrades. # -ProxyRequests Off +# Load lookup_identity module in case it has not been loaded yet +# The module is used to search users according the certificate. +<IfModule !lookup_identity_module> + LoadModule lookup_identity_module modules/mod_lookup_identity.so +</IfModule> +ProxyRequests Off #We use xhtml, a file format that the browser validates DirectoryIndex index.html @@ -70,6 +75,7 @@ WSGIScriptReloading Off SessionMaxAge 1800 GssapiSessionKey file:/etc/httpd/alias/ipasession.key + GssapiImpersonate On GssapiDelegCcacheDir /var/run/ipa/ccaches GssapiDelegCcachePerms mode:0660 gid:ipaapi GssapiUseS4U2Proxy on @@ -97,6 +103,28 @@ Alias /ipa/session/cookie "/usr/share/ipa/gssapi.login" Allow from all </Location> +# Login with user certificate/smartcard configuration +# This configuration needs to be loaded after <Location "/ipa"> +<Location "/ipa/session/login_x509"> + AuthType none + GssapiDelegCcacheDir /var/run/ipa/ccaches + GssapiDelegCcachePerms mode:0660 gid:ipaapi + NSSVerifyClient require + NSSUserName SSL_CLIENT_CERT + LookupUserByCertificate On + WSGIProcessGroup ipa + WSGIApplicationGroup ipa + + GssapiUseSessions On + Session On + SessionCookieName ipa_session path=/ipa;httponly;secure; + SessionHeader IPASESSION + SessionMaxAge 1800 + GssapiSessionKey file:/etc/httpd/alias/ipasession.key + + Header unset Set-Cookie +</Location> + <Location "/ipa/session/change_password"> Satisfy Any Order Deny,Allow diff --git a/install/share/gssproxy.conf.template b/install/share/gssproxy.conf.template index fbb158a..d703144 100644 --- a/install/share/gssproxy.conf.template +++ b/install/share/gssproxy.conf.template @@ -4,6 +4,7 @@ cred_store = keytab:$HTTP_KEYTAB cred_store = client_keytab:$HTTP_KEYTAB allow_protocol_transition = true + allow_constrained_delegation = true cred_usage = both euid = $HTTPD_USER diff --git a/ipaclient/install/client.py b/ipaclient/install/client.py index 774eaaf..579d1aa 100644 --- a/ipaclient/install/client.py +++ b/ipaclient/install/client.py @@ -846,6 +846,9 @@ def configure_sssd_conf( sssdconfig.new_config() domain = sssdconfig.new_domain(cli_domain) + if options.on_master: + sssd_enable_service(sssdconfig, 'ifp') + if ( (options.conf_ssh and file_exists(paths.SSH_CONFIG)) or (options.conf_sshd and file_exists(paths.SSHD_CONFIG)) @@ -948,6 +951,23 @@ def configure_sssd_conf( return 0 +def sssd_enable_service(sssdconfig, service): + try: + sssdconfig.new_service(service) + except SSSDConfig.ServiceAlreadyExists: + pass + except SSSDConfig.ServiceNotRecognizedError: + root_logger.error( + "Unable to activate the %s service in SSSD config.", service) + root_logger.info( + "Please make sure you have SSSD built with %s support " + "installed.", service) + root_logger.info( + "Configure %s support manually in /etc/sssd/sssd.conf.", service) + + sssdconfig.activate_service(service) + + def change_ssh_config(filename, changes, sections): if not changes: return True diff --git a/ipaserver/install/httpinstance.py b/ipaserver/install/httpinstance.py index 3e8fb0c..048f317 100644 --- a/ipaserver/install/httpinstance.py +++ b/ipaserver/install/httpinstance.py @@ -53,6 +53,7 @@ httpd_can_network_connect='on', httpd_manage_ipa='on', httpd_run_ipa='on', + httpd_dbus_sssd='on', ) HTTPD_USER = constants.HTTPD_USER diff --git a/ipaserver/install/server/upgrade.py b/ipaserver/install/server/upgrade.py index b19c2f0..993835e 100644 --- a/ipaserver/install/server/upgrade.py +++ b/ipaserver/install/server/upgrade.py @@ -23,6 +23,7 @@ import SSSDConfig import ipalib.util import ipalib.errors +from ipaclient.install.client import sssd_enable_service from ipaplatform import services from ipaplatform.tasks import tasks from ipapython import ipautil, version, certdb @@ -1771,6 +1772,10 @@ def upgrade_configuration(): set_sssd_domain_option('ipa_server_mode', 'True') + sssdconfig = SSSDConfig.SSSDConfig() + sssdconfig.import_config() + sssd_enable_service(sssdconfig, 'ifp') + krb = krbinstance.KrbInstance(fstore) krb.fqdn = fqdn krb.realm = api.env.realm From 2c34b32aa90c7ee21a9e86e2bd2105f794878d3a Mon Sep 17 00:00:00 2001 From: Pavel Vomacka <pvoma...@redhat.com> Date: Thu, 9 Mar 2017 12:17:00 +0100 Subject: [PATCH 2/2] WebUI: add link to login page which for login using certificate Also add error message when login failed. https://pagure.io/freeipa/issue/6225 --- install/ui/src/freeipa/auth.js | 4 +- install/ui/src/freeipa/widgets/LoginScreen.js | 73 ++++++++++++++++++++++- install/ui/src/freeipa/widgets/LoginScreenBase.js | 5 ++ 3 files changed, 78 insertions(+), 4 deletions(-) diff --git a/install/ui/src/freeipa/auth.js b/install/ui/src/freeipa/auth.js index 5e160a7..992b54a 100644 --- a/install/ui/src/freeipa/auth.js +++ b/install/ui/src/freeipa/auth.js @@ -111,7 +111,7 @@ auth.Auth = declare([Stateful, Evented], { * Enabled auth methods * @property {string[]} */ - auth_methods: ['kerberos', 'password'], + auth_methods: ['kerberos', 'password', 'certificate'], /** * Authenticated user's Kerberos principal @@ -249,4 +249,4 @@ auth.Auth = declare([Stateful, Evented], { auth.current = new auth.Auth(); return auth; -}); \ No newline at end of file +}); diff --git a/install/ui/src/freeipa/widgets/LoginScreen.js b/install/ui/src/freeipa/widgets/LoginScreen.js index 0096433..66d672e 100644 --- a/install/ui/src/freeipa/widgets/LoginScreen.js +++ b/install/ui/src/freeipa/widgets/LoginScreen.js @@ -19,10 +19,12 @@ */ define(['dojo/_base/declare', + 'dojo/Deferred', 'dojo/dom-construct', 'dojo/dom-style', 'dojo/query', 'dojo/on', + 'dojo/topic', '../ipa', '../auth', '../reg', @@ -31,7 +33,7 @@ define(['dojo/_base/declare', '../util', './LoginScreenBase' ], - function(declare, construct, dom_style, query, on, + function(declare, Deferred, construct, dom_style, query, on, topic, IPA, auth, reg, FieldBinder, text, util, LoginScreenBase) { @@ -55,11 +57,15 @@ define(['dojo/_base/declare', " have valid tickets (obtainable via kinit) and " + "<a href='http://${host}/ipa/config/unauthorized.html'>configured</a>" + " the browser correctly, then click Login. ", + cert_msg: "<i class=\"fa fa-info-circle\"></i> To login with <strong>certificate</strong>," + + " please make sure you have valid personal certificate. ", form_auth_failed: "Login failed due to an unknown reason. ", krb_auth_failed: "Authentication with Kerberos failed", + cert_auth_failed: "Authentication with personal certificate failed", + password_expired: "Your password has expired. Please enter a new password.", password_change_complete: "Password change complete", @@ -72,9 +78,12 @@ define(['dojo/_base/declare', user_locked: "The user account you entered is locked. ", + x509_url: '/ipa/session/login_x509', + //nodes: login_btn_node: null, reset_btn_node: null, + cert_btn_node: null, /** * View this form is in. @@ -86,6 +95,16 @@ define(['dojo/_base/declare', render_buttons: function(container) { + this.cert_btn_node = IPA.button({ + name: 'cert_auth', + title:"Login using personal certificate", + label: "Login Using Certificate", + button_class: 'btn btn-link', + click: this.login_with_cert.bind(this) + })[0]; + construct.place(this.cert_btn_node, container); + construct.place(document.createTextNode(" "), container); + this.sync_btn_node = IPA.button({ name: 'sync', label: text.get('@i18n:login.sync_otp_token', "Sync OTP Token"), @@ -251,6 +270,18 @@ define(['dojo/_base/declare', }.bind(this)); }, + login_with_cert: function() { + + this.lookup_credentials().then(function(status) { + if (status === 200) { + this.emit('logged_in'); + } else { + var val_summary = this.get_widget('validation'); + val_summary.add_error('login', this.cert_auth_failed); + } + }.bind(this)); + }, + login_and_reset: function() { var val_summary = this.get_widget('validation'); @@ -293,6 +324,40 @@ define(['dojo/_base/declare', }, + lookup_credentials: function() { + + var status; + var d = new Deferred(); + + function error_handler(xhr, text_status, error_thrown) { + d.resolve(xhr.status); + topic.publish('rpc-end'); + } + + function success_handler(data, text_status, xhr) { + auth.current.set_authenticated(true, 'kerberos'); + d.resolve(xhr.status); + topic.publish('rpc-end'); + } + + var login = this.get_field('username').get_value()[0]; + + var request = { + url: this.x509_url, + cache: false, + type: "GET", + data: $.param({ + 'username': login + }), + success: success_handler, + error: error_handler + }; + topic.publish('rpc-start'); + $.ajax(request); + + return d.promise; + }, + refresh: function() { if (this.view === 'reset') { this.show_reset_view(); @@ -307,7 +372,7 @@ define(['dojo/_base/declare', var val_summary = this.get_widget('validation'); val_summary.add_info('expired', this.expired_msg); } - this.set_visible_buttons(['sync', 'login']); + this.set_visible_buttons(['cert_auth', 'sync', 'login']); if (this.password_enabled()) { this.use_fields(['username', 'password']); var username_f = this.get_field('username'); @@ -344,6 +409,10 @@ define(['dojo/_base/declare', if (this.kerberos_enabled()) { aside += "<p>"+this.kerberos_msg+"<p/>"; } + if (this.certificate_enabled()) { + aside += "<p>"+this.cert_msg+"<p/>"; + } + this.set('aside', aside); }, diff --git a/install/ui/src/freeipa/widgets/LoginScreenBase.js b/install/ui/src/freeipa/widgets/LoginScreenBase.js index a1c986e..a8c207f 100644 --- a/install/ui/src/freeipa/widgets/LoginScreenBase.js +++ b/install/ui/src/freeipa/widgets/LoginScreenBase.js @@ -328,6 +328,11 @@ define(['dojo/_base/declare', return auth.current.auth_methods.indexOf('password') > -1; }, + certificate_enabled: function() { + return auth.current.auth_methods.indexOf('certificate') > -1; + }, + + postscript: function(args) { this.create_fields(); },
-- Manage your subscription for the Freeipa-devel mailing list: https://www.redhat.com/mailman/listinfo/freeipa-devel Contribute to FreeIPA: http://www.freeipa.org/page/Contribute/Code