import ipa-4.9.10-9.module+el8.7.0+17438+aa488955

This commit is contained in:
CentOS Sources 2023-01-12 03:27:02 -05:00 committed by Stepan Oksanichenko
parent a829c7c598
commit ad479c1a26
10 changed files with 1560 additions and 2 deletions

View File

@ -0,0 +1,346 @@
From 857713c5a9c8e0b62c06dd92e69c09eeb34b2e99 Mon Sep 17 00:00:00 2001
From: Anuja More <amore@redhat.com>
Date: Mon, 23 May 2022 12:26:34 +0530
Subject: [PATCH] Add end to end integration tests for external IdP
Added tests for HBAC and SUDO rule and other
test scenarios.
Related : https://pagure.io/freeipa/issue/8805
Related: https://pagure.io/freeipa/issue/8803
Related: https://pagure.io/freeipa/issue/8804
Signed-off-by: Anuja More <amore@redhat.com>
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
---
ipatests/test_integration/test_idp.py | 260 ++++++++++++++++++++++----
1 file changed, 226 insertions(+), 34 deletions(-)
diff --git a/ipatests/test_integration/test_idp.py b/ipatests/test_integration/test_idp.py
index 8f9e92e6a..2ffe6a208 100644
--- a/ipatests/test_integration/test_idp.py
+++ b/ipatests/test_integration/test_idp.py
@@ -1,6 +1,8 @@
from __future__ import absolute_import
import time
+import pytest
+import re
import textwrap
from ipaplatform.paths import paths
@@ -22,12 +24,12 @@ driver.get(verification_uri)
try:
element = WebDriverWait(driver, 90).until(
EC.presence_of_element_located((By.ID, "username")))
- driver.find_element_by_id("username").send_keys("testuser1")
- driver.find_element_by_id("password").send_keys("{passwd}")
- driver.find_element_by_id("kc-login").click()
+ driver.find_element(By.ID, "username").send_keys("testuser1")
+ driver.find_element(By.ID, "password").send_keys("{passwd}")
+ driver.find_element(By.ID, "kc-login").click()
element = WebDriverWait(driver, 90).until(
EC.presence_of_element_located((By.ID, "kc-login")))
- driver.find_element_by_id("kc-login").click()
+ driver.find_element(By.ID, "kc-login").click()
assert "Device Login Successful" in driver.page_source
finally:
now = datetime.now().strftime("%M-%S")
@@ -39,18 +41,12 @@ finally:
def add_user_code(host, verification_uri):
contents = user_code_script.format(uri=verification_uri,
passwd=host.config.admin_password)
- host.put_file_contents("/tmp/add_user_code.py", contents)
- tasks.run_repeatedly(
- host, ['python3', '/tmp/add_user_code.py'])
-
-
-def get_verification_uri(host, since, keycloak_server_name):
- command = textwrap.dedent("""
- journalctl -u ipa-otpd\\* --since="%s" | grep "user_code:" | awk '{ print substr($7,2,9) }'""" % since) # noqa: E501
- user_code = host.run_command(command).stdout_text.rstrip("\r\n")
- uri = ("https://{0}:8443/auth/realms/master/device?user_code={1}".format(
- keycloak_server_name, user_code))
- return uri
+ try:
+ host.put_file_contents("/tmp/add_user_code.py", contents)
+ tasks.run_repeatedly(
+ host, ['python3', '/tmp/add_user_code.py'])
+ finally:
+ host.run_command(["rm", "-f", "/tmp/add_user_code.py"])
def kinit_idp(host, user, keycloak_server):
@@ -58,11 +54,14 @@ def kinit_idp(host, user, keycloak_server):
tasks.kdestroy_all(host)
# create armor for FAST
host.run_command(["kinit", "-n", "-c", ARMOR])
- since = time.strftime('%Y-%m-%d %H:%M:%S')
cmd = ["kinit", "-T", ARMOR, user]
+
with host.spawn_expect(cmd, default_timeout=100) as e:
- e.expect('Authenticate at .+: ')
- uri = get_verification_uri(host, since, keycloak_server.hostname)
+ e.expect('Authenticate at (.+) and press ENTER.:')
+ prompt = e.get_last_output()
+ uri = re.search(r'Authenticate at (.*?) and press ENTER.:', prompt
+ ).group(1)
+ time.sleep(15)
if uri:
add_user_code(keycloak_server, uri)
e.sendline('\n')
@@ -74,21 +73,27 @@ def kinit_idp(host, user, keycloak_server):
class TestIDPKeycloak(IntegrationTest):
- num_replicas = 1
+ num_replicas = 2
topology = 'line'
@classmethod
def install(cls, mh):
- tasks.install_master(cls.master, setup_dns=True)
- tasks.install_client(cls.master, cls.replicas[0])
- content = cls.master.get_file_contents(paths.IPA_DEFAULT_CONF,
- encoding='utf-8')
- new_content = content + "\noidc_child_debug_level = 10"
- cls.master.put_file_contents(paths.IPA_DEFAULT_CONF, new_content)
+ cls.client = cls.replicas[0]
+ cls.replica = cls.replicas[1]
+ tasks.install_master(cls.master)
+ tasks.install_client(cls.master, cls.replicas[0],
+ extra_args=["--mkhomedir"])
+ tasks.install_replica(cls.master, cls.replicas[1])
+ for host in [cls.master, cls.replicas[0], cls.replicas[1]]:
+ content = host.get_file_contents(paths.IPA_DEFAULT_CONF,
+ encoding='utf-8')
+ new_content = content + "\noidc_child_debug_level = 10"
+ host.put_file_contents(paths.IPA_DEFAULT_CONF, new_content)
with tasks.remote_sssd_config(cls.master) as sssd_config:
sssd_config.edit_domain(
cls.master.domain, 'krb5_auth_timeout', 1100)
tasks.clear_sssd_cache(cls.master)
+ tasks.clear_sssd_cache(cls.replicas[0])
tasks.kinit_admin(cls.master)
cls.master.run_command(["ipa", "config-mod", "--user-auth-type=idp",
"--user-auth-type=password"])
@@ -97,20 +102,207 @@ class TestIDPKeycloak(IntegrationTest):
cls.replicas[0].run_command(xvfb)
def test_auth_keycloak_idp(self):
- keycloak_srv = self.replicas[0]
- create_quarkus.setup_keycloakserver(keycloak_srv)
+ """
+ Test case to check that OAuth 2.0 Device
+ Authorization Grant is working as
+ expected for user configured with external idp.
+ """
+ create_quarkus.setup_keycloakserver(self.client)
time.sleep(60)
- create_quarkus.setup_keycloak_client(keycloak_srv)
+ create_quarkus.setup_keycloak_client(self.client)
tasks.kinit_admin(self.master)
- cmd = ["ipa", "idp-add", "keycloak", "--provider=keycloak",
+ cmd = ["ipa", "idp-add", "keycloakidp", "--provider=keycloak",
"--client-id=ipa_oidc_client", "--org=master",
- "--base-url={0}:8443/auth".format(keycloak_srv.hostname)]
+ "--base-url={0}:8443/auth".format(self.client.hostname)]
self.master.run_command(cmd, stdin_text="{0}\n{0}".format(
- keycloak_srv.config.admin_password))
+ self.client.config.admin_password))
tasks.user_add(self.master, 'keycloakuser',
extra_args=["--user-auth-type=idp",
"--idp-user-id=testuser1@ipa.test",
- "--idp=keycloak"]
+ "--idp=keycloakidp"]
)
+ list_user = self.master.run_command(
+ ["ipa", "user-find", "--idp-user-id=testuser1@ipa.test"]
+ )
+ assert "keycloakuser" in list_user.stdout_text
+ list_by_idp = self.master.run_command(["ipa", "user-find",
+ "--idp=keycloakidp"]
+ )
+ assert "keycloakuser" in list_by_idp.stdout_text
+ list_by_user = self.master.run_command(
+ ["ipa", "user-find", "--idp-user-id=testuser1@ipa.test", "--all"]
+ )
+ assert "keycloakidp" in list_by_user.stdout_text
+ tasks.clear_sssd_cache(self.master)
+ kinit_idp(self.master, 'keycloakuser', keycloak_server=self.client)
+
+ @pytest.fixture
+ def hbac_setup_teardown(self):
+ # allow sshd only on given host
+ tasks.kinit_admin(self.master)
+ self.master.run_command(["ipa", "hbacrule-disable", "allow_all"])
+ self.master.run_command(["ipa", "hbacrule-add", "rule1"])
+ self.master.run_command(["ipa", "hbacrule-add-user", "rule1",
+ "--users=keycloakuser"]
+ )
+ self.master.run_command(["ipa", "hbacrule-add-host", "rule1",
+ "--hosts", self.replica.hostname])
+ self.master.run_command(["ipa", "hbacrule-add-service", "rule1",
+ "--hbacsvcs=sshd"]
+ )
+ tasks.clear_sssd_cache(self.master)
+ tasks.clear_sssd_cache(self.replica)
+ yield
+
+ # cleanup
+ tasks.kinit_admin(self.master)
+ self.master.run_command(["ipa", "hbacrule-enable", "allow_all"])
+ self.master.run_command(["ipa", "hbacrule-del", "rule1"])
+
+ def test_auth_hbac(self, hbac_setup_teardown):
+ """
+ Test case to check that hbacrule is working as
+ expected for user configured with external idp.
+ """
+ kinit_idp(self.master, 'keycloakuser', keycloak_server=self.client)
+ ssh_cmd = "ssh -q -K -l keycloakuser {0} whoami"
+ valid_ssh = self.master.run_command(
+ ssh_cmd.format(self.replica.hostname))
+ assert "keycloakuser" in valid_ssh.stdout_text
+ negative_ssh = self.master.run_command(
+ ssh_cmd.format(self.master.hostname), raiseonerr=False
+ )
+ assert negative_ssh.returncode == 255
+
+ def test_auth_sudo_idp(self):
+ """
+ Test case to check that sudorule is working as
+ expected for user configured with external idp.
+ """
+ tasks.kdestroy_all(self.master)
+ tasks.kinit_admin(self.master)
+ # rule: keycloakuser are allowed to execute yum on
+ # the client machine as root.
+ cmdlist = [
+ ["ipa", "sudocmd-add", "/usr/bin/yum"],
+ ["ipa", "sudorule-add", "sudorule"],
+ ['ipa', 'sudorule-add-user', '--users=keycloakuser',
+ 'sudorule'],
+ ['ipa', 'sudorule-add-host', '--hosts',
+ self.client.hostname, 'sudorule'],
+ ['ipa', 'sudorule-add-runasuser',
+ '--users=root', 'sudorule'],
+ ['ipa', 'sudorule-add-allow-command',
+ '--sudocmds=/usr/bin/yum', 'sudorule'],
+ ['ipa', 'sudorule-show', 'sudorule', '--all'],
+ ['ipa', 'sudorule-add-option',
+ 'sudorule', '--sudooption', "!authenticate"]
+ ]
+ for cmd in cmdlist:
+ self.master.run_command(cmd)
+ tasks.clear_sssd_cache(self.master)
+ tasks.clear_sssd_cache(self.client)
+ try:
+ cmd = 'sudo -ll -U keycloakuser'
+ test = self.client.run_command(cmd).stdout_text
+ assert "User keycloakuser may run the following commands" in test
+ assert "/usr/bin/yum" in test
+ kinit_idp(self.client, 'keycloakuser', self.client)
+ test_sudo = 'su -c "sudo yum list wget" keycloakuser'
+ self.client.run_command(test_sudo)
+ list_fail = self.master.run_command(cmd).stdout_text
+ assert "User keycloakuser is not allowed to run sudo" in list_fail
+ finally:
+ tasks.kinit_admin(self.master)
+ self.master.run_command(['ipa', 'sudorule-del', 'sudorule'])
+ self.master.run_command(["ipa", "sudocmd-del", "/usr/bin/yum"])
+
+ def test_auth_replica(self):
+ """
+ Test case to check that OAuth 2.0 Device
+ Authorization is working as expected on replica.
+ """
+ tasks.clear_sssd_cache(self.master)
+ tasks.clear_sssd_cache(self.replica)
+ tasks.kinit_admin(self.replica)
+ list_user = self.master.run_command(
+ ["ipa", "user-find", "--idp-user-id=testuser1@ipa.test"]
+ )
+ assert "keycloakuser" in list_user.stdout_text
+ list_by_idp = self.replica.run_command(["ipa", "user-find",
+ "--idp=keycloakidp"]
+ )
+ assert "keycloakuser" in list_by_idp.stdout_text
+ list_by_user = self.replica.run_command(
+ ["ipa", "user-find", "--idp-user-id=testuser1@ipa.test", "--all"]
+ )
+ assert "keycloakidp" in list_by_user.stdout_text
+ kinit_idp(self.replica, 'keycloakuser', keycloak_server=self.client)
+
+ def test_idp_with_services(self):
+ """
+ Test case to check that services can be configured
+ auth indicator as idp.
+ """
tasks.clear_sssd_cache(self.master)
- kinit_idp(self.master, 'keycloakuser', keycloak_srv)
+ tasks.kinit_admin(self.master)
+ domain = self.master.domain.name.upper()
+ services = [
+ "DNS/{0}@{1}".format(self.master.hostname, domain),
+ "HTTP/{0}@{1}".format(self.client.hostname, domain),
+ "dogtag/{0}@{1}".format(self.master.hostname, domain),
+ "ipa-dnskeysyncd/{0}@{1}".format(self.master.hostname, domain)
+ ]
+ try:
+ for service in services:
+ test = self.master.run_command(["ipa", "service-mod", service,
+ "--auth-ind=idp"]
+ )
+ assert "Authentication Indicators: idp" in test.stdout_text
+ finally:
+ for service in services:
+ self.master.run_command(["ipa", "service-mod", service,
+ "--auth-ind="])
+
+ def test_idp_backup_restore(self):
+ """
+ Test case to check that after restore data is retrieved
+ with related idp configuration.
+ """
+ tasks.kinit_admin(self.master)
+ user = "backupuser"
+ cmd = ["ipa", "idp-add", "testidp", "--provider=keycloak",
+ "--client-id=ipa_oidc_client", "--org=master",
+ "--base-url={0}:8443/auth".format(self.client.hostname)]
+ self.master.run_command(cmd, stdin_text="{0}\n{0}".format(
+ self.client.config.admin_password))
+
+ tasks.user_add(self.master, user,
+ extra_args=["--user-auth-type=idp",
+ "--idp-user-id=testuser1@ipa.test",
+ "--idp=testidp"]
+ )
+
+ backup_path = tasks.get_backup_dir(self.master)
+ # change data after backup
+ self.master.run_command(['ipa', 'user-del', user])
+ self.master.run_command(['ipa', 'idp-del', 'testidp'])
+ dirman_password = self.master.config.dirman_password
+ self.master.run_command(['ipa-restore', backup_path],
+ stdin_text=dirman_password + '\nyes')
+ try:
+ list_user = self.master.run_command(
+ ['ipa', 'user-show', 'backupuser', '--all']
+ ).stdout_text
+ assert "External IdP configuration: testidp" in list_user
+ assert "User authentication types: idp" in list_user
+ assert ("External IdP user identifier: "
+ "testuser1@ipa.test") in list_user
+ list_idp = self.master.run_command(['ipa', 'idp-find', 'testidp'])
+ assert "testidp" in list_idp.stdout_text
+ kinit_idp(self.master, user, self.client)
+ finally:
+ tasks.kdestroy_all(self.master)
+ tasks.kinit_admin(self.master)
+ self.master.run_command(["rm", "-rf", backup_path])
+ self.master.run_command(["ipa", "idp-del", "testidp"])
--
2.36.1

View File

@ -0,0 +1,67 @@
From 991849cf58fa990ad4540a61214b5ab4fcd4baa1 Mon Sep 17 00:00:00 2001
From: Armando Neto <abiagion@redhat.com>
Date: Fri, 8 Jul 2022 15:56:31 -0300
Subject: [PATCH] webui: Do not allow empty pagination size
Pagination size must be required, the current validators are triggered after
form is submitted, thus the only way for check if data is not empty is by making
the field required.
Fixes: https://pagure.io/freeipa/issue/9192
Signed-off-by: Armando Neto <abiagion@redhat.com>
Reviewed-By: Florence Blanc-Renaud <frenaud@redhat.com>
---
.../ui/src/freeipa/Application_controller.js | 1 +
ipatests/test_webui/test_misc_cases.py | 19 +++++++++++++++++++
2 files changed, 20 insertions(+)
diff --git a/install/ui/src/freeipa/Application_controller.js b/install/ui/src/freeipa/Application_controller.js
index 46aabc9c4..140ee8fe0 100644
--- a/install/ui/src/freeipa/Application_controller.js
+++ b/install/ui/src/freeipa/Application_controller.js
@@ -318,6 +318,7 @@ define([
$type: 'text',
name: 'pagination_size',
label: '@i18n:customization.table_pagination',
+ required: true,
validators: ['positive_integer']
}
]
diff --git a/ipatests/test_webui/test_misc_cases.py b/ipatests/test_webui/test_misc_cases.py
index 5f7ffb54e..aca9e1a99 100644
--- a/ipatests/test_webui/test_misc_cases.py
+++ b/ipatests/test_webui/test_misc_cases.py
@@ -11,6 +11,11 @@ from ipatests.test_webui.ui_driver import screenshot
import pytest
import re
+try:
+ from selenium.webdriver.common.by import By
+except ImportError:
+ pass
+
@pytest.mark.tier1
class TestMiscCases(UI_driver):
@@ -26,3 +31,17 @@ class TestMiscCases(UI_driver):
ver_re = re.compile('version: .*')
assert re.search(ver_re, about_text), 'Version not found'
self.dialog_button_click('ok')
+
+ @screenshot
+ def test_customization_pagination_input_required(self):
+ """Test if 'pagination size' is required when submitting the form."""
+ self.init_app()
+
+ self.profile_menu_action('configuration')
+ self.fill_input('pagination_size', '')
+ self.dialog_button_click('save')
+
+ pagination_size_elem = self.find(
+ ".widget[name='pagination_size']", By.CSS_SELECTOR)
+
+ self.assert_field_validation_required(parent=pagination_size_elem)
--
2.36.1

View File

@ -0,0 +1,56 @@
From ade5093b08f92b279c200f341e96972a74f644d8 Mon Sep 17 00:00:00 2001
From: Carla Martinez <carlmart@redhat.com>
Date: Fri, 29 Jul 2022 13:16:16 +0200
Subject: [PATCH] webui: Allow grace login limit
There was no support for setting the grace login limit on the WebUI. The
only way to so was only via CLI:
`ipa pwpolicy-mod --gracelimit=2 global_policy`
Thus, the grace login limit must be updated from the policy section and
this will reflect also on the user settings (under the 'Password Policy'
section)
Fixes: https://pagure.io/freeipa/issue/9211
Signed-off-by: Carla Martinez <carlmart@redhat.com>
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
---
install/ui/src/freeipa/policy.js | 3 +++
install/ui/src/freeipa/user.js | 5 +++++
2 files changed, 8 insertions(+)
diff --git a/install/ui/src/freeipa/policy.js b/install/ui/src/freeipa/policy.js
index fa2028a52..7ec103636 100644
--- a/install/ui/src/freeipa/policy.js
+++ b/install/ui/src/freeipa/policy.js
@@ -72,6 +72,9 @@ return {
{
name: 'cospriority',
required: true
+ },
+ {
+ name: 'passwordgracelimit'
}
]
}]
diff --git a/install/ui/src/freeipa/user.js b/install/ui/src/freeipa/user.js
index a580db035..b47c97f72 100644
--- a/install/ui/src/freeipa/user.js
+++ b/install/ui/src/freeipa/user.js
@@ -318,6 +318,11 @@ return {
label: '@mo-param:pwpolicy:krbpwdlockoutduration:label',
read_only: true,
measurement_unit: 'seconds'
+ },
+ {
+ name: 'passwordgracelimit',
+ label: '@mo-param:pwpolicy:passwordgracelimit:label',
+ read_only: true
}
]
},
--
2.37.2

View File

@ -0,0 +1,35 @@
From 05a298f56485222583cb7dd4f6a3a4c5c77fc8cf Mon Sep 17 00:00:00 2001
From: Florence Blanc-Renaud <flo@redhat.com>
Date: Sun, 7 Aug 2022 12:44:47 +0200
Subject: [PATCH] check_repl_update: in progress is a boolean
With the fix for https://pagure.io/freeipa/issue/9171,
nsds5replicaUpdateInProgress is now handled as a boolean.
One remaining occurrence was still handling it as a string
and calling lower() on its value.
Replace with direct boolean comparison.
Fixes: https://pagure.io/freeipa/issue/9218
Signed-off-by: Florence Blanc-Renaud <flo@redhat.com>
Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
---
ipaserver/install/replication.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/ipaserver/install/replication.py b/ipaserver/install/replication.py
index 16be3760c..9d9aa1c4b 100644
--- a/ipaserver/install/replication.py
+++ b/ipaserver/install/replication.py
@@ -1152,7 +1152,7 @@ class ReplicationManager:
except (ValueError, TypeError, KeyError):
end = 0
# incremental update is done if inprogress is false and end >= start
- done = inprogress and inprogress.lower() == 'false' and start <= end
+ done = inprogress is not None and not inprogress and start <= end
logger.info("Replication Update in progress: %s: status: %s: "
"start: %d: end: %d",
inprogress, status, start, end)
--
2.37.2

View File

@ -0,0 +1,125 @@
From 1316cd8b2252c2543cf2ef2186956a8833037b1e Mon Sep 17 00:00:00 2001
From: Rob Crittenden <rcritten@redhat.com>
Date: Thu, 21 Jul 2022 09:28:46 -0400
Subject: [PATCH] Disabling gracelimit does not prevent LDAP binds
Originally the code treated 0 as disabled. This was
changed during the review process to -1 but one remnant
was missed effetively allowing gracelimit 0 to also mean
disabled.
Add explicit tests for testing with gracelimit = 0 and
gracelimit = -1.
Also remove some extranous "str(self.master.domain.basedn)"
lines from some of the tests.
Fixes: https://pagure.io/freeipa/issue/9206
Signed-off-by: Rob Crittenden <rcritten@redhat.com>
Reviewed-By: Francisco Trivino <ftrivino@redhat.com>
---
.../ipa-graceperiod/ipa_graceperiod.c | 2 +-
ipatests/test_integration/test_pwpolicy.py | 55 ++++++++++++++++++-
2 files changed, 53 insertions(+), 4 deletions(-)
diff --git a/daemons/ipa-slapi-plugins/ipa-graceperiod/ipa_graceperiod.c b/daemons/ipa-slapi-plugins/ipa-graceperiod/ipa_graceperiod.c
index a3f57cb4b..345e1dee7 100644
--- a/daemons/ipa-slapi-plugins/ipa-graceperiod/ipa_graceperiod.c
+++ b/daemons/ipa-slapi-plugins/ipa-graceperiod/ipa_graceperiod.c
@@ -479,7 +479,7 @@ static int ipagraceperiod_preop(Slapi_PBlock *pb)
if (pwresponse_requested) {
slapi_pwpolicy_make_response_control(pb, -1, grace_limit - grace_user_time , -1);
}
- } else if ((grace_limit > 0) && (grace_user_time >= grace_limit)) {
+ } else if (grace_user_time >= grace_limit) {
LOG_TRACE("%s password is expired and out of grace limit\n", dn);
errstr = "Password is expired.\n";
ret = LDAP_INVALID_CREDENTIALS;
diff --git a/ipatests/test_integration/test_pwpolicy.py b/ipatests/test_integration/test_pwpolicy.py
index 6d6698284..41d6e9070 100644
--- a/ipatests/test_integration/test_pwpolicy.py
+++ b/ipatests/test_integration/test_pwpolicy.py
@@ -36,7 +36,7 @@ class TestPWPolicy(IntegrationTest):
cls.master.run_command(['ipa', 'group-add-member', POLICY,
'--users', USER])
cls.master.run_command(['ipa', 'pwpolicy-add', POLICY,
- '--priority', '1'])
+ '--priority', '1', '--gracelimit', '-1'])
cls.master.run_command(['ipa', 'passwd', USER],
stdin_text='{password}\n{password}\n'.format(
password=PASSWORD
@@ -265,7 +265,6 @@ class TestPWPolicy(IntegrationTest):
def test_graceperiod_expired(self):
"""Test the LDAP bind grace period"""
- str(self.master.domain.basedn)
dn = "uid={user},cn=users,cn=accounts,{base_dn}".format(
user=USER, base_dn=str(self.master.domain.basedn))
@@ -308,7 +307,6 @@ class TestPWPolicy(IntegrationTest):
def test_graceperiod_not_replicated(self):
"""Test that the grace period is reset on password reset"""
- str(self.master.domain.basedn)
dn = "uid={user},cn=users,cn=accounts,{base_dn}".format(
user=USER, base_dn=str(self.master.domain.basedn))
@@ -341,3 +339,54 @@ class TestPWPolicy(IntegrationTest):
)
assert 'passwordgraceusertime: 0' in result.stdout_text.lower()
self.reset_password(self.master)
+
+ def test_graceperiod_zero(self):
+ """Test the LDAP bind with zero grace period"""
+ dn = "uid={user},cn=users,cn=accounts,{base_dn}".format(
+ user=USER, base_dn=str(self.master.domain.basedn))
+
+ self.master.run_command(
+ ["ipa", "pwpolicy-mod", POLICY, "--gracelimit", "0", ],
+ )
+
+ # Resetting the password will mark it as expired
+ self.reset_password(self.master)
+
+ # Now grace is done and binds should fail.
+ result = self.master.run_command(
+ ["ldapsearch", "-e", "ppolicy", "-D", dn,
+ "-w", PASSWORD, "-b", dn], raiseonerr=False
+ )
+ assert result.returncode == 49
+
+ assert 'Password is expired' in result.stderr_text
+ assert 'Password expired, 0 grace logins remain' in result.stderr_text
+
+ def test_graceperiod_disabled(self):
+ """Test the LDAP bind with grace period disabled (-1)"""
+ str(self.master.domain.basedn)
+ dn = "uid={user},cn=users,cn=accounts,{base_dn}".format(
+ user=USER, base_dn=str(self.master.domain.basedn))
+
+ # This can fail if gracelimit is already -1 so ignore it
+ self.master.run_command(
+ ["ipa", "pwpolicy-mod", POLICY, "--gracelimit", "-1",],
+ raiseonerr=False,
+ )
+
+ # Ensure the password is expired
+ self.reset_password(self.master)
+
+ result = self.kinit_as_user(self.master, PASSWORD, PASSWORD)
+
+ for _i in range(0, 10):
+ result = self.master.run_command(
+ ["ldapsearch", "-e", "ppolicy", "-D", dn,
+ "-w", PASSWORD, "-b", dn]
+ )
+
+ # With graceperiod disabled it should not increment
+ result = tasks.ldapsearch_dm(
+ self.master, dn, ['passwordgraceusertime',],
+ )
+ assert 'passwordgraceusertime: 0' in result.stdout_text.lower()
--
2.37.2

View File

@ -0,0 +1,230 @@
From 434620ee342ac4767beccec647a318bfa7743dfa Mon Sep 17 00:00:00 2001
From: Rob Crittenden <rcritten@redhat.com>
Date: Thu, 18 Aug 2022 08:21:58 -0400
Subject: [PATCH] doc: Update LDAP grace period design with default values
New group password policies will get -1 (unlimited) on creation
by default.
Existing group password policies will remain untouched and
those created prior will be treated as no BIND allowed.
Fixes: https://pagure.io/freeipa/issue/9212
Signed-off-by: Rob Crittenden <rcritten@redhat.com>
Reviewed-By: Florence Blanc-Renaud <frenaud@redhat.com>
---
doc/designs/ldap_grace_period.md | 17 ++++++++++++++++-
1 file changed, 16 insertions(+), 1 deletion(-)
diff --git a/doc/designs/ldap_grace_period.md b/doc/designs/ldap_grace_period.md
index 4b9db3424..e26aedda9 100644
--- a/doc/designs/ldap_grace_period.md
+++ b/doc/designs/ldap_grace_period.md
@@ -51,7 +51,22 @@ The basic flow is:
On successful password reset (by anyone) reset the user's passwordGraceUserTime to 0.
-The default value on install/upgrade will be -1 to retail existing behavior.
+Range values for passwordgracelimit are:
+
+-1 : password grace checking is disabled
+ 0 : no grace BIND are allowed at all post-expiration
+ 1..MAXINT: the number of BIND allowed post-expiration
+
+The default value for the global policy on install/upgrade will be -1 to
+retain existing behavior.
+
+New group password policies will default to -1 to retain previous
+behavior.
+
+Existing group policies with no grace limit set are updated to use
+the default unlimited value, -1. This is done because lack of value in
+LDAP is treated as 0 so any existing group policies would not allow
+post-expiration BIND so this will avoid confusion.
The per-user attempts will not be replicated.
--
2.37.2
From 497a57e7a6872fa30d1855a1d91a455bfdbf9300 Mon Sep 17 00:00:00 2001
From: Rob Crittenden <rcritten@redhat.com>
Date: Thu, 4 Aug 2022 12:04:22 -0400
Subject: [PATCH] Set default gracelimit on group password policies to -1
This will retain previous behavior of unlimited LDAP BIND
post-expiration.
Fixes: https://pagure.io/freeipa/issue/9212
Signed-off-by: Rob Crittenden <rcritten@redhat.com>
Reviewed-By: Florence Blanc-Renaud <frenaud@redhat.com>
---
API.txt | 2 +-
ipaserver/plugins/pwpolicy.py | 2 ++
ipatests/test_xmlrpc/test_pwpolicy_plugin.py | 2 ++
3 files changed, 5 insertions(+), 1 deletion(-)
diff --git a/API.txt b/API.txt
index 5ba9add13..d7ea74f08 100644
--- a/API.txt
+++ b/API.txt
@@ -4075,7 +4075,7 @@ option: Int('krbpwdlockoutduration?', cli_name='lockouttime')
option: Int('krbpwdmaxfailure?', cli_name='maxfail')
option: Int('krbpwdmindiffchars?', cli_name='minclasses')
option: Int('krbpwdminlength?', cli_name='minlength')
-option: Int('passwordgracelimit?', cli_name='gracelimit', default=-1)
+option: Int('passwordgracelimit?', autofill=True, cli_name='gracelimit', default=-1)
option: Flag('raw', autofill=True, cli_name='raw', default=False)
option: Str('setattr*', cli_name='setattr')
option: Str('version?')
diff --git a/ipaserver/plugins/pwpolicy.py b/ipaserver/plugins/pwpolicy.py
index 4428aede2..f4ebffd5c 100644
--- a/ipaserver/plugins/pwpolicy.py
+++ b/ipaserver/plugins/pwpolicy.py
@@ -408,6 +408,7 @@ class pwpolicy(LDAPObject):
minvalue=-1,
maxvalue=Int.MAX_UINT32,
default=-1,
+ autofill=True,
),
)
@@ -539,6 +540,7 @@ class pwpolicy_add(LDAPCreate):
keys[-1], krbpwdpolicyreference=dn,
cospriority=options.get('cospriority')
)
+
return dn
def post_callback(self, ldap, dn, entry_attrs, *keys, **options):
diff --git a/ipatests/test_xmlrpc/test_pwpolicy_plugin.py b/ipatests/test_xmlrpc/test_pwpolicy_plugin.py
index 8eee69c18..fc785223b 100644
--- a/ipatests/test_xmlrpc/test_pwpolicy_plugin.py
+++ b/ipatests/test_xmlrpc/test_pwpolicy_plugin.py
@@ -387,6 +387,7 @@ class test_pwpolicy_mod_cospriority(Declarative):
krbpwdhistorylength=[u'10'],
krbpwdmindiffchars=[u'3'],
krbpwdminlength=[u'8'],
+ passwordgracelimit=[u'-1'],
objectclass=objectclasses.pwpolicy,
),
summary=None,
@@ -417,6 +418,7 @@ class test_pwpolicy_mod_cospriority(Declarative):
krbpwdhistorylength=[u'10'],
krbpwdmindiffchars=[u'3'],
krbpwdminlength=[u'8'],
+ passwordgracelimit=[u'-1'],
),
summary=None,
value=u'ipausers',
--
2.37.2
From a4ddaaf3048c4e8d78a1807af7266ee40ab3a30b Mon Sep 17 00:00:00 2001
From: Rob Crittenden <rcritten@redhat.com>
Date: Thu, 4 Aug 2022 12:04:41 -0400
Subject: [PATCH] Set default on group pwpolicy with no grace limit in upgrade
If an existing group policy lacks a password grace limit
update it to -1 on upgrade.
Fixes: https://pagure.io/freeipa/issue/9212
Signed-off-by: Rob Crittenden <rcritten@redhat.com>
Reviewed-By: Florence Blanc-Renaud <frenaud@redhat.com>
---
.../updates/90-post_upgrade_plugins.update | 1 +
ipaserver/install/plugins/update_pwpolicy.py | 66 +++++++++++++++++++
2 files changed, 67 insertions(+)
diff --git a/install/updates/90-post_upgrade_plugins.update b/install/updates/90-post_upgrade_plugins.update
index c7ec71d49..6fe91aa6c 100644
--- a/install/updates/90-post_upgrade_plugins.update
+++ b/install/updates/90-post_upgrade_plugins.update
@@ -26,6 +26,7 @@ plugin: update_ra_cert_store
plugin: update_mapping_Guests_to_nobody
plugin: fix_kra_people_entry
plugin: update_pwpolicy
+plugin: update_pwpolicy_grace
# last
# DNS version 1
diff --git a/ipaserver/install/plugins/update_pwpolicy.py b/ipaserver/install/plugins/update_pwpolicy.py
index dca44ce43..4185f0343 100644
--- a/ipaserver/install/plugins/update_pwpolicy.py
+++ b/ipaserver/install/plugins/update_pwpolicy.py
@@ -78,3 +78,69 @@ class update_pwpolicy(Updater):
return False, []
return False, []
+
+
+@register()
+class update_pwpolicy_grace(Updater):
+ """
+ Ensure all group policies have a grace period set.
+ """
+
+ def execute(self, **options):
+ ldap = self.api.Backend.ldap2
+
+ base_dn = DN(('cn', self.api.env.realm), ('cn', 'kerberos'),
+ self.api.env.basedn)
+ search_filter = (
+ "(&(objectClass=krbpwdpolicy)(!(passwordgracelimit=*)))"
+ )
+
+ while True:
+ # Run the search in loop to avoid issues when LDAP limits are hit
+ # during update
+
+ try:
+ (entries, truncated) = ldap.find_entries(
+ search_filter, ['objectclass'], base_dn, time_limit=0,
+ size_limit=0)
+
+ except errors.EmptyResult:
+ logger.debug("update_pwpolicy: no policies without "
+ "passwordgracelimit set")
+ return False, []
+
+ except errors.ExecutionError as e:
+ logger.error("update_pwpolicy: cannot retrieve list "
+ "of policies missing passwordgracelimit: %s", e)
+ return False, []
+
+ logger.debug("update_pwpolicy: found %d "
+ "policies to update, truncated: %s",
+ len(entries), truncated)
+
+ error = False
+
+ for entry in entries:
+ # Set unlimited BIND by default
+ entry['passwordgracelimit'] = -1
+ try:
+ ldap.update_entry(entry)
+ except (errors.EmptyModlist, errors.NotFound):
+ pass
+ except errors.ExecutionError as e:
+ logger.debug("update_pwpolicy: cannot "
+ "update policy: %s", e)
+ error = True
+
+ if error:
+ # Exit loop to avoid infinite cycles
+ logger.error("update_pwpolicy: error(s) "
+ "detected during pwpolicy update")
+ return False, []
+
+ elif not truncated:
+ # All affected entries updated, exit the loop
+ logger.debug("update_pwpolicy: all policies updated")
+ return False, []
+
+ return False, []
--
2.37.2

View File

@ -0,0 +1,62 @@
From 109cd579e3b089b7fad4c92bf25594eba1af8a21 Mon Sep 17 00:00:00 2001
From: Alexander Bokovoy <abokovoy@redhat.com>
Date: Tue, 23 Aug 2022 16:58:07 +0300
Subject: [PATCH] fix canonicalization issue in Web UI
When Kerberos principal alias is used to login to a Web UI, we end up
with a request that is authenticated by a ticket issued in the alias
name but metadata processed for the canonical user name. This confuses
RPC layer of Web UI code and causes infinite loop to reload the page.
Fix it by doing two things:
- force use of canonicalization of an enterprise principal on server
side, not just specifying that the principal is an enterprise one;
- recognize that a principal in the whoami()-returned object can have
aliases and the principal returned by the server in the JSON response
may be one of those aliases.
Fixes: https://pagure.io/freeipa/issue/9226
Signed-off-by: Alexander Bokovoy <abokovoy@redhat.com>
Reviewed-By: Armando Neto <abiagion@redhat.com>
---
install/ui/src/freeipa/ipa.js | 8 +++++++-
ipaserver/rpcserver.py | 1 +
2 files changed, 8 insertions(+), 1 deletion(-)
diff --git a/install/ui/src/freeipa/ipa.js b/install/ui/src/freeipa/ipa.js
index 758db1b00..a08d632e9 100644
--- a/install/ui/src/freeipa/ipa.js
+++ b/install/ui/src/freeipa/ipa.js
@@ -271,7 +271,13 @@ var IPA = function () {
var cn = that.whoami.data.krbcanonicalname;
if (cn) that.principal = cn[0];
if (!that.principal) {
- that.principal = that.whoami.data.krbprincipalname[0];
+ var principal = data.principal;
+ var idx = that.whoami.data.krbprincipalname.indexOf(principal);
+ if (idx > -1) {
+ that.principal = principal;
+ } else {
+ that.principal = that.whoami.data.krbprincipalname[0];
+ }
}
} else if (entity === 'idoverrideuser') {
that.principal = that.whoami.data.ipaoriginaluid[0];
diff --git a/ipaserver/rpcserver.py b/ipaserver/rpcserver.py
index 1f85e9898..4e8a08b66 100644
--- a/ipaserver/rpcserver.py
+++ b/ipaserver/rpcserver.py
@@ -1109,6 +1109,7 @@ class login_password(Backend, KerberosSession):
ccache_name,
armor_ccache_name=armor_path,
enterprise=True,
+ canonicalize=True,
lifetime=self.api.env.kinit_lifetime)
if armor_path:
--
2.37.3

View File

@ -0,0 +1,473 @@
From 69413325158a3ea06d1491acd77ee6e0955ee89a Mon Sep 17 00:00:00 2001
From: Rob Crittenden <rcritten@redhat.com>
Date: Sep 26 2022 11:48:47 +0000
Subject: Defer creating the final krb5.conf on clients
A temporary krb5.conf is created early during client enrollment
and was previously used only during the initial ipa-join call.
The final krb5.conf was written soon afterward.
If there are multiple servers it is possible that the client
may then choose a different KDC to connect. If the client
is faster than replication then the client may not exist
on all servers and therefore enrollment will fail.
This was seen in performance testing of how many simultaneous
client enrollments are possible.
Use a decorator to wrap the _install() method to ensure the
temporary files created during installation are cleaned up.
https://pagure.io/freeipa/issue/9228
Signed-off-by: Rob Crittenden <rcritten@redhat.com>
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
---
diff --git a/ipaclient/install/client.py b/ipaclient/install/client.py
index 920c517..93bc740 100644
--- a/ipaclient/install/client.py
+++ b/ipaclient/install/client.py
@@ -101,6 +101,37 @@ cli_basedn = None
# end of global variables
+def cleanup(func):
+ def inner(options, tdict):
+ # Add some additional options which contain the temporary files
+ # needed during installation.
+ fd, krb_name = tempfile.mkstemp()
+ os.close(fd)
+ ccache_dir = tempfile.mkdtemp(prefix='krbcc')
+
+ tdict['krb_name'] = krb_name
+ tdict['ccache_dir'] = ccache_dir
+
+ func(options, tdict)
+
+ os.environ.pop('KRB5_CONFIG', None)
+
+ try:
+ os.remove(krb_name)
+ except OSError:
+ logger.error("Could not remove %s", krb_name)
+ try:
+ os.rmdir(ccache_dir)
+ except OSError:
+ pass
+ try:
+ os.remove(krb_name + ".ipabkp")
+ except OSError:
+ logger.error("Could not remove %s.ipabkp", krb_name)
+
+ return inner
+
+
def remove_file(filename):
"""
Deletes a file. If the file does not exist (OSError 2) does nothing.
@@ -2652,7 +2683,7 @@ def restore_time_sync(statestore, fstore):
def install(options):
try:
- _install(options)
+ _install(options, dict())
except ScriptError as e:
if e.rval == CLIENT_INSTALL_ERROR:
if options.force:
@@ -2679,7 +2710,8 @@ def install(options):
pass
-def _install(options):
+@cleanup
+def _install(options, tdict):
env = {'PATH': SECURE_PATH}
fstore = sysrestore.FileStore(paths.IPA_CLIENT_SYSRESTORE)
@@ -2687,6 +2719,9 @@ def _install(options):
statestore.backup_state('installation', 'complete', False)
+ krb_name = tdict['krb_name']
+ ccache_dir = tdict['ccache_dir']
+
if not options.on_master:
# Try removing old principals from the keytab
purge_host_keytab(cli_realm)
@@ -2719,182 +2754,162 @@ def _install(options):
host_principal = 'host/%s@%s' % (hostname, cli_realm)
if not options.on_master:
nolog = tuple()
- # First test out the kerberos configuration
- fd, krb_name = tempfile.mkstemp()
- os.close(fd)
- ccache_dir = tempfile.mkdtemp(prefix='krbcc')
- try:
- configure_krb5_conf(
- cli_realm=cli_realm,
- cli_domain=cli_domain,
- cli_server=cli_server,
- cli_kdc=cli_kdc,
- dnsok=False,
- filename=krb_name,
- client_domain=client_domain,
- client_hostname=hostname,
- configure_sssd=options.sssd,
- force=options.force)
- env['KRB5_CONFIG'] = krb_name
- ccache_name = os.path.join(ccache_dir, 'ccache')
- join_args = [
- paths.SBIN_IPA_JOIN,
- "-s", cli_server[0],
- "-b", str(realm_to_suffix(cli_realm)),
- "-h", hostname,
- "-k", paths.KRB5_KEYTAB
- ]
- if options.debug:
- join_args.append("-d")
- env['XMLRPC_TRACE_CURL'] = 'yes'
- if options.force_join:
- join_args.append("-f")
- if options.principal is not None:
- stdin = None
- principal = options.principal
- if principal.find('@') == -1:
- principal = '%s@%s' % (principal, cli_realm)
- if options.password is not None:
- stdin = options.password
+ configure_krb5_conf(
+ cli_realm=cli_realm,
+ cli_domain=cli_domain,
+ cli_server=cli_server,
+ cli_kdc=cli_kdc,
+ dnsok=False,
+ filename=krb_name,
+ client_domain=client_domain,
+ client_hostname=hostname,
+ configure_sssd=options.sssd,
+ force=options.force)
+ env['KRB5_CONFIG'] = krb_name
+ ccache_name = os.path.join(ccache_dir, 'ccache')
+ join_args = [
+ paths.SBIN_IPA_JOIN,
+ "-s", cli_server[0],
+ "-b", str(realm_to_suffix(cli_realm)),
+ "-h", hostname,
+ "-k", paths.KRB5_KEYTAB
+ ]
+ if options.debug:
+ join_args.append("-d")
+ env['XMLRPC_TRACE_CURL'] = 'yes'
+ if options.force_join:
+ join_args.append("-f")
+ if options.principal is not None:
+ stdin = None
+ principal = options.principal
+ if principal.find('@') == -1:
+ principal = '%s@%s' % (principal, cli_realm)
+ if options.password is not None:
+ stdin = options.password
+ else:
+ if not options.unattended:
+ try:
+ stdin = getpass.getpass(
+ "Password for %s: " % principal)
+ except EOFError:
+ stdin = None
+ if not stdin:
+ raise ScriptError(
+ "Password must be provided for {}.".format(
+ principal),
+ rval=CLIENT_INSTALL_ERROR)
else:
- if not options.unattended:
- try:
- stdin = getpass.getpass(
- "Password for %s: " % principal)
- except EOFError:
- stdin = None
- if not stdin:
- raise ScriptError(
- "Password must be provided for {}.".format(
- principal),
- rval=CLIENT_INSTALL_ERROR)
+ if sys.stdin.isatty():
+ logger.error(
+ "Password must be provided in "
+ "non-interactive mode.")
+ logger.info(
+ "This can be done via "
+ "echo password | ipa-client-install ... "
+ "or with the -w option.")
+ raise ScriptError(rval=CLIENT_INSTALL_ERROR)
else:
- if sys.stdin.isatty():
- logger.error(
- "Password must be provided in "
- "non-interactive mode.")
- logger.info(
- "This can be done via "
- "echo password | ipa-client-install ... "
- "or with the -w option.")
- raise ScriptError(rval=CLIENT_INSTALL_ERROR)
- else:
- stdin = sys.stdin.readline()
+ stdin = sys.stdin.readline()
+ try:
+ kinit_password(principal, stdin, ccache_name,
+ config=krb_name)
+ except RuntimeError as e:
+ print_port_conf_info()
+ raise ScriptError(
+ "Kerberos authentication failed: {}".format(e),
+ rval=CLIENT_INSTALL_ERROR)
+ elif options.keytab:
+ join_args.append("-f")
+ if os.path.exists(options.keytab):
try:
- kinit_password(principal, stdin, ccache_name,
- config=krb_name)
- except RuntimeError as e:
+ kinit_keytab(host_principal,
+ options.keytab,
+ ccache_name,
+ config=krb_name,
+ attempts=options.kinit_attempts)
+ except gssapi.exceptions.GSSError as e:
print_port_conf_info()
raise ScriptError(
"Kerberos authentication failed: {}".format(e),
rval=CLIENT_INSTALL_ERROR)
- elif options.keytab:
- join_args.append("-f")
- if os.path.exists(options.keytab):
- try:
- kinit_keytab(host_principal,
- options.keytab,
- ccache_name,
- config=krb_name,
- attempts=options.kinit_attempts)
- except gssapi.exceptions.GSSError as e:
- print_port_conf_info()
- raise ScriptError(
- "Kerberos authentication failed: {}".format(e),
- rval=CLIENT_INSTALL_ERROR)
- else:
- raise ScriptError(
- "Keytab file could not be found: {}".format(
- options.keytab),
- rval=CLIENT_INSTALL_ERROR)
- elif options.password:
- nolog = (options.password,)
- join_args.append("-w")
- join_args.append(options.password)
- elif options.prompt_password:
- if options.unattended:
- raise ScriptError(
- "Password must be provided in non-interactive mode",
- rval=CLIENT_INSTALL_ERROR)
- try:
- password = getpass.getpass("Password: ")
- except EOFError:
- password = None
- if not password:
- raise ScriptError(
- "Password must be provided.",
- rval=CLIENT_INSTALL_ERROR)
- join_args.append("-w")
- join_args.append(password)
- nolog = (password,)
-
- env['KRB5CCNAME'] = os.environ['KRB5CCNAME'] = ccache_name
- # Get the CA certificate
+ else:
+ raise ScriptError(
+ "Keytab file could not be found: {}".format(
+ options.keytab),
+ rval=CLIENT_INSTALL_ERROR)
+ elif options.password:
+ nolog = (options.password,)
+ join_args.append("-w")
+ join_args.append(options.password)
+ elif options.prompt_password:
+ if options.unattended:
+ raise ScriptError(
+ "Password must be provided in non-interactive mode",
+ rval=CLIENT_INSTALL_ERROR)
try:
- os.environ['KRB5_CONFIG'] = env['KRB5_CONFIG']
- get_ca_certs(fstore, options, cli_server[0], cli_basedn,
- cli_realm)
- del os.environ['KRB5_CONFIG']
- except errors.FileError as e:
- logger.error('%s', e)
- raise ScriptError(rval=CLIENT_INSTALL_ERROR)
- except Exception as e:
- logger.error("Cannot obtain CA certificate\n%s", e)
- raise ScriptError(rval=CLIENT_INSTALL_ERROR)
-
- # Now join the domain
- result = run(
- join_args, raiseonerr=False, env=env, nolog=nolog,
- capture_error=True)
- stderr = result.error_output
+ password = getpass.getpass("Password: ")
+ except EOFError:
+ password = None
+ if not password:
+ raise ScriptError(
+ "Password must be provided.",
+ rval=CLIENT_INSTALL_ERROR)
+ join_args.append("-w")
+ join_args.append(password)
+ nolog = (password,)
- if result.returncode != 0:
- logger.error("Joining realm failed: %s", stderr)
- if not options.force:
- if result.returncode == 13:
- logger.info(
- "Use --force-join option to override the host "
- "entry on the server and force client enrollment.")
- raise ScriptError(rval=CLIENT_INSTALL_ERROR)
- logger.info(
- "Use ipa-getkeytab to obtain a host "
- "principal for this server.")
- else:
- logger.info("Enrolled in IPA realm %s", cli_realm)
+ env['KRB5CCNAME'] = os.environ['KRB5CCNAME'] = ccache_name
+ # Get the CA certificate
+ try:
+ os.environ['KRB5_CONFIG'] = env['KRB5_CONFIG']
+ get_ca_certs(fstore, options, cli_server[0], cli_basedn,
+ cli_realm)
+ except errors.FileError as e:
+ logger.error('%s', e)
+ raise ScriptError(rval=CLIENT_INSTALL_ERROR)
+ except Exception as e:
+ logger.error("Cannot obtain CA certificate\n%s", e)
+ raise ScriptError(rval=CLIENT_INSTALL_ERROR)
- if options.principal is not None:
- run([paths.KDESTROY], raiseonerr=False, env=env)
+ # Now join the domain
+ result = run(
+ join_args, raiseonerr=False, env=env, nolog=nolog,
+ capture_error=True)
+ stderr = result.error_output
- # Obtain the TGT. We do it with the temporary krb5.conf, so that
- # only the KDC we're installing under is contacted.
- # Other KDCs might not have replicated the principal yet.
- # Once we have the TGT, it's usable on any server.
- try:
- kinit_keytab(host_principal, paths.KRB5_KEYTAB, CCACHE_FILE,
- config=krb_name,
- attempts=options.kinit_attempts)
- env['KRB5CCNAME'] = os.environ['KRB5CCNAME'] = CCACHE_FILE
- except gssapi.exceptions.GSSError as e:
- print_port_conf_info()
- logger.error("Failed to obtain host TGT: %s", e)
- # failure to get ticket makes it impossible to login and bind
- # from sssd to LDAP, abort installation and rollback changes
+ if result.returncode != 0:
+ logger.error("Joining realm failed: %s", stderr)
+ if not options.force:
+ if result.returncode == 13:
+ logger.info(
+ "Use --force-join option to override the host "
+ "entry on the server and force client enrollment.")
raise ScriptError(rval=CLIENT_INSTALL_ERROR)
+ logger.info(
+ "Use ipa-getkeytab to obtain a host "
+ "principal for this server.")
+ else:
+ logger.info("Enrolled in IPA realm %s", cli_realm)
- finally:
- try:
- os.remove(krb_name)
- except OSError:
- logger.error("Could not remove %s", krb_name)
- try:
- os.rmdir(ccache_dir)
- except OSError:
- pass
- try:
- os.remove(krb_name + ".ipabkp")
- except OSError:
- logger.error("Could not remove %s.ipabkp", krb_name)
+ if options.principal is not None:
+ run([paths.KDESTROY], raiseonerr=False, env=env)
+
+ # Obtain the TGT. We do it with the temporary krb5.conf, so that
+ # only the KDC we're installing under is contacted.
+ # Other KDCs might not have replicated the principal yet.
+ # Once we have the TGT, it's usable on any server.
+ try:
+ kinit_keytab(host_principal, paths.KRB5_KEYTAB, CCACHE_FILE,
+ config=krb_name,
+ attempts=options.kinit_attempts)
+ env['KRB5CCNAME'] = os.environ['KRB5CCNAME'] = CCACHE_FILE
+ except gssapi.exceptions.GSSError as e:
+ print_port_conf_info()
+ logger.error("Failed to obtain host TGT: %s", e)
+ # failure to get ticket makes it impossible to login and bind
+ # from sssd to LDAP, abort installation and rollback changes
+ raise ScriptError(rval=CLIENT_INSTALL_ERROR)
# Configure ipa.conf
if not options.on_master:
@@ -2931,23 +2946,6 @@ def _install(options):
except gssapi.exceptions.GSSError as e:
logger.error("Failed to obtain host TGT: %s", e)
raise ScriptError(rval=CLIENT_INSTALL_ERROR)
- else:
- # Configure krb5.conf
- fstore.backup_file(paths.KRB5_CONF)
- configure_krb5_conf(
- cli_realm=cli_realm,
- cli_domain=cli_domain,
- cli_server=cli_server,
- cli_kdc=cli_kdc,
- dnsok=dnsok,
- filename=paths.KRB5_CONF,
- client_domain=client_domain,
- client_hostname=hostname,
- configure_sssd=options.sssd,
- force=options.force)
-
- logger.info(
- "Configured /etc/krb5.conf for IPA realm %s", cli_realm)
# Clear out any current session keyring information
try:
@@ -3274,6 +3272,23 @@ def _install(options):
configure_nisdomain(
options=options, domain=cli_domain, statestore=statestore)
+ # Configure the final krb5.conf
+ if not options.on_master:
+ fstore.backup_file(paths.KRB5_CONF)
+ configure_krb5_conf(
+ cli_realm=cli_realm,
+ cli_domain=cli_domain,
+ cli_server=cli_server,
+ cli_kdc=cli_kdc,
+ dnsok=dnsok,
+ filename=paths.KRB5_CONF,
+ client_domain=client_domain,
+ client_hostname=hostname,
+ configure_sssd=options.sssd,
+ force=options.force)
+
+ logger.info("Configured /etc/krb5.conf for IPA realm %s", cli_realm)
+
statestore.delete_state('installation', 'complete')
statestore.backup_state('installation', 'complete', True)
logger.info('Client configuration complete.')

View File

@ -0,0 +1,123 @@
From c643e56e4c45b7cb61aa53989657143627c23e04 Mon Sep 17 00:00:00 2001
From: Francisco Trivino <ftrivino@redhat.com>
Date: Nov 22 2022 06:56:00 +0000
Subject: Vault: fix interoperability issues with older RHEL systems
AES-128-CBC was recently enabled as default wrapping algorithm for transport of secrets.
This change was done in favor of FIPS as crypto-policies disabled 3DES in RHEL9, but
setting AES as default ended-up breaking backwards compatibility with older RHEL systems.
This commit is tuning some defaults so that interoperability with older RHEL systems
works again. The new logic reflects:
- when an old client is calling a new server, it doesn't send any value for wrapping_algo
and the old value is used (3DES), so that the client can decrypt using 3DES.
- when a new client is calling a new server, it sends wrapping_algo = AES128_CBC
- when a new client is calling an old server, it doesn't send any value and the default is
to use 3DES.
Finally, as this logic is able to handle overlapping wrapping algorithm between server and
client, the Option "--wrapping-algo" is hidden from "ipa vault-archive --help" and "ipa
vault-retrieve --help" commands.
Fixes: https://pagure.io/freeipa/issue/9259
Signed-off-by: Francisco Trivino <ftrivino@redhat.com>
Reviewed-By: Florence Blanc-Renaud <frenaud@redhat.com>
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
---
diff --git a/API.txt b/API.txt
index 9892211..2bd1cc2 100644
--- a/API.txt
+++ b/API.txt
@@ -6666,7 +6666,7 @@ option: Flag('shared?', autofill=True, default=False)
option: Str('username?', cli_name='user')
option: Bytes('vault_data')
option: Str('version?')
-option: StrEnum('wrapping_algo?', autofill=True, default=u'aes-128-cbc', values=[u'aes-128-cbc', u'des-ede3-cbc'])
+option: StrEnum('wrapping_algo?', autofill=True, default=u'des-ede3-cbc', values=[u'aes-128-cbc', u'des-ede3-cbc'])
output: Entry('result')
output: Output('summary', type=[<type 'unicode'>, <type 'NoneType'>])
output: PrimaryKey('value')
@@ -6766,7 +6766,7 @@ option: Bytes('session_key')
option: Flag('shared?', autofill=True, default=False)
option: Str('username?', cli_name='user')
option: Str('version?')
-option: StrEnum('wrapping_algo?', autofill=True, default=u'aes-128-cbc', values=[u'aes-128-cbc', u'des-ede3-cbc'])
+option: StrEnum('wrapping_algo?', autofill=True, default=u'des-ede3-cbc', values=[u'aes-128-cbc', u'des-ede3-cbc'])
output: Entry('result')
output: Output('summary', type=[<type 'unicode'>, <type 'NoneType'>])
output: PrimaryKey('value')
diff --git a/VERSION.m4 b/VERSION.m4
index 7d60b01..b4b1774 100644
--- a/VERSION.m4
+++ b/VERSION.m4
@@ -86,8 +86,8 @@ define(IPA_DATA_VERSION, 20100614120000)
# #
########################################################
define(IPA_API_VERSION_MAJOR, 2)
-# Last change: add graceperiodlimit
-define(IPA_API_VERSION_MINOR, 248)
+# Last change: fix vault interoperability issues.
+define(IPA_API_VERSION_MINOR, 251)
########################################################
# Following values are auto-generated from values above
diff --git a/ipaclient/plugins/vault.py b/ipaclient/plugins/vault.py
index 115171c..d4c84eb 100644
--- a/ipaclient/plugins/vault.py
+++ b/ipaclient/plugins/vault.py
@@ -687,7 +687,7 @@ class ModVaultData(Local):
default_algo = config.get('wrapping_default_algorithm')
if default_algo is None:
# old server
- wrapping_algo = constants.VAULT_WRAPPING_AES128_CBC
+ wrapping_algo = constants.VAULT_WRAPPING_3DES
elif default_algo in constants.VAULT_WRAPPING_SUPPORTED_ALGOS:
# try to use server default
wrapping_algo = default_algo
@@ -801,7 +801,8 @@ class vault_archive(ModVaultData):
if option.name not in ('nonce',
'session_key',
'vault_data',
- 'version'):
+ 'version',
+ 'wrapping_algo'):
yield option
for option in super(vault_archive, self).get_options():
yield option
@@ -1053,7 +1054,7 @@ class vault_retrieve(ModVaultData):
def get_options(self):
for option in self.api.Command.vault_retrieve_internal.options():
- if option.name not in ('session_key', 'version'):
+ if option.name not in ('session_key', 'version', 'wrapping_algo'):
yield option
for option in super(vault_retrieve, self).get_options():
yield option
diff --git a/ipaserver/plugins/vault.py b/ipaserver/plugins/vault.py
index 4d40f66..574c83a 100644
--- a/ipaserver/plugins/vault.py
+++ b/ipaserver/plugins/vault.py
@@ -1051,7 +1051,7 @@ class vault_archive_internal(PKQuery):
'wrapping_algo?',
doc=_('Key wrapping algorithm'),
values=VAULT_WRAPPING_SUPPORTED_ALGOS,
- default=VAULT_WRAPPING_DEFAULT_ALGO,
+ default=VAULT_WRAPPING_3DES,
autofill=True,
),
)
@@ -1130,7 +1130,7 @@ class vault_retrieve_internal(PKQuery):
'wrapping_algo?',
doc=_('Key wrapping algorithm'),
values=VAULT_WRAPPING_SUPPORTED_ALGOS,
- default=VAULT_WRAPPING_DEFAULT_ALGO,
+ default=VAULT_WRAPPING_3DES,
autofill=True,
),
)

View File

@ -191,7 +191,7 @@
Name: %{package_name}
Version: %{IPA_VERSION}
Release: 3%{?rc_version:.%rc_version}%{?dist}
Release: 9%{?rc_version:.%rc_version}%{?dist}
Summary: The Identity, Policy and Audit system
License: GPLv3+
@ -215,6 +215,15 @@ Patch0001: 0001-ipa-otpd-Fix-build-on-older-versions-of-gcc.patch
Patch0002: 0002-webui-IdP-Remove-arrow-notation-due-to-uglify-js-lim.patch
Patch0003: 0003-Preserve-user-fix-the-confusing-summary_rhbz#2022028.patch
Patch0004: 0004-Only-calculate-LDAP-password-grace-when-the-password_rhbz#782917.patch
Patch0005: 0005-Add-end-to-end-integration-tests-for-external-IdP.patch
Patch0006: 0006-webui-Do-not-allow-empty-pagination-size_rhbz#2094672.patch
Patch0007: 0007-webui-Allow-grace-login-limit_rhbz#2109243.patch
Patch0008: 0008-check_repl_update-in-progress-is-a-boolean_rhbz#2117303.patch
Patch0009: 0009-Disabling-gracelimit-does-not-prevent-LDAP-binds_rhbz#2109236.patch
Patch0010: 0010-Set-passwordgracelimit-to-match-global-policy-on-group-pw-policies_rhbz#2115475.patch
Patch0011: 0011-fix-canonicalization-issue-in-Web-UI_rhbz#2133050.patch
Patch0012: 0012-Defer-creating-the-final-krb5-conf-on-clients_rhbz#2150246.patch
Patch0013: 0013-Vault-fix-interoperability-issues-with-older-RHEL-systems_rhbz#2148255.patch
Patch1001: 1001-Change-branding-to-IPA-and-Identity-Management.patch
Patch1002: 1002-Revert-freeipa.spec-depend-on-bind-dnssec-utils.patch
%endif
@ -1712,11 +1721,43 @@ fi
%if %{with selinux}
%files selinux
%{_datadir}/selinux/packages/%{selinuxtype}/%{modulename}.pp.*
%ghost %{_sharedstatedir}/selinux/%{selinuxtype}/active/modules/200/%{modulename}
%ghost %verify(not md5 size mode mtime) %{_sharedstatedir}/selinux/%{selinuxtype}/active/modules/200/%{modulename}
# with selinux
%endif
%changelog
* Mon Dec 5 2022 Rafael Jeffman <rjeffman@redhat.com> - 4.9.10-9
- Exclude installed policy module file from RPM verification
Resolves: RHBZ#2150243
* Fri Dec 2 2022 Rafael Jeffman <rjeffman@redhat.com> - 4.9.10-8
- Defer creating the final krb5.conf on clients
Resolves: RHBZ#2150246
- Vault: fix interoperability issues with older RHEL systems
Resolves: RHBZ#2148255
* Thu Oct 13 2022 Rafael Jeffman <rjeffman@redhat.com> - 4.9.10-7
- Fix canonicalization issue in Web UI
Resolves: RHBZ#2133050
* Mon Aug 22 2022 Rafael Jeffman <rjeffman@redhat.com> - 4.9.10-6
- webui: Allow grace login limit
Resolves: RHBZ#2109243
- check_repl_update: in progress is a boolean
Resolves: RHBZ#2117303
- Disabling gracelimit does not prevent LDAP binds
Resolves: RHBZ#2109236
- Set passwordgracelimit to match global policy on group pw policies
Resolves: RHBZ#2115475
* Tue Jul 19 2022 Rafael Jeffman <rjeffman@redhat.com> - 4.9.10-5
- webui: Do not allow empty pagination size
Resolves: RHBZ#2094672
* Tue Jul 12 2022 Rafael Jeffman <rjeffman@redhat.com> - 4.9.10-4
- Add end to end integration tests for external IdP
Resolves: RHBZ#2106346
* Thu Jul 07 2022 Rafael Jeffman <rjeffman@redhat.com> - 4.9.10-3
- Add explicit dependency for libvert-libev
Resolves: RHBZ#2104929