ipa-4.13.0-1

- Resolves: RHEL-134542 Add modern WebUI as submodule and enable routing in Apache
- Resolves: RHEL-134540 Switch IPA to use the PKI python API directly rather than RPC calls
- Resolves: RHEL-134196 After upgrade from 9.7 to 9.8 ipactl restart fails to restart winbind service
- Resolves: RHEL-132334 Include latest fixes in python3-ipatests package
- Resolves: RHEL-129224 Fix ipatests for kdcproxy after CVE-2025-59088 fix
- Resolves: RHEL-126974 Minor typo in ipa_idrange_fix.py
- Resolves: RHEL-120954 Rebase ipa to latest 4.13.x version for RHEL 9.8

Signed-off-by: Florence Blanc-Renaud <flo@redhat.com>
This commit is contained in:
Florence Blanc-Renaud 2025-12-09 15:24:57 +01:00
parent 445b79154e
commit 7b095b326d
148 changed files with 1463 additions and 24817 deletions

2
.gitignore vendored
View File

@ -126,3 +126,5 @@
/freeipa-4.12.0.tar.gz.asc
/freeipa-4.12.2.tar.gz
/freeipa-4.12.2.tar.gz.asc
/freeipa-4.13.0.tar.gz
/freeipa-4.13.0.tar.gz.asc

View File

@ -45,9 +45,9 @@ index 6803de752bc122bf6e1eafd610d399cde994cad5..a532fe85c34a523519187b6fd1297c19
Requires: python3-netaddr >= %{python_netaddr_version}
-Requires: python3-ifaddr
+Requires: python3-netifaces >= 0.10.4
Requires: python3-packaging
Requires: python3-pyasn1 >= 0.3.2-2
Requires: python3-pyasn1-modules >= 0.3.2-2
Requires: python3-pyusb
diff --git a/ipaclient/install/client.py b/ipaclient/install/client.py
index 29aff5f413e7f27136e236382031171f068284c5..263dc8e302cccd76412c294e0740af9744a2c62d 100644
--- a/ipaclient/install/client.py

View File

@ -0,0 +1,890 @@
From 941d6a54e163ca0d5eee6ac391c0d2f5afb6cf55 Mon Sep 17 00:00:00 2001
From: Florence Blanc-Renaud <flo@redhat.com>
Date: Tue, 9 Dec 2025 10:27:12 +0100
Subject: [PATCH] Revert "Remove NIS server support"
This reverts commit e98a80827bcc42dc16b516355077fed844220107.
---
freeipa.spec.in | 2 +
install/share/Makefile.am | 2 +
install/share/nis-update.uldif | 38 ++++
install/share/nis.uldif | 96 ++++++++++
install/tools/Makefile.am | 2 +
install/tools/ipa-compat-manage.in | 17 +-
install/tools/ipa-nis-manage.in | 205 +++++++++++++++++++++
install/tools/man/Makefile.am | 1 +
install/tools/man/ipa-nis-manage.1 | 51 +++++
install/updates/10-enable-betxn.update | 3 +
install/updates/50-nis.update | 3 +
install/updates/Makefile.am | 1 +
ipaplatform/base/paths.py | 2 +
ipaserver/install/ipa_migrate.py | 27 ++-
ipaserver/install/ipa_migrate_constants.py | 24 +++
ipaserver/install/plugins/update_nis.py | 92 +++++++++
ipatests/test_cmdline/test_cli.py | 1 +
ipatests/test_integration/test_commands.py | 87 +++++++++
18 files changed, 638 insertions(+), 16 deletions(-)
create mode 100644 install/share/nis-update.uldif
create mode 100644 install/share/nis.uldif
create mode 100644 install/tools/ipa-nis-manage.in
create mode 100644 install/tools/man/ipa-nis-manage.1
create mode 100644 install/updates/50-nis.update
create mode 100644 ipaserver/install/plugins/update_nis.py
diff --git a/freeipa.spec.in b/freeipa.spec.in
index b93199a5783a89e0ff58affd3d416556f72dd00c..6ee0f43f059dad4bba2288b1b0d3a63437371861 100644
--- a/freeipa.spec.in
+++ b/freeipa.spec.in
@@ -1619,6 +1619,7 @@ fi
%{_sbindir}/ipa-ldap-updater
%{_sbindir}/ipa-otptoken-import
%{_sbindir}/ipa-compat-manage
+%{_sbindir}/ipa-nis-manage
%{_sbindir}/ipa-managed-entries
%{_sbindir}/ipactl
%{_sbindir}/ipa-advise
@@ -1694,6 +1695,7 @@ fi
%{_mandir}/man1/ipa-ca-install.1*
%{_mandir}/man1/ipa-kra-install.1*
%{_mandir}/man1/ipa-compat-manage.1*
+%{_mandir}/man1/ipa-nis-manage.1*
%{_mandir}/man1/ipa-managed-entries.1*
%{_mandir}/man1/ipa-ldap-updater.1*
%{_mandir}/man8/ipactl.8*
diff --git a/install/share/Makefile.am b/install/share/Makefile.am
index bb2eb7531b083e36827ba1ee111e39c29589acaa..87578d29644f897622f46a56c06cb24c7df3efe6 100644
--- a/install/share/Makefile.am
+++ b/install/share/Makefile.am
@@ -66,6 +66,8 @@ dist_app_DATA = \
master-entry.ldif \
memberof-task.ldif \
memberof-conf.ldif \
+ nis.uldif \
+ nis-update.uldif \
opendnssec_conf.template \
opendnssec_kasp.template \
unique-attributes.ldif \
diff --git a/install/share/nis-update.uldif b/install/share/nis-update.uldif
new file mode 100644
index 0000000000000000000000000000000000000000..e602c1de061fbcece349b2d86970c4db5051473b
--- /dev/null
+++ b/install/share/nis-update.uldif
@@ -0,0 +1,38 @@
+# Updates for NIS
+
+# Correct syntax error that caused users to not appear
+dn: nis-domain=$DOMAIN+nis-map=netgroup, cn=NIS Server, cn=plugins, cn=config
+replace:nis-value-format: %merge(" ","%{memberNisNetgroup}","(%link(\"%ifeq(\\\"hostCategory\\\",\\\"all\\\",\\\"\\\",\\\"%collect(\\\\\\\"%{externalHost}\\\\\\\",\\\\\\\"%deref(\\\\\\\\\\\\\\\"memberHost\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"fqdn\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"fqdn\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"memberHost\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"fqdn\\\\\\\\\\\\\\\")\\\\\\\")\\\")\",\"-\",\",\",\"%ifeq(\\\"userCategory\\\",\\\"all\\\",\\\"\\\",\\\"%collect(\\\"%deref(\\\\\\\"memberUser\\\\\\\",\\\\\\\"uid\\\\\\\")\\\",\\\"%deref_r(\\\\\\\"member\\\\\\\",\\\\\\\"uid\\\\\\\")\\\",\\\"%deref_r(\\\\\\\"memberUser\\\\\\\",\\\\\\\"member\\\\\\\",\\\\\\\"uid\\\\\\\")\\\")\\\")\",\"-\"),%{nisDomainName:-})")::%merge(" ","%{memberNisNetgroup}","(%link(\"%ifeq(\\\"hostCategory\\\",\\\"all\\\",\\\"\\\",\\\"%collect(\\\\\\\"%{externalHost}\\\\\\\",\\\\\\\"%deref(\\\\\\\\\\\\\\\"memberHost\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"fqdn\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"fqdn\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"memberHost\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"fqdn\\\\\\\\\\\\\\\")\\\\\\\")\\\")\",\"-\",\",\",\"%ifeq(\\\"userCategory\\\",\\\"all\\\",\\\"\\\",\\\"%collect(\\\\\\\"%deref(\\\\\\\\\\\\\\\"memberUser\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"uid\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"uid\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"memberUser\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"uid\\\\\\\\\\\\\\\")\\\\\\\")\\\")\",\"-\"),%{nisDomainName:-})")
+
+# Correct syntax error that caused nested netgroups to not work
+# https://bugzilla.redhat.com/show_bug.cgi?id=788625
+dn: nis-domain=$DOMAIN+nis-map=netgroup, cn=NIS Server, cn=plugins, cn=config
+replace:nis-value-format: %merge(" ","%{memberNisNetgroup}","(%link(\"%ifeq(\\\"hostCategory\\\",\\\"all\\\",\\\"\\\",\\\"%collect(\\\\\\\"%{externalHost}\\\\\\\",\\\\\\\"%deref(\\\\\\\\\\\\\\\"memberHost\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"fqdn\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"fqdn\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"memberHost\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"fqdn\\\\\\\\\\\\\\\")\\\\\\\")\\\")\",\"-\",\",\",\"%ifeq(\\\"userCategory\\\",\\\"all\\\",\\\"\\\",\\\"%collect(\\\\\\\"%deref(\\\\\\\\\\\\\\\"memberUser\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"uid\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"uid\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"memberUser\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"uid\\\\\\\\\\\\\\\")\\\\\\\")\\\")\",\"-\"),%{nisDomainName:-})")::%merge(" ","%deref_f(\"member\",\"(objectclass=ipanisNetgroup)\",\"cn\")","(%link(\"%ifeq(\\\"hostCategory\\\",\\\"all\\\",\\\"\\\",\\\"%collect(\\\\\\\"%{externalHost}\\\\\\\",\\\\\\\"%deref(\\\\\\\\\\\\\\\"memberHost\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"fqdn\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"fqdn\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"memberHost\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"fqdn\\\\\\\\\\\\\\\")\\\\\\\")\\\")\",\"-\",\",\",\"%ifeq(\\\"userCategory\\\",\\\"all\\\",\\\"\\\",\\\"%collect(\\\\\\\"%deref(\\\\\\\\\\\\\\\"memberUser\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"uid\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"uid\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"memberUser\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"uid\\\\\\\\\\\\\\\")\\\\\\\")\\\")\",\"-\"),%{nisDomainName:-})")
+
+# Make the padding an expression so usercat and hostcat always gets
+# evaluated when displaying entries.
+# https://bugzilla.redhat.com/show_bug.cgi?id=767372
+dn: nis-domain=$DOMAIN+nis-map=netgroup, cn=NIS Server, cn=plugins, cn=config
+replace:nis-value-format: %merge(" ","%deref_f(\"member\",\"(objectclass=ipanisNetgroup)\",\"cn\")","(%link(\"%ifeq(\\\"hostCategory\\\",\\\"all\\\",\\\"\\\",\\\"%collect(\\\\\\\"%{externalHost}\\\\\\\",\\\\\\\"%deref(\\\\\\\\\\\\\\\"memberHost\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"fqdn\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"fqdn\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"memberHost\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"fqdn\\\\\\\\\\\\\\\")\\\\\\\")\\\")\",\"-\",\",\",\"%ifeq(\\\"userCategory\\\",\\\"all\\\",\\\"\\\",\\\"%collect(\\\\\\\"%deref(\\\\\\\\\\\\\\\"memberUser\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"uid\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"uid\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"memberUser\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"uid\\\\\\\\\\\\\\\")\\\\\\\")\\\")\",\"-\"),%{nisDomainName:-})")::%merge(" ","%deref_f(\"member\",\"(objectclass=ipanisNetgroup)\",\"cn\")","(%link(\"%ifeq(\\\"hostCategory\\\",\\\"all\\\",\\\"\\\",\\\"%collect(\\\\\\\"%{externalHost}\\\\\\\",\\\\\\\"%deref(\\\\\\\\\\\\\\\"memberHost\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"fqdn\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"fqdn\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"memberHost\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"fqdn\\\\\\\\\\\\\\\")\\\\\\\")\\\")\",\"%ifeq(\\\"hostCategory\\\",\\\"all\\\",\\\"\\\",\\\"-\\\")\",\",\",\"%ifeq(\\\"userCategory\\\",\\\"all\\\",\\\"\\\",\\\"%collect(\\\\\\\"%deref(\\\\\\\\\\\\\\\"memberUser\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"uid\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"uid\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"memberUser\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"uid\\\\\\\\\\\\\\\")\\\\\\\")\\\")\",\"%ifeq(\\\"userCategory\\\",\\\"all\\\",\\\"\\\",\\\"-\\\")\"),%{nisDomainName:-})")
+
+dn: nis-domain=$DOMAIN+nis-map=ethers.byaddr, cn=NIS Server, cn=plugins, cn=config
+default:objectclass: top
+default:objectclass: extensibleObject
+default:nis-domain: $DOMAIN
+default:nis-map: ethers.byaddr
+default:nis-base: cn=computers, cn=accounts, $SUFFIX
+default:nis-filter: (&(macAddress=*)(fqdn=*)(objectClass=ipaHost))
+default:nis-keys-format: %mregsub("%{macAddress} %{fqdn}","(..)[:\\\|-](..)[:\\\|-](..)[:\\\|-](..)[:\\\|-](..)[:\\\|-](..) (.*)","%1:%2:%3:%4:%5:%6")
+default:nis-values-format: %mregsub("%{macAddress} %{fqdn}","(..)[:\\\|-](..)[:\\\|-](..)[:\\\|-](..)[:\\\|-](..)[:\\\|-](..) (.*)","%1:%2:%3:%4:%5:%6 %7")
+default:nis-secure: no
+
+dn: nis-domain=$DOMAIN+nis-map=ethers.byname, cn=NIS Server, cn=plugins, cn=config
+default:objectclass: top
+default:objectclass: extensibleObject
+default:nis-domain: $DOMAIN
+default:nis-map: ethers.byname
+default:nis-base: cn=computers, cn=accounts, $SUFFIX
+default:nis-filter: (&(macAddress=*)(fqdn=*)(objectClass=ipaHost))
+default:nis-keys-format: %mregsub("%{macAddress} %{fqdn}","(..)[:\\\|-](..)[:\\\|-](..)[:\\\|-](..)[:\\\|-](..)[:\\\|-](..) (.*)","%7")
+default:nis-values-format: %mregsub("%{macAddress} %{fqdn}","(..)[:\\\|-](..)[:\\\|-](..)[:\\\|-](..)[:\\\|-](..)[:\\\|-](..) (.*)","%1:%2:%3:%4:%5:%6 %7")
+default:nis-secure: no
diff --git a/install/share/nis.uldif b/install/share/nis.uldif
new file mode 100644
index 0000000000000000000000000000000000000000..1735fb55299d28ab40d864b24062309fca43241f
--- /dev/null
+++ b/install/share/nis.uldif
@@ -0,0 +1,96 @@
+dn: cn=NIS Server, cn=plugins, cn=config
+default:objectclass: top
+default:objectclass: nsSlapdPlugin
+default:objectclass: extensibleObject
+default:cn: NIS Server
+default:nsslapd-pluginpath: /usr/lib$LIBARCH/dirsrv/plugins/nisserver-plugin.so
+default:nsslapd-plugininitfunc: nis_plugin_init
+default:nsslapd-plugintype: object
+default:nsslapd-pluginbetxn: on
+default:nsslapd-pluginenabled: on
+default:nsslapd-pluginid: nis-server
+default:nsslapd-pluginversion: 0.10
+default:nsslapd-pluginvendor: redhat.com
+default:nsslapd-plugindescription: NIS Server Plugin
+default:nis-tcp-wrappers-name: nis-server
+
+dn: nis-domain=$DOMAIN+nis-map=passwd.byname, cn=NIS Server, cn=plugins, cn=config
+default:objectclass: top
+default:objectclass: extensibleObject
+default:nis-domain: $DOMAIN
+default:nis-map: passwd.byname
+default:nis-base: cn=users, cn=accounts, $SUFFIX
+default:nis-secure: no
+
+dn: nis-domain=$DOMAIN+nis-map=passwd.byuid, cn=NIS Server, cn=plugins, cn=config
+default:objectclass: top
+default:objectclass: extensibleObject
+default:nis-domain: $DOMAIN
+default:nis-map: passwd.byuid
+default:nis-base: cn=users, cn=accounts, $SUFFIX
+default:nis-secure: no
+
+dn: nis-domain=$DOMAIN+nis-map=group.byname, cn=NIS Server, cn=plugins, cn=config
+default:objectclass: top
+default:objectclass: extensibleObject
+default:nis-domain: $DOMAIN
+default:nis-map: group.byname
+default:nis-base: cn=groups, cn=accounts, $SUFFIX
+default:nis-secure: no
+
+dn: nis-domain=$DOMAIN+nis-map=group.bygid, cn=NIS Server, cn=plugins, cn=config
+default:objectclass: top
+default:objectclass: extensibleObject
+default:nis-domain: $DOMAIN
+default:nis-map: group.bygid
+default:nis-base: cn=groups, cn=accounts, $SUFFIX
+default:nis-secure: no
+
+dn: nis-domain=$DOMAIN+nis-map=netid.byname, cn=NIS Server, cn=plugins, cn=config
+default:objectclass: top
+default:objectclass: extensibleObject
+default:nis-domain: $DOMAIN
+default:nis-map: netid.byname
+default:nis-base: cn=users, cn=accounts, $SUFFIX
+default:nis-secure: no
+
+# Note that the escapes in this entry can be quite confusing. The trick
+# is that each level of nesting requires (2^n) - 1 escapes. So the
+# first level is \", the second is \\\", the third is \\\\\\\", etc.
+# (1, 3, 7, 15, more than that and you'll go insane)
+
+# Note that this configuration mirrors the Schema Compat configuration for
+# triples.
+dn: nis-domain=$DOMAIN+nis-map=netgroup, cn=NIS Server, cn=plugins, cn=config
+default:objectclass: top
+default:objectclass: extensibleObject
+default:nis-domain: $DOMAIN
+default:nis-map: netgroup
+default:nis-base: cn=ng, cn=alt, $SUFFIX
+default:nis-filter: (objectClass=ipanisNetgroup)
+default:nis-key-format: %{cn}
+default:nis-value-format:%merge(" ","%deref_f(\"member\",\"(objectclass=ipanisNetgroup)\",\"cn\")","(%link(\"%ifeq(\\\"hostCategory\\\",\\\"all\\\",\\\"\\\",\\\"%collect(\\\\\\\"%{externalHost}\\\\\\\",\\\\\\\"%deref(\\\\\\\\\\\\\\\"memberHost\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"fqdn\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"fqdn\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"memberHost\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"fqdn\\\\\\\\\\\\\\\")\\\\\\\")\\\")\",\"%ifeq(\\\"hostCategory\\\",\\\"all\\\",\\\"\\\",\\\"-\\\")\",\",\",\"%ifeq(\\\"userCategory\\\",\\\"all\\\",\\\"\\\",\\\"%collect(\\\\\\\"%deref(\\\\\\\\\\\\\\\"memberUser\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"uid\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"uid\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"memberUser\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"uid\\\\\\\\\\\\\\\")\\\\\\\")\\\")\",\"%ifeq(\\\"userCategory\\\",\\\"all\\\",\\\"\\\",\\\"-\\\")\"),%{nisDomainName:-})")
+default:nis-secure: no
+
+dn: nis-domain=$DOMAIN+nis-map=ethers.byaddr, cn=NIS Server, cn=plugins, cn=config
+default:objectclass: top
+default:objectclass: extensibleObject
+default:nis-domain: $DOMAIN
+default:nis-map: ethers.byaddr
+default:nis-base: cn=computers, cn=accounts, $SUFFIX
+default:nis-filter: (&(macAddress=*)(fqdn=*)(objectClass=ipaHost))
+default:nis-keys-format: %mregsub("%{macAddress} %{fqdn}","(..)[:\\\|-](..)[:\\\|-](..)[:\\\|-](..)[:\\\|-](..)[:\\\|-](..) (.*)","%1:%2:%3:%4:%5:%6")
+default:nis-values-format: %mregsub("%{macAddress} %{fqdn}","(..)[:\\\|-](..)[:\\\|-](..)[:\\\|-](..)[:\\\|-](..)[:\\\|-](..) (.*)","%1:%2:%3:%4:%5:%6 %7")
+default:nis-secure: no
+
+dn: nis-domain=$DOMAIN+nis-map=ethers.byname, cn=NIS Server, cn=plugins, cn=config
+default:objectclass: top
+default:objectclass: extensibleObject
+default:nis-domain: $DOMAIN
+default:nis-map: ethers.byname
+default:nis-base: cn=computers, cn=accounts, $SUFFIX
+default:nis-filter: (&(macAddress=*)(fqdn=*)(objectClass=ipaHost))
+default:nis-keys-format: %mregsub("%{macAddress} %{fqdn}","(..)[:\\\|-](..)[:\\\|-](..)[:\\\|-](..)[:\\\|-](..)[:\\\|-](..) (.*)","%7")
+default:nis-values-format: %mregsub("%{macAddress} %{fqdn}","(..)[:\\\|-](..)[:\\\|-](..)[:\\\|-](..)[:\\\|-](..)[:\\\|-](..) (.*)","%1:%2:%3:%4:%5:%6 %7")
+default:nis-secure: no
+
diff --git a/install/tools/Makefile.am b/install/tools/Makefile.am
index 74cd11c67dd3af623dd29802935ae63fe82fcecb..ca484ec37969c9c06ae7b408b55fa30cd4e8e4fe 100644
--- a/install/tools/Makefile.am
+++ b/install/tools/Makefile.am
@@ -19,6 +19,7 @@ dist_noinst_DATA = \
ipa-server-upgrade.in \
ipactl.in \
ipa-compat-manage.in \
+ ipa-nis-manage.in \
ipa-managed-entries.in \
ipa-ldap-updater.in \
ipa-otptoken-import.in \
@@ -56,6 +57,7 @@ nodist_sbin_SCRIPTS = \
ipa-server-upgrade \
ipactl \
ipa-compat-manage \
+ ipa-nis-manage \
ipa-managed-entries \
ipa-ldap-updater \
ipa-otptoken-import \
diff --git a/install/tools/ipa-compat-manage.in b/install/tools/ipa-compat-manage.in
index fb25c22edd5b2fac123144846ffc232cb5bbecc7..9650abd6f83ebc0a8ef347fee83989d4e9f13f09 100644
--- a/install/tools/ipa-compat-manage.in
+++ b/install/tools/ipa-compat-manage.in
@@ -25,7 +25,6 @@ import sys
from ipaplatform.paths import paths
try:
from ipapython import ipautil, config
- from ipapython.ipaldap import realm_to_serverid
from ipaserver.install import installutils
from ipaserver.install.ldapupdate import LDAPUpdate
from ipalib import api, errors
@@ -153,19 +152,9 @@ def main():
try:
entry = get_entry(nis_config_dn)
# We can't disable schema compat if the NIS plugin is enabled
- if (
- entry is not None
- and entry.get("nsslapd-pluginenabled", [""])[0].lower() == "on"
- ):
- instance = realm_to_serverid(api.env.realm)
- print(
- "The NIS plugin is configured, cannot "
- "disable compatibility.", file=sys.stderr,
- )
- print(
- f"Run \"dsconf {instance} plugin set --enabled off "
- "'NIS Server'\" first.", file=sys.stderr,
- )
+ if entry is not None and entry.get('nsslapd-pluginenabled', [''])[0].lower() == 'on':
+ print("The NIS plugin is configured, cannot disable compatibility.", file=sys.stderr)
+ print("Run 'ipa-nis-manage disable' first.", file=sys.stderr)
retval = 2
except errors.ExecutionError as lde:
print("An error occurred while talking to the server.")
diff --git a/install/tools/ipa-nis-manage.in b/install/tools/ipa-nis-manage.in
new file mode 100644
index 0000000000000000000000000000000000000000..6b156ce6a80d05d9cd2d7cab48e6dba08b0954f5
--- /dev/null
+++ b/install/tools/ipa-nis-manage.in
@@ -0,0 +1,205 @@
+#!/usr/bin/python3
+# Authors: Rob Crittenden <rcritten@redhat.com>
+# Authors: Simo Sorce <ssorce@redhat.com>
+#
+# Copyright (C) 2009 Red Hat
+# see file 'COPYING' for use and warranty information
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+from __future__ import print_function
+
+import sys
+import os
+from ipaplatform.paths import paths
+try:
+ from optparse import OptionParser # pylint: disable=deprecated-module
+ from ipapython import ipautil, config
+ from ipaserver.install import installutils
+ from ipaserver.install.ldapupdate import LDAPUpdate
+ from ipalib import api, errors
+ from ipapython.ipa_log_manager import standard_logging_setup
+ from ipapython.dn import DN
+ from ipaplatform import services
+except ImportError as e:
+ print("""\
+There was a problem importing one of the required Python modules. The
+error was:
+
+ %s
+""" % e, file=sys.stderr)
+ sys.exit(1)
+
+nis_config_dn = DN(('cn', 'NIS Server'), ('cn', 'plugins'), ('cn', 'config'))
+compat_dn = DN(('cn', 'Schema Compatibility'), ('cn', 'plugins'), ('cn', 'config'))
+
+def parse_options():
+ usage = "%prog [options] <enable|disable|status>\n"
+ usage += "%prog [options]\n"
+ parser = OptionParser(usage=usage, formatter=config.IPAFormatter())
+
+ parser.add_option("-d", "--debug", action="store_true", dest="debug",
+ help="Display debugging information about the update(s)")
+ parser.add_option("-y", dest="password",
+ help="File containing the Directory Manager password")
+
+ config.add_standard_options(parser)
+ options, args = parser.parse_args()
+
+ return options, args
+
+def get_dirman_password():
+ """Prompt the user for the Directory Manager password and verify its
+ correctness.
+ """
+ password = installutils.read_password("Directory Manager", confirm=False, validate=False, retry=False)
+
+ return password
+
+def get_entry(dn):
+ """
+ Return the entry for the given DN. If the entry is not found return
+ None.
+ """
+ entry = None
+ try:
+ entry = api.Backend.ldap2.get_entry(dn)
+ except errors.NotFound:
+ pass
+ return entry
+
+def main():
+ retval = 0
+ files = [paths.NIS_ULDIF]
+ servicemsg = ""
+
+ if os.getegid() != 0:
+ sys.exit('Must be root to use this tool.')
+
+ installutils.check_server_configuration()
+
+ options, args = parse_options()
+
+ if len(args) != 1:
+ sys.exit("You must specify one action: enable | disable | status")
+ elif args[0] not in {"enable", "disable", "status"}:
+ sys.exit("Unrecognized action [" + args[0] + "]")
+
+ standard_logging_setup(None, debug=options.debug)
+ dirman_password = ""
+ if options.password:
+ try:
+ pw = ipautil.template_file(options.password, [])
+ except IOError:
+ sys.exit("File \"%s\" not found or not readable" % options.password)
+ dirman_password = pw.strip()
+ else:
+ dirman_password = get_dirman_password()
+ if dirman_password is None:
+ sys.exit("Directory Manager password required")
+
+ if not dirman_password:
+ sys.exit("No password supplied")
+
+ api.bootstrap(
+ context='cli', confdir=paths.ETC_IPA,
+ debug=options.debug, in_server=True)
+ api.finalize()
+ api.Backend.ldap2.connect(bind_pw=dirman_password)
+
+ if args[0] == "enable":
+ compat = get_entry(compat_dn)
+ if compat is None or compat.get('nsslapd-pluginenabled', [''])[0].lower() == 'off':
+ sys.exit("The compat plugin needs to be enabled: ipa-compat-manage enable")
+ entry = None
+ try:
+ entry = get_entry(nis_config_dn)
+ except errors.ExecutionError as lde:
+ print("An error occurred while talking to the server.")
+ print(lde)
+ retval = 1
+
+ # Enable either the portmap or rpcbind service
+ portmap = services.knownservices.portmap
+ rpcbind = services.knownservices.rpcbind
+
+ if portmap.is_installed():
+ portmap.enable()
+ servicemsg = portmap.service_name
+ elif rpcbind.is_installed():
+ rpcbind.enable()
+ servicemsg = rpcbind.service_name
+ else:
+ print("Unable to enable either %s or %s" % (portmap.service_name, rpcbind.service_name))
+ retval = 3
+
+ # The cn=config entry for the plugin may already exist but it
+ # could be turned off, handle both cases.
+ if entry is None:
+ print("Enabling plugin")
+ ld = LDAPUpdate()
+ if ld.update(files) != True:
+ retval = 1
+ elif entry.get('nsslapd-pluginenabled', [''])[0].lower() == 'off':
+ print("Enabling plugin")
+ # Already configured, just enable the plugin
+ entry['nsslapd-pluginenabled'] = ['on']
+ api.Backend.ldap2.update_entry(entry)
+ else:
+ print("Plugin already Enabled")
+ retval = 2
+
+ elif args[0] == "disable":
+ try:
+ entry = api.Backend.ldap2.get_entry(nis_config_dn, ['nsslapd-pluginenabled'])
+ entry['nsslapd-pluginenabled'] = ['off']
+ api.Backend.ldap2.update_entry(entry)
+ except (errors.NotFound, errors.EmptyModlist):
+ print("Plugin is already disabled")
+ retval = 2
+ except errors.LDAPError as lde:
+ print("An error occurred while talking to the server.")
+ print(lde)
+ retval = 1
+
+ elif args[0] == "status":
+ nis_entry = get_entry(nis_config_dn)
+ enabled = (nis_entry and
+ nis_entry.get(
+ 'nsslapd-pluginenabled', '')[0].lower() == "on")
+ if enabled:
+ print("Plugin is enabled")
+ retval = 0
+ else:
+ print("Plugin is not enabled")
+ retval = 4
+
+ else:
+ retval = 1
+
+ if retval == 0:
+ if args[0] in {"enable", "disable"}:
+ print("This setting will not take effect until you restart "
+ "Directory Server.")
+
+ if args[0] == "enable":
+ print("The %s service may need to be started." % servicemsg)
+
+ api.Backend.ldap2.disconnect()
+
+ return retval
+
+if __name__ == '__main__':
+ installutils.run_script(main, operation_name='ipa-nis-manage')
diff --git a/install/tools/man/Makefile.am b/install/tools/man/Makefile.am
index bffce402c7df428a47c1710bdc47d59a95993a0d..e9542a77bbbb88054eae1e64311d6e9ec5bee499 100644
--- a/install/tools/man/Makefile.am
+++ b/install/tools/man/Makefile.am
@@ -18,6 +18,7 @@ dist_man1_MANS = \
ipa-kra-install.1 \
ipa-ldap-updater.1 \
ipa-compat-manage.1 \
+ ipa-nis-manage.1 \
ipa-managed-entries.1 \
ipa-backup.1 \
ipa-restore.1 \
diff --git a/install/tools/man/ipa-nis-manage.1 b/install/tools/man/ipa-nis-manage.1
new file mode 100644
index 0000000000000000000000000000000000000000..1107b7790531aa695defd660040734143c02474d
--- /dev/null
+++ b/install/tools/man/ipa-nis-manage.1
@@ -0,0 +1,51 @@
+.\" A man page for ipa-nis-manage
+.\" Copyright (C) 2009 Red Hat, Inc.
+.\"
+.\" This program is free software; you can redistribute it and/or modify
+.\" it under the terms of the GNU General Public License as published by
+.\" the Free Software Foundation, either version 3 of the License, or
+.\" (at your option) any later version.
+.\"
+.\" This program is distributed in the hope that it will be useful, but
+.\" WITHOUT ANY WARRANTY; without even the implied warranty of
+.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+.\" General Public License for more details.
+.\"
+.\" You should have received a copy of the GNU General Public License
+.\" along with this program. If not, see <http://www.gnu.org/licenses/>.
+.\"
+.\" Author: Rob Crittenden <rcritten@redhat.com>
+.\"
+.TH "ipa-nis-manage" "1" "April 25 2016" "IPA" "IPA Manual Pages"
+.SH "NAME"
+ipa\-nis\-manage \- Enables or disables the NIS listener plugin
+.SH "SYNOPSIS"
+ipa\-nis\-manage [options] <enable|disable|status>
+.SH "DESCRIPTION"
+Run the command with the \fBenable\fR option to enable the NIS plugin.
+
+Run the command with the \fBdisable\fR option to disable the NIS plugin.
+
+Run the command with the \fBstatus\fR option to read status of the NIS plugin. Return code 0 indicates enabled plugin, return code 4 indicates disabled plugin.
+
+In all cases the user will be prompted to provide the Directory Manager's password unless option \fB\-y\fR is used.
+
+Directory Server will need to be restarted after the NIS listener plugin has been enabled.
+
+.SH "OPTIONS"
+.TP
+\fB\-d\fR, \fB\-\-debug\fR
+Enable debug logging when more verbose output is needed
+.TP
+\fB\-y\fR \fIfile\fR
+File containing the Directory Manager password
+.SH "EXIT STATUS"
+0 if the command was successful
+
+1 if an error occurred
+
+2 if the plugin is already in the required status (enabled or disabled)
+
+3 if RPC services cannot be enabled.
+
+4 if status command detected plugin in disabled state.
diff --git a/install/updates/10-enable-betxn.update b/install/updates/10-enable-betxn.update
index 9525292cb2d66a738a2dec9cd881dbf960d2d944..1f89341c7f68616161d03b8a2e0c34f499bc3317 100644
--- a/install/updates/10-enable-betxn.update
+++ b/install/updates/10-enable-betxn.update
@@ -44,3 +44,6 @@ only: nsslapd-pluginbetxn: on
dn: cn=Schema Compatibility, cn=plugins, cn=config
onlyifexist: nsslapd-pluginbetxn: on
+
+dn: cn=NIS Server, cn=plugins, cn=config
+onlyifexist: nsslapd-pluginbetxn: on
diff --git a/install/updates/50-nis.update b/install/updates/50-nis.update
new file mode 100644
index 0000000000000000000000000000000000000000..05a166f003aefc50fc25f10f01f7364d752425bc
--- /dev/null
+++ b/install/updates/50-nis.update
@@ -0,0 +1,3 @@
+# Updates are applied only if NIS plugin has been configured
+# update definitions are located in install/share/nis-update.uldif
+plugin: update_nis_configuration
diff --git a/install/updates/Makefile.am b/install/updates/Makefile.am
index cce2670a6c31803dc534ac5b0093ec0aa317233e..fd96831d8fd4904b51d933847865d37c58a3508c 100644
--- a/install/updates/Makefile.am
+++ b/install/updates/Makefile.am
@@ -52,6 +52,7 @@ app_DATA = \
50-groupuuid.update \
50-hbacservice.update \
50-krbenctypes.update \
+ 50-nis.update \
50-ipaconfig.update \
55-pbacmemberof.update \
59-trusts-sysacount.update \
diff --git a/ipaplatform/base/paths.py b/ipaplatform/base/paths.py
index 5bc0e3ac3eed257fd7fa6a0830e3e2dbeb3d01d2..b84925bfc7d9af8584d9d4b185aa04b635c1e325 100644
--- a/ipaplatform/base/paths.py
+++ b/ipaplatform/base/paths.py
@@ -297,6 +297,8 @@ class BasePathNamespace:
CA_TOPOLOGY_ULDIF = "/usr/share/ipa/ca-topology.uldif"
IPA_HTML_DIR = "/usr/share/ipa/html"
CA_CRT = "/usr/share/ipa/html/ca.crt"
+ NIS_ULDIF = "/usr/share/ipa/nis.uldif"
+ NIS_UPDATE_ULDIF = "/usr/share/ipa/nis-update.uldif"
SCHEMA_COMPAT_ULDIF = "/usr/share/ipa/updates/91-schema_compat.update"
SCHEMA_COMPAT_POST_ULDIF = "/usr/share/ipa/schema_compat_post.uldif"
SUBID_GENERATORS_ULDIF = "/usr/share/ipa/subid-generators.uldif"
diff --git a/ipaserver/install/ipa_migrate.py b/ipaserver/install/ipa_migrate.py
index f07a878e739002346df894f93745ea60345e0557..b0a27de5dd40aa661271faa3a8a0855226b5892f 100644
--- a/ipaserver/install/ipa_migrate.py
+++ b/ipaserver/install/ipa_migrate.py
@@ -32,7 +32,7 @@ from ipapython.admintool import admin_cleanup_global_argv
from ipaserver.install.ipa_migrate_constants import (
DS_CONFIG, DB_OBJECTS, DS_INDEXES, BIND_DN, LOG_FILE_NAME,
STRIP_OP_ATTRS, STRIP_ATTRS, STRIP_OC, PROD_ATTRS,
- DNA_REGEN_VAL, DNA_REGEN_ATTRS, IGNORE_ATTRS,
+ DNA_REGEN_VAL, DNA_REGEN_ATTRS, NIS_PLUGIN, IGNORE_ATTRS,
DB_EXCLUDE_TREES, POLICY_OP_ATTRS, STATE_OPTIONS
)
@@ -749,7 +749,8 @@ class IPAMigrate():
self.log_info(title)
self.log_info('-' * (len(title) - 1))
logged_something = self.log_stats(DS_CONFIG)
- if self.args.verbose:
+ if self.args.verbose or NIS_PLUGIN['count'] > 0:
+ self.log_info(f" - NIS Server Plugin: {NIS_PLUGIN['count']}")
logged_something = True
if not self.log_stats(DS_INDEXES) and not logged_something:
self.log_info(" - No updates")
@@ -1983,6 +1984,28 @@ class IPAMigrate():
add_missing=True)
stats['config_processed'] += 1
+ # Slapi NIS Plugin
+ if DN(NIS_PLUGIN['dn']) == DN(entry['dn']):
+ # Parent plugin entry
+ self.process_config_entry(
+ entry['dn'], entry['attrs'], NIS_PLUGIN,
+ add_missing=True)
+ stats['config_processed'] += 1
+ elif DN(NIS_PLUGIN['dn']) in DN(entry['dn']):
+ # Child NIS plugin entry
+ nis_dn = entry['dn']
+ lc_remote_realm = self.remote_realm.lower()
+ lc_realm = self.realm.lower()
+ nis_dn = nis_dn.replace(lc_remote_realm, lc_realm)
+ if 'nis-domain' in entry['attrs']:
+ value = entry['attrs']['nis-domain'][0]
+ value = value.replace(lc_remote_realm, lc_realm)
+ entry['attrs']['nis-domain'][0] = value
+ # Process the entry
+ self.process_config_entry(nis_dn, entry['attrs'], NIS_PLUGIN,
+ add_missing=True)
+ stats['config_processed'] += 1
+
#
# Migration
#
diff --git a/ipaserver/install/ipa_migrate_constants.py b/ipaserver/install/ipa_migrate_constants.py
index 65e920d4bdc7511adb60bbdf87db88beb2630645..4fe8b467978936e4f8d621b7297ff577127c7298 100644
--- a/ipaserver/install/ipa_migrate_constants.py
+++ b/ipaserver/install/ipa_migrate_constants.py
@@ -527,6 +527,30 @@ DS_CONFIG = {
},
}
+#
+# Slpai NIS is an optional plugin. It requires special handling
+#
+NIS_PLUGIN = {
+ 'dn': 'cn=NIS Server,cn=plugins,cn=config',
+ 'attrs': [
+ 'nis-domain',
+ 'nis-base',
+ 'nis-map',
+ 'nis-filter',
+ 'nis-key-format:',
+ 'nis-values-format:',
+ 'nis-secure',
+ 'nis-disallowed-chars',
+ # Parent plugin entry
+ 'nsslapd-pluginarg0',
+ 'nsslapd-pluginenabled'
+ ],
+ 'multivalued': [],
+ 'label': 'NIS Server Plugin',
+ 'mode': 'all',
+ 'count': 0,
+}
+
#
# This mapping is simliar to above but it handles container entries
# This could be built into the above mapping using the "comma" approach
diff --git a/ipaserver/install/plugins/update_nis.py b/ipaserver/install/plugins/update_nis.py
new file mode 100644
index 0000000000000000000000000000000000000000..c02eb5f838a99b27cdd21e4aee17e5104f0e22e2
--- /dev/null
+++ b/ipaserver/install/plugins/update_nis.py
@@ -0,0 +1,92 @@
+#
+# Copyright (C) 2015 FreeIPA Contributors see COPYING for license
+#
+
+from __future__ import absolute_import
+
+import logging
+
+from ipalib.plugable import Registry
+from ipalib import errors
+from ipalib import Updater
+from ipaplatform.paths import paths
+from ipapython.dn import DN
+from ipaserver.install import sysupgrade
+from ipaserver.install.ldapupdate import LDAPUpdate
+
+logger = logging.getLogger(__name__)
+
+register = Registry()
+
+
+@register()
+class update_nis_configuration(Updater):
+ """Update NIS configuration
+
+ NIS configuration can be updated only if NIS Server was configured via
+ ipa-nis-manage command.
+ """
+
+ def __recover_from_missing_maps(self, ldap):
+ # https://fedorahosted.org/freeipa/ticket/5507
+ # if all following DNs are missing, but 'NIS Server' container exists
+ # we are experiencig bug and maps should be fixed
+
+ if sysupgrade.get_upgrade_state('nis',
+ 'done_recover_from_missing_maps'):
+ # this recover must be done only once, a user may deleted some
+ # maps, we do not want to restore them again
+ return
+
+ logger.debug("Recovering from missing NIS maps bug")
+
+ suffix = "cn=NIS Server,cn=plugins,cn=config"
+ domain = self.api.env.domain
+ missing_dn_list = [
+ DN(nis_map.format(domain=domain, suffix=suffix)) for nis_map in [
+ "nis-domain={domain}+nis-map=passwd.byname,{suffix}",
+ "nis-domain={domain}+nis-map=passwd.byuid,{suffix}",
+ "nis-domain={domain}+nis-map=group.byname,{suffix}",
+ "nis-domain={domain}+nis-map=group.bygid,{suffix}",
+ "nis-domain={domain}+nis-map=netid.byname,{suffix}",
+ "nis-domain={domain}+nis-map=netgroup,{suffix}",
+ ]
+ ]
+
+ for dn in missing_dn_list:
+ try:
+ ldap.get_entry(dn, attrs_list=['cn'])
+ except errors.NotFound:
+ pass
+ else:
+ # bug is not effective, at least one of 'possible missing'
+ # maps was detected
+ return
+
+ sysupgrade.set_upgrade_state('nis', 'done_recover_from_missing_maps',
+ True)
+
+ # bug is effective run update to recreate missing maps
+ ld = LDAPUpdate(api=self.api)
+ ld.update([paths.NIS_ULDIF])
+
+ def execute(self, **options):
+ ldap = self.api.Backend.ldap2
+ dn = DN(('cn', 'NIS Server'), ('cn', 'plugins'), ('cn', 'config'))
+ try:
+ ldap.get_entry(dn, attrs_list=['cn'])
+ except errors.NotFound:
+ # NIS is not configured on system, do not execute update
+ logger.debug("Skipping NIS update, NIS Server is not configured")
+
+ # container does not exist, bug #5507 is not effective
+ sysupgrade.set_upgrade_state(
+ 'nis', 'done_recover_from_missing_maps', True)
+ else:
+ self.__recover_from_missing_maps(ldap)
+
+ logger.debug("Executing NIS Server update")
+ ld = LDAPUpdate(api=self.api)
+ ld.update([paths.NIS_UPDATE_ULDIF])
+
+ return False, ()
diff --git a/ipatests/test_cmdline/test_cli.py b/ipatests/test_cmdline/test_cli.py
index 6c86bbb657a0d9a7b74ef34ad20a796a10073315..4acf8dd5290c687723a9c43a21e194bf663b6a3b 100644
--- a/ipatests/test_cmdline/test_cli.py
+++ b/ipatests/test_cmdline/test_cli.py
@@ -372,6 +372,7 @@ IPA_CLIENT_NOT_CONFIGURED = b'IPA client is not configured on this system'
'/usr/share/ipa/updates/05-pre_upgrade_plugins.update'],
2, None, IPA_NOT_CONFIGURED),
(['ipa-managed-entries'], 2, None, IPA_NOT_CONFIGURED),
+ (['ipa-nis-manage'], 2, None, IPA_NOT_CONFIGURED),
(['ipa-pkinit-manage'], 2, None, IPA_NOT_CONFIGURED),
(['ipa-replica-manage', 'list'], 1, IPA_NOT_CONFIGURED, None),
(['ipa-server-certinstall'], 2, None, IPA_NOT_CONFIGURED),
diff --git a/ipatests/test_integration/test_commands.py b/ipatests/test_integration/test_commands.py
index 5bc871ab71bc05ec1c26df8352e996a9e627b466..8f8548494eaec3afd00961c096772b9d0b1ab3a4 100644
--- a/ipatests/test_integration/test_commands.py
+++ b/ipatests/test_integration/test_commands.py
@@ -1382,6 +1382,93 @@ class TestIPACommand(IntegrationTest):
serverid = realm_to_serverid(self.master.domain.realm)
return ("dirsrv@%s.service" % serverid)
+ def test_ipa_nis_manage_enable(self):
+ """
+ This testcase checks if ipa-nis-manage enable
+ command enables plugin on an IPA master
+ """
+ dirsrv_service = self.get_dirsrv_id()
+ console_msg = (
+ "Enabling plugin\n"
+ "This setting will not take effect until "
+ "you restart Directory Server.\n"
+ "The rpcbind service may need to be started"
+ )
+ status_msg = "Plugin is enabled"
+ tasks.kinit_admin(self.master)
+ result = self.master.run_command(
+ ["ipa-nis-manage", "enable"],
+ stdin_text=self.master.config.admin_password,
+ )
+ assert console_msg in result.stdout_text
+ # verify using backend
+ conn = self.master.ldap_connect()
+ dn = DN(('cn', 'NIS Server'), ('cn', 'plugins'), ('cn', 'config'))
+ entry = conn.get_entry(dn)
+ nispluginstring = entry.get('nsslapd-pluginEnabled')
+ assert 'on' in nispluginstring
+ # restart for changes to take effect
+ self.master.run_command(["systemctl", "restart", dirsrv_service])
+ self.master.run_command(["systemctl", "restart", "rpcbind"])
+ time.sleep(DIRSRV_SLEEP)
+ # check status msg on the console
+ result = self.master.run_command(
+ ["ipa-nis-manage", "status"],
+ stdin_text=self.master.config.admin_password,
+ )
+ assert status_msg in result.stdout_text
+
+ def test_ipa_nis_manage_disable(self):
+ """
+ This testcase checks if ipa-nis-manage disable
+ command disable plugin on an IPA Master
+ """
+ dirsrv_service = self.get_dirsrv_id()
+ msg = (
+ "This setting will not take effect "
+ "until you restart Directory Server."
+ )
+ status_msg = "Plugin is not enabled"
+ tasks.kinit_admin(self.master)
+ result = self.master.run_command(
+ ["ipa-nis-manage", "disable"],
+ stdin_text=self.master.config.admin_password,
+ )
+ assert msg in result.stdout_text
+ # verify using backend
+ conn = self.master.ldap_connect()
+ dn = DN(('cn', 'NIS Server'), ('cn', 'plugins'), ('cn', 'config'))
+ entry = conn.get_entry(dn)
+ nispluginstring = entry.get('nsslapd-pluginEnabled')
+ assert 'off' in nispluginstring
+ # restart dirsrv for changes to take effect
+ self.master.run_command(["systemctl", "restart", dirsrv_service])
+ time.sleep(DIRSRV_SLEEP)
+ # check status msg on the console
+ result = self.master.run_command(
+ ["ipa-nis-manage", "status"],
+ stdin_text=self.master.config.admin_password,
+ raiseonerr=False,
+ )
+ assert result.returncode == 4
+ assert status_msg in result.stdout_text
+
+ def test_ipa_nis_manage_enable_incorrect_password(self):
+ """
+ This testcase checks if ipa-nis-manage enable
+ command throws error on console for invalid DS admin password
+ """
+ msg1 = "Insufficient access: "
+ msg2 = "Invalid credentials"
+ result = self.master.run_command(
+ ["ipa-nis-manage", "enable"],
+ stdin_text='Invalid_pwd',
+ raiseonerr=False,
+ )
+ assert result.returncode == 1
+ assert msg1 in result.stderr_text
+ assert msg2 in result.stderr_text
+
def test_pkispawn_log_is_present(self):
"""
This testcase checks if pkispawn logged properly.
--
2.52.0

View File

@ -1,92 +0,0 @@
From ad4b7f6cedaed54acf279033b650010c65face10 Mon Sep 17 00:00:00 2001
From: Sudhir Menon <sumenon@redhat.com>
Date: Tue, 20 Aug 2024 14:52:03 +0530
Subject: [PATCH] ipatests: Check Default PAC type is added to config
This patch checks that the default PAC type
is added to configuration i.e ipaKrbAuthzData: MS-PAC
during ipa-server-installation
The patch also checks that if 'ipaKrbAuthzData: MS-PAC'
attribute is deleted and then when we run 'ipa-server-upgrade'
command the attribute is added back.
Related: https://pagure.io/freeipa/issue/9632
Signed-off-by: Sudhir Menon <sumenon@redhat.com>
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
---
.../test_integration/test_installation.py | 15 +++++++++++
ipatests/test_integration/test_upgrade.py | 26 ++++++++++++++++++-
2 files changed, 40 insertions(+), 1 deletion(-)
diff --git a/ipatests/test_integration/test_installation.py b/ipatests/test_integration/test_installation.py
index ada43e33fe173ea3c315178c37e2a664b05b905b..c5565c452010f23f038ddf329454b591ef09f6af 100644
--- a/ipatests/test_integration/test_installation.py
+++ b/ipatests/test_integration/test_installation.py
@@ -1190,6 +1190,21 @@ class TestInstallMaster(IntegrationTest):
expected_stdout=f'href="https://{self.master.hostname}/'
)
+ def test_pac_configuration_enabled(self):
+ """
+ This testcase checks that the default PAC type
+ is added to configuration.
+ """
+ base_dn = str(self.master.domain.basedn)
+ dn = DN(
+ ("cn", "ipaConfig"),
+ ("cn", "etc"),
+ base_dn
+ )
+ result = tasks.ldapsearch_dm(self.master, str(dn),
+ ["ipaKrbAuthzData"])
+ assert 'ipaKrbAuthzData: MS-PAC' in result.stdout_text
+
def test_hostname_parameter(self, server_cleanup):
"""
Test that --hostname parameter is respected in interactive mode.
diff --git a/ipatests/test_integration/test_upgrade.py b/ipatests/test_integration/test_upgrade.py
index 011de939e92790734d63da2f85be1c25349116a8..a0f393780ccc25774466992976532c876aa876da 100644
--- a/ipatests/test_integration/test_upgrade.py
+++ b/ipatests/test_integration/test_upgrade.py
@@ -165,7 +165,6 @@ class TestUpgrade(IntegrationTest):
ldap.update_entry(location_krb_rec)
yield _setup_locations
-
ldap = self.master.ldap_connect()
modified = False
@@ -491,3 +490,28 @@ class TestUpgrade(IntegrationTest):
tasks.reinstall_packages(self.master, ['*ipa-client'])
assert not self.master.transport.file_exists(
paths.SSH_CONFIG + ".orig")
+
+ def test_mspac_attribute_set(self):
+ """
+ This testcase deletes the already existing attribute
+ 'ipaKrbAuthzData: MS-PAC'.
+ The test then runs ipa-server-upgrade and checks that
+ the attribute 'ipaKrbAuthzData: MS-PAC' is added again.
+ """
+ base_dn = str(self.master.domain.basedn)
+ dn = DN(
+ ("cn", "ipaConfig"),
+ ("cn", "etc"),
+ base_dn
+ )
+ ldif = textwrap.dedent("""
+ dn: cn=ipaConfig,cn=etc,{}
+ changetype: modify
+ delete: ipaKrbAuthzData
+ """).format(base_dn)
+ tasks.ldapmodify_dm(self.master, ldif)
+ tasks.kinit_admin(self.master)
+ self.master.run_command(['ipa-server-upgrade'])
+ result = tasks.ldapsearch_dm(self.master, str(dn),
+ ["ipaKrbAuthzData"])
+ assert 'ipaKrbAuthzData: MS-PAC' in result.stdout_text
--
2.46.2

View File

@ -0,0 +1,51 @@
From 6f0cd075e5a588628a98d3b4a95e755af59845d7 Mon Sep 17 00:00:00 2001
From: Florence Blanc-Renaud <flo@redhat.com>
Date: Thu, 4 Dec 2025 13:13:21 +0100
Subject: [PATCH] Trust: fix tdo with WITH_FOREST
When a trust was established pre samba 4.23, the trust domain object
could contain ipanttrustattributes: 8 (LSA_TRUST_ATTRIBUTE_WITHIN_FOREST)
This value prevents winbind restart.
The current code replaces 0 with LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE
but should also handle the case for LSA_TRUST_ATTRIBUTE_WITHIN_FOREST.
In this case we should drop the bit and replace it by FOREST_TRANSITIVE
one because otherwise Samba will skip the domain. Do not change the LDAP
representation to allow older replicas to continue operations.
Fixes: https://pagure.io/freeipa/issue/9892
Signed-off-by: Alexander Bokovoy <abokovoy@redhat.com>
Reviewed-By: Alexander Bokovoy <abbra@users.noreply.github.com>
---
daemons/ipa-sam/ipa_sam.c | 15 +++++++++++----
1 file changed, 11 insertions(+), 4 deletions(-)
diff --git a/daemons/ipa-sam/ipa_sam.c b/daemons/ipa-sam/ipa_sam.c
index c43ffddbbdd69123b5d568a937fbc12d138243d1..ea25934d569f378f41b386bbb57d33eaf2bb19c0 100644
--- a/daemons/ipa-sam/ipa_sam.c
+++ b/daemons/ipa-sam/ipa_sam.c
@@ -2545,10 +2545,17 @@ static bool fill_pdb_trusted_domain(TALLOC_CTX *mem_ctx,
if (!res) {
goto done;
}
- if (td->trust_attributes == 0 && (td->domain_name != dns_domain)) {
- /* attribute wasn't present and this is not a subdomain within
- * the parent forest */
- td->trust_attributes = LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE;
+ if (td->domain_name != dns_domain) {
+ if ((td->trust_attributes & LSA_TRUST_ATTRIBUTE_WITHIN_FOREST) != 0 ||
+ (td->trust_attributes == 0)) {
+ /* when trust attribute is not present or contains WITHIN_FOREST,
+ * we should drop the bit and replace it by FOREST_TRANSITIVE
+ * one because otherwise Samba will skip the domain.
+ * Do not change the LDAP representation to allow older replicas
+ * to continue operations. */
+ td->trust_attributes &= ~LSA_TRUST_ATTRIBUTE_WITHIN_FOREST;
+ td->trust_attributes |= LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE;
+ }
}
res = get_uint32_t_from_ldap_msg(ipasam_state, entry,
--
2.52.0

View File

@ -1,86 +0,0 @@
From 42eb97ee6bd8011b590aef321d4386ea9352933d Mon Sep 17 00:00:00 2001
From: Alexander Bokovoy <abokovoy@redhat.com>
Date: Wed, 28 Aug 2024 10:02:19 +0300
Subject: [PATCH] selinux: add all IPA log files to ipa_log_t file context
We have multiple log files that produced by IPA components. Some of them
are written by the tools that run as root and inherit their file context
from /var/log -> var_log_t. However, increasingly we get tools that were
run through oddjob helpers. These supposed to be run within ipa_helper_t
SELinux context which has write permissions for ipa_log_t file context.
Add all known log files from the base platform. The following script was
used to generate them:
$ git grep '_LOG = .*ipa.*\.log' ipaplatform/base/paths.py | cut -d= -f2 | \
xargs -I% echo -e "%\t--\tgen_context(system_u:object_r:ipa_log_t,s0)"
/var/log/ipabackup.log -- gen_context(system_u:object_r:ipa_log_t,s0)
/var/log/ipaclient-install.log -- gen_context(system_u:object_r:ipa_log_t,s0)
/var/log/ipaclient-uninstall.log -- gen_context(system_u:object_r:ipa_log_t,s0)
/var/log/ipaclientsamba-install.log -- gen_context(system_u:object_r:ipa_log_t,s0)
/var/log/ipaclientsamba-uninstall.log -- gen_context(system_u:object_r:ipa_log_t,s0)
/var/log/ipareplica-ca-install.log -- gen_context(system_u:object_r:ipa_log_t,s0)
/var/log/ipareplica-conncheck.log -- gen_context(system_u:object_r:ipa_log_t,s0)
/var/log/ipareplica-install.log -- gen_context(system_u:object_r:ipa_log_t,s0)
/var/log/iparestore.log -- gen_context(system_u:object_r:ipa_log_t,s0)
/var/log/ipaserver-enable-sid.log -- gen_context(system_u:object_r:ipa_log_t,s0)
/var/log/ipaserver-install.log -- gen_context(system_u:object_r:ipa_log_t,s0)
/var/log/ipaserver-adtrust-install.log -- gen_context(system_u:object_r:ipa_log_t,s0)
/var/log/ipaserver-dns-install.log -- gen_context(system_u:object_r:ipa_log_t,s0)
/var/log/ipaserver-kra-install.log -- gen_context(system_u:object_r:ipa_log_t,s0)
/var/log/ipaserver-uninstall.log -- gen_context(system_u:object_r:ipa_log_t,s0)
/var/log/ipaupgrade.log -- gen_context(system_u:object_r:ipa_log_t,s0)
/var/log/ipatrust-enable-agent.log -- gen_context(system_u:object_r:ipa_log_t,s0)
/var/log/ipaepn.log -- gen_context(system_u:object_r:ipa_log_t,s0)
/var/log/ipa-custodia.audit.log -- gen_context(system_u:object_r:ipa_log_t,s0)
/var/log/ipa-migrate.log -- gen_context(system_u:object_r:ipa_log_t,s0)
ipa-custodia.audit.log was already in the present list.
Additionally, ipa-migrate-conflict.ldif is used by the ipa-migrate tool
but is not provided through the ipaplatform mechanism. It is added
explicitly.
Fixes: https://pagure.io/freeipa/issue/9654
Signed-off-by: Alexander Bokovoy <abokovoy@redhat.com>
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
---
selinux/ipa.fc | 21 ++++++++++++++++++++-
1 file changed, 20 insertions(+), 1 deletion(-)
diff --git a/selinux/ipa.fc b/selinux/ipa.fc
index 700e3a14a11fcd403a2e6f57ec781c58dae77660..47bd19ba77418cad1f0904dc4a9a35ce9d6ff9d2 100644
--- a/selinux/ipa.fc
+++ b/selinux/ipa.fc
@@ -24,7 +24,26 @@
/var/log/ipa(/.*)? gen_context(system_u:object_r:ipa_log_t,s0)
-/var/log/ipareplica-conncheck.log.* -- gen_context(system_u:object_r:ipa_log_t,s0)
+/var/log/ipabackup.log -- gen_context(system_u:object_r:ipa_log_t,s0)
+/var/log/ipaclient-install.log -- gen_context(system_u:object_r:ipa_log_t,s0)
+/var/log/ipaclient-uninstall.log -- gen_context(system_u:object_r:ipa_log_t,s0)
+/var/log/ipaclientsamba-install.log -- gen_context(system_u:object_r:ipa_log_t,s0)
+/var/log/ipaclientsamba-uninstall.log -- gen_context(system_u:object_r:ipa_log_t,s0)
+/var/log/ipareplica-ca-install.log -- gen_context(system_u:object_r:ipa_log_t,s0)
+/var/log/ipareplica-conncheck.log -- gen_context(system_u:object_r:ipa_log_t,s0)
+/var/log/ipareplica-install.log -- gen_context(system_u:object_r:ipa_log_t,s0)
+/var/log/iparestore.log -- gen_context(system_u:object_r:ipa_log_t,s0)
+/var/log/ipaserver-enable-sid.log -- gen_context(system_u:object_r:ipa_log_t,s0)
+/var/log/ipaserver-install.log -- gen_context(system_u:object_r:ipa_log_t,s0)
+/var/log/ipaserver-adtrust-install.log -- gen_context(system_u:object_r:ipa_log_t,s0)
+/var/log/ipaserver-dns-install.log -- gen_context(system_u:object_r:ipa_log_t,s0)
+/var/log/ipaserver-kra-install.log -- gen_context(system_u:object_r:ipa_log_t,s0)
+/var/log/ipaserver-uninstall.log -- gen_context(system_u:object_r:ipa_log_t,s0)
+/var/log/ipaupgrade.log -- gen_context(system_u:object_r:ipa_log_t,s0)
+/var/log/ipatrust-enable-agent.log -- gen_context(system_u:object_r:ipa_log_t,s0)
+/var/log/ipaepn.log -- gen_context(system_u:object_r:ipa_log_t,s0)
+/var/log/ipa-migrate.log -- gen_context(system_u:object_r:ipa_log_t,s0)
+/var/log/ipa-migrate-conflict.ldif -- gen_context(system_u:object_r:ipa_log_t,s0)
/var/run/ipa(/.*)? gen_context(system_u:object_r:ipa_var_run_t,s0)
--
2.46.2

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,65 @@
From c03f7eb2b9a0ee36d0ad396f3e4e4e8a6e40ecd2 Mon Sep 17 00:00:00 2001
From: Florence Blanc-Renaud <flo@redhat.com>
Date: Thu, 4 Dec 2025 12:58:38 +0100
Subject: [PATCH] ipatest: add an integration test for samba upgrade
When a trust was establish pre samba 4.23, the trust domain object
could contain ipanttrustattributes = 40 (LSA_TRUST_ATTRIBUTE_WITHIN_FOREST)
and winbind would fail to restart after an upgrade to samba 4.23.
Add a test simulating the situation and calling ipa-server-upgrade
Related: https://pagure.io/freeipa/issue/9892
Signed-off-by: Florence Blanc-Renaud <flo@redhat.com>
Reviewed-By: Alexander Bokovoy <abbra@users.noreply.github.com>
---
ipatests/test_integration/test_trust.py | 33 +++++++++++++++++++++++++
1 file changed, 33 insertions(+)
diff --git a/ipatests/test_integration/test_trust.py b/ipatests/test_integration/test_trust.py
index 7bb74e2f5821719ffe2ceaf2bdcd8e7d46a6cd1f..13ad0afa4c1fb032d50f40cf7cb9b79283203225 100644
--- a/ipatests/test_integration/test_trust.py
+++ b/ipatests/test_integration/test_trust.py
@@ -1009,6 +1009,39 @@ class TestTrust(BaseTestTrust):
tasks.unconfigure_windows_dns_for_trust(self.ad, self.master)
tasks.unconfigure_dns_for_trust(self.master, self.ad)
+ def test_upgrade_within_forest(self):
+ """
+ Simulate an upgrade from a trust established with samba pre 4.23
+
+ With older samba version, the trust domain object had
+ ipanttrustattributes: 8
+ corresponding to LSA_TRUST_ATTRIBUTE_WITHIN_FOREST
+ and this breaks ipa-upgrade (winbind fails to restart)
+ """
+
+ tasks.configure_dns_for_trust(self.master, self.ad)
+ tasks.configure_windows_dns_for_trust(self.ad, self.master)
+ tasks.establish_trust_with_ad(
+ self.master, self.ad_domain,
+ extra_args=['--range-type', 'ipa-ad-trust'])
+
+ conn = self.master.ldap_connect()
+ trust_dn = DN("cn={},cn=ad,cn=trusts,{}".format(
+ self.ad.domain.name, self.master.domain.basedn
+ ))
+ entry = conn.get_entry(trust_dn)
+
+ # set the trust attributes to LSA_TRUST_ATTRIBUTE_WITHIN_FOREST
+ entry.single_value['ipanttrustattributes'] = '40'
+ conn.update_entry(entry)
+ self.master.run_command(['ipa-server-upgrade'])
+ self.master.run_command(['ipactl', 'restart'])
+
+ # cleanup for next test
+ self.remove_trust(self.ad)
+ tasks.unconfigure_windows_dns_for_trust(self.ad, self.master)
+ tasks.unconfigure_dns_for_trust(self.master, self.ad)
+
def test_server_option_with_unreachable_ad(self):
"""
Check trust can be established with partially unreachable AD topology
--
2.52.0

View File

@ -0,0 +1,321 @@
From 257740b66daf004a7333bfaeddece4732be5a48c Mon Sep 17 00:00:00 2001
From: Florence Blanc-Renaud <flo@redhat.com>
Date: Tue, 9 Dec 2025 17:52:36 +0100
Subject: [PATCH] Revert "Stop using deprecated pkg_resources"
This reverts commit ac791f7372d32d25c75eb61f949f1db38fe2f0d6.
---
freeipa.spec.in | 6 +++++-
ipaplatform/base/tasks.py | 2 +-
ipapython/version.py.in | 2 +-
ipaserver/custodia/server/__init__.py | 13 +++++++++----
ipaserver/install/cainstance.py | 2 +-
ipaserver/install/krbinstance.py | 2 +-
ipaserver/install/server/replicainstall.py | 2 +-
ipaserver/install/server/upgrade.py | 2 +-
.../pytest_ipa/integration/create_caless_pki.py | 2 +-
ipatests/pytest_ipa/integration/tasks.py | 2 +-
ipatests/test_custodia/test_plugins.py | 13 ++++++++-----
ipatests/test_integration/test_adtrust_install.py | 2 +-
ipatests/test_integration/test_cert.py | 2 +-
ipatests/test_integration/test_commands.py | 2 +-
ipatests/test_integration/test_idp.py | 2 +-
ipatests/test_integration/test_ipahealthcheck.py | 2 +-
ipatests/test_webui/ui_driver.py | 2 +-
ipatests/test_xmlrpc/test_automember_plugin.py | 1 +
18 files changed, 37 insertions(+), 24 deletions(-)
diff --git a/freeipa.spec.in b/freeipa.spec.in
index b93199a5783a89e0ff58affd3d416556f72dd00c..bf6fa216dcc4cfbac903aad43c6219f1e57db4dd 100644
--- a/freeipa.spec.in
+++ b/freeipa.spec.in
@@ -983,7 +983,6 @@ Requires: python3-jwcrypto >= 0.4.2
Requires: python3-libipa_hbac
Requires: python3-netaddr >= %{python_netaddr_version}
Requires: python3-netifaces >= 0.10.4
-Requires: python3-packaging
Requires: python3-pyasn1 >= 0.3.2-2
Requires: python3-pyasn1-modules >= 0.3.2-2
Requires: python3-pyusb
@@ -992,6 +991,11 @@ Requires: python3-requests
Requires: python3-six
Requires: python3-sss-murmur
Requires: python3-yubico >= 1.3.2-7
+%if 0%{?rhel} && 0%{?rhel} == 8
+Requires: platform-python-setuptools
+%else
+Requires: python3-setuptools
+%endif
%if 0%{?rhel}
Requires: python3-urllib3 >= 1.24.2-3
%else
diff --git a/ipaplatform/base/tasks.py b/ipaplatform/base/tasks.py
index 9e221d872e7ca9ac0607ff29e1b51dedcf688d75..ab3563aba3decf370a72c9ec273c8bccc2d85ca2 100644
--- a/ipaplatform/base/tasks.py
+++ b/ipaplatform/base/tasks.py
@@ -29,7 +29,7 @@ import os
import logging
import textwrap
-from packaging.version import parse as parse_version
+from pkg_resources import parse_version
from ipaplatform.paths import paths
from ipapython import ipautil
diff --git a/ipapython/version.py.in b/ipapython/version.py.in
index eee8900be5fa72759fd7ee87f72952599231e138..a8f4218a7bac2a2f52389ad3dc5f31a98a822e82 100644
--- a/ipapython/version.py.in
+++ b/ipapython/version.py.in
@@ -17,7 +17,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
-from packaging.version import parse as parse_version
+from pkg_resources import parse_version
# The full version including strings
VERSION = "@VERSION@"
diff --git a/ipaserver/custodia/server/__init__.py b/ipaserver/custodia/server/__init__.py
index 26ca0a481559da101891034798e0434f063367b8..e713de20d0e9ff42e65ed3d2170d6446baa8d4e8 100644
--- a/ipaserver/custodia/server/__init__.py
+++ b/ipaserver/custodia/server/__init__.py
@@ -2,9 +2,10 @@
from __future__ import absolute_import
import importlib
-import importlib.metadata
import os
+import pkg_resources
+
import six
from ipaserver.custodia import log
@@ -36,13 +37,17 @@ def _load_plugin_class(menu, name):
Entry points are preferred over dotted import path.
"""
group = 'custodia.{}'.format(menu)
- eps = importlib.metadata.entry_points(group=group, name=name)
+ eps = list(pkg_resources.iter_entry_points(group, name))
if len(eps) > 1:
raise ValueError(
"Multiple entry points for {} {}: {}".format(menu, name, eps))
elif len(eps) == 1:
- ep, *_ = eps
- return ep.load(require=False)
+ # backwards compatibility with old setuptools
+ ep = eps[0]
+ if hasattr(ep, 'resolve'):
+ return ep.resolve()
+ else:
+ return ep.load(require=False)
elif '.' in name:
# fall back to old style dotted name
module, classname = name.rsplit('.', 1)
diff --git a/ipaserver/install/cainstance.py b/ipaserver/install/cainstance.py
index b8267a625554f9375d27160f39b67ee2e64a2dbb..a92b3ed6f01db97a86464e5cb77a084f8a1de90f 100644
--- a/ipaserver/install/cainstance.py
+++ b/ipaserver/install/cainstance.py
@@ -35,7 +35,7 @@ import syslog
import time
import tempfile
from configparser import RawConfigParser
-from packaging.version import parse as parse_version
+from pkg_resources import parse_version
from ipalib import api
from ipalib import x509
diff --git a/ipaserver/install/krbinstance.py b/ipaserver/install/krbinstance.py
index 4a521a611bd1bebf925061ea88a60876dff48467..0f568122c94aab8e55604179003276d29580de76 100644
--- a/ipaserver/install/krbinstance.py
+++ b/ipaserver/install/krbinstance.py
@@ -27,7 +27,7 @@ import tempfile
import dbus
import dns.name
-from packaging.version import parse as parse_version
+from pkg_resources import parse_version
from ipalib import x509
from ipalib.install import certstore
diff --git a/ipaserver/install/server/replicainstall.py b/ipaserver/install/server/replicainstall.py
index b4d06d8e24097e12c97a8b881dfcc9b028467272..5b4f0161083bbf65ac423ebacad30c5743ffab8e 100644
--- a/ipaserver/install/server/replicainstall.py
+++ b/ipaserver/install/server/replicainstall.py
@@ -18,7 +18,7 @@ import tempfile
import textwrap
import traceback
-from packaging.version import parse as parse_version
+from pkg_resources import parse_version
import six
from ipaclient.install.client import check_ldap_conf, sssd_enable_ifp
diff --git a/ipaserver/install/server/upgrade.py b/ipaserver/install/server/upgrade.py
index 548ee02e1e8524ce0002dca1764d48728eb0509a..d9cb6656b4731b5cb803b870469635862f4269e5 100644
--- a/ipaserver/install/server/upgrade.py
+++ b/ipaserver/install/server/upgrade.py
@@ -17,7 +17,7 @@ import sys
import tempfile
from contextlib import contextmanager
from augeas import Augeas
-from packaging.version import parse as parse_version
+from pkg_resources import parse_version
from ipalib import api, x509
from ipalib.constants import RENEWAL_CA_NAME, RA_AGENT_PROFILE, IPA_CA_RECORD
diff --git a/ipatests/pytest_ipa/integration/create_caless_pki.py b/ipatests/pytest_ipa/integration/create_caless_pki.py
index d06f1dd8c328628bd692c2abf3acfc88ba6a7408..01d64462ce76eb76cf7efdbeb850955ca4990535 100644
--- a/ipatests/pytest_ipa/integration/create_caless_pki.py
+++ b/ipatests/pytest_ipa/integration/create_caless_pki.py
@@ -26,7 +26,7 @@ from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.x509.oid import NameOID
-from packaging.version import parse as parse_version
+from pkg_resources import parse_version
from pyasn1.type import univ, char, namedtype, tag
from pyasn1.codec.der import encoder as der_encoder
from pyasn1.codec.native import decoder as native_decoder
diff --git a/ipatests/pytest_ipa/integration/tasks.py b/ipatests/pytest_ipa/integration/tasks.py
index 3ef361807ee0e1c858f4bef8b7d9e26c5f0fa20a..c6e0cf3937a12c7bb13da4b83250ecced470b543 100755
--- a/ipatests/pytest_ipa/integration/tasks.py
+++ b/ipatests/pytest_ipa/integration/tasks.py
@@ -36,7 +36,7 @@ import time
from shlex import quote
import configparser
from contextlib import contextmanager
-from packaging.version import parse as parse_version
+from pkg_resources import parse_version
import uuid
import dns
diff --git a/ipatests/test_custodia/test_plugins.py b/ipatests/test_custodia/test_plugins.py
index 9d3f6c24ff2eecad9e9a1c574aaae2edd30e4921..be8aa936db2bc553e0a13bb0975e88d186e3da1c 100644
--- a/ipatests/test_custodia/test_plugins.py
+++ b/ipatests/test_custodia/test_plugins.py
@@ -1,6 +1,5 @@
# Copyright (C) 2016 Custodia Project Contributors - see LICENSE file
-import importlib.metadata
-
+import pkg_resources
import pytest
from ipaserver.custodia.plugin import (
@@ -13,8 +12,8 @@ class TestCustodiaPlugins:
def get_entry_points(self, group):
eps = []
- for e in importlib.metadata.entry_points(group=group):
- if e.dist.name != self.project_name:
+ for e in pkg_resources.iter_entry_points(group):
+ if e.dist.project_name != self.project_name:
# only interested in our own entry points
continue
eps.append(e)
@@ -22,7 +21,11 @@ class TestCustodiaPlugins:
def assert_ep(self, ep, basecls):
try:
- cls = ep.load(require=False)
+ # backwards compatibility with old setuptools
+ if hasattr(ep, "resolve"):
+ cls = ep.resolve()
+ else:
+ cls = ep.load(require=False)
except Exception as e: # pylint: disable=broad-except
pytest.fail("Failed to load %r: %r" % (ep, e))
if not issubclass(cls, basecls):
diff --git a/ipatests/test_integration/test_adtrust_install.py b/ipatests/test_integration/test_adtrust_install.py
index 09e227ec8125e90b37d1d92f0512f9819f5b48c3..ac7e5663aa1c70184f5a09ea5e14cca8c671d78e 100644
--- a/ipatests/test_integration/test_adtrust_install.py
+++ b/ipatests/test_integration/test_adtrust_install.py
@@ -13,7 +13,7 @@ from ipaplatform.paths import paths
from ipapython.dn import DN
from ipatests.pytest_ipa.integration import tasks
from ipatests.test_integration.base import IntegrationTest
-from packaging.version import parse as parse_version
+from pkg_resources import parse_version
import pytest
diff --git a/ipatests/test_integration/test_cert.py b/ipatests/test_integration/test_cert.py
index 21568c2421c21855df06bcf5fbb4d52b3651a523..03ea5cfb000200d4d77b97d8808e30dbd5392871 100644
--- a/ipatests/test_integration/test_cert.py
+++ b/ipatests/test_integration/test_cert.py
@@ -20,7 +20,7 @@ from ipapython.dn import DN
from cryptography import x509
from cryptography.x509.oid import ExtensionOID
from cryptography.hazmat.backends import default_backend
-from packaging.version import parse as parse_version
+from pkg_resources import parse_version
from ipatests.pytest_ipa.integration import tasks
from ipatests.test_integration.base import IntegrationTest
diff --git a/ipatests/test_integration/test_commands.py b/ipatests/test_integration/test_commands.py
index 5bc871ab71bc05ec1c26df8352e996a9e627b466..04d8f71ff922f98c71331fa9a5f0f6508dd3fc12 100644
--- a/ipatests/test_integration/test_commands.py
+++ b/ipatests/test_integration/test_commands.py
@@ -41,7 +41,7 @@ from ipatests.test_ipalib.test_x509 import good_pkcs7, badcert
from ipapython.ipautil import realm_to_suffix, ipa_generate_password
from ipatests.test_integration.test_topology import find_segment
from ipaserver.install.installutils import realm_to_serverid
-from packaging.version import parse as parse_version
+from pkg_resources import parse_version
logger = logging.getLogger(__name__)
diff --git a/ipatests/test_integration/test_idp.py b/ipatests/test_integration/test_idp.py
index 9983b3c9d62826afa395c2739b4fb9945afed80d..a29333ef232b0ae8229484e404866d8bb720554a 100644
--- a/ipatests/test_integration/test_idp.py
+++ b/ipatests/test_integration/test_idp.py
@@ -12,7 +12,7 @@ from ipatests.pytest_ipa.integration import tasks, create_keycloak
user_code_script = textwrap.dedent("""
from selenium import webdriver
from datetime import datetime
-from packaging.version import parse as parse_version
+from pkg_resources import parse_version
from selenium.webdriver.firefox.options import Options
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
diff --git a/ipatests/test_integration/test_ipahealthcheck.py b/ipatests/test_integration/test_ipahealthcheck.py
index 47392cec9345b6d2447b728fc21050edabe8f0cd..15fc38a6e155b343259ee61c6c7c5c561c7f6410 100644
--- a/ipatests/test_integration/test_ipahealthcheck.py
+++ b/ipatests/test_integration/test_ipahealthcheck.py
@@ -28,7 +28,7 @@ from ipaplatform.osinfo import osinfo
from ipaserver.install.installutils import resolve_ip_addresses_nss
from ipatests.test_integration.test_caless import CALessBase
from ipatests.test_integration.base import IntegrationTest
-from packaging.version import parse as parse_version
+from pkg_resources import parse_version
from ipatests.test_integration.test_cert import get_certmonger_fs_id
from ipatests.test_integration.test_external_ca import (
install_server_external_ca_step1,
diff --git a/ipatests/test_webui/ui_driver.py b/ipatests/test_webui/ui_driver.py
index 356c4a0998cf01a6dbf1f207f38db87504aa5522..5dcea8979346119b2c11af7cf3f0b8c9b438cb40 100644
--- a/ipatests/test_webui/ui_driver.py
+++ b/ipatests/test_webui/ui_driver.py
@@ -29,7 +29,7 @@ import re
import time
from datetime import datetime
from functools import wraps
-from packaging.version import parse as parse_version
+from pkg_resources import parse_version
from urllib.error import URLError
import pytest
diff --git a/ipatests/test_xmlrpc/test_automember_plugin.py b/ipatests/test_xmlrpc/test_automember_plugin.py
index aa7c1d65a7059a6a1e911f31204c05a42b6d9c3f..94f1b10985de61046bb65c092ab3d31c6f99e17c 100644
--- a/ipatests/test_xmlrpc/test_automember_plugin.py
+++ b/ipatests/test_xmlrpc/test_automember_plugin.py
@@ -35,6 +35,7 @@ from ipaserver.plugins.automember import REBUILD_TASK_CONTAINER
import time
import pytest
+from pkg_resources import parse_version
try:
from ipaserver.plugins.ldap2 import ldap2
--
2.52.0

View File

@ -1,36 +0,0 @@
From 4fef80aeaaf017b286bd12ebfc30529f6a65a80e Mon Sep 17 00:00:00 2001
From: Florence Blanc-Renaud <flo@redhat.com>
Date: Mon, 2 Sep 2024 18:28:27 +0200
Subject: [PATCH] ipatests: Add missing comma in
test_idrange_no_rid_bases_reversed
The test is calling ipa idrange-add but is missing a comma in
the arguments list.
The resulting call is using "--rid-base 100300000--secondary-rid-base".
Add the missing comma to build the command with
"--rid-base 100300000 --secondary-rid-base"
Fixes: https://pagure.io/freeipa/issue/9656
Signed-off-by: Florence Blanc-Renaud <flo@redhat.com>
Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
---
ipatests/test_integration/test_ipa_idrange_fix.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/ipatests/test_integration/test_ipa_idrange_fix.py b/ipatests/test_integration/test_ipa_idrange_fix.py
index de3da9bfd221ce74f1d1bbb0dbe12e4db08b8daa..ff8fbdac9d028d26fc55f5e357f89af879a61723 100644
--- a/ipatests/test_integration/test_ipa_idrange_fix.py
+++ b/ipatests/test_integration/test_ipa_idrange_fix.py
@@ -72,7 +72,7 @@ class TestIpaIdrangeFix(IntegrationTest):
"idrange_reversed",
"--base-id", '50000',
"--range-size", '20000',
- "--rid-base", '100300000'
+ "--rid-base", '100300000',
"--secondary-rid-base", '301000'
])
--
2.46.2

View File

@ -1,32 +0,0 @@
From a18eb8358675b3697ccf8f8d8dc230cc62df6a4d Mon Sep 17 00:00:00 2001
From: Erik Belko <ebelko@redhat.com>
Date: Thu, 29 Aug 2024 16:47:21 +0200
Subject: [PATCH] ipatests: Update ipa-adtrust-install test
update test_user_connects_smb_share_if_locked_specific_group with wait
for SSSD to be online after ipa-adtrust-install command
Related: https://pagure.io/freeipa/issue/9655
Signed-off-by: Erik Belko <ebelko@redhat.com>
Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
---
ipatests/test_integration/test_adtrust_install.py | 2 ++
1 file changed, 2 insertions(+)
diff --git a/ipatests/test_integration/test_adtrust_install.py b/ipatests/test_integration/test_adtrust_install.py
index 72e8d874fb17adadc556ba55b825a88a3ac21a67..de252db1705ad940c3b5ee4df967d7c17a4203a7 100644
--- a/ipatests/test_integration/test_adtrust_install.py
+++ b/ipatests/test_integration/test_adtrust_install.py
@@ -853,6 +853,8 @@ class TestIpaAdTrustInstall(IntegrationTest):
self.master.config.admin_password,
"-U"]
)
+ # Wait for SSSD to become online before doing any other check
+ tasks.wait_for_sssd_domain_status_online(self.master)
self.master.run_command(["mkdir", "/freeipa4234"])
self.master.run_command(
["chcon", "-t", "samba_share_t",
--
2.46.2

View File

@ -1,33 +0,0 @@
From 373d41f211c1a04dc432a068bc7d2ba825ff554c Mon Sep 17 00:00:00 2001
From: Francisco Trivino <ftrivino@redhat.com>
Date: Tue, 13 Aug 2024 12:44:21 +0200
Subject: [PATCH] Installer: activate ssh service in sssd.conf
This commit enables SSSD's ssh service in ipa-client-install to ensure
sss_ssh_knownhosts and sss_ssh_knownhostsproxy functions properly.
Fixes: https://pagure.io/freeipa/issue/9649
Related: https://pagure.io/freeipa/issue/9536
Signed-off-by: Francisco Trivino <ftrivino@redhat.com>
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
---
ipaclient/install/client.py | 2 ++
1 file changed, 2 insertions(+)
diff --git a/ipaclient/install/client.py b/ipaclient/install/client.py
index 802db9614b24553b2b49259f3aebb366093560ac..47a371f629f6ddfb1cd5e9fff9faad737aa01f54 100644
--- a/ipaclient/install/client.py
+++ b/ipaclient/install/client.py
@@ -974,6 +974,8 @@ def configure_sssd_conf(
sssd_enable_service(sssdconfig, 'nss')
sssd_enable_service(sssdconfig, 'pam')
+ if options.conf_ssh:
+ sssd_enable_service(sssdconfig, 'ssh')
domain.set_option('ipa_domain', cli_domain)
domain.set_option('ipa_hostname', client_hostname)
--
2.46.2

View File

@ -1,413 +0,0 @@
From 8d242ba741ec22b258d5e70a530cefd0940783c7 Mon Sep 17 00:00:00 2001
From: Mark Reynolds <mreynolds@redhat.com>
Date: Tue, 23 Jul 2024 17:07:06 -0400
Subject: [PATCH] ipa-migrate - fix migration issues with entries using
ipaUniqueId in the RDN
We need to handle these entries differently and specify what attribute
and search base to use to find the entry on the local server. Most
entries can use the "cn" attribute but for selinux usermaps we need to
search using the ipaOwner attribute which is a DN, and in turn requires
additional handling/converting in order to properly check if the usermap
exists or not.
Also fixed an issue where an attribute should be removed from the local
entry if it does not exist on the remote entry.
And fixed the handling od "sudoOrder" which is defined as multi-valued
in the schema, but we really need to treat it as single-valued
Fixes: https://pagure.io/freeipa/issue/9640
Signed-off-by: Mark Reynolds <mreynolds@redhat.com>
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
---
ipaserver/install/ipa_migrate.py | 119 +++++++++++++++++++--
ipaserver/install/ipa_migrate_constants.py | 84 +++++++++++++--
2 files changed, 188 insertions(+), 15 deletions(-)
diff --git a/ipaserver/install/ipa_migrate.py b/ipaserver/install/ipa_migrate.py
index e21937401b3463335d8297b41a403405071d3795..78c530f24fe5d8c9f5de0f816df9904bf30c7b94 100644
--- a/ipaserver/install/ipa_migrate.py
+++ b/ipaserver/install/ipa_migrate.py
@@ -32,7 +32,7 @@ from ipaserver.install.ipa_migrate_constants import (
DS_CONFIG, DB_OBJECTS, DS_INDEXES, BIND_DN, LOG_FILE_NAME,
STRIP_OP_ATTRS, STRIP_ATTRS, STRIP_OC, PROD_ATTRS,
DNA_REGEN_VAL, DNA_REGEN_ATTRS, NIS_PLUGIN, IGNORE_ATTRS,
- DB_EXCLUDE_TREES
+ DB_EXCLUDE_TREES, POLICY_OP_ATTRS
)
"""
@@ -529,6 +529,14 @@ class IPAMigrate():
#
# Helper functions
#
+ def attr_is_operational(self, attr):
+ schema = self.local_conn.schema
+ attr_obj = schema.get_obj(ldap.schema.AttributeType, attr)
+ if attr_obj is not None:
+ if attr_obj.usage == 1:
+ return True
+ return False
+
def replace_suffix(self, entry_dn):
"""
Replace the base DN in an entry DN
@@ -1122,6 +1130,18 @@ class IPAMigrate():
stats['reset_range'] += 1
return entry
+ def attr_is_required(self, attr, entry):
+ """
+ Check if an attribute is required in this entry
+ """
+ entry_oc = entry['objectClass']
+ for oc in entry_oc:
+ required_attrs = self.local_conn.get_allowed_attributes(
+ [oc], raise_on_unknown=False, attributes="must")
+ if attr.lower() in required_attrs:
+ return True
+ return False
+
def clean_entry(self, entry_dn, entry_type, entry_attrs):
"""
Clean up the entry from the remote server
@@ -1311,7 +1331,17 @@ class IPAMigrate():
f"'{old_value}' "
"new value "
f"'{local_entry[attr][0]}'")
-
+ elif 'single' == sp_attr[1]:
+ # The attribute is defined as multivalued, but
+ # we really need to treat it as single valued
+ self.log_debug("Entry is different and will "
+ f"be updated: '{local_dn}' "
+ f"attribute '{attr}' replaced "
+ "with val "
+ f"'{remote_attrs[attr][0]}' "
+ "old value: "
+ f"{local_entry[attr][0]}")
+ local_entry[attr][0] = remote_attrs[attr][0]
goto_next_attr = True
break
@@ -1358,6 +1388,31 @@ class IPAMigrate():
local_entry[attr] = remote_attrs[attr]
entry_updated = True
+ # Remove attributes in the local entry that do not exist in the
+ # remote entry
+ remove_attrs = []
+ for attr in local_entry:
+ if (self.attr_is_operational(attr)
+ and attr.lower() not in POLICY_OP_ATTRS) or \
+ attr.lower() in IGNORE_ATTRS or \
+ attr.lower() in STRIP_ATTRS or \
+ attr.lower() == "usercertificate":
+ # This is an attribute that we do not want to remove
+ continue
+
+ if attr not in remote_attrs and \
+ not self.attr_is_required(attr, local_entry):
+ # Mark this attribute for deletion
+ remove_attrs.append(attr)
+ entry_updated = True
+
+ # Remove attributes
+ for remove_attr in remove_attrs:
+ self.log_debug("Entry is different and will be updated: "
+ f"'{local_dn}' attribute '{remove_attr}' "
+ "is being removed")
+ del local_entry[remove_attr]
+
if range_reset:
stats['reset_range'] += 1
@@ -1371,6 +1426,9 @@ class IPAMigrate():
"""
Process chunks of remote entries from a paged results search
+ entry_dn = the remote entry DN
+ entry_attrs = the remote entry's attributes stored in a dict
+
Identify entry type
Process entry (removing/change attr/val/schema)
Compare processed remote entry with local entry, merge/overwrite?
@@ -1426,6 +1484,47 @@ class IPAMigrate():
# Based on the entry type do additional work
#
+ # For entries with alternate identifying needs we need to rebuild the
+ # local dn. Typically this is for entries that use ipaUniqueId as the
+ # RDN attr
+ if entry_type != "custom" and 'alt_id' in DB_OBJECTS[entry_type]:
+ attr = DB_OBJECTS[entry_type]['alt_id']['attr']
+ base = DB_OBJECTS[entry_type]['alt_id']['base']
+ srch_filter = f'{attr}={entry_attrs[attr][0]}'
+ if DB_OBJECTS[entry_type]['alt_id']['isDN'] is True:
+ # Convert the filter to match the local suffix
+ srch_filter = self.replace_suffix(srch_filter)
+ srch_base = base + str(self.local_suffix)
+
+ try:
+ entries = self.local_conn.get_entries(DN(srch_base),
+ filter=srch_filter)
+ if len(entries) == 1:
+ local_dn = entries[0].dn
+ elif len(entries) == 0:
+ # Not found, no problem just proceed and we will add it
+ pass
+ else:
+ # Found too many entries - should not happen
+ self.log_error('Found too many local matching entries '
+ f'for "{local_dn}"')
+ if self.args.force:
+ stats['ignored_errors'] += 1
+ return
+ else:
+ sys.exit(1)
+ except errors.EmptyResult:
+ # Not found, no problem just proceed and we will add it later
+ pass
+ except (errors.NetworkError, errors.DatabaseError) as e:
+ self.log_error('Failed to find a local matching entry for '
+ f'"{local_dn}" error: {str(e)}')
+ if self.args.force:
+ stats['ignored_errors'] += 1
+ return
+ else:
+ sys.exit(1)
+
# See if the entry exists on the local server
try:
local_entry = self.local_conn.get_entry(DN(local_dn),
@@ -1441,14 +1540,20 @@ class IPAMigrate():
if self.dryrun:
self.write_update_to_ldif(local_entry)
- DB_OBJECTS[entry_type]['count'] += 1
+ if entry_type == "custom":
+ stats['custom'] += 1
+ else:
+ DB_OBJECTS[entry_type]['count'] += 1
stats['total_db_migrated'] += 1
return
# Update the local entry
try:
self.local_conn.update_entry(local_entry)
- DB_OBJECTS[entry_type]['count'] += 1
+ if entry_type == "custom":
+ stats['custom'] += 1
+ else:
+ DB_OBJECTS[entry_type]['count'] += 1
except errors.ExecutionError as e:
self.log_error(f'Failed to update "{local_dn}" error: '
f'{str(e)}')
@@ -1567,7 +1672,7 @@ class IPAMigrate():
"""
Used paged search for online method to avoid large memory footprint
"""
- self.log_info("Migrating database ... (this make take a while)")
+ self.log_info("Migrating database ... (this may take a while)")
if self.args.db_ldif is not None:
self.processDBOffline()
else:
@@ -1608,7 +1713,7 @@ class IPAMigrate():
f"{len(objectclasses)} objectClasses")
# Loop over attributes and objectclasses and count them
- schema = self.local_conn._get_schema()
+ schema = self.local_conn.schema
local_schema = schema.ldap_entry()
for schema_type in [(attributes, "attributeTypes"),
(objectclasses, "objectClasses")]:
@@ -1967,7 +2072,7 @@ class IPAMigrate():
# Run ipa-server-upgrade
self.log_info("Running ipa-server-upgrade ... "
- "(this make take a while)")
+ "(this may take a while)")
if self.dryrun:
self.log_info("Skipping ipa-server-upgrade in dryrun mode.")
else:
diff --git a/ipaserver/install/ipa_migrate_constants.py b/ipaserver/install/ipa_migrate_constants.py
index 0e26c75497b216f09ed450aa25a09c2102582326..250f1b5b01bf066d316a98489ab6153b89615173 100644
--- a/ipaserver/install/ipa_migrate_constants.py
+++ b/ipaserver/install/ipa_migrate_constants.py
@@ -19,6 +19,28 @@ STRIP_OP_ATTRS = [
'nsuniqueid',
'dsentrydn',
'entryuuid',
+ 'entrydn',
+ 'entryid',
+ 'entryusn',
+ 'numsubordinates',
+ 'parentid',
+ 'tombstonenumsubordinates'
+]
+
+# Operational attributes that we would want to remove from the local entry if
+# they don't exist in the remote entry
+POLICY_OP_ATTRS = [
+ 'nsaccountlock',
+ 'passwordexpiratontime',
+ 'passwordgraceusertime',
+ 'pwdpolicysubentry',
+ 'passwordexpwarned',
+ 'passwordretrycount',
+ 'retrycountresettime',
+ 'accountunlocktime',
+ 'passwordhistory',
+ 'passwordallowchangetime',
+ 'pwdreset'
]
# Atributes to strip from users/groups
@@ -110,7 +132,7 @@ STRIP_OC = [
#
# The DS_CONFIG mapping breaks each config entry (or type of entry) into its
# own catagory. Each catagory, or type, as DN list "dn", the attributes# we
-# are intrested in. These attributes are broken into singel valued "attrs",
+# are intrested in. These attributes are broken into single valued "attrs",
# or multi-valued attributes "multivalued". If the attributes is single
# valued then the value is replaced, if it's multivalued then it is "appended"
#
@@ -503,7 +525,7 @@ DS_CONFIG = {
}
#
-# Slpai NIS is an optional plugin. It requires special handling
+# Slapi NIS is an optional plugin. It requires special handling
#
NIS_PLUGIN = {
'dn': 'cn=NIS Server,cn=plugins,cn=config',
@@ -565,6 +587,12 @@ DS_INDEXES = {
# identify the entry.
# The "label" and "count" attributes are used for the Summary Report
#
+# Some entries use ipaUniqueId as the RDN attribute, this makes comparing
+# entries between the remote and local servers problematic. So we need special
+# identifying information to find the local entry. In this case we use the
+# "alt_id" key which is a dict of an attribute 'attr' and partial base DN
+# 'base' - which is expected to end in a comma.
+#
DB_OBJECTS = {
# Plugins
'automember_def': {
@@ -640,8 +668,8 @@ DB_OBJECTS = {
'oc': ['ipaconfigobject', 'ipaguiconfig'],
'subtree': 'cn=ipaconfig,cn=etc,$SUFFIX',
'special_attrs': [
- # needs special handling, but
- # ipa-server-upgrade rewrites this attribute anyway!
+ # needs special handling, but ipa-server-upgrade rewrites this
+ # attribute anyway!
('ipausersearchfields', 'list'),
],
'label': 'IPA Config',
@@ -772,11 +800,16 @@ DB_OBJECTS = {
'mode': 'all',
'count': 0,
},
- 'subids': { # unknown what these entries look like TODO
+ 'subids': {
'oc': [],
'subtree': ',cn=subids,cn=accounts,$SUFFIX',
'label': 'Sub IDs',
- 'mode': 'all', # TODO Maybe production only?
+ 'mode': 'production',
+ 'alt_id': {
+ 'attr': 'ipaOwner',
+ 'isDN': True,
+ 'base': 'cn=subids,cn=accounts,',
+ },
'count': 0,
},
@@ -884,6 +917,11 @@ DB_OBJECTS = {
'oc': ['ipahbacrule'],
'subtree': ',cn=hbac,$SUFFIX',
'label': 'HBAC Rules',
+ 'alt_id': {
+ 'attr': 'cn',
+ 'base': 'cn=hbac,',
+ 'isDN': False,
+ },
'mode': 'all',
'count': 0,
},
@@ -892,6 +930,11 @@ DB_OBJECTS = {
'selinux_usermap': { # Not sure if this is needed, entry is empty TODO
'oc': [],
'subtree': ',cn=usermap,cn=selinux,$SUFFIX',
+ 'alt_id': {
+ 'attr': 'cn',
+ 'base': 'cn=usermap,cn=selinux,',
+ 'isDN': False,
+ },
'label': 'Selinux Usermaps',
'mode': 'all',
'count': 0,
@@ -902,12 +945,27 @@ DB_OBJECTS = {
'oc': ['ipasudorule'],
'subtree': ',cn=sudorules,cn=sudo,$SUFFIX',
'label': 'Sudo Rules',
+ 'alt_id': {
+ 'attr': 'cn',
+ 'base': 'cn=sudorules,cn=sudo,',
+ 'isDN': False,
+ },
+ 'special_attrs': [
+ # schema defines sudoOrder as mutlivalued, but we need to treat
+ # it as single valued
+ ('sudoorder', 'single'),
+ ],
'mode': 'all',
'count': 0,
},
'sudo_cmds': {
'oc': ['ipasudocmd'],
'subtree': ',cn=sudocmds,cn=sudo,$SUFFIX',
+ 'alt_id': {
+ 'attr': 'sudoCmd',
+ 'base': 'cn=sudocmds,cn=sudo,',
+ 'isDN': False,
+ },
'label': 'Sudo Commands',
'mode': 'all',
'count': 0,
@@ -991,6 +1049,11 @@ DB_OBJECTS = {
'oc': ['ipanisnetgroup'],
'not_oc': ['mepmanagedentry'],
'subtree': ',cn=ng,cn=alt,$SUFFIX',
+ 'alt_id': {
+ 'attr': 'cn',
+ 'base': 'cn=ng,cn=alt,',
+ 'isDN': False,
+ },
'label': 'Network Groups',
'mode': 'all',
'count': 0,
@@ -1006,9 +1069,14 @@ DB_OBJECTS = {
'count': 0,
},
'caacls': {
- 'oc': ['top'],
+ 'oc': ['ipacaacl'],
'subtree': ',cn=caacls,cn=ca,$SUFFIX',
- 'label': 'CA Certificates',
+ 'alt_id': {
+ 'attr': 'cn',
+ 'base': 'cn=caacls,cn=ca,',
+ 'isDN': False,
+ },
+ 'label': 'CA Certificate ACLs',
'mode': 'all',
'count': 0,
},
--
2.46.2

View File

@ -1,68 +0,0 @@
From 3b5a980f5b65b03b9fd7ad0cfbb6c87874d3ff24 Mon Sep 17 00:00:00 2001
From: Mark Reynolds <mreynolds@redhat.com>
Date: Tue, 3 Sep 2024 13:42:05 -0400
Subject: [PATCH] ipa-migrate - fix alternate entry search filter
Processing a filter like a DN can cause normalization issues that result
in an invalid filter. Make sure the filter is encapsulated with
parenthesis and we call replace_suffix_value() instead of
replace_suffix()
Fixes: https://pagure.io/freeipa/issue/9658
Signed-off-by: Mark Reynolds <mreynolds@redhat.com>
Fix typo in test
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
---
ipaserver/install/ipa_migrate.py | 4 ++--
ipatests/test_integration/test_ipa_ipa_migration.py | 6 +++---
2 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/ipaserver/install/ipa_migrate.py b/ipaserver/install/ipa_migrate.py
index 78c530f24fe5d8c9f5de0f816df9904bf30c7b94..38356aa23ea435e2a616f48356feaea7b50dd1e4 100644
--- a/ipaserver/install/ipa_migrate.py
+++ b/ipaserver/install/ipa_migrate.py
@@ -1490,10 +1490,10 @@ class IPAMigrate():
if entry_type != "custom" and 'alt_id' in DB_OBJECTS[entry_type]:
attr = DB_OBJECTS[entry_type]['alt_id']['attr']
base = DB_OBJECTS[entry_type]['alt_id']['base']
- srch_filter = f'{attr}={entry_attrs[attr][0]}'
+ srch_filter = f'({attr}={entry_attrs[attr][0]})'
if DB_OBJECTS[entry_type]['alt_id']['isDN'] is True:
# Convert the filter to match the local suffix
- srch_filter = self.replace_suffix(srch_filter)
+ srch_filter = self.replace_suffix_value(srch_filter)
srch_base = base + str(self.local_suffix)
try:
diff --git a/ipatests/test_integration/test_ipa_ipa_migration.py b/ipatests/test_integration/test_ipa_ipa_migration.py
index f697bbfbfc6169309274db689501c99fe148cc70..288165e8a83a96e6f6bd4e52866f98617f497c56 100644
--- a/ipatests/test_integration/test_ipa_ipa_migration.py
+++ b/ipatests/test_integration/test_ipa_ipa_migration.py
@@ -610,7 +610,7 @@ class TestIPAMigrateScenario1(IntegrationTest):
MIGRATION_SCHEMA_LOG_MSG = "Migrating schema ...\n"
MIGRATION_CONFIG_LOG_MSG = "Migrating configuration ...\n"
IPA_UPGRADE_LOG_MSG = (
- "Running ipa-server-upgrade ... (this make take a while)\n"
+ "Running ipa-server-upgrade ... (this may take a while)\n"
)
SIDGEN_TASK_LOG_MSG = "Running SIDGEN task ...\n"
MIGRATION_COMPLETE_LOG_MSG = "Migration complete!\n"
@@ -641,10 +641,10 @@ class TestIPAMigrateScenario1(IntegrationTest):
tasks.kinit_admin(self.replicas[0])
MIGRATION_SCHEMA_LOG_MSG = "Migrating schema ...\n"
MIGRATION_DATABASE_LOG_MSG = (
- "Migrating database ... (this make take a while)\n"
+ "Migrating database ... (this may take a while)\n"
)
IPA_UPGRADE_LOG_MSG = (
- "Running ipa-server-upgrade ... (this make take a while)\n"
+ "Running ipa-server-upgrade ... (this may take a while)\n"
)
SIDGEN_TASK_LOG_MSG = "Running SIDGEN task ...\n"
result = run_migrate(
--
2.46.2

View File

@ -1,67 +0,0 @@
From a343c149838a3058794f33c75c58b75bc1748f7f Mon Sep 17 00:00:00 2001
From: Florence Blanc-Renaud <flo@redhat.com>
Date: Tue, 17 Sep 2024 17:00:49 +0200
Subject: [PATCH] ipatests: provide a ccache to rpcclient deletetrustdom
With samba update to samba-4.20.4, rpcclient now needs a
ccache otherwise it prompts for a password.
Fixes: https://pagure.io/freeipa/issue/9667
Signed-off-by: Florence Blanc-Renaud <flo@redhat.com>
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
---
ipatests/pytest_ipa/integration/tasks.py | 23 ++++++++++++++++++++---
1 file changed, 20 insertions(+), 3 deletions(-)
diff --git a/ipatests/pytest_ipa/integration/tasks.py b/ipatests/pytest_ipa/integration/tasks.py
index 9d6b5f67a311a28c335801d59e0ff0f0c7faccdd..677fb7534256a65940fb5280fa6412789dcba54f 100755
--- a/ipatests/pytest_ipa/integration/tasks.py
+++ b/ipatests/pytest_ipa/integration/tasks.py
@@ -795,15 +795,22 @@ def remove_trust_info_from_ad(master, ad_domain, ad_hostname):
kinit_as_user(master,
'Administrator@{}'.format(ad_domain.upper()),
master.config.ad_admin_password)
+ # Find cache for the user
+ cache_args = []
+ cache = get_credential_cache(master)
+ if cache:
+ cache_args = ["--use-krb5-ccache", cache]
+
# Detect whether rpcclient supports -k or --use-kerberos option
res = master.run_command(['rpcclient', '-h'], raiseonerr=False)
if "--use-kerberos" in res.stderr_text:
rpcclient_krb5_knob = "--use-kerberos=desired"
else:
rpcclient_krb5_knob = "-k"
- master.run_command(['rpcclient', rpcclient_krb5_knob, ad_hostname,
- '-c', 'deletetrustdom {}'.format(master.domain.name)],
- raiseonerr=False)
+ cmd_args = ['rpcclient', rpcclient_krb5_knob, ad_hostname]
+ cmd_args.extend(cache_args)
+ cmd_args.extend(['-c', 'deletetrustdom {}'.format(master.domain.name)])
+ master.run_command(cmd_args, raiseonerr=False)
def configure_auth_to_local_rule(master, ad):
@@ -1086,6 +1093,16 @@ def kinit_admin(host, raiseonerr=True):
raiseonerr=raiseonerr)
+def get_credential_cache(host):
+ # Return the credential cache currently in use on host or None
+ result = host.run_command(["klist"]).stdout_text
+ pattern = re.compile(r'Ticket cache: (?P<cache>.*)\n')
+ res = pattern.search(result)
+ if res:
+ return res['cache']
+ return None
+
+
def uninstall_master(host, ignore_topology_disconnect=True,
ignore_last_of_role=True, clean=True, verbose=False):
uninstall_cmd = ['ipa-server-install', '--uninstall', '-U']
--
2.46.2

View File

@ -1,60 +0,0 @@
From 743c7b46e463bef666dc84e9f513eb7dee7f59f6 Mon Sep 17 00:00:00 2001
From: Florence Blanc-Renaud <flo@redhat.com>
Date: Tue, 17 Sep 2024 14:48:58 +0200
Subject: [PATCH] test_adtrust_install: add --use-krb5-ccache to smbclient
command
With samba 4.20.4 the smbclient commands needs a ccache otherwise it
prompts for a password.
Fixes: https://pagure.io/freeipa/issue/9666
Signed-off-by: Florence Blanc-Renaud <flo@redhat.com>
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
---
.../test_integration/test_adtrust_install.py | 28 ++++++++++++-------
1 file changed, 18 insertions(+), 10 deletions(-)
diff --git a/ipatests/test_integration/test_adtrust_install.py b/ipatests/test_integration/test_adtrust_install.py
index de252db1705ad940c3b5ee4df967d7c17a4203a7..79a91dfaa61276de74b10777c6d44b5942ed1be0 100644
--- a/ipatests/test_integration/test_adtrust_install.py
+++ b/ipatests/test_integration/test_adtrust_install.py
@@ -873,17 +873,25 @@ class TestIpaAdTrustInstall(IntegrationTest):
"path", "/freeipa4234"])
self.master.run_command(["touch", "before"])
self.master.run_command(["touch", "after"])
- self.master.run_command(
- ["smbclient", "--use-kerberos=desired",
- "-c=put before", "//{}/share".format(
- self.master.hostname)]
- )
+ # Find cache for the admin user
+ cache_args = []
+ cache = tasks.get_credential_cache(self.master)
+ if cache:
+ cache_args = ["--use-krb5-ccache", cache]
+
+ cmd_args = ["smbclient", "--use-kerberos=desired"]
+ cmd_args.extend(cache_args)
+ cmd_args.extend([
+ "-c=put before", "//{}/share".format(self.master.hostname)
+ ])
+ self.master.run_command(cmd_args)
self.master.run_command(
["net", "conf", "setparm", "share",
"valid users", "@admins"])
- result = self.master.run_command(
- ["smbclient", "--use-kerberos=desired",
- "-c=put after", "//{}/share".format(
- self.master.hostname)]
- )
+ cmd_args = ["smbclient", "--use-kerberos=desired"]
+ cmd_args.extend(cache_args)
+ cmd_args.extend([
+ "-c=put after", "//{}/share".format(self.master.hostname)
+ ])
+ result = self.master.run_command(cmd_args)
assert msg not in result.stdout_text
--
2.46.2

View File

@ -1,209 +0,0 @@
From a785d0c561b8e22bd9d56739481095e07e0a7eb7 Mon Sep 17 00:00:00 2001
From: Rob Crittenden <rcritten@redhat.com>
Date: Mon, 30 Sep 2024 13:30:46 -0400
Subject: [PATCH] Don't rely on removing the CA to uninstall the ACME depoyment
There has always been a pki-server commnd acme-remove. We were
not aware that it should be called prior to removing a CA. In
11.5.0 this is strongly encouraged by the PKI team. In 11.6.0
ACME is treated as a full subsystem so will be removed in the
future using pkidestroy -s ACME
The new class acmeinstance.ACMEInstance is introduced so its
uninstallation can be handled in a similar way as the other
PKI services via DogtagInstance. It is, right now, a pretty
thin wrapper.
We can discuss moving the ACME installation routines here at
some point. It would be ok as long as we don't have to introduce
another PKI restart as part of it.
In PKI 11.6.0 pkidestroy has new options to ensure a clean
uninstall: --remove-conf --remove-logs. Pass those options
into pkidestroy calls for 11.6.0+.
Clean up an additional IPA-generated file that needs to be
cleaned up during uninstall: /root/kracert.p12. 11.6.0 is
more sensitive to leftover files than previous versions.
Fixes: https://pagure.io/freeipa/issue/9673
Fixes: https://pagure.io/freeipa/issue/9674
Signed-off-by: Rob Crittenden <rcritten@redhat.com>
Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
---
ipaserver/install/acmeinstance.py | 31 +++++++++++++
ipaserver/install/ca.py | 5 ++-
ipaserver/install/cainstance.py | 1 +
ipaserver/install/dogtaginstance.py | 44 +++++++++++++------
.../test_integration/test_uninstallation.py | 21 +++++++++
5 files changed, 87 insertions(+), 15 deletions(-)
create mode 100644 ipaserver/install/acmeinstance.py
diff --git a/ipaserver/install/acmeinstance.py b/ipaserver/install/acmeinstance.py
new file mode 100644
index 0000000000000000000000000000000000000000..0027c314545f384d9b6ee24b279479e5360d8bef
--- /dev/null
+++ b/ipaserver/install/acmeinstance.py
@@ -0,0 +1,31 @@
+#
+# Copyright (C) 2024 FreeIPA Contributors see COPYING for license
+#
+
+import logging
+
+from ipaserver.install.dogtaginstance import DogtagInstance
+
+logger = logging.getLogger(__name__)
+
+
+class ACMEInstance(DogtagInstance):
+ """
+ ACME is deployed automatically with a CA subsystem but it is the
+ responsibility of IPA to uninstall the service.
+
+ This is mostly a placeholder for the uninstaller. We can
+ eventually move the ACME installation routines into this class
+ if we want but it might result in an extra PKI restart which
+ would be slow.
+ """
+ def __init__(self, realm=None, host_name=None):
+ super(ACMEInstance, self).__init__(
+ realm=realm,
+ subsystem="ACME",
+ service_desc="ACME server",
+ host_name=host_name
+ )
+
+ def uninstall(self):
+ DogtagInstance.uninstall(self)
diff --git a/ipaserver/install/ca.py b/ipaserver/install/ca.py
index ffcb5268399ce71128fc8de5f54d433d35e99dd2..520e3fc5de1084e7c22c0cf7eaa86e1d3c421373 100644
--- a/ipaserver/install/ca.py
+++ b/ipaserver/install/ca.py
@@ -22,7 +22,7 @@ from ipaplatform.constants import constants
from ipaserver.install import sysupgrade
from ipapython.install import typing
from ipapython.install.core import group, knob, extend_knob
-from ipaserver.install import cainstance, bindinstance, dsinstance
+from ipaserver.install import acmeinstance, cainstance, bindinstance, dsinstance
from ipapython import ipautil, certdb
from ipapython import ipaldap
from ipapython.admintool import ScriptError
@@ -715,6 +715,9 @@ def install_step_1(standalone, replica_config, options, custodia):
def uninstall():
+ acme = acmeinstance.ACMEInstance(api.env.realm)
+ acme.uninstall()
+
ca_instance = cainstance.CAInstance(api.env.realm)
ca_instance.stop_tracking_certificates()
ipautil.remove_file(paths.RA_AGENT_PEM)
diff --git a/ipaserver/install/cainstance.py b/ipaserver/install/cainstance.py
index 5dac2c0441752e7bb569cde1fc93bc17c3128cdf..5c2c9f8b981cf5d587865f7680e2b231eae655e2 100644
--- a/ipaserver/install/cainstance.py
+++ b/ipaserver/install/cainstance.py
@@ -1118,6 +1118,7 @@ class CAInstance(DogtagInstance):
ipautil.remove_file(paths.DOGTAG_ADMIN_P12)
ipautil.remove_file(paths.CACERT_P12)
+ ipautil.remove_file(paths.ADMIN_CERT_PATH)
def unconfigure_certmonger_renewal_guard(self):
if not self.is_configured():
diff --git a/ipaserver/install/dogtaginstance.py b/ipaserver/install/dogtaginstance.py
index e89492312deb8ca20668a62fd7a2a20e2866a3fb..4b0f4d274b0c33140ed6f939f1a3fd8b75930ff9 100644
--- a/ipaserver/install/dogtaginstance.py
+++ b/ipaserver/install/dogtaginstance.py
@@ -304,21 +304,37 @@ class DogtagInstance(service.Service):
if self.is_installed():
self.print_msg("Unconfiguring %s" % self.subsystem)
- args = [paths.PKIDESTROY,
- "-i", "pki-tomcat", "--force",
- "-s", self.subsystem]
-
- # specify --log-file <path> on PKI 11.0.0 or later
-
+ args = []
pki_version = pki.util.Version(pki.specification_version())
- if pki_version >= pki.util.Version("11.0.0"):
- timestamp = time.strftime(
- "%Y%m%d%H%M%S",
- time.localtime(time.time()))
- log_file = os.path.join(
- paths.VAR_LOG_PKI_DIR,
- "pki-%s-destroy.%s.log" % (self.subsystem.lower(), timestamp))
- args.extend(["--log-file", log_file])
+ if self.subsystem == "ACME":
+ if pki_version < pki.util.Version("11.0.0"):
+ return
+ elif (
+ pki.util.Version("11.0.0") <= pki_version
+ <= pki.util.Version("11.5.0")
+ ):
+ args = ['pki-server', 'acme-remove']
+ else:
+ # fall through for PKI >= 11.6.0
+ pass
+ if not args:
+ args = [paths.PKIDESTROY,
+ "-i", "pki-tomcat", "--force",
+ "-s", self.subsystem]
+
+ # specify --log-file <path> on PKI 11.0.0 or later
+
+ if pki_version >= pki.util.Version("11.0.0"):
+ timestamp = time.strftime(
+ "%Y%m%d%H%M%S",
+ time.localtime(time.time()))
+ log_file = os.path.join(
+ paths.VAR_LOG_PKI_DIR,
+ "pki-%s-destroy.%s.log" %
+ (self.subsystem.lower(), timestamp))
+ args.extend(["--log-file", log_file])
+ if pki_version >= pki.util.Version("11.6.0"):
+ args.extend(["--remove-conf", "--remove-logs"])
try:
ipautil.run(args)
diff --git a/ipatests/test_integration/test_uninstallation.py b/ipatests/test_integration/test_uninstallation.py
index 4f8f17ce3ad8d5376ecba11442f379e5691de7f7..049c50db536ae1070f5f958e76b12a1518da0aba 100644
--- a/ipatests/test_integration/test_uninstallation.py
+++ b/ipatests/test_integration/test_uninstallation.py
@@ -197,6 +197,7 @@ class TestUninstallCleanup(IntegrationTest):
'/var/lib/sss/pubconf/krb5.include.d/localauth_plugin',
'/var/named/dynamic/managed-keys.bind',
'/var/named/dynamic/managed-keys.bind.jnl',
+ '/var/lib/systemd/coredump/',
]
leftovers = []
@@ -217,3 +218,23 @@ class TestUninstallCleanup(IntegrationTest):
leftovers.append(line)
assert len(leftovers) == 0
+
+
+class TestUninstallReinstall(IntegrationTest):
+ """Test install, uninstall, re-install.
+
+ Reinstall with PKI 11.6.0 was failing
+ https://pagure.io/freeipa/issue/9673
+ """
+
+ num_replicas = 0
+
+ @classmethod
+ def install(cls, mh):
+ tasks.install_master(cls.master, setup_dns=False)
+
+ def test_uninstall_server(self):
+ tasks.uninstall_master(self.master)
+
+ def test_reinstall_server(self):
+ tasks.install_master(self.master, setup_dns=False)
--
2.46.2

View File

@ -1,35 +0,0 @@
From ae4c2ad6cd966d48c063814f494dcc16cf0ccd4c Mon Sep 17 00:00:00 2001
From: Sudhir Menon <sumenon@redhat.com>
Date: Tue, 24 Sep 2024 13:46:48 +0530
Subject: [PATCH] ipatests: Fixes for ipa-idrange-fix testsuite
This patch adds the line tasks.install_master(cls.master).
The kinit admin command fails with the below error as the
IPA is not configured on the test system
'ipa: ERROR: stderr: kinit: Configuration file does not specify default
realm when parsing name admin'
Signed-off-by: Sudhir Menon <sumenon@redhat.com>
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
---
ipatests/test_integration/test_ipa_idrange_fix.py | 3 +++
1 file changed, 3 insertions(+)
diff --git a/ipatests/test_integration/test_ipa_idrange_fix.py b/ipatests/test_integration/test_ipa_idrange_fix.py
index ff8fbdac9d028d26fc55f5e357f89af879a61723..0c915bd0931ed11a3aa86c533ee8748aa8a7ec07 100644
--- a/ipatests/test_integration/test_ipa_idrange_fix.py
+++ b/ipatests/test_integration/test_ipa_idrange_fix.py
@@ -17,6 +17,9 @@ logger = logging.getLogger(__name__)
class TestIpaIdrangeFix(IntegrationTest):
+
+ topology = 'line'
+
@classmethod
def install(cls, mh):
super(TestIpaIdrangeFix, cls).install(mh)
--
2.46.2

View File

@ -1,265 +0,0 @@
From 18303b94bea4e08a0c889fc357df6ba2f308fa0d Mon Sep 17 00:00:00 2001
From: Mark Reynolds <mreynolds@redhat.com>
Date: Wed, 2 Oct 2024 21:26:34 -0400
Subject: [PATCH] Do not let user with an expired OTP token to log in if only
OTP is allowed
If only OTP authentication is allowed, and a user tries to login with an
expired token, do not let them log in with their password. Forcing the
admin to intervene. If the user does not have an OTP token then allow
them to log in with a password until an OTP token is configured
Fixes: https://pagure.io/freeipa/issue/9387
Signed-off-by: Mark Reynolds <mreynolds@redhat.com>
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
Reviewed-By: Julien Rische <jrische@redhat.com>
---
daemons/ipa-kdb/ipa_kdb_principals.c | 63 +++++++++++--
.../ipa-slapi-plugins/ipa-pwd-extop/prepost.c | 3 +-
ipatests/test_integration/test_otp.py | 94 ++++++++++++++++++-
3 files changed, 151 insertions(+), 9 deletions(-)
diff --git a/daemons/ipa-kdb/ipa_kdb_principals.c b/daemons/ipa-kdb/ipa_kdb_principals.c
index 14603e528b43acb29234c425e97ad297ac6724a7..114957b884786dd3ca3b01c47f6bb82e8a040beb 100644
--- a/daemons/ipa-kdb/ipa_kdb_principals.c
+++ b/daemons/ipa-kdb/ipa_kdb_principals.c
@@ -107,7 +107,6 @@ static char *std_principal_obj_classes[] = {
"krbprincipal",
"krbprincipalaux",
"krbTicketPolicyAux",
-
NULL
};
@@ -338,14 +337,16 @@ static void ipadb_validate_otp(struct ipadb_context *ipactx,
if (dn == NULL)
return;
count = asprintf(&filter, ftmpl, dn, datetime, datetime);
- ldap_memfree(dn);
- if (count < 0)
+ if (count < 0) {
+ ldap_memfree(dn);
return;
+ }
/* Fetch the active token list. */
kerr = ipadb_simple_search(ipactx, ipactx->base, LDAP_SCOPE_SUBTREE,
filter, (char**) attrs, &res);
free(filter);
+ filter = NULL;
if (kerr != 0 || res == NULL)
return;
@@ -353,10 +354,60 @@ static void ipadb_validate_otp(struct ipadb_context *ipactx,
count = ldap_count_entries(ipactx->lcontext, res);
ldap_msgfree(res);
- /* If the user is configured for OTP, but has no active tokens, remove
- * OTP from the list since the user obviously can't log in this way. */
- if (count == 0)
+ /*
+ * If there are no valid tokens then we need to remove the OTP flag,
+ * unless OTP is the only auth type allowed...
+ */
+ if (count == 0) {
+ /* Remove the OTP flag for now */
*ua &= ~IPADB_USER_AUTH_OTP;
+
+ if (*ua == 0) {
+ /*
+ * Ok, we "only" allow OTP, so if there is an expired/disabled
+ * token then add back the OTP flag as the server will double
+ * check the validity and reject the entire bind. Otherwise, this
+ * is the first time the user is authenticating and the user
+ * should be allowed to bind using its password
+ */
+ static const char *expired_ftmpl = "(&"
+ "(objectClass=ipaToken)(ipatokenOwner=%s)"
+ "(|(ipatokenNotAfter<=%s)(!(ipatokenNotAfter=*))"
+ "(ipatokenDisabled=True))"
+ ")";
+ if (asprintf(&filter, expired_ftmpl, dn, datetime) < 0) {
+ ldap_memfree(dn);
+ return;
+ }
+
+ krb5_klog_syslog(LOG_INFO,
+ "Entry (%s) does not have a valid token and only OTP "
+ "authentication is supported, checking for expired tokens...",
+ dn);
+
+ kerr = ipadb_simple_search(ipactx, ipactx->base, LDAP_SCOPE_SUBTREE,
+ filter, (char**) attrs, &res);
+ free(filter);
+ if (kerr != 0 || res == NULL) {
+ ldap_memfree(dn);
+ return;
+ }
+
+ if (ldap_count_entries(ipactx->lcontext, res) > 0) {
+ /*
+ * Ok we only allow OTP, and there are expired/disabled tokens
+ * so add the OTP flag back, and the server will reject the
+ * bind
+ */
+ krb5_klog_syslog(LOG_INFO,
+ "Entry (%s) does have an expired/disabled token so this "
+ "user can not fall through to password auth", dn);
+ *ua |= IPADB_USER_AUTH_OTP;
+ }
+ ldap_msgfree(res);
+ }
+ }
+ ldap_memfree(dn);
}
static void ipadb_validate_radius(struct ipadb_context *ipactx,
diff --git a/daemons/ipa-slapi-plugins/ipa-pwd-extop/prepost.c b/daemons/ipa-slapi-plugins/ipa-pwd-extop/prepost.c
index c967e2cfffbd920280639f3188783ec150523b47..1c1340e31ac30cb01412a7065ea339cb5461e839 100644
--- a/daemons/ipa-slapi-plugins/ipa-pwd-extop/prepost.c
+++ b/daemons/ipa-slapi-plugins/ipa-pwd-extop/prepost.c
@@ -1528,7 +1528,8 @@ static int ipapwd_pre_bind(Slapi_PBlock *pb)
if (!syncreq && (otpreq == OTP_IS_NOT_REQUIRED)) {
ret = ipapwd_gen_checks(pb, &errMesg, &krbcfg, IPAPWD_CHECK_ONLY_CONFIG);
if (ret != 0) {
- LOG_FATAL("ipapwd_gen_checks failed!?\n");
+ LOG_FATAL("ipapwd_gen_checks failed for '%s': %s\n",
+ slapi_sdn_get_dn(sdn), errMesg);
slapi_entry_free(entry);
slapi_sdn_free(&sdn);
return 0;
diff --git a/ipatests/test_integration/test_otp.py b/ipatests/test_integration/test_otp.py
index 350371bfe1e4c1cc6dcc89f6584f813fcb0d32a0..878b4fb560ba8d7768ead54b065656462545babd 100644
--- a/ipatests/test_integration/test_otp.py
+++ b/ipatests/test_integration/test_otp.py
@@ -10,6 +10,7 @@ import re
import time
import textwrap
from urllib.parse import urlparse, parse_qs
+from paramiko import AuthenticationException
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes
@@ -83,7 +84,7 @@ def kinit_otp(host, user, *, password, otp, success=True):
)
-def ssh_2f(hostname, username, answers_dict, port=22):
+def ssh_2f(hostname, username, answers_dict, port=22, unwanted_prompt=""):
"""
:param hostname: hostname
:param username: username
@@ -103,6 +104,10 @@ def ssh_2f(hostname, username, answers_dict, port=22):
logger.info("Prompt is: '%s'", prmpt_str)
logger.info(
"Answer to ssh prompt is: '%s'", answers_dict[prmpt_str])
+ if unwanted_prompt and prmpt_str == unwanted_prompt:
+ # We should not see this prompt
+ raise ValueError("We got an unwanted prompt: "
+ + answers_dict[prmpt_str])
return resp
import paramiko
@@ -193,7 +198,8 @@ class TestOTPToken(IntegrationTest):
# skipping too many OTP fails
otp1 = hotp.generate(10).decode("ascii")
- kinit_otp(self.master, USER, password=PASSWORD, otp=otp1, success=False)
+ kinit_otp(self.master, USER, password=PASSWORD, otp=otp1,
+ success=False)
# Now the token is desynchronized
yield (otpuid, hotp)
@@ -536,3 +542,87 @@ class TestOTPToken(IntegrationTest):
finally:
master.run_command(['ipa', 'pwpolicy-mod', '--minlife', '1'])
master.run_command(['ipa', 'user-del', USER1])
+
+ def test_totp_expired_ldap(self):
+ master = self.master
+ basedn = master.domain.basedn
+ USER1 = 'user-expired-otp'
+ TMP_PASSWORD = 'Secret1234509'
+ binddn = DN(f"uid={USER1},cn=users,cn=accounts,{basedn}")
+ controls = [
+ BooleanControl(
+ controlType="2.16.840.1.113730.3.8.10.7",
+ booleanValue=True)
+ ]
+
+ tasks.kinit_admin(master)
+ master.run_command(['ipa', 'pwpolicy-mod', '--minlife', '0'])
+ tasks.user_add(master, USER1, password=TMP_PASSWORD)
+ # Enforce use of OTP token for this user
+ master.run_command(['ipa', 'user-mod', USER1,
+ '--user-auth-type=otp'])
+ try:
+ # Change initial password through the IPA endpoint
+ url = f'https://{master.hostname}/ipa/session/change_password'
+ master.run_command(['curl', '-d', f'user={USER1}',
+ '-d', f'old_password={TMP_PASSWORD}',
+ '-d', f'new_password={PASSWORD}',
+ '--referer', f'https://{master.hostname}/ipa',
+ url])
+ conn = master.ldap_connect()
+ # First, attempt authenticating with a password but without LDAP
+ # control to enforce OTP presence and without server-side
+ # enforcement of the OTP presence check.
+ conn.simple_bind(binddn, f"{PASSWORD}")
+
+ # Add an OTP token and then modify it to be expired
+ otpuid, totp = add_otptoken(master, USER1, otptype="totp")
+
+ # Make sure OTP auth is working
+ otpvalue = totp.generate(int(time.time())).decode("ascii")
+ conn = master.ldap_connect()
+ conn.simple_bind(binddn, f"{PASSWORD}{otpvalue}",
+ client_controls=controls)
+ conn.unbind()
+
+ # Modfy token so that is now expired
+ args = [
+ "ipa",
+ "otptoken-mod",
+ otpuid,
+ "--not-after",
+ "20241001010000Z",
+ ]
+ master.run_command(args)
+
+ # Next, authenticate with Password+OTP again and with the LDAP
+ # control this operation should now fail
+ time.sleep(45)
+ otpvalue = totp.generate(int(time.time())).decode("ascii")
+
+ conn = master.ldap_connect()
+ with pytest.raises(errors.ACIError):
+ conn.simple_bind(binddn, f"{PASSWORD}{otpvalue}",
+ client_controls=controls)
+
+ # Sleep to make sure we are going to use a different token value
+ time.sleep(45)
+
+ # Use OTP token again but authenticate over ssh and make sure it
+ # doesn't fallthrough to asking for a password
+ otpvalue = totp.generate(int(time.time())).decode("ascii")
+ answers = {
+ 'Enter first factor:': PASSWORD,
+ 'Enter second factor:': otpvalue
+ }
+ with pytest.raises(AuthenticationException):
+ # ssh should fail and NOT ask for a password
+ ssh_2f(master.hostname, USER1, answers,
+ unwanted_prompt="Password:")
+
+ # Remove token
+ del_otptoken(self.master, otpuid)
+
+ finally:
+ master.run_command(['ipa', 'pwpolicy-mod', '--minlife', '1'])
+ master.run_command(['ipa', 'user-del', USER1])
--
2.46.2

View File

@ -1,41 +0,0 @@
From 761647f842567713032709753b6d63467d9871a6 Mon Sep 17 00:00:00 2001
From: Sudhir Menon <sumenon@redhat.com>
Date: Mon, 23 Sep 2024 14:05:43 +0530
Subject: [PATCH] ipatests: Activate ssh in sssd.conf
This testcase checks that services: ssh
is included in the sssd.conf file when
ipa-client-install is successful.
Ref: https://pagure.io/freeipa/issue/9649
Signed-off-by: Sudhir Menon <sumenon@redhat.com>
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
---
ipatests/test_integration/test_installation_client.py | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/ipatests/test_integration/test_installation_client.py b/ipatests/test_integration/test_installation_client.py
index f8567b39eead4dffd522aad504fa72a086969257..884bff2f2f3e49f66da391424e128d8e31b82a8a 100644
--- a/ipatests/test_integration/test_installation_client.py
+++ b/ipatests/test_integration/test_installation_client.py
@@ -94,6 +94,16 @@ class TestInstallClient(IntegrationTest):
).encode() not in krb5_cfg
tasks.uninstall_client(self.clients[0])
+ def test_check_ssh_service_is_activated(self):
+ """
+ This test checks all default services are activated
+ in sssd.conf including ssh
+ """
+ tasks.install_client(self.master, self.clients[0])
+ sssd_cfg = self.clients[0].get_file_contents(paths.SSSD_CONF)
+ assert 'services = nss, pam, ssh, sudo' in sssd_cfg.decode()
+ tasks.uninstall_client(self.clients[0])
+
def test_install_with_automount(self):
"""Test that installation with automount is successful"""
tasks.install_client(self.master, self.clients[0],
--
2.46.2

View File

@ -1,131 +0,0 @@
From f978fa05e3ed9d4ad9d20493c05c77fb9b4976a7 Mon Sep 17 00:00:00 2001
From: Florence Blanc-Renaud <flo@redhat.com>
Date: Tue, 15 Oct 2024 17:04:55 +0200
Subject: [PATCH] ipa-migrate man page: fix typos and errors
ipa-migrate man page mentions non-existing option --hostname.
Fix the SYNOPSIS and various typos.
Fixes: https://pagure.io/freeipa/issue/9681
Signed-off-by: Florence Blanc-Renaud <flo@redhat.com>
Reviewed-By: Mark Reynolds <mreynolds@redhat.com>
---
install/tools/man/ipa-migrate.1 | 38 +++++++++++++++------------------
1 file changed, 17 insertions(+), 21 deletions(-)
diff --git a/install/tools/man/ipa-migrate.1 b/install/tools/man/ipa-migrate.1
index 47ae47ea4afa3a5a6fe25dd9bbd14c27ab5f1fdb..5106f4f0f5b5928909ccd5abcef3bb6d1586f5df 100644
--- a/install/tools/man/ipa-migrate.1
+++ b/install/tools/man/ipa-migrate.1
@@ -5,11 +5,11 @@
.SH "NAME"
ipa\-migrate \- Migrate an IPA server from one machine to another
.SH "SYNOPSIS"
-ipa\-migrate
+\fBipa\-migrate\fR [OPTIONS] \fBprod\-mode\fR|\fBstage\-mode\fR \fIhostname\fR
.SH "DESCRIPTION"
Use the \fIipa-migrate\fR command to migrate one
-IPA server to an existing local IPA server installation.
+IPA server \fIhostname\fR to an existing local IPA server installation.
Migrate IPA schema, configuration, and database to a local IPA server. This
migration can be done online, where the tool will query the remote server. Or,
@@ -19,7 +19,6 @@ and then use an exported LDIF file for the database migration portion (this
might be more useful for very large databases as you don't need to worry about
network interruptions)
-.SH POSITIONAL ARGUMENTS
.TP
\fBprod\-mode\fR
In this mode everything will be migrated including the current user SIDs and
@@ -28,13 +27,10 @@ DNA ranges
\fBstage\-mode\fR
In this mode, SIDs & DNA ranges are not migrated, and DNA attributes are reset
-.SH "COMMANDS"
+.SH "OPTIONS"
.TP
\fB\-v\fR, \fB\-\-verbose\fR
-Use verbose output while running the migration tool.
-.TP
-\fB\-e\fR, \fB\-\-hostname=HOSTNAME\fR
-The host name of the remote IPA server that is being migrated from.
+Use verbose output while running the migration tool
.TP
\fB\-D\fR, \fB\-\-bind\-dn=BIND_DN\fR
The Bind DN (Distinguished Name) or an LDAP entry to bind to the remote IPA server with.
@@ -43,10 +39,10 @@ access to read the userPassword attribute. If ommitted the default is "cn=direc
.TP
\fB\-w\fR, \fB\-\-bind\-pw=PASSWORD\fR
The password for the Bind DN that is authenticating against the remote IPA server. If
-a password is not provided then the tool with prompt for the password if needed.
+a password is not provided then the tool with prompt for the password if needed
.TP
-\fB\-Just\fR, \fB\-\-bind\-pw\-file=FILE_PATH\fR
-Path to a file containing the password for the Bind DN.
+\fB\-j\fR, \fB\-\-bind\-pw\-file=FILE_PATH\fR
+Path to a file containing the password for the Bind DN
.TP
\fB\-Z\fR, \fB\-\-cacertfile=FILE_PATH\fR
Path to a file containing a CA Certificate that the remote server trusts
@@ -55,23 +51,23 @@ Path to a file containing a CA Certificate that the remote server trusts
Path to a file containing the migration log. By default the tool will use \fI/var/log/ipa-migrate.log\fR
.TP
\fB\-x\fR, \fB\-\-dryrun\fR
-Go through the migration process but do not write and data to the new IPA server.
+Go through the migration process but do not write any data to the new IPA server
.TP
\fB\-o\fR, \fB\-\-dryrun\-record=FILE_PATH\fR
Go through the migration process but do not write any data to the new IPA server. However, write the
-migration operations to an LDIF file which can be applied later or reused for multiple migrations.
+migration operations to an LDIF file which can be applied later or reused for multiple migrations
.TP
\fB\-r\fR, \fB\-\-reset\-range\fR
Reset the ID range for migrated users/groups. In "stage-mode" this is done automatically
.TP
\fB\-F\fR, \fB\-\-force\fR
-Ignore any errors and continue to proceed with migration effort.
+Ignore any errors and continue to proceed with migration effort
.TP
\fB\-q\fR, \fB\-\-quiet\fR
-Only log errors during the migration process.
+Only log errors during the migration process
.TP
\fB\-B\fR, \fB\-\-migrate\-dns\fR
-Migrate thr DNS records
+Migrate the DNS records
.TP
\fB\-S\fR, \fB\-\-skip\-schema\fR
Do not migrate the database schema
@@ -80,21 +76,21 @@ Do not migrate the database schema
Do not migrate the database configuration (dse.ldif/cn=config)
.TP
\fB\-O\fR, \fB\-\-schema\-overwrite\fR
-Overwrite existing schema definitions. By default duplicate schema is skipped.
+Overwrite existing schema definitions. By default duplicate schema is skipped
.TP
\fB\-s\fR, \fB\-\-subtree=DN\fR
Specifies a custom database subtree that should be included in the migration.
This is only needed if non-default subtrees/branches were added to the database
-outside of IPA.
+outside of IPA
.TP
\fB\-f\fR, \fB\-\-db\-ldif=FILE_PATH\fR
-LDIF file containing the entire backend. If omitted the tool will query the remote IPA server.
+LDIF file containing the entire backend. If omitted the tool will query the remote IPA server
.TP
\fB\-m\fR, \fB\-\-schema\-ldif=FILE_PATH\fR
-LDIF file containing the schema. If omitted the tool will query the remote IPA server.
+LDIF file containing the schema. If omitted the tool will query the remote IPA server
.TP
\fB\-g\fR, \fB\-\-config\-ldif=FILE_PATH\fR
-LDIF file containing the entire "cn=config" DIT. If omitted the tool will query the remote IPA server.
+LDIF file containing the entire "cn=config" DIT. If omitted the tool will query the remote IPA server
.TP
\fB\-n\fR, \fB\-\-no\-prompt\fR
Do not prompt for confirmation before starting migration. Use at your own risk!
--
2.46.2

View File

@ -1,61 +0,0 @@
From 7f4e7e1d6a2ae9d05a2dfcf620f4df07d09d9d2b Mon Sep 17 00:00:00 2001
From: Sudhir Menon <sumenon@redhat.com>
Date: Thu, 3 Oct 2024 18:45:31 +0530
Subject: [PATCH] ipatests: Test for ipa hbac rule duplication
This test checks that ipa-migrate is not creating duplicate default hbac rules
for allow_all and allow_systemd-user rules.
Related: https://pagure.io/freeipa/issue/9640
Signed-off-by: Sudhir Menon <sumenon@redhat.com>
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
Reviewed-By: Florence Blanc-Renaud <frenaud@redhat.com>
---
.../test_ipa_ipa_migration.py | 26 +++++++++++++++++++
1 file changed, 26 insertions(+)
diff --git a/ipatests/test_integration/test_ipa_ipa_migration.py b/ipatests/test_integration/test_ipa_ipa_migration.py
index 288165e8a83a96e6f6bd4e52866f98617f497c56..70c268951a0d7e40806742b16e62b764b2bae37b 100644
--- a/ipatests/test_integration/test_ipa_ipa_migration.py
+++ b/ipatests/test_integration/test_ipa_ipa_migration.py
@@ -9,6 +9,7 @@ from __future__ import absolute_import
from ipatests.test_integration.base import IntegrationTest
from ipatests.pytest_ipa.integration import tasks
from ipaplatform.paths import paths
+from collections import Counter
import pytest
import textwrap
@@ -920,3 +921,28 @@ class TestIPAMigrateScenario1(IntegrationTest):
)
assert result.returncode == 1
assert ERR_MSG in result.stderr_text
+
+ def test_ipa_hbac_rule_duplication(self):
+ """
+ This testcase checks that default hbac rules
+ are not duplicated on the local server when
+ ipa-migrate command is run.
+ """
+ run_migrate(
+ self.replicas[0],
+ "prod-mode",
+ self.master.hostname,
+ "cn=Directory Manager",
+ self.master.config.admin_password,
+ extra_args=['-n']
+ )
+ result = self.replicas[0].run_command(
+ ['ipa', 'hbacrule-find']
+ )
+ lines = result.stdout_text.splitlines()
+ line = []
+ for i in lines:
+ line.append(i.strip())
+ count = Counter(line)
+ assert count.get('Rule name: allow_all') < 2
+ assert count.get('Rule name: allow_systemd-user') < 2
--
2.46.2

View File

@ -1,116 +0,0 @@
From 142f52fc981fe9f1d693b79a7b49506af2e98829 Mon Sep 17 00:00:00 2001
From: Mohammad Rizwan <myusuf@redhat.com>
Date: Mon, 19 Aug 2024 16:08:53 +0530
Subject: [PATCH] ipatests: refactor password file handling in TestHSMInstall
When token and associated certs are not being cleaned
up properly, the subsequent installation fails. Hence
Password file related scenarios moved out to new test class
so that it have fresh installation.
Signed-off-by: Mohammad Rizwan <myusuf@redhat.com>
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
Reviewed-By: Florence Blanc-Renaud <frenaud@redhat.com>
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
Reviewed-By: Florence Blanc-Renaud <frenaud@redhat.com>
---
.../nightly_ipa-4-12_latest.yaml | 12 ++++++++
.../nightly_ipa-4-12_latest_selinux.yaml | 13 ++++++++
ipatests/test_integration/test_hsm.py | 30 ++++++++++---------
3 files changed, 41 insertions(+), 14 deletions(-)
diff --git a/ipatests/prci_definitions/nightly_ipa-4-12_latest.yaml b/ipatests/prci_definitions/nightly_ipa-4-12_latest.yaml
index 6d18e708fb0512ce21d8db68d4f1ab26849f40b7..07e2a8399ae4cc953adb415b975101ed20c67fd2 100644
--- a/ipatests/prci_definitions/nightly_ipa-4-12_latest.yaml
+++ b/ipatests/prci_definitions/nightly_ipa-4-12_latest.yaml
@@ -1950,6 +1950,18 @@ jobs:
timeout: 6300
topology: *master_3repl_1client
+ fedora-latest-ipa-4-12/test_hsm_TestHSMInstallPasswordFile:
+ requires: [fedora-latest-ipa-4-12/build]
+ priority: 50
+ job:
+ class: RunPytest
+ args:
+ build_url: '{fedora-latest-ipa-4-12/build_url}'
+ test_suite: test_integration/test_hsm.py::TestHSMInstallPasswordFile
+ template: *ci-ipa-4-12-latest
+ timeout: 6300
+ topology: *master_1repl
+
fedora-latest-ipa-4-12/test_hsm_TestHSMInstallADTrustBase:
requires: [fedora-latest-ipa-4-12/build]
priority: 50
diff --git a/ipatests/prci_definitions/nightly_ipa-4-12_latest_selinux.yaml b/ipatests/prci_definitions/nightly_ipa-4-12_latest_selinux.yaml
index 52686df9713975c9590b8a99edb7c3442531fecc..11046be13fca1e7403d0fd74329a66ded3927a6c 100644
--- a/ipatests/prci_definitions/nightly_ipa-4-12_latest_selinux.yaml
+++ b/ipatests/prci_definitions/nightly_ipa-4-12_latest_selinux.yaml
@@ -2105,6 +2105,19 @@ jobs:
timeout: 6300
topology: *master_3repl_1client
+ fedora-latest-ipa-4-12/test_hsm_TestHSMInstallPasswordFile:
+ requires: [fedora-latest-ipa-4-12/build]
+ priority: 50
+ job:
+ class: RunPytest
+ args:
+ build_url: '{fedora-latest-ipa-4-12/build_url}'
+ selinux_enforcing: True
+ test_suite: test_integration/test_hsm.py::TestHSMInstallPasswordFile
+ template: *ci-ipa-4-12-latest
+ timeout: 6300
+ topology: *master_1repl
+
fedora-latest-ipa-4-12/test_hsm_TestHSMInstallADTrustBase:
requires: [fedora-latest-ipa-4-12/build]
priority: 50
diff --git a/ipatests/test_integration/test_hsm.py b/ipatests/test_integration/test_hsm.py
index 374f5c25fd3453cd45a15d2b0f20cee424282595..42895fcd60a7c02d3b6103c2f6751a367da30b2f 100644
--- a/ipatests/test_integration/test_hsm.py
+++ b/ipatests/test_integration/test_hsm.py
@@ -312,24 +312,26 @@ class TestHSMInstall(BaseHSMTest):
assert returncode == 0
assert output == "No issues found."
- def test_hsm_install_server_password_file(self):
- check_version(self.master)
- # cleanup before fresh install with password file
- for client in self.clients:
- tasks.uninstall_client(client)
- for replica in self.replicas:
- tasks.uninstall_master(replica)
+class TestHSMInstallPasswordFile(BaseHSMTest):
- tasks.uninstall_master(self.master)
+ num_replicas = 1
- delete_hsm_token([self.master] + self.replicas, self.token_name)
- self.token_name, self.token_password = get_hsm_token(self.master)
- self.master.put_file_contents(self.token_password_file,
- self.token_password)
- self.replicas[0].put_file_contents(self.token_password_file,
- self.token_password)
+ @classmethod
+ def install(cls, mh):
+ check_version(cls.master)
+ # Enable pkiuser to read softhsm tokens
+ cls.master.run_command(['usermod', 'pkiuser', '-a', '-G', 'ods'])
+ cls.token_name, cls.token_password = get_hsm_token(cls.master)
+ cls.master.put_file_contents(
+ cls.token_password_file, cls.token_password
+ )
+ cls.replicas[0].put_file_contents(
+ cls.token_password_file, cls.token_password
+ )
+ def test_hsm_install_server_password_file(self):
+ check_version(self.master)
tasks.install_master(
self.master, setup_dns=self.master_with_dns,
setup_kra=self.master_with_kra,
--
2.46.2

View File

@ -1,276 +0,0 @@
From 6ac11ae003740faf19f3c75bf542ec44f717114f Mon Sep 17 00:00:00 2001
From: Madhuri Upadhye <mupadhye@redhat.com>
Date: Tue, 23 Jul 2024 18:14:36 +0530
Subject: [PATCH] ipatests: 2FA test cases
Added following:
Added 'ssh_2fa_with_cmd' method for authentication,
as for '\n' with paramiko did not work. In a test case
need to just press `Enter` for `second factor`.
Advantage of above function is no having paramiko
dependancy.
We can run the any command in same session after
authentication of user.
Test cases:
1. Authenticate the user only with password,
just press enter at `Second factor` and check tgt after auth.
when User authentication types: otp, password
2. Authenticate the user with password and otpvalues and
check tgt of user after auth when
User authentication types: otp, password
related: https://github.com/SSSD/sssd/pull/7500
Signed-off-by: Madhuri Upadhye <mupadhye@redhat.com>
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
---
ipatests/test_integration/test_otp.py | 192 ++++++++++++++++++++++++--
1 file changed, 181 insertions(+), 11 deletions(-)
diff --git a/ipatests/test_integration/test_otp.py b/ipatests/test_integration/test_otp.py
index 878b4fb560ba8d7768ead54b065656462545babd..0babb45897c6107bf354477dbb0d3a805a3116f5 100644
--- a/ipatests/test_integration/test_otp.py
+++ b/ipatests/test_integration/test_otp.py
@@ -5,26 +5,27 @@
"""
import base64
import logging
-import pytest
import re
-import time
+import tempfile
import textwrap
-from urllib.parse import urlparse, parse_qs
-from paramiko import AuthenticationException
+import time
+from urllib.parse import parse_qs, urlparse
+import pytest
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.twofactor.hotp import HOTP
from cryptography.hazmat.primitives.twofactor.totp import TOTP
-
-from ipatests.test_integration.base import IntegrationTest
-from ipaplatform.paths import paths
-from ipatests.pytest_ipa.integration import tasks
-from ipapython.dn import DN
-
from ldap.controls.simple import BooleanControl
+from paramiko import AuthenticationException
from ipalib import errors
+from ipaplatform.osinfo import osinfo
+from ipaplatform.paths import paths
+from ipapython.dn import DN
+from ipatests.pytest_ipa.integration import tasks
+from ipatests.test_integration.base import IntegrationTest
+from ipatests.util import xfail_context
PASSWORD = "DummyPassword123"
USER = "opttestuser"
@@ -84,6 +85,65 @@ def kinit_otp(host, user, *, password, otp, success=True):
)
+def ssh_2fa_with_cmd(host, hostname, username, password, otpvalue,
+ command="exit 0"):
+ """ ssh to user and in same session pass the command to check tgt of user
+ :param host: host to ssh
+ :param hostname: hostname to ssh
+ :param str username: The name of user
+ :param str password: password, usually the first factor
+ :param str otpvalue: generated pin of user
+ :param str command: command to execute in same session,
+ by deafult set to "exit 0"
+ :return: object class of expect command run
+ """
+ temp_conf = tempfile.NamedTemporaryFile(suffix='.exp', delete=False)
+ with open(temp_conf.name, 'w') as tfile:
+ tfile.write('proc exitmsg { msg code } {\n')
+ tfile.write('\t# Close spawned program, if we are in the prompt\n')
+ tfile.write('\tcatch close\n\n')
+ tfile.write('\t# Wait for the exit code\n')
+ tfile.write('\tlassign [wait] pid spawnid os_error_flag rc\n\n')
+ tfile.write('\tputs ""\n')
+ tfile.write('\tputs "expect result: $msg"\n')
+ tfile.write('\tputs "expect exit code: $code"\n')
+ tfile.write('\tputs "expect spawn exit code: $rc"\n')
+ tfile.write('\texit $code\n')
+ tfile.write('}\n')
+ tfile.write('set timeout 60\n')
+ tfile.write('set prompt ".*\\[#\\$>\\] $"\n')
+ tfile.write(f'set password "{password}"\n')
+ tfile.write(f'set otpvalue "{otpvalue}"\n')
+ tfile.write(f'spawn ssh -o NumberOfPasswordPrompts=1 -o '
+ f'StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null'
+ f' -l {username} {hostname} {command}\n')
+ tfile.write('expect {\n')
+ tfile.write('"Enter first factor:*" {send -- "$password\r"}\n')
+ tfile.write('timeout {exitmsg "Unexpected output" 201}\n')
+ tfile.write('eof {exitmsg "Unexpected end of file" 202}\n')
+ tfile.write('}\n')
+ tfile.write('expect {\n')
+ tfile.write('"Enter second factor:*" {send -- "$otpvalue\r"}\n')
+ tfile.write('timeout {exitmsg "Unexpected output" 201}\n')
+ tfile.write('eof {exitmsg "Unexpected end of file" 202}\n')
+ tfile.write('}\n')
+ tfile.write('expect {\n')
+ tfile.write('"Authentication failure" '
+ '{exitmsg "Authentication failure" 1}\n')
+ tfile.write('eof {exitmsg "Authentication successful" 0}\n')
+ tfile.write('timeout {exitmsg "Unexpected output" 201}\n')
+ tfile.write('}\n')
+ tfile.write('expect {\n')
+ tfile.write('exitmsg "Unexpected code path" 203\n')
+ tfile.write('EOF\n')
+ tfile.write('}')
+ host.transport.put_file(temp_conf.name, '/tmp/ssh.exp')
+ tasks.clear_sssd_cache(host)
+ expect_cmd = 'expect -f /tmp/ssh.exp'
+ cmd = host.run_command(expect_cmd, raiseonerr=False)
+ return cmd
+
+
def ssh_2f(hostname, username, answers_dict, port=22, unwanted_prompt=""):
"""
:param hostname: hostname
@@ -91,6 +151,7 @@ def ssh_2f(hostname, username, answers_dict, port=22, unwanted_prompt=""):
:param answers_dict: dictionary of options with prompt_message and value.
:param port: port for ssh
"""
+
# Handler for server questions
def answer_handler(title, instructions, prompt_list):
resp = []
@@ -131,8 +192,9 @@ class TestOTPToken(IntegrationTest):
@classmethod
def install(cls, mh):
- super(TestOTPToken, cls).install(mh)
master = cls.master
+ tasks.install_packages(master, ['expect'])
+ super(TestOTPToken, cls).install(mh)
tasks.kinit_admin(master)
# create service with OTP auth indicator
@@ -398,6 +460,114 @@ class TestOTPToken(IntegrationTest):
self.master.run_command(['semanage', 'login', '-D'])
sssd_conf_backup.restore()
+ def test_2fa_only_with_password(self):
+ """Test ssh with 2FA only with the password(first factor) when
+ user-auth-type is opt and password.
+
+ Test for : https://github.com/SSSD/sssd/pull/7500
+
+ Add the IPA user and user-auth-type set to opt and password.
+ Authenticate the user only with password, just press enter
+ at `Second factor`
+ """
+ master = self.master
+ USER3 = 'sshuser3'
+ sssd_conf_backup = tasks.FileBackup(master, paths.SSSD_CONF)
+ first_prompt = 'Enter first factor:'
+ second_prompt = 'Enter second factor:'
+ add_contents = textwrap.dedent('''
+ [prompting/2fa/sshd]
+ single_prompt = False
+ first_prompt = {0}
+ second_prompt = {1}
+ ''').format(first_prompt, second_prompt)
+ set_sssd_conf(master, add_contents)
+ tasks.create_active_user(master, USER3, PASSWORD)
+ tasks.kinit_admin(master)
+ master.run_command(['ipa', 'user-mod', USER3, '--user-auth-type=otp',
+ '--user-auth-type=password'])
+ try:
+ otpuid, totp = add_otptoken(master, USER3, otptype='totp')
+ master.run_command(['ipa', 'otptoken-show', otpuid])
+ totp.generate(int(time.time())).decode('ascii')
+ otpvalue = "\n"
+ tasks.clear_sssd_cache(self.master)
+ github_ticket = "https://github.com/SSSD/sssd/pull/7500"
+ sssd_version = tasks.get_sssd_version(master)
+ rhel_fail = (
+ osinfo.id == 'rhel'
+ and sssd_version < tasks.parse_version("2.9.5")
+ )
+ fedora_fail = (
+ osinfo.id == 'fedora'
+ and sssd_version == tasks.parse_version("2.9.5")
+ )
+ with xfail_context(rhel_fail or fedora_fail, reason=github_ticket):
+ result = ssh_2fa_with_cmd(master,
+ self.master.external_hostname,
+ USER3, PASSWORD, otpvalue=otpvalue,
+ command="klist")
+ print(result.stdout_text)
+ assert ('Authentication successful') in result.stdout_text
+ assert USER3 in result.stdout_text
+ assert (f'Default principal: '
+ f'{USER3}@{self.master.domain.realm}' in
+ result.stdout_text)
+ cmd = self.master.run_command(['semanage', 'login', '-l'])
+ assert USER3 in cmd.stdout_text
+ finally:
+ master.run_command(['ipa', 'user-del', USER3])
+ self.master.run_command(['semanage', 'login', '-D'])
+ sssd_conf_backup.restore()
+
+ def test_2fa_with_otp_password(self):
+ """Test ssh with 2FA only with password and otpvalue when
+ user-auth-type is opt and password.
+
+ Test for : https://github.com/SSSD/sssd/pull/7500
+
+ Add the IPA user and user-auth-type set to opt and password.
+ Authenticate the user only with password and otpvalue.
+ """
+ master = self.master
+ USER4 = 'sshuser4'
+ sssd_conf_backup = tasks.FileBackup(master, paths.SSSD_CONF)
+ first_prompt = 'Enter first factor:'
+ second_prompt = 'Enter second factor:'
+ add_contents = textwrap.dedent('''
+ [prompting/2fa/sshd]
+ single_prompt = False
+ first_prompt = {0}
+ second_prompt = {1}
+ ''').format(first_prompt, second_prompt)
+ set_sssd_conf(master, add_contents)
+ tasks.create_active_user(master, USER4, PASSWORD)
+ tasks.kinit_admin(master)
+
+ master.run_command(['ipa', 'user-mod', USER4, '--user-auth-type=otp',
+ '--user-auth-type=password'])
+ try:
+ otpuid, totp = add_otptoken(master, USER4, otptype='totp')
+ master.run_command(['ipa', 'otptoken-show', otpuid])
+ otpvalue = totp.generate(int(time.time())).decode('ascii')
+ tasks.clear_sssd_cache(self.master)
+ result = ssh_2fa_with_cmd(master,
+ self.master.external_hostname,
+ USER4, PASSWORD, otpvalue=otpvalue,
+ command="klist")
+ print(result.stdout_text)
+ cmd = self.master.run_command(['semanage', 'login', '-l'])
+ # check the output
+ assert ('Authentication successful') in result.stdout_text
+ assert USER4 in result.stdout_text
+ assert (f'Default principal: {USER4}@'
+ f'{self.master.domain.realm}' in result.stdout_text)
+ assert USER4 in cmd.stdout_text
+ finally:
+ master.run_command(['ipa', 'user-del', USER4])
+ self.master.run_command(['semanage', 'login', '-D'])
+ sssd_conf_backup.restore()
+
@pytest.fixture
def setup_otp_nsslapd(self):
check_services = self.master.run_command(
--
2.46.2

View File

@ -1,34 +0,0 @@
From 9a2de23eb5e00efa72189c4a86d9db1fab52c2ca Mon Sep 17 00:00:00 2001
From: Rob Crittenden <rcritten@redhat.com>
Date: Thu, 24 Oct 2024 11:49:17 -0400
Subject: [PATCH] Small fixup to determine which ACME uninstaller to use
The conditional was <= 11.5.0 which it should have been
< 11.6.0 to allow for small updates to the 11.5.0 branch.
Fixes: https://pagure.io/freeipa/issue/9673
Fixes: https://pagure.io/freeipa/issue/9674
Signed-off-by: Rob Crittenden <rcritten@redhat.com>
Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
---
ipaserver/install/dogtaginstance.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/ipaserver/install/dogtaginstance.py b/ipaserver/install/dogtaginstance.py
index 4b0f4d274b0c33140ed6f939f1a3fd8b75930ff9..58421a1d8859e5dd1357e07fd605e84e49048951 100644
--- a/ipaserver/install/dogtaginstance.py
+++ b/ipaserver/install/dogtaginstance.py
@@ -311,7 +311,7 @@ class DogtagInstance(service.Service):
return
elif (
pki.util.Version("11.0.0") <= pki_version
- <= pki.util.Version("11.5.0")
+ < pki.util.Version("11.6.0")
):
args = ['pki-server', 'acme-remove']
else:
--
2.47.0

View File

@ -1,34 +0,0 @@
From a9e653ca36a0829ae59cd204e7388d7a6c91e082 Mon Sep 17 00:00:00 2001
From: Florence Blanc-Renaud <flo@redhat.com>
Date: Fri, 13 Sep 2024 09:58:36 +0200
Subject: [PATCH] UnsafeIPAddress: pass flag=0 to IPNetwork
When parsing a string, the constructor tries to parse the value
as an IP Address first, or falls back to an IPNetwork with the
flags INET_PTON.
Use the flag 0 instead for an IPNetwork.
Fixes: https://pagure.io/freeipa/issue/9645
Signed-off-by: Florence Blanc-Renaud <flo@redhat.com>
Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
---
ipapython/ipautil.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/ipapython/ipautil.py b/ipapython/ipautil.py
index 3e98bfd6a66f24933e7e4de8efb79f4f5bf8bd0e..c237d59fb4b8be4187fb0efb04b097ff4df6c182 100644
--- a/ipapython/ipautil.py
+++ b/ipapython/ipautil.py
@@ -119,7 +119,7 @@ class UnsafeIPAddress(netaddr.IPAddress):
if addr.version != 6:
raise
except ValueError:
- self._net = netaddr.IPNetwork(addr, flags=self.netaddr_ip_flags)
+ self._net = netaddr.IPNetwork(addr, flags=0)
addr = self._net.ip
super(UnsafeIPAddress, self).__init__(addr,
flags=self.netaddr_ip_flags)
--
2.47.0

View File

@ -1,35 +0,0 @@
From 3d0962014adda39b754c4274ccb5ca5d70963c33 Mon Sep 17 00:00:00 2001
From: Mark Reynolds <mreynolds@redhat.com>
Date: Mon, 21 Oct 2024 13:51:13 -0400
Subject: [PATCH] ipa-migrate - dryrun write updates crashes when removing
values
When removing values the mod value list is None and that leads to a
crash when trying to iterate it. Instead check that the vals are not
None before looping.
Fixes: https://pagure.io/freeipa/issue/9682
Signed-off-by: MArk Reynolds <mreynolds@redhat.com>
Reviewed-By: Florence Blanc-Renaud <frenaud@redhat.com>
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
---
ipaserver/install/ipa_migrate.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/ipaserver/install/ipa_migrate.py b/ipaserver/install/ipa_migrate.py
index 38356aa23ea435e2a616f48356feaea7b50dd1e4..f35629378490d3d45ca97f2aa5b4390c67d660ed 100644
--- a/ipaserver/install/ipa_migrate.py
+++ b/ipaserver/install/ipa_migrate.py
@@ -622,7 +622,7 @@ class IPAMigrate():
else:
action = "replace"
ldif_entry += f"{action}: {attr}\n"
- for val in vals:
+ for val in list(vals or []):
ldif_entry += get_ldif_attr_val(attr, val)
ldif_entry += "-\n"
ldif_entry += "\n"
--
2.47.0

View File

@ -1,30 +0,0 @@
From 6bdb8603054fc60e9479f6aaf8b6315dfe508891 Mon Sep 17 00:00:00 2001
From: Mark Reynolds <mreynolds@redhat.com>
Date: Tue, 22 Oct 2024 13:00:03 -0400
Subject: [PATCH] ipa-migrate should migrate dns forward zones
Fixes: https://pagure.io/freeipa/issue/9686
Signed-off-by: Mark Reynolds <mreynolds@redhat.com>
Reviewed-By: Florence Blanc-Renaud <frenaud@redhat.com>
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
---
ipaserver/install/ipa_migrate_constants.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/ipaserver/install/ipa_migrate_constants.py b/ipaserver/install/ipa_migrate_constants.py
index 250f1b5b01bf066d316a98489ab6153b89615173..c140414ea6c607a93e35ef0705480d1002b7945e 100644
--- a/ipaserver/install/ipa_migrate_constants.py
+++ b/ipaserver/install/ipa_migrate_constants.py
@@ -993,7 +993,7 @@ DB_OBJECTS = {
'count': 0,
},
'dns_records': {
- 'oc': ['idnsrecord', 'idnszone'],
+ 'oc': ['idnsrecord', 'idnszone', 'idnsforwardzone'],
'subtree': ',cn=dns,$SUFFIX',
'label': 'DNS Records',
'mode': 'all',
--
2.47.0

View File

@ -1,784 +0,0 @@
From 9da927c8eec7db6d1c75c296eef45beb93797e58 Mon Sep 17 00:00:00 2001
From: Sudhir Menon <sumenon@redhat.com>
Date: Thu, 1 Aug 2024 16:30:16 +0530
Subject: [PATCH] ipatests: Tests for ipa-migrate tool
This patch includes test to covers below scenarios
1. hbac and sudo rules are migrated to local server
2. uid for user migrated varies in stage/prod mode.
3. subids are migrated to local server
4. idranges are migrated to local server
5. vaults are not migrated to local server.
6. Ensure trust related data is also migrated to local server
7. Added paths.IPA_MIGRATE_LOG in ipatests/pytest_ipa/integration/__init__.py
Signed-off-by: Sudhir Menon <sumenon@redhat.com>
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
---
ipatests/pytest_ipa/integration/__init__.py | 2 +
.../test_ipa_ipa_migration.py | 596 ++++++++++++++----
2 files changed, 460 insertions(+), 138 deletions(-)
diff --git a/ipatests/pytest_ipa/integration/__init__.py b/ipatests/pytest_ipa/integration/__init__.py
index 34b6ef0fb1e49fbb9c86e7496de50cf5cda5e91e..eb032cd72d2aa2a5ed4c476e3cb04dc77f607eaa 100644
--- a/ipatests/pytest_ipa/integration/__init__.py
+++ b/ipatests/pytest_ipa/integration/__init__.py
@@ -88,6 +88,8 @@ CLASS_LOGFILES = [
paths.VAR_LOG_AUDIT,
# sssd
paths.VAR_LOG_SSSD_DIR,
+ # ipa-ipa-migration logs
+ paths.IPA_MIGRATE_LOG,
# system
paths.RESOLV_CONF,
paths.HOSTS,
diff --git a/ipatests/test_integration/test_ipa_ipa_migration.py b/ipatests/test_integration/test_ipa_ipa_migration.py
index 70c268951a0d7e40806742b16e62b764b2bae37b..d852ca63a6b3a7e7118d66ce1cd9bb98e56f1a73 100644
--- a/ipatests/test_integration/test_ipa_ipa_migration.py
+++ b/ipatests/test_integration/test_ipa_ipa_migration.py
@@ -12,6 +12,7 @@ from ipaplatform.paths import paths
from collections import Counter
import pytest
+import re
import textwrap
@@ -65,29 +66,7 @@ def prepare_ipa_server(master):
"--secondary-rid-base=400000",
]
)
-
- # Add Automount locations and maps
- master.run_command(["ipa", "automountlocation-add", "baltimore"])
- master.run_command(["ipa", "automountmap-add", "baltimore", "auto.share"])
- master.run_command(
- [
- "ipa",
- "automountmap-add-indirect",
- "baltimore",
- "--parentmap=auto.share",
- "--mount=sub auto.man",
- ]
- )
- master.run_command(
- [
- "ipa",
- "automountkey-add",
- "baltimore",
- "auto.master",
- "--key=/share",
- "--info=auto.share",
- ]
- )
+ master.run_command(["ipactl", "restart"])
# Run ipa-adtrust-install
master.run_command(["dnf", "install", "-y", "ipa-server-trust-ad"])
@@ -235,6 +214,17 @@ def prepare_ipa_server(master):
["ipa", "hbacrule-add-service", "--hbacsvcs=sshd", "testuser_sshd"]
)
+ # Add DNSForwardzone
+ master.run_command(
+ [
+ "ipa",
+ "dnsforwardzone-add",
+ "forwardzone.test",
+ "--forwarder",
+ "10.11.12.13",
+ ]
+ )
+
# Vault addition
master.run_command(
[
@@ -244,6 +234,7 @@ def prepare_ipa_server(master):
"vault1234",
"--type",
"symmetric",
+ "testvault",
]
)
@@ -260,7 +251,46 @@ def prepare_ipa_server(master):
# Modify passkeyconfig
master.run_command(
- ["ipa", "passkeyconfig-mod", "--require-user-verification=FALSE"]
+ [
+ "ipa", "passkeyconfig-mod",
+ "--require-user-verification=FALSE"
+ ]
+ )
+
+ # Adding automountlocation, maps, keys
+ master.run_command(
+ [
+ "ipa", "automountlocation-add",
+ "baltimore"
+ ]
+ )
+
+ master.run_command(
+ [
+ "ipa", "automountmap-add",
+ "baltimore",
+ "auto.share"
+ ]
+ )
+
+ master.run_command(
+ [
+ "ipa", "automountmap-add-indirect",
+ "baltimore",
+ "--parentmap=auto.share",
+ "--mount=sub",
+ "auto.man",
+ ]
+ )
+
+ master.run_command(
+ [
+ "ipa", "automountkey-add",
+ "baltimore",
+ "auto.master",
+ "--key=/share",
+ "--info=auto.share",
+ ]
)
@@ -288,12 +318,24 @@ def run_migrate(
return result
-class TestIPAMigrateScenario1(IntegrationTest):
+@pytest.fixture()
+def empty_log_file(request):
"""
- Tier-1 tests for ipa-migrate tool with DNS enabled on
- local and remote server
+ This fixture empties the log file before ipa-migrate tool
+ is run since the log is appended everytime the tool is run.
"""
+ request.cls.replicas[0].run_command(
+ ["truncate", "-s", "0", paths.IPA_MIGRATE_LOG]
+ )
+ yield
+
+class MigrationTest(IntegrationTest):
+ """
+ This class will help setup remote IPA server(cls.master)
+ and local IPA server(cls.replicas[0]) and it will
+ also prepare the remote IPA before migration actually begins.
+ """
num_replicas = 1
num_clients = 1
topology = "line"
@@ -303,14 +345,14 @@ class TestIPAMigrateScenario1(IntegrationTest):
tasks.install_master(cls.master, setup_dns=True, setup_kra=True)
prepare_ipa_server(cls.master)
tasks.install_client(cls.master, cls.clients[0], nameservers=None)
+ tasks.install_master(cls.replicas[0], setup_dns=True, setup_kra=True)
- def test_remote_server(self):
- """
- This test installs IPA server instead of replica on
- system under test with the same realm and domain name.
- """
- tasks.install_master(self.replicas[0], setup_dns=True, setup_kra=True)
+class TestIPAMigrateCLIOptions(MigrationTest):
+ """
+ Tests to check CLI options for ipa-migrate tool with
+ DNS enabled on local and remote server.
+ """
def test_ipa_migrate_without_kinit_as_admin(self):
"""
This test checks that ipa-migrate tool displays
@@ -417,7 +459,7 @@ class TestIPAMigrateScenario1(IntegrationTest):
"""
ldif_file = "/tmp/test.ldif"
param = ['-x', '-o', ldif_file]
- run_migrate(
+ result = run_migrate(
self.replicas[0],
"stage-mode",
self.master.hostname,
@@ -426,45 +468,21 @@ class TestIPAMigrateScenario1(IntegrationTest):
extra_args=param,
)
assert self.replicas[0].transport.file_exists("/tmp/test.ldif")
+ assert result.returncode == 0
- @pytest.fixture()
- def empty_log_file(self):
- """
- This fixture empties the log file before ipa-migrate tool
- is run since the log is appended everytime the tool is run.
- """
- self.replicas[0].run_command(
- ["truncate", "-s", "0", paths.IPA_MIGRATE_LOG]
- )
- yield
-
- def test_ipa_sigden_plugin_fail_error(self, empty_log_file):
- """
- This testcase checks that sidgen plugin fail error is
- not seen during migrate prod-mode
- """
- SIDGEN_ERR_MSG = "SIDGEN task failed: \n"
- run_migrate(
- self.replicas[0],
- "stage-mode",
- self.master.hostname,
- "cn=Directory Manager",
- self.master.config.admin_password,
- extra_args=['-x'],
- )
- error_msg = self.replicas[0].get_file_contents(
- paths.IPA_MIGRATE_LOG, encoding="utf-8"
- )
- assert SIDGEN_ERR_MSG not in error_msg
-
- def test_ipa_migrate_stage_mode_dry_run(self, empty_log_file):
+ def test_ipa_migrate_stage_mode_dry_run(self):
"""
Test ipa-migrate stage mode with dry-run option
+ This test also checks SIDGEN task failure is
+ not seen in ipa migrate log.
"""
tasks.kinit_admin(self.master)
tasks.kinit_admin(self.replicas[0])
+ SIDGEN_ERR_MSG = "SIDGEN task failed: \n"
IPA_MIGRATE_STAGE_DRY_RUN_LOG = "--dryrun=True\n"
- IPA_SERVER_UPRGADE_LOG = "Skipping ipa-server-upgrade in dryrun mode.\n"
+ IPA_SERVER_UPRGADE_LOG = (
+ "Skipping ipa-server-upgrade in dryrun mode.\n"
+ )
IPA_SKIP_SIDGEN_LOG = "Skipping SIDGEN task in dryrun mode."
result = run_migrate(
self.replicas[0],
@@ -481,6 +499,7 @@ class TestIPAMigrateScenario1(IntegrationTest):
assert IPA_MIGRATE_STAGE_DRY_RUN_LOG in install_msg
assert IPA_SERVER_UPRGADE_LOG in install_msg
assert IPA_SKIP_SIDGEN_LOG in install_msg
+ assert SIDGEN_ERR_MSG not in install_msg
def test_ipa_migrate_prod_mode_dry_run(self, empty_log_file):
"""
@@ -509,7 +528,7 @@ class TestIPAMigrateScenario1(IntegrationTest):
assert IPA_SERVER_UPRGADE_LOG in install_msg
assert IPA_SIDGEN_LOG in install_msg
- def test_ipa_migrate_with_skip_schema_option_dry_run(self, empty_log_file):
+ def test_ipa_migrate_skip_schema_dry_run(self, empty_log_file):
"""
This test checks that ipa-migrate tool works
with -S(schema) options in stage mode
@@ -532,7 +551,7 @@ class TestIPAMigrateScenario1(IntegrationTest):
)
assert SKIP_SCHEMA_MSG_LOG in install_msg
- def test_ipa_migrate_with_skip_config_option_dry_run(self, empty_log_file):
+ def test_ipa_migrate_skip_config_dry_run(self, empty_log_file):
"""
This test checks that ipa-migrate tool works
with -C(config) options in stage mode
@@ -579,7 +598,7 @@ class TestIPAMigrateScenario1(IntegrationTest):
)
assert RESET_RANGE_LOG in install_msg
- def test_ipa_migrate_stage_mode_dry_override_schema(self, empty_log_file):
+ def test_ipa_migrate_stage_mode_override_schema(self, empty_log_file):
"""
This test checks that -O option (override schema) works
in dry mode
@@ -601,70 +620,6 @@ class TestIPAMigrateScenario1(IntegrationTest):
)
assert SCHEMA_OVERRIDE_LOG in install_msg
- def test_ipa_migrate_stage_mode(self, empty_log_file):
- """
- This test checks that ipa-migrate is successful
- in dry run mode
- """
- tasks.kinit_admin(self.master)
- tasks.kinit_admin(self.replicas[0])
- MIGRATION_SCHEMA_LOG_MSG = "Migrating schema ...\n"
- MIGRATION_CONFIG_LOG_MSG = "Migrating configuration ...\n"
- IPA_UPGRADE_LOG_MSG = (
- "Running ipa-server-upgrade ... (this may take a while)\n"
- )
- SIDGEN_TASK_LOG_MSG = "Running SIDGEN task ...\n"
- MIGRATION_COMPLETE_LOG_MSG = "Migration complete!\n"
- result = run_migrate(
- self.replicas[0],
- "stage-mode",
- self.master.hostname,
- "cn=Directory Manager",
- self.master.config.admin_password,
- extra_args=['-n'],
- )
- install_msg = self.replicas[0].get_file_contents(
- paths.IPA_MIGRATE_LOG, encoding="utf-8"
- )
- assert result.returncode == 0
- assert MIGRATION_SCHEMA_LOG_MSG in install_msg
- assert MIGRATION_CONFIG_LOG_MSG in install_msg
- assert IPA_UPGRADE_LOG_MSG in install_msg
- assert SIDGEN_TASK_LOG_MSG in install_msg
- assert MIGRATION_COMPLETE_LOG_MSG in install_msg
-
- def test_ipa_migrate_prod_mode(self, empty_log_file):
- """
- This test checks that ipa-migrate is successful
- in prod run mode
- """
- tasks.kinit_admin(self.master)
- tasks.kinit_admin(self.replicas[0])
- MIGRATION_SCHEMA_LOG_MSG = "Migrating schema ...\n"
- MIGRATION_DATABASE_LOG_MSG = (
- "Migrating database ... (this may take a while)\n"
- )
- IPA_UPGRADE_LOG_MSG = (
- "Running ipa-server-upgrade ... (this may take a while)\n"
- )
- SIDGEN_TASK_LOG_MSG = "Running SIDGEN task ...\n"
- result = run_migrate(
- self.replicas[0],
- "prod-mode",
- self.master.hostname,
- "cn=Directory Manager",
- self.master.config.admin_password,
- extra_args=['-n'],
- )
- install_msg = self.replicas[0].get_file_contents(
- paths.IPA_MIGRATE_LOG, encoding="utf-8"
- )
- assert result.returncode == 0
- assert MIGRATION_SCHEMA_LOG_MSG in install_msg
- assert MIGRATION_DATABASE_LOG_MSG in install_msg
- assert IPA_UPGRADE_LOG_MSG in install_msg
- assert SIDGEN_TASK_LOG_MSG in install_msg
-
def test_ipa_migrate_with_bind_pwd_file_option(self, empty_log_file):
"""
This testcase checks that ipa-migrate tool
@@ -801,6 +756,9 @@ class TestIPAMigrateScenario1(IntegrationTest):
@pytest.fixture()
def modify_dns_zone(self):
+ """
+ This fixture adds dnszone and then removes the zone.
+ """
zone_name = 'ipatest.test'
self.master.run_command(
["ipa", "dnszone-add", zone_name, "--force"]
@@ -844,6 +802,20 @@ class TestIPAMigrateScenario1(IntegrationTest):
assert DNS_LOG2 in install_msg
assert DNS_LOG3 in install_msg
+ def test_ipa_migrate_dns_forwardzone(self):
+ """
+ This testcase checks that DNS forwardzone is
+ also migrated in prod-mode
+ """
+ zone_name = "forwardzone.test"
+ result = self.replicas[0].run_command(
+ ["ipa", "dnsforwardzone-show", zone_name]
+ )
+ assert 'Zone name: {}'.format(zone_name) in result.stdout_text
+ assert 'Active zone: True' in result.stdout_text
+ assert 'Zone forwarders: 10.11.12.13' in result.stdout_text
+ assert 'Forward policy: first' in result.stdout_text
+
def test_ipa_migrate_version_option(self):
"""
The -V option has been removed.
@@ -922,20 +894,179 @@ class TestIPAMigrateScenario1(IntegrationTest):
assert result.returncode == 1
assert ERR_MSG in result.stderr_text
- def test_ipa_hbac_rule_duplication(self):
+
+class TestIPAMigrationStageMode(MigrationTest):
+ """
+ Tests for ipa-migrate tool in stage mode
+ """
+ def test_ipa_migrate_stage_mode(self, empty_log_file):
"""
- This testcase checks that default hbac rules
- are not duplicated on the local server when
- ipa-migrate command is run.
+ This test checks that ipa-migrate is successful
+ in dry run mode
"""
+ tasks.kinit_admin(self.master)
+ tasks.kinit_admin(self.replicas[0])
+ MIGRATION_SCHEMA_LOG_MSG = "Migrating schema ...\n"
+ MIGRATION_CONFIG_LOG_MSG = "Migrating configuration ...\n"
+ IPA_UPGRADE_LOG_MSG = (
+ "Running ipa-server-upgrade ... (this may take a while)\n"
+ )
+ SIDGEN_TASK_LOG_MSG = "Running SIDGEN task ...\n"
+ MIGRATION_COMPLETE_LOG_MSG = "Migration complete!\n"
run_migrate(
+ self.replicas[0],
+ "stage-mode",
+ self.master.hostname,
+ "cn=Directory Manager",
+ self.master.config.admin_password,
+ extra_args=['-n'],
+ )
+ install_msg = self.replicas[0].get_file_contents(
+ paths.IPA_MIGRATE_LOG, encoding="utf-8"
+ )
+ assert MIGRATION_SCHEMA_LOG_MSG in install_msg
+ assert MIGRATION_CONFIG_LOG_MSG in install_msg
+ assert IPA_UPGRADE_LOG_MSG in install_msg
+ assert SIDGEN_TASK_LOG_MSG in install_msg
+ assert MIGRATION_COMPLETE_LOG_MSG in install_msg
+
+ def test_ipa_migrate_stage_mode_new_user(self):
+ """
+ This testcase checks that when a new user is added and
+ ipa-migrate is run in stage-mode, uid/gid of the
+ migrated user is not preserved i.e we have different
+ uid/gid for user on remote and local IPA server.
+ """
+ username = 'testuser4'
+ base_dn = str(self.master.domain.basedn)
+ LOG_MSG1 = (
+ "DEBUG Resetting the DNA range for new entry: "
+ "uid={},cn=users,cn=accounts,{}\n"
+ ).format(username, base_dn)
+ install_msg = self.replicas[0].get_file_contents(
+ paths.IPA_MIGRATE_LOG, encoding="utf-8"
+ )
+ assert LOG_MSG1 not in install_msg
+ tasks.clear_sssd_cache(self.master)
+ self.master.run_command(['ipa', 'user-show', username])
+ cmd1 = self.master.run_command(['id', username])
+ tasks.clear_sssd_cache(self.replicas[0])
+ self.replicas[0].run_command(['ipa', 'user-show', username])
+ cmd2 = self.replicas[0].run_command(['id', username])
+ assert cmd1.stdout_text != cmd2.stdout_text
+
+
+class TestIPAMigrationProdMode(MigrationTest):
+ """
+ Tests for ipa-migrate tool in prod mode
+ """
+ def test_ipa_migrate_prod_mode(self, empty_log_file):
+ """
+ This test checks that ipa-migrate is successful
+ in prod run mode
+ """
+ tasks.kinit_admin(self.master)
+ tasks.kinit_admin(self.replicas[0])
+ MIGRATION_SCHEMA_LOG_MSG = "Migrating schema ...\n"
+ MIGRATION_DATABASE_LOG_MSG = (
+ "Migrating database ... (this may take a while)\n"
+ )
+ IPA_UPGRADE_LOG_MSG = (
+ "Running ipa-server-upgrade ... (this may take a while)\n"
+ )
+ SIDGEN_TASK_LOG_MSG = "Running SIDGEN task ...\n"
+ result = run_migrate(
self.replicas[0],
"prod-mode",
self.master.hostname,
"cn=Directory Manager",
self.master.config.admin_password,
- extra_args=['-n']
+ extra_args=['-n'],
)
+ install_msg = self.replicas[0].get_file_contents(
+ paths.IPA_MIGRATE_LOG, encoding="utf-8"
+ )
+ assert result.returncode == 0
+ assert MIGRATION_SCHEMA_LOG_MSG in install_msg
+ assert MIGRATION_DATABASE_LOG_MSG in install_msg
+ assert IPA_UPGRADE_LOG_MSG in install_msg
+ assert SIDGEN_TASK_LOG_MSG in install_msg
+
+ def test_ipa_migrate_prod_mode_hbac_rule(self):
+ """
+ This testcase checks that hbac rule is migrated from
+ remote server to local server in prod mode.
+ """
+ hbac_rule_name1 = 'test1'
+ hbac_rule_name2 = 'testuser_sshd'
+ tasks.kinit_admin(self.replicas[0])
+ cmd1 = self.replicas[0].run_command(
+ ["ipa", "hbacrule-find", hbac_rule_name1])
+ cmd2 = self.replicas[0].run_command(
+ ["ipa", "hbacrule-find", hbac_rule_name2])
+ assert hbac_rule_name1 in cmd1.stdout_text
+ assert hbac_rule_name2 in cmd2.stdout_text
+
+ def test_ipa_migrate_prod_mode_sudo_rule(self):
+ """
+ This testcase checks that sudo cmd and rules are
+ migrated from remote server to local server in prod mode.
+ """
+ sudorule = 'readfiles'
+ sudocmd = '/usr/bin/less'
+ tasks.kinit_admin(self.replicas[0])
+ cmd1 = self.replicas[0].run_command(
+ ["ipa", "sudorule-find", sudorule])
+ cmd2 = self.replicas[0].run_command(
+ ["ipa", "sudocmd-find", sudocmd])
+ assert 'Rule name: readfiles\n' in cmd1.stdout_text
+ assert 'Sudo Command: /usr/bin/less\n' in cmd2.stdout_text
+
+ def test_ipa_migrate_prod_mode_new_user_sid(self):
+ """
+ This testcase checks that in prod-mode uid/gid of the
+ migrated user is preserved i.e we have same
+ uid/gid for user on remote and local IPA server.
+ """
+ username = 'testuser4'
+ tasks.clear_sssd_cache(self.master)
+ result1 = self.master.run_command(['id', username])
+ tasks.clear_sssd_cache(self.replicas[0])
+ result2 = self.replicas[0].run_command(['id', username])
+ assert result1.stdout_text == result2.stdout_text
+
+ def test_check_vault_is_not_migrated(self):
+ """
+ This testcase checks that vault is
+ not migrated
+ """
+ vault_name = "testvault"
+ CMD_OUTPUT = "Number of entries returned 0"
+ cmd = self.replicas[0].run_command(
+ ["ipa", "vault-find", vault_name], raiseonerr=False)
+ assert cmd.returncode != 0
+ assert CMD_OUTPUT in cmd.stdout_text
+
+ def test_ipa_migrate_subids(self):
+ """
+ This testcase checks that subids for users are migrated
+ to the local server from the remote server
+ """
+ user_name = 'admin'
+ CMD_MSG = "1 subordinate id matched"
+ cmd = self.replicas[0].run_command(
+ ['ipa', 'subid-find',
+ '--owner', user_name]
+ )
+ assert cmd.returncode == 0
+ assert CMD_MSG in cmd.stdout_text
+
+ def test_ipa_hbac_rule_duplication(self):
+ """
+ This testcase checks that default hbac rules
+ are not duplicated on the local server when
+ ipa-migrate command is run.
+ """
result = self.replicas[0].run_command(
['ipa', 'hbacrule-find']
)
@@ -946,3 +1077,192 @@ class TestIPAMigrateScenario1(IntegrationTest):
count = Counter(line)
assert count.get('Rule name: allow_all') < 2
assert count.get('Rule name: allow_systemd-user') < 2
+
+ def test_ipa_migrate_otptoken(self):
+ """
+ This testcase checks that the otptoken
+ is migrated for the user.
+ """
+ owner = "testuser1"
+ CMD_OUTPUT = "1 OTP token matched"
+ result = self.replicas[0].run_command([
+ "ipa", "otptoken-find"
+ ])
+ assert CMD_OUTPUT in result.stdout_text
+ assert 'Type: TOTP' in result.stdout_text
+ assert 'Owner: {}'.format(owner) in result.stdout_text
+
+ def test_ipa_migrate_check_passkey_config(self):
+ """
+ This testcase checks that passkey config
+ is migrated
+ """
+ CMD_OUTPUT = "Require user verification: False"
+ result = self.replicas[0].run_command([
+ "ipa", "passkeyconfig-show"
+ ])
+ assert CMD_OUTPUT in result.stdout_text
+
+ def test_ipa_migrate_check_service_status(self):
+ """
+ This testcase checks that ipactl and sssd
+ services are running post ipa-migrate tool
+ successful runs completed
+ """
+ cmd1 = self.replicas[0].run_command([
+ "ipactl", "status"
+ ])
+ assert cmd1.returncode == 0
+ cmd2 = self.replicas[0].run_command([
+ "systemctl", "status", "sssd"
+ ])
+ assert cmd2.returncode == 0
+
+ def test_custom_idrange_is_migrated(self):
+ """
+ This testcase checks that custom idrange is migrated
+ from remote server to local server in production
+ mode.
+ """
+ range_name = "testrange"
+ CMD_OUTPUT = (
+ "---------------\n"
+ "1 range matched\n"
+ "---------------\n"
+ " Range name: testrange\n"
+ " First Posix ID of the range: 10000\n"
+ " Number of IDs in the range: 10000\n"
+ " First RID of the corresponding RID range: 300000\n"
+ " First RID of the secondary RID range: 400000\n"
+ " Range type: local domain range\n"
+ "----------------------------\n"
+ "Number of entries returned 1\n"
+ "----------------------------\n"
+ )
+ cmd = self.replicas[0].run_command(
+ ["ipa", "idrange-find", range_name])
+ assert CMD_OUTPUT in cmd.stdout_text
+
+ def test_automountlocation_is_migrated(self):
+ """
+ This testcase checks that automount location/maps
+ and keys are migrated.
+ """
+ base_dn = str(self.master.domain.basedn)
+ automount_cn = "automount"
+ loc_name = "baltimore"
+ auto_map_name = "auto.share"
+ DEBUG_LOG = (
+ "Added entry: cn={},cn={},{}\n"
+ ).format(loc_name, automount_cn, base_dn)
+ CMD1_OUTPUT = (
+ " Location: baltimore\n"
+ )
+ CMD2_OUTPUT = (
+ " Map: auto.share\n"
+ )
+ CMD3_OUTPUT = (
+ "-----------------------\n"
+ "1 automount key matched\n"
+ "-----------------------\n"
+ " Key: sub\n"
+ " Mount information: -fstype=autofs ldap:auto.man\n"
+ )
+ cmd1 = self.replicas[0].run_command(
+ ["ipa", "automountlocation-show", loc_name])
+ cmd2 = self.replicas[0].run_command(
+ ["ipa", "automountmap-find", loc_name])
+ cmd3 = self.replicas[0].run_command(
+ ["ipa", "automountkey-find", loc_name, auto_map_name]
+ )
+ install_msg = self.replicas[0].get_file_contents(
+ paths.IPA_MIGRATE_LOG, encoding="utf-8"
+ )
+ assert CMD1_OUTPUT in cmd1.stdout_text
+ assert CMD2_OUTPUT in cmd2.stdout_text
+ assert CMD3_OUTPUT in cmd3.stdout_text
+ assert DEBUG_LOG in install_msg
+
+
+class TestIPAMigrationWithADtrust(IntegrationTest):
+ """
+ Test for ipa-migrate tool with IPA Master having trust setup
+ with Windows AD.
+ """
+ topology = "line"
+ num_ad_domains = 1
+ num_replicas = 1
+
+ @classmethod
+ def install(cls, mh):
+ tasks.install_master(
+ cls.master, setup_dns=True, extra_args=['--no-dnssec-validation']
+ )
+ cls.ad = cls.ads[0]
+ cls.ad_domain = cls.ad.domain.name
+ tasks.install_adtrust(cls.master)
+ tasks.configure_dns_for_trust(cls.master, cls.ad)
+ tasks.establish_trust_with_ad(cls.master, cls.ad.domain.name)
+
+ def test_install_local_server(self):
+ """
+ This test installs local IPA Server() i.e new IPA server with
+ the same realm and domain name that will receive the migration data.
+ """
+ tasks.install_master(
+ self.replicas[0], setup_dns=True,
+ extra_args=['--no-dnssec-validation']
+ )
+ tasks.install_adtrust(self.replicas[0])
+
+ def test_check_ad_attributes_migrate_prod_mode(self):
+ """
+ This test checks that IPA-AD trust related attributes
+ are migrated to local server.
+ """
+ result = run_migrate(
+ self.replicas[0],
+ "prod-mode",
+ self.master.hostname,
+ "cn=Directory Manager",
+ self.master.config.admin_password,
+ extra_args=['-n']
+ )
+ assert result.returncode == 0
+ trust1 = self.master.run_command(
+ ['ipa', 'trust-show', self.ad_domain]
+ ).stdout_text
+ trust2 = self.replicas[0].run_command(
+ ['ipa', 'trust-show', self.ad_domain]).stdout_text
+ assert trust1 == trust2
+
+ def test_check_domain_sid_is_migrated(self):
+ """
+ This testcase checks that domain sid is
+ migrated from a remote server having trust with AD
+ to local server and is displayed in the
+ ipa trustconfig-show command
+ """
+ regexp = (r'Security Identifier: (.*)$')
+ cmd1 = self.master.run_command(["ipa", "trustconfig-show"])
+ sid1 = re.findall(regexp, cmd1.stdout_text, re.MULTILINE)
+ cmd2 = self.replicas[0].run_command(
+ ["ipa", "trustconfig-show"]
+ )
+ sid2 = re.findall(regexp, cmd2.stdout_text, re.MULTILINE)
+ assert sid1 == sid2
+
+ def test_check_ad_idrange_is_migrated(self):
+ """
+ This testcase checks AD idrange is migrated
+ from remote IPA server having trust with AD
+ to local IPA server
+ """
+ ad_domain_name = self.ad.domain.name.upper()
+ cmd1 = self.master.run_command(
+ ["ipa", "idrange-show", ad_domain_name + "_id_range"]
+ )
+ cmd2 = self.replicas[0].run_command(
+ ["ipa", "idrange-show", ad_domain_name + "_id_range"]
+ )
+ assert cmd1.stdout_text == cmd2.stdout_text
--
2.47.0

View File

@ -1,34 +0,0 @@
From c64c098e1d0ae492499caa83a1b73532da511f84 Mon Sep 17 00:00:00 2001
From: Carla Martinez <carlmart@redhat.com>
Date: Tue, 29 Oct 2024 15:23:55 +0100
Subject: [PATCH] Fix: 'Organization' field in Okta not required
Although the 'Organization' field is not required
when using the Okta template, the WebUI requires it
in order to create a new IDP. If this is not provided,
an error is shown.
Fixes: https://pagure.io/freeipa/issue/9687
Signed-off-by: Carla Martinez <carlmart@redhat.com>
Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
---
install/ui/src/freeipa/idp.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/install/ui/src/freeipa/idp.js b/install/ui/src/freeipa/idp.js
index ada09c0754f5a51575831e127deb81d1f27f44d1..04daad591a8e94ea9b8c146c12e0c84aaad6cee4 100644
--- a/install/ui/src/freeipa/idp.js
+++ b/install/ui/src/freeipa/idp.js
@@ -41,7 +41,7 @@ idp.templates = [
fields: ['ipaidporg']},
{ value: 'okta',
label: text.get('@i18n:objects.idp.template_okta'),
- fields: ['ipaidporg', 'ipaidpbaseurl']}
+ fields: ['ipaidpbaseurl']}
];
--
2.47.0

View File

@ -1,42 +0,0 @@
From baa9fc3e3e2f6b39db5ec465c92dc597cd5399b9 Mon Sep 17 00:00:00 2001
From: Florence Blanc-Renaud <flo@redhat.com>
Date: Tue, 12 Nov 2024 16:44:46 +0100
Subject: [PATCH] ipatests: install master with allow-zone-overlap
In the IPA to IPA migration tests, install the destination master
with --setup-dns --allow-zone-overlap to allow installation
even if the zone is already served by the source master.
Fixes: https://pagure.io/freeipa/issue/9697
Signed-off-by: Florence Blanc-Renaud <flo@redhat.com>
Reviewed-By: Sudhir Menon <sumenon@redhat.com>
---
ipatests/test_integration/test_ipa_ipa_migration.py | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/ipatests/test_integration/test_ipa_ipa_migration.py b/ipatests/test_integration/test_ipa_ipa_migration.py
index d852ca63a6b3a7e7118d66ce1cd9bb98e56f1a73..0c637a0141d95f34f951c60a9648adf8e87eaa63 100644
--- a/ipatests/test_integration/test_ipa_ipa_migration.py
+++ b/ipatests/test_integration/test_ipa_ipa_migration.py
@@ -345,7 +345,8 @@ class MigrationTest(IntegrationTest):
tasks.install_master(cls.master, setup_dns=True, setup_kra=True)
prepare_ipa_server(cls.master)
tasks.install_client(cls.master, cls.clients[0], nameservers=None)
- tasks.install_master(cls.replicas[0], setup_dns=True, setup_kra=True)
+ tasks.install_master(cls.replicas[0], setup_dns=True, setup_kra=True,
+ extra_args=['--allow-zone-overlap'])
class TestIPAMigrateCLIOptions(MigrationTest):
@@ -1211,7 +1212,7 @@ class TestIPAMigrationWithADtrust(IntegrationTest):
"""
tasks.install_master(
self.replicas[0], setup_dns=True,
- extra_args=['--no-dnssec-validation']
+ extra_args=['--no-dnssec-validation', '--allow-zone-overlap']
)
tasks.install_adtrust(self.replicas[0])
--
2.47.0

View File

@ -1,148 +0,0 @@
From c71e12e902b3912c31245d46ad6f2c2ddee01126 Mon Sep 17 00:00:00 2001
From: Alexander Bokovoy <abokovoy@redhat.com>
Date: Tue, 1 Oct 2024 11:28:28 +0300
Subject: [PATCH] selinux: allow Cockpit to use HTTP keytab on IPA servers
Cockpit can use GSSAPI authentication and has pretty good definition of
how to enable it: https://cockpit-project.org/guide/latest/sso.html.
These instructions work on IPA clients but they cannot be used on IPA
servers because IPA framework already owns HTTP/.. Kerberos service and
its keytab.
Luckily, there are two changes that need to be done to enable Cockpit
single sign-on with GSSAPI on IPA servers:
- create a symlink /etc/cockpit/krb5.keytab to
/var/lib/ipa/gssproxy/http.keytab
- add SELinux policy to allow cockpit_session_t to operate on
/var/lib/ipa/gssproxy/http.keytab file
For existing installation an upgrade process would restore SELinux
context of the http.keytab file to the new value.
Note that Cockpit documentation above also talks about Kerberos service
modifications to enable delegation. These modifications should not be
done for IPA servers' HTTP services, as these services are already
enabled to handle delegation.
Fixes: https://pagure.io/freeipa/issue/9675
Signed-off-by: Alexander Bokovoy <abokovoy@redhat.com>
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
---
ipaserver/install/server/upgrade.py | 1 +
selinux/ipa.fc | 2 ++
selinux/ipa.if | 24 ++++++++++++++++++++++++
selinux/ipa.te | 19 +++++++++++++++++++
4 files changed, 46 insertions(+)
diff --git a/ipaserver/install/server/upgrade.py b/ipaserver/install/server/upgrade.py
index 31d4f8398cfb0251cc59ada909eb55635b83e960..d5c466ee2f905eafd15663fef46d052ade30d742 100644
--- a/ipaserver/install/server/upgrade.py
+++ b/ipaserver/install/server/upgrade.py
@@ -1124,6 +1124,7 @@ def update_http_keytab(http):
paths.OLD_IPA_KEYTAB, e
)
http.keytab_user.chown(http.keytab)
+ tasks.restore_context(http.keytab)
def ds_enable_sidgen_extdom_plugins(ds):
diff --git a/selinux/ipa.fc b/selinux/ipa.fc
index 47bd19ba77418cad1f0904dc4a9a35ce9d6ff9d2..15e8e41aa50228ff560e338044240b46bc24cc40 100644
--- a/selinux/ipa.fc
+++ b/selinux/ipa.fc
@@ -22,6 +22,8 @@
/var/lib/ipa(/.*)? gen_context(system_u:object_r:ipa_var_lib_t,s0)
+/var/lib/ipa/gssproxy/http.keytab -- gen_context(system_u:object_r:ipa_http_keytab_t,s0)
+
/var/log/ipa(/.*)? gen_context(system_u:object_r:ipa_log_t,s0)
/var/log/ipabackup.log -- gen_context(system_u:object_r:ipa_log_t,s0)
diff --git a/selinux/ipa.if b/selinux/ipa.if
index 8c47e7963af92b1ddcd59d92aa45d6b8e9c0c6cc..8f3147e10bd294665dd41e1c1f99c993d9699d20 100644
--- a/selinux/ipa.if
+++ b/selinux/ipa.if
@@ -155,6 +155,7 @@ interface(`ipa_manage_log',`
########################################
## <summary>
## Allow domain to manage ipa lib files/dirs.
+## This includes reading ipa_http_keytab_t files.
## </summary>
## <param name="domain">
## <summary>
@@ -164,10 +165,33 @@ interface(`ipa_manage_log',`
#
interface(`ipa_read_lib',`
gen_require(`
+ type ipa_http_keytab_t;
type ipa_var_lib_t;
')
read_files_pattern($1, ipa_var_lib_t, ipa_var_lib_t)
+ read_files_pattern($1, ipa_http_keytab_t, ipa_http_keytab_t)
+ list_dirs_pattern($1, ipa_var_lib_t, ipa_var_lib_t)
+')
+
+########################################
+## <summary>
+## Allow domain to manage ipa HTTP keytab file.
+## This includes reading ipa_var_lib_t directories.
+## </summary>
+## <param name="domain">
+## <summary>
+## Domain allowed access.
+## </summary>
+## </param>
+#
+interface(`ipa_read_http_keytab',`
+ gen_require(`
+ type ipa_http_keytab_t;
+ type ipa_var_lib_t;
+ ')
+
+ read_files_pattern($1, ipa_http_keytab_t, ipa_http_keytab_t)
list_dirs_pattern($1, ipa_var_lib_t, ipa_var_lib_t)
')
diff --git a/selinux/ipa.te b/selinux/ipa.te
index 2546a9bd9468200185c484974a9e71f16f89de71..e4ce66687a48b27e85591cdd8352f7cac94d3151 100644
--- a/selinux/ipa.te
+++ b/selinux/ipa.te
@@ -43,6 +43,9 @@ logging_log_file(ipa_log_t)
type ipa_var_lib_t;
files_type(ipa_var_lib_t)
+type ipa_http_keytab_t;
+files_type(ipa_http_keytab_t)
+
type ipa_var_run_t;
files_pid_file(ipa_var_run_t)
@@ -516,3 +519,19 @@ optional_policy(`
')
allow certmonger_t pki_tomcat_etc_rw_t:file { getattr ioctl open read };
')
+
+# gssproxy needs to read http keytab
+optional_policy(`
+ gen_require(`
+ type gssproxy_t;
+ ')
+ ipa_read_http_keytab(gssproxy_t)
+')
+
+# Allow Cockpit to use HTTP keytab on IPA servers for GSSAPI authentication
+optional_policy(`
+ gen_require(`
+ type cockpit_session_t;
+ ')
+ ipa_read_http_keytab(cockpit_session_t)
+')
--
2.47.0

View File

@ -1,94 +0,0 @@
From 0dadcbb4ac9f6142b5130f025f64d918d6f208a9 Mon Sep 17 00:00:00 2001
From: Alexander Bokovoy <abokovoy@redhat.com>
Date: Tue, 8 Oct 2024 10:25:08 +0300
Subject: [PATCH] Minimal test for Cockpit integration on IPA master
Add a test to share HTTP service keytab on IPA master between IPA and
Cockpit. The test configures Cockpit with IPA CA-issued certificate and
allows Cockpit to access IPA HTTP service keytab for authentication.
The test then attempts to authenticate with GSSAPI as admin user. A
successful result is when we receive CSRF token from the Cockpit as
the result of this authentication. This means we have logged in
successfully with Kerberos.
Fixes: https://pagure.io/freeipa/issue/9675
Signed-off-by: Alexander Bokovoy <abokovoy@redhat.com>
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
---
ipatests/test_integration/test_cockpit.py | 61 +++++++++++++++++++++++
1 file changed, 61 insertions(+)
create mode 100644 ipatests/test_integration/test_cockpit.py
diff --git a/ipatests/test_integration/test_cockpit.py b/ipatests/test_integration/test_cockpit.py
new file mode 100644
index 0000000000000000000000000000000000000000..cdc96170a116536c7aa00be78cc4e0225804e21c
--- /dev/null
+++ b/ipatests/test_integration/test_cockpit.py
@@ -0,0 +1,61 @@
+#
+# Copyright (C) 2024 FreeIPA Contributors see COPYING for license
+#
+
+from __future__ import absolute_import
+
+import time
+from ipatests.pytest_ipa.integration import tasks
+from ipatests.test_integration.base import IntegrationTest
+from ipaplatform.paths import paths
+
+
+class TestCockpitIntegration(IntegrationTest):
+ topology = "line"
+ reqcert = '/etc/cockpit/ws-certs.d/99-cockpit.cert'
+ reqkey = '/etc/cockpit/ws-certs.d/99-cockpit.key'
+ symlink = '/etc/cockpit/krb5.keytab'
+
+ @classmethod
+ def uninstall(cls, mh):
+ cls.master.run_command(['ipa-getcert', 'stop-tracking', '-f',
+ cls.reqcert], raiseonerr=False)
+ cls.master.run_command(['rm', '-f', cls.symlink], raiseonerr=False)
+ cls.master.run_command(['systemctl', 'disable', '--now',
+ 'cockpit.socket'])
+ super(TestCockpitIntegration, cls).uninstall(mh)
+
+ @classmethod
+ def install(cls, mh):
+ master = cls.master
+
+ # Install Cockpit and configure it to use IPA certificate and keytab
+ master.run_command(['dnf', 'install', '-y', 'cockpit', 'curl'],
+ raiseonerr=False)
+
+ super(TestCockpitIntegration, cls).install(mh)
+
+ master.run_command(['ipa-getcert', 'request', '-f', cls.reqcert, '-k',
+ cls.reqkey, '-D', cls.master.hostname, '-K',
+ 'host/' + cls.master.hostname, '-m', '0640', '-o',
+ 'root:cockpit-ws', '-O', 'root:root', '-M',
+ '0644'], raiseonerr=False)
+
+ master.run_command(['ln', '-s', paths.HTTP_KEYTAB, cls.symlink],
+ raiseonerr=False)
+
+ time.sleep(5)
+ master.run_command(['systemctl', 'enable', '--now', 'cockpit.socket'])
+
+ def test_login_with_kerberos(self):
+ """
+ Login to Cockpit using GSSAPI authentication
+ """
+ master = self.master
+ tasks.kinit_admin(master)
+
+ cockpit_login = f'https://{master.hostname}:9090/cockpit/login'
+ result = master.run_command([paths.BIN_CURL, '-u:', '--negotiate',
+ '--cacert', paths.IPA_CA_CRT,
+ cockpit_login])
+ assert ("csrf-token" in result.stdout_text)
--
2.47.0

View File

@ -1,38 +0,0 @@
From c306c613399cdd9a2c716b83ce0d47d320aec2a8 Mon Sep 17 00:00:00 2001
From: Alexander Bokovoy <abokovoy@redhat.com>
Date: Tue, 19 Nov 2024 12:57:46 +0200
Subject: [PATCH] ipaserver/dcerpc: support Samba 4.21
Samba 4.21 moved samba.trust_utils module to samba.lsa_utils.
Fixes: https://pagure.io/freeipa/issue/9702
Signed-off-by: Alexander Bokovoy <abokovoy@redhat.com>
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
---
ipaserver/dcerpc.py | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/ipaserver/dcerpc.py b/ipaserver/dcerpc.py
index a28c72361276f12a1a02cd126425ac3c62eddd4f..3344ea226e3cba61912e717f9c375612bb4707e0 100644
--- a/ipaserver/dcerpc.py
+++ b/ipaserver/dcerpc.py
@@ -55,9 +55,13 @@ from samba import ntstatus
import samba
try:
- from samba.trust_utils import CreateTrustedDomainRelax
+ from samba.lsa_utils import CreateTrustedDomainRelax
except ImportError:
- CreateTrustedDomainRelax = None
+ try:
+ from samba.trust_utils import CreateTrustedDomainRelax
+ except ImportError:
+ CreateTrustedDomainRelax = None
+
try:
from samba import arcfour_encrypt
except ImportError:
--
2.47.0

View File

@ -1,66 +0,0 @@
From 184589fac4ff36b5583541f40dff91296c33370a Mon Sep 17 00:00:00 2001
From: Rob Crittenden <rcritten@redhat.com>
Date: Mon, 2 Dec 2024 10:23:29 -0500
Subject: [PATCH] Allow looking up constants.Group by gid in addition to name
This adds flexibility so we can look up groups by both gid and
by name in order to have a more consistent API for management.
Related: https://pagure.io/freeipa/issue/9709
Signed-off-by: Rob Crittenden <rcritten@redhat.com>
Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
Reviewed-By: Florence Blanc-Renaud <frenaud@redhat.com>
---
ipaplatform/base/constants.py | 5 ++++-
ipatests/test_ipaplatform/test_constants.py | 11 +++++++++++
2 files changed, 15 insertions(+), 1 deletion(-)
diff --git a/ipaplatform/base/constants.py b/ipaplatform/base/constants.py
index 1689efe52466f00fd8b014f720e1d21ebdbf2504..f1ef7efff502573bab82e890bcdf87c0ec52a399 100644
--- a/ipaplatform/base/constants.py
+++ b/ipaplatform/base/constants.py
@@ -86,7 +86,10 @@ class Group(_Entity):
try:
self._entity = entity = grp.getgrnam(self)
except KeyError:
- raise ValueError(f"group '{self!s}' not found") from None
+ try:
+ self._entity = entity = grp.getgrgid(int(self))
+ except (TypeError, ValueError):
+ raise ValueError(f"group '{self!s}' not found") from None
return entity
@property
diff --git a/ipatests/test_ipaplatform/test_constants.py b/ipatests/test_ipaplatform/test_constants.py
index b57bfa48e5ccefe2b22cb00aca8436e0edc01a30..9bb12283609f87bcd875a2c55ee1e8b714dd8b3a 100644
--- a/ipatests/test_ipaplatform/test_constants.py
+++ b/ipatests/test_ipaplatform/test_constants.py
@@ -1,6 +1,7 @@
#
# Copyright (C) 2020 FreeIPA Contributors see COPYING for license
#
+import grp
import pytest
from ipaplatform.base.constants import User, Group
@@ -40,6 +41,16 @@ def test_group():
assert Group(str(group)) is not group
+def test_numeric_group():
+ g = grp.getgrnam('apache')
+ group = Group(g.gr_gid)
+ assert group.gid == g.gr_gid
+ assert type(str(group)) is str
+ assert repr(group) == '<Group "%d">' % g.gr_gid
+ assert group.gid == g.gr_gid
+ assert group.entity.gr_gid == g.gr_gid
+
+
def test_group_invalid():
invalid = Group("invalid")
with pytest.raises(ValueError) as e:
--
2.47.1

View File

@ -1,72 +0,0 @@
From 934d4a291d44a40b5ea006aa1f09afa8e4a985fc Mon Sep 17 00:00:00 2001
From: Rob Crittenden <rcritten@redhat.com>
Date: Mon, 2 Dec 2024 10:27:15 -0500
Subject: [PATCH] Pass all pkiuser groups as suplementary when validating an
HSM
We were doing a "best effort" when validating the HSM token is
visible with a valid PIN when it came to groups. A specific
workaround was added for softhsm2 but this didn't carry over
to other HSMs that may have group-specific read/write access.
Use the new capability in ipaplatform.constants.py::Group to be
able to use generate a valid entry from a group GID. Pair this
with os.getgrouplist() and all groups will be passed correctly
via ipautil.run().
Fixes: https://pagure.io/freeipa/issue/9709
Signed-off-by: Rob Crittenden <rcritten@redhat.com>
Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
Reviewed-By: Florence Blanc-Renaud <frenaud@redhat.com>
---
ipaserver/install/ca.py | 12 ++++--------
1 file changed, 4 insertions(+), 8 deletions(-)
diff --git a/ipaserver/install/ca.py b/ipaserver/install/ca.py
index 520e3fc5de1084e7c22c0cf7eaa86e1d3c421373..2959aceed5cd2fd4851457eaa4aeac3c0905d27d 100644
--- a/ipaserver/install/ca.py
+++ b/ipaserver/install/ca.py
@@ -211,11 +211,7 @@ def hsm_validator(token_name, token_library, token_password):
)
pkiuser = constants.PKI_USER
pkigroup = constants.PKI_GROUP
- if 'libsofthsm' in token_library:
- import grp
- group = grp.getgrnam(constants.ODS_GROUP)
- if str(constants.PKI_USER) in group.gr_mem:
- pkigroup = constants.ODS_GROUP
+ group_list = os.getgrouplist(pkiuser, pkigroup.gid)
with certdb.NSSDatabase() as tempnssdb:
tempnssdb.create_db(user=str(pkiuser), group=str(pkigroup))
# Try adding the token library to the temporary database in
@@ -231,7 +227,7 @@ def hsm_validator(token_name, token_library, token_password):
# It may fail if p11-kit has already registered the library, that's
# ok.
ipautil.run(command, stdin='\n', cwd=tempnssdb.secdir,
- runas=pkiuser, suplementary_groups=[pkigroup],
+ runas=pkiuser, suplementary_groups=group_list,
raiseonerr=False)
command = [
@@ -242,7 +238,7 @@ def hsm_validator(token_name, token_library, token_password):
]
lines = ipautil.run(
command, cwd=tempnssdb.secdir, capture_output=True,
- runas=pkiuser, suplementary_groups=[pkigroup]).output
+ runas=pkiuser, suplementary_groups=group_list).output
found = False
token_line = f'token: {token_name}'
for line in lines.split('\n'):
@@ -265,7 +261,7 @@ def hsm_validator(token_name, token_library, token_password):
]
result = ipautil.run(args, cwd=tempnssdb.secdir,
runas=pkiuser,
- suplementary_groups=[pkigroup],
+ suplementary_groups=group_list,
capture_error=True, raiseonerr=False)
if result.returncode != 0 and len(result.error_output):
if 'SEC_ERROR_BAD_PASSWORD' in result.error_output:
--
2.47.1

View File

@ -1,166 +0,0 @@
From d4d56a6705c870901bc73882e4804367f7c9c91a Mon Sep 17 00:00:00 2001
From: Alexander Bokovoy <abokovoy@redhat.com>
Date: Sun, 1 Dec 2024 20:16:54 +0200
Subject: [PATCH] ipalib/x509: support PyCA 44.0
PyCA made x509.Certificate class concrete, it cannot be extended anymore
by Python code. The intent is to use helper functions to instantiate
certificate objects and never create them directly.
FreeIPA wraps PyCA's x509.Certificate class and provides own shim
on top of it. In most cases we load the certificate content via the
helper functions and don't really need to derive from the certificate
class.
Move IPACertificate to be a normal Python object class that stores
x509.Certificate internally. The only place where this breaks is when
IPACertificate object needs to be passed to a code that expects
x509.Certificate (Dogtag PKI). In such cases, expose the underlying
certificate instance via IPACertificate.cert property.
Fixes: https://pagure.io/freeipa/issue/9708
Signed-off-by: Alexander Bokovoy <abokovoy@redhat.com>
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
---
ipalib/ipajson.py | 4 ++--
ipalib/x509.py | 10 +++++++++-
ipapython/ipaldap.py | 15 +++++++--------
ipaserver/plugins/dogtag.py | 3 ++-
4 files changed, 20 insertions(+), 12 deletions(-)
diff --git a/ipalib/ipajson.py b/ipalib/ipajson.py
index 5551d12e5fec7e458fa6fe85560664b2fd897337..fd99c8219c722c52321336f28ff27e1573e906c7 100644
--- a/ipalib/ipajson.py
+++ b/ipalib/ipajson.py
@@ -9,7 +9,7 @@ from decimal import Decimal
import json
import six
from ipalib.constants import LDAP_GENERALIZED_TIME_FORMAT
-from ipalib import capabilities
+from ipalib import capabilities, x509
from ipalib.x509 import Encoding as x509_Encoding
from ipapython.dn import DN
from ipapython.dnsutil import DNSName
@@ -72,7 +72,7 @@ class _JSONPrimer(dict):
list: self._enc_list,
tuple: self._enc_list,
dict: self._enc_dict,
- crypto_x509.Certificate: self._enc_certificate,
+ x509.IPACertificate: self._enc_certificate,
crypto_x509.CertificateSigningRequest: self._enc_certificate,
})
diff --git a/ipalib/x509.py b/ipalib/x509.py
index fd08238962b2b5e9cd056fb13c0a81ee8f31b092..6780bead00b50efdf03c62ce717572eeb9df2e5f 100644
--- a/ipalib/x509.py
+++ b/ipalib/x509.py
@@ -88,7 +88,7 @@ SAN_UPN = '1.3.6.1.4.1.311.20.2.3'
SAN_KRB5PRINCIPALNAME = '1.3.6.1.5.2.2'
-class IPACertificate(crypto_x509.Certificate):
+class IPACertificate:
"""
A proxy class wrapping a python-cryptography certificate representation for
IPA purposes
@@ -205,6 +205,10 @@ class IPACertificate(crypto_x509.Certificate):
"""
return self._cert.fingerprint(algorithm)
+ @property
+ def cert(self):
+ return self._cert
+
@property
def serial_number(self):
return self._cert.serial_number
@@ -457,6 +461,8 @@ def load_pem_x509_certificate(data):
:returns: a ``IPACertificate`` object.
:raises: ``ValueError`` if unable to load the certificate.
"""
+ if isinstance(data, IPACertificate):
+ return data
return IPACertificate(
crypto_x509.load_pem_x509_certificate(data, backend=default_backend())
)
@@ -469,6 +475,8 @@ def load_der_x509_certificate(data):
:returns: a ``IPACertificate`` object.
:raises: ``ValueError`` if unable to load the certificate.
"""
+ if isinstance(data, IPACertificate):
+ return data
return IPACertificate(
crypto_x509.load_der_x509_certificate(data, backend=default_backend())
)
diff --git a/ipapython/ipaldap.py b/ipapython/ipaldap.py
index 1888e40916aa6e641542f08fb30ff2b0d4b850b1..5bb81c1bc844fce9b14251d3702e09099d85cdb5 100644
--- a/ipapython/ipaldap.py
+++ b/ipapython/ipaldap.py
@@ -33,7 +33,6 @@ import warnings
from collections import OrderedDict
-from cryptography import x509 as crypto_x509
from cryptography.hazmat.primitives import serialization
import ldap
@@ -748,10 +747,10 @@ class LDAPClient:
'dnszoneidnsname': DNSName,
'krbcanonicalname': Principal,
'krbprincipalname': Principal,
- 'usercertificate': crypto_x509.Certificate,
- 'usercertificate;binary': crypto_x509.Certificate,
- 'cACertificate': crypto_x509.Certificate,
- 'cACertificate;binary': crypto_x509.Certificate,
+ 'usercertificate': x509.IPACertificate,
+ 'usercertificate;binary': x509.IPACertificate,
+ 'cACertificate': x509.IPACertificate,
+ 'cACertificate;binary': x509.IPACertificate,
'nsds5replicalastupdatestart': unicode,
'nsds5replicalastupdateend': unicode,
'nsds5replicalastinitstart': unicode,
@@ -1000,7 +999,7 @@ class LDAPClient:
return dct
elif isinstance(val, datetime):
return val.strftime(LDAP_GENERALIZED_TIME_FORMAT).encode('utf-8')
- elif isinstance(val, crypto_x509.Certificate):
+ elif isinstance(val, x509.IPACertificate):
return val.public_bytes(x509.Encoding.DER)
elif val is None:
return None
@@ -1027,7 +1026,7 @@ class LDAPClient:
return DNSName.from_text(val.decode('utf-8'))
elif target_type in (DN, Principal):
return target_type(val.decode('utf-8'))
- elif target_type is crypto_x509.Certificate:
+ elif target_type is x509.IPACertificate:
return x509.load_der_x509_certificate(val)
else:
return target_type(val)
@@ -1381,7 +1380,7 @@ class LDAPClient:
]
return cls.combine_filters(flts, rules)
elif value is not None:
- if isinstance(value, crypto_x509.Certificate):
+ if isinstance(value, x509.IPACertificate):
value = value.public_bytes(serialization.Encoding.DER)
if isinstance(value, bytes):
value = binascii.hexlify(value).decode('ascii')
diff --git a/ipaserver/plugins/dogtag.py b/ipaserver/plugins/dogtag.py
index 78afb279795ecf74f296cbbb8724505075a6e4a9..ee6d0e347d640a2664e38ba64785c3d8af54bbad 100644
--- a/ipaserver/plugins/dogtag.py
+++ b/ipaserver/plugins/dogtag.py
@@ -1581,7 +1581,8 @@ class kra(Backend):
crypto = cryptoutil.CryptographyCryptoProvider(
transport_cert_nick="ra_agent",
- transport_cert=x509.load_certificate_from_file(paths.RA_AGENT_PEM)
+ transport_cert=x509.load_certificate_from_file(
+ paths.RA_AGENT_PEM).cert
)
# TODO: obtain KRA host & port from IPA service list or point to KRA load balancer
--
2.47.1

View File

@ -1,120 +0,0 @@
From 8dfec28647f7c17e47fbfc96a1720dcde1592386 Mon Sep 17 00:00:00 2001
From: Stanislav Levin <slev@altlinux.org>
Date: Mon, 2 Dec 2024 15:04:30 +0300
Subject: [PATCH] pyca: adapt import paths for TripleDES cipher
https://cryptography.io/en/latest/hazmat/primitives/symmetric-encryption/#cryptography.hazmat.primitives.ciphers.algorithms.TripleDES
> This algorithm has been deprecated and moved to the Decrepit
cryptography module. If you need to continue using it then update your
code to use the new module path. It will be removed from this namespace
in 48.0.0.
Fixes: https://pagure.io/freeipa/issue/9708
Signed-off-by: Stanislav Levin <slev@altlinux.org>
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
---
ipaclient/plugins/vault.py | 8 +++++++-
ipalib/constants.py | 24 +++++++++++-------------
ipaserver/install/ipa_otptoken_import.py | 8 +++++++-
3 files changed, 25 insertions(+), 15 deletions(-)
diff --git a/ipaclient/plugins/vault.py b/ipaclient/plugins/vault.py
index 75415c03a57242ae674636fa31a72db2fa56d6ea..6af7297936924dfb80e7f79924b570421da65c97 100644
--- a/ipaclient/plugins/vault.py
+++ b/ipaclient/plugins/vault.py
@@ -34,6 +34,12 @@ from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
from cryptography.hazmat.primitives.asymmetric import padding
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
+try:
+ # cryptography>=43.0.0
+ from cryptography.hazmat.decrepit.ciphers.algorithms import TripleDES
+except ImportError:
+ # will be removed from this module in cryptography 48.0.0
+ from cryptography.hazmat.primitives.ciphers.algorithms import TripleDES
from cryptography.hazmat.primitives.padding import PKCS7
from cryptography.hazmat.primitives.serialization import (
load_pem_public_key, load_pem_private_key)
@@ -661,7 +667,7 @@ class ModVaultData(Local):
if name == constants.VAULT_WRAPPING_AES128_CBC:
return algorithms.AES(os.urandom(128 // 8))
elif name == constants.VAULT_WRAPPING_3DES:
- return algorithms.TripleDES(os.urandom(196 // 8))
+ return TripleDES(os.urandom(196 // 8))
else:
# unreachable
raise ValueError(name)
diff --git a/ipalib/constants.py b/ipalib/constants.py
index b657e5a9065d115d0eff2dbfffff49e992006536..c90caa22149ec3d93d45fcb5480f7401e4555799 100644
--- a/ipalib/constants.py
+++ b/ipalib/constants.py
@@ -25,20 +25,19 @@ All constants centralised in one file.
import os
import string
import uuid
-import warnings
-
-warnings.filterwarnings(
- "ignore",
- "TripleDES has been moved to "
- "cryptography.hazmat.decrepit.ciphers.algorithms.TripleDES and "
- "will be removed from this module in 48.0.0",
- category=UserWarning)
from ipaplatform.constants import constants as _constants
from ipapython.dn import DN
from ipapython.fqdn import gethostfqdn
from ipapython.version import VERSION, API_VERSION
-from cryptography.hazmat.primitives.ciphers import algorithms, modes
+from cryptography.hazmat.primitives.ciphers import modes
+try:
+ # cryptography>=43.0.0
+ from cryptography.hazmat.decrepit.ciphers.algorithms import TripleDES
+except ImportError:
+ # will be removed from this module in cryptography 48.0.0
+ from cryptography.hazmat.primitives.ciphers.algorithms import TripleDES
+
from cryptography.hazmat.backends.openssl.backend import backend
@@ -389,7 +388,6 @@ VAULT_WRAPPING_SUPPORTED_ALGOS = (
VAULT_WRAPPING_DEFAULT_ALGO = VAULT_WRAPPING_AES128_CBC
# Add 3DES for backwards compatibility if supported
-if getattr(algorithms, 'TripleDES', None):
- if backend.cipher_supported(algorithms.TripleDES(
- b"\x00" * 8), modes.CBC(b"\x00" * 8)):
- VAULT_WRAPPING_SUPPORTED_ALGOS += (VAULT_WRAPPING_3DES,)
+if backend.cipher_supported(TripleDES(
+ b"\x00" * 8), modes.CBC(b"\x00" * 8)):
+ VAULT_WRAPPING_SUPPORTED_ALGOS += (VAULT_WRAPPING_3DES,)
diff --git a/ipaserver/install/ipa_otptoken_import.py b/ipaserver/install/ipa_otptoken_import.py
index 279a7502d2f305309252b3b291e32b772a51a1d3..17457f6c5b81ab70a0ecee13bf744e242ec88ff0 100644
--- a/ipaserver/install/ipa_otptoken_import.py
+++ b/ipaserver/install/ipa_otptoken_import.py
@@ -37,6 +37,12 @@ from cryptography.hazmat.primitives import hashes, hmac
from cryptography.hazmat.primitives.padding import PKCS7
from cryptography.hazmat.primitives.kdf import pbkdf2
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
+try:
+ # cryptography>=43.0.0
+ from cryptography.hazmat.decrepit.ciphers.algorithms import TripleDES
+except ImportError:
+ # will be removed from this module in cryptography 48.0.0
+ from cryptography.hazmat.primitives.ciphers.algorithms import TripleDES
from cryptography.hazmat.backends import default_backend
from ipaplatform.paths import paths
@@ -169,7 +175,7 @@ def convertAlgorithm(value):
# in the list of the vault wrapping algorithms, we cannot use 3DES anywhere
if VAULT_WRAPPING_3DES in VAULT_WRAPPING_SUPPORTED_ALGOS:
supported_algs["http://www.w3.org/2001/04/xmlenc#tripledes-cbc"] = (
- algorithms.TripleDES, modes.CBC, 64)
+ TripleDES, modes.CBC, 64)
return supported_algs.get(value.lower(), (None, None, None))
--
2.47.1

View File

@ -1,134 +0,0 @@
From 3e7ec3dc49d0f559bdbe330e52019e59f0b57c18 Mon Sep 17 00:00:00 2001
From: Alexander Bokovoy <abokovoy@redhat.com>
Date: Tue, 3 Dec 2024 18:06:45 +0200
Subject: [PATCH] ipa-pwd-extop: clarify OTP use over LDAP binds
OTP use during LDAP bind can be enforced either explicitly via client
specifying a control with OID 2.16.840.1.113730.3.8.10.7 and no payload
or implicitly through the global IPA configuration with EnforceLDAPOTP.
OTP token enforcement overrides IPA user authentication types
requirements:
If OTP enforcement is required:
- if user authentication types still allow password authentication,
authentication with just a password is denied, regardless whether OTP
tokens are associated with the user or not.
If OTP enforcement is not required:
- if user has no OTP tokens but user authentication types require OTP
use, authentication with just a password is allowed until a token is
added.
- if user has OTP tokens and user authentication types require OTP use
but not password, authentication with just a password is denied.
Additionally, enforcement of OTP only applies to LDAP objects which
don't use 'simpleSecurityObject' objectclass. This allows system service
accounts to continue authenticate with a password regardless of the
OTP enforcement.
Fixes: https://pagure.io/freeipa/issue/9699
Fixes: https://pagure.io/freeipa/issue/9711
Signed-off-by: Alexander Bokovoy <abokovoy@redhat.com>
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
---
.../ipa-slapi-plugins/ipa-pwd-extop/prepost.c | 38 +++++++++++++++----
1 file changed, 30 insertions(+), 8 deletions(-)
diff --git a/daemons/ipa-slapi-plugins/ipa-pwd-extop/prepost.c b/daemons/ipa-slapi-plugins/ipa-pwd-extop/prepost.c
index 1c1340e31ac30cb01412a7065ea339cb5461e839..42e880fd0a5c8b4708b145b340209eb218f60c4e 100644
--- a/daemons/ipa-slapi-plugins/ipa-pwd-extop/prepost.c
+++ b/daemons/ipa-slapi-plugins/ipa-pwd-extop/prepost.c
@@ -1219,12 +1219,10 @@ typedef enum {
} otp_req_enum;
static bool ipapwd_pre_bind_otp(const char *bind_dn, Slapi_Entry *entry,
struct berval *creds, otp_req_enum otpreq,
- bool *notokens)
+ bool *notokens, uint32_t *auth_types)
{
- uint32_t auth_types;
-
/* Get the configured authentication types. */
- auth_types = otp_config_auth_types(otp_config, entry);
+ *auth_types = otp_config_auth_types(otp_config, entry);
*notokens = false;
/*
@@ -1237,7 +1235,8 @@ static bool ipapwd_pre_bind_otp(const char *bind_dn, Slapi_Entry *entry,
* 2. If PWD is enabled or OTP succeeded, fall through to PWD validation.
*/
- if (auth_types & OTP_CONFIG_AUTH_TYPE_OTP) {
+ if ((*auth_types & OTP_CONFIG_AUTH_TYPE_OTP) ||
+ (otpreq != OTP_IS_NOT_REQUIRED)) {
struct otp_token **tokens = NULL;
LOG_PLUGIN_NAME(IPAPWD_PLUGIN_NAME,
@@ -1270,7 +1269,7 @@ static bool ipapwd_pre_bind_otp(const char *bind_dn, Slapi_Entry *entry,
otp_token_free_array(tokens);
}
- return (auth_types & OTP_CONFIG_AUTH_TYPE_PASSWORD) &&
+ return (*auth_types & OTP_CONFIG_AUTH_TYPE_PASSWORD) &&
(otpreq == OTP_IS_NOT_REQUIRED);
}
@@ -1451,6 +1450,7 @@ static int ipapwd_pre_bind(Slapi_PBlock *pb)
struct ipapwd_krbcfg *krbcfg = NULL;
struct berval *credentials = NULL;
Slapi_Entry *entry = NULL;
+ Slapi_Value *objectclass = NULL;
Slapi_DN *target_sdn = NULL;
Slapi_DN *sdn = NULL;
const char *dn = NULL;
@@ -1465,6 +1465,7 @@ static int ipapwd_pre_bind(Slapi_PBlock *pb)
int rc = LDAP_INVALID_CREDENTIALS;
char *errMesg = NULL;
bool notokens = false;
+ uint32_t auth_types = 0;
/* get BIND parameters */
ret |= slapi_pblock_get(pb, SLAPI_BIND_TARGET_SDN, &target_sdn);
@@ -1538,12 +1539,33 @@ static int ipapwd_pre_bind(Slapi_PBlock *pb)
otpreq = OTP_IS_REQUIRED_IMPLICITLY;
}
}
+ /* we only apply OTP policy to Kerberos principals */
+ objectclass = slapi_value_new_string("krbprincipalaux");
+ if (objectclass == NULL) {
+ goto invalid_creds;
+ }
+ if (!slapi_entry_attr_has_syntax_value(entry, SLAPI_ATTR_OBJECTCLASS,
+ objectclass)) {
+ otpreq = OTP_IS_NOT_REQUIRED;
+ }
+ slapi_value_free(&objectclass);
+
if (!syncreq && !ipapwd_pre_bind_otp(dn, entry,
- credentials, otpreq, &notokens)) {
+ credentials, otpreq,
+ &notokens, &auth_types)) {
/* We got here because ipapwd_pre_bind_otp() returned false,
* it means that either token verification failed or
* a rule for empty tokens failed current policy. */
- if (!(notokens || (otpreq == OTP_IS_NOT_REQUIRED)))
+
+ /* Check if there were any tokens associated, thus
+ * OTP token verification has really failed */
+ if (notokens == false)
+ goto invalid_creds;
+
+ /* No tokens, check if auth type does not include OTP but OTP is
+ * enforced by the current policy */
+ if (!(auth_types & OTP_CONFIG_AUTH_TYPE_OTP) &&
+ (otpreq != OTP_IS_NOT_REQUIRED))
goto invalid_creds;
}
--
2.47.1

View File

@ -1,45 +0,0 @@
From 477dbba18bf987bf4461fdfdfba0d497159db7ce Mon Sep 17 00:00:00 2001
From: Stanislav Levin <slev@altlinux.org>
Date: Wed, 4 Dec 2024 19:56:51 +0300
Subject: [PATCH] adtrust: add missing ipaAllowedOperations objectclass
Per @abbra explanation:
> When expected Kerberos principal names for this object were flipped to
follow requirements for cross-realm krbtgt objects expected by Active
Directory, trusted object changed its canonical Kerberos principal name.
The keytab for this Kerberos principal name is fetched by SSSD and it
needs to be permitted to read the key. We added the virtual permission
to allow the keytab retrieval but didn't add the objectclass that
actually allows adding an LDAP attribute to express the permission. When
an attribute is added to an LDAP object, objectclasses of the object
must allow presence of that attribute.
This is the followup to #9471 and fixes the upgrade.
Thanks @abbra!
Related: https://pagure.io/freeipa/issue/9471
Fixes: https://pagure.io/freeipa/issue/9712
Signed-off-by: Stanislav Levin <slev@altlinux.org>
Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
---
ipaserver/install/plugins/adtrust.py | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/ipaserver/install/plugins/adtrust.py b/ipaserver/install/plugins/adtrust.py
index e6d49cb2512bff7dcce57f019ecb6c497d11ed52..ab3d427ef561aeb26eb098270446640ba451c8ad 100644
--- a/ipaserver/install/plugins/adtrust.py
+++ b/ipaserver/install/plugins/adtrust.py
@@ -705,7 +705,8 @@ class update_tdo_to_new_layout(Updater):
self.set_krb_principal([tgt_principal, nbt_principal],
passwd_incoming,
t_dn,
- flags=self.KRB_PRINC_CREATE_DEFAULT)
+ flags=self.KRB_PRINC_CREATE_DEFAULT
+ | self.KRB_PRINC_CREATE_AGENT_PERMISSION)
# 3. INBOUND: krbtgt/<OUR REALM>@<REMOTE REALM> must exist
trust_principal = self.tgt_principal_template.format(
--
2.47.1

View File

@ -1,37 +0,0 @@
From 6a2310eda39b1341258211c7630ef4baf4555df5 Mon Sep 17 00:00:00 2001
From: Sudhir Menon <sumenon@redhat.com>
Date: Mon, 9 Dec 2024 23:03:56 +0530
Subject: [PATCH] Fix the typo in ipa_migrate_constants.
ipa-migrate.log displays Privileges migrated as Privledges
due to typo in labelling i.e 'label': 'Privledges'
Hence changed that to 'label': 'Privileges'
---- LOG FILE ----
INFO - Privledges: 3
------------------
Fixes: https://pagure.io/freeipa/issue/9715
Signed-off-by: Sudhir Menon <sumenon@redhat.com>
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
---
ipaserver/install/ipa_migrate_constants.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/ipaserver/install/ipa_migrate_constants.py b/ipaserver/install/ipa_migrate_constants.py
index c140414ea6c607a93e35ef0705480d1002b7945e..e8192fb1aabae1c36669370eff242428a1f0355f 100644
--- a/ipaserver/install/ipa_migrate_constants.py
+++ b/ipaserver/install/ipa_migrate_constants.py
@@ -886,7 +886,7 @@ DB_OBJECTS = {
'pbac_priv': {
'oc': ['groupofnames'],
'subtree': ',cn=privileges,cn=pbac,$SUFFIX',
- 'label': 'Privledges',
+ 'label': 'Privileges',
'mode': 'all',
'count': 0,
},
--
2.47.1

View File

@ -1,35 +0,0 @@
From d8d4c5507bd3784d323e6836ff8456ba9608f115 Mon Sep 17 00:00:00 2001
From: Florence Blanc-Renaud <flo@redhat.com>
Date: Fri, 20 Sep 2024 09:36:41 +0200
Subject: [PATCH] test_ipahealthcheck: skip connectivity_and_data check
PKI removed the clones.check connectivity_and_data check in
11.5 and master branches. Skip the test depending on PKI version.
The most recent version on 11.5 is 11.5.4 and still contains the check,
hence skipping if version >= 11.5.5.
Fixes: https://pagure.io/freeipa/issue/9668
Signed-off-by: Florence Blanc-Renaud <flo@redhat.com>
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
---
ipatests/test_integration/test_ipahealthcheck.py | 3 +++
1 file changed, 3 insertions(+)
diff --git a/ipatests/test_integration/test_ipahealthcheck.py b/ipatests/test_integration/test_ipahealthcheck.py
index a3e10b04487f0a12fdde19a8d1971a09f7d79b63..cc51a5a6a62fbc50927fc2fc51f129a069e70b69 100644
--- a/ipatests/test_integration/test_ipahealthcheck.py
+++ b/ipatests/test_integration/test_ipahealthcheck.py
@@ -1219,6 +1219,9 @@ class TestIpaHealthCheck(IntegrationTest):
This testcase checks that when ClonesConnectivyAndDataCheck
is run it doesn't display source not found error
"""
+ if (tasks.get_pki_version(
+ self.master) >= tasks.parse_version('11.5.5')):
+ raise pytest.skip("PKI dropped ClonesConnectivyAndDataCheck")
error_msg = (
"Source 'pki.server.healthcheck.clones.connectivity_and_data' "
"not found"
--
2.47.1

View File

@ -1,36 +0,0 @@
From c294b7f6dbcae9c01d5366b19b3b070ffb730fa6 Mon Sep 17 00:00:00 2001
From: Sudhir Menon <sumenon@redhat.com>
Date: Wed, 11 Dec 2024 15:12:48 +0530
Subject: [PATCH] ipatests: Fixes for ipa-ipa-migration tool
The test test_ipa_migrate_with_invalid_host has been
failing in downstream run due to mismatch in the expected test output,
hence the assert statement has been modified.
Related: https://pagure.io/freeipa/issue/3656
Signed-off-by: Sudhir Menon <sumenon@redhat.com>
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
---
ipatests/test_integration/test_ipa_ipa_migration.py | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/ipatests/test_integration/test_ipa_ipa_migration.py b/ipatests/test_integration/test_ipa_ipa_migration.py
index 0c637a0141d95f34f951c60a9648adf8e87eaa63..95c29234fc7893d3eae5d900a58aa7b1162ed61d 100644
--- a/ipatests/test_integration/test_ipa_ipa_migration.py
+++ b/ipatests/test_integration/test_ipa_ipa_migration.py
@@ -437,10 +437,8 @@ class TestIPAMigrateCLIOptions(MigrationTest):
"""
hostname = "server.invalid.host"
ERR_MSG = (
- "IPA to IPA migration starting ...\n"
"Failed to bind to remote server: cannot connect to "
- "'ldap://"
- "{}': \n".format(hostname)
+ "'ldap://{}':".format(hostname)
)
result = run_migrate(
self.replicas[0],
--
2.47.1

View File

@ -1,47 +0,0 @@
From ff5bb485d709ea02422cbafba51d23db384822af Mon Sep 17 00:00:00 2001
From: Florence Blanc-Renaud <flo@redhat.com>
Date: Fri, 20 Dec 2024 15:45:01 +0100
Subject: [PATCH] Installation test: KRA on replica after cert renewal
Add a new test installing the KRA on a replica after the
KRA certs have been renewed on the master.
Related: https://pagure.io/freeipa/issue/9692
Signed-off-by: Florence Blanc-Renaud <flo@redhat.com>
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
---
ipatests/test_integration/test_installation.py | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)
diff --git a/ipatests/test_integration/test_installation.py b/ipatests/test_integration/test_installation.py
index c5565c452010f23f038ddf329454b591ef09f6af..1fae472673a3934c86af2f9adc7b343c70e25b2b 100644
--- a/ipatests/test_integration/test_installation.py
+++ b/ipatests/test_integration/test_installation.py
@@ -1322,7 +1322,7 @@ class TestInstallMaster(IntegrationTest):
class TestInstallMasterKRA(IntegrationTest):
- num_replicas = 0
+ num_replicas = 1
@classmethod
def install(cls, mh):
@@ -1379,6 +1379,14 @@ class TestInstallMasterKRA(IntegrationTest):
)
assert starting_serial != int(cert.serial_number)
+ def test_install_replica_after_kracert_renewal(self):
+ """
+ Test replica installation with CA after the KRA certs renewal
+ """
+ tasks.install_replica(self.master, self.replicas[0],
+ setup_ca=True)
+ tasks.install_kra(self.replicas[0])
+
class TestInstallMasterDNS(IntegrationTest):
--
2.47.1

View File

@ -1,45 +0,0 @@
From a707083b0987e6ffabb817fcc5e5138b4c755459 Mon Sep 17 00:00:00 2001
From: Florence Blanc-Renaud <flo@redhat.com>
Date: Fri, 20 Dec 2024 17:01:56 +0100
Subject: [PATCH] KRA cert renewal: update ca.connector.KRA.transportCert
After the KRA transport cert has been renewed, the value
of ca.connector.KRA.transportCert must also be updated in
/etc/pki/pki-tomcat/ca/CS.cfg.
Otherwise replica installation with KRA fails.
Fixes: https://pagure.io/freeipa/issue/9692
Signed-off-by: Florence Blanc-Renaud <flo@redhat.com>
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
---
ipaserver/install/cainstance.py | 13 ++++++++-----
1 file changed, 8 insertions(+), 5 deletions(-)
diff --git a/ipaserver/install/cainstance.py b/ipaserver/install/cainstance.py
index 5c2c9f8b981cf5d587865f7680e2b231eae655e2..e03a8c863e14782679e19c6887f5e220131e4234 100644
--- a/ipaserver/install/cainstance.py
+++ b/ipaserver/install/cainstance.py
@@ -1225,11 +1225,14 @@ class CAInstance(DogtagInstance):
"""
# The cert directive to update per nickname
- directives = {'auditSigningCert cert-pki-ca': 'ca.audit_signing.cert',
- 'ocspSigningCert cert-pki-ca': 'ca.ocsp_signing.cert',
- 'caSigningCert cert-pki-ca': 'ca.signing.cert',
- 'subsystemCert cert-pki-ca': 'ca.subsystem.cert',
- 'Server-Cert cert-pki-ca': 'ca.sslserver.cert'}
+ directives = {
+ 'auditSigningCert cert-pki-ca': 'ca.audit_signing.cert',
+ 'ocspSigningCert cert-pki-ca': 'ca.ocsp_signing.cert',
+ 'caSigningCert cert-pki-ca': 'ca.signing.cert',
+ 'subsystemCert cert-pki-ca': 'ca.subsystem.cert',
+ 'Server-Cert cert-pki-ca': 'ca.sslserver.cert',
+ 'transportCert cert-pki-kra': 'ca.connector.KRA.transportCert'
+ }
try:
self.backup_config()
--
2.47.1

View File

@ -1,45 +0,0 @@
From 2506d5de5a9dd8ebe6efc777c2eb76461f5b57e2 Mon Sep 17 00:00:00 2001
From: Rob Crittenden <rcritten@redhat.com>
Date: Mon, 6 Jan 2025 10:12:15 -0500
Subject: [PATCH] Add 30-second timeout for certmonger request/start tracking
certmonger needs to validate that the PIN/password and/or token
are valid and available. In the case of a very slow HSM this can
take longer than the 5-second default timeout.
We saw an HSM that took 18 seconds to start tracking the CA signing
certificate so default to 30 to be safe.
Fixes: https://pagure.io/freeipa/issue/9725
Signed-off-by: Rob Crittenden <rcritten@redhat.com>
Reviewed-By: Alexander Bokovoy <abbra@users.noreply.github.com>
---
ipalib/install/certmonger.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/ipalib/install/certmonger.py b/ipalib/install/certmonger.py
index 7b22295152f752b6ab4de0f3525d48c541677aff..efc1ba4f42eab98df5fac51bafa3acc83ae91831 100644
--- a/ipalib/install/certmonger.py
+++ b/ipalib/install/certmonger.py
@@ -477,7 +477,7 @@ def request_cert(
request_parameters['cert-perms'] = perms[0]
request_parameters['key-perms'] = perms[1]
- result = cm.obj_if.add_request(request_parameters)
+ result = cm.obj_if.add_request(request_parameters, timeout=30)
try:
if result[0]:
request = _cm_dbus_object(cm.bus, cm, result[1], DBUS_CM_REQUEST_IF,
@@ -581,7 +581,7 @@ def start_tracking(
if nss_user:
params['nss-user'] = nss_user
- result = cm.obj_if.add_request(params)
+ result = cm.obj_if.add_request(params, timeout=30)
try:
if result[0]:
request = _cm_dbus_object(cm.bus, cm, result[1], DBUS_CM_REQUEST_IF,
--
2.47.1

View File

@ -1,624 +0,0 @@
From d26ce5cccc211f83b3cce3fc5e548b5cb955bb81 Mon Sep 17 00:00:00 2001
From: Alexander Bokovoy <abokovoy@redhat.com>
Date: Fri, 13 Dec 2024 13:42:36 +0200
Subject: [PATCH] Unify use of option parsers
Do not use direct optparse references, instead import IPAOptionParser
Signed-off-by: Alexander Bokovoy <abokovoy@redhat.com>
---
install/tools/ipa-adtrust-install.in | 4 +---
install/tools/ipa-managed-entries.in | 3 ++-
ipaclient/install/ipa_client_automount.py | 4 ++--
ipaclient/install/ipa_client_samba.py | 4 ++--
ipalib/cli.py | 21 ++++++++++++---------
ipalib/plugable.py | 8 ++++----
ipapython/admintool.py | 3 +--
ipapython/config.py | 18 +++++++++++-------
ipapython/install/cli.py | 9 ++++-----
ipaserver/install/ipa_acme_manage.py | 6 ++----
ipaserver/install/ipa_backup.py | 5 ++---
ipaserver/install/ipa_cacert_manage.py | 9 ++++-----
ipaserver/install/ipa_kra_install.py | 5 ++---
ipaserver/install/ipa_restore.py | 5 ++---
ipaserver/install/ipa_server_certinstall.py | 7 +++----
ipatests/i18n.py | 8 ++++----
makeapi.in | 5 ++---
17 files changed, 60 insertions(+), 64 deletions(-)
diff --git a/install/tools/ipa-adtrust-install.in b/install/tools/ipa-adtrust-install.in
index cb2b78e504896b96cdc378fd879cc1f00c60904f..e7b0e369259da5d28d703558d9293ccfaf68f3ed 100644
--- a/install/tools/ipa-adtrust-install.in
+++ b/install/tools/ipa-adtrust-install.in
@@ -29,8 +29,6 @@ import sys
import six
-from optparse import SUPPRESS_HELP # pylint: disable=deprecated-module
-
from ipalib.install import sysrestore
from ipaserver.install import adtrust, service
from ipaserver.install.installutils import (
@@ -41,7 +39,7 @@ from ipapython.admintool import ScriptError
from ipapython import version
from ipapython import ipautil
from ipalib import api, errors, krb_utils
-from ipapython.config import IPAOptionParser
+from ipapython.config import IPAOptionParser, SUPPRESS_HELP
from ipaplatform.paths import paths
from ipapython.ipa_log_manager import standard_logging_setup
diff --git a/install/tools/ipa-managed-entries.in b/install/tools/ipa-managed-entries.in
index e9be41b7a34ed62e65ab40bf544a6e4cea7c598a..e3f121943eb3b18ca8f7f8bfeae7813cbc9bd753 100644
--- a/install/tools/ipa-managed-entries.in
+++ b/install/tools/ipa-managed-entries.in
@@ -39,7 +39,8 @@ logger = logging.getLogger(os.path.basename(__file__))
def parse_options():
usage = "%prog [options] <status|enable|disable>\n"
usage += "%prog [options]\n"
- parser = OptionParser(usage=usage, formatter=config.IPAFormatter())
+ parser = config.IPAOptionParser(usage=usage,
+ formatter=config.IPAFormatter())
parser.add_option("-d", "--debug", action="store_true", dest="debug",
help="Display debugging information about the update(s)")
diff --git a/ipaclient/install/ipa_client_automount.py b/ipaclient/install/ipa_client_automount.py
index 4439932bd723c40c429dac2c08e97d326e414d24..9f49ff9edeee2648d2be1dea6b09806ba0b5e041 100644
--- a/ipaclient/install/ipa_client_automount.py
+++ b/ipaclient/install/ipa_client_automount.py
@@ -34,7 +34,6 @@ import SSSDConfig
from six.moves.urllib.parse import urlsplit
-from optparse import OptionParser # pylint: disable=deprecated-module
from ipapython import ipachangeconf
from ipaclient import discovery
from ipaclient.install.client import (
@@ -52,6 +51,7 @@ from ipaplatform.tasks import tasks
from ipaplatform import services
from ipaplatform.paths import paths
from ipapython.admintool import ScriptError
+from ipapython.config import IPAOptionParser
logger = logging.getLogger(os.path.basename(__file__))
@@ -59,7 +59,7 @@ logger = logging.getLogger(os.path.basename(__file__))
def parse_options():
usage = "%prog [options]\n"
- parser = OptionParser(usage=usage)
+ parser = IPAOptionParser(usage=usage)
parser.add_option("--server", dest="server", help="FQDN of IPA server")
parser.add_option(
"--location",
diff --git a/ipaclient/install/ipa_client_samba.py b/ipaclient/install/ipa_client_samba.py
index 81d670c34cae0f91a436a2a2d1e0d525057b6cc7..5c33abb4cabe47ff311b7d769fefa37991f7d453 100755
--- a/ipaclient/install/ipa_client_samba.py
+++ b/ipaclient/install/ipa_client_samba.py
@@ -9,7 +9,6 @@ import logging
import os
import gssapi
from urllib.parse import urlsplit
-from optparse import OptionParser # pylint: disable=deprecated-module
from contextlib import contextmanager
from ipaclient import discovery
@@ -31,6 +30,7 @@ from ipaplatform.constants import constants
from ipaplatform import services
from ipapython.admintool import ScriptError
from samba import generate_random_password
+from ipapython.config import IPAOptionParser
logger = logging.getLogger(os.path.basename(__file__))
logger.setLevel(logging.DEBUG)
@@ -68,7 +68,7 @@ def use_api_as_principal(principal, keytab):
def parse_options():
usage = "%prog [options]\n"
- parser = OptionParser(usage=usage)
+ parser = IPAOptionParser(usage=usage)
parser.add_option(
"--server",
dest="server",
diff --git a/ipalib/cli.py b/ipalib/cli.py
index d9c2ac16513101098c8f9a2258ae930b3b2cebf0..667b213fd4e4706e6eecd4d2b4737fb3fede4c93 100644
--- a/ipalib/cli.py
+++ b/ipalib/cli.py
@@ -30,7 +30,6 @@ import textwrap
import sys
import getpass
import code
-import optparse # pylint: disable=deprecated-module
import os
import pprint
import fcntl
@@ -71,6 +70,8 @@ from ipalib.text import _
from ipalib import api
from ipapython.dnsutil import DNSName
from ipapython.admintool import ScriptError
+from ipapython.config import (IPAOptionParser, IPAFormatter,
+ OptionGroup, make_option)
import datetime
@@ -1121,7 +1122,8 @@ class Collector:
def __todict__(self):
return dict(self.__options)
-class CLIOptionParserFormatter(optparse.IndentedHelpFormatter):
+
+class CLIOptionParserFormatter(IPAFormatter):
def format_argument(self, name, help_string):
result = []
opt_width = self.help_position - self.current_indent - 2
@@ -1141,7 +1143,8 @@ class CLIOptionParserFormatter(optparse.IndentedHelpFormatter):
result.append("\n")
return "".join(result)
-class CLIOptionParser(optparse.OptionParser):
+
+class CLIOptionParser(IPAOptionParser):
"""
This OptionParser subclass adds an ability to print positional
arguments in CLI help. Custom formatter is used to format the argument
@@ -1151,13 +1154,13 @@ class CLIOptionParser(optparse.OptionParser):
self._arguments = []
if 'formatter' not in kwargs:
kwargs['formatter'] = CLIOptionParserFormatter()
- optparse.OptionParser.__init__(self, *args, **kwargs)
+ IPAOptionParser.__init__(self, *args, **kwargs)
def format_option_help(self, formatter=None):
"""
Prepend argument help to standard OptionParser's option help
"""
- option_help = optparse.OptionParser.format_option_help(self, formatter)
+ option_help = IPAOptionParser.format_option_help(self, formatter)
if isinstance(formatter, CLIOptionParserFormatter):
heading = unicode(_("Positional arguments"))
@@ -1272,7 +1275,7 @@ class cli(backend.Executioner):
"""Get or create an option group for the given name"""
option_group = option_groups.get(group_name)
if option_group is None:
- option_group = optparse.OptionGroup(parser, group_name)
+ option_group = OptionGroup(parser, group_name)
parser.add_option_group(option_group)
option_groups[group_name] = option_group
return option_group
@@ -1298,7 +1301,7 @@ class cli(backend.Executioner):
option_names = ['--%s' % cli_name]
if option.cli_short_name:
option_names.append('-%s' % option.cli_short_name)
- opt = optparse.make_option(*option_names, **kw)
+ opt = make_option(*option_names, **kw)
if option.option_group is None:
parser.add_option(opt)
else:
@@ -1312,7 +1315,7 @@ class cli(backend.Executioner):
group = _get_option_group(unicode(_('Deprecated options')))
for alias in option.deprecated_cli_aliases:
name = '--%s' % alias
- group.add_option(optparse.make_option(name, **new_kw))
+ group.add_option(make_option(name, **new_kw))
for arg in cmd.args():
name = self.__get_arg_name(arg, format_name=False)
@@ -1442,7 +1445,7 @@ class cli(backend.Executioner):
)
-class IPAHelpFormatter(optparse.IndentedHelpFormatter):
+class IPAHelpFormatter(IPAFormatter):
"""Formatter suitable for printing IPA command help
The default help formatter reflows text to fit the terminal, but it
diff --git a/ipalib/plugable.py b/ipalib/plugable.py
index 2e2861df054fa7ed3533d0f82a23b65322ab591b..a87e6e8914fa5579920aad8190c7f6a86cb3dfea 100644
--- a/ipalib/plugable.py
+++ b/ipalib/plugable.py
@@ -33,7 +33,6 @@ import sys
import threading
import os
from os import path
-import optparse # pylint: disable=deprecated-module
import textwrap
import collections
import importlib
@@ -47,6 +46,7 @@ from ipalib.util import classproperty
from ipalib.base import ReadOnly, lock, islocked
from ipalib.constants import DEFAULT_CONFIG
from ipapython import ipa_log_manager, ipautil
+from ipapython.config import IPAOptionParser, IPAFormatter
from ipapython.ipa_log_manager import (
LOGGING_FORMAT_FILE,
LOGGING_FORMAT_STDERR)
@@ -526,7 +526,7 @@ class API(ReadOnly):
def build_global_parser(self, parser=None, context=None):
"""
- Add global options to an optparse.OptionParser instance.
+ Add global options to an IPAOptionParser instance.
"""
def config_file_callback(option, opt, value, parser):
if not os.path.isfile(value):
@@ -536,7 +536,7 @@ class API(ReadOnly):
parser.values.conf = value
if parser is None:
- parser = optparse.OptionParser(
+ parser = IPAOptionParser(
add_help_option=False,
formatter=IPAHelpFormatter(),
usage='%prog [global-options] COMMAND [command-options]',
@@ -821,7 +821,7 @@ class API(ReadOnly):
return self.__next[plugin]
-class IPAHelpFormatter(optparse.IndentedHelpFormatter):
+class IPAHelpFormatter(IPAFormatter):
def format_epilog(self, epilog):
text_width = self.width - self.current_indent
indent = " " * self.current_indent
diff --git a/ipapython/admintool.py b/ipapython/admintool.py
index fdb4400d879165612405b3ba159a220773bd8d69..dff9112eba4009d2fc1a6202756c6671ba4d909d 100644
--- a/ipapython/admintool.py
+++ b/ipapython/admintool.py
@@ -26,7 +26,6 @@ import logging
import sys
import os
import traceback
-from optparse import OptionGroup # pylint: disable=deprecated-module
from ipaplatform.osinfo import osinfo
from ipapython import version
@@ -113,7 +112,7 @@ class AdminTool:
:param parser: The parser to add options to
:param debug_option: Add a --debug option as an alias to --verbose
"""
- group = OptionGroup(parser, "Logging and output options")
+ group = config.OptionGroup(parser, "Logging and output options")
group.add_option("-v", "--verbose", dest="verbose", default=False,
action="store_true", help="print debugging information")
if debug_option:
diff --git a/ipapython/config.py b/ipapython/config.py
index f53d0f998aaedf47715764577dd7f5048f4ca841..7af4dfdeb0047586de9694f233be8bf543190b9c 100644
--- a/ipapython/config.py
+++ b/ipapython/config.py
@@ -18,9 +18,9 @@
#
from __future__ import absolute_import
-# pylint: disable=deprecated-module
-from optparse import (
- Option, Values, OptionParser, IndentedHelpFormatter, OptionValueError)
+# pylint: disable=deprecated-module, disable=unused-import
+from optparse import (Option, Values, OptionGroup, OptionParser, SUPPRESS_HELP,
+ IndentedHelpFormatter, OptionValueError, make_option)
# pylint: enable=deprecated-module
from copy import copy
from configparser import ConfigParser as SafeConfigParser
@@ -113,10 +113,14 @@ class IPAOptionParser(OptionParser):
description=None,
formatter=None,
add_help_option=True,
- prog=None):
- OptionParser.__init__(self, usage, option_list, option_class,
- version, conflict_handler, description,
- formatter, add_help_option, prog)
+ prog=None,
+ epilog=None):
+ OptionParser.__init__(self, usage=usage, option_list=option_list,
+ option_class=option_class, version=version,
+ conflict_handler=conflict_handler,
+ description=description, formatter=formatter,
+ add_help_option=add_help_option, prog=prog,
+ epilog=epilog)
def get_safe_opts(self, opts):
"""
diff --git a/ipapython/install/cli.py b/ipapython/install/cli.py
index ab212be4e2c3f9c2aae97f7759672ef7c68c3347..a048b3c7c64bc22338e4517d64b33a44446c58a3 100644
--- a/ipapython/install/cli.py
+++ b/ipapython/install/cli.py
@@ -9,12 +9,11 @@ Command line support.
import collections
import enum
import logging
-import optparse # pylint: disable=deprecated-module
import signal
import six
-from ipapython import admintool
+from ipapython import admintool, config
from ipapython.ipa_log_manager import standard_logging_setup
from ipapython.ipautil import (CheckedIPAddress, CheckedIPAddressLoopback,
private_ccache)
@@ -158,7 +157,7 @@ class ConfigureTool(admintool.AdminTool):
try:
opt_group = groups[group_cls]
except KeyError:
- opt_group = groups[group_cls] = optparse.OptionGroup(
+ opt_group = groups[group_cls] = config.OptionGroup(
parser, "{0} options".format(group_cls.description))
parser.add_option_group(opt_group)
@@ -232,7 +231,7 @@ class ConfigureTool(admintool.AdminTool):
if not hidden:
help = knob_cls.description
else:
- help = optparse.SUPPRESS_HELP
+ help = config.SUPPRESS_HELP
opt_group.add_option(
*opt_strs,
@@ -256,7 +255,7 @@ class ConfigureTool(admintool.AdminTool):
# fake option parser to parse positional arguments
# (because optparse does not support positional argument parsing)
- fake_option_parser = optparse.OptionParser()
+ fake_option_parser = config.IPAOptionParser()
self.add_options(fake_option_parser, True)
fake_option_map = {option.dest: option
diff --git a/ipaserver/install/ipa_acme_manage.py b/ipaserver/install/ipa_acme_manage.py
index dc2359f49dfdd5c8f44ab96ee11a7240f8937e11..0decab394c1c18067fe0c194c040805a8d93d42d 100644
--- a/ipaserver/install/ipa_acme_manage.py
+++ b/ipaserver/install/ipa_acme_manage.py
@@ -7,14 +7,12 @@ import enum
import pki.util
import logging
-from optparse import OptionGroup # pylint: disable=deprecated-module
-
from ipalib import api, errors, x509
from ipalib import _
from ipalib.facts import is_ipa_configured
from ipaplatform.paths import paths
from ipapython.admintool import AdminTool
-from ipapython import cookie, dogtag
+from ipapython import cookie, dogtag, config
from ipapython.ipautil import run
from ipapython.certdb import NSSDatabase, EXTERNAL_CA_TRUST_FLAGS
from ipaserver.install import cainstance
@@ -143,7 +141,7 @@ class IPAACMEManage(AdminTool):
@classmethod
def add_options(cls, parser):
- group = OptionGroup(parser, 'Pruning')
+ group = config.OptionGroup(parser, 'Pruning')
group.add_option(
"--enable", dest="enable", action="store_true",
default=False, help="Enable certificate pruning")
diff --git a/ipaserver/install/ipa_backup.py b/ipaserver/install/ipa_backup.py
index 982e5dfc4c0339aada88f936ab450b7fc16944f2..b6af63813fc4eaadab44ad95386d86ae4f1e21ee 100644
--- a/ipaserver/install/ipa_backup.py
+++ b/ipaserver/install/ipa_backup.py
@@ -20,7 +20,6 @@
from __future__ import absolute_import, print_function
import logging
-import optparse # pylint: disable=deprecated-module
import os
import shutil
import sys
@@ -32,7 +31,7 @@ import six
from ipaplatform.paths import paths
from ipaplatform import services
from ipalib import api, errors
-from ipapython import version
+from ipapython import version, config
from ipapython.ipautil import run, write_tmp_file
from ipapython import admintool, certdb
from ipapython.dn import DN
@@ -245,7 +244,7 @@ class Backup(admintool.AdminTool):
parser.add_option(
"--gpg-keyring", dest="gpg_keyring",
- help=optparse.SUPPRESS_HELP)
+ help=config.SUPPRESS_HELP)
parser.add_option(
"--gpg", dest="gpg", action="store_true",
default=False, help="Encrypt the backup")
diff --git a/ipaserver/install/ipa_cacert_manage.py b/ipaserver/install/ipa_cacert_manage.py
index f6ab736fa985b00ba66a7001c4c4e2188841bcbe..048245237855212afe1f3ec4795b2253026ef864 100644
--- a/ipaserver/install/ipa_cacert_manage.py
+++ b/ipaserver/install/ipa_cacert_manage.py
@@ -22,14 +22,13 @@ from __future__ import print_function, absolute_import
import datetime
import logging
import os
-from optparse import OptionGroup # pylint: disable=deprecated-module
import gssapi
from ipalib.constants import (
RENEWAL_CA_NAME, RENEWAL_REUSE_CA_NAME, RENEWAL_SELFSIGNED_CA_NAME,
IPA_CA_CN)
from ipalib.install import certmonger, certstore
-from ipapython import admintool, ipautil
+from ipapython import admintool, ipautil, config
from ipapython.certdb import (EMPTY_TRUST_FLAGS,
EXTERNAL_CA_TRUST_FLAGS,
TrustFlags,
@@ -61,7 +60,7 @@ class CACertManage(admintool.AdminTool):
"-p", "--password", dest='password',
help="Directory Manager password")
- renew_group = OptionGroup(parser, "Renew options")
+ renew_group = config.OptionGroup(parser, "Renew options")
renew_group.add_option(
"--self-signed", dest='self_signed',
action='store_true',
@@ -89,7 +88,7 @@ class CACertManage(admintool.AdminTool):
"certificate chain")
parser.add_option_group(renew_group)
- install_group = OptionGroup(parser, "Install options")
+ install_group = config.OptionGroup(parser, "Install options")
install_group.add_option(
"-n", "--nickname", dest='nickname',
help="Nickname for the certificate")
@@ -98,7 +97,7 @@ class CACertManage(admintool.AdminTool):
help="Trust flags for the certificate in certutil format")
parser.add_option_group(install_group)
- delete_group = OptionGroup(parser, "Delete options")
+ delete_group = config.OptionGroup(parser, "Delete options")
delete_group.add_option(
"-f", "--force", action='store_true',
help="Force removing the CA even if chain validation fails")
diff --git a/ipaserver/install/ipa_kra_install.py b/ipaserver/install/ipa_kra_install.py
index 3e4cd67fa677db2534a639eb6beb14dfd78bf035..8a09179f7fa0c2ddad72d01e9c3eaf98575d0a88 100644
--- a/ipaserver/install/ipa_kra_install.py
+++ b/ipaserver/install/ipa_kra_install.py
@@ -22,13 +22,12 @@ from __future__ import print_function, absolute_import
import logging
import sys
import tempfile
-from optparse import SUPPRESS_HELP # pylint: disable=deprecated-module
from textwrap import dedent
from ipalib import api
from ipalib.constants import DOMAIN_LEVEL_1
from ipaplatform.paths import paths
-from ipapython import admintool
+from ipapython import admintool, config
from ipaserver.install import service
from ipaserver.install import cainstance
from ipaserver.install import custodiainstance
@@ -73,7 +72,7 @@ class KRAInstall(admintool.AdminTool):
parser.add_option(
"--uninstall",
dest="uninstall", action="store_true", default=False,
- help=SUPPRESS_HELP)
+ help=config.SUPPRESS_HELP)
parser.add_option(
"--pki-config-override", dest="pki_config_override",
diff --git a/ipaserver/install/ipa_restore.py b/ipaserver/install/ipa_restore.py
index 57ad8dd05206132d3458fa84e7fb996b135f7f71..8d75a0e6beba09086bc2ce8c73f3bcfe81e52113 100644
--- a/ipaserver/install/ipa_restore.py
+++ b/ipaserver/install/ipa_restore.py
@@ -20,7 +20,6 @@
from __future__ import absolute_import, print_function
import logging
-import optparse # pylint: disable=deprecated-module
import os
import shutil
import sys
@@ -34,7 +33,7 @@ import six
from ipaclient.install.client import update_ipa_nssdb
from ipalib import api, errors
from ipalib.constants import FQDN
-from ipapython import version, ipautil
+from ipapython import version, ipautil, config
from ipapython.ipautil import run, user_input
from ipapython import admintool, certdb
from ipapython.dn import DN
@@ -190,7 +189,7 @@ class Restore(admintool.AdminTool):
help="Directory Manager password")
parser.add_option(
"--gpg-keyring", dest="gpg_keyring",
- help=optparse.SUPPRESS_HELP)
+ help=config.SUPPRESS_HELP)
parser.add_option(
"--data", dest="data_only", action="store_true",
default=False, help="Restore only the data")
diff --git a/ipaserver/install/ipa_server_certinstall.py b/ipaserver/install/ipa_server_certinstall.py
index e29f00ec37779aeb10255c5df6e10d6ecc0a6d11..e9f680b1d1e505f89fc1611b8dbb3f84768f6781 100644
--- a/ipaserver/install/ipa_server_certinstall.py
+++ b/ipaserver/install/ipa_server_certinstall.py
@@ -22,12 +22,11 @@ from __future__ import print_function, absolute_import
import os
import os.path
import tempfile
-import optparse # pylint: disable=deprecated-module
from ipalib import x509
from ipalib.install import certmonger
from ipaplatform.paths import paths
-from ipapython import admintool, dogtag
+from ipapython import admintool, dogtag, config
from ipapython.certdb import NSSDatabase, get_ca_nickname
from ipapython.dn import DN
from ipapython import ipaldap
@@ -65,8 +64,8 @@ class ServerCertInstall(admintool.AdminTool):
help="The password of the PKCS#12 file")
parser.add_option(
"--dirsrv_pin", "--http_pin",
- dest="pin",
- help=optparse.SUPPRESS_HELP)
+ dest="pin", sensitive=True,
+ help=config.SUPPRESS_HELP)
parser.add_option(
"--cert-name",
dest="cert_name", metavar="NAME",
diff --git a/ipatests/i18n.py b/ipatests/i18n.py
index 49f5c4c3232346db3147bd7a5ba8056344ac907f..57915c286be72124fa23380f97f3922496f00c22 100644
--- a/ipatests/i18n.py
+++ b/ipatests/i18n.py
@@ -22,7 +22,6 @@ from __future__ import print_function
# WARNING: Do not import ipa modules, this is also used as a
# stand-alone script (invoked from po Makefile).
-import optparse # pylint: disable=deprecated-module
import sys
import gettext
import re
@@ -30,6 +29,7 @@ import os
import traceback
import polib
from collections import namedtuple
+from ipapython import config
import six
@@ -722,9 +722,9 @@ usage ='''
def main():
global verbose, print_traceback, pedantic, show_strings
- parser = optparse.OptionParser(usage=usage)
+ parser = config.IPAOptionParser(usage=usage)
- mode_group = optparse.OptionGroup(parser, 'Operational Mode',
+ mode_group = config.OptionGroup(parser, 'Operational Mode',
'You must select one these modes to run in')
mode_group.add_option('-g', '--test-gettext', action='store_const', const='test_gettext', dest='mode',
@@ -748,7 +748,7 @@ def main():
parser.add_option('--traceback', action='store_true', dest='print_traceback', default=False,
help='print the traceback when an exception occurs')
- param_group = optparse.OptionGroup(parser, 'Run Time Parameters',
+ param_group = config.OptionGroup(parser, 'Run Time Parameters',
'These may be used to modify the run time defaults')
param_group.add_option('--test-lang', action='store', dest='test_lang', default='test',
diff --git a/makeapi.in b/makeapi.in
index a801b9253db9500e0510fe591074ccf2e6d752c1..8fc87d23de743a6d661112ee616dce129a785a36 100644
--- a/makeapi.in
+++ b/makeapi.in
@@ -38,6 +38,7 @@ from ipalib.parameters import Param
from ipalib.output import Output
from ipalib.text import Gettext, NGettext, ConcatenatedLazyText
from ipalib.capabilities import capabilities
+from ipapython import config
API_FILE='API.txt'
@@ -84,9 +85,7 @@ OUTPUT_IGNORED_ATTRIBUTES = (
)
def parse_options():
- from optparse import OptionParser # pylint: disable=deprecated-module
-
- parser = OptionParser()
+ parser = config.IPAOptionParser()
parser.add_option("--validate", dest="validate", action="store_true",
default=False, help="Validate the API vs the stored API")
--
2.47.1

View File

@ -1,422 +0,0 @@
From f9314562aaae03619eb89ae762bc24182174ad28 Mon Sep 17 00:00:00 2001
From: Alexander Bokovoy <abokovoy@redhat.com>
Date: Fri, 8 Nov 2024 14:59:20 +0200
Subject: [PATCH] ipa tools: remove sensitive material from the commandline
When command line tools accept passwords, remove them from the command
line so that they don't get visible in '/proc/pid/commandline'.
There is no common method to access the original ARGV vector and modify
it from Python. Since this mostly affects Linux systems where IPA
services run, we expect use of GNU libc and thus can rely on internal
glibc symbols. If they aren't available, the code will skip removing
passwords.
Fixes: CVE-2024-11029
Signed-off-by: Alexander Bokovoy <abokovoy@redhat.com>
---
.../com.redhat.idm.trust-fetch-domains.in | 5 ++-
install/tools/ipa-adtrust-install.in | 3 +-
install/tools/ipa-ca-install.in | 2 +
install/tools/ipa-compat-manage.in | 6 ++-
install/tools/ipa-csreplica-manage.in | 12 +++---
install/tools/ipa-managed-entries.in | 5 ++-
install/tools/ipa-replica-conncheck.in | 7 ++--
install/tools/ipa-replica-manage.in | 10 +++--
ipapython/admintool.py | 40 +++++++++++++++++++
ipaserver/install/ipa_migrate.py | 17 +++++++-
ipaserver/install/ipa_restore.py | 2 +-
ipaserver/install/ipa_server_certinstall.py | 2 +-
12 files changed, 90 insertions(+), 21 deletions(-)
diff --git a/install/oddjob/com.redhat.idm.trust-fetch-domains.in b/install/oddjob/com.redhat.idm.trust-fetch-domains.in
index 45c1f1463d5849d753c2913aa0b86b845cfce784..b86be0212c462e82d9379f55d5d3198c1017d2e7 100644
--- a/install/oddjob/com.redhat.idm.trust-fetch-domains.in
+++ b/install/oddjob/com.redhat.idm.trust-fetch-domains.in
@@ -15,6 +15,7 @@ import six
import gssapi
from ipalib.install.kinit import kinit_keytab, kinit_password
+from ipapython.admintool import admin_cleanup_global_argv
if six.PY3:
unicode = str
@@ -52,11 +53,13 @@ def parse_options():
"--password",
action="store",
dest="password",
- help="Display debugging information",
+ help="Password for Active Directory administrator",
+ sensitive=True
)
options, args = parser.parse_args()
safe_options = parser.get_safe_opts(options)
+ admin_cleanup_global_argv(parser, options, sys.argv)
# We only use first argument of the passed args but as D-BUS interface
# in oddjobd cannot expose optional, we fill in empty slots from IPA side
diff --git a/install/tools/ipa-adtrust-install.in b/install/tools/ipa-adtrust-install.in
index e7b0e369259da5d28d703558d9293ccfaf68f3ed..1efccdb678d17410667b1721670bf6bf79152109 100644
--- a/install/tools/ipa-adtrust-install.in
+++ b/install/tools/ipa-adtrust-install.in
@@ -35,7 +35,7 @@ from ipaserver.install.installutils import (
read_password,
check_server_configuration,
run_script)
-from ipapython.admintool import ScriptError
+from ipapython.admintool import ScriptError, admin_cleanup_global_argv
from ipapython import version
from ipapython import ipautil
from ipalib import api, errors, krb_utils
@@ -93,6 +93,7 @@ def parse_options():
options, _args = parser.parse_args()
safe_options = parser.get_safe_opts(options)
+ admin_cleanup_global_argv(parser, options, sys.argv)
return safe_options, options
diff --git a/install/tools/ipa-ca-install.in b/install/tools/ipa-ca-install.in
index 9f3d16669679a245b73e044622ff52321524fcde..b437e761f760ab715c27bc330ac0f2e66e72ef5b 100644
--- a/install/tools/ipa-ca-install.in
+++ b/install/tools/ipa-ca-install.in
@@ -42,6 +42,7 @@ from ipalib.constants import DOMAIN_LEVEL_1
from ipapython.config import IPAOptionParser
from ipapython.ipa_log_manager import standard_logging_setup
from ipaplatform.paths import paths
+from ipapython.admintool import admin_cleanup_global_argv
logger = logging.getLogger(os.path.basename(__file__))
@@ -132,6 +133,7 @@ def parse_options():
options, args = parser.parse_args()
safe_options = parser.get_safe_opts(options)
+ admin_cleanup_global_argv(parser, options, sys.argv)
if args:
parser.error("Too many arguments provided")
diff --git a/install/tools/ipa-compat-manage.in b/install/tools/ipa-compat-manage.in
index 459f39fc826bbe6becd8be3517235af343d4b0d9..9650abd6f83ebc0a8ef347fee83989d4e9f13f09 100644
--- a/install/tools/ipa-compat-manage.in
+++ b/install/tools/ipa-compat-manage.in
@@ -24,13 +24,13 @@ from __future__ import print_function
import sys
from ipaplatform.paths import paths
try:
- from optparse import OptionParser # pylint: disable=deprecated-module
from ipapython import ipautil, config
from ipaserver.install import installutils
from ipaserver.install.ldapupdate import LDAPUpdate
from ipalib import api, errors
from ipapython.ipa_log_manager import standard_logging_setup
from ipapython.dn import DN
+ from ipapython.admintool import admin_cleanup_global_argv
except ImportError as e:
print("""\
There was a problem importing one of the required Python modules. The
@@ -46,7 +46,8 @@ nis_config_dn = DN(('cn', 'NIS Server'), ('cn', 'plugins'), ('cn', 'config'))
def parse_options():
usage = "%prog [options] <enable|disable|status>\n"
usage += "%prog [options]\n"
- parser = OptionParser(usage=usage, formatter=config.IPAFormatter())
+ parser = config.IPAOptionParser(usage=usage,
+ formatter=config.IPAFormatter())
parser.add_option("-d", "--debug", action="store_true", dest="debug",
help="Display debugging information about the update(s)")
@@ -55,6 +56,7 @@ def parse_options():
config.add_standard_options(parser)
options, args = parser.parse_args()
+ admin_cleanup_global_argv(parser, options, sys.argv)
return options, args
diff --git a/install/tools/ipa-csreplica-manage.in b/install/tools/ipa-csreplica-manage.in
index 6f248cc50f7f81b2014d629355f6f32d1d6ab7be..2fab27a94cfdbef8faadca82bc222bf96d212159 100644
--- a/install/tools/ipa-csreplica-manage.in
+++ b/install/tools/ipa-csreplica-manage.in
@@ -32,8 +32,8 @@ from ipaserver.install import (replication, installutils, bindinstance,
from ipalib import api, errors
from ipalib.constants import FQDN
from ipalib.util import has_managed_topology, print_replication_status
-from ipapython import ipautil, ipaldap, version
-from ipapython.admintool import ScriptError
+from ipapython import ipautil, ipaldap, version, config
+from ipapython.admintool import admin_cleanup_global_argv, ScriptError
from ipapython.dn import DN
logger = logging.getLogger(os.path.basename(__file__))
@@ -54,11 +54,10 @@ commands = {
def parse_options():
- from optparse import OptionParser # pylint: disable=deprecated-module
-
- parser = OptionParser(version=version.VERSION)
+ parser = config.IPAOptionParser(version=version.VERSION)
parser.add_option("-H", "--host", dest="host", help="starting host")
- parser.add_option("-p", "--password", dest="dirman_passwd", help="Directory Manager password")
+ parser.add_option("-p", "--password", dest="dirman_passwd", sensitive=True,
+ help="Directory Manager password")
parser.add_option("-v", "--verbose", dest="verbose", action="store_true", default=False,
help="provide additional information")
parser.add_option("-f", "--force", dest="force", action="store_true", default=False,
@@ -66,6 +65,7 @@ def parse_options():
parser.add_option("--from", dest="fromhost", help="Host to get data from")
options, args = parser.parse_args()
+ admin_cleanup_global_argv(parser, options, sys.argv)
valid_syntax = False
diff --git a/install/tools/ipa-managed-entries.in b/install/tools/ipa-managed-entries.in
index e3f121943eb3b18ca8f7f8bfeae7813cbc9bd753..ff2fd6a58ccb5b322f75008578fea81f561666fa 100644
--- a/install/tools/ipa-managed-entries.in
+++ b/install/tools/ipa-managed-entries.in
@@ -24,7 +24,6 @@ import logging
import os
import re
import sys
-from optparse import OptionParser # pylint: disable=deprecated-module
from ipaplatform.paths import paths
from ipapython import config
@@ -32,6 +31,7 @@ from ipaserver.install import installutils
from ipalib import api, errors
from ipapython.ipa_log_manager import standard_logging_setup
from ipapython.dn import DN
+from ipapython.admintool import admin_cleanup_global_argv
logger = logging.getLogger(os.path.basename(__file__))
@@ -51,9 +51,10 @@ def parse_options():
action="store_true",
help="List available Managed Entries")
parser.add_option("-p", "--password", dest="dirman_password",
- help="Directory Manager password")
+ sensitive=True, help="Directory Manager password")
options, args = parser.parse_args()
+ admin_cleanup_global_argv(parser, options, sys.argv)
return options, args
diff --git a/install/tools/ipa-replica-conncheck.in b/install/tools/ipa-replica-conncheck.in
index 8eee82483abc275cb37ad96ff272b6ee8192403f..81b7d13ac900031fa81356b894dc3eaaaee0f744 100644
--- a/install/tools/ipa-replica-conncheck.in
+++ b/install/tools/ipa-replica-conncheck.in
@@ -23,15 +23,15 @@ from __future__ import print_function
import logging
from ipapython import ipachangeconf
-from ipapython.config import IPAOptionParser
+from ipapython.config import (IPAOptionParser, OptionGroup,
+ OptionValueError)
+from ipapython.admintool import admin_cleanup_global_argv
from ipapython.dn import DN
from ipapython import version
from ipapython import ipautil, certdb
from ipalib import api, errors, x509
from ipalib.constants import FQDN
from ipaserver.install import installutils
-# pylint: disable=deprecated-module
-from optparse import OptionGroup, OptionValueError
# pylint: enable=deprecated-module
from ipapython.ipa_log_manager import standard_logging_setup
import copy
@@ -189,6 +189,7 @@ def parse_options():
options, _args = parser.parse_args()
safe_options = parser.get_safe_opts(options)
+ admin_cleanup_global_argv(parser, options, sys.argv)
if options.master and options.replica:
parser.error("on-master and on-replica options are mutually exclusive!")
diff --git a/install/tools/ipa-replica-manage.in b/install/tools/ipa-replica-manage.in
index d6e6ef57c39af70f164d41662227af3dc2535f9c..7e5b31a598537f57c471729f22fdec4a5bedc03d 100644
--- a/install/tools/ipa-replica-manage.in
+++ b/install/tools/ipa-replica-manage.in
@@ -43,6 +43,7 @@ from ipalib.util import (
print_replication_status,
verify_host_resolvable,
)
+from ipapython.admintool import admin_cleanup_global_argv
from ipapython.ipa_log_manager import standard_logging_setup
from ipapython.dn import DN
from ipapython.config import IPAOptionParser
@@ -84,7 +85,8 @@ class NoRUVsFound(Exception):
def parse_options():
parser = IPAOptionParser(version=version.VERSION)
parser.add_option("-H", "--host", dest="host", help="starting host")
- parser.add_option("-p", "--password", dest="dirman_passwd", help="Directory Manager password")
+ parser.add_option("-p", "--password", dest="dirman_passwd", sensitive=True,
+ help="Directory Manager password")
parser.add_option("-v", "--verbose", dest="verbose", action="store_true", default=False,
help="provide additional information")
parser.add_option("-d", "--debug", dest="debug", action="store_true", default=False,
@@ -95,7 +97,7 @@ def parse_options():
help="DANGER: clean up references to a ghost master")
parser.add_option("--binddn", dest="binddn", default=None, type="dn",
help="Bind DN to use with remote server")
- parser.add_option("--bindpw", dest="bindpw", default=None,
+ parser.add_option("--bindpw", dest="bindpw", default=None, sensitive=True,
help="Password for Bind DN to use with remote server")
parser.add_option("--winsync", dest="winsync", action="store_true", default=False,
help="This is a Windows Sync Agreement")
@@ -103,13 +105,15 @@ def parse_options():
help="Full path and filename of CA certificate to use with TLS/SSL to the remote server")
parser.add_option("--win-subtree", dest="win_subtree", default=None,
help="DN of Windows subtree containing the users you want to sync (default cn=Users,<domain suffix)")
- parser.add_option("--passsync", dest="passsync", default=None,
+ parser.add_option("--passsync", dest="passsync",
+ default=None, sensitive=True,
help="Password for the IPA system user used by the Windows PassSync plugin to synchronize passwords")
parser.add_option("--from", dest="fromhost", help="Host to get data from")
parser.add_option("--no-lookup", dest="nolookup", action="store_true", default=False,
help="do not perform DNS lookup checks")
options, args = parser.parse_args()
+ admin_cleanup_global_argv(parser, options, sys.argv)
valid_syntax = False
diff --git a/ipapython/admintool.py b/ipapython/admintool.py
index dff9112eba4009d2fc1a6202756c6671ba4d909d..602223ef627c99f36a27d28fa712a6c196d7cb9f 100644
--- a/ipapython/admintool.py
+++ b/ipapython/admintool.py
@@ -39,6 +39,45 @@ SERVER_NOT_CONFIGURED = 2
logger = logging.getLogger(__name__)
+def admin_cleanup_global_argv(option_parser, options, argv):
+ """Takes option parser and generated options and scrubs sensitive arguments
+ from the global program arguments. Note that this only works for GNU GLIBC
+ as Python has no generic way to get access to the original argv values to
+ modify them in place.
+
+ The code assumes Python behavior, e.g. there are two additional args in the
+ list (/path/to/python -I ...) than what's passed as 'argv' here.
+ """
+ import ctypes
+ import ctypes.util
+ try:
+ _c = ctypes.CDLL(ctypes.util.find_library("c"))
+ if _c._name is None:
+ return
+ _argv = ctypes.POINTER(ctypes.c_voidp).in_dll(_c, "_dl_argv")
+ # since we run as 'python -I <executable> ...', add two args
+ _argc = len(argv) + 2
+ all_options = []
+ if '_get_all_options' in dir(option_parser):
+ # OptParse parser
+ all_options = option_parser._get_all_options()
+ elif '_actions' in dir(option_parser):
+ # ArgParse parser
+ all_options = option_parser._actions
+
+ for opt in all_options:
+ if getattr(opt, 'sensitive', False):
+ v = getattr(options, opt.dest)
+ for i in range(0, _argc):
+ vi = ctypes.cast(_argv[i],
+ ctypes.c_char_p
+ ).value.decode('utf-8')
+ if vi == v:
+ ctypes.memset(_argv[i], ord('X'), len(v))
+ except Exception:
+ pass
+
+
class ScriptError(Exception):
"""An exception that records an error message and a return value
"""
@@ -148,6 +187,7 @@ class AdminTool:
cls._option_parsers[cls] = cls.option_parser
options, args = cls.option_parser.parse_args(argv[1:])
+ admin_cleanup_global_argv(cls.option_parser, options, argv)
command_class = cls.get_command_class(options, args)
command = command_class(options, args)
diff --git a/ipaserver/install/ipa_migrate.py b/ipaserver/install/ipa_migrate.py
index f35629378490d3d45ca97f2aa5b4390c67d660ed..ece473bc8cb525e2d563356b5b274502d6b703e8 100644
--- a/ipaserver/install/ipa_migrate.py
+++ b/ipaserver/install/ipa_migrate.py
@@ -28,6 +28,7 @@ from ipaplatform.paths import paths
from ipapython.dn import DN
from ipapython.ipaldap import LDAPClient, LDAPEntry, realm_to_ldapi_uri
from ipapython.ipa_log_manager import standard_logging_setup
+from ipapython.admintool import admin_cleanup_global_argv
from ipaserver.install.ipa_migrate_constants import (
DS_CONFIG, DB_OBJECTS, DS_INDEXES, BIND_DN, LOG_FILE_NAME,
STRIP_OP_ATTRS, STRIP_ATTRS, STRIP_OC, PROD_ATTRS,
@@ -284,6 +285,18 @@ class LDIFParser(ldif.LDIFParser):
self.mc.process_db_entry(entry_dn=dn, entry_attrs=entry_attrs)
+class SensitiveStoreAction(argparse._StoreAction):
+ def __init__(self, *, sensitive, **options):
+ super(SensitiveStoreAction, self).__init__(**options)
+ self.sensitive = sensitive
+
+ def _get_kwargs(self):
+ names = super(SensitiveStoreAction, self)._get_kwargs()
+ sensitive_name = 'sensitive'
+ names.extend((sensitive_name, getattr(self, sensitive_name)))
+ return names
+
+
#
# Migrate IPA to IPA Class
#
@@ -344,7 +357,8 @@ class IPAMigrate():
help='Password for the Bind DN. If a password '
'is not provided then the user will be '
'prompted to enter it',
- default=None)
+ default=None, sensitive=True,
+ action=SensitiveStoreAction)
parser.add_argument('-j', '--bind-pw-file',
help='A text file containing the clear text '
'password for the Bind DN', default=None)
@@ -2128,6 +2142,7 @@ class IPAMigrate():
parser = argparse.ArgumentParser(description=desc, allow_abbrev=True)
self.add_options(parser)
self.validate_options()
+ admin_cleanup_global_argv(parser, self.args, sys.argv)
# Check for dryrun mode
if self.args.dryrun or self.args.dryrun_record is not None:
diff --git a/ipaserver/install/ipa_restore.py b/ipaserver/install/ipa_restore.py
index 8d75a0e6beba09086bc2ce8c73f3bcfe81e52113..539501ab4f0c73571b680aeccf3854b511fd29dc 100644
--- a/ipaserver/install/ipa_restore.py
+++ b/ipaserver/install/ipa_restore.py
@@ -185,7 +185,7 @@ class Restore(admintool.AdminTool):
super(Restore, cls).add_options(parser, debug_option=True)
parser.add_option(
- "-p", "--password", dest="password",
+ "-p", "--password", dest="password", sensitive=True,
help="Directory Manager password")
parser.add_option(
"--gpg-keyring", dest="gpg_keyring",
diff --git a/ipaserver/install/ipa_server_certinstall.py b/ipaserver/install/ipa_server_certinstall.py
index e9f680b1d1e505f89fc1611b8dbb3f84768f6781..76ad37ca7bcc62364379d56b21ead43c5248f5f1 100644
--- a/ipaserver/install/ipa_server_certinstall.py
+++ b/ipaserver/install/ipa_server_certinstall.py
@@ -72,7 +72,7 @@ class ServerCertInstall(admintool.AdminTool):
help="Name of the certificate to install")
parser.add_option(
"-p", "--dirman-password",
- dest="dirman_password",
+ dest="dirman_password", sensitive=True,
help="Directory Manager password")
def validate_options(self):
--
2.47.1

View File

@ -1,75 +0,0 @@
From d857fcfcc21481cdf06b8cce1685e141921d2fbf Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose@redhat.com>
Date: Wed, 27 Nov 2024 12:16:09 +0100
Subject: [PATCH] ipa-otpd: use oidc_child's --client-secret-stdin option
To remove the client secret from the command line where it would be
visible e.g. when calling ps it is now passed via stdin to oidc_child.
Fixes: CVE-2024-11029
Signed-off-by: Sumit Bose <sbose@redhat.com>
---
daemons/ipa-otpd/oauth2.c | 24 ++++++++++++++----------
1 file changed, 14 insertions(+), 10 deletions(-)
diff --git a/daemons/ipa-otpd/oauth2.c b/daemons/ipa-otpd/oauth2.c
index a33cf51715ca2a3e7a0cef871aed5cfbbd037598..52d7d7c9cb6c410bdbaa2e5eddccfea2204d3e69 100644
--- a/daemons/ipa-otpd/oauth2.c
+++ b/daemons/ipa-otpd/oauth2.c
@@ -31,6 +31,7 @@
#include <unistd.h>
#include <fcntl.h>
#include <sys/random.h>
+#include <sys/uio.h>
#include "internal.h"
@@ -93,6 +94,7 @@ static void oauth2_on_child_writable(verto_ctx *vctx, verto_ev *ev)
(void)vctx; /* Unused */
ssize_t io;
struct child_ctx *child_ctx;
+ struct iovec iov[3];
child_ctx = verto_get_private(ev);
if (child_ctx == NULL) {
@@ -102,15 +104,18 @@ static void oauth2_on_child_writable(verto_ctx *vctx, verto_ev *ev)
}
if (child_ctx->oauth2_state == OAUTH2_GET_DEVICE_CODE) {
- /* no input needed */
- verto_del(ev);
- return;
- }
-
+ io = write(verto_get_fd(ev), child_ctx->item->idp.ipaidpClientSecret,
+ strlen(child_ctx->item->idp.ipaidpClientSecret));
+ } else {
+ iov[0].iov_base = child_ctx->item->idp.ipaidpClientSecret;
+ iov[0].iov_len = strlen(child_ctx->item->idp.ipaidpClientSecret);
+ iov[1].iov_base = "\n";
+ iov[1].iov_len = 1;
+ iov[2].iov_base = child_ctx->saved_item->oauth2.device_code_reply;
+ iov[2].iov_len = strlen(child_ctx->saved_item->oauth2.device_code_reply);
- io = write(verto_get_fd(ev),
- child_ctx->saved_item->oauth2.device_code_reply,
- strlen(child_ctx->saved_item->oauth2.device_code_reply));
+ io = writev(verto_get_fd(ev), iov, 3);
+ }
otpd_queue_item_free(child_ctx->saved_item);
if (io < 0) {
@@ -429,8 +434,7 @@ int oauth2(struct otpd_queue_item **item, enum oauth2_state oauth2_state)
args[args_idx++] = (*item)->idp.ipaidpClientID;
if ((*item)->idp.ipaidpClientSecret) {
- args[args_idx++] = "--client-secret";
- args[args_idx++] = (*item)->idp.ipaidpClientSecret;
+ args[args_idx++] = "--client-secret-stdin";
}
if ((*item)->idp.ipaidpScope) {
--
2.47.1

View File

@ -1,69 +0,0 @@
From eef544c1d331bbe80852ebe8b5fc9bad0539b6fa Mon Sep 17 00:00:00 2001
From: Florence Blanc-Renaud <flo@redhat.com>
Date: Wed, 15 Jan 2025 15:39:20 +0100
Subject: [PATCH] Fix pylint issue in ipatests/i18n.py
This file should not import ipa modules
Signed-off-by: Florence Blanc-Renaud <flo@redhat.com>
Reviewed-By: Rafael Guterres Jeffman <rjeffman@redhat.com>
---
ipatests/i18n.py | 8 ++++----
pylintrc | 3 ++-
2 files changed, 6 insertions(+), 5 deletions(-)
diff --git a/ipatests/i18n.py b/ipatests/i18n.py
index 57915c286be72124fa23380f97f3922496f00c22..49f5c4c3232346db3147bd7a5ba8056344ac907f 100644
--- a/ipatests/i18n.py
+++ b/ipatests/i18n.py
@@ -22,6 +22,7 @@ from __future__ import print_function
# WARNING: Do not import ipa modules, this is also used as a
# stand-alone script (invoked from po Makefile).
+import optparse # pylint: disable=deprecated-module
import sys
import gettext
import re
@@ -29,7 +30,6 @@ import os
import traceback
import polib
from collections import namedtuple
-from ipapython import config
import six
@@ -722,9 +722,9 @@ usage ='''
def main():
global verbose, print_traceback, pedantic, show_strings
- parser = config.IPAOptionParser(usage=usage)
+ parser = optparse.OptionParser(usage=usage)
- mode_group = config.OptionGroup(parser, 'Operational Mode',
+ mode_group = optparse.OptionGroup(parser, 'Operational Mode',
'You must select one these modes to run in')
mode_group.add_option('-g', '--test-gettext', action='store_const', const='test_gettext', dest='mode',
@@ -748,7 +748,7 @@ def main():
parser.add_option('--traceback', action='store_true', dest='print_traceback', default=False,
help='print the traceback when an exception occurs')
- param_group = config.OptionGroup(parser, 'Run Time Parameters',
+ param_group = optparse.OptionGroup(parser, 'Run Time Parameters',
'These may be used to modify the run time defaults')
param_group.add_option('--test-lang', action='store', dest='test_lang', default='test',
diff --git a/pylintrc b/pylintrc
index 50278cc76031af68959a138f6ea185c4288c7155..8fadeffbdfdb8013135a17b3493ecc4dc7e5e001 100644
--- a/pylintrc
+++ b/pylintrc
@@ -153,4 +153,5 @@ forbidden-imports=
ipaplatform/:ipaclient:ipalib:ipaserver,
ipapython/:ipaclient:ipalib:ipaserver
ipatests/pytest_ipa:ipaserver:ipaclient.install:ipalib.install
- ipatests/test_integration:ipaserver
+ ipatests/test_integration:ipaserver,
+ ipatests/i18n.py:ipapython
--
2.47.1

View File

@ -1,51 +0,0 @@
From 45f96a0f978dfda0e2faa8360182a1dfd3122b94 Mon Sep 17 00:00:00 2001
From: Florence Blanc-Renaud <flo@redhat.com>
Date: Fri, 10 Jan 2025 13:22:29 +0100
Subject: [PATCH] ipatests: skip test_ipahealthcheck_ds_configcheck for recent
versions
389-ds removed the parameter nsslapd-logging-hr-timestamps-enabled
in 2.5.3 and above. Skip the test that exercises the corresponding
healthcheck.
Fixes: https://pagure.io/freeipa/issue/9730
Signed-off-by: Florence Blanc-Renaud <flo@redhat.com>
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
---
ipatests/test_integration/test_ipahealthcheck.py | 12 +++++++++---
1 file changed, 9 insertions(+), 3 deletions(-)
diff --git a/ipatests/test_integration/test_ipahealthcheck.py b/ipatests/test_integration/test_ipahealthcheck.py
index cc51a5a6a62fbc50927fc2fc51f129a069e70b69..6b6f15aa433a423fe599118d2226e4c4ec62b13b 100644
--- a/ipatests/test_integration/test_ipahealthcheck.py
+++ b/ipatests/test_integration/test_ipahealthcheck.py
@@ -18,7 +18,7 @@ import uuid
import pytest
-from ipalib import x509
+from ipalib import errors, x509
from ipapython.dn import DN
from ipapython.ipaldap import realm_to_serverid
from ipapython.certdb import NSS_SQL_FILES
@@ -1146,8 +1146,14 @@ class TestIpaHealthCheck(IntegrationTest):
)
entry = ldap.get_entry(dn)
entry.single_value["nsslapd-logging-hr-timestamps-enabled"] = 'off'
- ldap.update_entry(entry)
-
+ try:
+ ldap.update_entry(entry)
+ except errors.DatabaseError as e:
+ expected_msg = "Unknown attribute " \
+ "nsslapd-logging-hr-timestamps-enabled"
+ if expected_msg in e.message:
+ pytest.skip(
+ "389-ds removed nsslapd-logging-hr-timestamps-enabled")
yield
entry = ldap.get_entry(dn)
--
2.47.1

View File

@ -1,35 +0,0 @@
From ec94ee72714296c86ba1227a5a945a7ed0bc7fac Mon Sep 17 00:00:00 2001
From: Florence Blanc-Renaud <flo@redhat.com>
Date: Thu, 16 Jan 2025 15:43:17 +0100
Subject: [PATCH] ipatests: restart dirsrv after time jumps
The test for ipa-healthcheck is moving the date in the future.
Restart the dirsrv instance because the LDAP server is
sensitive to large time jumps.
Signed-off-by: Florence Blanc-Renaud <flo@redhat.com>
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
---
ipatests/test_integration/test_ipahealthcheck.py | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/ipatests/test_integration/test_ipahealthcheck.py b/ipatests/test_integration/test_ipahealthcheck.py
index 6b6f15aa433a423fe599118d2226e4c4ec62b13b..7c3f5857a477070d8a9b52c04d41f35ac580c97f 100644
--- a/ipatests/test_integration/test_ipahealthcheck.py
+++ b/ipatests/test_integration/test_ipahealthcheck.py
@@ -1634,6 +1634,11 @@ class TestIpaHealthCheck(IntegrationTest):
grace_date = datetime.strftime(grace_date, "%Y-%m-%d 00:00:01 Z")
self.master.run_command(['date', '-s', grace_date])
+ # Restart dirsrv as it doesn't like time jumps
+ instance = realm_to_serverid(self.master.domain.realm)
+ cmd = ["systemctl", "restart", "dirsrv@{}".format(instance)]
+ self.master.run_command(cmd)
+
for check in ("IPACertmongerExpirationCheck",
"IPACertfileExpirationCheck",):
execute_expiring_check(check)
--
2.47.1

View File

@ -1,67 +0,0 @@
From f12c4ed600e9b35c930d386b37e36064fbf83968 Mon Sep 17 00:00:00 2001
From: Alexander Bokovoy <abokovoy@redhat.com>
Date: Fri, 17 Jan 2025 09:44:22 +0200
Subject: [PATCH] ipa-otpd: do not pass OIDC client secret if there is none to
pass
If there is no client secret specified for the OIDC client, don't push
it to oidc_child via stdin. oidc_child does only expect client secret if
--client-secret-stdin option was specified and we already specify it
only if client secret is not empty.
In addition, if client secret is empty (it is a public OIDC client),
then strlen(NULL) would crash in glibc internals. Avoid that!
Fixes: https://pagure.io/freeipa/issue/9734
Signed-off-by: Alexander Bokovoy <abokovoy@redhat.com>
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
---
daemons/ipa-otpd/oauth2.c | 29 +++++++++++++++++++----------
1 file changed, 19 insertions(+), 10 deletions(-)
diff --git a/daemons/ipa-otpd/oauth2.c b/daemons/ipa-otpd/oauth2.c
index 52d7d7c9cb6c410bdbaa2e5eddccfea2204d3e69..0eb43b2372701d47b9ef62cbbdb32b97a5f7a0ba 100644
--- a/daemons/ipa-otpd/oauth2.c
+++ b/daemons/ipa-otpd/oauth2.c
@@ -104,17 +104,26 @@ static void oauth2_on_child_writable(verto_ctx *vctx, verto_ev *ev)
}
if (child_ctx->oauth2_state == OAUTH2_GET_DEVICE_CODE) {
- io = write(verto_get_fd(ev), child_ctx->item->idp.ipaidpClientSecret,
- strlen(child_ctx->item->idp.ipaidpClientSecret));
+ if (child_ctx->item->idp.ipaidpClientSecret != NULL) {
+ io = write(verto_get_fd(ev), child_ctx->item->idp.ipaidpClientSecret,
+ strlen(child_ctx->item->idp.ipaidpClientSecret));
+ } else {
+ io = 0;
+ }
} else {
- iov[0].iov_base = child_ctx->item->idp.ipaidpClientSecret;
- iov[0].iov_len = strlen(child_ctx->item->idp.ipaidpClientSecret);
- iov[1].iov_base = "\n";
- iov[1].iov_len = 1;
- iov[2].iov_base = child_ctx->saved_item->oauth2.device_code_reply;
- iov[2].iov_len = strlen(child_ctx->saved_item->oauth2.device_code_reply);
-
- io = writev(verto_get_fd(ev), iov, 3);
+ int idx = 0;
+ if (child_ctx->item->idp.ipaidpClientSecret != NULL) {
+ iov[idx].iov_base = child_ctx->item->idp.ipaidpClientSecret;
+ iov[idx].iov_len = strlen(child_ctx->item->idp.ipaidpClientSecret);
+ idx++;
+ iov[idx].iov_base = "\n";
+ iov[idx].iov_len = 1;
+ idx++;
+ }
+ iov[idx].iov_base = child_ctx->saved_item->oauth2.device_code_reply;
+ iov[idx].iov_len = strlen(child_ctx->saved_item->oauth2.device_code_reply);
+ idx++;
+ io = writev(verto_get_fd(ev), iov, idx);
}
otpd_queue_item_free(child_ctx->saved_item);
--
2.47.1

View File

@ -1,165 +0,0 @@
From 4e43dd7cd30042588a2264fca98b6e6b9d4d25bb Mon Sep 17 00:00:00 2001
From: Alexander Bokovoy <abokovoy@redhat.com>
Date: Fri, 17 Jan 2025 12:33:54 +0200
Subject: [PATCH] Migrate Keycloak tests to JDK 21 and Keycloak 26
Signed-off-by: Alexander Bokovoy <abokovoy@redhat.com>
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
---
.../pytest_ipa/integration/create_bridge.py | 2 +-
.../pytest_ipa/integration/create_keycloak.py | 28 +++++++++----------
ipatests/test_integration/test_idp.py | 4 +--
ipatests/test_integration/test_sso.py | 4 +--
4 files changed, 18 insertions(+), 20 deletions(-)
diff --git a/ipatests/pytest_ipa/integration/create_bridge.py b/ipatests/pytest_ipa/integration/create_bridge.py
index 618c645feef86f846a60e5727e7777defc67624c..5dd2f305c2ba6f707ee40be12581ff62c951805b 100644
--- a/ipatests/pytest_ipa/integration/create_bridge.py
+++ b/ipatests/pytest_ipa/integration/create_bridge.py
@@ -147,7 +147,7 @@ def setup_keycloak_scim_plugin(host, bridge_server):
# Login to keycloak as admin
kcadmin_sh = "/opt/keycloak/bin/kcadm.sh"
kcadmin = [kcadmin_sh, "config", "credentials", "--server",
- f"https://{host.hostname}:8443/auth/",
+ f"https://{host.hostname}:8443",
"--realm", "master", "--user", "admin",
"--password", password]
tasks.run_repeatedly(host, kcadmin, timeout=60)
diff --git a/ipatests/pytest_ipa/integration/create_keycloak.py b/ipatests/pytest_ipa/integration/create_keycloak.py
index 1340b95715c25f1bf1cbbf2e3c6e60731f3af08e..addade7594d7a1b8edefdb8c67ec4bc7abe70ef4 100644
--- a/ipatests/pytest_ipa/integration/create_keycloak.py
+++ b/ipatests/pytest_ipa/integration/create_keycloak.py
@@ -6,10 +6,10 @@ from ipaplatform.paths import paths
from ipatests.pytest_ipa.integration import tasks
-def setup_keycloakserver(host, version='17.0.0'):
+def setup_keycloakserver(host, version='26.1.0'):
dir = "/opt/keycloak"
password = host.config.admin_password
- tasks.install_packages(host, ["unzip", "java-11-openjdk-headless",
+ tasks.install_packages(host, ["unzip", "java-21-openjdk-headless",
"openssl", "maven", "wget",
"firefox", "xorg-x11-server-Xvfb"])
# add keycloak system user/group and folder
@@ -33,7 +33,7 @@ def setup_keycloakserver(host, version='17.0.0'):
key = os.path.join(paths.OPENSSL_PRIVATE_DIR, "keycloak.key")
crt = os.path.join(paths.OPENSSL_PRIVATE_DIR, "keycloak.crt")
- keystore = os.path.join(paths.OPENSSL_PRIVATE_DIR, "keycloak.store")
+ keystore = os.path.join(paths.OPENSSL_PRIVATE_DIR, "keycloak.jks")
host.run_command(["ipa-getcert", "request", "-K",
"HTTP/{0}".format(host.hostname),
@@ -49,14 +49,13 @@ def setup_keycloakserver(host, version='17.0.0'):
# Setup keycloak service and config files
contents = textwrap.dedent("""
- KEYCLOAK_ADMIN=admin
- KEYCLOAK_ADMIN_PASSWORD={admin_pswd}
- KC_HOSTNAME={host}:8443
+ KC_BOOTSTRAP_ADMIN_USERNAME=admin
+ KC_BOOTSTRAP_ADMIN_PASSWORD={admin_pswd}
+ KC_HOSTNAME=https://{host}:8443/
KC_HTTPS_CERTIFICATE_FILE={crt}
KC_HTTPS_CERTIFICATE_KEY_FILE={key}
KC_HTTPS_TRUST_STORE_FILE={store}
KC_HTTPS_TRUST_STORE_PASSWORD={store_pswd}
- KC_HTTP_RELATIVE_PATH=/auth
""").format(admin_pswd=password, host=host.hostname, crt=crt, key=key,
store=keystore, store_pswd=password)
host.put_file_contents("/etc/sysconfig/keycloak", contents)
@@ -84,14 +83,13 @@ def setup_keycloakserver(host, version='17.0.0'):
# Run build stage first
env_vars = textwrap.dedent("""
- export KEYCLOAK_ADMIN=admin
- export KC_HOSTNAME={hostname}:8443
+ export KC_BOOTSTRAP_ADMIN_USERNAME=admin
+ export KC_HOSTNAME=https://{hostname}:8443/
export KC_HTTPS_CERTIFICATE_FILE=/etc/pki/tls/certs/keycloak.crt
export KC_HTTPS_CERTIFICATE_KEY_FILE=/etc/pki/tls/private/keycloak.key
- export KC_HTTPS_TRUST_STORE_FILE=/etc/pki/tls/private/keycloak.store
+ export KC_HTTPS_TRUST_STORE_FILE=/etc/pki/tls/private/keycloak.jks
export KC_HTTPS_TRUST_STORE_PASSWORD={STORE_PASS}
- export KEYCLOAK_ADMIN_PASSWORD={ADMIN_PASS}
- export KC_HTTP_RELATIVE_PATH=/auth
+ export KC_BOOTSTRAP_ADMIN_PASSWORD={ADMIN_PASS}
""").format(hostname=host.hostname, STORE_PASS=password,
ADMIN_PASS=password)
@@ -112,7 +110,7 @@ def setup_keycloakserver(host, version='17.0.0'):
host.run_command([kcadmin_sh, "config", "truststore",
"--trustpass", password, keystore])
kcadmin = [kcadmin_sh, "config", "credentials", "--server",
- "https://{0}:8443/auth/".format(host.hostname),
+ "https://{0}:8443/".format(host.hostname),
"--realm", "master", "--user", "admin",
"--password", password
]
@@ -133,7 +131,7 @@ def setup_keycloak_client(host):
password = host.config.admin_password
host.run_command(["/opt/keycloak/bin/kcreg.sh",
"config", "credentials", "--server",
- "https://{0}:8443/auth/".format(host.hostname),
+ "https://{0}:8443/".format(host.hostname),
"--realm", "master", "--user", "admin",
"--password", password]
)
@@ -163,7 +161,7 @@ def setup_keycloak_client(host):
def uninstall_keycloak(host):
key = os.path.join(paths.OPENSSL_PRIVATE_DIR, "keycloak.key")
crt = os.path.join(paths.OPENSSL_PRIVATE_DIR, "keycloak.crt")
- keystore = os.path.join(paths.OPENSSL_PRIVATE_DIR, "keycloak.store")
+ keystore = os.path.join(paths.OPENSSL_PRIVATE_DIR, "keycloak.jks")
host.run_command(["systemctl", "stop", "keycloak"], raiseonerr=False)
host.run_command(["getcert", "stop-tracking", "-k", key, "-f", crt],
diff --git a/ipatests/test_integration/test_idp.py b/ipatests/test_integration/test_idp.py
index ca2fcecb22459685450f2ed6c3ac1b9b215170f6..76edc9458e4448e05362ff040b8dab7a53cd3054 100644
--- a/ipatests/test_integration/test_idp.py
+++ b/ipatests/test_integration/test_idp.py
@@ -122,7 +122,7 @@ class TestIDPKeycloak(IntegrationTest):
tasks.kinit_admin(self.master)
cmd = ["ipa", "idp-add", "keycloakidp", "--provider=keycloak",
"--client-id=ipa_oidc_client", "--org=master",
- "--base-url={0}:8443/auth".format(self.client.hostname)]
+ "--base-url={0}:8443".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, 'keycloakuser',
@@ -282,7 +282,7 @@ class TestIDPKeycloak(IntegrationTest):
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)]
+ "--base-url={0}:8443".format(self.client.hostname)]
self.master.run_command(cmd, stdin_text="{0}\n{0}".format(
self.client.config.admin_password))
diff --git a/ipatests/test_integration/test_sso.py b/ipatests/test_integration/test_sso.py
index 9708e9fa05a75cb2657c657b39b015249f3fd208..57c5a96bae986ee9721fc540d2be2cdc443e78fb 100644
--- a/ipatests/test_integration/test_sso.py
+++ b/ipatests/test_integration/test_sso.py
@@ -18,7 +18,7 @@ from selenium.webdriver.support import expected_conditions as EC
options = Options()
options.headless = True
driver = webdriver.Firefox(executable_path="/opt/geckodriver", options=options)
-verification_uri = "https://{hostname}:8443/auth/realms/master/account/#/"
+verification_uri = "https://{hostname}:8443/realms/master/account/#/"
driver.get(verification_uri)
try:
@@ -60,7 +60,7 @@ def keycloak_add_user(host, kcadm_pass, username, password=None):
domain = host.domain.name
kcadmin_sh = "/opt/keycloak/bin/kcadm.sh"
kcadmin = [kcadmin_sh, "config", "credentials", "--server",
- f"https://{host.hostname}:8443/auth/",
+ f"https://{host.hostname}:8443",
"--realm", "master", "--user", "admin",
"--password", kcadm_pass]
--
2.47.1

View File

@ -1,162 +0,0 @@
From 9f30edef463237ba48efe45406626eb325bf6c39 Mon Sep 17 00:00:00 2001
From: Rob Crittenden <rcritten@redhat.com>
Date: Wed, 22 Jan 2025 13:19:43 -0500
Subject: [PATCH] Apply certmonger_timeout to start_tracking and request_cert
We've seen that with some slow HSMs the default DBus timeout
the HSM doesn't respond quickly enough to certmonger start
tracking requests which fails the entire installation.
A first attempt was made to bump up the default to 30 seconds
which turned out to not be long enough.
There is already a certmonger timeout defined in the API but it
is 300 seconds so I was hesitant to use it. It could delay the
actual failure of a blown install by 5 minutes. But it also gives
the end user the flexibility to be able to control success over
an installation so we'll go ahead and use it.
Fixes: https://pagure.io/freeipa/issue/9725
Signed-off-by: Rob Crittenden <rcritten@redhat.com>
Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
Reviewed-By: Mohammad Rizwan Yusuf <myusuf@redhat.com>
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
---
client/man/default.conf.5 | 10 ++++++++--
ipalib/install/certmonger.py | 7 +++++--
ipaserver/install/cainstance.py | 5 ++++-
ipaserver/install/dogtaginstance.py | 18 ++++++++++++++++++
ipaserver/install/service.py | 4 +++-
5 files changed, 38 insertions(+), 6 deletions(-)
diff --git a/client/man/default.conf.5 b/client/man/default.conf.5
index 3846de50c5d851471ea3ceed9fc38cb687c719e4..e0aec21f725d88ce2ba3cf52901fb15575892cde 100644
--- a/client/man/default.conf.5
+++ b/client/man/default.conf.5
@@ -20,7 +20,7 @@
.SH "NAME"
default.conf \- IPA configuration file
.SH "SYNOPSIS"
-/etc/ipa/default.conf, ~/.ipa/default.conf, /etc/ipa/server.conf, /etc/ipa/cli.conf
+/etc/ipa/default.conf, ~/.ipa/default.conf, /etc/ipa/server.conf, /etc/ipa/cli.conf, /etc/ipa/installer.conf, /etc/ipa/cli_installer.conf
.SH "DESCRIPTION"
The \fIdefault.conf \fRconfiguration file is used to set system\-wide defaults to be applied when running IPA clients and servers.
@@ -75,7 +75,7 @@ Specifies the hostname of the dogtag CA server. The default is the hostname of t
Specifies the insecure CA end user port. The default is 8080.
.TP
.B certmonger_wait_timeout <seconds>
-The time to wait for a certmonger request to complete during installation. The default value is 300 seconds.
+The time to wait for a certmonger request to complete during installation. The default value is 300 seconds. To tune create/add to /etc/ipa/installer.conf.
.TP
.B context <context>
Specifies the context that IPA is being executed in. IPA may operate differently depending on the context. The current defined contexts are cli, server and dns. Additionally this value is used to load /etc/ipa/\fBcontext\fR.conf to provide context\-specific configuration. For example, if you want to always perform client requests in verbose mode but do not want to have verbose enabled on the server, add the verbose option to \fI/etc/ipa/cli.conf\fR.
@@ -263,6 +263,12 @@ system\-wide IPA client configuration file
.TP
.I /etc/ipa/server.conf
system\-wide IPA server configuration file
+.TP
+.I /etc/ipa/installer.conf
+IPA configuration used while installing an IPA server or replica
+.TP
+.I /etc/ipa/cli_installer.conf
+IPA configuration used while installing an IPA client
.SH "EXAMPLES"
.TP
An example of a context-specific configuration file is \fB/etc/ipa/dns.conf\fR to be used to increase debug output of the IPA DNSSEC daemons.
diff --git a/ipalib/install/certmonger.py b/ipalib/install/certmonger.py
index efc1ba4f42eab98df5fac51bafa3acc83ae91831..675b2c96ce8ebe4f06822ad587a4bca734a1be09 100644
--- a/ipalib/install/certmonger.py
+++ b/ipalib/install/certmonger.py
@@ -477,7 +477,8 @@ def request_cert(
request_parameters['cert-perms'] = perms[0]
request_parameters['key-perms'] = perms[1]
- result = cm.obj_if.add_request(request_parameters, timeout=30)
+ result = cm.obj_if.add_request(request_parameters,
+ timeout=api.env.certmonger_wait_timeout)
try:
if result[0]:
request = _cm_dbus_object(cm.bus, cm, result[1], DBUS_CM_REQUEST_IF,
@@ -581,7 +582,9 @@ def start_tracking(
if nss_user:
params['nss-user'] = nss_user
- result = cm.obj_if.add_request(params, timeout=30)
+ logger.debug("start tracking %s", params)
+ result = cm.obj_if.add_request(params,
+ timeout=api.env.certmonger_wait_timeout)
try:
if result[0]:
request = _cm_dbus_object(cm.bus, cm, result[1], DBUS_CM_REQUEST_IF,
diff --git a/ipaserver/install/cainstance.py b/ipaserver/install/cainstance.py
index e03a8c863e14782679e19c6887f5e220131e4234..76718036dbd317651edc98ce631405e42bf814d7 100644
--- a/ipaserver/install/cainstance.py
+++ b/ipaserver/install/cainstance.py
@@ -513,7 +513,10 @@ class CAInstance(DogtagInstance):
if ra_only:
runtime = None
else:
- runtime = 180
+ if self.tokenname:
+ runtime = "HSM dependent"
+ else:
+ runtime = 180
try:
self.start_creation(runtime=runtime)
diff --git a/ipaserver/install/dogtaginstance.py b/ipaserver/install/dogtaginstance.py
index 32a52dbedaa34b24c2658460f0ae889e7a37aa64..002053ed797beec829d324e80fc55b57cabf04be 100644
--- a/ipaserver/install/dogtaginstance.py
+++ b/ipaserver/install/dogtaginstance.py
@@ -575,6 +575,24 @@ class DogtagInstance(service.Service):
except RuntimeError as e:
logger.error(
"certmonger failed to start tracking certificate: %s", e)
+ except dbus.exceptions.DBusException as e:
+ if e._dbus_error_name == "org.freedesktop.DBus.Error.NoReply":
+ logger.error(
+ "Timeout encountered starting tracking of '%s'."
+ "This timeout can be tuned using "
+ "certmonger_wait_timeout in /etc/ipa/installer.conf.",
+ nickname
+ )
+ if self.hsm_enabled:
+ logger.error(
+ "On an initial install failure this may leave "
+ "certificates and keys on the HSM token. These "
+ "need to be manually cleaned per your HSM-specific "
+ "documentation before installing IPA again. On a "
+ "replica install no clean-up should be done (it will "
+ "destroy your installation."
+ )
+ raise
def stop_tracking_certificates(self):
"""
diff --git a/ipaserver/install/service.py b/ipaserver/install/service.py
index cf0f64ab9794111761adf735bc488269bd1814fc..7755a4f2ff5e33e61f85dc24b71fd05a1837cd5a 100644
--- a/ipaserver/install/service.py
+++ b/ipaserver/install/service.py
@@ -59,6 +59,8 @@ def print_msg(message, output_fd=sys.stdout):
def format_seconds(seconds):
"""Format a number of seconds as an English minutes+seconds message"""
+ if type(seconds) is not int:
+ return seconds
parts = []
minutes, seconds = divmod(seconds, 60)
if minutes:
@@ -660,7 +662,7 @@ class Service:
else:
end_message = "Done configuring %s." % self.service_desc
- if runtime is not None and runtime > 0:
+ if runtime is not None or (type(runtime) is int and runtime > 0):
self.print_msg('%s. Estimated time: %s' % (start_message,
format_seconds(runtime)))
else:
--
2.48.1

File diff suppressed because it is too large Load Diff

View File

@ -1,55 +0,0 @@
From 47ce0982249ee7ce12b38eae5ce3ee6a9b5df52e Mon Sep 17 00:00:00 2001
From: Rob Crittenden <rcritten@redhat.com>
Date: Tue, 4 Feb 2025 12:54:48 -0500
Subject: [PATCH] Configure the pki-tomcatd service systemd timeout
IPA defines a startup timeout that is primarily used
during installation to extend service start-up timeouts
on slower systems.
This tends to work ok when runing pki-spawn but can fail when
systemd is starting the tomcat service.
Use the value of startup_timeout to set TimeoutStartSec in
the pki-tomcat systemd override file ipa.conf. This will
preserve the necessary startup_timeout for all future restarts.
This was seen with a very slow HSM where installation was successful
(pki-spawn) but pki-tomcatd startup timed out at the end of the
installation.
To increase the value in installation one needs to create the file
/etc/ipa/installer.conf with contents:
[global]
startup_timeout = 300 (or whatever)
Fixes: https://pagure.io/freeipa/issue/9743
Signed-off-by: Rob Crittenden <rcritten@redhat.com>
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
---
ipaserver/install/cainstance.py | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/ipaserver/install/cainstance.py b/ipaserver/install/cainstance.py
index 76718036dbd317651edc98ce631405e42bf814d7..c8ecde8f2e9649d57012fcda937ee5816105df4e 100644
--- a/ipaserver/install/cainstance.py
+++ b/ipaserver/install/cainstance.py
@@ -713,7 +713,12 @@ class CAInstance(DogtagInstance):
f.write('[Service]\n')
f.write('Environment=LC_ALL=C.UTF-8\n')
f.write('ExecStartPost={}\n'.format(paths.IPA_PKI_WAIT_RUNNING))
+ f.write('TimeoutStartSec=%d\n' % api.env.startup_timeout)
tasks.systemd_daemon_reload()
+ logger.info(
+ "Set start up timeout of pki-tomcatd service to %d seconds",
+ api.env.startup_timeout
+ )
def safe_backup_config(self):
"""
--
2.48.1

View File

@ -1,84 +0,0 @@
From 22cbc5ed4889d6c66e2916d5acde582b1868fbc9 Mon Sep 17 00:00:00 2001
From: Rob Crittenden <rcritten@redhat.com>
Date: Mon, 10 Feb 2025 10:45:39 -0500
Subject: [PATCH] Align startup_timeout with the systemd default and document
it
We had it set to 120 seconds while the systemd default is 90.
They should be the same because the first one that times out "wins".
Move where during the installation we create the systemd override
file so that the timeout will be applied across all subsequent
server starts during and post installation.
Fixes: https://pagure.io/freeipa/issue/9743
Signed-off-by: Rob Crittenden <rcritten@redhat.com>
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
---
client/man/default.conf.5 | 2 +-
ipalib/constants.py | 5 +++--
ipaserver/install/cainstance.py | 3 ++-
3 files changed, 6 insertions(+), 4 deletions(-)
diff --git a/client/man/default.conf.5 b/client/man/default.conf.5
index e0aec21f725d88ce2ba3cf52901fb15575892cde..461c60134124ed3e31e17ac350576487fda4c46e 100644
--- a/client/man/default.conf.5
+++ b/client/man/default.conf.5
@@ -191,7 +191,7 @@ Specifies the IPA Server hostname.
Skip client vs. server API version checking. Can lead to errors/strange behavior when newer clients talk to older servers. Use with caution.
.TP
.B startup_timeout <time in seconds>
-Controls the amount of time waited when starting a service. The default value is 120 seconds.
+Controls the amount of time waited when starting a service. The default value is 90 seconds, the same as the default systemd startup timeout. If configuring a CA the startup_timeout value will be added as an override for TimeoutStartSec in systemd. If installation times out when starting the CA create /etc/ipa/installer.conf with this value set.
.TP
.B startup_traceback <boolean>
If the IPA server fails to start and this value is True the server will attempt to generate a python traceback to make identifying the underlying problem easier.
diff --git a/ipalib/constants.py b/ipalib/constants.py
index c90caa22149ec3d93d45fcb5480f7401e4555799..2e4c9a8336efae9e02febd6d04ec226c84af255f 100644
--- a/ipalib/constants.py
+++ b/ipalib/constants.py
@@ -189,8 +189,9 @@ DEFAULT_CONFIG = (
# Time to wait for a service to start, in seconds.
# Note that systemd has a DefaultTimeoutStartSec of 90 seconds. Higher
- # values are not effective unless systemd is reconfigured, too.
- ('startup_timeout', 120),
+ # values are not effective unless systemd is reconfigured, too. Or you
+ # can update the systemd service file with its own TimeoutStartSec.
+ ('startup_timeout', 90),
# How long http connection should wait for reply [seconds].
('http_timeout', 30),
# How long to wait for an entry to appear on a replica
diff --git a/ipaserver/install/cainstance.py b/ipaserver/install/cainstance.py
index c8ecde8f2e9649d57012fcda937ee5816105df4e..3466c308829a576589874015542da9ea88bc2a2f 100644
--- a/ipaserver/install/cainstance.py
+++ b/ipaserver/install/cainstance.py
@@ -453,6 +453,7 @@ class CAInstance(DogtagInstance):
if promote:
self.step("destroying installation admin user",
self.teardown_admin)
+ self.step("updating IPA configuration", update_ipa_conf)
# Materialize config changes and new ACLs
self.step("starting certificate server instance",
self.start_instance)
@@ -480,7 +481,6 @@ class CAInstance(DogtagInstance):
self.step("configure certificate renewals", self.configure_renewal)
self.step("Configure HTTP to proxy connections",
self.http_proxy)
- self.step("updating IPA configuration", update_ipa_conf)
self.step("enabling CA instance", self.__enable_instance)
if not promote:
if self.clone:
@@ -2453,6 +2453,7 @@ def update_ipa_conf(ca_host=None):
parser.set('global', 'enable_ra', 'True')
parser.set('global', 'ra_plugin', 'dogtag')
parser.set('global', 'dogtag_version', '10')
+ parser.set('global', 'startup_timeout', api.env.startup_timeout)
if ca_host is None:
parser.remove_option('global', 'ca_host')
else:
--
2.48.1

View File

@ -1,62 +0,0 @@
From 91353b10748f1153540c6f5447a80864dee59d7f Mon Sep 17 00:00:00 2001
From: Antonio Torres <antorres@redhat.com>
Date: Wed, 12 Feb 2025 09:48:58 +0100
Subject: [PATCH] dns: only disable unbound when DoT is enabled
Ensure unbound is only stopped and disabled when DNS over TLS was
enabled during installation.
Signed-off-by: Antonio Torres <antorres@redhat.com>
Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
---
ipaserver/install/bindinstance.py | 11 +++++++----
ipaserver/install/dns.py | 3 +++
2 files changed, 10 insertions(+), 4 deletions(-)
diff --git a/ipaserver/install/bindinstance.py b/ipaserver/install/bindinstance.py
index 4f4ab9bbc66fcfd89354d6659cf9ac2bcaa270f6..0cc1f1325ce0a9dbdb09f4100a1a22bc4f24924a 100644
--- a/ipaserver/install/bindinstance.py
+++ b/ipaserver/install/bindinstance.py
@@ -689,6 +689,8 @@ class BindInstance(service.Service):
self.forward_policy = forward_policy
self.reverse_zones = reverse_zones
+ self.sstore.backup_state("dns_over_tls", "enabled", dns_over_tls)
+
if not zonemgr:
self.zonemgr = 'hostmaster.%s' % normalize_zone(self.domain)
else:
@@ -1377,10 +1379,11 @@ class BindInstance(service.Service):
self.named_conflict.unmask()
- certmonger.stop_tracking(certfile=paths.BIND_DNS_OVER_TLS_CRT)
- certmonger.stop_tracking(certfile=paths.BIND_DNS_OVER_TLS_KEY)
- services.knownservices.unbound.disable()
- services.knownservices.unbound.stop()
+ if self.sstore.restore_state("dns_over_tls", "enabled"):
+ if not self.sstore.restore_state("dns_over_tls", "external_crt"):
+ certmonger.stop_tracking(certfile=paths.BIND_DNS_OVER_TLS_CRT)
+ services.knownservices["unbound"].disable()
+ services.knownservices["unbound"].stop()
ipautil.remove_file(paths.NAMED_CONF_BAK)
ipautil.remove_file(paths.NAMED_CUSTOM_CONF)
diff --git a/ipaserver/install/dns.py b/ipaserver/install/dns.py
index 29ca0d2ff4efa8ad80784b393f49ac8ec0e03512..88aff19bcec11f778af5644167c32c45cbcab594 100644
--- a/ipaserver/install/dns.py
+++ b/ipaserver/install/dns.py
@@ -457,6 +457,9 @@ def install(standalone, replica, options, api=api):
bind.create_instance()
+ bind.sstore.backup_state("dns_over_tls",
+ "external_crt",
+ bool(options.dns_over_tls_cert))
if options.dns_over_tls:
print("Setting up DNS over TLS")
_setup_dns_over_tls(options)
--
2.48.1

View File

@ -1,66 +0,0 @@
From 7fd4b940abd2084fd6ec7de73dfd68551fce73fe Mon Sep 17 00:00:00 2001
From: Mark Reynolds <mreynolds@redhat.com>
Date: Wed, 29 Jan 2025 10:07:45 -0500
Subject: [PATCH] ipa-migrate - do not migrate tombstone entries, ignore
MidairCollisions, and krbpwdpolicyreference
Replication related entries should not be migrated. The main reason is
that we do not allow entries to be added that have an RDN of nsuniqueid
(only the server can internally add them).
Most midair collisions are transient issues and can be ignored for
migration purposes. In migration tests this only happens when an
attribute does not exist in the local server. This happens frequently
with COS attributes.
We should also ignore 'krbpwdpolicyreference' as it's an attribute that is
set by COS and does not need to be migrated.
Fixes: https://pagure.io/freeipa/issue/9737
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
---
ipaserver/install/ipa_migrate.py | 8 ++++++++
ipaserver/install/ipa_migrate_constants.py | 1 +
2 files changed, 9 insertions(+)
diff --git a/ipaserver/install/ipa_migrate.py b/ipaserver/install/ipa_migrate.py
index ece473bc8cb525e2d563356b5b274502d6b703e8..5ba140ce37156a6f2cb50d08427f5024925686e6 100644
--- a/ipaserver/install/ipa_migrate.py
+++ b/ipaserver/install/ipa_migrate.py
@@ -1462,6 +1462,10 @@ class IPAMigrate():
if DN(exclude_dn) in DN(entry_dn):
return
+ # Skip tombstones
+ if 'nsTombstone' in entry_attrs['objectClass']:
+ return
+
# Determine entry type: user, group, hbac, etc
entry_type = self.get_entry_type(entry_dn, entry_attrs)
if entry_type is None:
@@ -1568,6 +1572,10 @@ class IPAMigrate():
stats['custom'] += 1
else:
DB_OBJECTS[entry_type]['count'] += 1
+ except errors.MidairCollision as e:
+ # Typically means no such attribute, ok to ignore
+ self.log_debug(f'Failed to update "{local_dn}" error: '
+ f'{str(e)} - ok to ignore')
except errors.ExecutionError as e:
self.log_error(f'Failed to update "{local_dn}" error: '
f'{str(e)}')
diff --git a/ipaserver/install/ipa_migrate_constants.py b/ipaserver/install/ipa_migrate_constants.py
index e8192fb1aabae1c36669370eff242428a1f0355f..09856f07cabd124a7899bc5f355a56eb23023cc0 100644
--- a/ipaserver/install/ipa_migrate_constants.py
+++ b/ipaserver/install/ipa_migrate_constants.py
@@ -71,6 +71,7 @@ IGNORE_ATTRS = [
'serverhostname',
'krbpasswordexpiration',
'krblastadminunlock',
+ 'krbpwdpolicyreference', # COS attribute
]
# For production mode, bring everything over
--
2.48.1

View File

@ -1,35 +0,0 @@
From 100737fff5a0039cd883a92400d1495dd5bf7658 Mon Sep 17 00:00:00 2001
From: Florence Blanc-Renaud <flo@redhat.com>
Date: Fri, 7 Mar 2025 09:01:35 +0100
Subject: [PATCH] WebUI: fix the tooltip for Search Size limit
The tooltip for IPA Server > Configuration > Search size limit
is using the doc from ipasearchtimelimit instead of
ipasearchrecordslimit.
Use the right tooltip to properly display:
Maximum number of records to search (-1 or 0 is unlimited)
Fixes: https://pagure.io/freeipa/issue/9758
Signed-off-by: Florence Blanc-Renaud <flo@redhat.com>
Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
---
install/ui/src/freeipa/serverconfig.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/install/ui/src/freeipa/serverconfig.js b/install/ui/src/freeipa/serverconfig.js
index e81e48cfb405c4ccc2591edb754fa88f5586c8e0..d7f67e885bdb2c884a5dedd615bade5fcedc863d 100644
--- a/install/ui/src/freeipa/serverconfig.js
+++ b/install/ui/src/freeipa/serverconfig.js
@@ -47,7 +47,7 @@ return {
fields: [
{
name: 'ipasearchrecordslimit',
- tooltip: '@mc-opt:config_mod:ipasearchtimelimit:doc'
+ tooltip: '@mc-opt:config_mod:ipasearchrecordslimit:doc'
},
{
name: 'ipasearchtimelimit',
--
2.48.1

View File

@ -1,56 +0,0 @@
From 9b566fe458fb36eb5eb3212b01bc6ba48ac8349a Mon Sep 17 00:00:00 2001
From: Florence Blanc-Renaud <flo@redhat.com>
Date: Tue, 11 Mar 2025 15:55:11 +0100
Subject: [PATCH] Leapp upgrade: skip systemctl calls
During LEAPP upgrade, the system is booted in a special mode
without systemd. As a consequence, any scriptlet calling
systemctl fails and may break the upgrade.
Skip the call to systemctl if a LEAPP upgrade is in progress
(this is easily checked using the env variable $LEAPP_IPU_IN_PROGRESS
that is set for instance to LEAPP_IPU_IN_PROGRESS=8to9).
Fixes: RHEL-82089
Signed-off-by: Florence Blanc-Renaud <flo@redhat.com>
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
---
freeipa.spec.in | 14 ++++++++++----
1 file changed, 10 insertions(+), 4 deletions(-)
diff --git a/freeipa.spec.in b/freeipa.spec.in
index b539f51f88a19a3686684dd0a9138add97bbd285..143ee5c83d16b59531feda011c087c0ab4c82786 100755
--- a/freeipa.spec.in
+++ b/freeipa.spec.in
@@ -1241,8 +1241,11 @@ if [ $1 = 0 ]; then
# NOTE: systemd specific section
/bin/systemctl --quiet stop ipa.service || :
/bin/systemctl --quiet disable ipa.service || :
- /bin/systemctl reload-or-try-restart dbus
- /bin/systemctl reload-or-try-restart oddjobd
+ # Skip systemctl calls when leapp upgrade is in progress
+ if [ -z "$LEAPP_IPU_IN_PROGRESS" ] ; then
+ /bin/systemctl reload-or-try-restart dbus
+ /bin/systemctl reload-or-try-restart oddjobd
+ fi
# END
fi
@@ -1306,8 +1309,11 @@ fi
%preun server-trust-ad
if [ $1 -eq 0 ]; then
%{_sbindir}/update-alternatives --remove winbind_krb5_locator.so /dev/null
- /bin/systemctl reload-or-try-restart dbus
- /bin/systemctl reload-or-try-restart oddjobd
+ # Skip systemctl calls when leapp upgrade is in progress
+ if [ -z "$LEAPP_IPU_IN_PROGRESS" ] ; then
+ /bin/systemctl reload-or-try-restart dbus
+ /bin/systemctl reload-or-try-restart oddjobd
+ fi
fi
# ONLY_CLIENT
--
2.48.1

View File

@ -1,355 +0,0 @@
From 653b4b6971b1778988718840a301c10b3e35e700 Mon Sep 17 00:00:00 2001
From: David Hanina <dhanina@redhat.com>
Date: Thu, 6 Mar 2025 09:32:01 +0100
Subject: [PATCH] Disable --raw and --structured together
Disables --raw and --structured for dnsrecord-* command.
This is being shown in help for structured, as raw is implemented in
almost every command, therefore people are more likely to view
structured. Also contains tests, even though this is newly noted, this
combination has never worked in the past.
Fixes: https://pagure.io/freeipa/issue/9756
Signed-off-by: David Hanina <dhanina@redhat.com>
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
---
ipaclient/remote_plugins/2_114/dns.py | 15 +++++++----
ipaclient/remote_plugins/2_156/dns.py | 15 +++++++----
ipaclient/remote_plugins/2_164/dns.py | 15 +++++++----
ipaclient/remote_plugins/2_49/dns.py | 15 +++++++----
ipaserver/plugins/dns.py | 28 ++++++++++++++++++++
ipatests/test_xmlrpc/test_dns_plugin.py | 35 +++++++++++++++++++++++++
6 files changed, 103 insertions(+), 20 deletions(-)
diff --git a/ipaclient/remote_plugins/2_114/dns.py b/ipaclient/remote_plugins/2_114/dns.py
index 6260420008e3371dc95317d67d2f37a46b4d5d42..2f414927bad2f0838bec42bab734d3a42e87005f 100644
--- a/ipaclient/remote_plugins/2_114/dns.py
+++ b/ipaclient/remote_plugins/2_114/dns.py
@@ -2625,7 +2625,8 @@ class dnsrecord_add(Method):
parameters.Flag(
'structured',
label=_(u'Structured'),
- doc=_(u'Parse all raw DNS records and return them in a structured way'),
+ doc=_(u'Parse all raw DNS records and return them in a '
+ u'structured way. Can not be used with --raw.'),
default=False,
autofill=True,
),
@@ -2991,7 +2992,8 @@ class dnsrecord_del(Method):
parameters.Flag(
'structured',
label=_(u'Structured'),
- doc=_(u'Parse all raw DNS records and return them in a structured way'),
+ doc=_(u'Parse all raw DNS records and return them in a '
+ u'structured way. Can not be used with --raw.'),
default=False,
autofill=True,
),
@@ -3405,7 +3407,8 @@ class dnsrecord_find(Method):
parameters.Flag(
'structured',
label=_(u'Structured'),
- doc=_(u'Parse all raw DNS records and return them in a structured way'),
+ doc=_(u'Parse all raw DNS records and return them in a '
+ u'structured way. Can not be used with --raw.'),
default=False,
autofill=True,
),
@@ -4290,7 +4293,8 @@ class dnsrecord_mod(Method):
parameters.Flag(
'structured',
label=_(u'Structured'),
- doc=_(u'Parse all raw DNS records and return them in a structured way'),
+ doc=_(u'Parse all raw DNS records and return them in a '
+ u'structured way. Can not be used with --raw.'),
default=False,
autofill=True,
),
@@ -4363,7 +4367,8 @@ class dnsrecord_show(Method):
parameters.Flag(
'structured',
label=_(u'Structured'),
- doc=_(u'Parse all raw DNS records and return them in a structured way'),
+ doc=_(u'Parse all raw DNS records and return them in a '
+ u'structured way. Can not be used with --raw.'),
default=False,
autofill=True,
),
diff --git a/ipaclient/remote_plugins/2_156/dns.py b/ipaclient/remote_plugins/2_156/dns.py
index 4ebad93e79d38c1171b066cc5a1a0b8d6fce64b2..9ce8a7eef99eff7592f8550d0000506cc2d7824c 100644
--- a/ipaclient/remote_plugins/2_156/dns.py
+++ b/ipaclient/remote_plugins/2_156/dns.py
@@ -2540,7 +2540,8 @@ class dnsrecord_add(Method):
parameters.Flag(
'structured',
label=_(u'Structured'),
- doc=_(u'Parse all raw DNS records and return them in a structured way'),
+ doc=_(u'Parse all raw DNS records and return them in a '
+ u'structured way. Can not be used with --raw.'),
default=False,
autofill=True,
),
@@ -2861,7 +2862,8 @@ class dnsrecord_del(Method):
parameters.Flag(
'structured',
label=_(u'Structured'),
- doc=_(u'Parse all raw DNS records and return them in a structured way'),
+ doc=_(u'Parse all raw DNS records and return them in a '
+ u'structured way. Can not be used with --raw.'),
default=False,
autofill=True,
),
@@ -3230,7 +3232,8 @@ class dnsrecord_find(Method):
parameters.Flag(
'structured',
label=_(u'Structured'),
- doc=_(u'Parse all raw DNS records and return them in a structured way'),
+ doc=_(u'Parse all raw DNS records and return them in a '
+ u'structured way. Can not be used with --raw.'),
default=False,
autofill=True,
),
@@ -4065,7 +4068,8 @@ class dnsrecord_mod(Method):
parameters.Flag(
'structured',
label=_(u'Structured'),
- doc=_(u'Parse all raw DNS records and return them in a structured way'),
+ doc=_(u'Parse all raw DNS records and return them in a '
+ u'structured way. Can not be used with --raw.'),
default=False,
autofill=True,
),
@@ -4138,7 +4142,8 @@ class dnsrecord_show(Method):
parameters.Flag(
'structured',
label=_(u'Structured'),
- doc=_(u'Parse all raw DNS records and return them in a structured way'),
+ doc=_(u'Parse all raw DNS records and return them in a '
+ u'structured way. Can not be used with --raw.'),
default=False,
autofill=True,
),
diff --git a/ipaclient/remote_plugins/2_164/dns.py b/ipaclient/remote_plugins/2_164/dns.py
index f5adb4d54e8501b6b4efed06404ff299aa918cfb..284ef2cdaa757341db4eed044be3bb051db83d99 100644
--- a/ipaclient/remote_plugins/2_164/dns.py
+++ b/ipaclient/remote_plugins/2_164/dns.py
@@ -2548,7 +2548,8 @@ class dnsrecord_add(Method):
parameters.Flag(
'structured',
label=_(u'Structured'),
- doc=_(u'Parse all raw DNS records and return them in a structured way'),
+ doc=_(u'Parse all raw DNS records and return them in a '
+ u'structured way. Can not be used with --raw.'),
default=False,
autofill=True,
),
@@ -2869,7 +2870,8 @@ class dnsrecord_del(Method):
parameters.Flag(
'structured',
label=_(u'Structured'),
- doc=_(u'Parse all raw DNS records and return them in a structured way'),
+ doc=_(u'Parse all raw DNS records and return them in a '
+ u'structured way. Can not be used with --raw.'),
default=False,
autofill=True,
),
@@ -3238,7 +3240,8 @@ class dnsrecord_find(Method):
parameters.Flag(
'structured',
label=_(u'Structured'),
- doc=_(u'Parse all raw DNS records and return them in a structured way'),
+ doc=_(u'Parse all raw DNS records and return them in a '
+ u'structured way. Can not be used with --raw.'),
default=False,
autofill=True,
),
@@ -4073,7 +4076,8 @@ class dnsrecord_mod(Method):
parameters.Flag(
'structured',
label=_(u'Structured'),
- doc=_(u'Parse all raw DNS records and return them in a structured way'),
+ doc=_(u'Parse all raw DNS records and return them in a '
+ u'structured way. Can not be used with --raw.'),
default=False,
autofill=True,
),
@@ -4146,7 +4150,8 @@ class dnsrecord_show(Method):
parameters.Flag(
'structured',
label=_(u'Structured'),
- doc=_(u'Parse all raw DNS records and return them in a structured way'),
+ doc=_(u'Parse all raw DNS records and return them in a '
+ u'structured way. Can not be used with --raw.'),
default=False,
autofill=True,
),
diff --git a/ipaclient/remote_plugins/2_49/dns.py b/ipaclient/remote_plugins/2_49/dns.py
index 4b543a2c2539f7b67467b0a38ab8013a1ebe0840..1610f4af18ee46bc7304839ede2d587d61c6d0e2 100644
--- a/ipaclient/remote_plugins/2_49/dns.py
+++ b/ipaclient/remote_plugins/2_49/dns.py
@@ -2233,7 +2233,8 @@ class dnsrecord_add(Method):
parameters.Flag(
'structured',
label=_(u'Structured'),
- doc=_(u'Parse all raw DNS records and return them in a structured way'),
+ doc=_(u'Parse all raw DNS records and return them in a '
+ u'structured way. Can not be used with --raw.'),
default=False,
autofill=True,
),
@@ -2594,7 +2595,8 @@ class dnsrecord_del(Method):
parameters.Flag(
'structured',
label=_(u'Structured'),
- doc=_(u'Parse all raw DNS records and return them in a structured way'),
+ doc=_(u'Parse all raw DNS records and return them in a '
+ u'structured way. Can not be used with --raw.'),
default=False,
autofill=True,
),
@@ -3013,7 +3015,8 @@ class dnsrecord_find(Method):
parameters.Flag(
'structured',
label=_(u'Structured'),
- doc=_(u'Parse all raw DNS records and return them in a structured way'),
+ doc=_(u'Parse all raw DNS records and return them in a '
+ u'structured way. Can not be used with --raw.'),
default=False,
autofill=True,
),
@@ -4025,7 +4028,8 @@ class dnsrecord_mod(Method):
parameters.Flag(
'structured',
label=_(u'Structured'),
- doc=_(u'Parse all raw DNS records and return them in a structured way'),
+ doc=_(u'Parse all raw DNS records and return them in a '
+ u'structured way. Can not be used with --raw.'),
default=False,
autofill=True,
),
@@ -4094,7 +4098,8 @@ class dnsrecord_show(Method):
parameters.Flag(
'structured',
label=_(u'Structured'),
- doc=_(u'Parse all raw DNS records and return them in a structured way'),
+ doc=_(u'Parse all raw DNS records and return them in a '
+ u'structured way. Can not be used with --raw.'),
default=False,
autofill=True,
),
diff --git a/ipaserver/plugins/dns.py b/ipaserver/plugins/dns.py
index 0d6260cd6c4edb8c1a9d7ac8927b7595588fae58..ff2d3ff8a7c2839645c9906300cba0d399f2325a 100644
--- a/ipaserver/plugins/dns.py
+++ b/ipaserver/plugins/dns.py
@@ -3587,6 +3587,12 @@ class dnsrecord_add(LDAPCreate):
def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options):
assert isinstance(dn, DN)
+
+ if options.get('structured') and options.get('raw'):
+ raise errors.MutuallyExclusiveError(
+ reason=_("cannot use structured together with raw")
+ )
+
precallback_attrs = []
processed_attrs = []
for option, option_val in options.items():
@@ -3729,6 +3735,12 @@ class dnsrecord_mod(LDAPUpdate):
def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options):
assert isinstance(dn, DN)
+
+ if options.get('structured') and options.get('raw'):
+ raise errors.MutuallyExclusiveError(
+ reason=_("cannot use structured together with raw")
+ )
+
if options.get('rename') and self.obj.is_pkey_zone_record(*keys):
# zone rename is not allowed
raise errors.ValidationError(name='rename',
@@ -3883,6 +3895,7 @@ class dnsrecord_del(LDAPUpdate):
def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options):
assert isinstance(dn, DN)
+
try:
old_entry = ldap.get_entry(dn, _record_attributes)
except errors.NotFound:
@@ -3983,6 +3996,16 @@ class dnsrecord_show(LDAPRetrieve):
dnsrecord.structured_flag,
)
+ def pre_callback(self, ldap, dn, attrs_list, *keys, **options):
+ assert isinstance(dn, DN)
+
+ if options.get('structured') and options.get('raw'):
+ raise errors.MutuallyExclusiveError(
+ reason=_("cannot use structured together with raw")
+ )
+
+ return dn
+
def post_callback(self, ldap, dn, entry_attrs, *keys, **options):
assert isinstance(dn, DN)
if self.obj.is_pkey_zone_record(*keys):
@@ -4013,6 +4036,11 @@ class dnsrecord_find(LDAPSearch):
dnszoneidnsname, *args, **options):
assert isinstance(base_dn, DN)
+ if options.get('structured') and options.get('raw'):
+ raise errors.MutuallyExclusiveError(
+ reason=_("cannot use structured together with raw")
+ )
+
# validate if zone is master zone
self.obj.check_zone(dnszoneidnsname, **options)
diff --git a/ipatests/test_xmlrpc/test_dns_plugin.py b/ipatests/test_xmlrpc/test_dns_plugin.py
index 39d42e306d12c4f6623a1ed657aeac3d3bfa3e22..803b0a9571c2888dd02c4595c68403f37be7fed7 100644
--- a/ipatests/test_xmlrpc/test_dns_plugin.py
+++ b/ipatests/test_xmlrpc/test_dns_plugin.py
@@ -3426,6 +3426,41 @@ class test_dns(Declarative):
},
),
+ dict(
+ desc="Ensure --raw and --structure does not work "
+ "for ipa dnsrecord-add",
+ command=('dnrecord_add', [], {u'raw': True, u'structured': True}),
+ expected=errors.MutuallyExclusiveError(
+ reason=u"cannot use structured together with raw"
+ ),
+ ),
+
+ dict(
+ desc="Ensure --raw and --structure does not work "
+ "for ipa dnsrecord-mod",
+ command=('dnrecord_add', [], {u'raw': True, u'structured': True}),
+ expected=errors.MutuallyExclusiveError(
+ reason=u"cannot use structured together with raw"
+ ),
+ ),
+
+ dict(
+ desc="Ensure --raw and --structure does not work "
+ "for ipa dnsrecord-show",
+ command=('dnrecord_add', [], {u'raw': True, u'structured': True}),
+ expected=errors.MutuallyExclusiveError(
+ reason=u"cannot use structured together with raw"
+ ),
+ ),
+
+ dict(
+ desc="Ensure --raw and --structure does not work "
+ "for ipa dnsrecord-find",
+ command=('dnrecord_add', [], {u'raw': True, u'structured': True}),
+ expected=errors.MutuallyExclusiveError(
+ reason=u"cannot use structured together with raw"
+ ),
+ ),
]
--
2.48.1

View File

@ -1,435 +0,0 @@
From f906e3625491e9b6fc67fdd5ac6b429531658be1 Mon Sep 17 00:00:00 2001
From: Alexander Bokovoy <abokovoy@redhat.com>
Date: Fri, 28 Feb 2025 14:57:25 +0200
Subject: [PATCH] config-mod: allow disabling subordinate ID integration
When full 32-bit ID range usage is required, subordinate ID support have
to be disabled. However, even if ID range for subordinate IDs were to be
removed, it will be restored during the next data upgrade.
Change upgrade code to only apply subID range creation when subID
support is enabled.
Do not allow allocating subIDs if their use is disabled.
Allow full 32-bit uidNumber/gidNumber values in JSON payload.
Fixes: https://pagure.io/freeipa/issue/9757
Signed-off-by: Alexander Bokovoy <abokovoy@redhat.com>
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
---
API.txt | 2 +-
doc/api/config_mod.md | 2 +-
doc/designs/subordinate-ids.md | 22 +++++++++
install/share/Makefile.am | 1 +
install/share/subid-generators.uldif | 38 ++++++++++++++++
install/updates/73-subid.update | 37 ---------------
.../updates/90-post_upgrade_plugins.update | 1 +
ipalib/messages.py | 13 ++++++
ipaplatform/base/paths.py | 1 +
ipaserver/install/ipa_subids.py | 5 +++
.../install/plugins/update_subid_support.py | 45 +++++++++++++++++++
ipaserver/plugins/config.py | 34 +++++++++++++-
ipaserver/plugins/subid.py | 11 +++++
ipaserver/plugins/user.py | 4 +-
14 files changed, 174 insertions(+), 42 deletions(-)
create mode 100644 install/share/subid-generators.uldif
create mode 100644 ipaserver/install/plugins/update_subid_support.py
diff --git a/API.txt b/API.txt
index 61e8e463ab5c66b1609f8cc61f93ae2ded959bba..f19e3bf344cf6f23680c268c5081570ac629f851 100644
--- a/API.txt
+++ b/API.txt
@@ -1083,7 +1083,7 @@ option: Flag('all', autofill=True, cli_name='all', default=False)
option: Str('ca_renewal_master_server?', autofill=False)
option: Str('delattr*', cli_name='delattr')
option: Flag('enable_sid?', autofill=True, default=False)
-option: StrEnum('ipaconfigstring*', autofill=False, cli_name='ipaconfigstring', values=[u'AllowNThash', u'KDC:Disable Last Success', u'KDC:Disable Lockout', u'KDC:Disable Default Preauth for SPNs', u'EnforceLDAPOTP'])
+option: StrEnum('ipaconfigstring*', autofill=False, cli_name='ipaconfigstring', values=[u'AllowNThash', u'KDC:Disable Last Success', u'KDC:Disable Lockout', u'KDC:Disable Default Preauth for SPNs', u'EnforceLDAPOTP', u'SubID:Disable'])
option: Str('ipadefaultemaildomain?', autofill=False, cli_name='emaildomain')
option: Str('ipadefaultloginshell?', autofill=False, cli_name='defaultshell')
option: Str('ipadefaultprimarygroup?', autofill=False, cli_name='defaultgroup')
diff --git a/doc/api/config_mod.md b/doc/api/config_mod.md
index b3203c350605af5a386544c858a9a5f7f724342f..e18dd55c75993016afbcd8a15d33f13a38ef96b3 100644
--- a/doc/api/config_mod.md
+++ b/doc/api/config_mod.md
@@ -27,7 +27,7 @@ No arguments.
* ipauserobjectclasses : :ref:`Str<Str>`
* ipapwdexpadvnotify : :ref:`Int<Int>`
* ipaconfigstring : :ref:`StrEnum<StrEnum>`
- * Values: ('AllowNThash', 'KDC:Disable Last Success', 'KDC:Disable Lockout', 'KDC:Disable Default Preauth for SPNs', 'EnforceLDAPOTP')
+ * Values: ('AllowNThash', 'KDC:Disable Last Success', 'KDC:Disable Lockout', 'KDC:Disable Default Preauth for SPNs', 'EnforceLDAPOTP', 'SubID:Disable')
* ipaselinuxusermaporder : :ref:`Str<Str>`
* ipaselinuxusermapdefault : :ref:`Str<Str>`
* ipakrbauthzdata : :ref:`StrEnum<StrEnum>`
diff --git a/doc/designs/subordinate-ids.md b/doc/designs/subordinate-ids.md
index b280df1a9eb2fc8e0ff53271b19a2d5b13399506..dac1c3292fecdebcc7f49118ea0b23d8c5aeff37 100644
--- a/doc/designs/subordinate-ids.md
+++ b/doc/designs/subordinate-ids.md
@@ -64,6 +64,18 @@ and don't auto-map or auto-assign subordinate ids by default. Instead
we give the admin several options to assign them manually, semi-manual,
or automatically.
+For deployments where there is a need to consume IDs above 2^31 for normal UID
+and GID assignments, one has to disable subordinate ID feature. This should be
+done with `ipa config-mod --addattr ipaconfigstring=SubID:Disable` command.
+After it is done, subordinate ID range can be removed with `ipa idrange-del`
+command and on the IPA server one have to run `ipa-server-upgrade` command to
+make sure internal DNA plugin configuration is removed as well.
+Finally, a new local ID range can be added to cover required part of the
+2^31..2^32-1 space. The range must have RID bases to make sure FreeIPA will
+generate SIDs properly to users and groups created with IDs from this range.
+
+**NOTE**: Disabling subordinate ID feature can only be done if no subordinate
+IDs were already allocated.
### Revision 1 limitation
@@ -340,6 +352,16 @@ subordinate id entries for new users:
$ ipa config-mod --user-default-subid=true
```
+Subordinate ID feature can be disabled completely. This is done with `ipa
+config-mod --addattr ipaconfigstring=SubID:Disable` command. After it is done,
+subordinate ID range can be removed with `ipa idrange-del` command and on the
+IPA server one have to run `ipa-server-upgrade` command to make sure internal
+DNA plugin configuration is removed as well. Finally, a new local ID range can
+be added to cover the required part of the full 32-bit ID space.
+
+**NOTE**: Disabling subordinate ID feature can only be done if no subordinate
+IDs were already allocated.
+
Subordinate ids are managed by a new plugin class. The ``subid-add``
and ``subid-del`` commands are hidden from command line. New subordinate
ids are generated and auto-assigned with ``subid-generate``.
diff --git a/install/share/Makefile.am b/install/share/Makefile.am
index 4029297b76cc2f30dc9eab606e5670667978dd27..d8d270ca9f4b13ed01e65c6460a3a6b0dbbc5ebe 100644
--- a/install/share/Makefile.am
+++ b/install/share/Makefile.am
@@ -90,6 +90,7 @@ dist_app_DATA = \
vault.ldif \
kdcproxy-enable.uldif \
kdcproxy-disable.uldif \
+ subid-generators.uldif \
ipa-httpd.conf.template \
ipa-httpd-wsgi.conf.template \
gssapi.login \
diff --git a/install/share/subid-generators.uldif b/install/share/subid-generators.uldif
new file mode 100644
index 0000000000000000000000000000000000000000..118077382b860c655aa63907ab3db090110349d6
--- /dev/null
+++ b/install/share/subid-generators.uldif
@@ -0,0 +1,38 @@
+# DNA plugin and idrange configuration
+dn: cn=subordinate-ids,cn=dna,cn=ipa,cn=etc,$SUFFIX
+default: objectClass: nsContainer
+default: objectClass: top
+default: cn: subordinate-ids
+
+dn: cn=Subordinate IDs,cn=Distributed Numeric Assignment Plugin,cn=plugins,cn=config
+default: objectclass: top
+default: objectclass: extensibleObject
+default: cn: Subordinate IDs
+default: dnaType: ipasubuidnumber
+default: dnaType: ipasubgidnumber
+default: dnaNextValue: eval($SUBID_RANGE_START)
+default: dnaMaxValue: eval($SUBID_RANGE_MAX)
+default: dnaMagicRegen: -1
+default: dnaFilter: (objectClass=ipaSubordinateId)
+default: dnaScope: $SUFFIX
+default: dnaThreshold: eval($SUBID_DNA_THRESHOLD)
+default: dnaSharedCfgDN: cn=subordinate-ids,cn=dna,cn=ipa,cn=etc,$SUFFIX
+default: dnaExcludeScope: cn=provisioning,$SUFFIX
+default: dnaInterval: eval($SUBID_COUNT)
+add: aci: (targetattr = "dnaNextRange || dnaNextValue || dnaMaxValue")(version 3.0;acl "permission:Modify DNA Range";allow (write) groupdn = "ldap:///cn=Modify DNA Range,cn=permissions,cn=pbac,$SUFFIX";)
+add: aci: (targetattr = "cn || dnaMaxValue || dnaNextRange || dnaNextValue || dnaThreshold || dnaType || objectclass")(version 3.0;acl "permission:Read DNA Range";allow (read, search, compare) groupdn = "ldap:///cn=Read DNA Range,cn=permissions,cn=pbac,$SUFFIX";)
+
+dn: cn=${REALM}_subid_range,cn=ranges,cn=etc,$SUFFIX
+default: objectClass: top
+default: objectClass: ipaIDrange
+default: objectClass: ipaTrustedADDomainRange
+default: cn: ${REALM}_subid_range
+default: ipaBaseID: $SUBID_RANGE_START
+default: ipaIDRangeSize: $SUBID_RANGE_SIZE
+# HACK: RIDs to work around adtrust sidgen issue
+default: ipaBaseRID: eval($SUBID_BASE_RID)
+default: ipaNTTrustedDomainSID: S-1-5-21-738065-838566-$DOMAIN_HASH
+# HACK: "ipa-local-subid" range type causes issues with older SSSD clients
+# see https://github.com/SSSD/sssd/issues/5571
+default: ipaRangeType: ipa-ad-trust
+
diff --git a/install/updates/73-subid.update b/install/updates/73-subid.update
index 3c030b41e6d01ed48a0e5cc5c0ed7e536c9d3412..18bca60bcd85b32350a456f71ef9d97ef35b9584 100644
--- a/install/updates/73-subid.update
+++ b/install/updates/73-subid.update
@@ -67,40 +67,3 @@ dn: cn=subids,cn=accounts,$SUFFIX
add: aci: (targetfilter = "(objectclass=ipasubordinateidentry)")(targetattr="description || ipaowner || ipauniqueid")(targattrfilters = "add=objectClass:(|(objectClass=top)(objectClass=ipasubordinateid)(objectClass=ipasubordinateidentry)(objectClass=ipasubordinategid)(objectClass=ipasubordinateuid)) && ipasubuidnumber:(ipasubuidnumber=-1) && ipasubuidcount:(ipasubuidcount=eval($SUBID_COUNT)) && ipasubgidnumber:(ipasubgidnumber=-1) && ipasubgidcount:(ipasubgidcount=eval($SUBID_COUNT)), del=ipasubuidnumber:(!(ipasubuidnumber=*)) && ipasubuidcount:(!(ipasubuidcount=*)) && ipasubgidnumber:(!(ipasubgidnumber=*)) && ipasubgidcount:(!(ipasubgidcount=*))")(version 3.0;acl "selfservice: Add subordinate id";allow (add, write) userattr = "ipaowner#SELFDN" and groupdn="ldap:///cn=Self-service subordinate ID,cn=permissions,cn=pbac,$SUFFIX";)
add: aci: (targetfilter = "(objectclass=ipasubordinateidentry)")(targetattr="description || ipaowner || ipauniqueid")(targattrfilters = "add=objectClass:(|(objectClass=top)(objectClass=ipasubordinateid)(objectClass=ipasubordinateidentry)(objectClass=ipasubordinategid)(objectClass=ipasubordinateuid)) && ipasubuidnumber:(|(ipasubuidnumber>=1)(ipasubuidnumber=-1)) && ipasubuidcount:(ipasubuidcount=eval($SUBID_COUNT)) && ipasubgidnumber:(|(ipasubgidnumber>=1)(ipasubgidnumber=-1)) && ipasubgidcount:(ipasubgidcount=eval($SUBID_COUNT)), del=ipasubuidnumber:(!(ipasubuidnumber=*)) && ipasubuidcount:(!(ipasubuidcount=*)) && ipasubgidnumber:(!(ipasubgidnumber=*)) && ipasubgidcount:(!(ipasubgidcount=*))")(version 3.0;acl "Add subordinate ids to any user";allow (add, write) groupdn="ldap:///cn=Subordinate ID Administrators,cn=privileges,cn=pbac,$SUFFIX";)
-# DNA plugin and idrange configuration
-dn: cn=subordinate-ids,cn=dna,cn=ipa,cn=etc,$SUFFIX
-default: objectClass: nsContainer
-default: objectClass: top
-default: cn: subordinate-ids
-
-dn: cn=Subordinate IDs,cn=Distributed Numeric Assignment Plugin,cn=plugins,cn=config
-default: objectclass: top
-default: objectclass: extensibleObject
-default: cn: Subordinate IDs
-default: dnaType: ipasubuidnumber
-default: dnaType: ipasubgidnumber
-default: dnaNextValue: eval($SUBID_RANGE_START)
-default: dnaMaxValue: eval($SUBID_RANGE_MAX)
-default: dnaMagicRegen: -1
-default: dnaFilter: (objectClass=ipaSubordinateId)
-default: dnaScope: $SUFFIX
-default: dnaThreshold: eval($SUBID_DNA_THRESHOLD)
-default: dnaSharedCfgDN: cn=subordinate-ids,cn=dna,cn=ipa,cn=etc,$SUFFIX
-default: dnaExcludeScope: cn=provisioning,$SUFFIX
-default: dnaInterval: eval($SUBID_COUNT)
-add: aci: (targetattr = "dnaNextRange || dnaNextValue || dnaMaxValue")(version 3.0;acl "permission:Modify DNA Range";allow (write) groupdn = "ldap:///cn=Modify DNA Range,cn=permissions,cn=pbac,$SUFFIX";)
-add: aci: (targetattr = "cn || dnaMaxValue || dnaNextRange || dnaNextValue || dnaThreshold || dnaType || objectclass")(version 3.0;acl "permission:Read DNA Range";allow (read, search, compare) groupdn = "ldap:///cn=Read DNA Range,cn=permissions,cn=pbac,$SUFFIX";)
-
-dn: cn=${REALM}_subid_range,cn=ranges,cn=etc,$SUFFIX
-default: objectClass: top
-default: objectClass: ipaIDrange
-default: objectClass: ipaTrustedADDomainRange
-default: cn: ${REALM}_subid_range
-default: ipaBaseID: $SUBID_RANGE_START
-default: ipaIDRangeSize: $SUBID_RANGE_SIZE
-# HACK: RIDs to work around adtrust sidgen issue
-default: ipaBaseRID: eval($SUBID_BASE_RID)
-default: ipaNTTrustedDomainSID: S-1-5-21-738065-838566-$DOMAIN_HASH
-# HACK: "ipa-local-subid" range type causes issues with older SSSD clients
-# see https://github.com/SSSD/sssd/issues/5571
-default: ipaRangeType: ipa-ad-trust
diff --git a/install/updates/90-post_upgrade_plugins.update b/install/updates/90-post_upgrade_plugins.update
index 9a9d80a9245654691ef96bb048dfbe950a4a7c6f..7c3bba3e0317162d4739513e16b9fac973495c66 100644
--- a/install/updates/90-post_upgrade_plugins.update
+++ b/install/updates/90-post_upgrade_plugins.update
@@ -34,6 +34,7 @@ plugin: update_dnsforward_emptyzones
plugin: update_managed_post
plugin: update_managed_permissions
plugin: update_read_replication_agreements_permission
+plugin: update_subid_support
plugin: update_idrange_baserid
plugin: update_passync_privilege_update
plugin: update_dnsserver_configuration_into_ldap
diff --git a/ipalib/messages.py b/ipalib/messages.py
index 732de7cb92bb530a734a68440478dfda09062db8..6a70bbc7556126748cc2ec031fc2af36bfe76f74 100644
--- a/ipalib/messages.py
+++ b/ipalib/messages.py
@@ -506,6 +506,19 @@ class MissingTargetAttributesinPermission(PublicMessage):
"are set.")
+class ServerUpgradeRequired(PublicMessage):
+ """
+ **13033** Server upgrade required
+ """
+ errno = 13033
+ type = "warning"
+ format = _(
+ "Change of the state of '%(feature)s' feature requires to run "
+ "'ipa-server-upgrade' command on IPA server %(server)s "
+ "to apply configuration changes."
+ )
+
+
def iter_messages(variables, base):
"""Return a tuple with all subclasses
"""
diff --git a/ipaplatform/base/paths.py b/ipaplatform/base/paths.py
index f794aae6d7b19a60ba40282f83a41052584517cb..a5bca789bdb8d07b51779e28adf64c9b68892328 100644
--- a/ipaplatform/base/paths.py
+++ b/ipaplatform/base/paths.py
@@ -302,6 +302,7 @@ class BasePathNamespace:
NIS_UPDATE_ULDIF = "/usr/share/ipa/nis-update.uldif"
SCHEMA_COMPAT_ULDIF = "/usr/share/ipa/updates/91-schema_compat.update"
SCHEMA_COMPAT_POST_ULDIF = "/usr/share/ipa/schema_compat_post.uldif"
+ SUBID_GENERATORS_ULDIF = "/usr/share/ipa/subid-generators.uldif"
IPA_JS_PLUGINS_DIR = "/usr/share/ipa/ui/js/plugins"
UPDATES_DIR = "/usr/share/ipa/updates/"
DICT_WORDS = "/usr/share/dict/words"
diff --git a/ipaserver/install/ipa_subids.py b/ipaserver/install/ipa_subids.py
index 1537047c33431b59d776f9bfa6325d52561e1ac6..8c542e4eae4b6e378a99ed748cd3a2b311dc0ce8 100644
--- a/ipaserver/install/ipa_subids.py
+++ b/ipaserver/install/ipa_subids.py
@@ -116,6 +116,11 @@ class IPASubids(AdminTool):
api.finalize()
api.Backend.ldap2.connect()
self.ldap2 = api.Backend.ldap2
+
+ if api.Object.config.is_config_option_present('SubID:Disable'):
+ print("Support for subordinate IDs is disabled.")
+ return 2
+
subid_generate = api.Command.subid_generate
dry_run = self.safe_options.dry_run
diff --git a/ipaserver/install/plugins/update_subid_support.py b/ipaserver/install/plugins/update_subid_support.py
new file mode 100644
index 0000000000000000000000000000000000000000..54852d2034012bcac9d12b6e81a3025ac3fe7caf
--- /dev/null
+++ b/ipaserver/install/plugins/update_subid_support.py
@@ -0,0 +1,45 @@
+#
+# Copyright (C) 2025 FreeIPA Contributors see COPYING for license
+#
+import logging
+from ipalib import Registry, Updater, errors
+from ipaserver.install import ldapupdate
+from ipaplatform.paths import paths
+from ipapython.dn import DN
+
+logger = logging.getLogger(__name__)
+
+register = Registry()
+
+
+@register()
+class update_subid_support(Updater):
+ """
+ Conditionally add SubID ranges when subID support is enabled
+ """
+
+ dna_plugin_dn = DN(
+ ('cn', 'Distributed Numeric Assignment Plugin'),
+ ('cn', 'plugins'),
+ ('cn', 'config')
+ )
+
+ def execute(self, **options):
+ subid_disabled = self.api.Object.config.is_config_option_present(
+ 'SubID:Disable')
+ if not subid_disabled:
+ ld = ldapupdate.LDAPUpdate(api=self.api)
+ ld.update([paths.SUBID_GENERATORS_ULDIF])
+ else:
+ # make sure to remove DNA configuration
+ conn = self.api.Backend.ldap2
+ try:
+ subid_dna_config = DN(
+ ('cn', 'Subordinate IDs'), self.dna_plugin_dn
+ )
+ entry = conn.get_entry(subid_dna_config)
+ conn.delete_entry(entry)
+ except errors.NotFound:
+ pass
+
+ return False, []
diff --git a/ipaserver/plugins/config.py b/ipaserver/plugins/config.py
index adf21ea0c59f70714298af74d7e92f7200f75085..c509c2c13adfb4950741f63ffcbc9f3f806c0c3b 100644
--- a/ipaserver/plugins/config.py
+++ b/ipaserver/plugins/config.py
@@ -33,7 +33,7 @@ from .baseldap import (
LDAPUpdate,
LDAPRetrieve)
from .selinuxusermap import validate_selinuxuser
-from ipalib import _
+from ipalib import _, messages
from ipapython.admintool import ScriptError
from ipapython.dn import DN
from ipaserver.plugins.privilege import principal_has_privilege
@@ -261,7 +261,7 @@ class config(LDAPObject):
values=(u'AllowNThash',
u'KDC:Disable Last Success', u'KDC:Disable Lockout',
u'KDC:Disable Default Preauth for SPNs',
- u'EnforceLDAPOTP'),
+ u'EnforceLDAPOTP', u'SubID:Disable'),
),
Str('ipaselinuxusermaporder',
label=_('SELinux user map order'),
@@ -521,6 +521,12 @@ class config(LDAPObject):
for domain in submitted_domains:
self._validate_single_domain(attr_name, domain, known_domains)
+ def is_config_option_present(self, option):
+ dn = DN(('cn', 'ipaconfig'), ('cn', 'etc'), self.api.env.basedn)
+ configentry = self.api.Backend.ldap2.get_entry(dn, ['ipaconfigstring'])
+ configstring = configentry['ipaconfigstring']
+ return (option.lower() in map(str.lower, configstring))
+
@register()
class config_mod(LDAPUpdate):
@@ -695,6 +701,30 @@ class config_mod(LDAPUpdate):
raise errors.ValidationError(name=failedattr,
error=_('SELinux user map default user not in order list'))
+ if 'ipaconfigstring' in entry_attrs:
+ configstring = entry_attrs['ipaconfigstring']
+ if 'SubID:Disable'.lower() in map(str.lower, configstring):
+ # Check if SubIDs already allocated
+ try:
+ result = self.api.Command.subid_stats()
+ stats = result['result']
+ except errors.PublicError:
+ stats = {'assigned_subids': 0}
+ if stats["assigned_subids"] > 0:
+ error_message = _("Subordinate ID feature can not be "
+ "disabled when there are subIDs "
+ "already in use.")
+ raise errors.ValidationError(name='configuration state',
+ error=error_message)
+ # SubID:Disable enforces disabling default subid generation
+ entry_attrs['ipauserdefaultsubordinateid'] = False
+ self.add_message(
+ messages.ServerUpgradeRequired(
+ feature='Subordinate ID',
+ server=_('<all IPA servers>')
+ )
+ )
+
if 'ca_renewal_master_server' in options:
new_master = options['ca_renewal_master_server']
diff --git a/ipaserver/plugins/subid.py b/ipaserver/plugins/subid.py
index 132c85c7f198217ba70f2332306ee2550be86035..2be2cdeff920ff79eb7df6e3cf635df96d7f3348 100644
--- a/ipaserver/plugins/subid.py
+++ b/ipaserver/plugins/subid.py
@@ -265,6 +265,12 @@ class subid(LDAPObject):
def handle_subordinate_ids(self, ldap, dn, entry_attrs):
"""Handle ipaSubordinateId object class"""
+
+ if self.api.Object.config.is_config_option_present('SubID:Disable'):
+ raise errors.ValidationError(
+ name="configuration state",
+ error=_("Support for subordinate IDs is disabled"))
+
new_subuid = entry_attrs.single_value.get("ipasubuidnumber")
new_subgid = entry_attrs.single_value.get("ipasubgidnumber")
@@ -577,6 +583,11 @@ class subid_stats(LDAPQuery):
return int(entry.single_value["numSubordinates"])
def execute(self, *keys, **options):
+ if self.api.Object.config.is_config_option_present('SubID:Disable'):
+ raise errors.ValidationError(
+ name="configuration state",
+ error=_("Support for subordinate IDs is disabled"))
+
ldap = self.obj.backend
dna_remaining = self.get_remaining_dna(ldap, **options)
baseid, rangesize = self.get_idrange(ldap, **options)
diff --git a/ipaserver/plugins/user.py b/ipaserver/plugins/user.py
index a3e9c29035161af40c29093b3792f2d97847e5d1..875f2b4babc526359d76778321ba7402198acac9 100644
--- a/ipaserver/plugins/user.py
+++ b/ipaserver/plugins/user.py
@@ -718,7 +718,9 @@ class user_add(baseuser_add):
default_subid = config.single_value.get(
'ipaUserDefaultSubordinateId', False
)
- if default_subid:
+ subid_disabled = self.api.Object.config.is_config_option_present(
+ 'SubID:Disable')
+ if default_subid and not subid_disabled:
result = self.api.Command.subid_generate(
ipaowner=entry_attrs.single_value['uid'],
version=options['version']
--
2.48.1

View File

@ -1,33 +0,0 @@
From b8b91dfe71d7d049f5e55a8195cb37f87837bbce Mon Sep 17 00:00:00 2001
From: Alexander Bokovoy <abokovoy@redhat.com>
Date: Wed, 12 Mar 2025 21:52:56 +0200
Subject: [PATCH] update_dna_shared_config: do not fail when config is not
found
The helper function was supposed to return a DN or None.
Related: https://pagure.io/freeipa/issue/9757
Signed-off-by: Alexander Bokovoy <abokovoy@redhat.com>
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
---
ipaserver/install/plugins/update_dna_shared_config.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/ipaserver/install/plugins/update_dna_shared_config.py b/ipaserver/install/plugins/update_dna_shared_config.py
index 955bee5dd830f0dcad3f0810e7e2f1a1c725a0aa..42ee86d8b547fa9d6fb4cced5e36d243ba8cd4ff 100644
--- a/ipaserver/install/plugins/update_dna_shared_config.py
+++ b/ipaserver/install/plugins/update_dna_shared_config.py
@@ -49,7 +49,7 @@ class update_dna_shared_config(Updater):
except errors.NotFound:
logger.error("Could not find DNA config entry: %s",
dna_config_base)
- return False, ()
+ return None
else:
logger.debug('Found DNA config %s', dna_config_base)
--
2.48.1

View File

@ -1,71 +0,0 @@
From 65cb358c01568e9a11899dbfe21eaeb916af3cdf Mon Sep 17 00:00:00 2001
From: Alexander Bokovoy <abokovoy@redhat.com>
Date: Fri, 28 Feb 2025 15:34:12 +0200
Subject: [PATCH] baseuser: allow uidNumber and gidNumber of 32-bit range
JSON format allows to encode integers up to 2^53-1. Linux systems allow
for 32-bit IDs. Permit setting full 32-bit uidNumber and gidNumber
through IPA API. Administrators already can set 32-bit IDs via LDAP.
ID Range also needs to permit larger sizes of RID bases. SIDGEN plugin
already treats RID bases as 1..MAX_UINT32.
Fixes: https://pagure.io/freeipa/issue/9757
Signed-off-by: Alexander Bokovoy <abokovoy@redhat.com>
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
---
ipaserver/plugins/baseuser.py | 4 +++-
ipaserver/plugins/idrange.py | 4 ++++
2 files changed, 7 insertions(+), 1 deletion(-)
diff --git a/ipaserver/plugins/baseuser.py b/ipaserver/plugins/baseuser.py
index b66016305276f7f66d2e9dd4c7946cf49ec5cd96..22393b8f6c5d3e40b57f11947d0a0358d3a087bc 100644
--- a/ipaserver/plugins/baseuser.py
+++ b/ipaserver/plugins/baseuser.py
@@ -26,7 +26,7 @@ import six
from ipalib import api, errors, constants
from ipalib import (
Flag, Int, Password, Str, Bool, StrEnum, DateTime, DNParam)
-from ipalib.parameters import Principal, Certificate
+from ipalib.parameters import Principal, Certificate, MAX_UINT32
from ipalib.plugable import Registry
from .baseldap import (
DN, LDAPObject, LDAPCreate, LDAPUpdate, LDAPSearch, LDAPDelete,
@@ -348,11 +348,13 @@ class baseuser(LDAPObject):
label=_('UID'),
doc=_('User ID Number (system will assign one if not provided)'),
minvalue=1,
+ maxvalue=MAX_UINT32,
),
Int('gidnumber?',
label=_('GID'),
doc=_('Group ID Number'),
minvalue=1,
+ maxvalue=MAX_UINT32,
),
Str('street?',
cli_name='street',
diff --git a/ipaserver/plugins/idrange.py b/ipaserver/plugins/idrange.py
index ec061a455ca26aa7b5354b5b4cc8318e2559d5af..26a3bb666273013912e80d49b56031869157375a 100644
--- a/ipaserver/plugins/idrange.py
+++ b/ipaserver/plugins/idrange.py
@@ -235,10 +235,14 @@ class idrange(LDAPObject):
Int('ipabaserid?',
cli_name='rid_base',
label=_('First RID of the corresponding RID range'),
+ minvalue=1,
+ maxvalue=Int.MAX_UINT32
),
Int('ipasecondarybaserid?',
cli_name='secondary_rid_base',
label=_('First RID of the secondary RID range'),
+ minvalue=1,
+ maxvalue=Int.MAX_UINT32
),
Str('ipanttrusteddomainsid?',
cli_name='dom_sid',
--
2.48.1

View File

@ -1,133 +0,0 @@
From 015d26bab4296dc18e97dd10054a3f668282ef88 Mon Sep 17 00:00:00 2001
From: Alexander Bokovoy <abokovoy@redhat.com>
Date: Wed, 5 Mar 2025 12:49:27 +0200
Subject: [PATCH] ipatests: add a test to use full 32-bit ID range space
The test reconfigures IPA deployment to disable subordinate IDs support
and then configures an additional ID range to cover upper half of the
2^32 ID space. It then makes sure that a user with an UID/GID from that
ID range can be created and used.
Fixes: https://pagure.io/freeipa/issue/9757
Signed-off-by: Alexander Bokovoy <abokovoy@redhat.com>
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
---
.../test_integration/test_32bit_idranges.py | 104 ++++++++++++++++++
1 file changed, 104 insertions(+)
create mode 100644 ipatests/test_integration/test_32bit_idranges.py
diff --git a/ipatests/test_integration/test_32bit_idranges.py b/ipatests/test_integration/test_32bit_idranges.py
new file mode 100644
index 0000000000000000000000000000000000000000..e76e117e5f1627af02274a13d3ac12ca84eb7ad9
--- /dev/null
+++ b/ipatests/test_integration/test_32bit_idranges.py
@@ -0,0 +1,104 @@
+#
+# Copyright (C) 2025 FreeIPA Contributors see COPYING for license
+#
+
+from __future__ import absolute_import
+
+from ipatests.pytest_ipa.integration import tasks
+from ipatests.test_integration.base import IntegrationTest
+
+
+class Test32BitIdRanges(IntegrationTest):
+ topology = "line"
+
+ def test_remove_subid_range(self):
+ """
+ Test that allocating subid will fail after disabling global option
+ """
+ master = self.master
+ tasks.kinit_admin(master)
+
+ idrange = f"{master.domain.realm}_subid_range"
+ master.run_command(
+ ["ipa", "config-mod", "--addattr", "ipaconfigstring=SubID:Disable"]
+ )
+ master.run_command(["ipa", "idrange-del", idrange])
+
+ tasks.user_add(master, 'subiduser')
+ result = master.run_command(
+ ["ipa", "subid-generate", "--owner", "subiduser"], raiseonerr=False
+ )
+ assert result.returncode > 0
+ assert "Support for subordinate IDs is disabled" in result.stderr_text
+ tasks.user_del(master, 'subiduser')
+
+ def test_invoke_upgrader(self):
+ """Test that ipa-server-upgrade does not add subid ranges back"""
+
+ master = self.master
+ master.run_command(['ipa-server-upgrade'], raiseonerr=True)
+ idrange = f"{master.domain.realm}_subid_range"
+ result = master.run_command(
+ ["ipa", "idrange-show", idrange], raiseonerr=False
+ )
+ assert result.returncode > 0
+ assert f"{idrange}: range not found" in result.stderr_text
+
+ result = tasks.ldapsearch_dm(
+ master,
+ 'cn=Subordinate IDs,cn=Distributed Numeric Assignment Plugin,'
+ 'cn=plugins,cn=config',
+ ['dnaType'],
+ scope='base',
+ raiseonerr=False
+ )
+ assert result.returncode == 32
+ output = result.stdout_text.lower()
+ assert "dnatype: " not in output
+
+ def test_create_user_with_32bit_id(self):
+ """Test that ID range above 2^31 can be used to assign IDs
+ to users and groups. Also check that SIDs generated properly.
+ """
+
+ master = self.master
+ idrange = f"{master.domain.realm}_upper_32bit_range"
+ id_base = 1 << 31
+ id_length = (1 << 31) - 2
+ uid = id_base + 1
+ gid = id_base + 1
+ master.run_command(
+ [
+ "ipa",
+ "idrange-add",
+ idrange,
+ "--base-id", str(id_base),
+ "--range-size", str(id_length),
+ "--rid-base", str(int(id_base >> 3)),
+ "--secondary-rid-base", str(int(id_base >> 3) + id_length),
+ "--type=ipa-local"
+ ]
+ )
+
+ # We added new ID range, SIDGEN will only take it after
+ # restarting a directory server instance.
+ tasks.restart_ipa_server(master)
+
+ # Clear SSSD cache to pick up new ID range
+ tasks.clear_sssd_cache(master)
+
+ tasks.user_add(master, "user", extra_args=[
+ "--uid", str(uid), "--gid", str(gid)
+ ])
+
+ result = master.run_command(
+ ["ipa", "user-show", "user", "--all", "--raw"], raiseonerr=False
+ )
+ assert result.returncode == 0
+ assert "ipaNTSecurityIdentifier:" in result.stdout_text
+
+ result = master.run_command(
+ ["id", "user"], raiseonerr=False
+ )
+ assert result.returncode == 0
+ assert str(uid) in result.stdout_text
--
2.48.1

View File

@ -1,41 +0,0 @@
From 01f23216ab5b383710dad086a01bb73b2da383d1 Mon Sep 17 00:00:00 2001
From: Florence Blanc-Renaud <flo@redhat.com>
Date: Mon, 17 Mar 2025 16:21:23 +0100
Subject: [PATCH] idrange: use minvalue=0 for baserid and secondarybaserid
With the support of 32 bit idrange, the minvalue was set to 1
but this introduces a regression in the command ipa trust-add
as the range for AD trust is added with baserid=0
Lower the minvalue to 0
Fixes: https://pagure.io/freeipa/issue/9765
Signed-off-by: Florence Blanc-Renaud <flo@redhat.com>
Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
---
ipaserver/plugins/idrange.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/ipaserver/plugins/idrange.py b/ipaserver/plugins/idrange.py
index 26a3bb666273013912e80d49b56031869157375a..d155fb46da8240449a077d35e86a91ee9f95c132 100644
--- a/ipaserver/plugins/idrange.py
+++ b/ipaserver/plugins/idrange.py
@@ -235,13 +235,13 @@ class idrange(LDAPObject):
Int('ipabaserid?',
cli_name='rid_base',
label=_('First RID of the corresponding RID range'),
- minvalue=1,
+ minvalue=0,
maxvalue=Int.MAX_UINT32
),
Int('ipasecondarybaserid?',
cli_name='secondary_rid_base',
label=_('First RID of the secondary RID range'),
- minvalue=1,
+ minvalue=0,
maxvalue=Int.MAX_UINT32
),
Str('ipanttrusteddomainsid?',
--
2.48.1

View File

@ -1,190 +0,0 @@
From 47770b8626c353b95d4ae89a0fb7e23b3791d3ea Mon Sep 17 00:00:00 2001
From: Sudhir Menon <sumenon@redhat.com>
Date: Wed, 22 Jan 2025 16:03:37 +0530
Subject: [PATCH] ipatests: Tests to check data in journal log
This testcase checks that ipa administrative user
password is not displayed in journal log.
Related: https://issues.redhat.com/browse/RHEL-67190
Signed-off-by: Sudhir Menon <sumenon@redhat.com>
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
---
ipatests/pytest_ipa/integration/tasks.py | 10 ++
ipatests/test_integration/test_commands.py | 116 +++++++++++++++++----
2 files changed, 104 insertions(+), 22 deletions(-)
diff --git a/ipatests/pytest_ipa/integration/tasks.py b/ipatests/pytest_ipa/integration/tasks.py
index 4ce33bb47cbc52641088f73cdb75d7bb184c274b..dccfaf30e708f18c81d3f1662d6df7b116ed36ac 100755
--- a/ipatests/pytest_ipa/integration/tasks.py
+++ b/ipatests/pytest_ipa/integration/tasks.py
@@ -3004,3 +3004,13 @@ def copy_files(source_host, dest_host, filelist):
dest_host.transport.mkdir_recursive(os.path.dirname(file))
data = source_host.get_file_contents(file)
dest_host.transport.put_file_contents(file, data)
+
+
+def check_journal_does_not_contain_secret(host, cmd):
+ """
+ Helper to check journal logs doesnt reveal secrets
+ """
+ journalctl_cmd = ['journalctl', '-t', cmd, '-n1', '-o', 'json-pretty']
+ result = host.run_command(journalctl_cmd, raiseonerr=False)
+ assert (host.config.admin_password not in result.stdout_text)
+ assert (host.config.dirman_password not in result.stdout_text)
diff --git a/ipatests/test_integration/test_commands.py b/ipatests/test_integration/test_commands.py
index 9c65b7c6bbf4c6378bdf0fa9da0242805ddd17aa..47ef232563d67f86040e2c5944805e430ab2e26c 100644
--- a/ipatests/test_integration/test_commands.py
+++ b/ipatests/test_integration/test_commands.py
@@ -39,6 +39,7 @@ from ipaplatform.tasks import tasks as platform_tasks
from ipatests.create_external_ca import ExternalCA
from ipatests.test_ipalib.test_x509 import good_pkcs7, badcert
from ipapython.ipautil import realm_to_suffix, ipa_generate_password
+from ipatests.test_integration.test_topology import find_segment
from ipaserver.install.installutils import realm_to_serverid
from pkg_resources import parse_version
@@ -1662,28 +1663,77 @@ class TestIPACommand(IntegrationTest):
assert result.returncode == 1
assert 'cannot be deleted or disabled' in result.stderr_text
- def test_ipa_cacert_manage_prune(self):
- """Test for ipa-cacert-manage prune"""
-
- certfile = os.path.join(self.master.config.test_dir, 'cert.pem')
- self.master.put_file_contents(certfile, isrgrootx1)
- result = self.master.run_command(
- [paths.IPA_CACERT_MANAGE, 'install', certfile])
-
- certs_before_prune = self.master.run_command(
- [paths.IPA_CACERT_MANAGE, 'list'], raiseonerr=False
- ).stdout_text
+ def test_ipa_systemd_journal(self):
+ """
+ This testcase checks that administrative user credentials
+ is not leaked to journald log
+ """
+ tasks.kinit_admin(self.master)
+ tasks.kinit_admin(self.replicas[0])
+ tasks.kinit_admin(self.clients[0])
+ cmds = [
+ ['/usr/sbin/ipa-adtrust-install', '-a',
+ self.master.config.admin_password, '-U'],
+ ['/usr/sbin/ipa-replica-manage', 'del',
+ f"dummyhost.{self.master.domain.name}", '-p',
+ self.master.config.dirman_password],
+ ['/usr/sbin/ipa-csreplica-manage', 'del',
+ f"dummyhost.{self.master.domain.name}", '-p',
+ self.master.config.dirman_password],
+ ['/usr/sbin/ipa-kra-install', '-p',
+ self.master.config.dirman_password, '-U'],
+ ['/usr/sbin/ipa-server-certinstall', '-k', '--pin',
+ self.master.config.dirman_password, '-p',
+ self.master.config.dirman_password, paths.KDC_CERT,
+ paths.KDC_KEY]
+ ]
+ for cmd in cmds:
+ self.master.run_command(cmd, raiseonerr=False)
+ tasks.check_journal_does_not_contain_secret(
+ self.master, cmd[0]
+ )
+ for cmd in cmds:
+ self.replicas[0].run_command(cmd, raiseonerr=False)
+ tasks.check_journal_does_not_contain_secret(
+ self.replicas[0], cmd[0]
+ )
+ tasks.check_journal_does_not_contain_secret(
+ self.clients[0], 'python3'
+ )
+ # Backup and restore IPA and check secrets are not leaked.
+ backup_path = tasks.get_backup_dir(self.master)
+ restore_cmd = (
+ ['/usr/sbin/ipa-restore', '-p',
+ self.master.config.dirman_password,
+ backup_path, '-U']
+ )
+ self.master.run_command(restore_cmd)
- assert isrgrootx1_nick in certs_before_prune
+ # re-initializing topology after restore
+ for topo_suffix in 'domain', 'ca':
+ topo_name = find_segment(self.master, self.replicas[0], topo_suffix)
+ arg = ['ipa', 'topologysegment-reinitialize',
+ topo_suffix, topo_name]
+ if topo_name.split('-to-', maxsplit=1)[0] != self.master.hostname:
+ arg.append('--left')
+ else:
+ arg.append('--right')
+ self.replicas[0].run_command(arg)
- # Jump in time to make sure the cert is expired
- self.master.run_command(['date', '-s', '+15Years'])
- result = self.master.run_command(
- [paths.IPA_CACERT_MANAGE, 'prune'], raiseonerr=False
- ).stdout_text
- self.master.run_command(['date', '-s', '-15Years'])
+ # wait sometime for re-initialization
+ tasks.wait_for_replication(self.replicas[0].ldap_connect())
- assert isrgrootx1_nick in result
+ tasks.check_journal_does_not_contain_secret(
+ self.master, restore_cmd[0]
+ )
+ # Checking for secrets in IPA server install
+ tasks.check_journal_does_not_contain_secret(
+ self.master, '/usr/sbin/ipa-server-install'
+ )
+ # Checking for secrets in IPA replica install
+ tasks.check_journal_does_not_contain_secret(
+ self.replicas[0], '/usr/sbin/ipa-replica-install'
+ )
class TestIPACommandWithoutReplica(IntegrationTest):
@@ -1719,10 +1769,9 @@ class TestIPACommandWithoutReplica(IntegrationTest):
self.master.run_command(['ipa', 'user-show', 'ipauser1'])
def test_basesearch_compat_tree(self):
- """Test ldapsearch against compat tree is working
-
+ """
+ Test ldapsearch against compat tree is working
This to ensure that ldapsearch with base scope is not failing.
-
related: https://bugzilla.redhat.com/show_bug.cgi?id=1958909
"""
version = self.master.run_command(
@@ -1920,6 +1969,29 @@ class TestIPACommandWithoutReplica(IntegrationTest):
assert old_err_msg not in dirsrv_error_log
assert re.search(new_err_msg, dirsrv_error_log)
+ def test_ipa_cacert_manage_prune(self):
+ """Test for ipa-cacert-manage prune"""
+
+ certfile = os.path.join(self.master.config.test_dir, 'cert.pem')
+ self.master.put_file_contents(certfile, isrgrootx1)
+ result = self.master.run_command(
+ [paths.IPA_CACERT_MANAGE, 'install', certfile])
+
+ certs_before_prune = self.master.run_command(
+ [paths.IPA_CACERT_MANAGE, 'list'], raiseonerr=False
+ ).stdout_text
+
+ assert isrgrootx1_nick in certs_before_prune
+
+ # Jump in time to make sure the cert is expired
+ self.master.run_command(['date', '-s', '+15Years'])
+ result = self.master.run_command(
+ [paths.IPA_CACERT_MANAGE, 'prune'], raiseonerr=False
+ ).stdout_text
+ self.master.run_command(['date', '-s', '-15Years'])
+
+ assert isrgrootx1_nick in result
+
class TestIPAautomount(IntegrationTest):
@classmethod
--
2.48.1

View File

@ -1,69 +0,0 @@
From ac308ab8f5685465e755b4ba7e5d428fe38bea4d Mon Sep 17 00:00:00 2001
From: David Hanina <dhanina@redhat.com>
Date: Mon, 17 Mar 2025 09:26:44 +0100
Subject: [PATCH] Disallow removal of dogtag and ipa-dnskeysyncd services on
IPA servers
Also removes dogtagldap from unremovable services
Fixes: https://pagure.io/freeipa/issue/9764
Signed-off-by: David Hanina <dhanina@redhat.com>
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
---
ipaserver/plugins/service.py | 2 +-
ipatests/test_xmlrpc/test_service_plugin.py | 26 +++++++++++++++++++++
2 files changed, 27 insertions(+), 1 deletion(-)
diff --git a/ipaserver/plugins/service.py b/ipaserver/plugins/service.py
index 075a1be8aab5d638cb632b64e766231d3761f731..f50406472a7c1d636bd8731dc550c0d850b2264d 100644
--- a/ipaserver/plugins/service.py
+++ b/ipaserver/plugins/service.py
@@ -323,7 +323,7 @@ def check_required_principal(ldap, principal):
try:
host_is_master(ldap, principal.hostname)
except errors.ValidationError:
- service_types = {'http', 'ldap', 'dns', 'dogtagldap'}
+ service_types = {'http', 'ldap', 'dns', 'dogtag', 'ipa-dnskeysyncd'}
if principal.service_name.lower() in service_types:
raise errors.ValidationError(
name='principal',
diff --git a/ipatests/test_xmlrpc/test_service_plugin.py b/ipatests/test_xmlrpc/test_service_plugin.py
index a3b245679a224572a999354bc7d63360b1f06eed..4aeeb9d89971a56a2ccfccd616b15392f5f0e0ee 100644
--- a/ipatests/test_xmlrpc/test_service_plugin.py
+++ b/ipatests/test_xmlrpc/test_service_plugin.py
@@ -864,6 +864,32 @@ class test_service(Declarative):
),
),
+ dict(
+ desc=('Delete the current host (master?) %s dogtag service,'
+ ' should be caught' % api.env.host),
+ command=('service_del', ['dogtag/%s' % api.env.host], {}),
+ expected=errors.ValidationError(
+ name='principal',
+ error='dogtag/%s@%s is required by the IPA master' % (
+ api.env.host,
+ api.env.realm
+ )
+ ),
+ ),
+
+ dict(
+ desc=('Delete the current host (master?) %s ipa-dnskeysyncd'
+ ' service, should be caught' % api.env.host),
+ command=('service_del', ['ipa-dnskeysyncd/%s' % api.env.host], {}),
+ expected=errors.ValidationError(
+ name='principal',
+ error='ipa-dnskeysyncd/%s@%s is required by the IPA master' % (
+ api.env.host,
+ api.env.realm
+ )
+ ),
+ ),
+
dict(
desc='Disable the current host (master?) %s HTTP service, should be caught' % api.env.host,
--
2.48.1

View File

@ -1,888 +0,0 @@
From 722a5a4e0f0c6948252d385da4ffef7c03338aec Mon Sep 17 00:00:00 2001
From: Rob Crittenden <rcritten@redhat.com>
Date: Thu, 8 Aug 2024 16:48:19 -0400
Subject: [PATCH] Don't require certificates to have unique ipaCertSubject
In the wild a public CA issued a new subordinate CA certificate
with an identical subject to another, with a new private key.
This was uninstallable using ipa-cacert-manage because it would
fail with "subject public key info mismatch" during verification
because a different certificate with the same subject but
different public key was installed.
I'm not sure of the reasoning to prevent this situation but I
see it as giving users flexibility. This may be hurtful to them
but they can always remove any affected certs.
This is backwards compatible with older releases from the client
perspective. Older servers will choke on the duplicates and
won't be able to manage these.
A new serial number option is added for displaying the list of
certificates and for use when deleting one with a duplicate subject.
ipa-cacert-manage delete on systems without this patch will
successfully remove ALL of the requested certificates. There is no
way to distinguish. At least it won't break anything and the
deleted certificates can be re-added.
Fixes: https://pagure.io/freeipa/issue/9652
Signed-off-by: Rob Crittenden <rcritten@redhat.com>
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
---
install/restart_scripts/renew_ca_cert.in | 4 +-
install/tools/man/ipa-cacert-manage.1 | 11 ++
install/updates/10-uniqueness.update | 21 +--
ipaclient/install/client.py | 6 +-
ipaclient/install/ipa_certupdate.py | 2 +-
ipalib/install/certstore.py | 46 +++++-
ipaplatform/debian/tasks.py | 2 +-
ipaplatform/redhat/tasks.py | 2 +-
ipapython/certdb.py | 110 ++++++++------
ipaserver/install/certs.py | 7 +-
ipaserver/install/installutils.py | 3 +-
ipaserver/install/ipa_cacert_manage.py | 82 +++++++----
ipaserver/install/ipa_server_certinstall.py | 5 +-
ipaserver/install/krbinstance.py | 2 +-
ipaserver/install/service.py | 4 +-
ipatests/test_integration/test_commands.py | 151 +++++++++++++++++++-
16 files changed, 351 insertions(+), 107 deletions(-)
diff --git a/install/restart_scripts/renew_ca_cert.in b/install/restart_scripts/renew_ca_cert.in
index cbb2c89a81e15bf02c09d7d4328a866cb77f8837..814acdaa772acbf241a8169e273d5b7bbb5773b8 100644
--- a/install/restart_scripts/renew_ca_cert.in
+++ b/install/restart_scripts/renew_ca_cert.in
@@ -168,7 +168,7 @@ def _main():
ca_certs = []
realm_nickname = get_ca_nickname(api.env.realm)
- for ca_cert, ca_nick, ca_flags in ca_certs:
+ for ca_cert, ca_nick, ca_flags, _serial in ca_certs:
try:
if ca_nick == realm_nickname:
ca_nick = 'caSigningCert cert-pki-ca'
@@ -180,7 +180,7 @@ def _main():
# Pass Dogtag's self-tests
for ca_nick in db.find_root_cert(nickname)[-2:-1]:
- ca_flags = dict(cc[1:] for cc in ca_certs)[ca_nick]
+ ca_flags = dict(cc[1:3] for cc in ca_certs)[ca_nick]
usages = ca_flags.usages or set()
ca_flags_modified = TrustFlags(ca_flags.has_key,
True, True,
diff --git a/install/tools/man/ipa-cacert-manage.1 b/install/tools/man/ipa-cacert-manage.1
index 8913fe5d2abab5e5b78cf51abd4fae5d7a0ad78f..1e15d47a55492b31c3198496050508dec3fb6a82 100644
--- a/install/tools/man/ipa-cacert-manage.1
+++ b/install/tools/man/ipa-cacert-manage.1
@@ -57,6 +57,14 @@ Important: this does not replace IPA CA but adds the provided certificate as a k
Please do not forget to run ipa-certupdate on the master, all the replicas and all the clients after this command in order to update IPA certificates databases.
.sp
The supported formats for the certificate files are DER, PEM and PKCS#7 format.
+.sp
+CA certificates with the same subject but different private keys maybe installed simultaneously with the following restrictions from NSS:
+.IP \[bu]
+The certificates cannot have different NSS trust flags.
+.IP \[bu]
+The nickname is not configurable between different certificates of the same subject. It will always be the same (even if you try).
+.sp
+Additionally CA certificates with the same subject should include the Authority Key Identifier extension in order to identify the public key of the certificate issuer (CA) that signed the certificate (it may be itself). Similarly it should have a Subject Key Identifier extension. This is used to create the trust chain not through subjects but by using the SKID and AKID which is what allows duplicate certificate subjects to be resolved correctly. Without an AKID multiple certificates of the same subject will not resolve as expected.
.RE
.TP
\fBdelete\fR
@@ -153,6 +161,9 @@ p \- not trusted
.TP
\fB\-f\fR, \fB\-\-force\fR
Force a CA certificate to be removed even if chain validation fails.
+.TP
+\fB\-s\fR \fISERIAL_NUMBER\fR, \fB\-\-serial\fR=\fISERIAL_NUMBER\fR
+Serial number of the certificate to delete (decimal). This is needed to determine which certificate to remove if there are multiple certificates stored with the same name.
.SH "EXIT STATUS"
0 if the command was successful
diff --git a/install/updates/10-uniqueness.update b/install/updates/10-uniqueness.update
index 699de3b4d3305def5d81aeb945106b80eef0ef40..fa17911f2ed9c7bcaa851de0f0a4790a550e1c91 100644
--- a/install/updates/10-uniqueness.update
+++ b/install/updates/10-uniqueness.update
@@ -15,23 +15,6 @@ default:nsslapd-pluginId: NSUniqueAttr
default:nsslapd-pluginVersion: 1.1.0
default:nsslapd-pluginVendor: Fedora Project
-dn: cn=certificate store subject uniqueness,cn=plugins,cn=config
-default:objectClass: top
-default:objectClass: nsSlapdPlugin
-default:objectClass: extensibleObject
-default:cn: certificate store subject uniqueness
-default:nsslapd-pluginDescription: Enforce unique attribute values
-default:nsslapd-pluginPath: libattr-unique-plugin
-default:nsslapd-pluginInitfunc: NSUniqueAttr_Init
-default:nsslapd-pluginType: preoperation
-default:nsslapd-pluginEnabled: on
-default:uniqueness-attribute-name: ipaCertSubject
-default:uniqueness-subtrees: cn=certificates,cn=ipa,cn=etc,$SUFFIX
-default:nsslapd-plugin-depends-on-type: database
-default:nsslapd-pluginId: NSUniqueAttr
-default:nsslapd-pluginVersion: 1.1.0
-default:nsslapd-pluginVendor: Fedora Project
-
dn: cn=certificate store issuer/serial uniqueness,cn=plugins,cn=config
default:objectClass: top
default:objectClass: nsSlapdPlugin
@@ -128,3 +111,7 @@ default:nsslapd-plugin-depends-on-type: database
default:nsslapd-pluginId: NSUniqueAttr
default:nsslapd-pluginVersion: 1.1.0
default:nsslapd-pluginVendor: Fedora Project
+
+# A unique ipaCertSubject is no longer required
+dn: cn=certificate store subject uniqueness,cn=plugins,cn=config
+deleteentry: cn=certificate store subject uniqueness,cn=plugins,cn=config
diff --git a/ipaclient/install/client.py b/ipaclient/install/client.py
index 9e4d3bbe70826a18c55f4c4abe0ff1d42b0b509d..372daa51e4647023dde76e183189eeebdd9525b8 100644
--- a/ipaclient/install/client.py
+++ b/ipaclient/install/client.py
@@ -3200,15 +3200,15 @@ def _install(options, tdict):
ca_certs = certstore.make_compat_ca_certs(ca_certs, cli_realm,
ca_subject)
ca_certs_trust = [(c, n, certstore.key_policy_to_trust_flags(t, True, u))
- for (c, n, t, u) in ca_certs]
+ for (c, n, t, u, s) in ca_certs]
x509.write_certificate_list(
- [c for c, n, t, u in ca_certs if t is not False],
+ [c for c, n, t, u, s in ca_certs if t is not False],
paths.KDC_CA_BUNDLE_PEM,
mode=0o644
)
x509.write_certificate_list(
- [c for c, n, t, u in ca_certs if t is not False],
+ [c for c, n, t, u, s in ca_certs if t is not False],
paths.CA_BUNDLE_PEM,
mode=0o644
)
diff --git a/ipaclient/install/ipa_certupdate.py b/ipaclient/install/ipa_certupdate.py
index bc70254e2b34f21889793d34724c13d73882418b..88618a9c23265e1ab1328361125acc3724cd329f 100644
--- a/ipaclient/install/ipa_certupdate.py
+++ b/ipaclient/install/ipa_certupdate.py
@@ -276,7 +276,7 @@ def update_db(path, certs):
for name, flags in db.list_certs():
if flags.ca:
db.delete_cert(name)
- for cert, nickname, trusted, eku in certs:
+ for cert, nickname, trusted, eku, _serial in certs:
trust_flags = certstore.key_policy_to_trust_flags(trusted, True, eku)
try:
db.add_cert(cert, nickname, trust_flags)
diff --git a/ipalib/install/certstore.py b/ipalib/install/certstore.py
index 8b182958c26e066eaeca859f451073c83e82bd67..fb4f09a2b44aa5b65efb6e10afcd7c515535e56b 100644
--- a/ipalib/install/certstore.py
+++ b/ipalib/install/certstore.py
@@ -179,10 +179,9 @@ def update_ca_cert(ldap, base_dn, cert, trusted=None, ext_key_usage=None,
# We are adding a new cert, validate it
if entry.single_value['ipaCertSubject'].lower() != subject.lower():
raise ValueError("subject name mismatch")
- if entry.single_value['ipaPublicKey'] != public_key:
- raise ValueError("subject public key info mismatch")
entry['ipaCertIssuerSerial'].append(issuer_serial)
entry['cACertificate;binary'].append(cert)
+ entry['ipaPublicKey'].append(public_key)
# Update key trust
if trusted is not None:
@@ -224,6 +223,38 @@ def update_ca_cert(ldap, base_dn, cert, trusted=None, ext_key_usage=None,
clean_old_config(ldap, base_dn, dn, config_ipa, config_compat)
+def delete_ca_cert(ldap, base_dn, cert):
+ """
+ Remove a CA certificate in the certificate store.
+ """
+ subject, issuer_serial, _public_key = _parse_cert(cert)
+
+ filter = ldap.make_filter({'ipaCertSubject': subject})
+ result, _truncated = ldap.find_entries(
+ base_dn=DN(('cn', 'certificates'), ('cn', 'ipa'), ('cn', 'etc'),
+ base_dn),
+ filter=filter,
+ attrs_list=['cn', 'ipaCertSubject', 'ipaCertIssuerSerial',
+ 'ipaPublicKey', 'ipaKeyTrust', 'ipaKeyExtUsage',
+ 'ipaConfigString', 'cACertificate;binary'])
+ entry = result[0]
+
+ for old_cert in entry['cACertificate;binary']:
+ # Check if we are adding a new cert
+ if old_cert == cert:
+ break
+ else:
+ raise ValueError("certificate not found")
+
+ entry['ipaCertIssuerSerial'].remove(issuer_serial)
+ entry['cACertificate;binary'].remove(cert)
+
+ if len(entry['ipaCertIssuerSerial']) == 0:
+ ldap.delete_entry(entry.dn)
+ else:
+ ldap.update_entry(entry)
+
+
def put_ca_cert(ldap, base_dn, cert, nickname, trusted=None,
ext_key_usage=None, config_ipa=False, config_compat=False):
"""
@@ -309,11 +340,14 @@ def get_ca_certs(ldap, base_dn, compat_realm, compat_ipa_ca,
for cert in entry.get('cACertificate;binary', []):
try:
- _parse_cert(cert)
+ _subject, issuer_serial, _pkinfo = _parse_cert(cert)
except ValueError:
certs = []
break
- certs.append((cert, nickname, trusted, ext_key_usage))
+ serial_number = issuer_serial.split(';')[1]
+ certs.append(
+ (cert, nickname, trusted, ext_key_usage, serial_number)
+ )
except errors.NotFound:
try:
ldap.get_entry(container_dn, [''])
@@ -381,9 +415,9 @@ def get_ca_certs_nss(ldap, base_dn, compat_realm, compat_ipa_ca,
certs = get_ca_certs(ldap, base_dn, compat_realm, compat_ipa_ca,
filter_subject=filter_subject)
- for cert, nickname, trusted, ext_key_usage in certs:
+ for cert, nickname, trusted, ext_key_usage, _serial_number in certs:
trust_flags = key_policy_to_trust_flags(trusted, True, ext_key_usage)
- nss_certs.append((cert, nickname, trust_flags))
+ nss_certs.append((cert, nickname, trust_flags, _serial_number))
return nss_certs
diff --git a/ipaplatform/debian/tasks.py b/ipaplatform/debian/tasks.py
index a7b5cdf38d23669bd8beaa9b85020355eaeb2af2..8a50c66bc1facac4a793db209d54fc59049a94c0 100644
--- a/ipaplatform/debian/tasks.py
+++ b/ipaplatform/debian/tasks.py
@@ -126,7 +126,7 @@ used by ca-certificates and is provided for information only.\
logger.error("Could not create %s", path)
raise
- for cert, nickname, trusted, _ext_key_usage in ca_certs:
+ for cert, nickname, trusted, _ext_key_usage, _serial in ca_certs:
if not trusted:
continue
diff --git a/ipaplatform/redhat/tasks.py b/ipaplatform/redhat/tasks.py
index 4fb6208073d7326b80042408fff98e4124e0dbed..d3eda01720655df4bebb317d636621a3dee9a24d 100644
--- a/ipaplatform/redhat/tasks.py
+++ b/ipaplatform/redhat/tasks.py
@@ -329,7 +329,7 @@ class RedHatTaskNamespace(BaseTaskNamespace):
raise
has_eku = set()
- for cert, nickname, trusted, _ext_key_usage in ca_certs:
+ for cert, nickname, trusted, _ext_key_usage, _serial in ca_certs:
try:
subject = cert.subject_bytes
issuer = cert.issuer_bytes
diff --git a/ipapython/certdb.py b/ipapython/certdb.py
index ec8f639051b0d4134fccc1c02aff6b4f3b43ebce..3314c3a03d815cc69a2c9036d434a00391dc378f 100644
--- a/ipapython/certdb.py
+++ b/ipapython/certdb.py
@@ -633,7 +633,7 @@ class NSSDatabase:
pkcs12_password_file.close()
def import_files(self, files, import_keys=False, key_password=None,
- key_nickname=None):
+ key_nickname=None, trust_flags=EMPTY_TRUST_FLAGS):
"""
Import certificates and a single private key from multiple files
@@ -809,7 +809,7 @@ class NSSDatabase:
for cert in extracted_certs:
nickname = str(DN(cert.subject))
- self.add_cert(cert, nickname, EMPTY_TRUST_FLAGS)
+ self.add_cert(cert, nickname, trust_flags)
if extracted_key:
with tempfile.NamedTemporaryFile() as in_file, \
@@ -867,6 +867,27 @@ class NSSDatabase:
cert, _start = find_cert_from_txt(result.output, start=0)
return cert
+ def get_all_certs(self, nickname):
+ """
+ :param nickname: nickname of the certificate in the NSS database
+ :returns: list of bytes of all certificates for the nickname
+ """
+ args = ['-L', '-n', nickname, '-a']
+ try:
+ result = self.run_certutil(args, capture_output=True)
+ except ipautil.CalledProcessError:
+ raise RuntimeError("Failed to get %s" % nickname)
+ certs = []
+
+ st = 0
+ while True:
+ try:
+ cert, st = find_cert_from_txt(result.output, start=st)
+ except RuntimeError:
+ break
+ certs.append(cert)
+ return certs
+
def has_nickname(self, nickname):
try:
self.get_cert(nickname)
@@ -990,53 +1011,58 @@ class NSSDatabase:
raise ValueError('invalid for server %s' % hostname)
def verify_ca_cert_validity(self, nickname, minpathlen=None):
- cert = self.get_cert(nickname)
- self._verify_cert_validity(cert)
+ def verify_ca_cert(cert, nickname, minpathlen):
+ self._verify_cert_validity(cert)
- if not cert.subject:
- raise ValueError("has empty subject")
+ if not cert.subject:
+ raise ValueError("has empty subject")
- try:
- bc = cert.extensions.get_extension_for_class(
+ try:
+ bc = cert.extensions.get_extension_for_class(
cryptography.x509.BasicConstraints)
- except cryptography.x509.ExtensionNotFound:
- raise ValueError("missing basic constraints")
-
- if not bc.value.ca:
- raise ValueError("not a CA certificate")
- if minpathlen is not None:
- # path_length is None means no limitation
- pl = bc.value.path_length
- if pl is not None and pl < minpathlen:
- raise ValueError(
- "basic contraint pathlen {}, must be at least {}".format(
- pl, minpathlen
+ except cryptography.x509.ExtensionNotFound:
+ raise ValueError("missing basic constraints")
+
+ if not bc.value.ca:
+ raise ValueError("not a CA certificate")
+ if minpathlen is not None:
+ # path_length is None means no limitation
+ pl = bc.value.path_length
+ if pl is not None and pl < minpathlen:
+ raise ValueError(
+ "basic contraint pathlen {}, "
+ "must be at least {}".format(
+ pl, minpathlen
+ )
)
- )
- try:
- ski = cert.extensions.get_extension_for_class(
+ try:
+ ski = cert.extensions.get_extension_for_class(
cryptography.x509.SubjectKeyIdentifier)
- except cryptography.x509.ExtensionNotFound:
- raise ValueError("missing subject key identifier extension")
- else:
- if len(ski.value.digest) == 0:
- raise ValueError("subject key identifier must not be empty")
+ except cryptography.x509.ExtensionNotFound:
+ raise ValueError("missing subject key identifier extension")
+ else:
+ if len(ski.value.digest) == 0:
+ raise ValueError("subject key identifier must not be empty")
- try:
- self.run_certutil(
- [
- '-V', # check validity of cert and attrs
- '-n', nickname,
- '-u', 'L', # usage; 'L' means "SSL CA"
- '-e', # check signature(s); this checks
- # key sizes, sig algorithm, etc.
- ],
- capture_output=True)
- except ipautil.CalledProcessError as e:
- # certutil output in case of error is
- # 'certutil: certificate is invalid: <ERROR_STRING>\n'
- raise ValueError(e.output)
+ try:
+ self.run_certutil(
+ [
+ '-V', # check validity of cert and attrs
+ '-n', nickname,
+ '-u', 'L', # usage; 'L' means "SSL CA"
+ '-e', # check signature(s); this checks
+ # key sizes, sig algorithm, etc.
+ ],
+ capture_output=True)
+ except ipautil.CalledProcessError as e:
+ # certutil output in case of error is
+ # 'certutil: certificate is invalid: <ERROR_STRING>\n'
+ raise ValueError(e.output)
+
+ certlist = self.get_all_certs(nickname)
+ for cert in certlist:
+ verify_ca_cert(cert, nickname, minpathlen)
def verify_kdc_cert_validity(self, nickname, realm):
nicknames = self.get_trust_chain(nickname)
diff --git a/ipaserver/install/certs.py b/ipaserver/install/certs.py
index f8be1ef0641fa30567f0b95fe8aa00ccc68d27e5..a3e38cdaaf4c6b358ec93c8ca55646086812bf0e 100644
--- a/ipaserver/install/certs.py
+++ b/ipaserver/install/certs.py
@@ -373,7 +373,7 @@ class CertDB:
except RuntimeError:
break
- def get_cert_from_db(self, nickname):
+ def get_cert_from_db(self, nickname, all=False):
"""
Retrieve a certificate from the current NSS database for nickname.
"""
@@ -386,7 +386,10 @@ class CertDB:
if token:
args.extend(['-h', token])
result = self.run_certutil(args, capture_output=True)
- return x509.load_pem_x509_certificate(result.raw_output)
+ if all:
+ return x509.load_certificate_list(result.raw_output)
+ else:
+ return x509.load_pem_x509_certificate(result.raw_output)
except ipautil.CalledProcessError:
return None
diff --git a/ipaserver/install/installutils.py b/ipaserver/install/installutils.py
index 3a31f8a98231bf5a2e0337de7be10b37626add86..f6f06c9a18d75f44e13f5d6bf5a3dc0976dc26a2 100644
--- a/ipaserver/install/installutils.py
+++ b/ipaserver/install/installutils.py
@@ -901,7 +901,8 @@ def load_pkcs12(cert_files, key_password, key_nickname, ca_cert_files,
if ca_cert_files:
try:
- nssdb.import_files(ca_cert_files)
+ nssdb.import_files(ca_cert_files,
+ trust_flags=EXTERNAL_CA_TRUST_FLAGS)
except RuntimeError as e:
raise ScriptError(str(e))
diff --git a/ipaserver/install/ipa_cacert_manage.py b/ipaserver/install/ipa_cacert_manage.py
index 048245237855212afe1f3ec4795b2253026ef864..6a03fa74e34bd068090ae1f4d5adfc79608fd02b 100644
--- a/ipaserver/install/ipa_cacert_manage.py
+++ b/ipaserver/install/ipa_cacert_manage.py
@@ -101,6 +101,9 @@ class CACertManage(admintool.AdminTool):
delete_group.add_option(
"-f", "--force", action='store_true',
help="Force removing the CA even if chain validation fails")
+ delete_group.add_option(
+ "-s", "--serial",
+ help="Serial number of the certificate to delete (decimal)")
parser.add_option_group(delete_group)
def validate_options(self):
@@ -413,6 +416,11 @@ class CACertManage(admintool.AdminTool):
"Nickname can only be used if only a single "
"certificate is loaded")
+ for nickname, trust_flags in imported:
+ if trust_flags.has_key:
+ continue
+ tmpdb.trust_root_cert(nickname, EXTERNAL_CA_TRUST_FLAGS)
+
# If a nickname was provided re-import the cert
if options.nickname:
(nickname, trust_flags) = imported[0]
@@ -421,7 +429,7 @@ class CACertManage(admintool.AdminTool):
tmpdb.add_cert(cert, options.nickname, EXTERNAL_CA_TRUST_FLAGS)
imported = tmpdb.list_certs()
- for ca_cert, ca_nickname, ca_trust_flags in ca_certs:
+ for ca_cert, ca_nickname, ca_trust_flags, _serial in ca_certs:
tmpdb.add_cert(ca_cert, ca_nickname, ca_trust_flags)
for nickname, trust_flags in imported:
@@ -461,10 +469,11 @@ class CACertManage(admintool.AdminTool):
for nickname, _trust_flags in imported:
try:
- cert = tmpdb.get_cert(nickname)
- certstore.put_ca_cert_nss(
- api.Backend.ldap2, api.env.basedn, cert, nickname,
- trust_flags)
+ certlist = tmpdb.get_all_certs(nickname)
+ for cert in certlist:
+ certstore.put_ca_cert_nss(
+ api.Backend.ldap2, api.env.basedn, cert, nickname,
+ trust_flags)
except ValueError as e:
raise admintool.ScriptError(
"Failed to install the certificate: %s" % e)
@@ -476,8 +485,8 @@ class CACertManage(admintool.AdminTool):
api.env.basedn,
api.env.realm,
False)
- for _ca_cert, ca_nickname, _ca_trust_flags in ca_certs:
- print(ca_nickname)
+ for _ca_cert, ca_nickname, _ca_trust_flags, serial in ca_certs:
+ print(f"{ca_nickname} {serial}")
def _delete_by_nickname(self, nicknames, options):
conn = api.Backend.ldap2
@@ -489,9 +498,25 @@ class CACertManage(admintool.AdminTool):
ipa_ca_nickname = get_ca_nickname(api.env.realm)
+ # Count the number of times the nickname appears in case we
+ # have a duplicate. If a serial number is provided we can skip
+ # this.
+ cert_count = 0
+ if not options.serial:
+ for nickname in nicknames:
+ for _ca_cert, ca_nickname, _ca_trust_flags, _serial in ca_certs:
+ if ca_nickname == nickname:
+ cert_count += 1
+ if cert_count > 1:
+ raise admintool.ScriptError(
+ 'Multiple matching certificates found (%d). Use the '
+ '--serial option to specify which one to remove.' %
+ cert_count
+ )
+
for nickname in nicknames:
found = False
- for _ca_cert, ca_nickname, _ca_trust_flags in ca_certs:
+ for _ca_cert, ca_nickname, _ca_trust_flags, _serial in ca_certs:
if ca_nickname == nickname:
if ca_nickname == ipa_ca_nickname:
raise admintool.ScriptError(
@@ -508,13 +533,17 @@ class CACertManage(admintool.AdminTool):
with certs.NSSDatabase() as tmpdb:
tmpdb.create_db()
- for ca_cert, ca_nickname, ca_trust_flags in ca_certs:
+ for ca_cert, ca_nickname, ca_trust_flags, serial in ca_certs:
+ if nickname == ca_nickname:
+ if options.serial and options.serial == serial:
+ continue
tmpdb.add_cert(ca_cert, ca_nickname, ca_trust_flags)
loaded = tmpdb.list_certs()
logger.debug("loaded raw certs '%s'", loaded)
- for nickname in nicknames:
- tmpdb.delete_cert(nickname)
+ if not options.serial:
+ for nickname in nicknames:
+ tmpdb.delete_cert(nickname)
for ca_nickname, _trust_flags in loaded:
if ca_nickname in nicknames:
@@ -526,8 +555,8 @@ class CACertManage(admintool.AdminTool):
try:
tmpdb.verify_ca_cert_validity(ca_nickname)
except ValueError as e:
- msg = "Verifying \'%s\' failed. Removing part of the " \
- "chain? %s" % (nickname, e)
+ msg = "Verifying removal of \'%s\' failed. Removing " \
+ "part of the chain? %s" % (nickname, e)
if options.force:
print(msg)
continue
@@ -535,15 +564,20 @@ class CACertManage(admintool.AdminTool):
else:
logger.debug("Verified %s", ca_nickname)
- for _ca_cert, ca_nickname, _ca_trust_flags in ca_certs:
+ for ca_cert, ca_nickname, _ca_trust_flags, serial in ca_certs:
if ca_nickname in nicknames:
- container_dn = DN(('cn', 'certificates'), ('cn', 'ipa'),
- ('cn', 'etc'), api.env.basedn)
- dn = DN(('cn', nickname), container_dn)
+ if options.serial and options.serial != serial:
+ continue
logger.debug("Deleting %s", ca_nickname)
- conn.delete_entry(dn)
+ certstore.delete_ca_cert(conn, api.env.basedn, ca_cert)
+
return
+ raise admintool.ScriptError(
+ "Certificate with name %s and serial number %s not found"
+ % (ca_nickname, options.serial)
+ )
+
def delete(self):
nickname = self.args[1]
self._delete_by_nickname([nickname], self.options)
@@ -556,17 +590,17 @@ class CACertManage(admintool.AdminTool):
False)
now = datetime.datetime.now(tz=datetime.timezone.utc)
- for ca_cert, ca_nickname, _ca_trust_flags in ca_certs:
+ for ca_cert, ca_nickname, _ca_trust_flags, _serial in ca_certs:
if ca_cert.not_valid_after_utc < now:
expired_certs.append(ca_nickname)
-
+ del_options = self.options
+ del_options.force = True
if expired_certs:
- self._delete_by_nickname(expired_certs, self.options)
-
print("Expired certificates deleted:")
- for nickname in expired_certs:
- print(nickname)
+ for ca_cert in expired_certs:
+ self._delete_by_nickname([ca_cert], del_options)
+ print(ca_cert)
print("Run ipa-certupdate on enrolled machines to apply changes.")
else:
print("No certificates were deleted")
diff --git a/ipaserver/install/ipa_server_certinstall.py b/ipaserver/install/ipa_server_certinstall.py
index 76ad37ca7bcc62364379d56b21ead43c5248f5f1..6eaf9d197c0e69161e751b21262e9112176f342c 100644
--- a/ipaserver/install/ipa_server_certinstall.py
+++ b/ipaserver/install/ipa_server_certinstall.py
@@ -276,8 +276,9 @@ class ServerCertInstall(admintool.AdminTool):
# import all the CA certs from nssdb into the temp db
for nickname, flags in nssdb.list_certs():
if not flags.has_key:
- cert = nssdb.get_cert_from_db(nickname)
- tempnssdb.add_cert(cert, nickname, flags)
+ certs = nssdb.get_cert_from_db(nickname, all=True)
+ for cert in certs:
+ tempnssdb.add_cert(cert, nickname, flags)
# now get the server certs from tempnssdb and check their validity
try:
diff --git a/ipaserver/install/krbinstance.py b/ipaserver/install/krbinstance.py
index 99995ea0b2c9a176e1a157281fa8f64ee99cdbf5..a9887553840d944e0aa29d58d12c8582a32e46f8 100644
--- a/ipaserver/install/krbinstance.py
+++ b/ipaserver/install/krbinstance.py
@@ -536,7 +536,7 @@ class KrbInstance(service.Service):
self.api.env.basedn,
self.api.env.realm,
False)
- ca_certs = [c for c, _n, t, _u in ca_certs if t is not False]
+ ca_certs = [c for c, _n, t, _u, _s in ca_certs if t is not False]
x509.write_certificate_list(ca_certs, paths.CACERT_PEM, mode=0o644)
def issue_selfsigned_pkinit_certs(self):
diff --git a/ipaserver/install/service.py b/ipaserver/install/service.py
index 7755a4f2ff5e33e61f85dc24b71fd05a1837cd5a..5e5c60b4bd1d941669cee460587230b3b84c6137 100644
--- a/ipaserver/install/service.py
+++ b/ipaserver/install/service.py
@@ -541,7 +541,7 @@ class Service:
pass
else:
with open(cafile, 'wb') as fd:
- for cert, _unused1, _unused2, _unused3 in ca_certs:
+ for cert, _unused1, _unused2, _unused3, _unused4 in ca_certs:
fd.write(cert.public_bytes(x509.Encoding.PEM))
def export_ca_certs_nssdb(self, db, ca_is_configured, conn=None):
@@ -561,7 +561,7 @@ class Service:
except errors.NotFound:
pass
else:
- for cert, nickname, trust_flags in ca_certs:
+ for cert, nickname, trust_flags, _serial in ca_certs:
db.add_cert(cert, nickname, trust_flags)
def is_configured(self):
diff --git a/ipatests/test_integration/test_commands.py b/ipatests/test_integration/test_commands.py
index 47ef232563d67f86040e2c5944805e430ab2e26c..3c883b8bb63f0084b4b8c2e97543855572ef970b 100644
--- a/ipatests/test_integration/test_commands.py
+++ b/ipatests/test_integration/test_commands.py
@@ -123,6 +123,82 @@ letsencryptauthorityr3 = (
)
le_r3_nick = "CN=R3,O=Let's Encrypt,C=US"
+# Certificates for reproducing duplicate ipaCertSubject values.
+# The trick to creating the second intermediate is for the validity
+# period to be different. In this case the second CA certificate
+# was issued 3 years+1day after the original.
+originalsubjectchain = (
+ b'-----BEGIN CERTIFICATE-----\n'
+ b'MIIDcjCCAlqgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwRDEeMBwGA1UECgwVQ2Vy\n'
+ b'dGlmaWNhdGUgU2hhY2sgTHRkMSIwIAYDVQQDDBlDZXJ0aWZpY2F0ZSBTaGFjayBS\n'
+ b'b290IENBMB4XDTIxMDgwNzE4MDQyNloXDTQxMDgwMTE4MDQyNlowTDEeMBwGA1UE\n'
+ b'CgwVQ2VydGlmaWNhdGUgU2hhY2sgTHRkMSowKAYDVQQDDCFDZXJ0aWZpY2F0ZSBT\n'
+ b'aGFjayBJbnRlcm1lZGlhdGUgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK\n'
+ b'AoIBAQC2RNo7atuVWC/6tDCGforNFvvSFdUwqHxltFmg61i2hmdHAjTaYI1ZJdgB\n'
+ b'y7ApGc8RYc7tfaNrUNA8Chd/9Cu4eW2KuTnAozxytXQneNXloK2xb9iLIhETa1FC\n'
+ b'Hw5BbrmJSWjiVYQsM6bzeiFsKJs4qnP1T9iFHuqmggTtCTPajoYhn6ZKfK3pmB8P\n'
+ b'6XRcp5O9vUhNHJWdpuUjOL32fsBEpV0vKWlsemqDhJrhzj3+YCKt6xrSdpK64HUW\n'
+ b'Kf3YM/K4G6vU5M8DgSFex6T1u2vCsQYJ4Mv8LVCho8awTZoBsimy1tiM0V7GmmBE\n'
+ b'0Uck/U0381NBpNYdv7eyF682SbihAgMBAAGjZjBkMB0GA1UdDgQWBBTtHQCp1dBF\n'
+ b'ypsegtWcXhXDdopIgDAfBgNVHSMEGDAWgBRJuz/14J1ZXqvpOuikJJ62NtuiGTAS\n'
+ b'BgNVHRMBAf8ECDAGAQH/AgEBMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsF\n'
+ b'AAOCAQEAkCBm6u+k/x4QoqqwOJvy8sjq7bUCh73qNPAFlqVSSB8UdCyu21EaXCj8\n'
+ b'dbZa3GNRGk6JACTEUVQ1SD8SkC1E1/IWuEzYOKOP6FmTFbC4V5zU9LAnGFJapS6Q\n'
+ b'CGwU2F44oflBbfOodFznqKPPuENX0gmm4ddvoT915WUOvVLKLuVujkU/ffGKAc8U\n'
+ b'RxRIJ3W2Ybjs9ANg7JqB3Ny8i5QAGHzjRVwU+IgTrJCYPS2DrRYtN3glKBTlyKyR\n'
+ b'xMy0PVKwVo/ItDO3fZ0fsAiIO+4pI51A0lFge5Bg/DzsotZxcWhdTelWjYI9JNca\n'
+ b'y2GPzV1wlxK+ui1uLCWEvKbPtaCfeQ==\n'
+ b'-----END CERTIFICATE-----\n'
+ b'-----BEGIN CERTIFICATE-----\n'
+ b'MIIDeTCCAmGgAwIBAgIUUbo+eGRT5jiS2eIoEzRhXaUx4gwwDQYJKoZIhvcNAQEL\n'
+ b'BQAwRDEeMBwGA1UECgwVQ2VydGlmaWNhdGUgU2hhY2sgTHRkMSIwIAYDVQQDDBlD\n'
+ b'ZXJ0aWZpY2F0ZSBTaGFjayBSb290IENBMB4XDTIxMDgwNzE4MDQyNloXDTQxMDgw\n'
+ b'MjE4MDQyNlowRDEeMBwGA1UECgwVQ2VydGlmaWNhdGUgU2hhY2sgTHRkMSIwIAYD\n'
+ b'VQQDDBlDZXJ0aWZpY2F0ZSBTaGFjayBSb290IENBMIIBIjANBgkqhkiG9w0BAQEF\n'
+ b'AAOCAQ8AMIIBCgKCAQEArh41PPmI6rg7nz3cRqsbCqGgD3+vAD4DNs/Cnp+vhM//\n'
+ b'7Di8FuMoyyLDpD+RdT/Vkvh2Xhp+OcjYSFLX8xeFRy0blfzel2Tq7PiD83BwewsG\n'
+ b'BOarlhkbQGxlGxkr4Fi6z0kNNAfbE2ZzBIs4XSppm7xl4YJyLQD0FkzdrU+zrZuK\n'
+ b'3ELQzk3UWfSSrnbYABY2LBgkny5m7y/kJOMyqn+/T1CUthXD3OpGtyQm2kuEooDZ\n'
+ b'xP1eq30gS8oGYAw2nR/8vJPuyeZaMxM4eNLuc35uq8/6pI+xNEpzGt7xAk1ul/xc\n'
+ b'ewOY2kjh4KJCNK/nCjALzxqhNRHhnH8bA6xtOcgdBwIDAQABo2MwYTAdBgNVHQ4E\n'
+ b'FgQUSbs/9eCdWV6r6TropCSetjbbohkwHwYDVR0jBBgwFoAUSbs/9eCdWV6r6Tro\n'
+ b'pCSetjbbohkwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZI\n'
+ b'hvcNAQELBQADggEBAC35stv/1WZhWblRTZP3XHhH0usHRGTUY7zNSrgS5sb3ERsf\n'
+ b'hgbmFbomra5jKaBqffToOZKLEo+n3tfIPokus35NUQn7ox/6qPp0rJEK8dfLx9jA\n'
+ b'0VTqREbgaAf5xLaX874++OTiM1sPVYG3Egsb1A/YCtDek8mZkKk21g+DZlFMOSDl\n'
+ b'Hw+c3gZUnv6bIT8P09z+9yca2Lvg/dpj2ln3PbOykXzwuGSoNxjUt2OSdCbwyN+f\n'
+ b'hO4NFtDvx74Ggi5bcTrz0ZKO6g8SQotii7cSKAdpIWDpXl8cfsK3SRbkCsg+Fg1S\n'
+ b'kMJEFyDEkKu8Qe6zwKXIAoeKULLO6ADgFVH9CmM=\n'
+ b'-----END CERTIFICATE-----\n'
+)
+interm_nick = "CN=Certificate Shack Intermediate CA,O=Certificate Shack Ltd"
+intermediate_serial = "4096"
+
+duplicatesubject = (
+ b'-----BEGIN CERTIFICATE-----\n'
+ b'MIIDcjCCAlqgAwIBAgICEAEwDQYJKoZIhvcNAQELBQAwRDEeMBwGA1UECgwVQ2Vy\n'
+ b'dGlmaWNhdGUgU2hhY2sgTHRkMSIwIAYDVQQDDBlDZXJ0aWZpY2F0ZSBTaGFjayBS\n'
+ b'b290IENBMB4XDTI0MDgwODE4MDQyNloXDTQ0MDgwMjE4MDQyNlowTDEeMBwGA1UE\n'
+ b'CgwVQ2VydGlmaWNhdGUgU2hhY2sgTHRkMSowKAYDVQQDDCFDZXJ0aWZpY2F0ZSBT\n'
+ b'aGFjayBJbnRlcm1lZGlhdGUgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK\n'
+ b'AoIBAQCzUmUBEO/w1wslS8H304/qfsbeIJX0C5Tm8K2H9JXoauFFej1GZoHqeE+x\n'
+ b'YQvSMuMFcKks3ps9+9yVKuBPtMwbmXsqwlQXORU8DuKhtRzKIOj7nEGw6AQIsfkG\n'
+ b'Q4DjD1ytXliyM7vVfxYD+P1CFDK4NR+K1JLdi3WkYOdCelOQMwNspN/ebiqvwonl\n'
+ b'2asQ6+a13Y0ln1AdrLBvqtR5Z+Gq5+tiC5tA+LKea0e3neQGKjfp/BNPJ+ooNHPR\n'
+ b'86iKDjBKAabvfrHLG2t6oo9+N4xRBGtPYQh9LOQPZ4OedciCo1s2zs+F+4/6co6T\n'
+ b'DsbQt7NJKQ3BJKosvZBhC62lc4evAgMBAAGjZjBkMB0GA1UdDgQWBBTvALT5i2gq\n'
+ b'8yq2Uh8lZGgMoKVClzAfBgNVHSMEGDAWgBRJuz/14J1ZXqvpOuikJJ62NtuiGTAS\n'
+ b'BgNVHRMBAf8ECDAGAQH/AgEBMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsF\n'
+ b'AAOCAQEAVjx1aGNK08/Nhf0JYMxMb9Dqg5m7LNOVBs1jurPtwS3uN+84997GRqIQ\n'
+ b'i+gp/tQVF2YT/RAmt+X0aDLFiSkBcOk87zoFRkR7PZrhhtPo6pSVMN7ngD4/dmp9\n'
+ b'ESbiI8+iF5ZxqI7c3o2N/LtZpi+hWSCJ/xwbOl05jpNQ6ddl+UzDpJ0oNsyndiJA\n'
+ b'yciaCvluK027J4xNym166lqwm6CqiOkm8R/G6NJrEH2Xs5XBCyfeH9V0pkXDbrUe\n'
+ b'Ldqc9ys7l7/MGZi6Qg2nA7J8ErCkrI6eZOocJktSF6SRfXd1NqiqCiNZZQjD6XKZ\n'
+ b'4fMKTKPX6Q2k10iriAIn4RgVjzM05A==\n'
+ b'-----END CERTIFICATE-----\n'
+)
+duplicate_serial = "4097"
+
class TestIPACommand(IntegrationTest):
"""
@@ -827,6 +903,12 @@ class TestIPACommand(IntegrationTest):
paths.IPA_CACERT_MANAGE,
'install',
filename])
+ # remove the subject of good_pkcs7 we just added to avoid
+ # future failures.
+ self.master.run_command([
+ paths.IPA_CACERT_MANAGE,
+ 'delete',
+ 'CN=Certificate Authority,O=EXAMPLE.COM'])
for contents in (badcert,):
self.master.put_file_contents(filename, contents)
@@ -1160,7 +1242,7 @@ class TestIPACommand(IntegrationTest):
raiseonerr=False
)
assert result.returncode != 0
- assert "Verifying \'%s\' failed. Removing part of the " \
+ assert "Verifying removal of \'%s\' failed. Removing part of the " \
"chain? certutil: certificate is invalid: Peer's " \
"Certificate issuer is not recognized." \
% isrgrootx1_nick in result.stderr_text
@@ -1735,6 +1817,68 @@ class TestIPACommand(IntegrationTest):
self.replicas[0], '/usr/sbin/ipa-replica-install'
)
+ def test_ipa_cacert_manage_duplicate_certsubject(self):
+ """Test for ipa-cacert-manage install with duplicated
+ certificate subjects. This relies on the behavior
+ of NSS to show the certificates separately rather than
+ lumping the duplicates together. This requires different
+ validity periods, say 3 years + 1 day.
+ """
+
+ certfile = os.path.join(self.master.config.test_dir, 'chain.pem')
+ self.master.put_file_contents(certfile, originalsubjectchain)
+ result = self.master.run_command(
+ [paths.IPA_CACERT_MANAGE, 'install', certfile])
+
+ certs = self.master.run_command(
+ [paths.IPA_CACERT_MANAGE, 'list'], raiseonerr=False
+ ).stdout_text
+
+ assert f"{interm_nick} {intermediate_serial}" in certs
+
+ certfile = os.path.join(self.master.config.test_dir, 'interm.pem')
+ self.master.put_file_contents(certfile, duplicatesubject)
+ result = self.master.run_command(
+ [paths.IPA_CACERT_MANAGE, 'install', certfile])
+
+ certs = self.master.run_command(
+ [paths.IPA_CACERT_MANAGE, 'list'], raiseonerr=False
+ ).stdout_text
+
+ # If the duplicate subject certificates are not sufficiently
+ # different in validity period, or prior to the this fix,
+ # the test will fail because only one of the duplicately named
+ # subject certificates will be visible: the second one (4097).
+ assert f"{interm_nick} {intermediate_serial}" in certs
+ assert f"{interm_nick} {duplicate_serial}" in certs
+
+ # Make sure we can install the new certs systemwide
+ # No assertions needed, it will work or it won't
+ self.master.run_command(["ipa-certupdate"])
+
+ # delete one of the duplicate subjects, no serial number
+ result = self.master.run_command(
+ ['ipa-cacert-manage', 'delete', interm_nick],
+ raiseonerr=False
+ )
+ assert result.returncode == 1
+ assert 'Multiple matching certificates' in result.stderr_text
+
+ # delete one of the duplicate subjects by the serial number
+ result = self.master.run_command(
+ ['ipa-cacert-manage', 'delete', interm_nick,
+ '--serial', intermediate_serial,],
+ raiseonerr=False
+ )
+ assert result.returncode == 0
+
+ certs = self.master.run_command(
+ [paths.IPA_CACERT_MANAGE, 'list'], raiseonerr=False
+ ).stdout_text
+
+ assert f"{interm_nick} {intermediate_serial}" not in certs
+ assert f"{interm_nick} {duplicate_serial}" in certs
+
class TestIPACommandWithoutReplica(IntegrationTest):
"""
@@ -1970,7 +2114,10 @@ class TestIPACommandWithoutReplica(IntegrationTest):
assert re.search(new_err_msg, dirsrv_error_log)
def test_ipa_cacert_manage_prune(self):
- """Test for ipa-cacert-manage prune"""
+ """Test for ipa-cacert-manage prune
+
+ This twiddles with time so should be run last in the class.
+ """
certfile = os.path.join(self.master.config.test_dir, 'cert.pem')
self.master.put_file_contents(certfile, isrgrootx1)
--
2.48.1

View File

@ -1,49 +0,0 @@
From e1d517032afa2a8258c1ff8bd6bfdd4175b42327 Mon Sep 17 00:00:00 2001
From: Antonio Torres <antorres@redhat.com>
Date: Mon, 17 Feb 2025 10:21:53 +0100
Subject: [PATCH] dns: don't populate forwarders with DoT forwarders
DNS over TLS setup overrides global forwarder to point to Unbound, so no
need to setup regular forwarders.
Resolves: https://pagure.io/freeipa/issue/9748
Signed-off-by: Antonio Torres <antorres@redhat.com>
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
---
ipaserver/install/dns.py | 12 ++----------
1 file changed, 2 insertions(+), 10 deletions(-)
diff --git a/ipaserver/install/dns.py b/ipaserver/install/dns.py
index 88aff19bcec11f778af5644167c32c45cbcab594..470e1915971f66d84e4e4f279caaf81bd3a85cd3 100644
--- a/ipaserver/install/dns.py
+++ b/ipaserver/install/dns.py
@@ -360,14 +360,9 @@ def install_check(standalone, api, replica, options, hostname):
if options.no_forwarders:
options.forwarders = []
- elif (options.forwarders
- or options.dot_forwarders or options.auto_forwarders):
+ elif options.forwarders or options.auto_forwarders:
if not options.forwarders:
- if options.dot_forwarders:
- options.forwarders = [fw.split("#")[0]
- for fw in options.dot_forwarders]
- else:
- options.forwarders = []
+ options.forwarders = []
if options.auto_forwarders:
options.forwarders.extend(dnsforwarders.get_nameservers())
elif standalone or not replica:
@@ -436,9 +431,6 @@ def install(standalone, replica, options, api=api):
"and IPA CA is not present."
)
- if not options.forwarders and options.dot_forwarders:
- options.forwaders = [fw.split("#")[0] for fw in options.dot_forwarders]
-
bind = bindinstance.BindInstance(fstore, api=api)
bind.setup(api.env.host, ip_addresses, api.env.realm, api.env.domain,
options.forwarders, options.forward_policy,
--
2.49.0

View File

@ -1,46 +0,0 @@
From 3f7d84677775bd9e237b28b08fe961a157b8b14e Mon Sep 17 00:00:00 2001
From: Aleksandr Sharov <asharov@redhat.com>
Date: Sat, 8 Mar 2025 14:55:09 +0100
Subject: [PATCH] Add a check into ipa-cert-fix tool to avoid updating certs if
CA is close to being expired.
Fixes: https://pagure.io/freeipa/issue/9760
Signed-off-by: Aleksandr Sharov <asharov@redhat.com>
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
---
ipaserver/install/ipa_cert_fix.py | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/ipaserver/install/ipa_cert_fix.py b/ipaserver/install/ipa_cert_fix.py
index 8e02d1e75cc4cb936b77a6c9f3f9df2b8605a58b..960d7b9e08614ff6ee23c948a0a5fa08b109627e 100644
--- a/ipaserver/install/ipa_cert_fix.py
+++ b/ipaserver/install/ipa_cert_fix.py
@@ -69,6 +69,7 @@ logger = logging.getLogger(__name__)
cert_nicknames = {
+ 'ca_issuing': 'caSigningCert cert-pki-ca',
'sslserver': 'Server-Cert cert-pki-ca',
'subsystem': 'subsystemCert cert-pki-ca',
'ca_ocsp_signing': 'ocspSigningCert cert-pki-ca',
@@ -137,6 +138,16 @@ class IPACertFix(AdminTool):
print("Nothing to do.")
return 0
+ if any(key == 'ca_issuing' for key, _ in certs):
+ logger.debug("CA signing cert is expired, exiting!")
+ print(
+ "The CA signing certificate is expired or will expire within "
+ "the next two weeks.\n\nipa-cert-fix cannot proceed, please "
+ "refer to the ipa-cacert-manage tool to renew the CA "
+ "certificate before proceeding."
+ )
+ return 1
+
print(msg)
print_intentions(certs, extra_certs, non_renewed)
--
2.49.0

View File

@ -1,41 +0,0 @@
From cdc03d7b6233f736c51c10aa07225aac9715e4c0 Mon Sep 17 00:00:00 2001
From: Aleksandr Sharov <asharov@redhat.com>
Date: Sat, 8 Mar 2025 15:04:57 +0100
Subject: [PATCH] Test fix for the update
Fixes: https://pagure.io/freeipa/issue/9760
Signed-off-by: Aleksandr Sharov <asharov@redhat.com>
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
---
ipatests/test_integration/test_ipa_cert_fix.py | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/ipatests/test_integration/test_ipa_cert_fix.py b/ipatests/test_integration/test_ipa_cert_fix.py
index 15d8a81575dc7f2077c34b8907fbeb3e2f6eb66f..d11fd3d611e7e5755569e8fc70de6f261473e3f3 100644
--- a/ipatests/test_integration/test_ipa_cert_fix.py
+++ b/ipatests/test_integration/test_ipa_cert_fix.py
@@ -301,13 +301,18 @@ class TestIpaCertFix(IntegrationTest):
valid. If CA cert expired, ipa-cert-fix won't work.
related: https://pagure.io/freeipa/issue/8721
+
+ If CA cert is close to expiry, there's no reason to issue new certs
+ with short validity period. So, ipa-cert-fix should fail in this case.
+
+ related: https://pagure.io/freeipa/issue/9760
"""
result = self.master.run_command(['ipa-cert-fix', '-v'],
stdin_text='yes\n',
raiseonerr=False)
# check that pki-server cert-fix command fails
- err_msg = ("ERROR: CalledProcessError(Command "
- "['pki-server', 'cert-fix'")
+ err_msg = ("CA signing cert is expired, exiting!")
+ assert result.returncode == 1
assert err_msg in result.stderr_text
--
2.49.0

View File

@ -1,90 +0,0 @@
From d3e9e35ef73729956c649f2ee0d0ff3963f99e4e Mon Sep 17 00:00:00 2001
From: David Hanina <dhanina@redhat.com>
Date: Fri, 28 Mar 2025 10:33:15 +0100
Subject: [PATCH] Correct dnsrecord_* tests for --raw --structured
Fixes typo in the tests, --raw --structured is only checked if rest of
the command is correct as well, therefore test changes were required.
Fixes: https://pagure.io/freeipa/issue/9768
Signed-off-by: David Hanina <dhanina@redhat.com>
Reviewed-By: Michal Polovka <mpolovka@redhat.com>
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
---
ipatests/test_xmlrpc/test_dns_plugin.py | 32 ++++++++++++++-----------
1 file changed, 18 insertions(+), 14 deletions(-)
diff --git a/ipatests/test_xmlrpc/test_dns_plugin.py b/ipatests/test_xmlrpc/test_dns_plugin.py
index 803b0a9571c2888dd02c4595c68403f37be7fed7..864d5287f8317a5154aec4c792f56deab7ff0120 100644
--- a/ipatests/test_xmlrpc/test_dns_plugin.py
+++ b/ipatests/test_xmlrpc/test_dns_plugin.py
@@ -3416,20 +3416,11 @@ class test_dns(Declarative):
},
),
- dict(
- desc='Delete zone %r' % zone1,
- command=('dnszone_del', [zone1], {}),
- expected={
- 'value': [zone1_absolute_dnsname],
- 'summary': u'Deleted DNS zone "%s"' % zone1_absolute,
- 'result': {'failed': []},
- },
- ),
-
dict(
desc="Ensure --raw and --structure does not work "
"for ipa dnsrecord-add",
- command=('dnrecord_add', [], {u'raw': True, u'structured': True}),
+ command=('dnsrecord_add', [zone1, name1],
+ {'arecord': arec2, u'raw': True, u'structured': True}),
expected=errors.MutuallyExclusiveError(
reason=u"cannot use structured together with raw"
),
@@ -3438,7 +3429,8 @@ class test_dns(Declarative):
dict(
desc="Ensure --raw and --structure does not work "
"for ipa dnsrecord-mod",
- command=('dnrecord_add', [], {u'raw': True, u'structured': True}),
+ command=('dnsrecord_mod', [zone1, name1],
+ {'arecord': arec1, u'raw': True, u'structured': True}),
expected=errors.MutuallyExclusiveError(
reason=u"cannot use structured together with raw"
),
@@ -3447,7 +3439,8 @@ class test_dns(Declarative):
dict(
desc="Ensure --raw and --structure does not work "
"for ipa dnsrecord-show",
- command=('dnrecord_add', [], {u'raw': True, u'structured': True}),
+ command=('dnsrecord_show', [zone1, name1],
+ {u'raw': True, u'structured': True}),
expected=errors.MutuallyExclusiveError(
reason=u"cannot use structured together with raw"
),
@@ -3456,11 +3449,22 @@ class test_dns(Declarative):
dict(
desc="Ensure --raw and --structure does not work "
"for ipa dnsrecord-find",
- command=('dnrecord_add', [], {u'raw': True, u'structured': True}),
+ command=('dnsrecord_find', [zone1],
+ {u'raw': True, u'structured': True}),
expected=errors.MutuallyExclusiveError(
reason=u"cannot use structured together with raw"
),
),
+
+ dict(
+ desc='Delete zone %r' % zone1,
+ command=('dnszone_del', [zone1], {}),
+ expected={
+ 'value': [zone1_absolute_dnsname],
+ 'summary': u'Deleted DNS zone "%s"' % zone1_absolute,
+ 'result': {'failed': []},
+ },
+ ),
]
--
2.49.0

View File

@ -1,42 +0,0 @@
From 1aac0a5f7e0702e23e0ba6dad726734b5d75710d Mon Sep 17 00:00:00 2001
From: Julien Rische <jrische@redhat.com>
Date: Mon, 31 Mar 2025 11:50:41 +0200
Subject: [PATCH] ipa-sidgen: fix memory leak in ipa_sidgen_add_post_op
Also remove unused "search_pb" variable and its associated free
functions.
Fixes: https://pagure.io/freeipa/issue/9772
Signed-off-by: Julien Rische <jrische@redhat.com>
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
---
daemons/ipa-slapi-plugins/ipa-sidgen/ipa_sidgen.c | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/daemons/ipa-slapi-plugins/ipa-sidgen/ipa_sidgen.c b/daemons/ipa-slapi-plugins/ipa-sidgen/ipa_sidgen.c
index 99e6b850b04145cefcb7830df5fe4b36adec45de..35ecef228d7fac1e7009dbf97983089755aa6768 100644
--- a/daemons/ipa-slapi-plugins/ipa-sidgen/ipa_sidgen.c
+++ b/daemons/ipa-slapi-plugins/ipa-sidgen/ipa_sidgen.c
@@ -81,7 +81,6 @@ static int ipa_sidgen_add_post_op(Slapi_PBlock *pb)
const char *dn_str;
Slapi_DN *dn = NULL;
struct ipa_sidgen_ctx *ctx;
- Slapi_PBlock *search_pb = NULL;
char *errmsg = NULL;
ret = slapi_pblock_get(pb, SLAPI_IS_REPLICATED_OPERATION, &is_repl_op);
@@ -152,9 +151,8 @@ static int ipa_sidgen_add_post_op(Slapi_PBlock *pb)
ret = 0;
done:
- slapi_free_search_results_internal(search_pb);
- slapi_pblock_destroy(search_pb);
slapi_sdn_free(&dn);
+ slapi_entry_free(entry);
if (ret != 0) {
if (errmsg == NULL) {
--
2.49.0

View File

@ -1,66 +0,0 @@
From 5f632d9d7813f89d498cfb21c8472ff3cac2538a Mon Sep 17 00:00:00 2001
From: Mark Reynolds <mreynolds@redhat.com>
Date: Tue, 29 Apr 2025 13:55:23 -0400
Subject: [PATCH] ipa-migrate - remove replication state information
Remove replication state information (happens when LDIFs are used).
State information is written like:
attribute;adcsn=<CSN>
But we also support ";binary" which should not be removed so special
handling is needed in that case.
Signed-off-by: Mark Reynolds <mareynol@redhat.com>
Fixes: https://pagure.io/freeipa/issue/9776
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
---
ipaserver/install/ipa_migrate.py | 16 ++++++++++++----
1 file changed, 12 insertions(+), 4 deletions(-)
diff --git a/ipaserver/install/ipa_migrate.py b/ipaserver/install/ipa_migrate.py
index 95ef0ac5adc830d04a6bb3a899b20aae86a77072..8ef0071f5c2edc1ce6cba780ac9a7d74122ea79d 100644
--- a/ipaserver/install/ipa_migrate.py
+++ b/ipaserver/install/ipa_migrate.py
@@ -202,6 +202,14 @@ def decode_attr_vals(entry_attrs):
decoded_attrs = {}
for attr in entry_attrs:
vals = ensure_list_str(entry_attrs[attr])
+ # Remove replication state data, but don't remove ";binary"
+ # e.g. userCertififccate;binary;adcsn=<CSN>
+ parts = attr.split(";")
+ if len(parts) > 1 and not attr.endswith(";binary"):
+ if parts[1] == "binary":
+ attr = parts[0] + ";binary"
+ else:
+ attr = parts[0]
decoded_attrs[attr] = vals
return decoded_attrs
@@ -269,19 +277,19 @@ class LDIFParser(ldif.LDIFParser):
if self.mc is None:
return
+ entry_attrs = decode_attr_vals(entry)
if self.get_realm:
# Get the realm from krb container
if DN(("cn", "kerberos"), self.mc.remote_suffix) in DN(dn):
# check objectclass krbrealmcontainer
oc_attr = 'objectClass'
- if 'objectclass' in entry:
+ if 'objectclass' in entry_attrs:
oc_attr = 'objectclass'
- if 'krbrealmcontainer' in ensure_list_str(entry[oc_attr]):
- self.mc.remote_realm = ensure_str(entry['cn'][0])
+ if 'krbrealmcontainer' in entry_attrs[oc_attr]:
+ self.mc.remote_realm = ensure_str(entry_attrs['cn'][0])
self.mc.log_debug("Found remote realm from ldif: "
f"{self.mc.remote_realm}")
else:
- entry_attrs = decode_attr_vals(entry)
self.mc.process_db_entry(entry_dn=dn, entry_attrs=entry_attrs)
--
2.49.0

View File

@ -1,32 +0,0 @@
From 4e23fa92f1a07565618d49ed27b54d33618bba73 Mon Sep 17 00:00:00 2001
From: Mark Reynolds <mreynolds@redhat.com>
Date: Tue, 29 Apr 2025 14:00:51 -0400
Subject: [PATCH] ipa-migrate - do not process AD entgries in staging mode
Only migrate AD entries in production mode due to schema conflicts
created when removing certain AD attributes (e.g.
ipantsecurityidentifier)
SIgned-off-by: Mark Reynolds <mreynolds@redhat.com>
relates: https://pagure.io/freeipa/issue/9776
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
---
ipaserver/install/ipa_migrate_constants.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/ipaserver/install/ipa_migrate_constants.py b/ipaserver/install/ipa_migrate_constants.py
index 09856f07cabd124a7899bc5f355a56eb23023cc0..4beaa4f42a667ba83008213075b3ded782a83260 100644
--- a/ipaserver/install/ipa_migrate_constants.py
+++ b/ipaserver/install/ipa_migrate_constants.py
@@ -870,7 +870,7 @@ DB_OBJECTS = {
'oc': ['ipantdomainattrs'],
'subtree': ',cn=ad,cn=etc,$SUFFIX',
'label': 'AD',
- 'mode': 'all',
+ 'mode': 'production',
'count': 0,
},
--
2.49.0

View File

@ -1,47 +0,0 @@
From c052bbbfd2737f88b6496be7d4849cf17d9a126f Mon Sep 17 00:00:00 2001
From: Mark Reynolds <mreynolds@redhat.com>
Date: Tue, 29 Apr 2025 14:05:15 -0400
Subject: [PATCH] ipa-migrate - improve suffix replacement
When values are "normalized/converted" to a new domain the order in
which the host/release/suffix are converted matters. Replacing the
suffix first can lead to incorrect results, so convert the host/realm
before converting the suffix
Signed-off-by: Mark Reynolds <mreynolds@redhat.com>
relates: https://pagure.io/freeipa/issue/9776
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
---
ipaserver/install/ipa_migrate.py | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/ipaserver/install/ipa_migrate.py b/ipaserver/install/ipa_migrate.py
index 8ef0071f5c2edc1ce6cba780ac9a7d74122ea79d..a24a2ab7a5ffd4cf1d59179f14e2f5d348fd57e2 100644
--- a/ipaserver/install/ipa_migrate.py
+++ b/ipaserver/install/ipa_migrate.py
@@ -1084,11 +1084,9 @@ class IPAMigrate():
if isinstance(val, bytes) or isinstance(val, DN):
return val
- # Replace base DN
- val = self.replace_suffix_value(val)
-
# For DNS DN we only replace suffix
if dns:
+ val = self.replace_suffix_value(val)
return val
# Replace host
@@ -1102,6 +1100,9 @@ class IPAMigrate():
# Replace realm
val = val.replace(self.remote_realm, self.realm)
+ # Lastly, replace base DN
+ val = self.replace_suffix_value(val)
+
return val
def convert_values(self, values, dns=False):
--
2.49.0

View File

@ -1,87 +0,0 @@
From 5d893c9c3b8d384873f40d2524b1ebf0f34fb452 Mon Sep 17 00:00:00 2001
From: Julien Rische <jrische@redhat.com>
Date: Mon, 28 Apr 2025 18:01:39 +0200
Subject: [PATCH] kdb: keep ipadb_get_connection() from succeeding with null
LDAP context
The final call to ipadb_reinit_mspac() in ipadb_get_connection() is not
considered essential for the function to succeed, as there might be
cases where the required pieces of information to generate PACs are not
yet configured in the database. However, in environments where 389ds is
overwhelmed, the LDAP connection established at the beginning of
ipadb_get_connection() might already be lost while executing
ipadb_reinit_mspac().
Connection errors were not distinguished from configuration errors,
which could result in ipadb_get_connection() succeeding while the LDAP
context is set to null, leading to a KDC crash on the next LDAP request.
ipadb_get_connection() now explicitly checks the value of the LDAP
context before returning.
Fixes: https://pagure.io/freeipa/issue/9777
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
Reviewed-By: Rafael Guterres Jeffman <rjeffman@redhat.com>
---
daemons/ipa-kdb/ipa_kdb.c | 31 ++++++++++++++++++++++++-------
1 file changed, 24 insertions(+), 7 deletions(-)
diff --git a/daemons/ipa-kdb/ipa_kdb.c b/daemons/ipa-kdb/ipa_kdb.c
index 903e19e83bbe383b878a3b9261dd501f96058d51..531ee223e1d5157c87a5c31dfe44b9cfa8dcc554 100644
--- a/daemons/ipa-kdb/ipa_kdb.c
+++ b/daemons/ipa-kdb/ipa_kdb.c
@@ -530,26 +530,43 @@ int ipadb_get_connection(struct ipadb_context *ipactx)
/* get adtrust options using default refresh interval */
ret = ipadb_reinit_mspac(ipactx, false, &stmsg);
- if (ret && stmsg)
- krb5_klog_syslog(LOG_WARNING, "MS-PAC generator: %s", stmsg);
+ if (ret) {
+ if (stmsg) {
+ krb5_klog_syslog(LOG_WARNING, "MS-PAC generator: %s", stmsg);
+ }
+ /* Initialization of the MS-PAC generator is an optional dependency.
+ * Fail only if the connection was lost. */
+ if (!ipactx->lcontext) {
+ goto done;
+ }
+ }
ret = 0;
done:
ldap_msgfree(res);
+ /* LDAP context should never be null on success, but keep this test out of
+ * security to make sure we do not return an invalid context. */
+ if (ret == 0 && !ipactx->lcontext) {
+ krb5_klog_syslog(LOG_WARNING, "Internal malfunction: LDAP connection "
+ "process resulted in an invalid context "
+ "(please report this incident)");
+ ret = LDAP_SERVER_DOWN;
+ }
+
if (ret) {
+ /* Cleanup LDAP context if connection failed. */
if (ipactx->lcontext) {
ldap_unbind_ext_s(ipactx->lcontext, NULL, NULL);
ipactx->lcontext = NULL;
}
- if (ret == LDAP_SERVER_DOWN) {
- return ETIMEDOUT;
- }
- return EIO;
+
+ /* Replace LDAP error code by POSIX error code. */
+ ret = ret == LDAP_SERVER_DOWN ? ETIMEDOUT : EIO;
}
- return 0;
+ return ret;
}
static krb5_principal ipadb_create_local_tgs(krb5_context kcontext,
--
2.49.0

View File

@ -1,35 +0,0 @@
From 17fdff8f2f1664a387147e13a851bc1248abc29c Mon Sep 17 00:00:00 2001
From: Florence Blanc-Renaud <flo@redhat.com>
Date: Mon, 19 May 2025 09:56:36 +0200
Subject: [PATCH] ipatests: test_manual_renewal_master_transfer must wait for
replication
The test is transferring the CA renewal role from master to replica.
It calls ipa config-mod on the replica then checks with ipa config-show
on the master.
Wait for replication to complete between the 2 steps.
Fixes: https://pagure.io/freeipa/issue/9790
Signed-off-by: Florence Blanc-Renaud <flo@redhat.com>
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
---
ipatests/test_integration/test_replica_promotion.py | 3 +++
1 file changed, 3 insertions(+)
diff --git a/ipatests/test_integration/test_replica_promotion.py b/ipatests/test_integration/test_replica_promotion.py
index c754cef88cb275987f5afdaad43f2ea07e3b7476..3c67833d3101aef095539953e04c31d028c746d3 100644
--- a/ipatests/test_integration/test_replica_promotion.py
+++ b/ipatests/test_integration/test_replica_promotion.py
@@ -417,6 +417,9 @@ class TestRenewalMaster(IntegrationTest):
replica = self.replicas[0]
replica.run_command(['ipa', 'config-mod',
'--ca-renewal-master-server', replica.hostname])
+ # wait for replication to complete before checking on the master
+ tasks.wait_for_replication(replica.ldap_connect())
+
result = self.master.run_command(["ipa", "config-show"]).stdout_text
assert("IPA CA renewal master: %s" % replica.hostname in result), (
"Replica hostname not found among CA renewal masters"
--
2.49.0

View File

@ -1,225 +0,0 @@
From 6f1b9a4228e400ef23f0f411ebf8a98c30cd2f9f Mon Sep 17 00:00:00 2001
From: David Hanina <dhanina@redhat.com>
Date: Mon, 5 May 2025 17:31:18 +0200
Subject: [PATCH] Require baserid and secondarybaserid
This has been already required for some time, just not really enforced.
Also adds few new tests, and removes test without providing rid.
Fixes: https://pagure.io/freeipa/issue/9779
Signed-off-by: David Hanina <dhanina@redhat.com>
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
---
ipaclient/plugins/idrange.py | 31 +++------------
ipaserver/plugins/idrange.py | 35 +++++++----------
ipatests/test_cmdline/test_cli.py | 13 -------
ipatests/test_xmlrpc/test_range_plugin.py | 46 +++++++++++++++++++++++
4 files changed, 66 insertions(+), 59 deletions(-)
diff --git a/ipaclient/plugins/idrange.py b/ipaclient/plugins/idrange.py
index 1a8d68ed7ff724854d5ea2f3dd43ec9644b5c671..b62cb1e3526d33a0d762809142b6e372f6f608ea 100644
--- a/ipaclient/plugins/idrange.py
+++ b/ipaclient/plugins/idrange.py
@@ -19,7 +19,6 @@
from ipaclient.frontend import MethodOverride
from ipalib.plugable import Registry
-from ipalib import api
register = Registry()
@@ -33,8 +32,7 @@ class idrange_add(MethodOverride):
Also ensure that secondary-rid-base is prompted for when rid-base is
specified and vice versa, in case that dom-sid was not specified.
- Also ensure that rid-base and secondary-rid-base is prompted for
- if ipa-adtrust-install has been run on the system.
+ Also ensure that rid-base and secondary-rid-base is prompted for.
"""
# dom-sid can be specified using dom-sid or dom-name options
@@ -63,27 +61,10 @@ class idrange_add(MethodOverride):
else:
# This is a local range
- # Find out whether ipa-adtrust-install has been ran
- adtrust_is_enabled = api.Command['adtrust_is_enabled']()['result']
- if adtrust_is_enabled:
- # If ipa-adtrust-install has been ran, all local ranges
- # require both RID base and secondary RID base
-
- if rid_base is None:
- set_from_prompt('ipabaserid')
-
- if secondary_rid_base is None:
- set_from_prompt('ipasecondarybaserid')
-
- else:
- # This is a local range on a server with no adtrust support
-
- # Prompt for secondary RID base only if RID base was given
- if rid_base is not None and secondary_rid_base is None:
- set_from_prompt('ipasecondarybaserid')
+ # All local ranges require both RID base and secondary RID base
+ if rid_base is None:
+ set_from_prompt('ipabaserid')
- # Symetrically, prompt for RID base if secondary RID base was
- # given
- if rid_base is None and secondary_rid_base is not None:
- set_from_prompt('ipabaserid')
+ if secondary_rid_base is None:
+ set_from_prompt('ipasecondarybaserid')
diff --git a/ipaserver/plugins/idrange.py b/ipaserver/plugins/idrange.py
index d155fb46da8240449a077d35e86a91ee9f95c132..1c8b5c6899ec927d753b7d9b116d35396b536339 100644
--- a/ipaserver/plugins/idrange.py
+++ b/ipaserver/plugins/idrange.py
@@ -73,10 +73,14 @@ Both types have the following attributes in common:
With those two attributes a range object can reserve the Posix IDs starting
with base-id up to but not including base-id+range-size exclusively.
-Additionally an ID range of the local domain may set
+Additionally an ID range of the local domain must set
- rid-base: the first RID(*) of the corresponding RID range
- secondary-rid-base: first RID of the secondary RID range
+If the server is updated from a previous version and defines local ID ranges
+missing the rid-base and secondary-rid-base, it is recommended to use
+`ipa-idrange-fix` command to identify the missing values and fix the ID ranges.
+
and an ID range of a trusted domain must set
- rid-base: the first RID of the corresponding RID range
- sid: domain SID of the trusted domain
@@ -519,11 +523,15 @@ class idrange_add(LDAPCreate):
'or ipa-ad-trust-posix when '
'auto-private-groups is specified'))
- # secondary base rid must be set if and only if base rid is set
- if is_set('ipasecondarybaserid') != is_set('ipabaserid'):
- raise errors.ValidationError(name='ID Range setup',
- error=_('Options secondary-rid-base and rid-base must '
- 'be used together'))
+ # base rid and secondary base rid must be set for sidgen
+ if not (is_set('ipabaserid') and is_set('ipasecondarybaserid')):
+ raise errors.ValidationError(
+ name='ID Range setup',
+ error=_(
+ 'You must specify both rid-base and '
+ 'secondary-rid-base options.'
+ )
+ )
# and they must not overlap
if is_set('ipabaserid') and is_set('ipasecondarybaserid'):
@@ -534,21 +542,6 @@ class idrange_add(LDAPCreate):
raise errors.ValidationError(name='ID Range setup',
error=_("Primary RID range and secondary RID range"
" cannot overlap"))
-
- # rid-base and secondary-rid-base must be set if
- # ipa-adtrust-install has been run on the system
- adtrust_is_enabled = api.Command['adtrust_is_enabled']()['result']
-
- if adtrust_is_enabled and not (
- is_set('ipabaserid') and is_set('ipasecondarybaserid')):
- raise errors.ValidationError(
- name='ID Range setup',
- error=_(
- 'You must specify both rid-base and '
- 'secondary-rid-base options, because '
- 'ipa-adtrust-install has already been run.'
- )
- )
return dn
def post_callback(self, ldap, dn, entry_attrs, *keys, **options):
diff --git a/ipatests/test_cmdline/test_cli.py b/ipatests/test_cmdline/test_cli.py
index 718798d68083285ce8aefe23af951bc819bdefdb..6c86bbb657a0d9a7b74ef34ad20a796a10073315 100644
--- a/ipatests/test_cmdline/test_cli.py
+++ b/ipatests/test_cmdline/test_cli.py
@@ -276,25 +276,12 @@ class TestCLIParsing:
ipasecondarybaserid=u'500000',
)
- def test_without_options():
- self.check_command(
- 'idrange_add range1 --base-id=1 --range-size=1',
- 'idrange_add',
- cn=u'range1',
- ipabaseid=u'1',
- ipaidrangesize=u'1',
- )
-
adtrust_dn = 'cn=ADTRUST,cn=%s,cn=masters,cn=ipa,cn=etc,%s' % \
(api.env.host, api.env.basedn)
adtrust_is_enabled = api.Command['adtrust_is_enabled']()['result']
mockldap = None
if not adtrust_is_enabled:
- # ipa-adtrust-install not run - no need to pass rid-base
- # and secondary-rid-base
- test_without_options()
-
# Create a mock service object to test against
adtrust_add = dict(
ipaconfigstring=b'enabledService',
diff --git a/ipatests/test_xmlrpc/test_range_plugin.py b/ipatests/test_xmlrpc/test_range_plugin.py
index 36469525b14ee507f2d8580b1f021ff09b82c99d..ffc89c028168740e7b8ae217259af512abff2d8a 100644
--- a/ipatests/test_xmlrpc/test_range_plugin.py
+++ b/ipatests/test_xmlrpc/test_range_plugin.py
@@ -1086,4 +1086,50 @@ class test_range(Declarative):
),
),
+ # Fail without baserid and secondarybaserid
+
+ dict(
+ desc='Try creating ID range %r without both rid' % (testrange9),
+ command=('idrange_add', [testrange9],
+ dict(ipabaseid=testrange9_base_id,
+ ipaidrangesize=testrange9_size)),
+ expected=errors.ValidationError(
+ name='ID Range setup',
+ error=(
+ 'You must specify both rid-base and '
+ 'secondary-rid-base options.'
+ )
+ )
+ ),
+
+ dict(
+ desc='Try creating ID range %r without'
+ 'secondarybaserid' % (testrange9),
+ command=('idrange_add', [testrange9],
+ dict(ipabaseid=testrange9_base_id,
+ ipaidrangesize=testrange9_size,
+ ipabaserid=testrange9_base_rid)),
+ expected=errors.ValidationError(
+ name='ID Range setup',
+ error=(
+ 'You must specify both rid-base and '
+ 'secondary-rid-base options.'
+ )
+ )
+ ),
+
+ dict(
+ desc='Try creating ID range %r without baserid' % (testrange9),
+ command=('idrange_add', [testrange9],
+ dict(ipabaseid=testrange9_base_id,
+ ipaidrangesize=testrange9_size,
+ ipasecondarybaserid=testrange9_secondary_base_rid)),
+ expected=errors.ValidationError(
+ name='ID Range setup',
+ error=(
+ 'You must specify both rid-base and '
+ 'secondary-rid-base options.'
+ )
+ )
+ ),
]
--
2.49.0

View File

@ -1,90 +0,0 @@
From 1c069653806ce8224132a35d6d3bd01ac53098b6 Mon Sep 17 00:00:00 2001
From: Florence Blanc-Renaud <flo@redhat.com>
Date: Mon, 26 May 2025 18:24:12 +0200
Subject: [PATCH] ipa config-mod: fix internalerror when setting an empty
ipaconfigstring
When ipa config-mod is called with --ipaconfigstring="", the command
fails with an InternalError.
This happens because the code added for 32bits uid did not properly
handle this case.
Same issue if ipa subid-stats is called with a null ipaconfigstring.
This commit now handles when ipaconfigstring is empty or None, and adds
a test.
Fixes: https://pagure.io/freeipa/issue/9794
Signed-off-by: Florence Blanc-Renaud <flo@redhat.com>
Reviewed-By: Francisco Trivino <ftrivino@redhat.com>
---
ipaserver/plugins/config.py | 4 +--
ipatests/test_integration/test_commands.py | 30 ++++++++++++++++++++++
2 files changed, 32 insertions(+), 2 deletions(-)
diff --git a/ipaserver/plugins/config.py b/ipaserver/plugins/config.py
index c509c2c13adfb4950741f63ffcbc9f3f806c0c3b..d9769ab1fb8498c24ce41ad32af40938bdaee804 100644
--- a/ipaserver/plugins/config.py
+++ b/ipaserver/plugins/config.py
@@ -524,7 +524,7 @@ class config(LDAPObject):
def is_config_option_present(self, option):
dn = DN(('cn', 'ipaconfig'), ('cn', 'etc'), self.api.env.basedn)
configentry = self.api.Backend.ldap2.get_entry(dn, ['ipaconfigstring'])
- configstring = configentry['ipaconfigstring']
+ configstring = configentry.get('ipaconfigstring') or []
return (option.lower() in map(str.lower, configstring))
@@ -702,7 +702,7 @@ class config_mod(LDAPUpdate):
error=_('SELinux user map default user not in order list'))
if 'ipaconfigstring' in entry_attrs:
- configstring = entry_attrs['ipaconfigstring']
+ configstring = entry_attrs['ipaconfigstring'] or []
if 'SubID:Disable'.lower() in map(str.lower, configstring):
# Check if SubIDs already allocated
try:
diff --git a/ipatests/test_integration/test_commands.py b/ipatests/test_integration/test_commands.py
index f64152908b3e1cbca451697043c1fcc8ad37fee6..9cad5772127bcd860aeecc8dabe73d5f160faf7b 100644
--- a/ipatests/test_integration/test_commands.py
+++ b/ipatests/test_integration/test_commands.py
@@ -2123,6 +2123,36 @@ class TestIPACommandWithoutReplica(IntegrationTest):
assert old_err_msg not in dirsrv_error_log
assert re.search(new_err_msg, dirsrv_error_log)
+ @pytest.fixture
+ def update_ipaconfigstring(self):
+ """
+ This fixture stores the value of ipaconfigstring parameter
+ and reverts to the initial value
+ """
+ ldap = self.master.ldap_connect()
+ dn = DN(
+ ("cn", "ipaconfig"), ('cn', 'etc'),
+ self.master.domain.basedn
+ )
+ entry = ldap.get_entry(dn)
+ val = entry.get("ipaconfigstring")
+ yield
+
+ # re-read the entry as the value may have been changed by the test
+ entry = ldap.get_entry(dn)
+ entry["ipaconfigstring"] = val
+ ldap.update_entry(entry)
+
+ def test_empty_ipaconfigstring(self, update_ipaconfigstring):
+ """
+ Test for https://pagure.io/freeipa/issue/9794
+
+ Test that setting an empty ipaconfigstring does not fail.
+ Subsequent calls to ipa subid-stats should also succeed.
+ """
+ self.master.run_command(['ipa', 'config-mod', "--ipaconfigstring="])
+ self.master.run_command(['ipa', 'subid-stats'])
+
def test_ipa_cacert_manage_prune(self):
"""Test for ipa-cacert-manage prune
--
2.49.0

View File

@ -1,52 +0,0 @@
From 383574be4e645155fb58a79612138e51c3bdc4eb Mon Sep 17 00:00:00 2001
From: Sudhir Menon <sumenon@redhat.com>
Date: Tue, 13 May 2025 15:58:56 +0530
Subject: [PATCH] ipatests: Test to check dot forwarders are added to unbound.
This test checks that dns forwarder is listed in
dnsserver-show command and also the dot forwarder is
added to unbound and included in /etc/unbound/conf.d/zzz-ipa.conf
Signed-off-by: Sudhir Menon <sumenon@redhat.com>
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
Reviewed-By: Antonio Torres <antorres@redhat.com>
---
ipatests/test_integration/test_edns.py | 18 ++++++++++++++++++
1 file changed, 18 insertions(+)
diff --git a/ipatests/test_integration/test_edns.py b/ipatests/test_integration/test_edns.py
index b42570ffa2c1cba8271ff08e084da0107e57d054..dd046f226926d09074d8d6ce536999c5d452fcc4 100644
--- a/ipatests/test_integration/test_edns.py
+++ b/ipatests/test_integration/test_edns.py
@@ -247,6 +247,7 @@ class TestDNSOverTLS(IntegrationTest):
class TestDNS_DoT(TestDNS):
+
@classmethod
def install(cls, mh):
tasks.install_packages(cls.master, ['*ipa-server-encrypted-dns'])
@@ -255,3 +256,20 @@ class TestDNS_DoT(TestDNS):
"--dot-forwarder", "1.1.1.1#cloudflare-dns.com"
]
tasks.install_master(cls.master, extra_args=args)
+
+ def test_check_dot_forwarder_added_in_ipa_conf(self):
+ """
+ This test checks that forwarders is listed in
+ dnsserver-show command and also the dot forwarder is
+ added to unbound and included in
+ /etc/unbound/conf.d/zzz-ipa.conf
+ """
+ msg = 'Forwarders: 127.0.0.55'
+ cmd1 = self.master.run_command(
+ ["ipa", "dnsserver-show", self.master.hostname]
+ )
+ assert msg in cmd1.stdout_text
+ contents = self.master.get_file_contents(
+ paths.UNBOUND_CONF, encoding='utf-8'
+ )
+ assert 'forward-addr: 1.1.1.1#cloudflare-dns.com' in contents
--
2.49.0

View File

@ -1,147 +0,0 @@
From 777f4c0ed631f70b64f6a972e7e6cb140155ef1f Mon Sep 17 00:00:00 2001
From: Rob Crittenden <rcritten@redhat.com>
Date: Thu, 8 May 2025 13:55:34 -0400
Subject: [PATCH] Fix some issues identified by a static analyzer
Remove resource leak when reading the IPA config in ipa-getkeytab
Free popt in ipa-getkeytab
Initialize ret in ipa-otpd/passkey.c
Use the correct free function in util/ipa_krb5.c
Related: https://pagure.io/freeipa/issue/9468
Fixes: https://pagure.io/freeipa/issue/9365
Signed-off-by: Rob Crittenden <rcritten@redhat.com>
Reviewed-By: David Hanina <dhanina@redhat.com>
---
client/ipa-getkeytab.c | 13 ++++++++++++-
daemons/ipa-otpd/passkey.c | 2 +-
util/ipa_krb5.c | 2 +-
3 files changed, 14 insertions(+), 3 deletions(-)
diff --git a/client/ipa-getkeytab.c b/client/ipa-getkeytab.c
index 228b981c2c38c5f9227d84cbae20f598564b5dcf..8ca4b8130cd668cbbc03e969399b5fe47ce42f1e 100644
--- a/client/ipa-getkeytab.c
+++ b/client/ipa-getkeytab.c
@@ -866,6 +866,7 @@ static int read_ipa_config(struct ipa_config **ipacfg)
(*ipacfg)->domain = ini_get_string_config_value(obj, &ret);
}
+ ini_config_destroy(cfgctx);
return 0;
}
@@ -984,7 +985,7 @@ int main(int argc, const char *argv[])
krb5_context krbctx;
krb5_ccache ccache;
krb5_principal uprinc = NULL;
- krb5_principal sprinc;
+ krb5_principal sprinc = NULL;
krb5_error_code krberr;
struct keys_container keys = { 0 };
krb5_keytab kt;
@@ -1026,6 +1027,7 @@ int main(int argc, const char *argv[])
fprintf(stdout, "%s\n", enc);
}
ipa_krb5_free_ktypes(krbctx, ktypes);
+ poptFreeContext(pc);
exit (0);
}
@@ -1033,6 +1035,7 @@ int main(int argc, const char *argv[])
if (!quiet) {
poptPrintUsage(pc, stderr, 0);
}
+ poptFreeContext(pc);
exit(2);
}
@@ -1041,12 +1044,14 @@ int main(int argc, const char *argv[])
if (!quiet) {
poptPrintUsage(pc, stderr, 0);
}
+ poptFreeContext(pc);
exit(2);
}
if (askbindpw) {
bindpw = ask_password(krbctx, _("Enter LDAP password"), NULL, false);
if (!bindpw) {
+ poptFreeContext(pc);
exit(2);
}
}
@@ -1056,6 +1061,7 @@ int main(int argc, const char *argv[])
_("Bind password required when using a bind DN (-w or -W).\n"));
if (!quiet)
poptPrintUsage(pc, stderr, 0);
+ poptFreeContext(pc);
exit(10);
}
@@ -1064,6 +1070,7 @@ int main(int argc, const char *argv[])
"and bind DN simultaneously.\n"));
if (!quiet)
poptPrintUsage(pc, stderr, 0);
+ poptFreeContext(pc);
exit(2);
}
@@ -1071,6 +1078,7 @@ int main(int argc, const char *argv[])
fprintf(stderr, _("Invalid SASL bind mechanism\n"));
if (!quiet)
poptPrintUsage(pc, stderr, 0);
+ poptFreeContext(pc);
exit(2);
}
@@ -1083,8 +1091,10 @@ int main(int argc, const char *argv[])
"simultaneously.\n"));
if (!quiet)
poptPrintUsage(pc, stderr, 0);
+ poptFreeContext(pc);
exit(2);
}
+ poptFreeContext(pc);
if (server && (strcasecmp(server, "_srv_") == 0)) {
struct srvrec *srvrecs, *srv;
@@ -1119,6 +1129,7 @@ int main(int argc, const char *argv[])
/* Discovery failed, fall through to option methods */
server = NULL;
}
+ free(ipacfg);
}
if (!server && !ldap_uri) {
diff --git a/daemons/ipa-otpd/passkey.c b/daemons/ipa-otpd/passkey.c
index 8351f0fcf9e2245a83563eefe2c17b04c5b9f4e3..ad3c45467ba9af46cf2e333e2dbfd938c8c8d643 100644
--- a/daemons/ipa-otpd/passkey.c
+++ b/daemons/ipa-otpd/passkey.c
@@ -307,7 +307,7 @@ bool is_passkey(struct otpd_queue_item *item)
static json_t *ipa_passkey_to_json_array(char **ipa_passkey)
{
- int ret;
+ int ret = 0;
const char *sep;
char *start;
size_t c;
diff --git a/util/ipa_krb5.c b/util/ipa_krb5.c
index bb98ab897cf8ea933c025bdb9abf7d394cae4583..0087e53e689fc4dc5549908b3eadd6d963d94489 100644
--- a/util/ipa_krb5.c
+++ b/util/ipa_krb5.c
@@ -80,7 +80,7 @@ static krb5_error_code ipa_get_random_salt(krb5_context krbctx,
void
ipa_krb5_free_ktypes(krb5_context context, krb5_enctype *val)
{
- free(val);
+ krb5_free_enctypes(context, val);
}
/*
--
2.49.0

View File

@ -1,30 +0,0 @@
From a31654e5c4ba61177928abede5885a247365d067 Mon Sep 17 00:00:00 2001
From: PRANAV THUBE <pthube@redhat.com>
Date: Mon, 19 May 2025 14:46:19 +0530
Subject: [PATCH] ipatests: Ignore /run/log/journal in test_uninstallation.py
Update - Add /run/log/journal to the allowed list for leftover files/directories
Fixes: https://pagure.io/freeipa/issue/9788
Signed-off-by: PRANAV THUBE <pthube@redhat.com>
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
---
ipatests/test_integration/test_uninstallation.py | 1 +
1 file changed, 1 insertion(+)
diff --git a/ipatests/test_integration/test_uninstallation.py b/ipatests/test_integration/test_uninstallation.py
index 049c50db536ae1070f5f958e76b12a1518da0aba..f1cc1917dd0f216be3b11803554e86d1d22c3888 100644
--- a/ipatests/test_integration/test_uninstallation.py
+++ b/ipatests/test_integration/test_uninstallation.py
@@ -178,6 +178,7 @@ class TestUninstallCleanup(IntegrationTest):
'/var/log',
'/var/tmp/systemd-private',
'/run/systemd',
+ '/run/log/journal',
'/var/lib/authselect/backups/pre_ipaclient',
'/var/named/data/named.run',
paths.DNSSEC_SOFTHSM_PIN_SO, # See commit eb54814741
--
2.49.0

View File

@ -1,77 +0,0 @@
From 3ba0f6a34cb018a36bc548667e2b433d05da6a45 Mon Sep 17 00:00:00 2001
From: Sudhir Menon <sumenon@redhat.com>
Date: Tue, 6 May 2025 15:37:54 +0530
Subject: [PATCH] ipatests: Tests for krbLastSuccessfulAuth warning
This testcase checks that ipa-healthcheck issues
warning when ipaconfigstring=AllowNThash
Ref: https://github.com/freeipa/freeipa-healthcheck/issues/315
Signed-off-by: Sudhir Menon <sumenon@redhat.com>
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
---
.../test_integration/test_ipahealthcheck.py | 40 ++++++++++++++++++-
1 file changed, 39 insertions(+), 1 deletion(-)
diff --git a/ipatests/test_integration/test_ipahealthcheck.py b/ipatests/test_integration/test_ipahealthcheck.py
index b8ee2884de51a2e0b2dcf2991452486c29c4ed00..0ebc7149f88394bf6b6355adbb88b3ad92697517 100644
--- a/ipatests/test_integration/test_ipahealthcheck.py
+++ b/ipatests/test_integration/test_ipahealthcheck.py
@@ -1526,6 +1526,45 @@ class TestIpaHealthCheck(IntegrationTest):
]
)
+ @pytest.fixture
+ def change_pwd_plugin_default(self):
+ """
+ Fixture to change the password plugin feature
+ to AllowNThash and change it to default
+ """
+ self.master.run_command(
+ [
+ "ipa", "config-mod", "--delattr",
+ "ipaconfigstring=KDC:Disable Last Success"
+ ]
+ )
+ yield
+ self.master.run_command(
+ [
+ "ipa", "config-mod", "--addattr",
+ "ipaconfigstring=KDC:Disable Last Success"
+ ]
+ )
+
+ def test_krbLastSuccessfulAuth_warning(self, change_pwd_plugin_default):
+ """
+ This test checks that warning message is displayed
+ when password plugin feature is modified to
+ AllowNThash
+ """
+ err_msg = (
+ "Last Successful Auth is enabled. "
+ "It may cause performance problems."
+ )
+ returncode, data = run_healthcheck(
+ self.master, "ipahealthcheck.ipa.config",
+ "IPAkrbLastSuccessfulAuth",
+ )
+ assert returncode == 1
+ for check in data:
+ assert check["result"] == "WARNING"
+ assert check["kw"]["msg"] == err_msg
+
@pytest.fixture
def expire_cert_critical(self):
"""
@@ -1553,7 +1592,6 @@ class TestIpaHealthCheck(IntegrationTest):
assert "Expired Certificate" in check["kw"]["items"]
assert check["kw"]["msg"] == msg
-
def test_ipa_healthcheck_expiring(self, restart_service):
"""
There are two overlapping tests for expiring certs, check both.
--
2.49.0

View File

@ -1,82 +0,0 @@
From cef199631109b91462bf25ae8893ca8980faf5bf Mon Sep 17 00:00:00 2001
From: Sudhir Menon <sumenon@redhat.com>
Date: Wed, 21 May 2025 17:20:04 +0530
Subject: [PATCH] ipatests: ipahealthcheck warns for user provided certificates
about to expire
This patch tests that ipa-healthcheck tools warns when IPA server is
installed CALess and user provided certificates are about to expire.
Signed-off-by: Sudhir Menon <sumenon@redhat.com>
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
---
.../test_integration/test_ipahealthcheck.py | 48 +++++++++++++++++++
1 file changed, 48 insertions(+)
diff --git a/ipatests/test_integration/test_ipahealthcheck.py b/ipatests/test_integration/test_ipahealthcheck.py
index 0ebc7149f88394bf6b6355adbb88b3ad92697517..13fcc3d43545590e025598fcc9c9ee40f62dae76 100644
--- a/ipatests/test_integration/test_ipahealthcheck.py
+++ b/ipatests/test_integration/test_ipahealthcheck.py
@@ -26,6 +26,7 @@ from ipatests.pytest_ipa.integration import tasks
from ipaplatform.paths import paths
from ipaplatform.osinfo import osinfo
from ipaserver.install.installutils import resolve_ip_addresses_nss
+from ipatests.test_integration.test_caless import CALessBase
from ipatests.test_integration.base import IntegrationTest
from pkg_resources import parse_version
from ipatests.test_integration.test_cert import get_certmonger_fs_id
@@ -3135,3 +3136,50 @@ class TestIpaHealthCheckSingleMaster(IntegrationTest):
finally:
# cleanup
tasks.uninstall_master(self.master)
+
+
+class TestIPAHealthcheckWithCALess(CALessBase):
+ """
+ Install CALess server with user provided certificate.
+ """
+ num_replicas = 0
+
+ @classmethod
+ def install(cls, mh):
+ super(TestIPAHealthcheckWithCALess, cls).install(mh)
+ cls.create_pkcs12('ca1/server')
+ cls.prepare_cacert('ca1')
+ result = cls.install_server()
+ assert result.returncode == 0
+
+ @pytest.fixture
+ def expire_cert_warn(self):
+ """
+ Fixture to move the cert to about to expire, by moving the
+ system date using date -s command and revert it back
+ """
+ self.master.run_command(['date','-s', '+11Months10Days'])
+ yield
+ self.master.run_command(['date','-s', '-11Months10Days'])
+ self.master.run_command(['ipactl', 'restart'])
+
+ def test_ipahealthcheck_warns_on_expired_user_certs(self, expire_cert_warn):
+ """
+ This testcase checks that ipa-healthcheck warns
+ on expiring user-provided certificates.
+ """
+ msg = (
+ 'Request id {key} expires in {days} days. '
+ 'You need to manually renew this certificate.'
+ )
+ returncode, data = run_healthcheck(
+ self.master, "ipahealthcheck.ipa.certs",
+ "IPAUserProvidedExpirationCheck",
+ )
+ assert returncode == 1
+ certs = [d["kw"]["key"] for d in data]
+ assert set(certs) == {'HTTP', 'LDAP', 'KDC'}
+ for check in data:
+ assert check["result"] == "WARNING"
+ assert check["kw"]["key"] in ("LDAP", "HTTP", "KDC")
+ assert check["kw"]["msg"] == msg
--
2.49.0

View File

@ -1,278 +0,0 @@
From 0c98af9f70c62da3d3dea02b91a9330a5f9f669a Mon Sep 17 00:00:00 2001
From: David Hanina <dhanina@redhat.com>
Date: Thu, 22 May 2025 08:25:07 +0200
Subject: [PATCH] Warn when UID is out of local ID ranges
Provides simple warning when creating new user with uid out of
all local ranges, as this is the main culprit of breaking Kerberos, by
not generating ipantsecurityidentifier. We don't have to check for
user-mod, because modification never changes ipantsecurityidentifier.
We do not have to check groups, as groups are ignored for ipa without
AD trust. It's reasonable to revisit this in the future for group
creation and warn against groups out of ranges as well as
warn for users with groups without SID, in case AD trust is enabled.
Fixes: https://pagure.io/freeipa/issue/9781
Signed-off-by: David Hanina <dhanina@redhat.com>
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
---
ipalib/messages.py | 12 +++++
ipaserver/plugins/baseuser.py | 29 +++++++++++-
ipatests/test_xmlrpc/test_stageuser_plugin.py | 45 ++++++++++++++++++-
ipatests/test_xmlrpc/test_user_plugin.py | 43 ++++++++++++++++++
.../test_xmlrpc/tracker/stageuser_plugin.py | 22 +++++++++
5 files changed, 148 insertions(+), 3 deletions(-)
diff --git a/ipalib/messages.py b/ipalib/messages.py
index 6a70bbc7556126748cc2ec031fc2af36bfe76f74..a440ca6221d00e6d753c94f87396fc5d7ae177b5 100644
--- a/ipalib/messages.py
+++ b/ipalib/messages.py
@@ -519,6 +519,18 @@ class ServerUpgradeRequired(PublicMessage):
)
+class UidNumberOutOfLocalIDRange(PublicMessage):
+ """
+ **13034** UID Number is out of all local ID Ranges
+ """
+ errno = 13034
+ type = "warning"
+ format = _(
+ "User '%(user)s', with UID Number '%(uidnumber)d' is out of all ID "
+ "Ranges, 'SID' will not be correctly generated."
+ )
+
+
def iter_messages(variables, base):
"""Return a tuple with all subclasses
"""
diff --git a/ipaserver/plugins/baseuser.py b/ipaserver/plugins/baseuser.py
index 22393b8f6c5d3e40b57f11947d0a0358d3a087bc..21e05d4d983502fde76af549594d678d51451e9c 100644
--- a/ipaserver/plugins/baseuser.py
+++ b/ipaserver/plugins/baseuser.py
@@ -23,7 +23,7 @@ from cryptography.hazmat.primitives.serialization import load_pem_public_key
import re
import six
-from ipalib import api, errors, constants
+from ipalib import api, errors, constants, messages
from ipalib import (
Flag, Int, Password, Str, Bool, StrEnum, DateTime, DNParam)
from ipalib.parameters import Principal, Certificate, MAX_UINT32
@@ -198,6 +198,22 @@ def validate_passkey(ugettext, key):
return None
+def is_in_local_idrange(uidnumber):
+ result = api.Command.idrange_find(
+ iparangetype='ipa-local',
+ sizelimit=0,
+ )
+
+ for r in result['result']:
+ if 'ipabaserid' in r:
+ ipabaseid = int(r['ipabaseid'][0])
+ ipaidrangesize = int(r['ipaidrangesize'][0])
+ if ipabaseid <= uidnumber < ipabaseid + ipaidrangesize:
+ return True
+
+ return False
+
+
class baseuser(LDAPObject):
"""
baseuser object.
@@ -621,6 +637,17 @@ class baseuser_add(LDAPCreate):
add_missing_object_class(ldap, 'ipaidpuser', dn,
entry_attrs, update=False)
+ # Check and warn if we're out of local idrange
+ # Skip dynamically assigned uid, old clients say 999
+ uidnumber = entry_attrs.get('uidnumber')
+ if (
+ uidnumber != -1
+ and uidnumber != 999
+ and not is_in_local_idrange(uidnumber)
+ ):
+ self.add_message(messages.UidNumberOutOfLocalIDRange(
+ user=entry_attrs.get('uid'), uidnumber=uidnumber))
+
def post_common_callback(self, ldap, dn, entry_attrs, *keys, **options):
assert isinstance(dn, DN)
self.obj.convert_usercertificate_post(entry_attrs, **options)
diff --git a/ipatests/test_xmlrpc/test_stageuser_plugin.py b/ipatests/test_xmlrpc/test_stageuser_plugin.py
index 6ed593fbf24dd2e8ce087625b9cb4c21c9a3c145..dc4940a9983a410640d93efb1185ed4d394a8c2c 100644
--- a/ipatests/test_xmlrpc/test_stageuser_plugin.py
+++ b/ipatests/test_xmlrpc/test_stageuser_plugin.py
@@ -80,9 +80,7 @@ options_def = OrderedDict([
('car license', {u'carlicense': u'abc1234'}),
('SSH key', {u'ipasshpubkey': sshpubkey}),
('manager', {u'manager': u'auser1'}),
- ('user ID number', {u'uidnumber': uid}),
('group ID number', {u'gidnumber': gid}),
- ('UID and GID numbers', {u'uidnumber': uid, u'gidnumber': gid}),
('password', {u'userpassword': u'Secret123'}),
('random password', {u'random': True}),
])
@@ -90,6 +88,13 @@ options_def = OrderedDict([
options_ok = list(options_def.values())
options_ids = list(options_def.keys())
+warn_options_def = OrderedDict([
+ ('user ID number', {u'uidnumber': uid}),
+ ('UID and GID numbers', {u'uidnumber': uid, u'gidnumber': gid}),
+])
+
+warn_options_ok = list(warn_options_def.values())
+warn_options_ids = list(warn_options_def.keys())
@pytest.fixture(scope='class')
def stageduser(request, xmlrpc_setup):
@@ -108,6 +113,12 @@ def stageduser2(request, xmlrpc_setup):
return tracker.make_fixture_activate(request)
+@pytest.fixture(scope='class', params=warn_options_ok, ids=warn_options_ids)
+def warn_stageduser(request, xmlrpc_setup):
+ tracker = StageUserTracker(u'warnuser', u'staged', u'user', **request.param)
+ return tracker.make_fixture_activate(request)
+
+
@pytest.fixture(scope='class')
def user_activated(request, xmlrpc_setup):
tracker = UserTracker(u'suser2', u'staged', u'user')
@@ -273,6 +284,36 @@ class TestStagedUser(XMLRPC_test):
user_activated.delete()
+ def test_warn_create_with_attr(self, warn_stageduser, user, user_activated):
+ """ Tests creating a user with various valid attributes that throw
+ a warning listed in 'warn_options_ok' list"""
+ # create staged user with specified parameters
+ user.ensure_exists() # necessary for manager test
+ warn_stageduser.ensure_missing()
+ command = warn_stageduser.make_create_command()
+ result = command()
+ warn_stageduser.track_create()
+ warn_stageduser.check_create_with_warning(result, (13034,))
+
+ # activate user, verify that specified values were preserved
+ # after activation
+ user_activated.ensure_missing()
+ user_activated = UserTracker(
+ warn_stageduser.uid, warn_stageduser.givenname,
+ warn_stageduser.sn, **warn_stageduser.kwargs)
+ user_activated.create_from_staged(warn_stageduser)
+ command = warn_stageduser.make_activate_command()
+ result = command()
+ user_activated.check_activate(result)
+
+ # verify the staged user does not exist after activation
+ command = warn_stageduser.make_retrieve_command()
+ with raises_exact(errors.NotFound(
+ reason=u'%s: stage user not found' % warn_stageduser.uid)):
+ command()
+
+ user_activated.delete()
+
def test_delete_stageduser(self, stageduser):
stageduser.delete()
diff --git a/ipatests/test_xmlrpc/test_user_plugin.py b/ipatests/test_xmlrpc/test_user_plugin.py
index c0415cae6eb0389c91b804ab28dc2d9f131930c6..420c80213177dc513e10451c0c53506e879ba93f 100644
--- a/ipatests/test_xmlrpc/test_user_plugin.py
+++ b/ipatests/test_xmlrpc/test_user_plugin.py
@@ -826,6 +826,49 @@ class TestCreate(XMLRPC_test):
user_idp.check_create(result, ['ipaidpsub'])
user_idp.delete()
+ def test_out_of_idrange(self):
+ """Test ensuring warning is thrown when uid is out of range"""
+ uidnumber = 2000
+ testuser = UserTracker(
+ name="testwarning", givenname="test",
+ sn="warning", uidnumber=uidnumber
+ )
+ testuser.attrs.update(
+ uidnumber=[u'2000'],
+ )
+ command = testuser.make_create_command()
+ result = command()
+ result_messages = result['messages']
+ assert len(result_messages) == 1
+ assert result_messages[0]['type'] == 'warning'
+ assert result_messages[0]['code'] == 13034
+ testuser.delete()
+
+ def test_in_idrange(self):
+ """Test ensuring no warning is thrown when uid is in range"""
+ result = api.Command.idrange_find(
+ iparangetype='ipa-local',
+ sizelimit=0,
+ )
+
+ assert len(result) >= 1
+ ipabaseid = int(result['result'][0]['ipabaseid'][0])
+ ipaidrangesize = int(result['result'][0]['ipaidrangesize'][0])
+
+ # Take the last valid id, as we're not sure which has not yet been used
+ valid_id = ipabaseid + ipaidrangesize - 1
+ testuser = UserTracker(
+ name="testnowarning", givenname="test",
+ sn="nowarning", uidnumber=valid_id
+ )
+ testuser.attrs.update(
+ uidnumber=[str(valid_id)],
+ )
+ command = testuser.make_create_command()
+ result = command()
+ assert "messages" not in result
+ testuser.delete()
+
@pytest.mark.tier1
class TestUserWithGroup(XMLRPC_test):
diff --git a/ipatests/test_xmlrpc/tracker/stageuser_plugin.py b/ipatests/test_xmlrpc/tracker/stageuser_plugin.py
index 17744a98e9d4a8c5939e9c912b348689674becd9..93157ba3a44362c56a955c3d52d0d18678a9bc5d 100644
--- a/ipatests/test_xmlrpc/tracker/stageuser_plugin.py
+++ b/ipatests/test_xmlrpc/tracker/stageuser_plugin.py
@@ -3,6 +3,7 @@
#
import six
+import copy
from ipalib import api, errors
from ipaplatform.constants import constants as platformconstants
@@ -187,6 +188,27 @@ class StageUserTracker(PasskeyMixin, KerberosAliasMixin, Tracker):
result=self.filter_attrs(expected),
), result)
+ def check_create_with_warning(self, result,
+ warning_codes=(), extra_keys=()):
+ """ Check 'stageuser-add' command result """
+ expected = self.filter_attrs(self.create_keys | set(extra_keys))
+
+ result = copy.deepcopy(result)
+ assert 'messages' in result
+ assert len(result['messages']) == len(warning_codes)
+ codes = [message['code'] for message in result['messages']]
+ for code in warning_codes:
+ assert code in codes
+ codes.pop(codes.index(code))
+
+ del result['messages']
+
+ assert_deepequal(dict(
+ value=self.uid,
+ summary=u'Added stage user "%s"' % self.uid,
+ result=self.filter_attrs(expected),
+ ), result)
+
def check_delete(self, result):
""" Check 'stageuser-del' command result """
assert_deepequal(dict(
--
2.49.0

View File

@ -1,80 +0,0 @@
From 0155718308fa58f43f2ec8df240c1df1c929195e Mon Sep 17 00:00:00 2001
From: Florence Blanc-Renaud <flo@redhat.com>
Date: Mon, 2 Jun 2025 14:47:48 +0200
Subject: [PATCH] ipatests: fix invalid range creation in
test_ipa_idrange_fix.py
The test is creating a local ID range without rid-base and
secondary-rid-base in order to test the behavior of ipa-idrange-fix.
Since the patch for ticket #9779 it is not possible any more to call
ipa idrange-add for local range without these parameters. The test needs
to create the invalid local range using a direct ldapmodify instead.
Fixes: https://pagure.io/freeipa/issue/9801
Signed-off-by: Florence Blanc-Renaud <flo@redhat.com>
Reviewed-By: David Hanina <dhanina@redhat.com>
---
.../test_integration/test_ipa_idrange_fix.py | 39 ++++++++++++-------
1 file changed, 25 insertions(+), 14 deletions(-)
diff --git a/ipatests/test_integration/test_ipa_idrange_fix.py b/ipatests/test_integration/test_ipa_idrange_fix.py
index 0c915bd0931ed11a3aa86c533ee8748aa8a7ec07..6559818d3b290211ed421b652be7a424a3b51052 100644
--- a/ipatests/test_integration/test_ipa_idrange_fix.py
+++ b/ipatests/test_integration/test_ipa_idrange_fix.py
@@ -40,13 +40,18 @@ class TestIpaIdrangeFix(IntegrationTest):
def test_idrange_no_rid_bases(self):
"""Test ipa-idrange-fix command with IDrange with no RID bases."""
- self.master.run_command([
- "ipa",
- "idrange-add",
- "idrange_no_rid_bases",
- "--base-id", '10000',
- "--range-size", '20000',
- ])
+ # Use ldapmodify to create the range without rid bases
+ idrange_ldif = (
+ "dn: cn=idrange_no_rid_bases,cn=ranges,cn=etc,{suffix}\n"
+ "changetype: add\n"
+ "objectclass: top\n"
+ "objectclass: ipaIDrange\n"
+ "objectclass: ipadomainidrange\n"
+ "ipaRangeType: ipa-local\n"
+ "ipaBaseID: 10000\n"
+ "ipaIDRangeSize: 20000\n"
+ ).format(suffix=str(self.master.domain.basedn))
+ tasks.ldapmodify_dm(self.master, idrange_ldif)
result = self.master.run_command(["ipa-idrange-fix", "--unattended"])
expected_text = "RID bases updated for range 'idrange_no_rid_bases'"
@@ -62,13 +67,19 @@ class TestIpaIdrangeFix(IntegrationTest):
previously had a range with RID bases reversed - secondary lower than
primary. It is a valid configuration, so we should fix no-RID range.
"""
- self.master.run_command([
- "ipa",
- "idrange-add",
- "idrange_no_rid_bases",
- "--base-id", '10000',
- "--range-size", '20000',
- ])
+ # Use ldapmodify to create the range without rid bases
+ idrange_ldif = (
+ "dn: cn=idrange_no_rid_bases,cn=ranges,cn=etc,{suffix}\n"
+ "changetype: add\n"
+ "objectclass: top\n"
+ "objectclass: ipaIDrange\n"
+ "objectclass: ipadomainidrange\n"
+ "ipaRangeType: ipa-local\n"
+ "ipaBaseID: 10000\n"
+ "ipaIDRangeSize: 20000\n"
+ ).format(suffix=str(self.master.domain.basedn))
+ tasks.ldapmodify_dm(self.master, idrange_ldif)
+
self.master.run_command([
"ipa",
"idrange-add",
--
2.49.0

View File

@ -1,34 +0,0 @@
From 8ecf75b29429aa6f9e0fc0abfb1d74068b5d4f48 Mon Sep 17 00:00:00 2001
From: Florence Blanc-Renaud <flo@redhat.com>
Date: Mon, 6 Jan 2025 14:37:42 +0100
Subject: [PATCH] ipatests: certbot removed the --manual-public-ip-logging-ok
parameter
The certbot CLI has deprecated the parameter --manual-public-ip-logging-ok
and finally removed it from certbot 3.0.
The test test_acme.py is using this parameter and fails in rawhide.
Do not use this parameter any more.
Fixes: https://pagure.io/freeipa/issue/9724
Signed-off-by: Florence Blanc-Renaud <flo@redhat.com>
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
---
ipatests/test_integration/test_acme.py | 1 -
1 file changed, 1 deletion(-)
diff --git a/ipatests/test_integration/test_acme.py b/ipatests/test_integration/test_acme.py
index 4032d266a8dc72fae6ee11857c306aa3a21e51bc..b0d79182cfc98d23333266ee4c3d710bfffb4d73 100644
--- a/ipatests/test_integration/test_acme.py
+++ b/ipatests/test_integration/test_acme.py
@@ -311,7 +311,6 @@ class TestACME(CALessBase):
'--domain', self.clients[0].hostname,
'--preferred-challenges', 'dns',
'--manual',
- '--manual-public-ip-logging-ok',
'--manual-auth-hook', CERTBOT_DNS_IPA_SCRIPT,
'--manual-cleanup-hook', CERTBOT_DNS_IPA_SCRIPT,
'--key-type', 'rsa',
--
2.50.0

View File

@ -1,45 +0,0 @@
From d03164fc104588e88ad75483e7233b7fccacabb6 Mon Sep 17 00:00:00 2001
From: Florence Blanc-Renaud <flo@redhat.com>
Date: Thu, 6 Mar 2025 15:58:53 +0100
Subject: [PATCH] ipatests: adapt error code and message for samba 4.22
When establishing trust with an unreachable AD domain controller,
the error code and message have changed with samba 4.22.
Update the test to be compatible with any version of samba.
Fixes: https://pagure.io/freeipa/issue/9751
Signed-off-by: Florence Blanc-Renaud <flo@redhat.com>
Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
---
ipatests/test_integration/test_trust.py | 14 ++++++++++++--
1 file changed, 12 insertions(+), 2 deletions(-)
diff --git a/ipatests/test_integration/test_trust.py b/ipatests/test_integration/test_trust.py
index f71ec377b429b104241e85a3d04ee42c6721494c..4086cb30ac5d52ee595c1ecdbe86a8d511cbb704 100644
--- a/ipatests/test_integration/test_trust.py
+++ b/ipatests/test_integration/test_trust.py
@@ -1072,8 +1072,18 @@ class TestTrust(BaseTestTrust):
paths.VAR_LOG_HTTPD_ERROR,
encoding='utf-8'
)
- assert 'CIFS server communication error: code "3221225653", ' \
- 'message "{Device Timeout}' in httpd_error_log
+
+ # The error code and message changed in samba 4.22
+ old_msg = 'CIFS server communication error: code "3221225653", ' \
+ 'message "{Device Timeout}'
+ new_msg = 'CIFS server communication error: code "3221226021", ' \
+ 'message "The object was not found."'
+ result = self.master.run_command(["smbstatus", "-V"]).stdout_text
+ version = result.split()[1]
+ if tasks.parse_version(version) < tasks.parse_version('4.22.0rc4'):
+ assert old_msg in httpd_error_log
+ else:
+ assert new_msg in httpd_error_log
# Check that trust is successfully established with --server option
tasks.establish_trust_with_ad(
--
2.50.0

View File

@ -1,59 +0,0 @@
From d0f6979c0250bdf5299404bf711cef74dd458042 Mon Sep 17 00:00:00 2001
From: Antonio Torres <antorres@redhat.com>
Date: Wed, 4 Jun 2025 09:36:55 +0200
Subject: [PATCH] Fix inconsistency in manpage for DoT forwarder option
The example given in manpages for --dot-forwarder option is inconsistent
to the format that is required.
Fixes: https://pagure.io/freeipa/issue/9804
Signed-off-by: Antonio Torres <antorres@redhat.com>
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
---
install/tools/man/ipa-dns-install.1 | 2 +-
install/tools/man/ipa-replica-install.1 | 2 +-
install/tools/man/ipa-server-install.1 | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/install/tools/man/ipa-dns-install.1 b/install/tools/man/ipa-dns-install.1
index 6008d2028e1d91a39c8cbffc9e240121d8fd18f5..96eee1ef713a5069804088d68785f333c76b2369 100644
--- a/install/tools/man/ipa-dns-install.1
+++ b/install/tools/man/ipa-dns-install.1
@@ -74,7 +74,7 @@ An unattended installation that will never prompt for user input
Configure DNS over TLS.
.TP
\fB\-\-dot\-forwarder\fR=\fIIP_ADDRESS#HOSTNAME\fR
-Add a DNS-over-TLS-enabled forwarder in the format of ip#hostname, e.g.: dns.example.com#1.2.3.4. This option can be used multiple times.
+Add a DNS-over-TLS-enabled forwarder in the format of ip#hostname, e.g.: 1.2.3.4#dns.example.com. This option can be used multiple times.
.TP
\fB\-\-dns\-over\-tls\-cert\fR=\fIFILE\fR
Certificate to use for DNS over TLS. If empty, a new certificate will be requested from IPA CA.
diff --git a/install/tools/man/ipa-replica-install.1 b/install/tools/man/ipa-replica-install.1
index c55d21253f8d565e04605ea7d632ab9794cdd938..637c5c1b55031c3c1636477f867e519fbe98efeb 100644
--- a/install/tools/man/ipa-replica-install.1
+++ b/install/tools/man/ipa-replica-install.1
@@ -228,7 +228,7 @@ Disable DNSSEC validation on this server.
Configure DNS over TLS.
.TP
\fB\-\-dot\-forwarder\fR=\fIIP_ADDRESS#HOSTNAME\fR
-Add a DNS-over-TLS-enabled forwarder in the format of ip#hostname, e.g.: dns.example.com#1.2.3.4. This option can be used multiple times.
+Add a DNS-over-TLS-enabled forwarder in the format of ip#hostname, e.g.: 1.2.3.4#dns.example.com. This option can be used multiple times.
.TP
\fB\-\-dns\-over\-tls\-cert\fR=\fIFILE\fR
Certificate to use for DNS over TLS. If empty, a new certificate will be requested from IPA CA.
diff --git a/install/tools/man/ipa-server-install.1 b/install/tools/man/ipa-server-install.1
index 84d82531c50f05b9756eee967e13de90caf578f8..b9367ce1194724147a3f88d24f2d42854aff31a3 100644
--- a/install/tools/man/ipa-server-install.1
+++ b/install/tools/man/ipa-server-install.1
@@ -257,7 +257,7 @@ Allow creation of (reverse) zone even if the zone is already resolvable. Using t
Configure DNS over TLS.
.TP
\fB\-\-dot\-forwarder\fR=\fIIP_ADDRESS#HOSTNAME\fR
-Add a DNS-over-TLS-enabled forwarder in the format of ip#hostname, e.g.: dns.example.com#1.2.3.4. This option can be used multiple times.
+Add a DNS-over-TLS-enabled forwarder in the format of ip#hostname, e.g.: 1.2.3.4#dns.example.com. This option can be used multiple times.
.TP
\fB\-\-dns\-over\-tls\-cert\fR=\fIFILE\fR
Certificate to use for DNS over TLS. If empty, a new certificate will be requested from IPA CA.
--
2.50.0

View File

@ -1,188 +0,0 @@
From 796ed20092d554ee0c9e23295e346ec1e8a0bf6e Mon Sep 17 00:00:00 2001
From: Rob Crittenden <rcritten@redhat.com>
Date: Mon, 28 Apr 2025 13:43:40 -0400
Subject: [PATCH] Set krbCanonicalName=admin@REALM on the admin user
The admin must always own this name. If another entry has this
value set then remove it.
There is a uniqueness plugin for this attribute so the only two
possibilities are:
- no entry has this value set
- the admin user has this value set
- a different entry has the value set
Still, for robustness purposes, the upgrade plugin will handle
more entries.
Signed-off-by: Rob Crittenden <rcritten@redhat.com>
---
install/share/bootstrap-template.ldif | 1 +
.../updates/90-post_upgrade_plugins.update | 1 +
.../plugins/add_admin_krbcanonicalname.py | 79 +++++++++++++++++++
ipatests/test_integration/test_commands.py | 38 +++++++++
4 files changed, 119 insertions(+)
create mode 100644 ipaserver/install/plugins/add_admin_krbcanonicalname.py
diff --git a/install/share/bootstrap-template.ldif b/install/share/bootstrap-template.ldif
index 325eb8450c786899e7b5e4ae2ef8978f42a8425b..94972eb7270fc9224650bd414c740fc2e8f6c149 100644
--- a/install/share/bootstrap-template.ldif
+++ b/install/share/bootstrap-template.ldif
@@ -239,6 +239,7 @@ objectClass: ipasshuser
uid: admin
krbPrincipalName: admin@$REALM
krbPrincipalName: root@$REALM
+krbCanonicalName: admin@$REALM
cn: Administrator
sn: Administrator
uidNumber: $IDSTART
diff --git a/install/updates/90-post_upgrade_plugins.update b/install/updates/90-post_upgrade_plugins.update
index 7c3bba3e0317162d4739513e16b9fac973495c66..3d78c7b5a983f418502a7902a53ff8cd8d6847c4 100644
--- a/install/updates/90-post_upgrade_plugins.update
+++ b/install/updates/90-post_upgrade_plugins.update
@@ -25,6 +25,7 @@ plugin: update_mapping_Guests_to_nobody
plugin: fix_kra_people_entry
plugin: update_pwpolicy
plugin: update_pwpolicy_grace
+plugin: add_admin_krbcanonicalname
# last
# DNS version 1
diff --git a/ipaserver/install/plugins/add_admin_krbcanonicalname.py b/ipaserver/install/plugins/add_admin_krbcanonicalname.py
new file mode 100644
index 0000000000000000000000000000000000000000..e9ffdf55a3f9a1e182bcadda352eda99e536cf16
--- /dev/null
+++ b/ipaserver/install/plugins/add_admin_krbcanonicalname.py
@@ -0,0 +1,79 @@
+#
+# Copyright (C) 2025 FreeIPA Contributors see COPYING for license
+#
+
+from __future__ import absolute_import
+
+import logging
+
+from ipalib import errors
+from ipalib import Registry
+from ipalib import Updater
+from ipapython.dn import DN
+
+logger = logging.getLogger(__name__)
+
+register = Registry()
+
+
+@register()
+class add_admin_krbcanonicalname(Updater):
+ """
+ Ensures that only the admin user has the krbCanonicalName of
+ admin@$REALM.
+ """
+
+ def execute(self, **options):
+ ldap = self.api.Backend.ldap2
+
+ search_filter = (
+ "(krbcanonicalname=admin@{})".format(self.api.env.realm))
+ try:
+ (entries, _truncated) = ldap.find_entries(
+ filter=search_filter, base_dn=self.api.env.basedn,
+ time_limit=0, size_limit=0)
+ except errors.EmptyResult:
+ logger.debug("add_admin_krbcanonicalname: No user set with "
+ "admin krbcanonicalname")
+ entries = []
+ # fall through
+ except errors.ExecutionError as e:
+ logger.error("add_admin_krbcanonicalname: Can not get list "
+ "of krbcanonicalname: %s", e)
+ return False, []
+
+ admin_set = False
+ # admin should be only user with admin@ as krbcanonicalname
+ # It has a uniquness setting so there can be only one, we
+ # just didn't automatically set it for admin.
+ for entry in entries:
+ if entry.single_value.get('uid') != 'admin':
+ logger.critical(
+ "add_admin_krbcanonicalname: "
+ "entry %s has a krbcanonicalname of admin. Removing.",
+ entry.dn)
+ del entry['krbcanonicalname']
+ ldap.update_entry(entry)
+ else:
+ admin_set = True
+
+ if not admin_set:
+ dn = DN(
+ ('uid', 'admin'),
+ self.api.env.container_user,
+ self.api.env.basedn)
+ entry = ldap.get_entry(dn)
+ entry['krbcanonicalname'] = 'admin@%s' % self.api.env.realm
+ try:
+ ldap.update_entry(entry)
+ except errors.DuplicateEntry:
+ logger.critical(
+ "add_admin_krbcanonicalname: "
+ "Failed to set krbcanonicalname on admin. It is set "
+ "on another entry.")
+ except errors.ExecutionError as e:
+ logger.critical(
+ "add_admin_krbcanonicalname: "
+ "Failed to set krbcanonicalname on admin: %s", e)
+
+ return False, []
diff --git a/ipatests/test_integration/test_commands.py b/ipatests/test_integration/test_commands.py
index 9cad5772127bcd860aeecc8dabe73d5f160faf7b..ad97affe62e15c68442239d669032f0c84e7f5c9 100644
--- a/ipatests/test_integration/test_commands.py
+++ b/ipatests/test_integration/test_commands.py
@@ -2179,6 +2179,44 @@ class TestIPACommandWithoutReplica(IntegrationTest):
assert isrgrootx1_nick in result
+ def test_unique_krbcanonicalname(self):
+ """Verify that the uniqueness for krbcanonicalname is working"""
+ master = self.master
+
+ base_dn = str(master.domain.basedn)
+ hostname = master.hostname
+ realm = master.domain.realm
+ principal = f'test/{hostname}@{realm}'
+ entry_ldif = textwrap.dedent("""
+ dn: krbprincipalname={principal},cn=services,cn=accounts,{base_dn}
+ changetype: add
+ ipakrbprincipalalias: test/{hostname}@{realm}
+ krbprincipalname: {principal}
+ objectclass: ipakrbprincipal
+ objectclass: ipaobject
+ objectclass: ipaservice
+ objectclass: krbprincipal
+ objectclass: krbprincipalaux
+ objectclass: top
+ krbcanonicalname: admin@{realm}
+ managedby: fqdn={hostname},cn=computers,cn=accounts,{base_dn}
+ """).format(
+ base_dn=base_dn,
+ hostname=hostname,
+ principal=principal,
+ realm=realm)
+ tasks.kdestroy_all(master)
+ master.run_command(
+ ['kinit', '-kt', '/etc/krb5.keytab', f'host/{hostname}@{realm}'])
+ args = [
+ 'ldapmodify',
+ '-Y',
+ 'GSSAPI'
+ ]
+ result = master.run_command(args, stdin_text=entry_ldif,
+ raiseonerr=False)
+ assert "entry with the same attribute value" in result.stderr_text
+
class TestIPAautomount(IntegrationTest):
@classmethod
--
2.50.0

View File

@ -1,43 +0,0 @@
From 7b4317979080cb8efe901e2ab491f6f4e4ccad15 Mon Sep 17 00:00:00 2001
From: Thomas Woerner <twoerner@redhat.com>
Date: Thu, 12 Jun 2025 17:44:44 +0200
Subject: [PATCH] ipa-client-install: Fix nsupdate issues when dns_over_tls is
enabled
The server commands for nsupdate.txt to define the server with the port
853 have been added for dns_over_tls. These commands do not have a leading
newline. This results in a syntax error as the next line is added to the
command.
Fixes: https://pagure.io/freeipa/issue/9806
Signed-off-by: Thomas Woerner <twoerner@redhat.com>
Reviewed-By: David Hanina <dhanina@redhat.com>
---
ipaclient/install/client.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/ipaclient/install/client.py b/ipaclient/install/client.py
index 372daa51e4647023dde76e183189eeebdd9525b8..43a71828335ad655ad067b5320572d40bee1a44b 100644
--- a/ipaclient/install/client.py
+++ b/ipaclient/install/client.py
@@ -1540,7 +1540,7 @@ def update_dns(server, hostname, options):
update_txt = "debug\n"
if options.dns_over_tls:
- update_txt += "server %s 853" % server
+ update_txt += "server %s 853\n" % server
update_txt += ipautil.template_str(DELETE_TEMPLATE_A,
dict(HOSTNAME=hostname))
update_txt += ipautil.template_str(DELETE_TEMPLATE_AAAA,
@@ -1788,7 +1788,7 @@ def update_ssh_keys(hostname, ssh_dir, options, server):
update_txt = 'debug\n'
if options.dns_over_tls:
- update_txt += "server %s 853" % server
+ update_txt += "server %s 853\n" % server
update_txt += 'update delete %s. IN SSHFP\nshow\nsend\n' % hostname
for pubkey in pubkeys:
sshfp = pubkey.fingerprint_dns_sha1()
--
2.50.0

View File

@ -1,90 +0,0 @@
From 39e92c4033d0ecd702281f3ecbeac3b5f654e973 Mon Sep 17 00:00:00 2001
From: Florence Blanc-Renaud <flo@redhat.com>
Date: Thu, 19 Jun 2025 17:17:44 +0200
Subject: [PATCH] ipatests: fix test_adtrust_install_with_non_ipa_user
Fix the test scenario:
create a user with a second krbprincipalname but no
krbcanonical name.
kinit -E with the other name
try ipa-adtrust-install with the other name
It should fail with the error message 'user not found'
Fixes: https://pagure.io/freeipa/issue/9812
Signed-off-by: Florence Blanc-Renaud <flo@redhat.com>
Reviewed-By: Alexander Bokovoy <abbra@users.noreply.github.com>
---
.../test_integration/test_adtrust_install.py | 48 ++++++++++++++-----
1 file changed, 36 insertions(+), 12 deletions(-)
diff --git a/ipatests/test_integration/test_adtrust_install.py b/ipatests/test_integration/test_adtrust_install.py
index 99d3029443ea39bb5f0e333a5087d30291191968..09e227ec8125e90b37d1d92f0512f9819f5b48c3 100644
--- a/ipatests/test_integration/test_adtrust_install.py
+++ b/ipatests/test_integration/test_adtrust_install.py
@@ -360,27 +360,51 @@ class TestIpaAdTrustInstall(IntegrationTest):
assert msg in result.stdout_text
assert result.returncode == 0
- def test_adtrust_install_with_non_ipa_user(self):
+ @pytest.fixture
+ def create_user(self):
+ # create a user with 'othername' as 2nd krbprincipalname but
+ # no krbcanonicalname
+ basedn = self.master.domain.basedn
+ self.test_user = 'idmuser'
+ self.test_alias = 'othername'
+ tasks.create_active_user(
+ self.master, self.test_user, self.master.config.admin_password,
+ first=self.test_user, last=self.test_user)
+ user_update_ldif = textwrap.dedent("""
+ dn: uid={user},cn=users,cn=accounts,{base_dn}
+ changetype: modify
+ add: krbprincipalname
+ krbprincipalname: {alias}@{realm}
+ -
+ delete: krbcanonicalname
+ """.format(base_dn=basedn, user=self.test_user,
+ alias=self.test_alias, realm=self.master.domain.realm))
+ tasks.ldapmodify_dm(self.master, user_update_ldif)
+ yield
+ tasks.kinit_admin(self.master)
+ self.master.run_command(["ipa", "user-del", self.test_user])
+
+ def test_adtrust_install_with_user_missing_krbcanonical(self, create_user):
"""
Test that ipa-adtrust-install command returns
- an error when kinit is done as alias
- i.e root which is not an ipa user.
+ an error when kinit is done as an alias
+ for which there is no krbcanonicalname.
"""
- msg = (
- 'Unrecognized error during check of admin rights: '
- 'root: user not found'
- )
- user = 'root'
+ self.master.run_command(["kdestroy", "-A"])
self.master.run_command(
- ["kinit", "-E", user],
- stdin_text=self.master.config.admin_password
- )
+ ["kinit", "-E", self.test_alias],
+ stdin_text=self.master.config.admin_password)
+
result = self.master.run_command(
- ["ipa-adtrust-install", "-A", user,
+ ["ipa-adtrust-install", "-A", self.test_alias,
"-a", self.master.config.admin_password,
"-U"], raiseonerr=False
)
assert result.returncode != 0
+ msg = (
+ 'Unrecognized error during check of admin rights: '
+ '{alias}: user not found'
+ ).format(alias=self.test_alias)
assert msg in result.stderr_text
def test_adtrust_install_as_regular_ipa_user(self):
--
2.50.0

View File

@ -1,63 +0,0 @@
From fba7aa10c8487116075d56c8dedeebefc40b74eb Mon Sep 17 00:00:00 2001
From: Florence Blanc-Renaud <flo@redhat.com>
Date: Mon, 16 Jun 2025 18:15:22 +0200
Subject: [PATCH] ipa-idrange-fix: check that IPA server is installed
If ipa-idrange-fix is called on a system where the server is not configured,
it crashes with a Traceback when trying to access api.env.basedn.
Check that IPA server is configured before processing further
ipatests: add test launching ipa-idrange-fix on unconfigured server
Fixes: https://pagure.io/freeipa/issue/9809
Signed-off-by: Florence Blanc-Renaud <flo@redhat.com>
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
Reviewed-By: David Hanina <dhanina@redhat.com>
---
ipaserver/install/ipa_idrange_fix.py | 3 +++
.../test_integration/test_cli_ipa_not_configured.py | 10 ++++++++++
2 files changed, 13 insertions(+)
diff --git a/ipaserver/install/ipa_idrange_fix.py b/ipaserver/install/ipa_idrange_fix.py
index c6c67ae9330e2d0184efc09d09a84216ef0772a6..cd21ed4281d37b013537174fb4ab9e773382990e 100644
--- a/ipaserver/install/ipa_idrange_fix.py
+++ b/ipaserver/install/ipa_idrange_fix.py
@@ -10,6 +10,7 @@ from ipalib import api, errors
from ipapython.admintool import AdminTool
from ipapython.dn import DN
from ipapython import ipautil
+from ipaserver.install.installutils import check_server_configuration
from typing import List, Tuple
logger = logging.getLogger(__name__)
@@ -169,6 +170,8 @@ for confirmation",
super().validate_options(needs_root)
def run(self):
+ check_server_configuration()
+
api.bootstrap(in_server=True)
api.finalize()
diff --git a/ipatests/test_integration/test_cli_ipa_not_configured.py b/ipatests/test_integration/test_cli_ipa_not_configured.py
index 1bf36d8ee6e9d1e6019786f3b62d79dbce22655e..7c5601247d7d3c98cf6513eecc333de6aa59d704 100644
--- a/ipatests/test_integration/test_cli_ipa_not_configured.py
+++ b/ipatests/test_integration/test_cli_ipa_not_configured.py
@@ -22,3 +22,13 @@ class TestIPANotConfigured(IntegrationTest):
assert (exp_str in cmd.stderr_text and
cmd.returncode == SERVER_NOT_CONFIGURED and
unexp_str not in cmd.stderr_text)
+
+ def test_ipa_idrange_fix(self):
+ """
+ Test for https://pagure.io/freeipa/issue/9809
+ Launch ipa-idrange-fix command when the server is not configured.
+ """
+ exp_str = "IPA is not configured"
+ cmd = self.master.run_command(["ipa-idrange-fix"], raiseonerr=False)
+ assert (exp_str in cmd.stderr_text
+ and cmd.returncode == SERVER_NOT_CONFIGURED)
--
2.50.0

View File

@ -1,74 +0,0 @@
From ceaa1c9a244499534343dc667227e47a923212ee Mon Sep 17 00:00:00 2001
From: Mark Reynolds <mreynolds@redhat.com>
Date: Tue, 17 Jun 2025 12:50:36 -0400
Subject: [PATCH] ipa-migrate - only remove repl state attribute options
Improve how we process attributes that might include replication state
data. Previously we only cared about ";binary" but there are other
attribute options that are used in IPA. Now we completely break down the
attribute into each option and rebuild it without any repl state options
Fixes: https://pagure.io/freeipa/issue/9784
Signed-off-by: Mark Reynolds <mreynolds@redhat.com>
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
---
ipaserver/install/ipa_migrate.py | 17 +++++++++--------
ipaserver/install/ipa_migrate_constants.py | 2 ++
2 files changed, 11 insertions(+), 8 deletions(-)
diff --git a/ipaserver/install/ipa_migrate.py b/ipaserver/install/ipa_migrate.py
index a24a2ab7a5ffd4cf1d59179f14e2f5d348fd57e2..b26fb66853ce91a139c3193753b34bed1ce2f586 100644
--- a/ipaserver/install/ipa_migrate.py
+++ b/ipaserver/install/ipa_migrate.py
@@ -33,7 +33,7 @@ from ipaserver.install.ipa_migrate_constants import (
DS_CONFIG, DB_OBJECTS, DS_INDEXES, BIND_DN, LOG_FILE_NAME,
STRIP_OP_ATTRS, STRIP_ATTRS, STRIP_OC, PROD_ATTRS,
DNA_REGEN_VAL, DNA_REGEN_ATTRS, NIS_PLUGIN, IGNORE_ATTRS,
- DB_EXCLUDE_TREES, POLICY_OP_ATTRS
+ DB_EXCLUDE_TREES, POLICY_OP_ATTRS, STATE_OPTIONS
)
"""
@@ -202,14 +202,15 @@ def decode_attr_vals(entry_attrs):
decoded_attrs = {}
for attr in entry_attrs:
vals = ensure_list_str(entry_attrs[attr])
- # Remove replication state data, but don't remove ";binary"
- # e.g. userCertififccate;binary;adcsn=<CSN>
+ # Remove "only" replication state data, but don't remove other attr
+ # options like ";binary"
+ # e.g. userCertificate;binary;adcsn=<CSN>
parts = attr.split(";")
- if len(parts) > 1 and not attr.endswith(";binary"):
- if parts[1] == "binary":
- attr = parts[0] + ";binary"
- else:
- attr = parts[0]
+ attr_parts = [
+ parts[0]] + [p for p in parts[1:]
+ if not any(p.startswith(opt)
+ for opt in STATE_OPTIONS)]
+ attr = (';').join(attr_parts)
decoded_attrs[attr] = vals
return decoded_attrs
diff --git a/ipaserver/install/ipa_migrate_constants.py b/ipaserver/install/ipa_migrate_constants.py
index 4beaa4f42a667ba83008213075b3ded782a83260..19cd5141316d018cf1d81f8db174197f4c5f15ff 100644
--- a/ipaserver/install/ipa_migrate_constants.py
+++ b/ipaserver/install/ipa_migrate_constants.py
@@ -117,6 +117,8 @@ AD_TRUST_ATTRS = [ # ipaNTTrustedDomain objectclass
'ipantadditionalsuffixes',
]
+STATE_OPTIONS = ('adcsn-', 'mdcsn-', 'vucsn-', 'vdcsn-')
+
DNA_REGEN_VAL = "-1"
DNA_REGEN_ATTRS = [
--
2.50.0

View File

@ -1,224 +0,0 @@
From cafaed1c2119fb2e25209eefac74bc21ccab3dcb Mon Sep 17 00:00:00 2001
From: Julien Rische <jrische@redhat.com>
Date: Thu, 25 Jul 2024 19:35:33 +0200
Subject: [PATCH] ipa-kdb: support storing multiple KVNO for the same principal
All MIT krb5 keys are encoded with an encryption type identifier and a
KVNO (key version number). The KVNO is referring to the original
credentials string (and its associated salt) which was used to generate
the key using the derivation function of the associated encryption type.
So far, when a set of Kerberos keys was provided to ipa-kdb, only the
newest KVNO ones were saved in a single "krbPrincipalKey" LDAP
attribute. All the older ones were deleted in the process.
This commit allows to keep older keys by splitting them in multiple
"krbPrincipalKey" attribute based on their KVNO.
Fixes: https://pagure.io/freeipa/issue/9370
Signed-off-by: Julien Rische <jrische@redhat.com>
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
---
daemons/ipa-kdb/ipa_kdb_principals.c | 125 +++++++++++++++++++++++----
util/ipa_krb5.c | 7 ++
2 files changed, 115 insertions(+), 17 deletions(-)
diff --git a/daemons/ipa-kdb/ipa_kdb_principals.c b/daemons/ipa-kdb/ipa_kdb_principals.c
index 114957b884786dd3ca3b01c47f6bb82e8a040beb..19998c2a38b5d8ae80aeedeb003f54241d2c2a9f 100644
--- a/daemons/ipa-kdb/ipa_kdb_principals.c
+++ b/daemons/ipa-kdb/ipa_kdb_principals.c
@@ -22,6 +22,7 @@
#include "ipa_kdb.h"
#include "ipa_krb5.h"
+#include <stdlib.h>
#include <unicase.h>
/*
@@ -279,22 +280,64 @@ done:
static int ipadb_ldap_attr_to_key_data(LDAP *lcontext, LDAPMessage *le,
char *attrname,
krb5_key_data **result, int *num,
- krb5_kvno *res_mkvno)
+ krb5_kvno *mkvno)
{
- struct berval **vals;
- int mkvno;
+ struct berval **vals, **p;
+ krb5_key_data *cur_res = NULL, *fin_res = NULL, *tmp_res;
+ int fin_mkvno = 0, cur_mkvno, cur_num, fin_num = 0;
int ret;
vals = ldap_get_values_len(lcontext, le, attrname);
- if (!vals) {
+ if (!vals)
return ENOENT;
+
+ for (p = vals; *p; ++p) {
+ ret = ber_decode_krb5_key_data(*p, &cur_mkvno, &cur_num, &cur_res);
+ if (ret)
+ goto end;
+
+ /* All keys in a principal entry should be encrypted with the same
+ * master key. */
+ if (fin_mkvno == 0) {
+ fin_mkvno = cur_mkvno;
+ } else if (cur_mkvno != fin_mkvno) {
+ ret = EINVAL;
+ goto end;
+ }
+
+ if (!fin_res) {
+ fin_res = cur_res;
+ } else {
+ tmp_res = realloc(fin_res, (fin_num + cur_num) * sizeof(*fin_res));
+ if (!tmp_res) {
+ ret = ENOMEM;
+ goto end;
+ } else {
+ fin_res = tmp_res;
+ }
+
+ memcpy(fin_res + fin_num, cur_res, cur_num * sizeof(*fin_res));
+ free(cur_res);
+ }
+
+ cur_res = NULL;
+ fin_num += cur_num;
}
- ret = ber_decode_krb5_key_data(vals[0], &mkvno, num, result);
- ldap_value_free_len(vals);
- if (ret == 0) {
- *res_mkvno = mkvno;
+ if (mkvno)
+ *mkvno = fin_mkvno;
+ if (num)
+ *num = fin_num;
+ if (result) {
+ *result = fin_res;
+ } else {
+ free(fin_res);
}
+
+end:
+ ldap_value_free_len(vals);
+ if (cur_res && fin_res != cur_res)
+ free(cur_res);
return ret;
}
@@ -2532,15 +2575,26 @@ static krb5_error_code ipadb_get_mkvno_from_tl_data(krb5_tl_data *tl_data,
return 0;
}
+static int desc_key_data(const void *a, const void *b)
+{
+ const krb5_key_data *ka = a;
+ const krb5_key_data *kb = b;
+
+ return ka->key_data_kvno != kb->key_data_kvno
+ ? kb->key_data_kvno - ka->key_data_kvno
+ : kb->key_data_type[0] - ka->key_data_type[0];
+}
+
static krb5_error_code ipadb_get_ldap_mod_key_data(struct ipadb_mods *imods,
krb5_key_data *key_data,
int n_key_data, int mkvno,
int mod_op)
{
krb5_error_code kerr;
- struct berval *bval = NULL;
+ krb5_key_data *kvno_kdata;
+ struct berval **bvals = NULL;
LDAPMod *mod;
- int ret;
+ int i, j, begin, n_kvno;
/* If the key data is empty, remove all keys. */
if (n_key_data == 0 || key_data == NULL) {
@@ -2559,19 +2613,56 @@ static krb5_error_code ipadb_get_ldap_mod_key_data(struct ipadb_mods *imods,
return 0;
}
- ret = ber_encode_krb5_key_data(key_data, n_key_data, mkvno, &bval);
- if (ret != 0) {
- kerr = ret;
+ /* Copy key list. */
+ kvno_kdata = calloc(n_key_data, sizeof(*kvno_kdata));
+ if (!kvno_kdata)
+ return ENOMEM;
+
+ memcpy(kvno_kdata, key_data, n_key_data * sizeof(*kvno_kdata));
+
+ /* Make sure the key list is sorted by KVNO and enctype. */
+ qsort(kvno_kdata, n_key_data, sizeof(*kvno_kdata), desc_key_data);
+
+ /* Count number of distinct KVNOs. */
+ for (i = 1, n_kvno = 1; i < n_key_data; ++i) {
+ if (kvno_kdata[i - 1].key_data_kvno != kvno_kdata[i].key_data_kvno)
+ ++n_kvno;
+ }
+
+ bvals = calloc(n_kvno, sizeof(*bvals));
+ if (!bvals) {
+ kerr = ENOMEM;
goto done;
}
- kerr = ipadb_get_ldap_mod_bvalues(imods, "krbPrincipalKey",
- &bval, 1, mod_op);
+ /* Add a "krbPrincipalKey" attribute for each KVNO. */
+ for (i = 0, j = 0, begin = 0; i < n_key_data; ++i) {
+ if (kvno_kdata[begin].key_data_kvno != kvno_kdata[i].key_data_kvno) {
+ kerr = ber_encode_krb5_key_data(kvno_kdata + begin, i - begin,
+ mkvno, bvals + j);
+ if (kerr)
+ goto done;
+
+ begin = i;
+ ++j;
+ }
+ }
+
+ kerr = ber_encode_krb5_key_data(kvno_kdata + begin, i - begin, mkvno,
+ bvals + j);
+ if (kerr)
+ goto done;
+
+ kerr = ipadb_get_ldap_mod_bvalues(imods, "krbPrincipalKey", bvals, n_kvno,
+ mod_op);
done:
- if (kerr) {
- ber_bvfree(bval);
+ if (kerr && bvals) {
+ for (i = 0; i < n_kvno; ++i)
+ ber_bvfree(bvals[i]);
+ free(bvals);
}
+ free(kvno_kdata);
return kerr;
}
diff --git a/util/ipa_krb5.c b/util/ipa_krb5.c
index 0087e53e689fc4dc5549908b3eadd6d963d94489..fbd4d57de6728cd6d9449f25c7eb0d5e48269dbe 100644
--- a/util/ipa_krb5.c
+++ b/util/ipa_krb5.c
@@ -394,6 +394,13 @@ int ber_encode_krb5_key_data(krb5_key_data *data,
for (i = 0; i < numk; i++) {
+ /* All keys must have the same KVNO, because there is only one attribute
+ * for all of them. */
+ if (data[i].key_data_kvno != data[0].key_data_kvno) {
+ ret = EINVAL;
+ goto done;
+ }
+
ret = ber_printf(be, "{");
if (ret == -1) {
ret = EFAULT;
--
2.50.0

View File

@ -1,214 +0,0 @@
From fb90a9492150d668003984345dcac874c4e26e61 Mon Sep 17 00:00:00 2001
From: Julien Rische <jrische@redhat.com>
Date: Wed, 12 Mar 2025 13:49:47 +0100
Subject: [PATCH] Use ipaplatform tasks for krb5 enctypes
Provide the master key encryption type and the list of supported
encryption types as ipaplatform.<platform>.tasks methods. This allows
to generate the list at runtime based on the environment (e.g. FIPS) and
override the list depending of the platform.
3DES HMAC-SHA1 encryption type is now removed from supported encryption
types if it is present.
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
---
install/share/kerberos.ldif | 18 +-----------------
install/updates/50-krbenctypes.update | 11 +----------
ipaplatform/base/tasks.py | 25 +++++++++++++++++++++++++
ipaplatform/redhat/tasks.py | 8 ++++++++
ipaserver/install/krbinstance.py | 27 +++++++++++----------------
ipaserver/install/ldapupdate.py | 17 ++++++++++++++++-
6 files changed, 62 insertions(+), 44 deletions(-)
diff --git a/install/share/kerberos.ldif b/install/share/kerberos.ldif
index 3a5c30ec8533b6d60c614f276090bf2f4cfcd075..78c29923b166594c0c977304f4eed30823e69afb 100644
--- a/install/share/kerberos.ldif
+++ b/install/share/kerberos.ldif
@@ -14,25 +14,9 @@ objectClass: krbrealmcontainer
objectClass: krbticketpolicyaux
krbSubTrees: $SUFFIX
krbSearchScope: 2
-krbSupportedEncSaltTypes: aes256-cts:normal
-krbSupportedEncSaltTypes: aes256-cts:special
-krbSupportedEncSaltTypes: aes128-cts:normal
-krbSupportedEncSaltTypes: aes128-cts:special
-krbSupportedEncSaltTypes: aes128-sha2:normal
-krbSupportedEncSaltTypes: aes128-sha2:special
-krbSupportedEncSaltTypes: aes256-sha2:normal
-krbSupportedEncSaltTypes: aes256-sha2:special
-${FIPS}krbSupportedEncSaltTypes: camellia128-cts-cmac:normal
-${FIPS}krbSupportedEncSaltTypes: camellia128-cts-cmac:special
-${FIPS}krbSupportedEncSaltTypes: camellia256-cts-cmac:normal
-${FIPS}krbSupportedEncSaltTypes: camellia256-cts-cmac:special
krbMaxTicketLife: 86400
krbMaxRenewableAge: 604800
-krbDefaultEncSaltTypes: aes256-sha2:special
-krbDefaultEncSaltTypes: aes128-sha2:special
-krbDefaultEncSaltTypes: aes256-cts:special
-krbDefaultEncSaltTypes: aes128-cts:special
-
+${LDIF_SUPPORTED_ENCTYPES}${LDIF_DEFAULT_ENCTYPES}
# Default password Policy
dn: cn=global_policy,cn=$REALM,cn=kerberos,$SUFFIX
changetype: add
diff --git a/install/updates/50-krbenctypes.update b/install/updates/50-krbenctypes.update
index 1bf2bf33a6566586639767771dff501d91a03508..3061b98c94b255dd99d77ed32e155f0447c45413 100644
--- a/install/updates/50-krbenctypes.update
+++ b/install/updates/50-krbenctypes.update
@@ -1,11 +1,2 @@
dn: cn=$REALM,cn=kerberos,$SUFFIX
-${FIPS}add: krbSupportedEncSaltTypes: camellia128-cts-cmac:normal
-${FIPS}add: krbSupportedEncSaltTypes: camellia128-cts-cmac:special
-${FIPS}add: krbSupportedEncSaltTypes: camellia256-cts-cmac:normal
-${FIPS}add: krbSupportedEncSaltTypes: camellia256-cts-cmac:special
-add: krbSupportedEncSaltTypes: aes128-sha2:normal
-add: krbSupportedEncSaltTypes: aes128-sha2:special
-add: krbSupportedEncSaltTypes: aes256-sha2:normal
-add: krbSupportedEncSaltTypes: aes256-sha2:special
-remove: krbDefaultEncSaltTypes: des3-hmac-sha1:special
-remove: krbDefaultEncSaltTypes: arcfour-hmac:special
+${ADD_SUPPORTED_ENCTYPES}${ADD_DEFAULT_ENCTYPES}${REMOVE_SUPPORTED_ENCTYPES}${REMOVE_DEFAULT_ENCTYPES}
diff --git a/ipaplatform/base/tasks.py b/ipaplatform/base/tasks.py
index 4108a7ced240c3fa98a2bd58d21f655227d95a55..9e221d872e7ca9ac0607ff29e1b51dedcf688d75 100644
--- a/ipaplatform/base/tasks.py
+++ b/ipaplatform/base/tasks.py
@@ -540,4 +540,29 @@ class BaseTaskNamespace:
'ipa-client-automount-nsswitch', 'previous-automount'
)
+ def get_masterkey_enctype(self):
+ return 'aes256-sha2'
+
+ # Encryption types allowed for Kerberos keys
+ def get_supported_enctypes(self):
+ return ('aes256-sha2:special', 'aes128-sha2:special',
+ 'aes256-sha2:normal', 'aes128-sha2:normal',
+ 'aes256-cts:special', 'aes128-cts:special',
+ 'aes256-cts:normal', 'aes128-cts:normal',
+ 'camellia256-cts:special', 'camellia128-cts:special',
+ 'camellia256-cts:normal', 'camellia128-cts:normal')
+
+ # Encryption types used in the past, not supported anymore
+ def get_removed_supported_enctypes(self):
+ return ('des3-hmac-sha1:special')
+
+ # Encryption types used by default when generating Kerberos keys
+ def get_default_enctypes(self):
+ return ('aes256-sha2:special', 'aes128-sha2:special',
+ 'aes256-cts:special', 'aes128-cts:special')
+
+ # Encryption types no longer used by default when generating keys
+ def get_removed_default_enctypes(self):
+ return ('des3-hmac-sha1:special', 'arcfour-hmac:special')
+
tasks = BaseTaskNamespace()
diff --git a/ipaplatform/redhat/tasks.py b/ipaplatform/redhat/tasks.py
index 18203bf0e25838ef529d4da998a84bdfbb715ce6..4953dc78ab0437b4c0041c1194f06b30d8628228 100644
--- a/ipaplatform/redhat/tasks.py
+++ b/ipaplatform/redhat/tasks.py
@@ -751,5 +751,13 @@ class RedHatTaskNamespace(BaseTaskNamespace):
logger.info("It may happen if the configuration was done "
"using authconfig instead of authselect")
+ def get_supported_enctypes(self):
+ enctypes = super().get_supported_enctypes()
+
+ if not self.is_fips_enabled():
+ return enctypes
+
+ return tuple(e for e in enctypes if not e.startswith('camellia'))
+
tasks = RedHatTaskNamespace()
diff --git a/ipaserver/install/krbinstance.py b/ipaserver/install/krbinstance.py
index 9f0eea56301d66391b6ba87a7a2a4b7ed4e9eaa5..fb1a2884226a45e3c76f32432fe9b0831eebb888 100644
--- a/ipaserver/install/krbinstance.py
+++ b/ipaserver/install/krbinstance.py
@@ -55,14 +55,6 @@ from ipaplatform.paths import paths
logger = logging.getLogger(__name__)
-MASTER_KEY_TYPE = 'aes256-sha2'
-SUPPORTED_ENCTYPES = ('aes256-sha2:special', 'aes128-sha2:special',
- 'aes256-sha2:normal', 'aes128-sha2:normal',
- 'aes256-cts:special', 'aes128-cts:special',
- 'aes256-cts:normal', 'aes128-cts:normal',
- 'camellia256-cts:special', 'camellia128-cts:special',
- 'camellia256-cts:normal', 'camellia128-cts:normal')
-
def get_pkinit_request_ca():
"""
@@ -299,15 +291,18 @@ class KrbInstance(service.Service):
INCLUDES=includes,
FIPS='#' if fips_enabled else '')
- if fips_enabled:
- supported_enctypes = list(
- filter(lambda e: not e.startswith('camellia'),
- SUPPORTED_ENCTYPES))
- else:
- supported_enctypes = SUPPORTED_ENCTYPES
- self.sub_dict['SUPPORTED_ENCTYPES'] = ' '.join(supported_enctypes)
+ supported_enctypes = tasks.get_supported_enctypes()
+ str_supported_enctypes = ' '.join(supported_enctypes)
+ ldif_supported_enctypes = ''.join(f'krbSupportedEncSaltTypes: {e}\n'
+ for e in supported_enctypes)
+ ldif_default_enctypes = ''.join(f'krbDefaultEncSaltTypes: {e}\n'
+ for e in tasks.get_default_enctypes())
+
+ self.sub_dict['SUPPORTED_ENCTYPES'] = str_supported_enctypes
+ self.sub_dict['LDIF_SUPPORTED_ENCTYPES'] = ldif_supported_enctypes
+ self.sub_dict['LDIF_DEFAULT_ENCTYPES'] = ldif_default_enctypes
- self.sub_dict['MASTER_KEY_TYPE'] = MASTER_KEY_TYPE
+ self.sub_dict['MASTER_KEY_TYPE'] = tasks.get_masterkey_enctype()
# IPA server/KDC is not a subdomain of default domain
# Proper domain-realm mapping needs to be specified
diff --git a/ipaserver/install/ldapupdate.py b/ipaserver/install/ldapupdate.py
index c3b59732ae8991b2e9ff203caab00da72e093781..9a1a1167ee168e5fea45517e01199baaeb29645b 100644
--- a/ipaserver/install/ldapupdate.py
+++ b/ipaserver/install/ldapupdate.py
@@ -54,6 +54,9 @@ UPDATES_DIR=paths.UPDATES_DIR
UPDATE_SEARCH_TIME_LIMIT = 30 # seconds
+def ldif_mod(op, attr, values):
+ return ''.join(f'{op}: {attr}: {v}\n' for v in values)
+
def get_sub_dict(realm, domain, suffix, fqdn, idstart=None, idmax=None):
"""LDAP template substitution dict for installer and updater
"""
@@ -73,6 +76,15 @@ def get_sub_dict(realm, domain, suffix, fqdn, idstart=None, idmax=None):
named_uid = None
named_gid = None
+ add_supported_enctypes = ldif_mod('add', 'krbSupportedEncSaltTypes',
+ tasks.get_supported_enctypes())
+ add_default_enctypes = ldif_mod('add', 'krbDefaultEncSaltTypes',
+ tasks.get_default_enctypes())
+ rm_supported_enctypes = ldif_mod('remove', 'krbSupportedEncSaltTypes',
+ tasks.get_removed_supported_enctypes())
+ rm_default_enctypes = ldif_mod('remove', 'krbDefaultEncSaltTypes',
+ tasks.get_removed_default_enctypes())
+
return dict(
REALM=realm,
DOMAIN=domain,
@@ -82,7 +94,10 @@ def get_sub_dict(realm, domain, suffix, fqdn, idstart=None, idmax=None):
HOST=fqdn,
LIBARCH=paths.LIBARCH,
TIME=int(time.time()),
- FIPS="#" if tasks.is_fips_enabled() else "",
+ ADD_SUPPORTED_ENCTYPES=add_supported_enctypes,
+ ADD_DEFAULT_ENCTYPES=add_default_enctypes,
+ REMOVE_SUPPORTED_ENCTYPES=rm_supported_enctypes,
+ REMOVE_DEFAULT_ENCTYPES=rm_default_enctypes,
# idstart, idmax, and idrange_size may be None
IDSTART=idstart,
IDMAX=idmax,
--
2.50.0

Some files were not shown because too many files have changed in this diff Show More