Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add oidc config parameters #1201

Open
wants to merge 3 commits into
base: master
Choose a base branch
from

Conversation

WoutResseler
Copy link

This PR adds a oidc parameters, to allow enabling oidc authentication with for example keycloak completely from config as code. The parameters get added to settings.yml config.

Copy link
Member

@ekohl ekohl left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Exposing these makes sense to me. In hindsight I'm not happy with how we (I) have implemented Keycloak integration. It relies a lot on keycloak-httpd-client-install and that's problematic. This lower level "expose the parameters" approach is IMHO better, but I have some implementation notes.

I'd prefer to avoid exposing those authorize_login_delegation. At first I thought about a more specific Boolean $oidc, but prefer a more generic auth parameter like Enum['internal', 'ipa', 'keycloak', 'oidc'] $auth = 'internal'. We have places where we could then use a case statement to handle it. This makes it obvious to users they need to select a single auth mechanism.

This is where we handle external auth on the Foreman side:

if $foreman::ipa_authentication {
if $facts['os']['selinux']['enabled'] {
selboolean { ['allow_httpd_mod_auth_pam', 'httpd_dbus_sssd']:
persistent => true,
value => 'on',
}
}
if $foreman::ipa_manage_sssd {
service { 'sssd':
ensure => running,
enable => true,
require => Package['sssd-dbus'],
}
}
file { "/etc/pam.d/${foreman::pam_service}":
ensure => file,
owner => root,
group => root,
mode => '0644',
content => template('foreman/pam_service.erb'),
}
$http_keytab = pick($foreman::http_keytab, "${apache::conf_dir}/http.keytab")
exec { 'ipa-getkeytab':
command => "/bin/echo Get keytab \
&& KRB5CCNAME=KEYRING:session:get-http-service-keytab kinit -k \
&& KRB5CCNAME=KEYRING:session:get-http-service-keytab /usr/sbin/ipa-getkeytab -k ${http_keytab} -p HTTP/${facts['networking']['fqdn']} \
&& kdestroy -c KEYRING:session:get-http-service-keytab",
creates => $http_keytab,
}
-> file { $http_keytab:
ensure => file,
owner => $apache::user,
mode => '0600',
}
$gssapi_local_name = bool2str($foreman::gssapi_local_name, 'On', 'Off')
foreman::config::apache::fragment { 'intercept_form_submit':
ssl_content => template('foreman/intercept_form_submit.conf.erb'),
}
foreman::config::apache::fragment { 'lookup_identity':
ssl_content => template('foreman/lookup_identity.conf.erb'),
}
foreman::config::apache::fragment { 'auth_gssapi':
ssl_content => template('foreman/auth_gssapi.conf.erb'),
}
foreman::config::apache::fragment { 'external_auth_api':
ssl_content => template('foreman/external_auth_api.conf.erb'),
}
if $foreman::ipa_manage_sssd {
$sssd = pick(fact('foreman_sssd'), {})
$sssd_services = join(unique(pick($sssd['services'], []) + ['ifp']), ', ')
$sssd_ldap_user_extra_attrs = join(unique(pick($sssd['ldap_user_extra_attrs'], []) + ['email:mail', 'lastname:sn', 'firstname:givenname']), ', ')
$sssd_allowed_uids = join(unique(pick($sssd['allowed_uids'], []) + [$apache::user, 'root']), ', ')
$sssd_user_attributes = join(unique(pick($sssd['user_attributes'], []) + ['+email', '+firstname', '+lastname']), ', ')
$sssd_ifp_extra_attributes = [
"set target[.=~regexp('domain/.*')]/ldap_user_extra_attrs '${sssd_ldap_user_extra_attrs}'",
"set target[.='sssd']/services '${sssd_services}'",
'set target[.=\'ifp\'] \'ifp\'',
"set target[.='ifp']/allowed_uids '${sssd_allowed_uids}'",
"set target[.='ifp']/user_attributes '${sssd_user_attributes}'",
]
$sssd_changes = $sssd_ifp_extra_attributes + ($foreman::ipa_sssd_default_realm ? {
undef => [],
default => ["set target[.='sssd']/default_domain_suffix '${$foreman::ipa_sssd_default_realm}'"],
})
augeas { 'sssd-ifp-extra-attributes':
context => '/files/etc/sssd/sssd.conf',
changes => $sssd_changes,
notify => Service['sssd'],
}
}
foreman::settings_fragment { 'authorize_login_delegation.yaml':
content => template('foreman/settings-external-auth.yaml.erb'),
order => '02',
}
foreman::settings_fragment { 'authorize_login_delegation_api.yaml':
content => template('foreman/settings-external-auth-api.yaml.erb'),
order => '03',
}
}
} else {
$foreman_socket_override = undef
}

And this is where we configure Apache:

if $ipa_authentication {
include apache::mod::authnz_pam
include apache::mod::auth_basic
include apache::mod::intercept_form_submit
include apache::mod::lookup_identity
include apache::mod::auth_gssapi
} elsif $keycloak {
include apache::mod::auth_openidc
# This file is generated by keycloak-httpd-client-install and that manages
# the content. The command would be:
#
# keycloak-httpd-client-install --app-name ${keycloak_app_name} --keycloak-server-url $KEYCLOAK_URL --keycloak-admin-username $KEYCLOAK_USER --keycloak-realm ${keycloak_realm} --keycloak-admin-realm master --keycloak-auth-role root-admin --client-type openidc --client-hostname ${servername} --protected-locations /users/extlogin
#
# If $suburi is used, --location-root should also be passed in
#
# By defining it here we avoid purging it and also tighten the
# permissions so the world can't read its secrets.
# This is functionally equivalent to apache::custom_config without content/source
file { "${apache::confd_dir}/${keycloak_app_name}_oidc_keycloak_${keycloak_realm}.conf":
ensure => file,
owner => 'root',
group => 'root',
mode => '0640',
}
}

As you can see, this part relies on keycloak-http-client-install but I'd prefer to configure Apache's mod_auth_openidc ourselves and I think the parameters you expose can do that. You can do so by passing oidc parameters to the vhost: https://github.com/puppetlabs/puppetlabs-apache/blob/97449e4a0a9f395a2765a3100336070e8298f1dc/manifests/vhost.pp#L1649-L1653 and see https://github.com/puppetlabs/puppetlabs-apache/blob/main/types/oidcsettings.pp for the specific types. You'd probably add them very similar to vhost_https_internal_options.

In #1172 I started some refactoring in the auth area that you might find interesting as well. In general I want to move to EPP templates instead of ERB but haven't fully pursued it.

Lastly, I would really like to see some tests before we merge this, but I can imagine you first want to get the design right to avoid rewriting those all the time.

Comment on lines +13 to +23
<% if scope.lookupvar('foreman::authorize_login_delegation') -%>
# The following values are used for oidc authentication
:authorize_login_delegation: <%= scope.lookupvar("foreman::authorize_login_delegation") %>
:authorize_login_delegation_auth_source_user_autocreate: <%= scope.lookupvar("foreman::authorize_login_delegation_auth_source_user_autocreate") %>
:login_delegation_logout_url: <%= scope.lookupvar("foreman::login_delegation_logout_url") %>
:oidc_jwks_url: <%= scope.lookupvar("foreman::oidc_jwks_url") %>
:oidc_audience: <%= scope.lookupvar("foreman::oidc_audience") %>
:oidc_issuer: <%= scope.lookupvar("foreman::oidc_issuer") %>
:oidc_algorithm: <%= scope.lookupvar("foreman::oidc_algorithm") %>

<% end -%>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer to have this as a separate fragment. Possibly reuse the existing one: https://github.com/theforeman/puppet-foreman/blob/master/templates/settings-external-auth.yaml.erb

@ekohl
Copy link
Member

ekohl commented Jan 13, 2025

As you can see, this part relies on keycloak-http-client-install but I'd prefer to configure Apache's mod_auth_openidc ourselves and I think the parameters you expose can do that. You can do so by passing oidc parameters to the vhost: https://github.com/puppetlabs/puppetlabs-apache/blob/97449e4a0a9f395a2765a3100336070e8298f1dc/manifests/vhost.pp#L1649-L1653 and see https://github.com/puppetlabs/puppetlabs-apache/blob/main/types/oidcsettings.pp for the specific types. You'd probably add them very similar to vhost_https_internal_options.

If it helps, https://github.com/latchset/keycloak-httpd-client-install/blob/master/templates/oidc_httpd.conf is what's used today in keycloak-httpd-client-install. Some of those values (client_https_url, oidc_redirect_uri and protected_locations) are already known in advance from existing variables.

@ekohl
Copy link
Member

ekohl commented Jan 14, 2025

cc @adamruzicka this might interest you

@ekohl
Copy link
Member

ekohl commented Jan 14, 2025

@ekohl
Copy link
Member

ekohl commented Jan 15, 2025

An incomplete version of what I suggested is in #1203.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants