From 4996734dfde56f348fc75762422a9ba1fefdc068 Mon Sep 17 00:00:00 2001 From: Francesco Di Nucci Date: Tue, 26 Nov 2024 14:02:49 +0100 Subject: [PATCH] feat: add ldap user/dn/attribute/filter Allow use of Require ldap-user/ldap-dn/ldap-attribute/ldap-filter in Apache config. Compatible with pre-existing ldap-group settings. --- REFERENCE.md | 72 +++++++++++++ manifests/apache/conf.pp | 12 +++ manifests/apache/vhost.pp | 12 +++ spec/acceptance/class_spec.rb | 196 ++++++++++++++++++++++++++++++++++ templates/apache/conf.epp | 12 ++- templates/apache/ldap.epp | 12 ++- 6 files changed, 312 insertions(+), 4 deletions(-) diff --git a/REFERENCE.md b/REFERENCE.md index f9ce918e..fa661518 100644 --- a/REFERENCE.md +++ b/REFERENCE.md @@ -446,6 +446,10 @@ The following parameters are available in the `puppetboard::apache::conf` class: * [`ldap_bind_authoritative`](#-puppetboard--apache--conf--ldap_bind_authoritative) * [`ldap_require_group`](#-puppetboard--apache--conf--ldap_require_group) * [`ldap_require_group_dn`](#-puppetboard--apache--conf--ldap_require_group_dn) +* [`ldap_require_user`](#-puppetboard--apache--conf--ldap_require_user) +* [`ldap_require_dn`](#-puppetboard--apache--conf--ldap_require_dn) +* [`ldap_require_attribute`](#-puppetboard--apache--conf--ldap_require_attribute) +* [`ldap_require_filter`](#-puppetboard--apache--conf--ldap_require_filter) * [`virtualenv_dir`](#-puppetboard--apache--conf--virtualenv_dir) ##### `wsgi_alias` @@ -552,6 +556,38 @@ LDAP group DN for LDAP group Default value: `undef` +##### `ldap_require_user` + +Data type: `Optional[String]` + +if set, list of uids for Require ldap-user directive + +Default value: `undef` + +##### `ldap_require_dn` + +Data type: `Optional[String]` + +if set, dn to be matched by Require ldap-dn directive + +Default value: `undef` + +##### `ldap_require_attribute` + +Data type: `Optional[String]` + +if set, attributes of LDAP users for Require ldap-attribute directive + +Default value: `undef` + +##### `ldap_require_filter` + +Data type: `Optional[String]` + +if set, LDAP search filter for Require ldap-filter directive + +Default value: `undef` + ##### `virtualenv_dir` Data type: `Stdlib::Absolutepath` @@ -588,6 +624,10 @@ The following parameters are available in the `puppetboard::apache::vhost` class * [`ldap_bind_authoritative`](#-puppetboard--apache--vhost--ldap_bind_authoritative) * [`ldap_require_group`](#-puppetboard--apache--vhost--ldap_require_group) * [`ldap_require_group_dn`](#-puppetboard--apache--vhost--ldap_require_group_dn) +* [`ldap_require_user`](#-puppetboard--apache--vhost--ldap_require_user) +* [`ldap_require_dn`](#-puppetboard--apache--vhost--ldap_require_dn) +* [`ldap_require_attribute`](#-puppetboard--apache--vhost--ldap_require_attribute) +* [`ldap_require_filter`](#-puppetboard--apache--vhost--ldap_require_filter) * [`virtualenv_dir`](#-puppetboard--apache--vhost--virtualenv_dir) * [`custom_apache_parameters`](#-puppetboard--apache--vhost--custom_apache_parameters) @@ -749,6 +789,38 @@ LDAP group DN for LDAP group Default value: `undef` +##### `ldap_require_user` + +Data type: `Optional[String]` + +if set, list of uids for Require ldap-user directive + +Default value: `undef` + +##### `ldap_require_dn` + +Data type: `Optional[String]` + +if set, dn to be matched by Require ldap-dn directive + +Default value: `undef` + +##### `ldap_require_attribute` + +Data type: `Optional[String]` + +if set, attributes of LDAP users for Require ldap-attribute directive + +Default value: `undef` + +##### `ldap_require_filter` + +Data type: `Optional[String]` + +if set, LDAP search filter for Require ldap-filter directive + +Default value: `undef` + ##### `virtualenv_dir` Data type: `Stdlib::Absolutepath` diff --git a/manifests/apache/conf.pp b/manifests/apache/conf.pp index 81802f4e..c5325d07 100644 --- a/manifests/apache/conf.pp +++ b/manifests/apache/conf.pp @@ -13,6 +13,10 @@ # @param ldap_bind_authoritative Determines if other authentication providers are used when a user can be mapped to a DN but the server cannot bind with the credentials # @param ldap_require_group LDAP group to require on login # @param ldap_require_group_dn LDAP group DN for LDAP group +# @param ldap_require_user if set, list of uids for Require ldap-user directive +# @param ldap_require_dn if set, dn to be matched by Require ldap-dn directive +# @param ldap_require_attribute if set, attributes of LDAP users for Require ldap-attribute directive +# @param ldap_require_filter if set, LDAP search filter for Require ldap-filter directive # @param virtualenv_dir Set location where virtualenv will be installed # # @note Make sure you have purge_configs set to false in your apache class! @@ -32,6 +36,10 @@ Optional[String[1]] $ldap_bind_authoritative = undef, Boolean $ldap_require_group = $puppetboard::ldap_require_group, Optional[String[1]] $ldap_require_group_dn = undef, + Optional[String] $ldap_require_user = undef, + Optional[String] $ldap_require_dn = undef, + Optional[String] $ldap_require_attribute = undef, + Optional[String] $ldap_require_filter = undef, Stdlib::Absolutepath $virtualenv_dir = $puppetboard::virtualenv_dir, ) { $wsgi = $facts['os']['family'] ? { @@ -70,6 +78,10 @@ 'ldap_bind_password' => $ldap_bind_password, 'ldap_require_group_dn' => $ldap_require_group_dn, 'ldap_require_group' => $ldap_require_group, + 'ldap_require_user' => $ldap_require_user, + 'ldap_require_dn' => $ldap_require_dn, + 'ldap_require_attribute' => $ldap_require_attribute, + 'ldap_require_filter' => $ldap_require_filter, 'ldap_url' => $ldap_url, 'max_reqs' => $max_reqs, 'threads' => $threads, diff --git a/manifests/apache/vhost.pp b/manifests/apache/vhost.pp index 30672b06..3c5f19e8 100644 --- a/manifests/apache/vhost.pp +++ b/manifests/apache/vhost.pp @@ -20,6 +20,10 @@ # @param ldap_bind_authoritative Determines if other authentication providers are used when a user can be mapped to a DN but the server cannot bind with the credentials # @param ldap_require_group LDAP group to require on login # @param ldap_require_group_dn LDAP group DN for LDAP group +# @param ldap_require_user if set, list of uids for Require ldap-user directive +# @param ldap_require_dn if set, dn to be matched by Require ldap-dn directive +# @param ldap_require_attribute if set, attributes of LDAP users for Require ldap-attribute directive +# @param ldap_require_filter if set, LDAP search filter for Require ldap-filter directive # @param virtualenv_dir Set location where virtualenv will be installed # @param custom_apache_parameters A hash passed to the `apache::vhost` for custom settings class puppetboard::apache::vhost ( @@ -43,6 +47,10 @@ Optional[String[1]] $ldap_bind_authoritative = undef, Boolean $ldap_require_group = $puppetboard::ldap_require_group, Optional[String[1]] $ldap_require_group_dn = undef, + Optional[String] $ldap_require_user = undef, + Optional[String] $ldap_require_dn = undef, + Optional[String] $ldap_require_attribute = undef, + Optional[String] $ldap_require_filter = undef, Stdlib::Absolutepath $virtualenv_dir = $puppetboard::virtualenv_dir, Hash $custom_apache_parameters = {}, ) { @@ -94,6 +102,10 @@ 'ldap_bind_password' => $ldap_bind_password, 'ldap_require_group_dn' => $ldap_require_group_dn, 'ldap_require_group' => $ldap_require_group, + 'ldap_require_user' => $ldap_require_user, + 'ldap_require_dn' => $ldap_require_dn, + 'ldap_require_attribute' => $ldap_require_attribute, + 'ldap_require_filter' => $ldap_require_filter, 'ldap_url' => $ldap_url, }, ), diff --git a/spec/acceptance/class_spec.rb b/spec/acceptance/class_spec.rb index 458dc0d7..c41b67e3 100644 --- a/spec/acceptance/class_spec.rb +++ b/spec/acceptance/class_spec.rb @@ -219,4 +219,200 @@ class { 'puppetdb': it { is_expected.to contain "PUPPETDB_CERT = '/var/lib/puppet/ssl/certs/test.networkninjas.net.pem'" } end end + + context 'AUTH ldap-user' do + it 'works with no errors' do + pp = <<-EOS + # Configure Apache on this server + class { 'apache': } + class { 'apache::mod::authnz_ldap': } + -> class { 'puppetboard': + manage_virtualenv => true, + manage_git => true, + puppetdb_host => 'puppet.example.com', + puppetdb_port => 8081, + puppetdb_key => "/var/lib/puppet/ssl/private_keys/test.networkninjas.net.pem", + puppetdb_ssl_verify => true, + puppetdb_cert => "/var/lib/puppet/ssl/certs/test.networkninjas.net.pem", + require => Class['puppetdb'], + secret_key => 'this_should_be_a_long_secret_string', + } + class { 'puppetboard::apache::conf': + enable_ldap_auth => true, + ldap_bind_dn => 'cn=user,dc=puppet,dc=example,dc=com', + ldap_bind_password => 'password', + ldap_url => 'ldap://puppet.example.com', + ldap_require_user => 'admin1uid admin2uid', + } + # Configure PuppetDB + class { 'puppetdb': + disable_ssl => true, + manage_firewall => false, + } + EOS + + # Run it twice and test for idempotency + apply_manifest(pp, catch_failures: true) + apply_manifest(pp, catch_failures: true) + end + + describe file(apache_conf_file) do + it { is_expected.to contain 'AuthBasicProvider ldap' } + it { is_expected.to contain 'AuthLDAPBindDN "cn=user,dc=puppet,dc=example,dc=com"' } + it { is_expected.to contain 'AuthLDAPURL "ldap://puppet.example.com"' } + it { is_expected.to contain 'Require ldap-user admin1uid admin2uid' } + end + + describe file('/srv/puppetboard/puppetboard/settings.py') do + it { is_expected.to contain "PUPPETDB_KEY = '/var/lib/puppet/ssl/private_keys/test.networkninjas.net.pem'" } + it { is_expected.to contain "PUPPETDB_CERT = '/var/lib/puppet/ssl/certs/test.networkninjas.net.pem'" } + end + end + + context 'AUTH ldap-dn' do + it 'works with no errors' do + pp = <<-EOS + # Configure Apache on this server + class { 'apache': } + class { 'apache::mod::authnz_ldap': } + -> class { 'puppetboard': + manage_virtualenv => true, + manage_git => true, + puppetdb_host => 'puppet.example.com', + puppetdb_port => 8081, + puppetdb_key => "/var/lib/puppet/ssl/private_keys/test.networkninjas.net.pem", + puppetdb_ssl_verify => true, + puppetdb_cert => "/var/lib/puppet/ssl/certs/test.networkninjas.net.pem", + require => Class['puppetdb'], + secret_key => 'this_should_be_a_long_secret_string', + } + class { 'puppetboard::apache::conf': + enable_ldap_auth => true, + ldap_bind_dn => 'cn=user,dc=puppet,dc=example,dc=com', + ldap_bind_password => 'password', + ldap_url => 'ldap://puppet.example.com', + ldap_require_dn => 'cn=admin,o=example', + } + # Configure PuppetDB + class { 'puppetdb': + disable_ssl => true, + manage_firewall => false, + } + EOS + + # Run it twice and test for idempotency + apply_manifest(pp, catch_failures: true) + apply_manifest(pp, catch_failures: true) + end + + describe file(apache_conf_file) do + it { is_expected.to contain 'AuthBasicProvider ldap' } + it { is_expected.to contain 'AuthLDAPBindDN "cn=user,dc=puppet,dc=example,dc=com"' } + it { is_expected.to contain 'AuthLDAPURL "ldap://puppet.example.com"' } + it { is_expected.to contain 'Require ldap-dn cn=admin,o=example' } + end + + describe file('/srv/puppetboard/puppetboard/settings.py') do + it { is_expected.to contain "PUPPETDB_KEY = '/var/lib/puppet/ssl/private_keys/test.networkninjas.net.pem'" } + it { is_expected.to contain "PUPPETDB_CERT = '/var/lib/puppet/ssl/certs/test.networkninjas.net.pem'" } + end + end + + context 'AUTH ldap-attribute' do + it 'works with no errors' do + pp = <<-EOS + # Configure Apache on this server + class { 'apache': } + class { 'apache::mod::authnz_ldap': } + -> class { 'puppetboard': + manage_virtualenv => true, + manage_git => true, + puppetdb_host => 'puppet.example.com', + puppetdb_port => 8081, + puppetdb_key => "/var/lib/puppet/ssl/private_keys/test.networkninjas.net.pem", + puppetdb_ssl_verify => true, + puppetdb_cert => "/var/lib/puppet/ssl/certs/test.networkninjas.net.pem", + require => Class['puppetdb'], + secret_key => 'this_should_be_a_long_secret_string', + } + class { 'puppetboard::apache::conf': + enable_ldap_auth => true, + ldap_bind_dn => 'cn=user,dc=puppet,dc=example,dc=com', + ldap_bind_password => 'password', + ldap_url => 'ldap://puppet.example.com', + ldap_require_attribute => 'role=admin status=active', + } + # Configure PuppetDB + class { 'puppetdb': + disable_ssl => true, + manage_firewall => false, + } + EOS + + # Run it twice and test for idempotency + apply_manifest(pp, catch_failures: true) + apply_manifest(pp, catch_failures: true) + end + + describe file(apache_conf_file) do + it { is_expected.to contain 'AuthBasicProvider ldap' } + it { is_expected.to contain 'AuthLDAPBindDN "cn=user,dc=puppet,dc=example,dc=com"' } + it { is_expected.to contain 'AuthLDAPURL "ldap://puppet.example.com"' } + it { is_expected.to contain 'Require ldap-attribute role=admin status=active' } + end + + describe file('/srv/puppetboard/puppetboard/settings.py') do + it { is_expected.to contain "PUPPETDB_KEY = '/var/lib/puppet/ssl/private_keys/test.networkninjas.net.pem'" } + it { is_expected.to contain "PUPPETDB_CERT = '/var/lib/puppet/ssl/certs/test.networkninjas.net.pem'" } + end + end + + context 'AUTH ldap-filter' do + it 'works with no errors' do + pp = <<-EOS + # Configure Apache on this server + class { 'apache': } + class { 'apache::mod::authnz_ldap': } + -> class { 'puppetboard': + manage_virtualenv => true, + manage_git => true, + puppetdb_host => 'puppet.example.com', + puppetdb_port => 8081, + puppetdb_key => "/var/lib/puppet/ssl/private_keys/test.networkninjas.net.pem", + puppetdb_ssl_verify => true, + puppetdb_cert => "/var/lib/puppet/ssl/certs/test.networkninjas.net.pem", + require => Class['puppetdb'], + secret_key => 'this_should_be_a_long_secret_string', + } + class { 'puppetboard::apache::conf': + enable_ldap_auth => true, + ldap_bind_dn => 'cn=user,dc=puppet,dc=example,dc=com', + ldap_bind_password => 'password', + ldap_url => 'ldap://puppet.example.com', + ldap_require_filter => '&(role=sysadmin)(memberOf=g:puppetboard::ag:*)', + } + # Configure PuppetDB + class { 'puppetdb': + disable_ssl => true, + manage_firewall => false, + } + EOS + + # Run it twice and test for idempotency + apply_manifest(pp, catch_failures: true) + apply_manifest(pp, catch_failures: true) + end + + describe file(apache_conf_file) do + it { is_expected.to contain 'AuthBasicProvider ldap' } + it { is_expected.to contain 'AuthLDAPBindDN "cn=user,dc=puppet,dc=example,dc=com"' } + it { is_expected.to contain 'AuthLDAPURL "ldap://puppet.example.com"' } + it { is_expected.to contain 'Require ldap-filter &(role=sysadmin)(memberOf=g:puppetboard::ag:*)' } + end + + describe file('/srv/puppetboard/puppetboard/settings.py') do + it { is_expected.to contain "PUPPETDB_KEY = '/var/lib/puppet/ssl/private_keys/test.networkninjas.net.pem'" } + it { is_expected.to contain "PUPPETDB_CERT = '/var/lib/puppet/ssl/certs/test.networkninjas.net.pem'" } + end + end end diff --git a/templates/apache/conf.epp b/templates/apache/conf.epp index 9fcb8a7a..5178f637 100644 --- a/templates/apache/conf.epp +++ b/templates/apache/conf.epp @@ -32,9 +32,17 @@ WSGIScriptAlias <%= $wsgi_alias %> <%= $docroot %>/wsgi.py <%- if $ldap_bind_authoritative { %> AuthLDAPBindAuthoritative <%= $ldap_bind_authoritative %> <%- } %> - <%- if $ldap_require_group { %> + <%- if $ldap_require_user { %> + Require ldap-user <%= $ldap_require_user %> + <%- } elsif $ldap_require_group { %> Require ldap-group <%= $ldap_require_group_dn %> - <% } else { %> + <%- } elsif $ldap_require_dn { %> + Require ldap-dn <%= $ldap_require_dn %> + <%- } elsif $ldap_require_attribute { %> + Require ldap-attribute <%= $ldap_require_attribute %> + <%- } elsif $ldap_require_filter { %> + Require ldap-filter <%= $ldap_require_filter %> + <% } else { %> Require valid-user <% } %> diff --git a/templates/apache/ldap.epp b/templates/apache/ldap.epp index 31fad32a..0afb1997 100644 --- a/templates/apache/ldap.epp +++ b/templates/apache/ldap.epp @@ -17,9 +17,17 @@ <%- if $ldap_bind_authoritative { %> AuthLDAPBindAuthoritative <%= $ldap_bind_authoritative %> <%- } %> - <%- if $ldap_require_group { %> + <%- if $ldap_require_user { %> + Require ldap-user <%= $ldap_require_user %> + <%- } elsif $ldap_require_group { %> Require ldap-group <%= $ldap_require_group_dn %> - <% } else { %> + <%- } elsif $ldap_require_dn { %> + Require ldap-dn <%= $ldap_require_dn %> + <%- } elsif $ldap_require_attribute { %> + Require ldap-attribute <%= $ldap_require_attribute %> + <%- } elsif $ldap_require_filter { %> + Require ldap-filter <%= $ldap_require_filter %> + <% } else { %> Require valid-user <% } %>