diff --git a/.fixtures-latest.yml b/.fixtures-latest.yml index 8623da4a..aa8fb6f6 100644 --- a/.fixtures-latest.yml +++ b/.fixtures-latest.yml @@ -14,9 +14,13 @@ fixtures: repo: https://github.com/voxpupuli/puppet-archive.git systemd: repo: https://github.com/camptocamp/puppet-systemd.git + ref: "8f68b0dcf3bbbafc60c025879a28004fc9815aab" yumrepo_core: repo: https://github.com/puppetlabs/puppetlabs-yumrepo_core.git puppet_version: ">= 6.0.0" + augeas_core: + repo: https://github.com/puppetlabs/puppetlabs-augeas_core.git + puppet_version: ">= 6.0.0" apt: repo: https://github.com/puppetlabs/puppetlabs-apt.git concat: diff --git a/.fixtures.yml b/.fixtures.yml index d94a4d66..2f948b44 100644 --- a/.fixtures.yml +++ b/.fixtures.yml @@ -21,9 +21,14 @@ fixtures: systemd: repo: https://github.com/camptocamp/puppet-systemd.git ref: 0.4.0 + augeas_core: + repo: https://github.com/puppetlabs/puppetlabs-augeas_core.git + puppet_version: ">= 6.0.0" + ref: 1.1.1 yumrepo_core: repo: https://github.com/puppetlabs/puppetlabs-yumrepo_core.git puppet_version: ">= 6.0.0" + ref: 1.0.7 apt: repo: https://github.com/puppetlabs/puppetlabs-apt.git concat: diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 2de08d73..e94b91c8 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -68,6 +68,8 @@ jobs: - "12.0.1" keycloak_full: - "no" + keycloak_domain_mode_cluster: + - "no" include: - set: "centos-7" puppet: "puppet5" @@ -85,6 +87,22 @@ jobs: puppet: "puppet6" keycloak_version: "12.0.1" keycloak_full: "yes" + - set: "centos-7-domain-mode-cluster" + puppet: puppet5 + keycloak_version: 8.0.1 + keycloak_domain_mode_cluster: 'yes' + - set: "centos-7-domain-mode-cluster" + puppet: puppet5 + keycloak_version: 12.0.1 + keycloak_domain_mode_cluster: 'yes' + - set: "centos-7-domain-mode-cluster" + puppet: puppet6 + keycloak_version: 8.0.1 + keycloak_domain_mode_cluster: 'yes' + - set: "centos-7-domain-mode-cluster" + puppet: puppet6 + keycloak_version: 12.0.1 + keycloak_domain_mode_cluster: 'yes' env: BUNDLE_WITHOUT: development:release BEAKER_debug: true @@ -115,3 +133,4 @@ jobs: BEAKER_set: ${{ matrix.set }} BEAKER_keycloak_version: ${{ matrix.keycloak_version }} BEAKER_keycloak_full: ${{ matrix.keycloak_full }} + BEAKER_keycloak_domain_mode_cluster: ${{ matrix.keycloak_domain_mode_cluster }} diff --git a/.gitignore b/.gitignore index 2767022c..f0c1c0e6 100644 --- a/.gitignore +++ b/.gitignore @@ -25,3 +25,5 @@ .project .envrc /inventory.yaml +/vagrant/.vagrant/ +/vagrant/*.log diff --git a/README.md b/README.md index 1016f06d..17f27e6f 100644 --- a/README.md +++ b/README.md @@ -158,6 +158,44 @@ apache::vhost { 'idp.example.com': ssl_key => '/etc/pki/tls/private/idp.example.com.key', } ``` +Setup a domain master. (This needs a shared database, here '1.2.3.4'). + +```puppet +class { '::keycloak': + operating_mode => 'domain', + role => 'master', + wildfly_user => 'wildfly, + wildfly_user_password => 'changeme, + manage_datasource => false, + datasource_driver => 'postgresql', + datasource_host => '1.2.3.4, + datasource_dbname => 'keycloak, + datasource_username => 'keycloak, + datasource_password => 'changeme, + admin_user => 'admin, + admin_user_password => 'changeme, +} +``` + +Setup a domain slave. (This needs a shared database, here '1.2.3.4'). + +```puppet +class { '::keycloak': + operating_mode => 'domain', + role => 'slave', + wildfly_user => 'wildfly, + wildfly_user_password => 'changeme, + manage_datasource => false, + datasource_driver => 'postgresql', + datasource_host => '1.2.3.4, + datasource_dbname => 'keycloak, + datasource_username => 'keycloak, + datasource_password => 'changeme, + admin_user => 'admin, + admin_user_password => 'changeme, +} +``` +**NOTE:** The wilfdly user and password need to match those in domain master. These are required for authentication in a cluster. Setup a host for theme development so that theme changes don't require a service restart, not recommended for production. diff --git a/manifests/config.pp b/manifests/config.pp index 6c5dca37..a9332123 100644 --- a/manifests/config.pp +++ b/manifests/config.pp @@ -21,16 +21,6 @@ show_diff => false, } - $_add_user_keycloak_cmd = "${keycloak::install_base}/bin/add-user-keycloak.sh" - $_add_user_keycloak_args = "--user ${keycloak::admin_user} --password ${keycloak::admin_user_password} --realm master" - $_add_user_keycloak_state = "${keycloak::install_base}/.create-keycloak-admin-${keycloak::datasource_driver}" - exec { 'create-keycloak-admin': - command => "${_add_user_keycloak_cmd} ${_add_user_keycloak_args} && touch ${_add_user_keycloak_state}", - creates => $_add_user_keycloak_state, - notify => Class['keycloak::service'], - user => $keycloak::user, - } - file { "${keycloak::install_base}/tmp": ensure => 'directory', owner => $keycloak::user, @@ -38,20 +28,39 @@ mode => '0755', } - file { "${keycloak::install_base}/standalone/configuration": - ensure => 'directory', - owner => $keycloak::user, - group => $keycloak::group, - mode => '0750', + $_add_user_keycloak_cmd = "${keycloak::install_base}/bin/add-user-keycloak.sh" + $_add_user_keycloak_state = "${keycloak::install_base}/.create-keycloak-admin-${keycloak::datasource_driver}" + $_config_cli_content = template('keycloak/config.cli.erb') + + if $::keycloak::operating_mode != 'domain' { + $_add_user_keycloak_args = "--user ${keycloak::admin_user} --password ${keycloak::admin_user_password} --realm master" + $_subdir = 'standalone' + $_java_opts_path = "${keycloak::install_base}/bin/standalone.conf" + } else { + $_server_conf_dir = "${keycloak::install_base}/domain/servers/${keycloak::server_name}/configuration" + $_add_user_keycloak_args = "--user ${keycloak::admin_user} --password ${keycloak::admin_user_password} --realm master --sc ${_server_conf_dir}/" # lint:ignore:140chars + $_subdir = 'domain' + $_java_opts_path = "${keycloak::install_base}/bin/domain.conf" + + $_dirs = [ + "${keycloak::install_base}/domain/servers", + "${keycloak::install_base}/domain/servers/${keycloak::server_name}", + "${keycloak::install_base}/domain/servers/${keycloak::server_name}/configuration", + ] + + file { $_dirs: + ensure => 'directory', + owner => $keycloak::user, + group => $keycloak::group, + mode => '0755', + } } - file { "${keycloak::install_base}/standalone/configuration/profile.properties": - ensure => 'file', - owner => $keycloak::user, - group => $keycloak::group, - content => template('keycloak/profile.properties.erb'), - mode => '0644', + exec { 'create-keycloak-admin': + command => "${_add_user_keycloak_cmd} ${_add_user_keycloak_args} && touch ${_add_user_keycloak_state}", + creates => $_add_user_keycloak_state, notify => Class['keycloak::service'], + user => $keycloak::user, } concat { "${keycloak::install_base}/config.cli": @@ -64,7 +73,7 @@ concat::fragment { 'config.cli-keycloak': target => "${keycloak::install_base}/config.cli", - content => template('keycloak/config.cli.erb'), + content => $_config_cli_content, order => '00', } @@ -105,11 +114,163 @@ } else { $_java_opts = $java_opts } - file_line { 'standalone.conf-JAVA_OPTS': + file_line { 'keycloak-JAVA_OPTS': ensure => $java_opts_ensure, - path => "${keycloak::install_base}/bin/standalone.conf", + path => $_java_opts_path, line => "JAVA_OPTS=\"${_java_opts}\"", match => '^JAVA_OPTS=', notify => Class['keycloak::service'], } + + file { "${keycloak::install_base}/${_subdir}/configuration": + ensure => 'directory', + owner => $keycloak::user, + group => $keycloak::group, + mode => '0750', + } + + file { "${keycloak::install_base}/${_subdir}/configuration/profile.properties": + ensure => 'file', + owner => $keycloak::user, + group => $keycloak::group, + content => template('keycloak/profile.properties.erb'), + mode => '0644', + notify => Class['keycloak::service'], + } + + if $::keycloak::operating_mode == 'domain' { + $_add_user_wildfly_cmd = "${keycloak::install_base}/bin/add-user.sh" + $_add_user_wildfly_args = "--user ${keycloak::wildfly_user} --password ${keycloak::wildfly_user_password} -e -s" + $_add_user_wildfly_state = "${::keycloak::install_base}/.create-wildfly-user" + + exec { 'create-wildfly-user': + command => "${_add_user_wildfly_cmd} ${_add_user_wildfly_args} && touch ${_add_user_wildfly_state}", + creates => $_add_user_wildfly_state, + notify => Class['keycloak::service'], + } + + if $keycloak::role == 'master' { + + # Remove load balancer group + # Rename the server + # Set port offset to zero to run server on port 8080 + augeas { 'ensure-servername': + incl => "${keycloak::install_base}/domain/configuration/host-master.xml", + context => "/files${keycloak::install_base}/domain/configuration/host-master.xml/host/servers", + load_path => '/opt/puppetlabs/puppet/share/augeas/lenses/dist', + lens => 'Xml.lns', + changes => [ + 'rm server[1]', + 'rm server', + "set server/#attribute/name ${keycloak::server_name}", + 'set server/#attribute/group auth-server-group', + 'set server/#attribute/auto-start true', + 'set server/socket-bindings/#attribute/port-offset 0', + ], + notify => Class['keycloak::service'], + } + + # Set up interface names and defaults in host-master.xml + augeas { 'ensure-interface-names-defaults-master': + incl => "${keycloak::install_base}/domain/configuration/host-master.xml", + context => "/files${keycloak::install_base}/domain/configuration/host-master.xml/host/interfaces", + load_path => '/opt/puppetlabs/puppet/share/augeas/lenses/dist', + lens => 'Xml.lns', + changes => [ + # lint:ignore:single_quote_string_with_variables + 'set interface[1]/#attribute/name management', + 'set interface[1]/inet-address/#attribute/value ${jboss.bind.address.management:127.0.0.1}', + 'set interface[2]/#attribute/name private', + 'set interface[2]/inet-address/#attribute/value ${jboss.bind.address.private:127.0.0.1}', + 'set interface[3]/#attribute/name public', + 'set interface[3]/inet-address/#attribute/value ${jboss.bind.address:127.0.0.1}', + # lint:endignore + ], + notify => Class['keycloak::service'], + } + + # Assing management interfaces to logical interfaces + augeas { 'assign-management-interaces-master': + incl => "${keycloak::install_base}/domain/configuration/host-master.xml", + context => "/files${keycloak::install_base}/domain/configuration/host-master.xml/host/management/management-interfaces", + load_path => '/opt/puppetlabs/puppet/share/augeas/lenses/dist', + lens => 'Xml.lns', + changes => [ + 'set native-interface/socket/#attribute/interface management', + 'set http-interface/socket/#attribute/interface private', + ], + notify => Class['keycloak::service'], + } + } else { + # Rename the server + # Set port offset to zero, to run server in port 8080 + augeas { 'ensure-servername': + incl => "${keycloak::install_base}/domain/configuration/host-slave.xml", + context => "/files${keycloak::install_base}/domain/configuration/host-slave.xml/host/servers", + load_path => '/opt/puppetlabs/puppet/share/augeas/lenses/dist', + lens => 'Xml.lns', + changes => [ + "set server/#attribute/name ${keycloak::server_name}", + 'set server/socket-bindings/#attribute/port-offset 0' + ], + notify => Class['keycloak::service'], + } + + # Set username for authentication to master + augeas { 'ensure-username': + incl => "${keycloak::install_base}/domain/configuration/host-slave.xml", + context => "/files${keycloak::install_base}/domain/configuration/host-slave.xml/host/domain-controller/remote", + load_path => '/opt/puppetlabs/puppet/share/augeas/lenses/dist', + lens => 'Xml.lns', + changes => [ + "set #attribute/username ${keycloak::wildfly_user}" + ], + notify => Class['keycloak::service'], + } + + # Set secret for authentication to master + augeas { 'ensure-secret': + incl => "${keycloak::install_base}/domain/configuration/host-slave.xml", + context => "/files${keycloak::install_base}/domain/configuration/host-slave.xml/host/management/security-realms/security-realm[1]/server-identities/secret", # lint:ignore:140chars + load_path => '/opt/puppetlabs/puppet/share/augeas/lenses/dist', + lens => 'Xml.lns', + changes => [ + "set #attribute/value ${keycloak::wildfly_user_password_base64}" + ], + notify => Class['keycloak::service'], + } + + # Set up interface names and default in host-slave.xml + augeas { 'ensure-interface-names-defaults-slave': + incl => "${keycloak::install_base}/domain/configuration/host-slave.xml", + context => "/files${keycloak::install_base}/domain/configuration/host-slave.xml/host/interfaces", + load_path => '/opt/puppetlabs/puppet/share/augeas/lenses/dist', + lens => 'Xml.lns', + changes => [ + # lint:ignore:single_quote_string_with_variables + 'set interface[1]/#attribute/name management', + 'set interface[1]/inet-address/#attribute/value ${jboss.bind.address.management:127.0.0.1}', + 'set interface[2]/#attribute/name private', + 'set interface[2]/inet-address/#attribute/value ${jboss.bind.address.private:127.0.0.1}', + 'set interface[3]/#attribute/name public', + 'set interface[3]/inet-address/#attribute/value ${jboss.bind.address:127.0.0.1}', + # lint:endignore + ], + notify => Class['keycloak::service'], + } + + # Assing management interfaces to logical interfaces + augeas { 'assign-management-interaces-slave': + incl => "${keycloak::install_base}/domain/configuration/host-slave.xml", + context => "/files${keycloak::install_base}/domain/configuration/host-slave.xml/host/management/management-interfaces", + load_path => '/opt/puppetlabs/puppet/share/augeas/lenses/dist', + lens => 'Xml.lns', + changes => [ + 'set native-interface/socket/#attribute/interface management', + 'set http-interface/socket/#attribute/interface private', + ], + notify => Class['keycloak::service'], + } + } + } } diff --git a/manifests/init.pp b/manifests/init.pp index 8dbef44f..2629c246 100644 --- a/manifests/init.pp +++ b/manifests/init.pp @@ -34,6 +34,9 @@ # @param service_bind_address # Bind address for Keycloak service. # Default is '0.0.0.0'. +# @param management_bind_address +# Bind address for Keycloak management. +# Default is '0.0.0.0'. # @param java_opts # Sets additional options to Java virtual machine environment variable. # @param java_opts_append @@ -65,6 +68,10 @@ # @param admin_user_password # Keycloak administrative user password. # Default is `changeme`. +# @param wildfly_user +# Wildfly user. Required for domain mode. +# @param wildfly_user_password +# Wildfly user password. Required for domain mode. # @param manage_datasource # Boolean that determines if configured datasource will be managed. # Default is `true`. @@ -211,13 +218,15 @@ # @param enable_jdbc_ping # Use JDBC_PING to discover the nodes and manage the replication of data # More info: http://jgroups.org/manual/#_jdbc_ping -# Only applies when `operating_mode` is `clustered` +# Only applies when `operating_mode` is either `clustered` or `domain` # JDBC_PING uses port 7600 to ensure cluster members are discoverable by each other # This module does not manage firewall changes # @param jboss_bind_public_address # JBoss bind public IP address # @param jboss_bind_private_address # JBoss bind private IP address +# @param role +# Role when operating mode is domain. # @param user_cache # Boolean that determines if userCache is enabled # @param tech_preview_features @@ -232,7 +241,10 @@ # Custom configuration content to be added to config.cli # @param custom_config_source # Custom configuration source file to be added to config.cli -# +# @param master_address +# IP address of the master in domain mode +# @param server_name +# Server name in domain mode. Defaults to hostname. class keycloak ( Boolean $manage_install = true, String $version = '8.0.1', @@ -245,6 +257,7 @@ Boolean $service_hasstatus = true, Boolean $service_hasrestart = true, Stdlib::IP::Address $service_bind_address = '0.0.0.0', + Stdlib::IP::Address $management_bind_address = '0.0.0.0', Optional[Variant[String, Array]] $java_opts = undef, Boolean $java_opts_append = true, Optional[String] $service_extra_opts = undef, @@ -257,6 +270,8 @@ Optional[Integer] $group_gid = undef, String $admin_user = 'admin', String $admin_user_password = 'changeme', + Optional[String] $wildfly_user = undef, + Optional[String] $wildfly_user_password = undef, Boolean $manage_datasource = true, Enum['h2', 'mysql', 'oracle', 'postgresql'] $datasource_driver = 'h2', Optional[String] $datasource_host = undef, @@ -315,10 +330,11 @@ Array $sssd_ifp_user_attributes = [], Boolean $restart_sssd = true, Optional[Stdlib::Absolutepath] $service_environment_file = undef, - Enum['standalone', 'clustered'] $operating_mode = 'standalone', + Enum['standalone', 'clustered', 'domain'] $operating_mode = 'standalone', Boolean $enable_jdbc_ping = false, Stdlib::IP::Address $jboss_bind_public_address = $facts['networking']['ip'], Stdlib::IP::Address $jboss_bind_private_address = $facts['networking']['ip'], + Optional[Enum['master', 'slave']] $role = undef, Boolean $user_cache = true, Array $tech_preview_features = [], Boolean $auto_deploy_exploded = false, @@ -326,12 +342,40 @@ Hash $spi_deployments = {}, Optional[String] $custom_config_content = undef, Optional[Variant[String, Array]] $custom_config_source = undef, + Optional[Stdlib::Host] $master_address = undef, + String $server_name = $facts['hostname'], ) { if ! ($facts['os']['family'] in ['RedHat','Debian']) { fail("Unsupported osfamily: ${facts['os']['family']}, module ${module_name} only support osfamilies Debian and Redhat") } + if $role and ! ($operating_mode == 'domain') { + fail('Role can only be specified in domain operating mode') + } + + if $operating_mode == 'domain' { + unless $role { + fail("Role not specified: in domain mode role needs to be specified. This needs to be either 'master' or 'slave'.") + } + unless $wildfly_user { + fail('Wildfly user not specified: in domain mode Wildfly user needs to be specified.') + } + unless $wildfly_user_password { + fail('Wildfly user password not specified: in domain, mode Wildfly user password needs to be specified.') + } + + if $role == 'slave' and ! $master_address { + fail('Master address not specified: in domain mode, master address needs to be specified for a slave.') + } + + if $datasource_driver == 'h2' { + fail("Invalid datasource driver for domain mode: ${datasource_driver}") + } + + $wildfly_user_password_base64 = strip(base64('encode', $wildfly_user_password)) + } + if versioncmp($version, '12.0.0') >= 0 { $download_url = pick($package_url, "https://github.com/keycloak/keycloak/releases/download/${version}/keycloak-${version}.tar.gz") } else { diff --git a/manifests/truststore/host.pp b/manifests/truststore/host.pp index 8ad68b1c..a5e20e3a 100644 --- a/manifests/truststore/host.pp +++ b/manifests/truststore/host.pp @@ -17,10 +17,16 @@ include keycloak + if $keycloak::operating_mode == 'domain' { + $_path = "${keycloak::install_base}/domain/configuration/truststore.jks" + } else { + $_path = "${keycloak::install_base}/standalone/configuration/truststore.jks" + } + java_ks { $name: ensure => $ensure, certificate => $certificate, - target => "${keycloak::install_base}/standalone/configuration/truststore.jks", + target => $_path, password => $keycloak::truststore_password, trustcacerts => true, notify => Class['keycloak::service'], diff --git a/metadata.json b/metadata.json index 825b58f1..800f2228 100644 --- a/metadata.json +++ b/metadata.json @@ -28,6 +28,14 @@ "name": "puppetlabs/java_ks", "version_requirement": ">= 1.0.0 <4.0.0" }, + { + "name": "puppetlabs/augeas_core", + "version_requirement": ">= 1.0.0 <4.0.0" + }, + { + "name": "puppetlabs/yumrepo_core", + "version_requirement": ">= 1.0.0 <2.0.0" + }, { "name": "puppet/archive", "version_requirement": ">= 0.5.1 <5.0.0" diff --git a/spec/acceptance/1_class_spec.rb b/spec/acceptance/1_class_spec.rb index 1106fe32..961081c8 100644 --- a/spec/acceptance/1_class_spec.rb +++ b/spec/acceptance/1_class_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper_acceptance' -describe 'keycloak class:' do +describe 'keycloak class:', unless: RSpec.configuration.keycloak_domain_mode_cluster do context 'default parameters' do it 'runs successfully' do pp = <<-EOS diff --git a/spec/acceptance/1_domain_mode_cluster_spec.rb b/spec/acceptance/1_domain_mode_cluster_spec.rb new file mode 100644 index 00000000..60bb589a --- /dev/null +++ b/spec/acceptance/1_domain_mode_cluster_spec.rb @@ -0,0 +1,136 @@ +require 'spec_helper_acceptance' + +# This check needs to be here or Beaker will try to run find_only_one on +# non-domain-mode tests and fail miserably. +if RSpec.configuration.keycloak_domain_mode_cluster + describe 'keycloak domain mode cluster' do + domain_master = find_only_one('domain_master') + domain_slave = find_only_one('domain_slave') + db = find_only_one('db') + + context 'new cluster' do + it 'launches' do + db_pp = <<-EOS + class { '::postgresql::globals': + manage_package_repo => true, + version => '9.6', + } + + class { '::postgresql::server': + listen_addresses => '*', + require => Class['::postgresql::globals'] + } + + ::postgresql::server::role { 'keycloak': + password_hash => postgresql_password('keycloak', 'keycloak'), + connection_limit => 300, + require => Class['::postgresql::server'] + } + + ::postgresql::server::database_grant { 'Grant all to keycloak': + privilege => 'ALL', + db => 'keycloak', + role => 'keycloak', + } + + ::postgresql::server::db { 'keycloak': + user => 'keycloak', + password => postgresql_password('keycloak', 'keycloak'), + } + + postgresql::server::pg_hba_rule { 'Allow Keycloak instances network access to the database': + description => 'Open up PostgreSQL for access from anywhere', + type => 'host', + database => 'keycloak', + user => 'keycloak', + address => '0.0.0.0/0', + auth_method => 'md5', + require => Class['::postgresql::server'] + } + EOS + + master_pp = <<-EOS + class { '::keycloak': + operating_mode => 'domain', + role => 'master', + management_bind_address => $::ipaddress, + enable_jdbc_ping => true, + wildfly_user => 'wildfly', + wildfly_user_password => 'wildfly', + manage_install => true, + manage_datasource => false, + version => '10.0.1', + datasource_driver => 'postgresql', + datasource_host => 'centos-7-db', + datasource_port => 5432, + datasource_dbname => 'keycloak', + datasource_username => 'keycloak', + datasource_password => 'keycloak', + admin_user => 'admin', + admin_user_password => 'changeme', + service_bind_address => '0.0.0.0', + proxy_https => false, + } + EOS + + slave_pp = <<-EOS + class { '::keycloak': + operating_mode => 'domain', + role => 'slave', + enable_jdbc_ping => true, + management_bind_address => $::ipaddress, + wildfly_user => 'wildfly', + wildfly_user_password => 'wildfly', + master_address => 'centos-7-master', + manage_install => true, + manage_datasource => false, + version => '10.0.1', + datasource_driver => 'postgresql', + datasource_host => 'centos-7-db', + datasource_port => 5432, + datasource_dbname => 'keycloak', + datasource_username => 'keycloak', + datasource_password => 'keycloak', + admin_user => 'admin', + admin_user_password => 'changeme', + service_bind_address => '0.0.0.0', + proxy_https => false, + } + EOS + + apply_manifest_on(db, db_pp, catch_failures: true) + apply_manifest_on(domain_master, master_pp, catch_failures: true) + apply_manifest_on(domain_master, master_pp, catch_changes: true) + apply_manifest_on(domain_slave, slave_pp, catch_failures: true) + apply_manifest_on(domain_slave, slave_pp, catch_changes: true) + end + + describe service('keycloak'), node: domain_master do + it { is_expected.to be_enabled } + it { is_expected.to be_running } + end + + describe service('keycloak'), node: domain_slave do + it { is_expected.to be_enabled } + it { is_expected.to be_running } + end + + it 'data replicates from master to slave' do + on domain_master, '/opt/keycloak/bin/kcadm-wrapper.sh create roles -r master -s name=testrole' + on domain_slave, '/opt/keycloak/bin/kcadm-wrapper.sh get roles/testrole -r master' do + data = JSON.parse(stdout) + expect(data['name']).to eq('testrole') + end + end + + it 'data replicates from slave to master' do + on domain_slave, '/opt/keycloak/bin/kcadm-wrapper.sh delete roles/testrole -r master' + on domain_master, '/opt/keycloak/bin/kcadm-wrapper.sh get roles -r master' do + data = JSON.parse(stdout) + match = data.select { |role| role['name'] == 'testrole' } + expect(match).to be_empty + end + end + end + end +end diff --git a/spec/acceptance/nodesets/centos-7-domain-mode-cluster.yml b/spec/acceptance/nodesets/centos-7-domain-mode-cluster.yml new file mode 100644 index 00000000..2ec51e52 --- /dev/null +++ b/spec/acceptance/nodesets/centos-7-domain-mode-cluster.yml @@ -0,0 +1,48 @@ +HOSTS: + centos-7-master: + roles: + - agent + - default + - domain_master + platform: el-7-x86_64 + hypervisor: docker + image: centos:7 + docker_preserve_image: true + docker_cmd: + - '/usr/sbin/init' + docker_image_commands: + - 'yum install -y wget which cronie iproute initscripts' + docker_container_name: 'keycloak-master-el7' + centos-7-slave: + roles: + - agent + - domain_slave + platform: el-7-x86_64 + hypervisor: docker + image: centos:7 + docker_preserve_image: true + docker_cmd: + - '/usr/sbin/init' + docker_image_commands: + - 'yum install -y wget which cronie iproute initscripts' + docker_container_name: 'keycloak-slave-el7' + centos-7-db: + roles: + - agent + - db + platform: el-7-x86_64 + hypervisor: docker + image: centos:7 + docker_preserve_image: true + docker_cmd: + - '/usr/sbin/init' + docker_image_commands: + - 'yum install -y wget which cronie iproute initscripts' + docker_container_name: 'keycloak-db-el7' +CONFIG: + log_level: debug + type: foss +ssh: + password: root + auth_methods: ["password"] + diff --git a/spec/classes/init_spec.rb b/spec/classes/init_spec.rb index a449555b..70df8fa1 100644 --- a/spec/classes/init_spec.rb +++ b/spec/classes/init_spec.rb @@ -23,6 +23,95 @@ it { is_expected.to contain_class('keycloak::config').that_comes_before('Class[keycloak::service]') } it { is_expected.to contain_class('keycloak::service') } + context 'domain master' do + let(:params) do + { + operating_mode: 'domain', + install_dir: '/opt/keycloak-x', + role: 'master', + datasource_driver: 'postgresql', + wildfly_user: 'wildfly', + wildfly_user_password: 'changeme', + } + end + + it { is_expected.to compile.with_all_deps } + it do + is_expected.to contain_augeas('ensure-servername').with(incl: '/opt/keycloak-x/domain/configuration/host-master.xml') + is_expected.to contain_exec('create-wildfly-user').with(command: '/opt/keycloak-x/bin/add-user.sh --user wildfly --password changeme -e -s && touch /opt/keycloak-x/.create-wildfly-user') + end + end + + context 'domain slave' do + let(:params) do + { + operating_mode: 'domain', + install_dir: '/opt/keycloak-x', + role: 'slave', + master_address: '10.0.5.10', + datasource_driver: 'postgresql', + wildfly_user: 'wildfly', + wildfly_user_password: 'changeme', + } + end + + it { is_expected.to compile.with_all_deps } + + it do + is_expected.to contain_augeas('ensure-servername').with(incl: '/opt/keycloak-x/domain/configuration/host-slave.xml', + context: '/files/opt/keycloak-x/domain/configuration/host-slave.xml/host/servers') + is_expected.to contain_exec('create-wildfly-user').with(command: '/opt/keycloak-x/bin/add-user.sh --user wildfly --password changeme -e -s && touch /opt/keycloak-x/.create-wildfly-user') + end + end + + context 'standalone with domain role defined' do + let(:params) do + { + operating_mode: 'standalone', + role: 'master', + } + end + + it { is_expected.not_to compile } + end + + context 'domain slave without master_address' do + let(:params) do + { + operating_mode: 'domain', + wildfly_user: 'wildfly', + wildfly_user_password: 'wildfly', + role: 'slave', + } + end + + it { is_expected.not_to compile } + end + + context 'domain master without wildfly user' do + let(:params) do + { + operating_mode: 'domain', + role: 'master', + wildfly_user_password: 'wildfly', + } + end + + it { is_expected.not_to compile } + end + + context 'domain master without wildfly user password' do + let(:params) do + { + operating_mode: 'domain', + role: 'master', + wildfly_user: 'wildfly', + } + end + + it { is_expected.not_to compile } + end + context 'keycloak::install' do it do is_expected.to contain_user('keycloak').only_with(ensure: 'present', @@ -139,7 +228,7 @@ end it do - is_expected.to contain_file_line('standalone.conf-JAVA_OPTS').with( + is_expected.to contain_file_line('keycloak-JAVA_OPTS').with( ensure: 'absent', path: "/opt/keycloak-#{version}/bin/standalone.conf", line: 'JAVA_OPTS="$JAVA_OPTS "', @@ -160,7 +249,7 @@ let(:params) { { java_opts: '-Xmx512m -Xms64m' } } it do - is_expected.to contain_file_line('standalone.conf-JAVA_OPTS').with( + is_expected.to contain_file_line('keycloak-JAVA_OPTS').with( ensure: 'present', path: "/opt/keycloak-#{version}/bin/standalone.conf", line: 'JAVA_OPTS="$JAVA_OPTS -Xmx512m -Xms64m"', @@ -173,7 +262,7 @@ let(:params) { { java_opts: '-Xmx512m -Xms64m', java_opts_append: false } } it do - is_expected.to contain_file_line('standalone.conf-JAVA_OPTS').with( + is_expected.to contain_file_line('keycloak-JAVA_OPTS').with( ensure: 'present', path: "/opt/keycloak-#{version}/bin/standalone.conf", line: 'JAVA_OPTS="-Xmx512m -Xms64m"', diff --git a/spec/spec_helper_acceptance_setup.rb b/spec/spec_helper_acceptance_setup.rb index c10b8440..b96db6c5 100644 --- a/spec/spec_helper_acceptance_setup.rb +++ b/spec/spec_helper_acceptance_setup.rb @@ -8,6 +8,8 @@ c.keycloak_version = keycloak_version c.add_setting :keycloak_full c.keycloak_full = (ENV['BEAKER_keycloak_full'] == 'true' || ENV['BEAKER_keycloak_full'] == 'yes') + c.add_setting :keycloak_domain_mode_cluster + c.keycloak_domain_mode_cluster = (ENV['BEAKER_keycloak_domain_mode_cluster'] == 'true' || ENV['BEAKER_keycloak_domain_mode_cluster'] == 'yes') end proj_root = File.expand_path(File.join(File.dirname(__FILE__), '..')) diff --git a/templates/config.cli.erb b/templates/config.cli.erb index 91e74b03..e8e53080 100644 --- a/templates/config.cli.erb +++ b/templates/config.cli.erb @@ -1,37 +1,46 @@ <% if scope['keycloak::operating_mode'] == 'standalone'-%> embed-server +<% @prefix=''-%> <% elsif scope['keycloak::operating_mode'] == 'clustered'-%> embed-server --server-config=standalone-ha.xml +<% @prefix=''-%> +<% else -%> +embed-host-controller +<% @prefix='/profile=auth-server-clustered'-%> <% end -%> -<%- if scope['keycloak::proxy_https'] -%> -if (result.proxy-address-forwarding != true) of /subsystem=undertow/server=default-server/http-listener=default:read-resource -/subsystem=undertow/server=default-server/http-listener=default:write-attribute(name=proxy-address-forwarding,value=true) + +<%- # https proxying -%> +<%- if scope['keycloak::proxy_https'] -%> +if (result.proxy-address-forwarding != true) of <%= @prefix -%>/subsystem=undertow/server=default-server/http-listener=default:read-resource +<%= @prefix -%>/subsystem=undertow/server=default-server/http-listener=default:write-attribute(name=proxy-address-forwarding,value=true) end-if -if (result.proxy-address-forwarding != true) of /subsystem=undertow/server=default-server/https-listener=https:read-resource -/subsystem=undertow/server=default-server/https-listener=https:write-attribute(name=proxy-address-forwarding,value=true) +if (result.proxy-address-forwarding != true) of <%= @prefix -%>/subsystem=undertow/server=default-server/https-listener=https:read-resource +<%= @prefix -%>/subsystem=undertow/server=default-server/https-listener=https:write-attribute(name=proxy-address-forwarding,value=true) end-if -if (outcome != success) of /socket-binding-group=standard-sockets/socket-binding=proxy-https:read-resource -/socket-binding-group=standard-sockets/socket-binding=proxy-https:add(port=443) +if (outcome != success) of <%= @prefix -%>/socket-binding-group=standard-sockets/socket-binding=proxy-https:read-resource +<%= @prefix -%>/socket-binding-group=standard-sockets/socket-binding=proxy-https:add(port=443) end-if -if (result.redirect-socket != proxy-https) of /subsystem=undertow/server=default-server/http-listener=default:read-resource -/subsystem=undertow/server=default-server/http-listener=default:write-attribute(name=redirect-socket,value=proxy-https) +if (result.redirect-socket != proxy-https) of <%= @prefix -%>/subsystem=undertow/server=default-server/http-listener=default:read-resource +<%= @prefix -%>/subsystem=undertow/server=default-server/http-listener=default:write-attribute(name=redirect-socket,value=proxy-https) end-if <%- end -%> -/subsystem=datasources/data-source=KeycloakDS:write-attribute(name=driver-name, value=<%= scope['keycloak::datasource_driver'] %>) -/subsystem=datasources/data-source=KeycloakDS:write-attribute(name=connection-url, value="<%= scope['keycloak::datasource_connection_url'] %>") -/subsystem=datasources/data-source=KeycloakDS:write-attribute(name=jndi-name, value=java:jboss/datasources/KeycloakDS) -/subsystem=datasources/data-source=KeycloakDS:write-attribute(name=user-name, value=<%= scope['keycloak::datasource_username'] %>) -/subsystem=datasources/data-source=KeycloakDS:write-attribute(name=password, value=<%= scope['keycloak::datasource_password'] %>) + +<%- # datasources -%> +<%= @prefix -%>/subsystem=datasources/data-source=KeycloakDS:write-attribute(name=driver-name, value=<%= scope['keycloak::datasource_driver'] %>) +<%= @prefix -%>/subsystem=datasources/data-source=KeycloakDS:write-attribute(name=connection-url, value="<%= scope['keycloak::datasource_connection_url'] %>") +<%= @prefix -%>/subsystem=datasources/data-source=KeycloakDS:write-attribute(name=jndi-name, value=java:jboss/datasources/KeycloakDS) +<%= @prefix -%>/subsystem=datasources/data-source=KeycloakDS:write-attribute(name=user-name, value=<%= scope['keycloak::datasource_username'] %>) +<%= @prefix -%>/subsystem=datasources/data-source=KeycloakDS:write-attribute(name=password, value=<%= scope['keycloak::datasource_password'] %>) <%- if scope['keycloak::datasource_driver'] == 'mysql' -%> -/subsystem=datasources/data-source=KeycloakDS:write-attribute(name=background-validation, value=true) -/subsystem=datasources/data-source=KeycloakDS:write-attribute(name=check-valid-connection-sql, value="SELECT 1") -/subsystem=datasources/data-source=KeycloakDS:write-attribute(name=background-validation-millis, value=60000) -/subsystem=datasources/data-source=KeycloakDS:write-attribute(name=flush-strategy, value=IdleConnections) +<%= @prefix -%>/subsystem=datasources/data-source=KeycloakDS:write-attribute(name=background-validation, value=true) +<%= @prefix -%>/subsystem=datasources/data-source=KeycloakDS:write-attribute(name=check-valid-connection-sql, value="SELECT 1") +<%= @prefix -%>/subsystem=datasources/data-source=KeycloakDS:write-attribute(name=background-validation-millis, value=60000) +<%= @prefix -%>/subsystem=datasources/data-source=KeycloakDS:write-attribute(name=flush-strategy, value=IdleConnections) try -/subsystem=datasources/jdbc-driver=mysql:add(driver-module-name=com.mysql.jdbc,driver-name=mysql,driver-xa-datasource-class-name=<%= scope['keycloak::mysql_datasource_class'] %>) +<%= @prefix -%>/subsystem=datasources/jdbc-driver=mysql:add(driver-module-name=com.mysql.jdbc,driver-name=mysql,driver-xa-datasource-class-name=<%= scope['keycloak::mysql_datasource_class'] %>) catch -/subsystem=datasources/jdbc-driver=mysql:remove -/subsystem=datasources/jdbc-driver=mysql:add(driver-module-name=com.mysql.jdbc,driver-name=mysql,driver-xa-datasource-class-name=<%= scope['keycloak::mysql_datasource_class'] %>) +<%= @prefix -%>/subsystem=datasources/jdbc-driver=mysql:remove +<%= @prefix -%>/subsystem=datasources/jdbc-driver=mysql:add(driver-module-name=com.mysql.jdbc,driver-name=mysql,driver-xa-datasource-class-name=<%= scope['keycloak::mysql_datasource_class'] %>) end-try <%- elsif scope['keycloak::datasource_driver'] == 'h2' -%> /subsystem=datasources/data-source=KeycloakDS:undefine-attribute(name=background-validation) @@ -39,81 +48,171 @@ end-try /subsystem=datasources/data-source=KeycloakDS:undefine-attribute(name=background-validation-millis) /subsystem=datasources/data-source=KeycloakDS:undefine-attribute(name=flush-strategy) <%- elsif scope['keycloak::datasource_driver'] == 'oracle' -%> -/subsystem=datasources/data-source=KeycloakDS:write-attribute(name=background-validation, value=true) -/subsystem=datasources/data-source=KeycloakDS:write-attribute(name=check-valid-connection-sql, value="SELECT 1 FROM DUAL") -/subsystem=datasources/data-source=KeycloakDS:write-attribute(name=background-validation-millis, value=60000) -/subsystem=datasources/data-source=KeycloakDS:write-attribute(name=flush-strategy, value=IdleConnections) +<%= @prefix -%>/subsystem=datasources/data-source=KeycloakDS:write-attribute(name=background-validation, value=true) +<%= @prefix -%>/subsystem=datasources/data-source=KeycloakDS:write-attribute(name=check-valid-connection-sql, value="SELECT 1 FROM DUAL") +<%= @prefix -%>/subsystem=datasources/data-source=KeycloakDS:write-attribute(name=background-validation-millis, value=60000) +<%= @prefix -%>/subsystem=datasources/data-source=KeycloakDS:write-attribute(name=flush-strategy, value=IdleConnections) try -/subsystem=datasources/jdbc-driver=oracle:add(driver-module-name=org.oracle,driver-name=oracle,driver-xa-datasource-class-name=oracle.jdbc.xa.client.OracleXADataSource) +<%= @prefix -%>/subsystem=datasources/jdbc-driver=oracle:add(driver-module-name=org.oracle,driver-name=oracle,driver-xa-datasource-class-name=oracle.jdbc.xa.client.OracleXADataSource) catch -/subsystem=datasources/jdbc-driver=oracle:remove -/subsystem=datasources/jdbc-driver=oracle:add(driver-module-name=org.oracle,driver-name=oracle,driver-xa-datasource-class-name=oracle.jdbc.xa.client.OracleXADataSource) +<%= @prefix -%>/subsystem=datasources/jdbc-driver=oracle:remove +<%= @prefix -%>/subsystem=datasources/jdbc-driver=oracle:add(driver-module-name=org.oracle,driver-name=oracle,driver-xa-datasource-class-name=oracle.jdbc.xa.client.OracleXADataSource) end-try <%- elsif scope['keycloak::datasource_driver'] == 'postgresql' -%> -/subsystem=datasources/data-source=KeycloakDS:write-attribute(name=background-validation, value=true) -/subsystem=datasources/data-source=KeycloakDS:write-attribute(name=check-valid-connection-sql, value="SELECT 1") -/subsystem=datasources/data-source=KeycloakDS:write-attribute(name=background-validation-millis, value=60000) -/subsystem=datasources/data-source=KeycloakDS:write-attribute(name=flush-strategy, value=IdleConnections) +<%= @prefix -%>/subsystem=datasources/data-source=KeycloakDS:write-attribute(name=background-validation, value=true) +<%= @prefix -%>/subsystem=datasources/data-source=KeycloakDS:write-attribute(name=check-valid-connection-sql, value="SELECT 1") +<%= @prefix -%>/subsystem=datasources/data-source=KeycloakDS:write-attribute(name=background-validation-millis, value=60000) +<%= @prefix -%>/subsystem=datasources/data-source=KeycloakDS:write-attribute(name=flush-strategy, value=IdleConnections) try -/subsystem=datasources/jdbc-driver=postgresql:add(driver-module-name=org.postgresql,driver-name=postgresql,driver-xa-datasource-class-name=org.postgresql.xa.PGXADataSource) +<%= @prefix -%>/subsystem=datasources/jdbc-driver=postgresql:add(driver-module-name=org.postgresql,driver-name=postgresql,driver-xa-datasource-class-name=org.postgresql.xa.PGXADataSource) catch -/subsystem=datasources/jdbc-driver=postgresql:remove -/subsystem=datasources/jdbc-driver=postgresql:add(driver-module-name=org.postgresql,driver-name=postgresql,driver-xa-datasource-class-name=org.postgresql.xa.PGXADataSource) +<%= @prefix -%>/subsystem=datasources/jdbc-driver=postgresql:remove +<%= @prefix -%>/subsystem=datasources/jdbc-driver=postgresql:add(driver-module-name=org.postgresql,driver-name=postgresql,driver-xa-datasource-class-name=org.postgresql.xa.PGXADataSource) end-try <%- end -%> + +<%- # truststore -%> <%- if scope['keycloak::truststore'] -%> -if (outcome != success) of /subsystem=keycloak-server/spi=truststore:read-resource -/subsystem=keycloak-server/spi=truststore/:add -/subsystem=keycloak-server/spi=truststore/provider=file/:add(enabled=true) -end-if -/subsystem=keycloak-server/spi=truststore/provider=file/:map-put(name=properties,key=file,value=<%= scope['keycloak::install_base'] %>/standalone/configuration/truststore.jks) -/subsystem=keycloak-server/spi=truststore/provider=file/:map-put(name=properties,key=password,value=<%= scope['keycloak::truststore_password'] %>) -/subsystem=keycloak-server/spi=truststore/provider=file/:map-put(name=properties,key=hostname-verification-policy,value=<%= scope['keycloak::truststore_hostname_verification_policy'] %>) -/subsystem=keycloak-server/spi=truststore/provider=file/:map-put(name=properties,key=disabled,value=false) +if (outcome != success) of <%= @prefix -%>/subsystem=keycloak-server/spi=truststore:read-resource +<%= @prefix -%>/subsystem=keycloak-server/spi=truststore/:add +<%= @prefix -%>/subsystem=keycloak-server/spi=truststore/provider=file/:add(enabled=true) +end-if +<% if scope['keycloak::operating_mode'] == 'domain'-%> +<%= @prefix -%>/subsystem=keycloak-server/spi=truststore/provider=file/:map-put(name=properties,key=file,value=<%= scope['keycloak::install_base'] %>/domain/configuration/truststore.jks) +<% else -%> +<%= @prefix -%>/subsystem=keycloak-server/spi=truststore/provider=file/:map-put(name=properties,key=file,value=<%= scope['keycloak::install_base'] %>/standalone/configuration/truststore.jks) +<% end -%> +<%= @prefix -%>/subsystem=keycloak-server/spi=truststore/provider=file/:map-put(name=properties,key=password,value=<%= scope['keycloak::truststore_password'] %>) +<%= @prefix -%>/subsystem=keycloak-server/spi=truststore/provider=file/:map-put(name=properties,key=hostname-verification-policy,value=<%= scope['keycloak::truststore_hostname_verification_policy'] %>) +<%= @prefix -%>/subsystem=keycloak-server/spi=truststore/provider=file/:map-put(name=properties,key=disabled,value=false) <%- else -%> -if (outcome == success) of /subsystem=keycloak-server/spi=truststore:read-resource -/subsystem=keycloak-server/spi=truststore/:remove +if (outcome == success) of <%= @prefix -%>/subsystem=keycloak-server/spi=truststore:read-resource +<%= @prefix -%>/subsystem=keycloak-server/spi=truststore/:remove end-if <%- end -%> -/subsystem=keycloak-server/theme=defaults/:write-attribute(name=staticMaxAge, value=<%= scope['keycloak::theme_static_max_age'] %>) -/subsystem=keycloak-server/theme=defaults/:write-attribute(name=cacheThemes, value=<%= scope['keycloak::theme_cache_themes'] %>) -/subsystem=keycloak-server/theme=defaults/:write-attribute(name=cacheTemplates, value=<%= scope['keycloak::theme_cache_templates'] %>) -/subsystem=deployment-scanner/scanner=default:write-attribute(name="auto-deploy-exploded",value=<%= scope['keycloak::auto_deploy_exploded'] %>) -/subsystem=deployment-scanner/scanner=default:write-attribute(name="auto-deploy-zipped",value=<%= scope['keycloak::auto_deploy_zipped'] %>) + +<%- # theming -%> +<%= @prefix -%>/subsystem=keycloak-server/theme=defaults/:write-attribute(name=staticMaxAge, value=<%= scope['keycloak::theme_static_max_age'] %>) +<%= @prefix -%>/subsystem=keycloak-server/theme=defaults/:write-attribute(name=cacheThemes, value=<%= scope['keycloak::theme_cache_themes'] %>) +<%= @prefix -%>/subsystem=keycloak-server/theme=defaults/:write-attribute(name=cacheTemplates, value=<%= scope['keycloak::theme_cache_templates'] %>) + +<%- # deployment scanner is not compatible with domain mode -%> +<% if scope['keycloak::operating_mode'] != 'domain'-%> +<%= @prefix -%>/subsystem=deployment-scanner/scanner=default:write-attribute(name="auto-deploy-exploded",value=<%= scope['keycloak::auto_deploy_exploded'] %>) +<%= @prefix -%>/subsystem=deployment-scanner/scanner=default:write-attribute(name="auto-deploy-zipped",value=<%= scope['keycloak::auto_deploy_zipped'] %>) +<% end -%> + try -/subsystem=keycloak-server/spi=userCache/provider=default/:add(enabled=<%= scope['keycloak::user_cache']%>) +<%= @prefix -%>/subsystem=keycloak-server/spi=userCache/provider=default/:add(enabled=<%= scope['keycloak::user_cache']%>) catch -/subsystem=keycloak-server/spi=userCache/provider=default/:remove -/subsystem=keycloak-server/spi=userCache/provider=default/:add(enabled=<%= scope['keycloak::user_cache']%>) +<%= @prefix -%>/subsystem=keycloak-server/spi=userCache/provider=default/:remove +<%= @prefix -%>/subsystem=keycloak-server/spi=userCache/provider=default/:add(enabled=<%= scope['keycloak::user_cache']%>) end-try -<%- if scope['keycloak::operating_mode'] == 'clustered' && scope['keycloak::enable_jdbc_ping'] -%> -if (outcome != success) of /subsystem=jgroups/stack=tcp/protocol=JDBC_PING:read-resource + +<%- # JDBC_PING & remove udp stack -%> +<%- if scope['keycloak::operating_mode'] != 'standalone' && scope['keycloak::enable_jdbc_ping'] -%> +if (outcome != success) of <%= @prefix -%>/subsystem=jgroups/stack=tcp/protocol=JDBC_PING:read-resource <%- if scope['keycloak::datasource_driver'] == 'postgresql' -%> -/subsystem=jgroups/stack=tcp/protocol=JDBC_PING: add(add-index=0, data-source="KeycloakDS", properties=[initialize_sql="CREATE TABLE IF NOT EXISTS JGROUPSPING ( own_addr varchar(200) NOT NULL, cluster_name varchar(200) NOT NULL, created TIMESTAMP DEFAULT CURRENT_TIMESTAMP, ping_data BYTEA, constraint PK_JGROUPSPING PRIMARY KEY (own_addr, cluster_name))"]) +<%= @prefix -%>/subsystem=jgroups/stack=tcp/protocol=JDBC_PING: add(add-index=0, data-source="KeycloakDS", properties=[initialize_sql="CREATE TABLE IF NOT EXISTS JGROUPSPING ( own_addr varchar(200) NOT NULL, cluster_name varchar(200) NOT NULL, created TIMESTAMP DEFAULT CURRENT_TIMESTAMP, ping_data BYTEA, constraint PK_JGROUPSPING PRIMARY KEY (own_addr, cluster_name))"]) <%- end -%> <%- if scope['keycloak::datasource_driver'] == 'mysql' -%> -/subsystem=jgroups/stack=tcp/protocol=JDBC_PING: add(add-index=0, data-source="KeycloakDS", properties=[initialize_sql="CREATE TABLE IF NOT EXISTS JGROUPSPING (own_addr varchar(200) NOT NULL, cluster_name varchar(200) NOT NULL, updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, ping_data varbinary(5000) DEFAULT NULL, PRIMARY KEY (own_addr, cluster_name)) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin"]) +<%= @prefix -%>/subsystem=jgroups/stack=tcp/protocol=JDBC_PING: add(add-index=0, data-source="KeycloakDS", properties=[initialize_sql="CREATE TABLE IF NOT EXISTS JGROUPSPING (own_addr varchar(200) NOT NULL, cluster_name varchar(200) NOT NULL, updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, ping_data varbinary(5000) DEFAULT NULL, PRIMARY KEY (own_addr, cluster_name)) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin"]) <%- end -%> end-if -if (outcome == success) of /subsystem=jgroups/stack=tcp/protocol=MPING:read-resource -/subsystem=jgroups/stack=tcp/protocol=MPING: remove() +if (outcome == success) of <%= @prefix -%>/subsystem=jgroups/stack=tcp/protocol=MPING:read-resource +<%= @prefix -%>/subsystem=jgroups/stack=tcp/protocol=MPING: remove() end-if -if (outcome == success) of /subsystem=jgroups/stack=tcp/protocol=pbcast.GMS:read-resource -/subsystem=jgroups/stack=tcp/protocol=pbcast.GMS: remove() -/subsystem=jgroups/stack=tcp/protocol=pbcast.GMS: add(properties=[join_timeout=30000, print_local_addr=true, print_physical_addrs=true]) +if (outcome == success) of <%= @prefix -%>/subsystem=jgroups/stack=tcp/protocol=pbcast.GMS:read-resource +<%= @prefix -%>/subsystem=jgroups/stack=tcp/protocol=pbcast.GMS: remove() +<%= @prefix -%>/subsystem=jgroups/stack=tcp/protocol=pbcast.GMS: add(properties=[join_timeout=30000, print_local_addr=true, print_physical_addrs=true]) end-if -if (outcome != success) of /subsystem=jgroups/stack=tcp/protocol=JDBC_PING:read-resource +if (outcome != success) of <%= @prefix -%>/subsystem=jgroups/stack=tcp/protocol=JDBC_PING:read-resource end-if -/subsystem=jgroups/channel=ee:write-attribute(name=stack, value="tcp") -if (outcome == success) of /subsystem=jgroups/stack=udp:read-resource -/subsystem=jgroups/stack=udp: remove() +<%= @prefix -%>/subsystem=jgroups/channel=ee:write-attribute(name=stack, value="tcp") +if (outcome == success) of <%= @prefix -%>/subsystem=jgroups/stack=udp:read-resource +<%= @prefix -%>/subsystem=jgroups/stack=udp: remove() end-if -if (outcome == success) of /socket-binding-group=standard-sockets/socket-binding=jgroups-udp:read-resource -/socket-binding-group=standard-sockets/socket-binding=jgroups-udp:remove() +if (outcome == success) of <%= @prefix -%>/socket-binding-group=standard-sockets/socket-binding=jgroups-udp:read-resource +<%= @prefix -%>/socket-binding-group=standard-sockets/socket-binding=jgroups-udp:remove() end-if -if (outcome == success) of /socket-binding-group=standard-sockets/socket-binding=jgroups-mping:read-resource -/socket-binding-group=standard-sockets/socket-binding=jgroups-mping:remove() +if (outcome == success) of <%= @prefix -%>/socket-binding-group=standard-sockets/socket-binding=jgroups-mping:read-resource +<%= @prefix -%>/socket-binding-group=standard-sockets/socket-binding=jgroups-mping:remove() end-if +<%- if scope['keycloak::operating_mode'] != 'domain' -%> /interface=private:write-attribute(name=inet-address, value=${jboss.bind.address.private:<%= scope['keycloak::jboss_bind_private_address'] %>}) /interface=public:write-attribute(name=inet-address, value=${jboss.bind.address:<%= scope['keycloak::jboss_bind_public_address'] %>}) <%- end -%> +<%- end -%> + +<%- # domain mode specific things -%> +<% if scope['keycloak::operating_mode'] == 'domain' -%> + +<%- # remove load balancer -%> +if (outcome == success) of /host=master/server-config=load-balancer:read-resource +/host=master/server-config=load-balancer:remove +end-if +if (outcome == success) of /server-group=load-balancer-group:read-resource +/server-group=load-balancer-group:remove +end-if +if (outcome == success) of /profile=load-balancer:read-resource +/profile=load-balancer:remove +end-if +if (outcome == success) of /socket-binding-group=load-balancer-sockets:read-resource +/socket-binding-group=load-balancer-sockets:remove +end-if + +<%- # ensure proxying for ha sockets -%> +if (outcome != success) of /socket-binding-group=ha-sockets/socket-binding=proxy-https:read-resource +/socket-binding-group=ha-sockets/socket-binding=proxy-https:add(port=443) +end-if + +<%- # caches -%> +<%= @prefix -%>/subsystem=infinispan/cache-container=keycloak/distributed-cache=sessions:write-attribute(name=owners, value=${env.CACHE_OWNERS:2}) +<%= @prefix -%>/subsystem=infinispan/cache-container=keycloak/distributed-cache=authenticationSessions:write-attribute(name=owners, value=${env.CACHE_OWNERS:2}) +<%= @prefix -%>/subsystem=infinispan/cache-container=keycloak/distributed-cache=offlineSessions:write-attribute(name=owners, value=${env.CACHE_OWNERS:2}) +<%= @prefix -%>/subsystem=infinispan/cache-container=keycloak/distributed-cache=clientSessions:write-attribute(name=owners, value=${env.CACHE_OWNERS:2}) +<%= @prefix -%>/subsystem=infinispan/cache-container=keycloak/distributed-cache=offlineClientSessions:write-attribute(name=owners, value=${env.CACHE_OWNERS:2}) +<%= @prefix -%>/subsystem=infinispan/cache-container=keycloak/distributed-cache=loginFailures:write-attribute(name=owners, value=${env.CACHE_OWNERS:2}) +<%= @prefix -%>/subsystem=infinispan/cache-container=keycloak/distributed-cache=actionTokens:write-attribute(name=owners, value=${env.CACHE_OWNERS:2}) + +<%- # take control of the interfaces -%> +if (outcome != success) of /interface=management:read-resource() +/interface=management:add() +end-if +if (result != undefined) of /interface=management:read-attribute(name=inet-address) +/interface=management:write-attribute(name=inet-address, value=undefined) +end-if +if (outcome != success) of /interface=private:read-resource() +/interface=private:add() +end-if +if (result != undefined) of /interface=private:read-attribute(name=inet-address) +/interface=private:write-attribute(name=inet-address, value=undefined) +end-if +if (outcome != success) of /interface=public:read-resource() +/interface=public:add() +end-if +if (result != undefined) of /interface=public:read-attribute(name=inet-address) +/interface=public:write-attribute(name=inet-address, value=undefined) +end-if +if (result != public) of /socket-binding-group=ha-sockets:read-attribute(name=default-interface) +/socket-binding-group=ha-sockets:write-attribute(name=default-interface, value=public) +end-if +if (result != defined) of /socket-binding-group=ha-sockets/socket-binding=ajp:read-attribute(name=interface) +/socket-binding-group=ha-sockets/socket-binding=ajp:write-attribute(name=interface, value=undefined) +end-if +if (result != defined) of /socket-binding-group=ha-sockets/socket-binding=http:read-attribute(name=interface) +/socket-binding-group=ha-sockets/socket-binding=http:write-attribute(name=interface, value=undefined) +end-if +if (result != defined) of /socket-binding-group=ha-sockets/socket-binding=https:read-attribute(name=interface) +/socket-binding-group=ha-sockets/socket-binding=https:write-attribute(name=interface, value=undefined) +end-if +if (result != management) of /socket-binding-group=ha-sockets/socket-binding=jgroups-tcp:read-attribute(name=interface) +/socket-binding-group=ha-sockets/socket-binding=jgroups-tcp:write-attribute(name=interface,value=management) +end-if + +<%- # ensure datasource for ee default bindings is correct -%> +if (result != java:jboss/datasources/KeycloakDS) of <%= @prefix -%>/subsystem=ee/service=default-bindings:read-attribute(name=datasource) +<%= @prefix -%>/subsystem=ee/service=default-bindings:write-attribute(name=datasource,value=java:jboss/datasources/KeycloakDS) +end-if +stop-embedded-host-controller +<% end -%> diff --git a/templates/keycloak.service.erb b/templates/keycloak.service.erb index aab12de7..0392f2cf 100644 --- a/templates/keycloak.service.erb +++ b/templates/keycloak.service.erb @@ -14,6 +14,12 @@ Group=<%= scope['keycloak::group'] %> ExecStart=<%= scope['keycloak::install_base'] %>/bin/standalone.sh -b <%= scope['keycloak::service_bind_address'] %> -Djboss.http.port=<%= scope['keycloak::http_port'] %><% if scope['keycloak::service_extra_opts'] -%> <%= scope['keycloak::service_extra_opts'] -%><% end %> <% elsif scope['keycloak::operating_mode'] == 'clustered'-%> ExecStart=<%= scope['keycloak::install_base'] %>/bin/standalone.sh --server-config=standalone-ha.xml -b <%= scope['keycloak::service_bind_address'] %> -Djboss.http.port=<%= scope['keycloak::http_port'] %><% if scope['keycloak::service_extra_opts'] -%> <%= scope['keycloak::service_extra_opts'] -%><% end %> +<% elsif scope['keycloak::operating_mode'] == 'domain'-%> +<% if scope['keycloak::role'] == 'master' -%> +ExecStart=<%= scope['keycloak::install_base'] %>/bin/domain.sh --host-config=host-master.xml -b <%= scope['keycloak::service_bind_address'] %> -Djboss.http.port=<%= scope['keycloak::http_port'] %> -Djboss.bind.address.management=<%= scope['keycloak::management_bind_address'] %> <% if scope['keycloak::service_extra_opts'] -%> <%= scope['keycloak::service_extra_opts'] -%><% end %> +<% else -%> +ExecStart=<%= scope['keycloak::install_base'] %>/bin/domain.sh --host-config=host-slave.xml -b <%= scope['keycloak::service_bind_address'] %> -Djboss.http.port=<%= scope['keycloak::http_port'] %> -Djboss.domain.master.address=<%= scope['keycloak::master_address'] %> -Djboss.bind.address.management=<%= scope['keycloak::management_bind_address'] %> <% if scope['keycloak::service_extra_opts'] -%> <%= scope['keycloak::service_extra_opts'] -%><% end %> +<% end -%> <% end -%> TimeoutStartSec=600 TimeoutStopSec=600 diff --git a/vagrant/Puppetfile b/vagrant/Puppetfile new file mode 100644 index 00000000..d09d55ba --- /dev/null +++ b/vagrant/Puppetfile @@ -0,0 +1,24 @@ +#!/usr/bin/env ruby +#^syntax detection + +forge "https://forgeapi.puppetlabs.com" + +mod 'puppetlabs-stdlib' +mod 'puppetfinland-easy_ipa', + :git => 'https://github.com/Puppet-Finland/puppet-ipa.git', + :commit => '67874ab7f40e4643b77adfd4155f9eb494776bc8' +mod 'puppetlabs-mysql' +mod 'puppetlabs-java' +mod 'puppetlabs-java_ks' +mod 'puppet-archive' +mod 'camptocamp-systemd' +mod 'puppetlabs-concat' +mod 'puppetlabs-apt' +mod 'puppetlabs-postgresql' +mod 'puppetlabs-cron_core' +mod 'puppetlabs-inifile' +mod 'puppetlabs-k5login_core' +mod 'puppetlabs-resource_api' +mod 'puppetlabs-translate' +mod 'puppetlabs-puppetserver_gem' +mod 'puppetlabs-haproxy' diff --git a/vagrant/Vagrantfile b/vagrant/Vagrantfile new file mode 100644 index 00000000..b34252ba --- /dev/null +++ b/vagrant/Vagrantfile @@ -0,0 +1,107 @@ +# -*- mode: ruby -*- +# vi: set ft=ruby : + +Vagrant.configure(2) do |config| + config.hostmanager.enabled = true + config.hostmanager.manage_host = true + config.hostmanager.manage_guest = true + config.hostmanager.ignore_private_ip = false + config.hostmanager.include_offline = false + + config.vm.define "db" do |box| + box.vm.box = "centos/7" + box.vm.hostname = 'db.local' + box.vm.synced_folder "..", "/vagrant", type: "virtualbox" + box.hostmanager.manage_guest = true + box.hostmanager.aliases = %w(db) + box.vm.network "private_network", ip: "192.168.168.254" + box.vm.provider 'virtualbox' do |vb| + vb.linked_clone = true + vb.gui = false + vb.memory = 1024 + vb.customize ["modifyvm", :id, "--ioapic", "on"] + vb.customize ["modifyvm", :id, "--hpet", "on"] + vb.customize ["modifyvm", :id, "--audio", "none"] + end + box.vm.provision "shell" do |s| + s.path = "install_agent.sh" + end + box.vm.provision "shell" do |s| + s.path = "run_puppet.sh" + s.args = ["-b", "/vagrant", "-m", "prepare.pp db.pp" ] + end + end + + config.vm.define "master" do |box| + box.vm.box = "centos/7" + box.vm.hostname = 'master.local' + box.vm.synced_folder "..", "/vagrant", type: "virtualbox" + box.hostmanager.manage_guest = true + box.hostmanager.aliases = %w(master) + box.vm.network "private_network", ip: "192.168.168.253" + box.vm.provider 'virtualbox' do |vb| + vb.linked_clone = true + vb.gui = false + vb.memory = 1024 + vb.customize ["modifyvm", :id, "--ioapic", "on"] + vb.customize ["modifyvm", :id, "--hpet", "on"] + vb.customize ["modifyvm", :id, "--audio", "none"] + end + box.vm.provision "shell" do |s| + s.path = "install_agent.sh" + end + box.vm.provision "shell" do |s| + s.path = "run_puppet.sh" + s.args = ["-b", "/vagrant", "-m", "prepare.pp master.pp"] + end + end + + config.vm.define "slave" do |box| + box.vm.box = "centos/7" + box.vm.hostname = 'slave.local' + box.vm.synced_folder "..", "/vagrant", type: "virtualbox" + box.hostmanager.manage_guest = true + box.hostmanager.aliases = %w(slave) + box.vm.network "private_network", ip: "192.168.168.252" + box.vm.provider 'virtualbox' do |vb| + vb.linked_clone = true + vb.gui = false + vb.memory = 1024 + vb.customize ["modifyvm", :id, "--ioapic", "on"] + vb.customize ["modifyvm", :id, "--hpet", "on"] + vb.customize ["modifyvm", :id, "--audio", "none"] + end + box.vm.provision "shell" do |s| + s.path = "install_agent.sh" + end + box.vm.provision "shell" do |s| + s.path = "run_puppet.sh" + s.args = ["-b", "/vagrant", "-m", "prepare.pp slave.pp"] + end + end + + config.vm.define "lb" do |box| + box.vm.box = "centos/7" + box.vm.hostname = 'lb.local' + box.vm.synced_folder "..", "/vagrant", type: "virtualbox" + box.hostmanager.manage_guest = true + box.hostmanager.aliases = %w(lb) + box.vm.network "private_network", ip: "192.168.168.251" + box.vm.provider 'virtualbox' do |vb| + vb.linked_clone = true + vb.gui = false + vb.memory = 1024 + vb.customize ["modifyvm", :id, "--ioapic", "on"] + vb.customize ["modifyvm", :id, "--hpet", "on"] + vb.customize ["modifyvm", :id, "--audio", "none"] + end + box.vm.provision "shell" do |s| + s.path = "install_agent.sh" + end + box.vm.provision "shell" do |s| + s.path = "run_puppet.sh" + s.args = ["-b", "/vagrant", "-m", "prepare.pp lb.pp"] + end + end +end + diff --git a/vagrant/db.pp b/vagrant/db.pp new file mode 100644 index 00000000..b9fb0faa --- /dev/null +++ b/vagrant/db.pp @@ -0,0 +1,36 @@ +class { '::postgresql::globals': + manage_package_repo => $manage_package_repo, + version => $postgresql_version, +} + +class { '::postgresql::server': + listen_addresses => $postgresql_listen_address, + require => Class['::postgresql::globals'] +} + +::postgresql::server::role { $db_username: + password_hash => postgresql_password($db_username, $db_password), + connection_limit => $db_connection_limit, + require => Class['::postgresql::server'] +} + +::postgresql::server::database_grant { "Grant all to ${db_username}": + privilege => 'ALL', + db => $db_database, + role => $db_username, +} + +::postgresql::server::db { $db_database: + user => $db_username, + password => postgresql_password($db_username, $db_password), +} + +postgresql::server::pg_hba_rule { 'Allow Keycloak instances network access to the database': + description => 'Open up PostgreSQL for access from 192.168.168.0/24', + type => 'host', + database => $db_username, + user => $db_password, + address => '192.168.168.0/24', + auth_method => 'md5', + require => Class['::postgresql::server'] +} diff --git a/vagrant/install_agent.sh b/vagrant/install_agent.sh new file mode 100755 index 00000000..19b70e92 --- /dev/null +++ b/vagrant/install_agent.sh @@ -0,0 +1,69 @@ +#!/bin/sh + +# Exit on any error +set -e + +CWD=`pwd` + +detect_osfamily() { + if [ -f /etc/redhat-release ]; then + OSFAMILY='redhat' + RELEASE=$(cat /etc/redhat-release) + if [ "`echo $RELEASE | grep -E 7\.[0-9]+`" ]; then + RHEL_VERSION="7" + else + echo "Unsupported Redhat/Centos version. Supported versions are 7.x" + exit 1 + fi + elif [ "`lsb_release -d | grep -E '(Ubuntu|Debian)'`" ]; then + OSFAMILY='debian' + DESCR="$(lsb_release -d | awk '{ print $2}')" + if [ `echo $DESCR|grep Ubuntu` ]; then + UBUNTU_VERSION="$(lsb_release -c | awk '{ print $2}')" + elif [ `echo $DESCR|grep Debian` ]; then + DEBIAN_VERSION="$(lsb_release -c | awk '{ print $2}')" + else + echo "Unsupported Debian family operating system. Supported are Debian and Ubuntu" + exit 1 + fi + else + echo "ERROR: unsupported osfamily. Supported are Debian and RedHat" + exit 1 + fi +} + +setup_puppet() { + if [ -x /opt/puppetlabs/bin/puppet ]; then + true + else + if [ $RHEL_VERSION ]; then + RELEASE_URL="https://yum.puppetlabs.com/puppet6/puppet6-release-el-${RHEL_VERSION}.noarch.rpm" + rpm -hiv "${RELEASE_URL}" || (c=$?; echo "Failed to install ${RELEASE_URL}"; (exit $c)) + yum -y install puppet-agent || (c=$?; echo "Failed to install puppet agent"; (exit $c)) + if systemctl list-unit-files --type=service | grep firewalld; then + systemctl stop firewalld + systemctl disable firewalld + systemctl mask firewalld + fi + else + if [ $UBUNTU_VERSION ]; then + APT_URL="https://apt.puppetlabs.com/puppet6-release-${UBUNTU_VERSION}.deb" + fi + if [ $DEBIAN_VERSION ]; then + APT_URL="https://apt.puppetlabs.com/puppet6-release-${DEBIAN_VERSION}.deb" + fi + # https://serverfault.com/questions/500764/dpkg-reconfigure-unable-to-re-open-stdin-no-file-or-directory + export DEBIAN_FRONTEND=noninteractive + FILE="$(mktemp -d)/puppet-release.db" + wget "${APT_URL}" -qO $FILE || (c=$?; echo "Failed to retrieve ${APT_URL}"; (exit $c)) + dpkg --install $FILE; rm $FILE; apt-get update || (c=$?; echo "Failed to install from ${FILE}"; (exit $c)) + apt-get -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" -y install puppet-agent || (c=$?; echo "Failed to install puppet agent"; (exit $c)) + fi + fi +} + +# Main program +detect_osfamily +setup_puppet + +cd $CWD diff --git a/vagrant/lb.pp b/vagrant/lb.pp new file mode 100644 index 00000000..3be769c7 --- /dev/null +++ b/vagrant/lb.pp @@ -0,0 +1,38 @@ +notify { 'Installing Load Balancer': } + +include ::haproxy + +haproxy::listen { 'kc': + collect_exported => false, + ipaddress => $facts['networking']['interfaces']['eth1']['ip'], + mode => 'http', + ports => '80', + options => { + 'option' => [ + 'tcplog', + 'forwardfor', + 'http-keep-alive' + ], + 'balance' => 'roundrobin', + 'cookie' => 'SRVNAME insert', + 'http-request' => 'set-header X-Forwarded-Port %[dst_port]', + }, +} + +haproxy::balancermember { 'master': + listening_service => 'kc', + server_names => 'master.local', + ipaddresses => '192.168.168.253', + ports => '8080', + options => 'cookie DC check', +} + +haproxy::balancermember { 'slave': + listening_service => 'kc', + server_names => 'slave.local', + ipaddresses => '192.168.168.252', + ports => '8080', + options => 'cookie HC check', +} + + diff --git a/vagrant/master.pp b/vagrant/master.pp new file mode 100644 index 00000000..0a35ad4b --- /dev/null +++ b/vagrant/master.pp @@ -0,0 +1,50 @@ +notify { 'Installing Master': } + +class { '::keycloak': + operating_mode => 'domain', + role => 'master', + management_bind_address => '192.168.168.253', + enable_jdbc_ping => true, + wildfly_user => $keycloak_wildfly_user, + wildfly_user_password => $keycloak_wildfly_user_password, + manage_install => true, + manage_datasource => false, + version => $keycloak_version, + datasource_driver => 'postgresql', + datasource_host => $keycloak_datasource_host, + datasource_port => 5432, + datasource_dbname => $keycloak_datasource_dbname, + datasource_username => $keycloak_datasource_username, + datasource_password => $keycloak_datasource_password, + admin_user => $keycloak_admin_user, + admin_user_password => $keycloak_admin_user_password, + service_bind_address => '0.0.0.0', + proxy_https => false, +} + +keycloak_realm { 'TEST.NET': + ensure => 'present', + display_name => 'TEST.NET', + display_name_html => 'TEST.NET', + login_with_email_allowed => false, + remember_me => false, + events_enabled => true, + admin_events_enabled => true, + admin_events_details_enabled => true, +} + +keycloak_client { 'example.com': + ensure => 'present', + realm => 'TEST.NET', + standard_flow_enabled => true, + protocol => 'saml', + full_scope_allowed => true, + service_accounts_enabled => false, + base_url => 'https://example.com/', + redirect_uris => [ + 'https://example.com/', + 'https://example.com/*', + ], + require => Keycloak_realm['TEST.NET'], +} + diff --git a/vagrant/prepare.pp b/vagrant/prepare.pp new file mode 100644 index 00000000..2c6d655b --- /dev/null +++ b/vagrant/prepare.pp @@ -0,0 +1,28 @@ +notify { 'Preparing for setup': } + +$tools = [ 'tcpdump', 'strace', 'nmap', 'screen', 'net-tools' ] + +package { $tools: + ensure => 'installed', +} + +package { 'r10k': + ensure => 'present', + provider => 'puppet_gem', +} + +package { 'git': + ensure => 'latest', +} + +exec { 'Update modules': + logoutput => true, + command => "r10k puppetfile install --puppetfile ${::basedir}/vagrant/Puppetfile --verbose --moduledir /etc/puppetlabs/code/environments/production/modules", # lint:ignore:140chars + timeout => 600, + path => ['/bin','/usr/bin','/opt/puppetlabs/bin','/opt/puppetlabs/puppet/bin'], +} + +file { '/etc/puppetlabs/code/environments/production/modules/keycloak': + ensure => 'link', + target => $::basedir, +} diff --git a/vagrant/run_puppet.sh b/vagrant/run_puppet.sh new file mode 100755 index 00000000..0c1f2f47 --- /dev/null +++ b/vagrant/run_puppet.sh @@ -0,0 +1,72 @@ +#!/bin/sh + +# Exit on any error +set -e + +# Preparations required prior to "puppet apply". + +usage() { + echo + echo "Usage: run_puppet.sh -b basedir" + echo + echo "Options:" + echo " -b Base directory for dependency Puppet modules installed by" + echo " librarian-puppet." + echo " -m Puppet manifests to run. Put them in the provision folder" + echo " -d Turn on debugging" + exit 1 +} + +# Parse the options + +# We are run without parameters -> usage +if [ "$1" = "" ]; then + usage +fi + +while getopts "b:m:h:d:" options; do + case $options in + b ) BASEDIR=$OPTARG;; + m ) MANIFESTS=$OPTARG;; + d ) DEBUG=$OPTARG;; + h ) usage;; + \? ) usage;; + * ) usage;; + esac +done + +CWD=`pwd` + +# Configure with "puppet apply" +if [ "$DEBUG" == "true" ]; then + PUPPET_APPLY="/opt/puppetlabs/bin/puppet apply --verbose --debug --trace --summarize" +else + PUPPET_APPLY="/opt/puppetlabs/bin/puppet apply" +fi + +# Pass variables to Puppet manifests via environment variables +export FACTER_profile='/etc/profile.d/myprofile.sh' +export FACTER_basedir="$BASEDIR" +export FACTER_keycloak_version='12.0.2' +export FACTER_keycloak_datasource_host='db.local' +export FACTER_keycloak_datasource_dbname='keycloak' +export FACTER_keycloak_datasource_username='keycloak' +export FACTER_keycloak_datasource_password='keycloak' +export FACTER_keycloak_admin_user='admin' +export FACTER_keycloak_admin_user_password='changeme' +export FACTER_keycloak_wildfly_user='wildfly' +export FACTER_keycloak_wildfly_user_password='wildfly' +export FACTER_manage_package_repo='false' +export FACTER_postgresql_version='9.6' +export FACTER_postgresql_manage_package_repo='true' +export FACTER_postgresql_listen_address='*' +export FACTER_db_username='keycloak' +export FACTER_db_password='keycloak' +export FACTER_db_database='keycloak' +export FACTER_db_connection_limit='300' + +for manifest in $MANIFESTS; do + $PUPPET_APPLY /vagrant/vagrant/$manifest +done + +cd $CWD diff --git a/vagrant/slave.pp b/vagrant/slave.pp new file mode 100644 index 00000000..0f876bc0 --- /dev/null +++ b/vagrant/slave.pp @@ -0,0 +1,25 @@ +notify { 'Installing Slave': } + +class { '::keycloak': + operating_mode => 'domain', + role => 'slave', + enable_jdbc_ping => true, + management_bind_address => '192.168.168.252', + wildfly_user => $keycloak_wildfly_user, + wildfly_user_password => $keycloak_wildfly_user_password, + master_address => '192.168.168.253', + manage_install => true, + manage_datasource => false, + version => $keycloak_version, + datasource_driver => 'postgresql', + datasource_host => $keycloak_datasource_host, + datasource_port => 5432, + datasource_dbname => $keycloak_datasource_dbname, + datasource_username => $keycloak_datasource_username, + datasource_password => $keycloak_datasource_password, + admin_user => $keycloak_admin_user, + admin_user_password => $keycloak_admin_user_password, + service_bind_address => '0.0.0.0', + proxy_https => false, +} +