|
| 1 | +module ApplianceConsole |
| 2 | + module ExternalHttpdConfiguration |
| 3 | + # |
| 4 | + # External Authentication Definitions |
| 5 | + # |
| 6 | + IPA_INSTALL_COMMAND = "/usr/sbin/ipa-client-install" |
| 7 | + |
| 8 | + PAM_MODULE = "httpd-auth" |
| 9 | + PAM_CONFIG = "/etc/pam.d/#{PAM_MODULE}" |
| 10 | + PAM_CONFIGURATION = <<EOS |
| 11 | +auth required pam_sss.so |
| 12 | +account required pam_sss.so |
| 13 | +EOS |
| 14 | + SSSD_CONFIG = "/etc/sssd/sssd.conf" |
| 15 | + |
| 16 | + EXTERNAL_AUTH_FILE = "conf.d/cfme-external-auth" |
| 17 | + HTTPD_EXTERNAL_AUTH = "/etc/httpd/#{EXTERNAL_AUTH_FILE}" |
| 18 | + HTTPD_CONFIG = "/etc/httpd/conf.d/cfme-https-application.conf" |
| 19 | + |
| 20 | + RPM_COMMAND = "/bin/rpm" |
| 21 | + SERVICE_COMMAND = "/sbin/service" |
| 22 | + CHKCONFIG_COMMAND = "/sbin/chkconfig" |
| 23 | + |
| 24 | + GETSEBOOL_COMMAND = "/usr/sbin/getsebool" |
| 25 | + SETSEBOOL_COMMAND = "/usr/sbin/setsebool" |
| 26 | + |
| 27 | + INTERCEPT_FORM = "/dashboard/authenticate" |
| 28 | + |
| 29 | + LDAP_ATTRS = { |
| 30 | + "mail" => "REMOTE_USER_EMAIL", |
| 31 | + "givenname" => "REMOTE_USER_FIRSTNAME", |
| 32 | + "sn" => "REMOTE_USER_LASTNAME", |
| 33 | + "displayname" => "REMOTE_USER_FULLNAME" |
| 34 | + } |
| 35 | + |
| 36 | + # |
| 37 | + # IPA Configuration Methods |
| 38 | + # |
| 39 | + def ipa_client_configured? |
| 40 | + File.exist?(SSSD_CONFIG) |
| 41 | + end |
| 42 | + |
| 43 | + def ipa_client_configure(realm, domain, server, principal, password) |
| 44 | + say("Configuring the IPA Client ...") |
| 45 | + AwesomeSpawn.run!(IPA_INSTALL_COMMAND, |
| 46 | + :params => {"-N" => nil, |
| 47 | + "--force-join" => nil, |
| 48 | + "--realm=" => realm, |
| 49 | + "--domain=" => domain, |
| 50 | + "--server=" => server, |
| 51 | + "--principal=" => principal, |
| 52 | + "--password=" => password, |
| 53 | + "--fixed-primary" => nil, |
| 54 | + "--unattended" => nil}) |
| 55 | + end |
| 56 | + |
| 57 | + def ipa_client_unconfigure |
| 58 | + say("Un-Configuring the IPA Client ...") |
| 59 | + AwesomeSpawn.run(IPA_INSTALL_COMMAND, |
| 60 | + :params => {"--uninstall" => nil, |
| 61 | + "--unattended" => nil}) |
| 62 | + end |
| 63 | + |
| 64 | + # |
| 65 | + # HTTPD Configuration Methods |
| 66 | + # |
| 67 | + def httpd_mod_intercept_config |
| 68 | + config = " |
| 69 | +LoadModule authnz_pam_module modules/mod_authnz_pam.so |
| 70 | +LoadModule intercept_form_submit_module modules/mod_intercept_form_submit.so |
| 71 | +LoadModule lookup_identity_module modules/mod_lookup_identity.so |
| 72 | +
|
| 73 | +<Location #{INTERCEPT_FORM}> |
| 74 | + InterceptFormPAMService #{PAM_MODULE} |
| 75 | + InterceptFormLogin user_name |
| 76 | + InterceptFormPassword user_password |
| 77 | + InterceptFormLoginSkip admin |
| 78 | + InterceptFormClearRemoteUserForSkipped on |
| 79 | +</Location> |
| 80 | +
|
| 81 | +<Location #{INTERCEPT_FORM}> |
| 82 | +" |
| 83 | + LDAP_ATTRS.each { |ldap, http| config << " LookupUserAttr #{ldap} #{http}\n" } |
| 84 | + config << " |
| 85 | + LookupUserGroups REMOTE_USER_GROUPS \":\" |
| 86 | + LookupDbusTimeout 5000 |
| 87 | +</Location> |
| 88 | +" |
| 89 | + end |
| 90 | + |
| 91 | + def httpd_external_auth_config |
| 92 | + config = "RequestHeader unset X_REMOTE_USER\n" |
| 93 | + attrs = %w(REMOTE_USER EXTERNAL_AUTH_ERROR) + LDAP_ATTRS.values + %w(REMOTE_USER_GROUPS) |
| 94 | + attrs.each { |attr| config << "RequestHeader set X_#{attr} %{#{attr}}e env=#{attr}\n" } |
| 95 | + config.chomp! |
| 96 | + end |
| 97 | + |
| 98 | + def configure_httpd_application(config) |
| 99 | + ext_auth_include = "Include #{EXTERNAL_AUTH_FILE}" |
| 100 | + unless config.include?(ext_auth_include) |
| 101 | + config[/(\n)<VirtualHost/, 1] = "\n#{ext_auth_include}\n\n" |
| 102 | + end |
| 103 | + |
| 104 | + if config.include?("set X_REMOTE_USER") |
| 105 | + config[/RequestHeader unset X_REMOTE_USER(\n.*)+env=REMOTE_USER_GROUPS/] = httpd_external_auth_config |
| 106 | + else |
| 107 | + config[/set X_FORWARDED_PROTO 'https'(\n)/, 1] = "\n\n#{httpd_external_auth_config}\n" |
| 108 | + end |
| 109 | + end |
| 110 | + |
| 111 | + # |
| 112 | + # SSSD File Methods |
| 113 | + # |
| 114 | + def configure_sssd_domain(config, domain) |
| 115 | + ldap_user_extra_attrs = LDAP_ATTRS.keys.join(", ") |
| 116 | + if config.include?("ldap_user_extra_attrs = ") |
| 117 | + pattern = "[domain/#{domain}](\n.*)+ldap_user_extra_attrs = (.*)" |
| 118 | + config[/#{pattern}/, 2] = ldap_user_extra_attrs |
| 119 | + else |
| 120 | + pattern = "[domain/#{domain}].*(\n)" |
| 121 | + config[/#{pattern}/, 1] = "\nldap_user_extra_attrs = #{ldap_user_extra_attrs}\n" |
| 122 | + end |
| 123 | + end |
| 124 | + |
| 125 | + def configure_sssd_service(config) |
| 126 | + services = config.match(/\[sssd\](\n.*)+services = (.*)/)[2] |
| 127 | + services = "#{services}, ifp" unless services.include?("ifp") |
| 128 | + config[/\[sssd\](\n.*)+services = (.*)/, 2] = services |
| 129 | + end |
| 130 | + |
| 131 | + def configure_sssd_ifp(config) |
| 132 | + user_attributes = LDAP_ATTRS.keys.collect { |k| "+#{k}" }.join(", ") |
| 133 | + if config.include?("[ifp]") |
| 134 | + config[/\[ifp\](\n.*)+user_attributes = (.*)/, 2] = user_attributes |
| 135 | + else |
| 136 | + config << "\n[ifp] |
| 137 | + allowed_uids = apache, root |
| 138 | + user_attributes = #{user_attributes}\n" |
| 139 | + end |
| 140 | + end |
| 141 | + |
| 142 | + # |
| 143 | + # RPM Utilities |
| 144 | + # |
| 145 | + def rpm_installed?(rpm_package) |
| 146 | + result = AwesomeSpawn.run!(RPM_COMMAND, :params => {"-qa" => rpm_package}) |
| 147 | + if result.output.blank? |
| 148 | + say("#{rpm_package} RPM is not installed") |
| 149 | + return false |
| 150 | + end |
| 151 | + true |
| 152 | + end |
| 153 | + |
| 154 | + # |
| 155 | + # Validation Methods |
| 156 | + # |
| 157 | + def installation_valid? |
| 158 | + rpm_packages = %w(ipa-client sssd-dbus mod_intercept_form_submit mod_authnz_pam mod_lookup_identity) |
| 159 | + missing = rpm_packages.count { |package| !rpm_installed?(package) } |
| 160 | + if missing > 0 |
| 161 | + say("\nAppliance Installation is not valid for enabling External Authentication\n") |
| 162 | + return false |
| 163 | + end |
| 164 | + true |
| 165 | + end |
| 166 | + |
| 167 | + def valid_environment? |
| 168 | + return false unless installation_valid? |
| 169 | + if ipa_client_configured? |
| 170 | + return false unless agree("\nIPA Client already configured on this Appliance, Un-Configure first? (Y/N): ") |
| 171 | + ipa_client_unconfigure |
| 172 | + return false unless agree("\nProceed with External Authentication Configuration? (Y/N): ") |
| 173 | + end |
| 174 | + true |
| 175 | + end |
| 176 | + |
| 177 | + def valid_parameters?(ipaserver) |
| 178 | + host_reachable?(ipaserver, "IPA Server") |
| 179 | + end |
| 180 | + |
| 181 | + # |
| 182 | + # Config File I/O Methods |
| 183 | + # |
| 184 | + def config_file_read(path) |
| 185 | + File.open(path, "r") { |f| f.read } |
| 186 | + end |
| 187 | + |
| 188 | + def config_file_write(config, path, timestamp) |
| 189 | + FileUtils.copy(path, "#{path}.#{timestamp}") if File.exist?(path) |
| 190 | + File.open(path, "w") { |f| f.write(config) } |
| 191 | + end |
| 192 | + |
| 193 | + # |
| 194 | + # Network validation |
| 195 | + # |
| 196 | + def host_reachable?(host, what = "Server") |
| 197 | + require 'net/ping' |
| 198 | + say("Checking connectivity to #{host} ... ") |
| 199 | + unless Net::Ping::External.new(host).ping |
| 200 | + say("Failed.\nCould not connect to #{host},") |
| 201 | + say("the #{what} must be reachable by name.") |
| 202 | + return false |
| 203 | + end |
| 204 | + say("Succeeded.") |
| 205 | + true |
| 206 | + end |
| 207 | + end |
| 208 | +end |
0 commit comments