mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2025-02-25 18:55:28 -06:00
Merge commit '78a6434e323ebc357472745d97627065ae5b8169'
This commit is contained in:
commit
0b2e6a526a
2
ACI.txt
2
ACI.txt
@ -51,7 +51,7 @@ aci: (targetattr = "cospriority")(targetfilter = "(objectclass=costemplate)")(ve
|
||||
dn: cn=costemplates,cn=accounts,dc=ipa,dc=example
|
||||
aci: (targetattr = "cn || cospriority || createtimestamp || entryusn || krbpwdpolicyreference || modifytimestamp || objectclass")(targetfilter = "(objectclass=costemplate)")(version 3.0;acl "permission:System: Read Group Password Policy costemplate";allow (compare,read,search) groupdn = "ldap:///cn=System: Read Group Password Policy costemplate,cn=permissions,cn=pbac,dc=ipa,dc=example";)
|
||||
dn: dc=ipa,dc=example
|
||||
aci: (targetattr = "createtimestamp || entryusn || idnsallowsyncptr || idnsforwarders || idnsforwardpolicy || idnspersistentsearch || idnszonerefresh || modifytimestamp || objectclass")(target = "ldap:///cn=dns,dc=ipa,dc=example")(targetfilter = "(objectclass=idnsConfigObject)")(version 3.0;acl "permission:System: Read DNS Configuration";allow (read) groupdn = "ldap:///cn=System: Read DNS Configuration,cn=permissions,cn=pbac,dc=ipa,dc=example";)
|
||||
aci: (targetattr = "createtimestamp || entryusn || idnsallowsyncptr || idnsforwarders || idnsforwardpolicy || idnspersistentsearch || idnszonerefresh || ipadnsversion || modifytimestamp || objectclass")(target = "ldap:///cn=dns,dc=ipa,dc=example")(targetfilter = "(objectclass=idnsConfigObject)")(version 3.0;acl "permission:System: Read DNS Configuration";allow (read) groupdn = "ldap:///cn=System: Read DNS Configuration,cn=permissions,cn=pbac,dc=ipa,dc=example";)
|
||||
dn: dc=ipa,dc=example
|
||||
aci: (targetattr = "idnsallowsyncptr || idnsforwarders || idnsforwardpolicy || idnspersistentsearch || idnszonerefresh")(target = "ldap:///cn=dns,dc=ipa,dc=example")(targetfilter = "(objectclass=idnsConfigObject)")(version 3.0;acl "permission:System: Write DNS Configuration";allow (write) groupdn = "ldap:///cn=System: Write DNS Configuration,cn=permissions,cn=pbac,dc=ipa,dc=example";)
|
||||
dn: dc=ipa,dc=example
|
||||
|
9
Makefile
9
Makefile
@ -4,7 +4,7 @@
|
||||
include VERSION
|
||||
|
||||
SUBDIRS=asn1 daemons install ipapython ipalib
|
||||
CLIENTDIRS=ipapython client asn1
|
||||
CLIENTDIRS=ipapython ipalib client asn1
|
||||
CLIENTPYDIRS=ipaclient ipaplatform
|
||||
|
||||
PRJ_PREFIX=freeipa
|
||||
@ -87,6 +87,11 @@ check: bootstrap-autogen server tests
|
||||
(cd $$subdir && $(MAKE) check) || exit 1; \
|
||||
done
|
||||
|
||||
client-check: client-autogen
|
||||
@for subdir in $(CLIENTDIRS); do \
|
||||
(cd $$subdir && $(MAKE) check) || exit 1; \
|
||||
done
|
||||
|
||||
bootstrap-autogen: version-update client-autogen
|
||||
@echo "Building IPA $(IPA_VERSION)"
|
||||
cd asn1; if [ ! -e Makefile ]; then ../autogen.sh --prefix=/usr --sysconfdir=/etc --localstatedir=/var --libdir=$(LIBDIR); fi
|
||||
@ -188,7 +193,7 @@ version-update: release-update
|
||||
fi
|
||||
|
||||
if [ "$(SKIP_API_VERSION_CHECK)" != "yes" ]; then \
|
||||
./makeapi --validate; \
|
||||
./makeapi --validate && \
|
||||
./makeaci --validate; \
|
||||
fi
|
||||
|
||||
|
2
VERSION
2
VERSION
@ -21,7 +21,7 @@
|
||||
########################################################
|
||||
IPA_VERSION_MAJOR=4
|
||||
IPA_VERSION_MINOR=3
|
||||
IPA_VERSION_RELEASE=1
|
||||
IPA_VERSION_RELEASE=2
|
||||
|
||||
########################################################
|
||||
# For 'alpha' releases the version will be #
|
||||
|
@ -45,7 +45,7 @@ try:
|
||||
import ipaclient.ntpconf
|
||||
from ipapython.ipautil import (
|
||||
run, user_input, CalledProcessError, file_exists, dir_exists,
|
||||
realm_to_suffix)
|
||||
realm_to_suffix, is_fips_enabled)
|
||||
from ipaplatform.tasks import tasks
|
||||
from ipaplatform import services
|
||||
from ipaplatform.paths import paths
|
||||
@ -54,7 +54,8 @@ try:
|
||||
from ipapython.config import IPAOptionParser
|
||||
from ipalib import api, errors
|
||||
from ipalib import x509, certstore
|
||||
from ipalib.util import verify_host_resolvable
|
||||
from ipalib.util import (
|
||||
normalize_hostname, validate_domain_name, verify_host_resolvable)
|
||||
from ipalib.constants import CACERT
|
||||
from ipapython.dn import DN
|
||||
from ipapython.ssh import SSHPublicKey
|
||||
@ -224,6 +225,13 @@ def parse_options():
|
||||
if (options.server and not options.domain):
|
||||
parser.error("--server cannot be used without providing --domain")
|
||||
|
||||
if options.domain:
|
||||
try:
|
||||
validate_domain_name(options.domain)
|
||||
except ValueError as ex:
|
||||
parser.error("invalid domain name: %s" % ex)
|
||||
options.domain = normalize_hostname(options.domain)
|
||||
|
||||
if options.force_ntpd and not options.conf_ntp:
|
||||
parser.error("--force-ntpd cannot be used together with --no-ntp")
|
||||
|
||||
@ -1750,12 +1758,16 @@ def get_server_connection_interface(server):
|
||||
def client_dns(server, hostname, options):
|
||||
|
||||
try:
|
||||
verify_host_resolvable(hostname, root_logger)
|
||||
verify_host_resolvable(hostname)
|
||||
dns_ok = True
|
||||
except errors.DNSNotARecordError:
|
||||
root_logger.warning("Hostname (%s) does not have A/AAAA record.",
|
||||
hostname)
|
||||
dns_ok = False
|
||||
except errors.DNSResolverError as ex:
|
||||
root_logger.warning("DNS resolution for hostname %s failed: %s",
|
||||
hostname, ex)
|
||||
dns_ok = False
|
||||
|
||||
if (options.dns_updates or options.all_ip_addresses or options.ip_addresses
|
||||
or not dns_ok):
|
||||
@ -3052,6 +3064,9 @@ def main():
|
||||
|
||||
if not os.getegid() == 0:
|
||||
sys.exit("\nYou must be root to run ipa-client-install.\n")
|
||||
if is_fips_enabled():
|
||||
sys.exit("Installing IPA client in FIPS mode is not supported")
|
||||
|
||||
tasks.check_selinux_status()
|
||||
logging_setup(options)
|
||||
root_logger.debug(
|
||||
|
@ -176,6 +176,17 @@ valid for the IPA domain.
|
||||
.TP
|
||||
\fB\-\-request\-cert\fR
|
||||
Request certificate for the machine. The certificate will be stored in /etc/ipa/nssdb under the nickname "Local IPA host".
|
||||
|
||||
Using this option requires that D-Bus is properly configured or not configured
|
||||
at all. In environment where this condition is not met (e.g. anaconda kickstart
|
||||
chroot environment) set the system bus address to /dev/null to enable
|
||||
workaround in ipa-client-install.
|
||||
|
||||
# env DBUS_SYSTEM_BUS_ADDRESS=unix:path=/dev/null ipa-client-install --request-cert
|
||||
|
||||
Note that requesting the certificate when certmonger is not running only
|
||||
creates tracking request and the certmonger service must be started to be able
|
||||
to track certificates.
|
||||
.TP
|
||||
\fB\-\-automount\-location\fR=\fILOCATION\fR
|
||||
Configure automount by running ipa\-client\-automount(1) with \fILOCATION\fR as
|
||||
|
@ -42,7 +42,7 @@ Source0: freeipa-%{version}.tar.gz
|
||||
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
|
||||
|
||||
%if ! %{ONLY_CLIENT}
|
||||
BuildRequires: 389-ds-base-devel >= 1.3.4.4
|
||||
BuildRequires: 389-ds-base-devel >= 1.3.5
|
||||
BuildRequires: svrcore-devel
|
||||
BuildRequires: policycoreutils >= 2.1.12-5
|
||||
BuildRequires: systemd-units
|
||||
@ -130,7 +130,7 @@ Requires: %{name}-client = %{version}-%{release}
|
||||
Requires: %{name}-admintools = %{version}-%{release}
|
||||
Requires: %{name}-common = %{version}-%{release}
|
||||
Requires: python2-ipaserver = %{version}-%{release}
|
||||
Requires: 389-ds-base >= 1.3.4.6
|
||||
Requires: 389-ds-base >= 1.3.5
|
||||
Requires: openldap-clients > 2.4.35-4
|
||||
Requires: nss >= 3.14.3-12.0
|
||||
Requires: nss-tools >= 3.14.3-12.0
|
||||
@ -140,7 +140,7 @@ Requires: cyrus-sasl-gssapi%{?_isa}
|
||||
Requires: ntp
|
||||
Requires: httpd >= 2.4.6-6
|
||||
Requires: mod_wsgi
|
||||
Requires: mod_auth_gssapi >= 1.3.0-2
|
||||
Requires: mod_auth_gssapi >= 1.4.0
|
||||
Requires: mod_nss >= 1.0.8-26
|
||||
Requires: python-ldap >= 2.4.15
|
||||
Requires: python-gssapi >= 1.1.2
|
||||
@ -154,15 +154,15 @@ Requires(post): systemd-units
|
||||
Requires: selinux-policy >= %{selinux_policy_version}
|
||||
Requires(post): selinux-policy-base >= %{selinux_policy_version}
|
||||
Requires: slapi-nis >= 0.55-1
|
||||
Requires: pki-ca >= 10.2.6-13
|
||||
Requires: pki-kra >= 10.2.6-13
|
||||
Requires: pki-ca >= 10.2.6-19
|
||||
Requires: pki-kra >= 10.2.6-19
|
||||
Requires(preun): python systemd-units
|
||||
Requires(postun): python systemd-units
|
||||
Requires: zip
|
||||
Requires: policycoreutils >= 2.1.12-5
|
||||
Requires: tar
|
||||
Requires(pre): certmonger >= 0.78
|
||||
Requires(pre): 389-ds-base >= 1.3.4.6
|
||||
Requires(pre): 389-ds-base >= 1.3.5
|
||||
Requires: fontawesome-fonts
|
||||
Requires: open-sans-fonts
|
||||
Requires: openssl
|
||||
@ -501,6 +501,7 @@ Requires: python-ldap >= 2.4.15
|
||||
Requires: python-requests
|
||||
Requires: python-custodia
|
||||
Requires: python-dns >= 1.11.1
|
||||
Requires: pyusb
|
||||
|
||||
Conflicts: %{alt_name}-python < %{version}
|
||||
|
||||
@ -550,6 +551,7 @@ Requires: python3-pyldap >= 2.4.15
|
||||
Requires: python3-custodia
|
||||
Requires: python3-requests
|
||||
Requires: python3-dns >= 1.11.1
|
||||
Requires: python3-pyusb
|
||||
|
||||
%description -n python3-ipalib
|
||||
IPA is an integrated solution to provide centrally managed Identity (users,
|
||||
@ -603,6 +605,7 @@ Requires: python-polib
|
||||
Requires: python-pytest-multihost >= 0.5
|
||||
Requires: python-pytest-sourceorder
|
||||
Requires: ldns-utils
|
||||
Requires: python-sssdconfig
|
||||
|
||||
Provides: %{alt_name}-tests = %{version}
|
||||
Conflicts: %{alt_name}-tests
|
||||
@ -634,6 +637,7 @@ Requires: python3-polib
|
||||
Requires: python3-pytest-multihost >= 0.5
|
||||
Requires: python3-pytest-sourceorder
|
||||
Requires: ldns-utils
|
||||
Requires: python3-sssdconfig
|
||||
|
||||
%description -n python3-ipatests
|
||||
IPA is an integrated solution to provide centrally managed Identity (users,
|
||||
@ -682,7 +686,11 @@ make IPA_VERSION_IS_GIT_SNAPSHOT=no %{?_smp_mflags} client
|
||||
|
||||
|
||||
%check
|
||||
%if ! %{ONLY_CLIENT}
|
||||
make %{?_smp_mflags} check VERBOSE=yes
|
||||
%else
|
||||
make %{?_smp_mflags} client-check VERBOSE=yes
|
||||
%endif # ONLY_CLIENT
|
||||
|
||||
|
||||
%install
|
||||
@ -731,6 +739,11 @@ make client-install DESTDIR=%{buildroot}
|
||||
(cd ipaclient && %{__python3} setup.py install --root %{buildroot})
|
||||
%endif # with_python3
|
||||
|
||||
# Switch shebang of /usr/bin/ipa
|
||||
# XXX: ipa cli is not stable enough for enabling py3 support, keep it in py2
|
||||
# in any case
|
||||
sed -i -e'1s/python\(3\|$\)/python2/' %{buildroot}%{_bindir}/ipa
|
||||
|
||||
%find_lang %{gettext_domain}
|
||||
|
||||
mkdir -p %{buildroot}%{_usr}/share/ipa
|
||||
@ -1115,6 +1128,7 @@ fi
|
||||
%defattr(-,root,root,-)
|
||||
%doc README Contributors.txt
|
||||
%license COPYING
|
||||
%{python_sitelib}/freeipa-*.egg-info
|
||||
%dir %{python_sitelib}/ipaserver
|
||||
%dir %{python_sitelib}/ipaserver/install
|
||||
%dir %{python_sitelib}/ipaserver/install/plugins
|
||||
@ -1353,7 +1367,6 @@ fi
|
||||
%{python_sitelib}/ipaplatform/*
|
||||
%{python_sitelib}/ipapython-*.egg-info
|
||||
%{python_sitelib}/ipalib-*.egg-info
|
||||
%{python_sitelib}/freeipa-*.egg-info
|
||||
%{python_sitelib}/ipaplatform-*.egg-info
|
||||
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
#
|
||||
# VERSION 19 - DO NOT REMOVE THIS LINE
|
||||
# VERSION 21 - DO NOT REMOVE THIS LINE
|
||||
#
|
||||
# This file may be overwritten on upgrades.
|
||||
#
|
||||
@ -65,12 +65,15 @@ WSGIScriptReloading Off
|
||||
GssapiCredStore keytab:/etc/httpd/conf/ipa.keytab
|
||||
GssapiCredStore client_keytab:/etc/httpd/conf/ipa.keytab
|
||||
GssapiDelegCcacheDir /var/run/httpd/ipa/clientcaches
|
||||
GssapiDelegCcacheUnique On
|
||||
GssapiUseS4U2Proxy on
|
||||
GssapiAllowedMech krb5
|
||||
Require valid-user
|
||||
ErrorDocument 401 /ipa/errors/unauthorized.html
|
||||
WSGIProcessGroup ipa
|
||||
WSGIApplicationGroup ipa
|
||||
Header always append X-Frame-Options DENY
|
||||
Header always append Content-Security-Policy "frame-ancestors 'none'"
|
||||
</Location>
|
||||
|
||||
# Turn off Apache authentication for sessions
|
||||
|
@ -8,19 +8,18 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: ipa\n"
|
||||
"Report-Msgid-Bugs-To: https://hosted.fedoraproject.org/projects/freeipa/"
|
||||
"newticket\n"
|
||||
"POT-Creation-Date: 2015-06-24 13:48+0200\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Report-Msgid-Bugs-To: https://fedorahosted.org/freeipa/newticket\n"
|
||||
"POT-Creation-Date: 2016-05-31 15:43+0200\n"
|
||||
"PO-Revision-Date: 2014-07-03 04:37-0400\n"
|
||||
"Last-Translator: Petr Viktorin <encukou@gmail.com>\n"
|
||||
"Language-Team: Bengali (India) (http://www.transifex.com/projects/p/freeipa/"
|
||||
"language/bn_IN/)\n"
|
||||
"Language: bn-IN\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
"X-Generator: Zanata 3.7.3\n"
|
||||
"X-Generator: Zanata 3.8.4\n"
|
||||
|
||||
msgid "Passwords do not match"
|
||||
msgstr "পাসওয়ার্ড দুটি মিলছে না"
|
||||
|
@ -7,19 +7,18 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: ipa\n"
|
||||
"Report-Msgid-Bugs-To: https://hosted.fedoraproject.org/projects/freeipa/"
|
||||
"newticket\n"
|
||||
"POT-Creation-Date: 2015-06-24 13:48+0200\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Report-Msgid-Bugs-To: https://fedorahosted.org/freeipa/newticket\n"
|
||||
"POT-Creation-Date: 2016-05-31 15:43+0200\n"
|
||||
"PO-Revision-Date: 2014-09-23 08:41-0400\n"
|
||||
"Last-Translator: Petr Viktorin <encukou@gmail.com>\n"
|
||||
"Language-Team: Catalan (http://www.transifex.com/projects/p/freeipa/language/"
|
||||
"ca/)\n"
|
||||
"Language: ca\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
"X-Generator: Zanata 3.7.3\n"
|
||||
"X-Generator: Zanata 3.8.4\n"
|
||||
|
||||
msgid "Passwords do not match"
|
||||
msgstr "Les contrasenyes no coincideixen"
|
||||
@ -366,6 +365,9 @@ msgstr "Exclusiu"
|
||||
msgid "Expression"
|
||||
msgstr "Expressió"
|
||||
|
||||
msgid "Anyone"
|
||||
msgstr "Qualsevol"
|
||||
|
||||
msgid "Affiliation Changed"
|
||||
msgstr "L'afiliació ha canviat"
|
||||
|
||||
@ -441,9 +443,6 @@ msgstr "Extern"
|
||||
msgid "Normal"
|
||||
msgstr "Normal"
|
||||
|
||||
msgid "Anyone"
|
||||
msgstr "Qualsevol"
|
||||
|
||||
msgid "Who"
|
||||
msgstr "Qui"
|
||||
|
||||
|
@ -4,23 +4,22 @@
|
||||
#
|
||||
# Translators:
|
||||
# Petr Viktorin <encukou@gmail.com>, 2013
|
||||
# Josef Hruška <josef.hruska@upcmail.cz>, 2015. #zanata
|
||||
# Josef Hruška <hrusjos@gmail.com>, 2015. #zanata
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: ipa\n"
|
||||
"Report-Msgid-Bugs-To: https://hosted.fedoraproject.org/projects/freeipa/"
|
||||
"newticket\n"
|
||||
"POT-Creation-Date: 2015-06-24 13:48+0200\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Report-Msgid-Bugs-To: https://fedorahosted.org/freeipa/newticket\n"
|
||||
"POT-Creation-Date: 2016-05-31 15:43+0200\n"
|
||||
"PO-Revision-Date: 2015-06-29 01:17-0400\n"
|
||||
"Last-Translator: Josef Hruška <josef.hruska@upcmail.cz>\n"
|
||||
"Last-Translator: Josef Hruška <hrusjos@gmail.com>\n"
|
||||
"Language-Team: Czech (http://www.transifex.com/projects/p/freeipa/language/"
|
||||
"cs/)\n"
|
||||
"Language: cs\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n"
|
||||
"X-Generator: Zanata 3.7.3\n"
|
||||
"X-Generator: Zanata 3.8.4\n"
|
||||
|
||||
#, python-format
|
||||
msgid "Enter %(label)s again to verify: "
|
||||
|
1002
install/po/de.po
1002
install/po/de.po
File diff suppressed because it is too large
Load Diff
@ -15,19 +15,18 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: ipa\n"
|
||||
"Report-Msgid-Bugs-To: https://hosted.fedoraproject.org/projects/freeipa/"
|
||||
"newticket\n"
|
||||
"POT-Creation-Date: 2015-06-24 13:48+0200\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Report-Msgid-Bugs-To: https://fedorahosted.org/freeipa/newticket\n"
|
||||
"POT-Creation-Date: 2016-05-31 15:43+0200\n"
|
||||
"PO-Revision-Date: 2014-09-23 08:41-0400\n"
|
||||
"Last-Translator: Petr Viktorin <encukou@gmail.com>\n"
|
||||
"Language-Team: Spanish (http://www.transifex.com/projects/p/freeipa/language/"
|
||||
"es/)\n"
|
||||
"Language: es\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
"X-Generator: Zanata 3.7.3\n"
|
||||
"X-Generator: Zanata 3.8.4\n"
|
||||
|
||||
#, python-format
|
||||
msgid "Enter %(label)s again to verify: "
|
||||
@ -1174,15 +1173,9 @@ msgstr ""
|
||||
msgid "Time Limit"
|
||||
msgstr "Tiempo límite"
|
||||
|
||||
msgid "Time limit of search in seconds"
|
||||
msgstr "Tiempo máximo de búsqueda en segundos"
|
||||
|
||||
msgid "Size Limit"
|
||||
msgstr "Tamaño límite"
|
||||
|
||||
msgid "Maximum number of entries returned"
|
||||
msgstr "Cantidad máxima de entradas obtenidas"
|
||||
|
||||
msgid "Kerberos keys available"
|
||||
msgstr "Claves Kerberos disponibles"
|
||||
|
||||
@ -1420,9 +1413,6 @@ msgstr "Error"
|
||||
msgid "Status"
|
||||
msgstr "Estatus"
|
||||
|
||||
msgid "searchtimelimit must be -1 or > 1."
|
||||
msgstr "searchtimelimit debe ser -1 o> 1."
|
||||
|
||||
msgid "configuration options"
|
||||
msgstr "opciones de configuración"
|
||||
|
||||
@ -1456,18 +1446,9 @@ msgstr "Dominio de correo electrónico por defecto"
|
||||
msgid "Search time limit"
|
||||
msgstr "Buscar límite de tiempo"
|
||||
|
||||
msgid ""
|
||||
"Maximum amount of time (seconds) for a search (> 0, or -1 for unlimited)"
|
||||
msgstr ""
|
||||
"Máxima cantidad de tiempo (segundos) parab una búsqueda (> 0, o -1 para "
|
||||
"ilimitado)"
|
||||
|
||||
msgid "Search size limit"
|
||||
msgstr "Límite del tamaño de la búsqueda"
|
||||
|
||||
msgid "Maximum number of records to search (-1 is unlimited)"
|
||||
msgstr "Máximo número de registros a buscar (-1 es ilimitado)"
|
||||
|
||||
msgid "User search fields"
|
||||
msgstr "Campos de búsqueda de usuario"
|
||||
|
||||
@ -1597,10 +1578,6 @@ msgstr "formato de dirección no válido"
|
||||
msgid "%(port)s is not a valid port"
|
||||
msgstr "%(port)s no es un puerto válido"
|
||||
|
||||
#, python-format
|
||||
msgid "DNS reverse zone for IP address %(addr)s not found"
|
||||
msgstr "Zona invertida DNS para dirección IP %(addr)s no encontrada"
|
||||
|
||||
#, python-format
|
||||
msgid "DNS zone %(zone)s not found"
|
||||
msgstr "Zona DNS %(zone)s no encontrado"
|
||||
@ -1832,15 +1809,15 @@ msgstr "Permitir transferencia"
|
||||
msgid "Create new DNS zone (SOA record)."
|
||||
msgstr "Crear nueva zona DNS (registro SOA)."
|
||||
|
||||
msgid "Force"
|
||||
msgstr "Forzar"
|
||||
|
||||
msgid "Delete DNS zone (SOA record)."
|
||||
msgstr "Borrar zona DNS (registro SOA)."
|
||||
|
||||
msgid "Modify DNS zone (SOA record)."
|
||||
msgstr "Modificar zona DNS (registro SOA)."
|
||||
|
||||
msgid "Force"
|
||||
msgstr "Forzar"
|
||||
|
||||
msgid "Forward zones only"
|
||||
msgstr "Zonas de reenvío sólo"
|
||||
|
||||
@ -1937,9 +1914,6 @@ msgstr "Mostrar un recurso DNS."
|
||||
msgid "Search for DNS resources."
|
||||
msgstr "Buscar recursos DNS."
|
||||
|
||||
msgid "Resolve a host name in DNS."
|
||||
msgstr "Resolver un nombre de host en DNS."
|
||||
|
||||
#, python-format
|
||||
msgid "Found '%(value)s'"
|
||||
msgstr "Ha sido encontrado '%(value)s'"
|
||||
@ -2888,6 +2862,27 @@ msgstr "Directo"
|
||||
msgid "Indirect"
|
||||
msgstr "Indirecto"
|
||||
|
||||
msgid "Any Host"
|
||||
msgstr "Cualquier host"
|
||||
|
||||
msgid "Any Service"
|
||||
msgstr "Cualquier servicio"
|
||||
|
||||
msgid "Anyone"
|
||||
msgstr "Cualquiera"
|
||||
|
||||
msgid "Rule status"
|
||||
msgstr "Estatus de reglas"
|
||||
|
||||
msgid "Specified Hosts and Groups"
|
||||
msgstr "Hosts y grupos especificados"
|
||||
|
||||
msgid "Specified Services and Groups"
|
||||
msgstr "Servicios y grupos especificados"
|
||||
|
||||
msgid "Specified Users and Groups"
|
||||
msgstr "Usuarios y grupos específicos"
|
||||
|
||||
msgid "AA Compromise"
|
||||
msgstr "AA transacción"
|
||||
|
||||
@ -3071,33 +3066,12 @@ msgstr "POSIX"
|
||||
msgid "Group Type"
|
||||
msgstr "Tipo de grupo"
|
||||
|
||||
msgid "Any Host"
|
||||
msgstr "Cualquier host"
|
||||
|
||||
msgid "Any Service"
|
||||
msgstr "Cualquier servicio"
|
||||
|
||||
msgid "Anyone"
|
||||
msgstr "Cualquiera"
|
||||
|
||||
msgid "Accessing"
|
||||
msgstr "Acceso"
|
||||
|
||||
msgid "Rule status"
|
||||
msgstr "Estatus de reglas"
|
||||
|
||||
msgid "Via Service"
|
||||
msgstr "Vía de servicio"
|
||||
|
||||
msgid "Specified Hosts and Groups"
|
||||
msgstr "Hosts y grupos especificados"
|
||||
|
||||
msgid "Specified Services and Groups"
|
||||
msgstr "Servicios y grupos especificados"
|
||||
|
||||
msgid "Specified Users and Groups"
|
||||
msgstr "Usuarios y grupos específicos"
|
||||
|
||||
msgid "Who"
|
||||
msgstr "¿Quién?"
|
||||
|
||||
@ -4830,10 +4804,6 @@ msgstr "el dominio no está configurado"
|
||||
msgid "SID is not valid"
|
||||
msgstr "el SID no es válido"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to communicate with CMS (%s)"
|
||||
msgstr "No es posible comunicarse con CMS (%s)"
|
||||
|
||||
msgid "The hostname to register as"
|
||||
msgstr "El nombre del equipo a ser registrado como"
|
||||
|
||||
|
@ -7,19 +7,18 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: ipa\n"
|
||||
"Report-Msgid-Bugs-To: https://hosted.fedoraproject.org/projects/freeipa/"
|
||||
"newticket\n"
|
||||
"POT-Creation-Date: 2015-06-24 13:48+0200\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Report-Msgid-Bugs-To: https://fedorahosted.org/freeipa/newticket\n"
|
||||
"POT-Creation-Date: 2016-05-31 15:43+0200\n"
|
||||
"PO-Revision-Date: 2014-09-23 08:41-0400\n"
|
||||
"Last-Translator: Petr Viktorin <encukou@gmail.com>\n"
|
||||
"Language-Team: Basque (http://www.transifex.com/projects/p/freeipa/language/"
|
||||
"eu/)\n"
|
||||
"Language: eu\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
"X-Generator: Zanata 3.7.3\n"
|
||||
"X-Generator: Zanata 3.8.4\n"
|
||||
|
||||
#, c-format
|
||||
msgid "Passwords do not match!"
|
||||
|
2217
install/po/fr.po
2217
install/po/fr.po
File diff suppressed because it is too large
Load Diff
@ -7,19 +7,18 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: ipa\n"
|
||||
"Report-Msgid-Bugs-To: https://hosted.fedoraproject.org/projects/freeipa/"
|
||||
"newticket\n"
|
||||
"POT-Creation-Date: 2015-06-24 13:48+0200\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Report-Msgid-Bugs-To: https://fedorahosted.org/freeipa/newticket\n"
|
||||
"POT-Creation-Date: 2016-05-31 15:43+0200\n"
|
||||
"PO-Revision-Date: 2014-07-04 05:21-0400\n"
|
||||
"Last-Translator: solomonsunder <solomonsunder@gmail.com>\n"
|
||||
"Language-Team: Hindi (http://www.transifex.com/projects/p/freeipa/language/"
|
||||
"hi/)\n"
|
||||
"Language: hi\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
"X-Generator: Zanata 3.7.3\n"
|
||||
"X-Generator: Zanata 3.8.4\n"
|
||||
|
||||
#, python-format
|
||||
msgid "Enter %(label)s again to verify: "
|
||||
|
@ -7,19 +7,18 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: ipa\n"
|
||||
"Report-Msgid-Bugs-To: https://hosted.fedoraproject.org/projects/freeipa/"
|
||||
"newticket\n"
|
||||
"POT-Creation-Date: 2015-06-24 13:48+0200\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Report-Msgid-Bugs-To: https://fedorahosted.org/freeipa/newticket\n"
|
||||
"POT-Creation-Date: 2016-05-31 15:43+0200\n"
|
||||
"PO-Revision-Date: 2014-09-23 08:41-0400\n"
|
||||
"Last-Translator: Petr Viktorin <encukou@gmail.com>\n"
|
||||
"Language-Team: Hungarian (http://www.transifex.com/projects/p/freeipa/"
|
||||
"language/hu/)\n"
|
||||
"Language: hu\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
"X-Generator: Zanata 3.7.3\n"
|
||||
"X-Generator: Zanata 3.8.4\n"
|
||||
|
||||
msgid "Passwords do not match"
|
||||
msgstr "A jelszavak nem egyeznek meg"
|
||||
|
@ -8,19 +8,18 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: ipa\n"
|
||||
"Report-Msgid-Bugs-To: https://hosted.fedoraproject.org/projects/freeipa/"
|
||||
"newticket\n"
|
||||
"POT-Creation-Date: 2015-06-24 13:48+0200\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Report-Msgid-Bugs-To: https://fedorahosted.org/freeipa/newticket\n"
|
||||
"POT-Creation-Date: 2016-05-31 15:43+0200\n"
|
||||
"PO-Revision-Date: 2014-09-23 08:41-0400\n"
|
||||
"Last-Translator: Petr Viktorin <encukou@gmail.com>\n"
|
||||
"Language-Team: Indonesian (http://www.transifex.com/projects/p/freeipa/"
|
||||
"language/id/)\n"
|
||||
"Language: id\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=1; plural=0;\n"
|
||||
"X-Generator: Zanata 3.7.3\n"
|
||||
"X-Generator: Zanata 3.8.4\n"
|
||||
|
||||
#, python-format
|
||||
msgid "Enter %(label)s again to verify: "
|
||||
@ -273,7 +272,3 @@ msgstr "Pengguna \"%(value)s\" telah dihapus"
|
||||
#, python-format
|
||||
msgid "Modified user \"%(value)s\""
|
||||
msgstr "Pengguna \"%(value)s\" telah dimodifikasi"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to communicate with CMS (%s)"
|
||||
msgstr "Tidak dapat berkomunikasi dengan CMS (%s)"
|
||||
|
2707
install/po/ipa.pot
2707
install/po/ipa.pot
File diff suppressed because it is too large
Load Diff
@ -8,19 +8,18 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: ipa\n"
|
||||
"Report-Msgid-Bugs-To: https://hosted.fedoraproject.org/projects/freeipa/"
|
||||
"newticket\n"
|
||||
"POT-Creation-Date: 2015-06-24 13:48+0200\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Report-Msgid-Bugs-To: https://fedorahosted.org/freeipa/newticket\n"
|
||||
"POT-Creation-Date: 2016-05-31 15:43+0200\n"
|
||||
"PO-Revision-Date: 2014-09-23 08:41-0400\n"
|
||||
"Last-Translator: Petr Viktorin <encukou@gmail.com>\n"
|
||||
"Language-Team: Japanese (http://www.transifex.com/projects/p/freeipa/"
|
||||
"language/ja/)\n"
|
||||
"Language: ja\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=1; plural=0;\n"
|
||||
"X-Generator: Zanata 3.7.3\n"
|
||||
"X-Generator: Zanata 3.8.4\n"
|
||||
|
||||
#, python-format
|
||||
msgid "Enter %(label)s again to verify: "
|
||||
|
@ -8,19 +8,18 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: ipa\n"
|
||||
"Report-Msgid-Bugs-To: https://hosted.fedoraproject.org/projects/freeipa/"
|
||||
"newticket\n"
|
||||
"POT-Creation-Date: 2015-06-24 13:48+0200\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Report-Msgid-Bugs-To: https://fedorahosted.org/freeipa/newticket\n"
|
||||
"POT-Creation-Date: 2016-05-31 15:43+0200\n"
|
||||
"PO-Revision-Date: 2014-09-23 08:41-0400\n"
|
||||
"Last-Translator: Petr Viktorin <encukou@gmail.com>\n"
|
||||
"Language-Team: Kannada (http://www.transifex.com/projects/p/freeipa/language/"
|
||||
"kn/)\n"
|
||||
"Language: kn\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=1; plural=0;\n"
|
||||
"X-Generator: Zanata 3.7.3\n"
|
||||
"X-Generator: Zanata 3.8.4\n"
|
||||
|
||||
#, python-format
|
||||
msgid "Enter %(label)s again to verify: "
|
||||
@ -695,7 +694,3 @@ msgstr "\"%(value)s\" ಬಳಕೆದಾರನನ್ನು ಅಳಿಸಲಾಗ
|
||||
#, python-format
|
||||
msgid "Modified user \"%(value)s\""
|
||||
msgstr "\"%(value)s\" ಬಳಕೆದಾರನನ್ನು ಬದಲಾಯಿಸಲಾಗಿದೆ"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to communicate with CMS (%s)"
|
||||
msgstr "CMS (%s) ಜೊತೆ ಸಂಪರ್ಕಿಸಲು ಸಾಧ್ಯವಾಗುತ್ತಿಲ್ಲ"
|
||||
|
16299
install/po/mr.po
Normal file
16299
install/po/mr.po
Normal file
File diff suppressed because it is too large
Load Diff
@ -7,19 +7,18 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: ipa\n"
|
||||
"Report-Msgid-Bugs-To: https://hosted.fedoraproject.org/projects/freeipa/"
|
||||
"newticket\n"
|
||||
"POT-Creation-Date: 2015-06-24 13:48+0200\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Report-Msgid-Bugs-To: https://fedorahosted.org/freeipa/newticket\n"
|
||||
"POT-Creation-Date: 2016-05-31 15:43+0200\n"
|
||||
"PO-Revision-Date: 2014-07-03 04:37-0400\n"
|
||||
"Last-Translator: Petr Viktorin <encukou@gmail.com>\n"
|
||||
"Language-Team: Dutch (http://www.transifex.com/projects/p/freeipa/language/"
|
||||
"nl/)\n"
|
||||
"Language: nl\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
"X-Generator: Zanata 3.7.3\n"
|
||||
"X-Generator: Zanata 3.8.4\n"
|
||||
|
||||
#, c-format
|
||||
msgid "Passwords do not match!"
|
||||
|
@ -6,23 +6,23 @@
|
||||
# jdennis <jdennis@redhat.com>, 2011
|
||||
# Mateusz Marzantowicz <mmarzantowicz@osdf.com.pl>, 2013
|
||||
# Piotr Drąg <piotrdrag@gmail.com>, 2010,2013
|
||||
# Piotr Drąg <pdrag@aviary.pl>, 2016. #zanata
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: ipa\n"
|
||||
"Report-Msgid-Bugs-To: https://hosted.fedoraproject.org/projects/freeipa/"
|
||||
"newticket\n"
|
||||
"POT-Creation-Date: 2015-06-24 13:48+0200\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"PO-Revision-Date: 2014-09-23 08:41-0400\n"
|
||||
"Last-Translator: Petr Viktorin <encukou@gmail.com>\n"
|
||||
"Report-Msgid-Bugs-To: https://fedorahosted.org/freeipa/newticket\n"
|
||||
"POT-Creation-Date: 2016-05-31 15:43+0200\n"
|
||||
"PO-Revision-Date: 2016-05-14 05:41-0400\n"
|
||||
"Last-Translator: Piotr Drąg <pdrag@aviary.pl>\n"
|
||||
"Language-Team: Polish (http://www.transifex.com/projects/p/freeipa/language/"
|
||||
"pl/)\n"
|
||||
"Language: pl\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 "
|
||||
"|| n%100>=20) ? 1 : 2);\n"
|
||||
"X-Generator: Zanata 3.7.3\n"
|
||||
"X-Generator: Zanata 3.8.4\n"
|
||||
|
||||
#, python-format
|
||||
msgid "Enter %(label)s again to verify: "
|
||||
@ -436,15 +436,9 @@ msgstr "Liczba usuniętych elementów"
|
||||
msgid "Time Limit"
|
||||
msgstr "Ograniczenie czasu"
|
||||
|
||||
msgid "Time limit of search in seconds"
|
||||
msgstr "Ograniczenie czasu wyszukiwania w sekundach"
|
||||
|
||||
msgid "Size Limit"
|
||||
msgstr "Ograniczenie rozmiaru"
|
||||
|
||||
msgid "Maximum number of entries returned"
|
||||
msgstr "Maksymalna liczba zwróconych wpisów"
|
||||
|
||||
msgid "User"
|
||||
msgstr "Użytkownik"
|
||||
|
||||
@ -1235,10 +1229,6 @@ msgstr "Wyłączono konto użytkownika \"%(value)s\""
|
||||
msgid "Enabled user account \"%(value)s\""
|
||||
msgstr "Włączono konto użytkownika \"%(value)s\""
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to communicate with CMS (%s)"
|
||||
msgstr "Nie można komunikować się z CMS (%s)"
|
||||
|
||||
msgid "The hostname to register as"
|
||||
msgstr "Nazwa komputera, pod jaką zarejestrować"
|
||||
|
||||
@ -1315,8 +1305,8 @@ msgstr "Nazwa serwera"
|
||||
|
||||
msgid "The principal to get a keytab for (ex: ftp/ftp.example.com@EXAMPLE.COM)"
|
||||
msgstr ""
|
||||
"Naczelnik, dla którego uzyskać tablicę kluczy (np.: ftp/ftp.przykład."
|
||||
"pl@PRZYKŁAD.PL)"
|
||||
"Naczelnik, dla którego uzyskać tablicę kluczy (np.: ftp/ftp.example."
|
||||
"com@EXAMPLE.COM)"
|
||||
|
||||
msgid "Kerberos Service Principal Name"
|
||||
msgstr "Nazwa naczelnika usługi Kerberos"
|
||||
|
16299
install/po/pt_BR.po
Normal file
16299
install/po/pt_BR.po
Normal file
File diff suppressed because it is too large
Load Diff
@ -8,23 +8,23 @@
|
||||
# Azamat Hackimov <azamat.hackimov@gmail.com>, 2012
|
||||
# jdennis <jdennis@redhat.com>, 2011
|
||||
# Alex <alexgluck@bk.ru>, 2015. #zanata
|
||||
# Martin Bašti <mbasti@redhat.com>, 2016. #zanata
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: ipa\n"
|
||||
"Report-Msgid-Bugs-To: https://hosted.fedoraproject.org/projects/freeipa/"
|
||||
"newticket\n"
|
||||
"POT-Creation-Date: 2015-06-24 13:48+0200\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"PO-Revision-Date: 2015-08-19 10:07-0400\n"
|
||||
"Last-Translator: Alex <alexgluck@bk.ru>\n"
|
||||
"Report-Msgid-Bugs-To: https://fedorahosted.org/freeipa/newticket\n"
|
||||
"POT-Creation-Date: 2016-05-31 15:43+0200\n"
|
||||
"PO-Revision-Date: 2016-06-13 04:55-0400\n"
|
||||
"Last-Translator: Martin Bašti <mbasti@redhat.com>\n"
|
||||
"Language-Team: Russian (http://www.transifex.com/projects/p/freeipa/language/"
|
||||
"ru/)\n"
|
||||
"Language: ru\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
|
||||
"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
|
||||
"X-Generator: Zanata 3.7.3\n"
|
||||
"X-Generator: Zanata 3.8.4\n"
|
||||
|
||||
#, python-format
|
||||
msgid "Enter %(label)s again to verify: "
|
||||
@ -691,13 +691,6 @@ msgstr "Отобразить расположения автомонтирова
|
||||
msgid "Search for an automount location."
|
||||
msgstr "Поиск для расположения автомонтирования."
|
||||
|
||||
#, python-format
|
||||
msgid "%(count)d automount location matched"
|
||||
msgid_plural "%(count)d automount locations matched"
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
msgstr[2] ""
|
||||
|
||||
msgid "Generate automount files for a specific location."
|
||||
msgstr "Сгенерировать файлы автомонтирования для особого расположения."
|
||||
|
||||
@ -843,15 +836,9 @@ msgstr ""
|
||||
msgid "Time Limit"
|
||||
msgstr "Ограничение по времени"
|
||||
|
||||
msgid "Time limit of search in seconds"
|
||||
msgstr "Ограничение времени поиска в секундах"
|
||||
|
||||
msgid "Size Limit"
|
||||
msgstr "Ограничение по размеру"
|
||||
|
||||
msgid "Maximum number of entries returned"
|
||||
msgstr "Максимальное количество совпадений возвращено"
|
||||
|
||||
msgid "Kerberos keys available"
|
||||
msgstr "Kerberos ключей доступно"
|
||||
|
||||
@ -1067,13 +1054,6 @@ msgstr "Изменён CA ACL \"%(value)s\""
|
||||
msgid "Search for CA ACLs."
|
||||
msgstr "Поиск по CA ACLs."
|
||||
|
||||
#, python-format
|
||||
msgid "%(count)d CA ACL matched"
|
||||
msgid_plural "%(count)d CA ACLs matched"
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
msgstr[2] ""
|
||||
|
||||
msgid "Display the properties of a CA ACL."
|
||||
msgstr "Показать свойства of a CA ACL."
|
||||
|
||||
@ -1228,10 +1208,6 @@ msgstr "Серийный номер"
|
||||
msgid "Serial number (hex)"
|
||||
msgstr "Серийный номер (hex)"
|
||||
|
||||
#, python-format
|
||||
msgid "extension %s is forbidden"
|
||||
msgstr "расширение %s запрещено"
|
||||
|
||||
msgid "RFC822Name does not match any of user's email addresses"
|
||||
msgstr "RFC822 Имя не совпадает ни с одним пользователем электронной почты"
|
||||
|
||||
@ -1861,10 +1837,6 @@ msgstr "неверный открытый ключ SSH"
|
||||
msgid "objectclass %s not found"
|
||||
msgstr "класс объектов %s не найден"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to communicate with CMS (%s)"
|
||||
msgstr "Невозможно связаться с CMS (%s)"
|
||||
|
||||
msgid "Hardware platform of the host (e.g. Lenovo T61)"
|
||||
msgstr "Аппаратная платформа узла (например, Lenovo T61)"
|
||||
|
||||
|
@ -7,19 +7,18 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: ipa\n"
|
||||
"Report-Msgid-Bugs-To: https://hosted.fedoraproject.org/projects/freeipa/"
|
||||
"newticket\n"
|
||||
"POT-Creation-Date: 2015-06-24 13:48+0200\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Report-Msgid-Bugs-To: https://fedorahosted.org/freeipa/newticket\n"
|
||||
"POT-Creation-Date: 2016-05-31 15:43+0200\n"
|
||||
"PO-Revision-Date: 2014-09-23 08:41-0400\n"
|
||||
"Last-Translator: Petr Viktorin <encukou@gmail.com>\n"
|
||||
"Language-Team: Tajik (http://www.transifex.com/projects/p/freeipa/language/"
|
||||
"tg/)\n"
|
||||
"Language: tg\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
"X-Generator: Zanata 3.7.3\n"
|
||||
"X-Generator: Zanata 3.8.4\n"
|
||||
|
||||
#, c-format
|
||||
msgid "Passwords do not match!"
|
||||
|
1716
install/po/uk.po
1716
install/po/uk.po
File diff suppressed because it is too large
Load Diff
@ -9,19 +9,18 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: ipa\n"
|
||||
"Report-Msgid-Bugs-To: https://hosted.fedoraproject.org/projects/freeipa/"
|
||||
"newticket\n"
|
||||
"POT-Creation-Date: 2015-06-24 13:48+0200\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Report-Msgid-Bugs-To: https://fedorahosted.org/freeipa/newticket\n"
|
||||
"POT-Creation-Date: 2016-05-31 15:43+0200\n"
|
||||
"PO-Revision-Date: 2015-03-12 12:06-0400\n"
|
||||
"Last-Translator: Martin Liu <martin@aws-faq.com>\n"
|
||||
"Language-Team: Chinese (China) (http://www.transifex.com/projects/p/freeipa/"
|
||||
"language/zh_CN/)\n"
|
||||
"Language: zh-CN\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=1; plural=0;\n"
|
||||
"X-Generator: Zanata 3.7.3\n"
|
||||
"X-Generator: Zanata 3.8.4\n"
|
||||
|
||||
#, python-format
|
||||
msgid "Enter %(label)s again to verify: "
|
||||
|
@ -70,9 +70,11 @@ attributeTypes: ( 2.16.840.1.113730.3.8.5.25 NAME 'idnsSecKeyRevoke' DESC 'DNSKE
|
||||
attributeTypes: ( 2.16.840.1.113730.3.8.5.26 NAME 'idnsSecKeySep' DESC 'DNSKEY SEP flag (equivalent to bit 15): RFC 4035' EQUALITY booleanMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 SINGLE-VALUE X-ORIGIN 'IPA v4.1' )
|
||||
attributeTypes: ( 2.16.840.1.113730.3.8.5.27 NAME 'idnsSecAlgorithm' DESC 'DNSKEY algorithm: string used as mnemonic' EQUALITY caseIgnoreIA5Match SUBSTR caseIgnoreIA5SubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE X-ORIGIN 'IPA v4.1' )
|
||||
attributeTypes: ( 2.16.840.1.113730.3.8.5.28 NAME 'idnsSecKeyRef' DESC 'PKCS#11 URI of the key' EQUALITY caseExactMatch SINGLE-VALUE SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-ORIGIN 'IPA v4.1' )
|
||||
attributeTypes: ( 2.16.840.1.113730.3.8.11.74 NAME 'ipaDNSVersion' DESC 'IPA DNS data version' EQUALITY integerMatch ORDERING integerOrderingMatch SINGLE-VALUE SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 X-ORIGIN 'IPA v4.3' )
|
||||
objectClasses: ( 2.16.840.1.113730.3.8.6.0 NAME 'idnsRecord' DESC 'dns Record, usually a host' SUP top STRUCTURAL MUST idnsName MAY ( cn $ idnsAllowDynUpdate $ dNSTTL $ dNSClass $ aRecord $ aAAARecord $ a6Record $ nSRecord $ cNAMERecord $ pTRRecord $ sRVRecord $ tXTRecord $ mXRecord $ mDRecord $ hInfoRecord $ mInfoRecord $ aFSDBRecord $ SigRecord $ KeyRecord $ LocRecord $ nXTRecord $ nAPTRRecord $ kXRecord $ certRecord $ dNameRecord $ dSRecord $ sSHFPRecord $ rRSIGRecord $ nSECRecord $ DLVRecord $ TLSARecord $ UnknownRecord $ RPRecord $ APLRecord $ IPSECKEYRecord $ DHCIDRecord $ HIPRecord $ SPFRecord ) )
|
||||
objectClasses: ( 2.16.840.1.113730.3.8.6.1 NAME 'idnsZone' DESC 'Zone class' SUP idnsRecord STRUCTURAL MUST ( idnsZoneActive $ idnsSOAmName $ idnsSOArName $ idnsSOAserial $ idnsSOArefresh $ idnsSOAretry $ idnsSOAexpire $ idnsSOAminimum ) MAY ( idnsUpdatePolicy $ idnsAllowQuery $ idnsAllowTransfer $ idnsAllowSyncPTR $ idnsForwardPolicy $ idnsForwarders $ idnsSecInlineSigning $ nSEC3PARAMRecord ) )
|
||||
objectClasses: ( 2.16.840.1.113730.3.8.6.2 NAME 'idnsConfigObject' DESC 'DNS global config options' STRUCTURAL MAY ( idnsForwardPolicy $ idnsForwarders $ idnsAllowSyncPTR $ idnsZoneRefresh $ idnsPersistentSearch ) )
|
||||
objectClasses: ( 2.16.840.1.113730.3.8.12.18 NAME 'ipaDNSZone' SUP top AUXILIARY MUST idnsName MAY managedBy X-ORIGIN 'IPA v3' )
|
||||
objectClasses: ( 2.16.840.1.113730.3.8.6.3 NAME 'idnsForwardZone' DESC 'Forward Zone class' SUP top STRUCTURAL MUST ( idnsName $ idnsZoneActive ) MAY ( idnsForwarders $ idnsForwardPolicy ) )
|
||||
objectClasses: ( 2.16.840.1.113730.3.8.6.4 NAME 'idnsSecKey' DESC 'DNSSEC key metadata' STRUCTURAL MUST ( idnsSecKeyRef $ idnsSecKeyCreated $ idnsSecAlgorithm ) MAY ( idnsSecKeyPublish $ idnsSecKeyActivate $ idnsSecKeyInactive $ idnsSecKeyDelete $ idnsSecKeyZone $ idnsSecKeyRevoke $ idnsSecKeySep $ cn ) X-ORIGIN 'IPA v4.1' )
|
||||
objectClasses: ( 2.16.840.1.113730.3.8.12.36 NAME 'ipaDNSContainer' DESC 'IPA DNS container' AUXILIARY MUST ( ipaDNSVersion ) X-ORIGIN 'IPA v4.3' )
|
||||
|
@ -8,7 +8,7 @@ options {
|
||||
statistics-file "data/named_stats.txt";
|
||||
memstatistics-file "data/named_mem_stats.txt";
|
||||
|
||||
forward first;
|
||||
forward $FORWARD_POLICY;
|
||||
forwarders {$FORWARDERS};
|
||||
|
||||
// Any host is permitted to issue recursive queries
|
||||
|
@ -3,9 +3,11 @@ changetype: add
|
||||
objectClass: idnsConfigObject
|
||||
objectClass: nsContainer
|
||||
objectClass: ipaConfigObject
|
||||
objectClass: ipaDNSContainer
|
||||
objectClass: top
|
||||
cn: dns
|
||||
ipaConfigString: DNSVersion 1
|
||||
ipaDNSVersion: 2
|
||||
aci: (targetattr = "*")(version 3.0; acl "Allow read access"; allow (read,search,compare) groupdn = "ldap:///cn=Read DNS Entries,cn=permissions,cn=pbac,$SUFFIX" or userattr = "parent[0,1].managedby#GROUPDN";)
|
||||
aci: (target = "ldap:///idnsname=*,cn=dns,$SUFFIX")(version 3.0;acl "Add DNS entries in a zone";allow (add) userattr = "parent[1].managedby#GROUPDN";)
|
||||
aci: (target = "ldap:///idnsname=*,cn=dns,$SUFFIX")(version 3.0;acl "Remove DNS entries from a zone";allow (delete) userattr = "parent[1].managedby#GROUPDN";)
|
||||
|
@ -16,7 +16,7 @@ default:nsslapd-pluginid: schema-compat-plugin
|
||||
# We need to run schema-compat pre-bind callback before
|
||||
# other IPA pre-bind callbacks to make sure bind DN is
|
||||
# rewritten to the original entry if needed
|
||||
default:nsslapd-pluginprecedence: 49
|
||||
default:nsslapd-pluginprecedence: 40
|
||||
default:nsslapd-pluginversion: 0.8
|
||||
default:nsslapd-pluginbetxn: on
|
||||
default:nsslapd-pluginvendor: redhat.com
|
||||
|
@ -52,6 +52,9 @@ def parse_options():
|
||||
parser.add_option("--auto-forwarders", dest="auto_forwarders",
|
||||
action="store_true", default=False,
|
||||
help="Use DNS forwarders configured in /etc/resolv.conf")
|
||||
parser.add_option("--forward-policy", dest="forward_policy",
|
||||
choices=("first", "only"), default=None,
|
||||
help="DNS forwarding policy for global forwarders")
|
||||
parser.add_option("--reverse-zone", dest="reverse_zones",
|
||||
default=[], action="append", metavar="REVERSE_ZONE",
|
||||
help="The reverse DNS zone to use. This option can be used multiple times")
|
||||
@ -96,8 +99,11 @@ def parse_options():
|
||||
parser.error("You cannot specify a --auto-reverse option together with --no-reverse")
|
||||
|
||||
if options.unattended:
|
||||
if not options.forwarders and not options.no_forwarders:
|
||||
parser.error("You must specify at least one --forwarder option or --no-forwarders option")
|
||||
if (not options.forwarders
|
||||
and not options.no_forwarders
|
||||
and not options.auto_forwarders):
|
||||
parser.error("You must specify at least one option: "
|
||||
"--forwarder or --no-forwarders or --auto-forwarders")
|
||||
|
||||
if options.kasp_db_file and not ipautil.file_exists(options.kasp_db_file):
|
||||
parser.error("File %s does not exist" % options.kasp_db_file)
|
||||
|
@ -26,7 +26,7 @@ from ipaserver.install.server import Replica
|
||||
ReplicaInstall = cli.install_tool(
|
||||
Replica,
|
||||
command_name='ipa-replica-install',
|
||||
positional_arguments='replica_file',
|
||||
positional_arguments=['replica_file'],
|
||||
usage='%prog [options] REPLICA_FILE',
|
||||
log_file_name=paths.IPAREPLICA_INSTALL_LOG,
|
||||
debug_option=True,
|
||||
|
@ -38,9 +38,8 @@ from ipaserver.install import opendnssecinstance, dnskeysyncinstance
|
||||
from ipapython import version, ipaldap
|
||||
from ipalib import api, errors, util
|
||||
from ipalib.constants import CACERT
|
||||
from ipalib.util import (create_topology_graph,
|
||||
get_topology_connection_errors, has_managed_topology)
|
||||
from ipapython.ipa_log_manager import *
|
||||
from ipalib.util import has_managed_topology, verify_host_resolvable
|
||||
from ipapython.ipa_log_manager import root_logger, standard_logging_setup
|
||||
from ipapython.dn import DN
|
||||
from ipapython.config import IPAOptionParser
|
||||
from ipaclient import ipadiscovery
|
||||
@ -69,6 +68,14 @@ commands = {
|
||||
"dnanextrange-set":(2, 2, "<master fqdn> <range>", "must provide a master and ID range"),
|
||||
}
|
||||
|
||||
# tuple of commands that work with ca tree and need Directory Manager password
|
||||
dirman_passwd_req_commands = ("list-ruv", "clean-ruv", "abort-clean-ruv",
|
||||
"clean-dangling-ruv")
|
||||
|
||||
|
||||
class NoRUVsFound(Exception):
|
||||
pass
|
||||
|
||||
|
||||
def parse_options():
|
||||
parser = IPAOptionParser(version=version.VERSION)
|
||||
@ -362,8 +369,9 @@ def get_ruv(realm, host, dirman_passwd, nolookup=False, ca=False):
|
||||
else:
|
||||
thisrepl = replication.ReplicationManager(realm, host, dirman_passwd)
|
||||
except Exception as e:
|
||||
print("Failed to connect to server %s: %s" % (host, e))
|
||||
sys.exit(1)
|
||||
root_logger.debug(traceback.format_exc())
|
||||
raise RuntimeError("Failed to connect to server {host}: {err}"
|
||||
.format(host=host, err=e))
|
||||
|
||||
search_filter = '(&(nsuniqueid=ffffffff-ffffffff-ffffffff-ffffffff)(objectclass=nstombstone))'
|
||||
try:
|
||||
@ -371,8 +379,8 @@ def get_ruv(realm, host, dirman_passwd, nolookup=False, ca=False):
|
||||
thisrepl.db_suffix, thisrepl.conn.SCOPE_SUBTREE, search_filter,
|
||||
['nsds50ruv'])
|
||||
except errors.NotFound:
|
||||
print("No RUV records found.")
|
||||
sys.exit(0)
|
||||
root_logger.debug(traceback.format_exc())
|
||||
raise NoRUVsFound("No RUV records found.")
|
||||
|
||||
servers = []
|
||||
for e in entries:
|
||||
@ -389,26 +397,86 @@ def get_ruv(realm, host, dirman_passwd, nolookup=False, ca=False):
|
||||
|
||||
return servers
|
||||
|
||||
|
||||
def get_ruv_both_suffixes(realm, host, dirman_passwd, verbose, nolookup=False):
|
||||
"""
|
||||
Get RUVs for both domain and ipaca suffixes
|
||||
"""
|
||||
ruvs = {}
|
||||
fail_gracefully = True
|
||||
|
||||
try:
|
||||
ruvs['ca'] = get_ruv(realm, host, dirman_passwd, nolookup, True)
|
||||
except (NoRUVsFound, RuntimeError) as e:
|
||||
err = "Failed to get CS-RUVs from {host}: {err}".format(host=host,
|
||||
err=e)
|
||||
if isinstance(e, RuntimeError):
|
||||
fail_gracefully = False
|
||||
if verbose:
|
||||
print(err)
|
||||
root_logger.debug(err)
|
||||
try:
|
||||
ruvs['domain'] = get_ruv(realm, host, dirman_passwd, nolookup)
|
||||
except (NoRUVsFound, RuntimeError) as e:
|
||||
err = "Failed to get RUVs from {host}: {err}".format(host=host, err=e)
|
||||
if isinstance(e, RuntimeError):
|
||||
if not fail_gracefully:
|
||||
raise
|
||||
if verbose:
|
||||
print(err)
|
||||
root_logger.debug(err)
|
||||
|
||||
if not ruvs.keys():
|
||||
raise NoRUVsFound("No RUV records found.")
|
||||
|
||||
return ruvs
|
||||
|
||||
|
||||
def list_ruv(realm, host, dirman_passwd, verbose, nolookup=False):
|
||||
"""
|
||||
List the Replica Update Vectors on this host to get the available
|
||||
replica IDs.
|
||||
"""
|
||||
try:
|
||||
servers = get_ruv_both_suffixes(realm, host, dirman_passwd,
|
||||
verbose, nolookup)
|
||||
except (NoRUVsFound, RuntimeError) as e:
|
||||
print(e)
|
||||
sys.exit(0 if isinstance(e, NoRUVsFound) else 1)
|
||||
|
||||
print('Replica Update Vectors:')
|
||||
if servers.get('domain'):
|
||||
for netloc, rid in servers['domain']:
|
||||
print("\t{name}: {id}".format(name=netloc, id=rid))
|
||||
else:
|
||||
print('\tNo RUVs found.')
|
||||
|
||||
print('Certificate Server Replica Update Vectors:')
|
||||
if servers.get('ca'):
|
||||
for netloc, rid in servers['ca']:
|
||||
print("\t{name}: {id}".format(name=netloc, id=rid))
|
||||
else:
|
||||
print('\tNo CS-RUVs found.')
|
||||
|
||||
servers = get_ruv(realm, host, dirman_passwd, nolookup)
|
||||
for (netloc, rid) in servers:
|
||||
print("%s: %s" % (netloc, rid))
|
||||
|
||||
def get_rid_by_host(realm, sourcehost, host, dirman_passwd, nolookup=False):
|
||||
"""
|
||||
Try to determine the RID by host name.
|
||||
"""
|
||||
servers = get_ruv(realm, sourcehost, dirman_passwd, nolookup)
|
||||
try:
|
||||
servers = get_ruv(realm, sourcehost, dirman_passwd, nolookup)
|
||||
except RuntimeError as e:
|
||||
print(e)
|
||||
sys.exit(1)
|
||||
except NoRUVsFound as e:
|
||||
print(e)
|
||||
servers = []
|
||||
for (netloc, rid) in servers:
|
||||
if '%s:389' % host == netloc:
|
||||
return int(rid)
|
||||
|
||||
def clean_ruv(realm, ruv, options, ca=False):
|
||||
|
||||
def clean_ruv(realm, ruv, options):
|
||||
"""
|
||||
Given an RID create a CLEANALLRUV task to clean it up.
|
||||
"""
|
||||
@ -417,19 +485,29 @@ def clean_ruv(realm, ruv, options, ca=False):
|
||||
except ValueError:
|
||||
sys.exit("Replica ID must be an integer: %s" % ruv)
|
||||
|
||||
servers = get_ruv(realm, options.host, options.dirman_passwd,
|
||||
options.nolookup, ca=ca)
|
||||
found = False
|
||||
for (netloc, rid) in servers:
|
||||
if ruv == int(rid):
|
||||
found = True
|
||||
hostname = netloc
|
||||
break
|
||||
try:
|
||||
servers = get_ruv_both_suffixes(realm, options.host,
|
||||
options.dirman_passwd,
|
||||
options.verbose,
|
||||
options.nolookup)
|
||||
except (NoRUVsFound, RuntimeError) as e:
|
||||
print(e)
|
||||
sys.exit(0 if isinstance(e, NoRUVsFound) else 1)
|
||||
|
||||
if not found:
|
||||
tree_found = None
|
||||
for tree, ruvs in servers.items():
|
||||
for netloc, rid in ruvs:
|
||||
if ruv == int(rid):
|
||||
tree_found = tree
|
||||
hostname = netloc
|
||||
break
|
||||
if tree_found:
|
||||
break
|
||||
|
||||
if not tree_found:
|
||||
sys.exit("Replica ID %s not found" % ruv)
|
||||
|
||||
if ca:
|
||||
if tree_found == 'ca':
|
||||
print("Clean the Certificate Server Replication Update Vector for %s"
|
||||
% hostname)
|
||||
else:
|
||||
@ -444,7 +522,7 @@ def clean_ruv(realm, ruv, options, ca=False):
|
||||
if not ipautil.user_input("Continue to clean?", False):
|
||||
sys.exit("Aborted")
|
||||
|
||||
if ca:
|
||||
if tree_found == 'ca':
|
||||
thisrepl = replication.get_cs_replication_manager(realm, options.host,
|
||||
options.dirman_passwd)
|
||||
else:
|
||||
@ -453,6 +531,7 @@ def clean_ruv(realm, ruv, options, ca=False):
|
||||
thisrepl.cleanallruv(ruv)
|
||||
print("Cleanup task created")
|
||||
|
||||
|
||||
def abort_clean_ruv(realm, ruv, options):
|
||||
"""
|
||||
Given an RID abort a CLEANALLRUV task.
|
||||
@ -462,38 +541,41 @@ def abort_clean_ruv(realm, ruv, options):
|
||||
except ValueError:
|
||||
sys.exit("Replica ID must be an integer: %s" % ruv)
|
||||
|
||||
servers = get_ruv(realm, options.host, options.dirman_passwd,
|
||||
options.nolookup)
|
||||
found = False
|
||||
for (netloc, rid) in servers:
|
||||
if ruv == int(rid):
|
||||
found = True
|
||||
hostname = netloc
|
||||
break
|
||||
try:
|
||||
servers = get_ruv_both_suffixes(realm, options.host,
|
||||
options.dirman_passwd,
|
||||
options.verbose,
|
||||
options.nolookup)
|
||||
except (NoRUVsFound, RuntimeError) as e:
|
||||
print(e)
|
||||
sys.exit(0 if isinstance(e, NoRUVsFound) else 1)
|
||||
|
||||
if not found:
|
||||
sys.exit("Replica ID %s not found" % ruv)
|
||||
tree_found = None
|
||||
for tree, ruvs in servers.items():
|
||||
for netloc, rid in ruvs:
|
||||
if ruv == int(rid):
|
||||
tree_found = tree
|
||||
hostname = netloc
|
||||
break
|
||||
if tree_found:
|
||||
break
|
||||
|
||||
servers = get_ruv(realm, options.host, options.dirman_passwd,
|
||||
options.nolookup)
|
||||
found = False
|
||||
for (netloc, rid) in servers:
|
||||
if ruv == int(rid):
|
||||
found = True
|
||||
hostname = netloc
|
||||
break
|
||||
|
||||
if not found:
|
||||
if not tree_found:
|
||||
sys.exit("Replica ID %s not found" % ruv)
|
||||
|
||||
print("Aborting the clean Replication Update Vector task for %s" % hostname)
|
||||
print()
|
||||
thisrepl = replication.ReplicationManager(realm, options.host,
|
||||
options.dirman_passwd)
|
||||
if tree_found == 'ca':
|
||||
thisrepl = replication.get_cs_replication_manager(realm, options.host,
|
||||
options.dirman_passwd)
|
||||
else:
|
||||
thisrepl = replication.ReplicationManager(realm, options.host,
|
||||
options.dirman_passwd)
|
||||
thisrepl.abortcleanallruv(ruv, options.force)
|
||||
|
||||
print("Cleanup task stopped")
|
||||
|
||||
|
||||
def list_clean_ruv(realm, host, dirman_passwd, verbose, nolookup=False):
|
||||
"""
|
||||
List all clean RUV tasks.
|
||||
@ -541,15 +623,6 @@ def clean_dangling_ruvs(realm, host, options):
|
||||
Cleans all RUVs and CS-RUVs that are left in the system from
|
||||
uninstalled replicas
|
||||
"""
|
||||
# get the Directory Manager password
|
||||
if not options.dirman_passwd:
|
||||
options.dirman_passwd = installutils.read_password('Directory Manager',
|
||||
confirm=False,
|
||||
validate=False,
|
||||
retry=False)
|
||||
if options.dirman_passwd is None:
|
||||
sys.exit('Directory Manager password is required')
|
||||
|
||||
conn = ipaldap.IPAdmin(host, 636, cacert=CACERT)
|
||||
try:
|
||||
conn.do_simple_bind(bindpw=options.dirman_passwd)
|
||||
@ -604,32 +677,44 @@ def clean_dangling_ruvs(realm, host, options):
|
||||
.format(host=master_cn))
|
||||
offlines.add(master_cn)
|
||||
continue
|
||||
|
||||
try:
|
||||
entry = conn.get_entry(replica_dn)
|
||||
ruv = (master_cn, entry.single_value.get('nsDS5ReplicaID'))
|
||||
# the check whether ruv is already in ruvs is performed by set type
|
||||
ruvs.add(ruv)
|
||||
|
||||
if(master_info['ca']):
|
||||
entry = conn.get_entry(csreplica_dn)
|
||||
csruv = (master_cn, entry.single_value.get('nsDS5ReplicaID'))
|
||||
csruvs.add(csruv)
|
||||
|
||||
# get_ruv returns server names with :port which needs to be split off
|
||||
ruv_list = get_ruv(realm, master_cn, options.dirman_passwd,
|
||||
options.nolookup)
|
||||
master_info['ruvs'] = set([
|
||||
(re.sub(':\d+', '', x), y)
|
||||
for (x, y) in ruv_list
|
||||
])
|
||||
try:
|
||||
entry = conn.get_entry(replica_dn)
|
||||
ruv = (master_cn, entry.single_value.get('nsDS5ReplicaID'))
|
||||
# the check whether ruv is already in ruvs is performed
|
||||
# by the set type
|
||||
ruvs.add(ruv)
|
||||
except errors.NotFound:
|
||||
pass
|
||||
|
||||
if master_info['ca']:
|
||||
ruv_list = get_ruv(realm, master_cn, options.dirman_passwd,
|
||||
options.nolookup, ca=True)
|
||||
try:
|
||||
entry = conn.get_entry(csreplica_dn)
|
||||
csruv = (master_cn,
|
||||
entry.single_value.get('nsDS5ReplicaID'))
|
||||
csruvs.add(csruv)
|
||||
except errors.NotFound:
|
||||
pass
|
||||
|
||||
try:
|
||||
ruv_dict = get_ruv_both_suffixes(realm, master_cn,
|
||||
options.dirman_passwd,
|
||||
options.verbose,
|
||||
options.nolookup)
|
||||
except (RuntimeError, NoRUVsFound):
|
||||
continue
|
||||
|
||||
# get_ruv_both_suffixes returns server names with :port
|
||||
# This needs needs to be split off
|
||||
if ruv_dict.get('domain'):
|
||||
master_info['ruvs'] = set([
|
||||
(re.sub(':\d+', '', x), y)
|
||||
for (x, y) in ruv_dict['domain']
|
||||
])
|
||||
if ruv_dict.get('ca'):
|
||||
master_info['csruvs'] = set([
|
||||
(re.sub(':\d+', '', x), y)
|
||||
for (x, y) in ruv_list
|
||||
for (x, y) in ruv_dict['ca']
|
||||
])
|
||||
except Exception as e:
|
||||
sys.exit("Failed to obtain information from '{host}': {error}"
|
||||
@ -671,10 +756,6 @@ def clean_dangling_ruvs(realm, host, options):
|
||||
print('\t\tid: {id}, hostname: {host}'
|
||||
.format(id=csruv[1], host=csruv[0]))
|
||||
|
||||
# TODO: this can be removed when #5396 is fixed
|
||||
if offlines:
|
||||
sys.exit("ERROR: All replicas need to be online to proceed.")
|
||||
|
||||
if not options.force and not ipautil.user_input("Proceed with cleaning?", False):
|
||||
sys.exit("Aborted")
|
||||
|
||||
@ -689,7 +770,7 @@ def clean_dangling_ruvs(realm, host, options):
|
||||
for csruv in master_info['clean_csruv']:
|
||||
if csruv[1] not in cleaned:
|
||||
cleaned.add(csruv[1])
|
||||
clean_ruv(realm, csruv[1], options, ca=True)
|
||||
clean_ruv(realm, csruv[1], options)
|
||||
|
||||
|
||||
def check_last_link(delrepl, realm, dirman_passwd, force):
|
||||
@ -744,10 +825,14 @@ def check_last_link(delrepl, realm, dirman_passwd, force):
|
||||
|
||||
|
||||
def enforce_host_existence(host, message=None):
|
||||
if host is not None and not ipautil.host_exists(host):
|
||||
if message is None:
|
||||
message = "Unknown host %s" % host
|
||||
if host is None:
|
||||
return
|
||||
|
||||
try:
|
||||
verify_host_resolvable(host)
|
||||
except errors.DNSNotARecordError as ex:
|
||||
if message is None:
|
||||
message = "Unknown host %s: %s" % (host, ex)
|
||||
sys.exit(message)
|
||||
|
||||
def ensure_last_services(conn, hostname, masters, options):
|
||||
@ -876,10 +961,6 @@ def del_master_managed(realm, hostname, options):
|
||||
# And pick new CA master.
|
||||
ensure_last_services(api.Backend.ldap2, hostname, masters, options)
|
||||
|
||||
# Save the RID value before we start deleting
|
||||
rid = get_rid_by_host(realm, options.host, hostname,
|
||||
options.dirman_passwd, options.nolookup)
|
||||
|
||||
# 5. Remove master entry. Topology plugin will remove replication agreements.
|
||||
try:
|
||||
api.Command.server_del(hostname_u)
|
||||
@ -1566,10 +1647,12 @@ def main():
|
||||
if options.dirman_passwd:
|
||||
dirman_passwd = options.dirman_passwd
|
||||
else:
|
||||
if not test_connection(realm, host, options.nolookup):
|
||||
if (not test_connection(realm, host, options.nolookup) or
|
||||
args[0] in dirman_passwd_req_commands):
|
||||
dirman_passwd = installutils.read_password("Directory Manager",
|
||||
confirm=False, validate=False, retry=False)
|
||||
if dirman_passwd is None:
|
||||
if dirman_passwd is None or (
|
||||
not dirman_passwd and args[0] in dirman_passwd_req_commands):
|
||||
sys.exit("Directory Manager password required")
|
||||
|
||||
options.dirman_passwd = dirman_passwd
|
||||
|
@ -31,7 +31,8 @@ from ipaserver.install.dsinstance import config_dirname
|
||||
from ipaserver.install.installutils import is_ipa_configured, ScriptError
|
||||
from ipalib import api, errors
|
||||
from ipapython.ipaldap import IPAdmin
|
||||
from ipapython.ipautil import wait_for_open_ports, wait_for_open_socket
|
||||
from ipapython.ipautil import (
|
||||
wait_for_open_ports, wait_for_open_socket, is_fips_enabled)
|
||||
from ipapython import config, dogtag
|
||||
from ipaplatform.tasks import tasks
|
||||
from ipapython.dn import DN
|
||||
@ -537,6 +538,9 @@ def main():
|
||||
elif args[0] != "start" and args[0] != "stop" and args[0] != "restart" and args[0] != "status":
|
||||
raise IpactlError("Unrecognized action [" + args[0] + "]", 2)
|
||||
|
||||
if is_fips_enabled():
|
||||
raise IpactlError("Starting IPA server in FIPS mode is not supported")
|
||||
|
||||
# check if IPA is configured at all
|
||||
try:
|
||||
check_IPA_configuration()
|
||||
|
@ -41,6 +41,12 @@ Do not add any DNS forwarders, send non\-resolvable addresses to the DNS root se
|
||||
\fB\-\-auto\-forwarders\fR
|
||||
Add DNS forwarders configured in /etc/resolv.conf to the list of forwarders used by IPA DNS.
|
||||
.TP
|
||||
\fB\-\-forward\-policy\fR=\fIfirst|only\fR
|
||||
DNS forwarding policy for global forwarders specified using other options.
|
||||
Defaults to first if no IP address belonging to a private or reserved ranges is
|
||||
detected on local interfaces (RFC 6303). Defaults to only if a private
|
||||
IP address is detected.
|
||||
.TP
|
||||
\fB\-\-reverse\-zone\fR=\fIREVERSE_ZONE\fR
|
||||
The reverse DNS zone to use. This option can be used multiple times to specify multiple reverse zones.
|
||||
.TP
|
||||
|
@ -149,6 +149,12 @@ Do not add any DNS forwarders. Root DNS servers will be used instead.
|
||||
\fB\-\-auto\-forwarders\fR
|
||||
Add DNS forwarders configured in /etc/resolv.conf to the list of forwarders used by IPA DNS.
|
||||
.TP
|
||||
\fB\-\-forward\-policy\fR=\fIfirst|only\fR
|
||||
DNS forwarding policy for global forwarders specified using other options.
|
||||
Defaults to first if no IP address belonging to a private or reserved ranges is
|
||||
detected on local interfaces (RFC 6303). Defaults to only if a private
|
||||
IP address is detected.
|
||||
.TP
|
||||
\fB\-\-reverse\-zone\fR=\fIREVERSE_ZONE\fR
|
||||
The reverse DNS zone to use. This option can be used multiple times to specify multiple reverse zones.
|
||||
.TP
|
||||
|
@ -135,6 +135,7 @@ Password for the IPA system user used by the Windows PassSync plugin to synchron
|
||||
.TP
|
||||
\fB\-\-from\fR=\fISERVER\fR
|
||||
The server to pull the data from, used by the re\-initialize and force\-sync commands.
|
||||
.TP
|
||||
.SH "RANGES"
|
||||
IPA uses the 389\-ds Distributed Numeric Assignment (DNA) Plugin to allocate POSIX ids for users and groups. A range is created when IPA is installed and half the range is assigned to the first IPA master for the purposes of allocation.
|
||||
.TP
|
||||
@ -190,8 +191,11 @@ Using connect/disconnect you can manage the replication topology.
|
||||
.TP
|
||||
List the replication IDs in use:
|
||||
# ipa\-replica\-manage list\-ruv
|
||||
srv1.example.com:389: 7
|
||||
srv2.example.com:389: 4
|
||||
Replica Update Vectors:
|
||||
srv1.example.com:389: 7
|
||||
srv2.example.com:389: 4
|
||||
Certificate Server Replica Update Vectors:
|
||||
srv1.example.com:389: 9
|
||||
.TP
|
||||
Remove references to an orphaned and deleted master:
|
||||
# ipa\-replica\-manage del \-\-force \-\-cleanup master.example.com
|
||||
|
@ -158,6 +158,12 @@ Do not add any DNS forwarders. Root DNS servers will be used instead.
|
||||
\fB\-\-auto\-forwarders\fR
|
||||
Add DNS forwarders configured in /etc/resolv.conf to the list of forwarders used by IPA DNS.
|
||||
.TP
|
||||
\fB\-\-forward\-policy\fR=\fIfirst|only\fR
|
||||
DNS forwarding policy for global forwarders specified using other options.
|
||||
Defaults to first if no IP address belonging to a private or reserved ranges is
|
||||
detected on local interfaces (RFC 6303). Defaults to only if a private
|
||||
IP address is detected.
|
||||
.TP
|
||||
\fB\-\-reverse\-zone\fR=\fIREVERSE_ZONE\fR
|
||||
The reverse DNS zone to use. This option can be used multiple times to specify multiple reverse zones.
|
||||
.TP
|
||||
|
@ -38,6 +38,9 @@ Stop all of the services that make up IPA
|
||||
restart
|
||||
Stop then start all of the services that make up IPA
|
||||
.TP
|
||||
status
|
||||
Provides status of all the services that make up IPA
|
||||
.TP
|
||||
\fB\-d\fR, \fB\-\-debug\fR
|
||||
Display debugging information
|
||||
.TP
|
||||
|
@ -298,6 +298,19 @@ return {
|
||||
validators: ['network']
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'options',
|
||||
fields: [
|
||||
{
|
||||
$type: 'checkbox',
|
||||
name: 'skip_overlap_check',
|
||||
label: '@i18n:objects.dnszone.skip_overlap_check',
|
||||
tooltip: {
|
||||
title: '@mc-opt:dnszone_add:skip_overlap_check:doc'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
policies: [
|
||||
@ -457,6 +470,19 @@ return {
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'options',
|
||||
fields: [
|
||||
{
|
||||
$type: 'checkbox',
|
||||
name: 'skip_overlap_check',
|
||||
label: '@i18n:objects.dnszone.skip_overlap_check',
|
||||
tooltip: {
|
||||
title: '@mc-opt:dnsforwardzone_add:skip_overlap_check:doc'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
policies: [
|
||||
|
@ -322,6 +322,7 @@
|
||||
"remove_permission": "Remove Permission",
|
||||
"remove_permission_confirm": "Are you sure you want to remove permission for DNS Zone ${object}?",
|
||||
"skip_dns_check": "Skip DNS check",
|
||||
"skip_overlap_check": "Skip overlap check",
|
||||
"soamname_change_message": "Do you want to check if new authoritative nameserver address is in DNS",
|
||||
"soamname_change_title": "Authoritative nameserver change"
|
||||
},
|
||||
|
9
install/updates/10-ipapwd.update
Normal file
9
install/updates/10-ipapwd.update
Normal file
@ -0,0 +1,9 @@
|
||||
dn: cn=ipa_pwd_extop,cn=plugins,cn=config
|
||||
# DS core server provides a default plugin (passwd_modify_extop) to handle
|
||||
# 1.3.6.1.4.1.4203.1.11.1 extended op (https://www.ietf.org/rfc/rfc3062.txt)
|
||||
# the pluginprecedence of the passwd_modify_extop is 50 (default value)
|
||||
#
|
||||
# IPA delivers ipa_pwd_extop plugin to handle that extended op
|
||||
# we need to make sure ipa_pwd_extop is called and so to set a lower
|
||||
# precedence value
|
||||
add:nsslapd-pluginprecedence: 49
|
@ -74,7 +74,7 @@ dn: cn=Schema Compatibility,cn=plugins,cn=config
|
||||
# We need to run schema-compat pre-bind callback before
|
||||
# other IPA pre-bind callbacks to make sure bind DN is
|
||||
# rewritten to the original entry if needed
|
||||
add:nsslapd-pluginprecedence: 49
|
||||
add:nsslapd-pluginprecedence: 40
|
||||
|
||||
dn: cn=users,cn=Schema Compatibility,cn=plugins,cn=config
|
||||
add:schema-compat-entry-attribute: %ifeq("ipauniqueid","%{ipauniqueid}","objectclass=ipaOverrideTarget","")
|
||||
|
@ -2,7 +2,6 @@
|
||||
# update DNS container
|
||||
dn: cn=dns, $SUFFIX
|
||||
addifexist: objectClass: idnsConfigObject
|
||||
addifexist: objectClass: ipaConfigObject
|
||||
addifexist: aci:(target = "ldap:///idnsname=*,cn=dns,$SUFFIX")(version 3.0;acl "Add DNS entries in a zone";allow (add) userattr = "parent[1].managedby#GROUPDN";)
|
||||
addifexist: aci:(target = "ldap:///idnsname=*,cn=dns,$SUFFIX")(version 3.0;acl "Remove DNS entries from a zone";allow (delete) userattr = "parent[1].managedby#GROUPDN";)
|
||||
addifexist: aci:(targetattr = "a6record || aaaarecord || afsdbrecord || aplrecord || arecord || certrecord || cn || cnamerecord || dhcidrecord || dlvrecord || dnamerecord || dnsclass || dnsttl || dsrecord || hinforecord || hiprecord || idnsallowdynupdate || idnsallowquery || idnsallowsyncptr || idnsallowtransfer || idnsforwarders || idnsforwardpolicy || idnsname || idnssecinlinesigning || idnssoaexpire || idnssoaminimum || idnssoamname || idnssoarefresh || idnssoaretry || idnssoarname || idnssoaserial || idnsupdatepolicy || idnszoneactive || ipseckeyrecord || keyrecord || kxrecord || locrecord || mdrecord || minforecord || mxrecord || naptrrecord || nsecrecord || nsec3paramrecord || nsrecord || nxtrecord || ptrrecord || rprecord || rrsigrecord || sigrecord || spfrecord || srvrecord || sshfprecord || tlsarecord || txtrecord || unknownrecord ")(target = "ldap:///idnsname=*,cn=dns,$SUFFIX")(version 3.0;acl "Update DNS entries in a zone";allow (write) userattr = "parent[0,1].managedby#GROUPDN";)
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
# middle
|
||||
plugin: update_ca_topology
|
||||
plugin: update_ipaconfigstring_dnsversion_to_ipadnsversion
|
||||
plugin: update_dnszones
|
||||
plugin: update_dns_limits
|
||||
plugin: update_sigden_extdom_broken_config
|
||||
@ -16,7 +17,10 @@ plugin: update_service_principalalias
|
||||
plugin: update_upload_cacrt
|
||||
|
||||
# last
|
||||
# DNS version 1
|
||||
plugin: update_master_to_dnsforwardzones
|
||||
# DNS version 2
|
||||
plugin: update_dnsforward_emptyzones
|
||||
plugin: update_managed_post
|
||||
plugin: update_managed_permissions
|
||||
plugin: update_read_replication_agreements_permission
|
||||
|
10
ipa.1
10
ipa.1
@ -1,5 +1,5 @@
|
||||
.\" A man page for ipa
|
||||
.\" Copyright (C) 2010 Red Hat, Inc.
|
||||
.\" Copyright (C) 2010-2016 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
|
||||
@ -16,7 +16,7 @@
|
||||
.\"
|
||||
.\" Author: Pavel Zuna <pzuna@redhat.com>
|
||||
.\"
|
||||
.TH "ipa" "1" "Jan 24 2012" "FreeIPA" "FreeIPA Manual Pages"
|
||||
.TH "ipa" "1" "Apr 29 2016" "FreeIPA" "FreeIPA Manual Pages"
|
||||
.SH "NAME"
|
||||
ipa \- IPA command\-line interface
|
||||
.SH "SYNOPSIS"
|
||||
@ -174,9 +174,6 @@ Same as the previous example, except this time the users first name has to be ex
|
||||
\fBipa user\-find foo \-\-first bar \-\-last foo\fR
|
||||
A user with uid "foobar", first name "bar" and last name "foo" would match the search criteria.
|
||||
.TP
|
||||
\fBipa user\-find \-\-uuid 936407bd\-da9b\-11de\-9abd\-54520012e7cd\fR
|
||||
Only the user with the specified IPA unique ID would match the search criteria.
|
||||
.TP
|
||||
\fBipa user\-find\fR
|
||||
All users would match the search criteria (as there are none).
|
||||
.SH "SERVERS"
|
||||
@ -203,6 +200,5 @@ IPA default configuration file.
|
||||
ipa\-client\-install(1), ipa\-compat\-manage(1), ipactl(1), ipa\-dns\-install(1),
|
||||
ipa\-getcert(1), ipa\-getkeytab(1), ipa\-join(1), ipa\-ldap\-updater(1),
|
||||
ipa\-nis\-manage(1), ipa\-replica\-install(1), ipa\-replica\-manage(1), ipa\-replica\-prepare(1),
|
||||
ipa\-rmkeytab(1), ipa\-server\-certinstall(2), ipa\-server\-install(1), ipa\-upgradeconfig(1),
|
||||
ipa\-host\-net\-manage(1)
|
||||
ipa\-rmkeytab(1), ipa\-server\-certinstall(2), ipa\-server\-install(1), ipa\-server\-upgrade(1)
|
||||
|
||||
|
@ -61,6 +61,8 @@ def setup_package():
|
||||
package_dir = {'ipaclient': ''},
|
||||
packages = ["ipaclient",
|
||||
],
|
||||
scripts=['../ipa'],
|
||||
data_files = [('share/man/man1', ["../ipa.1"])],
|
||||
)
|
||||
finally:
|
||||
del sys.path[0]
|
||||
|
@ -165,10 +165,11 @@ class textui(backend.Backend):
|
||||
def encode_binary(self, value):
|
||||
"""
|
||||
Convert a binary value to base64. We know a value is binary
|
||||
if it is a python str type, otherwise it is a plain string.
|
||||
if it is a python bytes type, otherwise it is a plain string.
|
||||
This function also converts datetime and DNSName values to string.
|
||||
"""
|
||||
if type(value) is bytes:
|
||||
return base64.b64encode(value)
|
||||
return base64.b64encode(value).decode('ascii')
|
||||
elif type(value) is datetime.datetime:
|
||||
return value.strftime(LDAP_GENERALIZED_TIME_FORMAT)
|
||||
elif isinstance(value, DNSName):
|
||||
|
@ -261,3 +261,4 @@ REPL_AGMT_STRIP_ATTRS = ('modifiersName',
|
||||
|
||||
DOMAIN_SUFFIX_NAME = 'domain'
|
||||
CA_SUFFIX_NAME = 'ca'
|
||||
IPA_CA_RECORD = "ipa-ca"
|
||||
|
@ -93,7 +93,9 @@ current block assignments:
|
||||
|
||||
- **4300 - 4399** `CertificateError` and its subclasses
|
||||
|
||||
- **4400 - 4999** *Reserved for future use*
|
||||
- **4400 - 4499** `DNSError` and (some of) its subclasses
|
||||
|
||||
- **4500 - 4999** *Reserved for future use*
|
||||
|
||||
- **5000 - 5999** `GenericError` and its subclasses
|
||||
|
||||
@ -1144,21 +1146,6 @@ class DefaultGroupError(ExecutionError):
|
||||
errno = 4018
|
||||
format = _('The default users group cannot be removed')
|
||||
|
||||
class DNSNotARecordError(ExecutionError):
|
||||
"""
|
||||
**4019** Raised when a hostname is not a DNS A/AAAA record
|
||||
|
||||
For example:
|
||||
|
||||
>>> raise DNSNotARecordError()
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
DNSNotARecordError: Host does not have corresponding DNS A/AAAA record
|
||||
|
||||
"""
|
||||
|
||||
errno = 4019
|
||||
format = _('Host does not have corresponding DNS A/AAAA record')
|
||||
|
||||
class ManagedGroupError(ExecutionError):
|
||||
"""
|
||||
@ -1578,24 +1565,6 @@ class DatabaseTimeout(DatabaseError):
|
||||
format = _('LDAP timeout')
|
||||
|
||||
|
||||
class DNSDataMismatch(ExecutionError):
|
||||
"""
|
||||
**4212** Raised when an DNS query didn't return expected answer
|
||||
in a configured time limit.
|
||||
|
||||
For example:
|
||||
|
||||
>>> raise DNSDataMismatch(expected="zone3.test. 86400 IN A 192.0.2.1", \
|
||||
got="zone3.test. 86400 IN A 192.168.1.1")
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
DNSDataMismatch: DNS check failed: Expected {zone3.test. 86400 IN A 192.0.2.1} got {zone3.test. 86400 IN A 192.168.1.1}
|
||||
"""
|
||||
|
||||
errno = 4212
|
||||
format = _('DNS check failed: Expected {%(expected)s} got {%(got)s}')
|
||||
|
||||
|
||||
class TaskTimeout(DatabaseError):
|
||||
"""
|
||||
**4213** Raised when an LDAP task times out
|
||||
@ -1785,6 +1754,67 @@ class CertificateInvalidError(CertificateError):
|
||||
format = _('%(name)s certificate is not valid')
|
||||
|
||||
|
||||
class DNSError(ExecutionError):
|
||||
"""
|
||||
**4400** Base class for DNS execution errors (*4400 - 4499*).
|
||||
These are typically wrapper exceptions around dns.exception.DNSException.
|
||||
"""
|
||||
|
||||
errno = 4400
|
||||
|
||||
|
||||
class DNSNotARecordError(DNSError):
|
||||
"""
|
||||
**4019** Raised when a hostname is not a DNS A/AAAA record
|
||||
|
||||
For example:
|
||||
|
||||
>>> raise DNSNotARecordError(hostname='x')
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
DNSNotARecordError: Host 'x' does not have corresponding DNS A/AAAA record
|
||||
|
||||
"""
|
||||
|
||||
errno = 4019 # this exception was defined before DNSError
|
||||
format = _(
|
||||
'Host \'%(hostname)s\' does not have corresponding DNS A/AAAA record')
|
||||
|
||||
|
||||
class DNSDataMismatch(DNSError):
|
||||
"""
|
||||
**4212** Raised when an DNS query didn't return expected answer
|
||||
in a configured time limit.
|
||||
|
||||
For example:
|
||||
|
||||
>>> raise DNSDataMismatch(expected="zone3.test. 86400 IN A 192.0.2.1", \
|
||||
got="zone3.test. 86400 IN A 192.168.1.1")
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
DNSDataMismatch: DNS check failed: Expected {zone3.test. 86400 IN A 192.0.2.1} got {zone3.test. 86400 IN A 192.168.1.1}
|
||||
"""
|
||||
|
||||
errno = 4212 # this exception was defined before DNSError
|
||||
format = _('DNS check failed: Expected {%(expected)s} got {%(got)s}')
|
||||
|
||||
|
||||
class DNSResolverError(DNSError):
|
||||
"""
|
||||
**4401** Wrapper around dns.exception.DNSException.
|
||||
Raised when an error occured in dns.resolver.
|
||||
|
||||
For example:
|
||||
>>> raise DNSResolverError(exception=ValueError("this is bad"))
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
DNSResolverError: this is bad
|
||||
"""
|
||||
|
||||
errno = 4401
|
||||
format = _('%(exception)s')
|
||||
|
||||
|
||||
##############################################################################
|
||||
# 5000 - 5999: Generic errors
|
||||
|
||||
|
@ -350,6 +350,23 @@ class ResultFormattingError(PublicMessage):
|
||||
type = "warning"
|
||||
|
||||
|
||||
class DNSForwardPolicyConflictWithEmptyZone(PublicMessage):
|
||||
"""
|
||||
**13021** Forward zone 1.10.in-addr.arpa with policy "first"
|
||||
will not forward anything because BIND automatically prefers
|
||||
empty zone "10.in-addr.arpa.".
|
||||
"""
|
||||
|
||||
errno = 13021
|
||||
type = "warning"
|
||||
format = _(
|
||||
"Forwarding policy conflicts with some automatic empty zones. "
|
||||
"Queries for zones specified by RFC 6303 will ignore "
|
||||
"forwarding and recursion and always result in NXDOMAIN answers. "
|
||||
"To override this behavior use forward policy 'only'."
|
||||
)
|
||||
|
||||
|
||||
def iter_messages(variables, base):
|
||||
"""Return a tuple with all subclasses
|
||||
"""
|
||||
|
@ -559,6 +559,8 @@ class Param(ReadOnly):
|
||||
value = self.__kw[key]
|
||||
if callable(value) and hasattr(value, '__name__'):
|
||||
value = value.__name__
|
||||
elif isinstance(value, six.integer_types):
|
||||
value = str(value)
|
||||
else:
|
||||
value = repr(value)
|
||||
yield '%s=%s' % (key, value)
|
||||
|
@ -108,7 +108,10 @@ class batch(Command):
|
||||
|
||||
result = api.Command[name](*a, **newkw)
|
||||
self.info(
|
||||
'%s: batch: %s(%s): SUCCESS', context.principal, name, ', '.join(api.Command[name]._repr_iter(**params))
|
||||
'%s: batch: %s(%s): SUCCESS',
|
||||
getattr(context, 'principal', 'UNKNOWN'),
|
||||
name,
|
||||
', '.join(api.Command[name]._repr_iter(**params))
|
||||
)
|
||||
result['error']=None
|
||||
except Exception as e:
|
||||
|
@ -61,14 +61,14 @@ def _acl_make_request(principal_type, principal, ca_ref, profile_id):
|
||||
req.targethost.name = ca_ref
|
||||
req.service.name = profile_id
|
||||
if principal_type == 'user':
|
||||
req.user.name = principal
|
||||
req.user.name = name
|
||||
elif principal_type == 'host':
|
||||
req.user.name = name
|
||||
elif principal_type == 'service':
|
||||
req.user.name = normalize_principal(principal)
|
||||
groups = []
|
||||
if principal_type == 'user':
|
||||
user_obj = api.Command.user_show(principal)['result']
|
||||
user_obj = api.Command.user_show(name)['result']
|
||||
groups = user_obj.get('memberof_group', [])
|
||||
groups += user_obj.get('memberofindirect_group', [])
|
||||
elif principal_type == 'host':
|
||||
|
@ -226,7 +226,7 @@ class certprofile_show(LDAPRetrieve):
|
||||
|
||||
result = super(certprofile_show, self).forward(*keys, **options)
|
||||
if 'out' in options and 'config' in result['result']:
|
||||
with open(options['out'], 'w') as f:
|
||||
with open(options['out'], 'wb') as f:
|
||||
f.write(result['result'].pop('config'))
|
||||
result['summary'] = (
|
||||
_("Profile configuration stored in file '%(file)s'")
|
||||
|
@ -53,8 +53,11 @@ from ipalib.util import (normalize_zonemgr,
|
||||
validate_dnssec_zone_forwarder_step1,
|
||||
validate_dnssec_zone_forwarder_step2,
|
||||
verify_host_resolvable)
|
||||
from ipapython.ipautil import CheckedIPAddress, check_zone_overlap
|
||||
from ipapython.dn import DN
|
||||
from ipapython.ipautil import CheckedIPAddress
|
||||
from ipapython.dnsutil import check_zone_overlap
|
||||
from ipapython.dnsutil import DNSName
|
||||
from ipapython.dnsutil import related_to_auto_empty_zone
|
||||
|
||||
if six.PY3:
|
||||
unicode = str
|
||||
@ -1561,7 +1564,7 @@ def check_ns_rec_resolvable(zone, name, log):
|
||||
# this is a DNS name relative to the zone
|
||||
name = name.derelativize(zone.make_absolute())
|
||||
try:
|
||||
verify_host_resolvable(name, log)
|
||||
verify_host_resolvable(name)
|
||||
except errors.DNSNotARecordError:
|
||||
raise errors.NotFound(
|
||||
reason=_('Nameserver \'%(host)s\' does not have a corresponding '
|
||||
@ -1744,9 +1747,11 @@ def _normalize_zone(zone):
|
||||
if isinstance(zone, unicode):
|
||||
# normalize only non-IDNA zones
|
||||
try:
|
||||
return unicode(zone.encode('ascii')).lower()
|
||||
zone.encode('ascii')
|
||||
except UnicodeError:
|
||||
pass
|
||||
else:
|
||||
return zone.lower()
|
||||
return zone
|
||||
|
||||
|
||||
@ -1983,6 +1988,20 @@ def _add_warning_fw_zone_is_not_effective(api, result, fwzone, version):
|
||||
)
|
||||
|
||||
|
||||
def _add_warning_fw_policy_conflict_aez(result, fwzone, **options):
|
||||
"""Warn if forwarding policy conflicts with an automatic empty zone."""
|
||||
fwd_policy = result['result'].get(u'idnsforwardpolicy',
|
||||
dnsforwardzone.default_forward_policy)
|
||||
if (
|
||||
fwd_policy != [u'only']
|
||||
and related_to_auto_empty_zone(DNSName(fwzone))
|
||||
):
|
||||
messages.add_message(
|
||||
options['version'], result,
|
||||
messages.DNSForwardPolicyConflictWithEmptyZone()
|
||||
)
|
||||
|
||||
|
||||
class DNSZoneBase(LDAPObject):
|
||||
"""
|
||||
Base class for DNS Zone
|
||||
@ -2785,11 +2804,10 @@ class dnszone_add(DNSZoneBase_add):
|
||||
assert isinstance(dn, DN)
|
||||
|
||||
# Add entry to realmdomains
|
||||
# except for our own domain, forward zones, reverse zones and root zone
|
||||
# except for our own domain, reverse zones and root zone
|
||||
zone = keys[0]
|
||||
|
||||
if (zone != DNSName(api.env.domain).make_absolute() and
|
||||
not options.get('idnsforwarders') and
|
||||
not zone.is_reverse() and
|
||||
zone != DNSName.root):
|
||||
try:
|
||||
@ -4218,7 +4236,7 @@ class dns_resolve(Command):
|
||||
query=args[0]
|
||||
|
||||
try:
|
||||
verify_host_resolvable(query, self.log)
|
||||
verify_host_resolvable(query)
|
||||
except errors.DNSNotARecordError:
|
||||
raise errors.NotFound(
|
||||
reason=_('Host \'%(host)s\' not found') % {'host': query}
|
||||
@ -4298,6 +4316,9 @@ class dnsconfig(LDAPObject):
|
||||
cli_name='zone_refresh',
|
||||
label=_('Zone refresh interval'),
|
||||
),
|
||||
Int('ipadnsversion?', # available only in installer/upgrade
|
||||
label=_('IPA DNS version'),
|
||||
),
|
||||
)
|
||||
managed_permissions = {
|
||||
'System: Write DNS Configuration': {
|
||||
@ -4324,7 +4345,7 @@ class dnsconfig(LDAPObject):
|
||||
'ipapermdefaultattr': {
|
||||
'objectclass',
|
||||
'idnsallowsyncptr', 'idnsforwarders', 'idnsforwardpolicy',
|
||||
'idnspersistentsearch', 'idnszonerefresh'
|
||||
'idnspersistentsearch', 'idnszonerefresh', 'ipadnsversion'
|
||||
},
|
||||
'default_privileges': {'DNS Administrators', 'DNS Servers'},
|
||||
},
|
||||
@ -4345,11 +4366,17 @@ class dnsconfig(LDAPObject):
|
||||
result['summary'] = unicode(_('Global DNS configuration is empty'))
|
||||
|
||||
|
||||
|
||||
@register()
|
||||
class dnsconfig_mod(LDAPUpdate):
|
||||
__doc__ = _('Modify global DNS configuration.')
|
||||
|
||||
def get_options(self):
|
||||
"""hide ipadnsversion outside of installer/upgrade"""
|
||||
for option in super(dnsconfig_mod, self).get_options():
|
||||
if option.name == 'ipadnsversion':
|
||||
option = option.clone(include=('installer', 'updates'))
|
||||
yield option
|
||||
|
||||
def interactive_prompt_callback(self, kw):
|
||||
|
||||
# show informative message on client side
|
||||
@ -4367,7 +4394,13 @@ class dnsconfig_mod(LDAPUpdate):
|
||||
result = super(dnsconfig_mod, self).execute(*keys, **options)
|
||||
self.obj.postprocess_result(result)
|
||||
|
||||
# this check makes sense only when resulting forwarders are non-empty
|
||||
if result['result'].get('idnsforwarders'):
|
||||
fwzone = DNSName('.')
|
||||
_add_warning_fw_policy_conflict_aez(result, fwzone, **options)
|
||||
|
||||
if forwarders:
|
||||
# forwarders were changed
|
||||
for forwarder in forwarders:
|
||||
try:
|
||||
validate_dnssec_global_forwarder(forwarder, log=self.log)
|
||||
@ -4407,6 +4440,7 @@ class dnsconfig_show(LDAPRetrieve):
|
||||
return result
|
||||
|
||||
|
||||
|
||||
@register()
|
||||
class dnsforwardzone(DNSZoneBase):
|
||||
"""
|
||||
@ -4508,6 +4542,7 @@ class dnsforwardzone(DNSZoneBase):
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@register()
|
||||
class dnsforwardzone_add(DNSZoneBase_add):
|
||||
__doc__ = _('Create new DNS forward zone.')
|
||||
@ -4538,8 +4573,10 @@ class dnsforwardzone_add(DNSZoneBase_add):
|
||||
return dn
|
||||
|
||||
def execute(self, *keys, **options):
|
||||
fwzone = keys[-1]
|
||||
result = super(dnsforwardzone_add, self).execute(*keys, **options)
|
||||
self.obj._warning_fw_zone_is_not_effective(result, *keys, **options)
|
||||
_add_warning_fw_policy_conflict_aez(result, fwzone, **options)
|
||||
if options.get('idnsforwarders'):
|
||||
self.obj._warning_if_forwarders_do_not_work(
|
||||
result, True, *keys, **options)
|
||||
@ -4595,7 +4632,9 @@ class dnsforwardzone_mod(DNSZoneBase_mod):
|
||||
return dn
|
||||
|
||||
def execute(self, *keys, **options):
|
||||
fwzone = keys[-1]
|
||||
result = super(dnsforwardzone_mod, self).execute(*keys, **options)
|
||||
_add_warning_fw_policy_conflict_aez(result, fwzone, **options)
|
||||
if options.get('idnsforwarders'):
|
||||
self.obj._warning_if_forwarders_do_not_work(result, False, *keys,
|
||||
**options)
|
||||
|
@ -44,7 +44,7 @@ from ipalib import x509
|
||||
from ipalib import output
|
||||
from ipalib.request import context
|
||||
from ipalib.util import (normalize_sshpubkey, validate_sshpubkey_no_options,
|
||||
convert_sshpubkey_post, validate_hostname)
|
||||
convert_sshpubkey_post, validate_hostname, normalize_hostname)
|
||||
from ipapython.ipautil import ipa_generate_password, CheckedIPAddress
|
||||
from ipapython.dnsutil import DNSName
|
||||
from ipapython.ssh import SSHPublicKey
|
||||
@ -267,14 +267,6 @@ def validate_ipaddr(ugettext, ipaddr):
|
||||
return None
|
||||
|
||||
|
||||
def normalize_hostname(hostname):
|
||||
"""Use common fqdn form without the trailing dot"""
|
||||
if hostname.endswith(u'.'):
|
||||
hostname = hostname[:-1]
|
||||
hostname = hostname.lower()
|
||||
return hostname
|
||||
|
||||
|
||||
def _hostname_validator(ugettext, value):
|
||||
try:
|
||||
validate_hostname(value)
|
||||
@ -631,7 +623,7 @@ class host_add(LDAPCreate):
|
||||
check_forward=True,
|
||||
check_reverse=check_reverse)
|
||||
if not options.get('force', False) and not 'ip_address' in options:
|
||||
util.verify_host_resolvable(keys[-1], self.log)
|
||||
util.verify_host_resolvable(keys[-1])
|
||||
if 'locality' in entry_attrs:
|
||||
entry_attrs['l'] = entry_attrs['locality']
|
||||
entry_attrs['cn'] = keys[-1]
|
||||
|
@ -468,6 +468,7 @@ class i18n_messages(Command):
|
||||
"remove_permission": _("Remove Permission"),
|
||||
"remove_permission_confirm": _("Are you sure you want to remove permission for DNS Zone ${object}?"),
|
||||
"skip_dns_check": _("Skip DNS check"),
|
||||
"skip_overlap_check": _("Skip overlap check"),
|
||||
"soamname_change_message": _("Do you want to check if new authoritative nameserver address is in DNS"),
|
||||
"soamname_change_title": _("Authoritative nameserver change"),
|
||||
},
|
||||
|
@ -368,7 +368,7 @@ class otptoken_add(LDAPCreate):
|
||||
encoding = locale.getpreferredencoding(False)
|
||||
|
||||
try:
|
||||
qr_code = qr_output.getvalue().decode(encoding)
|
||||
qr_code = qr_output.getvalue().encode(encoding)
|
||||
except UnicodeError:
|
||||
add_message(
|
||||
version,
|
||||
|
@ -78,7 +78,7 @@ def validate_radiusserver(ugettext, server):
|
||||
validate_hostname(server, check_fqdn=True, allow_underscore=True)
|
||||
except ValueError as e:
|
||||
raise errors.ValidationError(name="ipatokenradiusserver",
|
||||
error=e.message)
|
||||
error=str(e))
|
||||
|
||||
|
||||
@register()
|
||||
|
@ -551,10 +551,10 @@ class service_add(LDAPCreate):
|
||||
entry_attrs['usercertificate'] = certs_der
|
||||
|
||||
if not options.get('force', False):
|
||||
# We know the host exists if we've gotten this far but we
|
||||
# really want to discourage creating services for hosts that
|
||||
# don't exist in DNS.
|
||||
util.verify_host_resolvable(hostname, self.log)
|
||||
# We know the host exists if we've gotten this far but we
|
||||
# really want to discourage creating services for hosts that
|
||||
# don't exist in DNS.
|
||||
util.verify_host_resolvable(hostname)
|
||||
if not 'managedby' in entry_attrs:
|
||||
entry_attrs['managedby'] = hostresult['dn']
|
||||
|
||||
|
@ -371,6 +371,8 @@ class stageuser_add(baseuser_add):
|
||||
answer = self.api.Object['radiusproxy'].get_dn_if_exists(cl)
|
||||
entry_attrs['ipatokenradiusconfiglink'] = answer
|
||||
|
||||
self.pre_common_callback(ldap, dn, entry_attrs, **options)
|
||||
|
||||
return dn
|
||||
|
||||
def post_callback(self, ldap, dn, entry_attrs, *keys, **options):
|
||||
|
@ -298,7 +298,10 @@ def json_encode_binary(val, version):
|
||||
new_list = [json_encode_binary(v, version) for v in val]
|
||||
return new_list
|
||||
elif isinstance(val, bytes):
|
||||
return {'__base64__': base64.b64encode(val)}
|
||||
encoded = base64.b64encode(val)
|
||||
if not six.PY2:
|
||||
encoded = encoded.decode('ascii')
|
||||
return {'__base64__': encoded}
|
||||
elif isinstance(val, Decimal):
|
||||
return {'__base64__': base64.b64encode(str(val))}
|
||||
elif isinstance(val, DN):
|
||||
|
@ -44,6 +44,7 @@ from ipalib.text import _
|
||||
from ipapython.ssh import SSHPublicKey
|
||||
from ipapython.dn import DN, RDN
|
||||
from ipapython.dnsutil import DNSName
|
||||
from ipapython.dnsutil import resolve_ip_addresses
|
||||
from ipapython.graph import Graph
|
||||
|
||||
if six.PY3:
|
||||
@ -66,31 +67,14 @@ def json_serialize(obj):
|
||||
return ''
|
||||
return json_serialize(obj.__json__())
|
||||
|
||||
def verify_host_resolvable(fqdn, log):
|
||||
"""
|
||||
See if the hostname has a DNS A/AAAA record.
|
||||
"""
|
||||
if not isinstance(fqdn, DNSName):
|
||||
fqdn = DNSName(fqdn)
|
||||
|
||||
fqdn = fqdn.make_absolute()
|
||||
for rdtype in ('A', 'AAAA'):
|
||||
try:
|
||||
answers = resolver.query(fqdn, rdtype)
|
||||
log.debug(
|
||||
'IPA: found %d %s records for %s: %s' % (len(answers),
|
||||
rdtype, fqdn, ' '.join(str(answer) for answer in answers))
|
||||
)
|
||||
except DNSException:
|
||||
log.debug(
|
||||
'IPA: DNS %s record lookup failed for %s' %
|
||||
(rdtype, fqdn)
|
||||
)
|
||||
continue
|
||||
else:
|
||||
return
|
||||
# dns lookup failed in both tries
|
||||
raise errors.DNSNotARecordError()
|
||||
def verify_host_resolvable(fqdn):
|
||||
try:
|
||||
if not resolve_ip_addresses(fqdn):
|
||||
raise errors.DNSNotARecordError(hostname=fqdn)
|
||||
except dns.exception.DNSException as ex:
|
||||
# wrap DNSException in a PublicError
|
||||
raise errors.DNSResolverError(exception=ex)
|
||||
|
||||
|
||||
def has_soa_or_ns_record(domain):
|
||||
@ -860,3 +844,11 @@ def detect_dns_zone_realm_type(api, domain):
|
||||
def has_managed_topology(api):
|
||||
domainlevel = api.Command['domainlevel_get']().get('result', DOMAIN_LEVEL_0)
|
||||
return domainlevel > DOMAIN_LEVEL_0
|
||||
|
||||
|
||||
def normalize_hostname(hostname):
|
||||
"""Use common fqdn form without the trailing dot"""
|
||||
if hostname.endswith(u'.'):
|
||||
hostname = hostname[:-1]
|
||||
hostname = hostname.lower()
|
||||
return hostname
|
||||
|
@ -133,6 +133,7 @@ class BasePathNamespace(object):
|
||||
SYSTEMD_PKI_TOMCAT_SERVICE = "/etc/systemd/system/pki-tomcatd.target.wants/pki-tomcatd@pki-tomcat.service"
|
||||
DNSSEC_TRUSTED_KEY = "/etc/trusted-key.key"
|
||||
HOME_DIR = "/home"
|
||||
PROC_FIPS_ENABLED = "/proc/sys/crypto/fips_enabled"
|
||||
ROOT_IPA_CACHE = "/root/.ipa_cache"
|
||||
ROOT_PKI = "/root/.pki"
|
||||
DOGTAG_ADMIN_P12 = "/root/ca-agent.p12"
|
||||
|
@ -84,13 +84,17 @@ class IPAVersion(object):
|
||||
def __init__(self, version):
|
||||
self.version = version
|
||||
|
||||
@property
|
||||
def _bytes(self):
|
||||
return self.version.encode('utf-8')
|
||||
|
||||
def __eq__(self, other):
|
||||
assert isinstance(other, IPAVersion)
|
||||
return _librpm.rpmvercmp(self.version, other.version) == 0
|
||||
return _librpm.rpmvercmp(self._bytes, other._bytes) == 0
|
||||
|
||||
def __lt__(self, other):
|
||||
assert isinstance(other, IPAVersion)
|
||||
return _librpm.rpmvercmp(self.version, other.version) < 0
|
||||
return _librpm.rpmvercmp(self._bytes, other._bytes) < 0
|
||||
|
||||
|
||||
class RedHatTaskNamespace(BaseTaskNamespace):
|
||||
|
@ -19,10 +19,14 @@
|
||||
|
||||
import dns.name
|
||||
import dns.exception
|
||||
import dns.resolver
|
||||
import copy
|
||||
|
||||
import six
|
||||
|
||||
from ipapython.ipautil import UnsafeIPAddress
|
||||
from ipapython.ipa_log_manager import root_logger
|
||||
|
||||
if six.PY3:
|
||||
unicode = str
|
||||
|
||||
@ -109,3 +113,254 @@ DNSName.root = DNSName(dns.name.root) # '.'
|
||||
DNSName.empty = DNSName(dns.name.empty) # '@'
|
||||
DNSName.ip4_rev_zone = DNSName(('in-addr', 'arpa', ''))
|
||||
DNSName.ip6_rev_zone = DNSName(('ip6', 'arpa', ''))
|
||||
|
||||
# Empty zones are defined in various RFCs. BIND is by default serving them.
|
||||
# This constat should contain everything listed in
|
||||
# IANA registry "Locally-Served DNS Zones"
|
||||
# URL: http://www.iana.org/assignments/locally-served-dns-zones
|
||||
# + AS112 zone defined in RFC 7534. It is not in the registry for some
|
||||
# reason but BIND 9.10 is serving it as automatic empty zones.
|
||||
EMPTY_ZONES = [DNSName(aez).make_absolute() for aez in [
|
||||
# RFC 1918
|
||||
"10.IN-ADDR.ARPA", "16.172.IN-ADDR.ARPA", "17.172.IN-ADDR.ARPA",
|
||||
"18.172.IN-ADDR.ARPA", "19.172.IN-ADDR.ARPA", "20.172.IN-ADDR.ARPA",
|
||||
"21.172.IN-ADDR.ARPA", "22.172.IN-ADDR.ARPA", "23.172.IN-ADDR.ARPA",
|
||||
"24.172.IN-ADDR.ARPA", "25.172.IN-ADDR.ARPA", "26.172.IN-ADDR.ARPA",
|
||||
"27.172.IN-ADDR.ARPA", "28.172.IN-ADDR.ARPA", "29.172.IN-ADDR.ARPA",
|
||||
"30.172.IN-ADDR.ARPA", "31.172.IN-ADDR.ARPA", "168.192.IN-ADDR.ARPA",
|
||||
# RFC 6598
|
||||
"64.100.IN-ADDR.ARPA", "65.100.IN-ADDR.ARPA", "66.100.IN-ADDR.ARPA",
|
||||
"67.100.IN-ADDR.ARPA", "68.100.IN-ADDR.ARPA", "69.100.IN-ADDR.ARPA",
|
||||
"70.100.IN-ADDR.ARPA", "71.100.IN-ADDR.ARPA", "72.100.IN-ADDR.ARPA",
|
||||
"73.100.IN-ADDR.ARPA", "74.100.IN-ADDR.ARPA", "75.100.IN-ADDR.ARPA",
|
||||
"76.100.IN-ADDR.ARPA", "77.100.IN-ADDR.ARPA", "78.100.IN-ADDR.ARPA",
|
||||
"79.100.IN-ADDR.ARPA", "80.100.IN-ADDR.ARPA", "81.100.IN-ADDR.ARPA",
|
||||
"82.100.IN-ADDR.ARPA", "83.100.IN-ADDR.ARPA", "84.100.IN-ADDR.ARPA",
|
||||
"85.100.IN-ADDR.ARPA", "86.100.IN-ADDR.ARPA", "87.100.IN-ADDR.ARPA",
|
||||
"88.100.IN-ADDR.ARPA", "89.100.IN-ADDR.ARPA", "90.100.IN-ADDR.ARPA",
|
||||
"91.100.IN-ADDR.ARPA", "92.100.IN-ADDR.ARPA", "93.100.IN-ADDR.ARPA",
|
||||
"94.100.IN-ADDR.ARPA", "95.100.IN-ADDR.ARPA", "96.100.IN-ADDR.ARPA",
|
||||
"97.100.IN-ADDR.ARPA", "98.100.IN-ADDR.ARPA", "99.100.IN-ADDR.ARPA",
|
||||
"100.100.IN-ADDR.ARPA", "101.100.IN-ADDR.ARPA",
|
||||
"102.100.IN-ADDR.ARPA", "103.100.IN-ADDR.ARPA",
|
||||
"104.100.IN-ADDR.ARPA", "105.100.IN-ADDR.ARPA",
|
||||
"106.100.IN-ADDR.ARPA", "107.100.IN-ADDR.ARPA",
|
||||
"108.100.IN-ADDR.ARPA", "109.100.IN-ADDR.ARPA",
|
||||
"110.100.IN-ADDR.ARPA", "111.100.IN-ADDR.ARPA",
|
||||
"112.100.IN-ADDR.ARPA", "113.100.IN-ADDR.ARPA",
|
||||
"114.100.IN-ADDR.ARPA", "115.100.IN-ADDR.ARPA",
|
||||
"116.100.IN-ADDR.ARPA", "117.100.IN-ADDR.ARPA",
|
||||
"118.100.IN-ADDR.ARPA", "119.100.IN-ADDR.ARPA",
|
||||
"120.100.IN-ADDR.ARPA", "121.100.IN-ADDR.ARPA",
|
||||
"122.100.IN-ADDR.ARPA", "123.100.IN-ADDR.ARPA",
|
||||
"124.100.IN-ADDR.ARPA", "125.100.IN-ADDR.ARPA",
|
||||
"126.100.IN-ADDR.ARPA", "127.100.IN-ADDR.ARPA",
|
||||
# RFC 5735 and RFC 5737
|
||||
"0.IN-ADDR.ARPA", "127.IN-ADDR.ARPA", "254.169.IN-ADDR.ARPA",
|
||||
"2.0.192.IN-ADDR.ARPA", "100.51.198.IN-ADDR.ARPA",
|
||||
"113.0.203.IN-ADDR.ARPA", "255.255.255.255.IN-ADDR.ARPA",
|
||||
# Local IPv6 Unicast Addresses
|
||||
"0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.IP6.ARPA",
|
||||
"1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.IP6.ARPA",
|
||||
# LOCALLY ASSIGNED LOCAL ADDRESS SCOPE
|
||||
"D.F.IP6.ARPA", "8.E.F.IP6.ARPA", "9.E.F.IP6.ARPA", "A.E.F.IP6.ARPA",
|
||||
"B.E.F.IP6.ARPA",
|
||||
# Example Prefix, RFC 3849.
|
||||
"8.B.D.0.1.0.0.2.IP6.ARPA",
|
||||
# RFC 7534
|
||||
"EMPTY.AS112.ARPA",
|
||||
]]
|
||||
|
||||
|
||||
def assert_absolute_dnsname(name):
|
||||
"""Raise AssertionError if name is not DNSName or is not absolute.
|
||||
|
||||
>>> assert_absolute_dnsname(DNSName('absolute.name.example.'))
|
||||
>>> assert_absolute_dnsname(DNSName('relative.name.example'))
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
AssertionError: name must be absolute, ...
|
||||
>>> assert_absolute_dnsname('absolute.string.example.')
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
AssertionError: name must be DNSName instance, ...
|
||||
"""
|
||||
|
||||
assert isinstance(name, DNSName), ("name must be DNSName instance, "
|
||||
"got '%s'" % type(name))
|
||||
assert name.is_absolute(), "name must be absolute, got '%s'" % name
|
||||
|
||||
|
||||
def is_auto_empty_zone(zone):
|
||||
"""True if specified zone name exactly matches an automatic empty zone.
|
||||
|
||||
>>> is_auto_empty_zone(DNSName('in-addr.arpa.'))
|
||||
False
|
||||
>>> is_auto_empty_zone(DNSName('10.in-addr.arpa.'))
|
||||
True
|
||||
>>> is_auto_empty_zone(DNSName('1.10.in-addr.arpa.'))
|
||||
False
|
||||
>>> is_auto_empty_zone(DNSName('10.in-addr.arpa'))
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
AssertionError: ...
|
||||
"""
|
||||
assert_absolute_dnsname(zone)
|
||||
return zone in EMPTY_ZONES
|
||||
|
||||
|
||||
def inside_auto_empty_zone(name):
|
||||
"""True if specified absolute name is a subdomain of an automatic empty
|
||||
zone.
|
||||
|
||||
DNS domain is a subdomain of itself so this function
|
||||
returns True for zone apexes, too.
|
||||
|
||||
>>> inside_auto_empty_zone(DNSName('in-addr.arpa.'))
|
||||
False
|
||||
>>> inside_auto_empty_zone(DNSName('10.in-addr.arpa.'))
|
||||
True
|
||||
>>> inside_auto_empty_zone(DNSName('1.10.in-addr.arpa.'))
|
||||
True
|
||||
>>> inside_auto_empty_zone(DNSName('1.10.in-addr.arpa'))
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
AssertionError: ...
|
||||
"""
|
||||
assert_absolute_dnsname(name)
|
||||
for aez in EMPTY_ZONES:
|
||||
if name.is_subdomain(aez):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def related_to_auto_empty_zone(name):
|
||||
"""True if specified absolute name is a sub/superdomain of an automatic
|
||||
empty zone.
|
||||
|
||||
DNS domain is a subdomain of itself so this function
|
||||
returns True for zone apexes, too.
|
||||
|
||||
>>> related_to_auto_empty_zone(DNSName('.'))
|
||||
True
|
||||
>>> related_to_auto_empty_zone(DNSName('in-addr.arpa.'))
|
||||
True
|
||||
>>> related_to_auto_empty_zone(DNSName('10.in-addr.arpa.'))
|
||||
True
|
||||
>>> related_to_auto_empty_zone(DNSName('1.10.in-addr.arpa.'))
|
||||
True
|
||||
>>> related_to_auto_empty_zone(DNSName('unrelated.example.'))
|
||||
False
|
||||
>>> related_to_auto_empty_zone(DNSName('1.10.in-addr.arpa'))
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
AssertionError: ...
|
||||
"""
|
||||
assert_absolute_dnsname(name)
|
||||
relations = {dns.name.NAMERELN_SUBDOMAIN,
|
||||
dns.name.NAMERELN_EQUAL,
|
||||
dns.name.NAMERELN_SUPERDOMAIN}
|
||||
return any(name.fullcompare(aez)[0] in relations
|
||||
for aez in EMPTY_ZONES)
|
||||
|
||||
|
||||
def has_empty_zone_addresses(hostname):
|
||||
"""Detect if given host is using IP address belonging to
|
||||
an automatic empty zone.
|
||||
|
||||
Information from --ip-address option used in installed is lost by
|
||||
the time when upgrade is run. Use IP addresses from DNS as best
|
||||
approximation.
|
||||
|
||||
This is brain-dead and duplicates logic from DNS installer
|
||||
but I did not find other way around.
|
||||
"""
|
||||
ip_addresses = resolve_ip_addresses(hostname)
|
||||
return any(
|
||||
inside_auto_empty_zone(DNSName(ip.reverse_dns))
|
||||
for ip in ip_addresses
|
||||
)
|
||||
|
||||
|
||||
def resolve_rrsets(fqdn, rdtypes):
|
||||
"""
|
||||
Get Resource Record sets for given FQDN.
|
||||
CNAME chain is followed during resolution
|
||||
but CNAMEs are not returned in the resulting rrset.
|
||||
|
||||
:returns:
|
||||
set of dns.rrset.RRset objects, can be empty
|
||||
if the FQDN does not exist or if none of rrtypes exist
|
||||
"""
|
||||
# empty set of rdtypes would always return empty set of rrsets
|
||||
assert rdtypes, "rdtypes must not be empty"
|
||||
|
||||
if not isinstance(fqdn, DNSName):
|
||||
fqdn = DNSName(fqdn)
|
||||
|
||||
fqdn = fqdn.make_absolute()
|
||||
rrsets = []
|
||||
for rdtype in rdtypes:
|
||||
try:
|
||||
answer = dns.resolver.query(fqdn, rdtype)
|
||||
root_logger.debug('found %d %s records for %s: %s',
|
||||
len(answer), rdtype, fqdn, ' '.join(
|
||||
str(rr) for rr in answer))
|
||||
rrsets.append(answer.rrset)
|
||||
except dns.resolver.NXDOMAIN as ex:
|
||||
root_logger.debug(ex)
|
||||
break # no such FQDN, do not iterate
|
||||
except dns.resolver.NoAnswer as ex:
|
||||
root_logger.debug(ex) # record type does not exist for given FQDN
|
||||
except dns.exception.DNSException as ex:
|
||||
root_logger.error('DNS query for %s %s failed: %s',
|
||||
fqdn, rdtype, ex)
|
||||
raise
|
||||
|
||||
return rrsets
|
||||
|
||||
|
||||
def resolve_ip_addresses(fqdn):
|
||||
"""Get IP addresses from DNS A/AAAA records for given host (using DNS).
|
||||
:returns:
|
||||
list of IP addresses as UnsafeIPAddress objects
|
||||
"""
|
||||
rrsets = resolve_rrsets(fqdn, ['A', 'AAAA'])
|
||||
ip_addresses = set()
|
||||
for rrset in rrsets:
|
||||
ip_addresses.update({UnsafeIPAddress(ip) for ip in rrset})
|
||||
return ip_addresses
|
||||
|
||||
|
||||
def check_zone_overlap(zone, raise_on_error=True):
|
||||
root_logger.info("Checking DNS domain %s, please wait ..." % zone)
|
||||
if not isinstance(zone, DNSName):
|
||||
zone = DNSName(zone).make_absolute()
|
||||
|
||||
# automatic empty zones always exist so checking them is pointless,
|
||||
# do not report them to avoid meaningless error messages
|
||||
if is_auto_empty_zone(zone):
|
||||
return
|
||||
|
||||
try:
|
||||
containing_zone = dns.resolver.zone_for_name(zone)
|
||||
except dns.exception.DNSException as e:
|
||||
msg = ("DNS check for domain %s failed: %s." % (zone, e))
|
||||
if raise_on_error:
|
||||
raise ValueError(msg)
|
||||
else:
|
||||
root_logger.warning(msg)
|
||||
return
|
||||
|
||||
if containing_zone == zone:
|
||||
try:
|
||||
ns = [ans.to_text() for ans in dns.resolver.query(zone, 'NS')]
|
||||
except dns.exception.DNSException as e:
|
||||
root_logger.debug("Failed to resolve nameserver(s) for domain"
|
||||
" {0}: {1}".format(zone, e))
|
||||
ns = []
|
||||
|
||||
msg = u"DNS zone {0} already exists in DNS".format(zone)
|
||||
if ns:
|
||||
msg += u" and is handled by server(s): {0}".format(', '.join(ns))
|
||||
raise ValueError(msg)
|
||||
|
@ -299,9 +299,9 @@ class ConfigureTool(admintool.AdminTool):
|
||||
knob_cls = knob_classes[e.name]
|
||||
try:
|
||||
if self.positional_arguments is None:
|
||||
raise IndexError
|
||||
raise ValueError
|
||||
index = self.positional_arguments.index(e.name)
|
||||
except IndexError:
|
||||
except ValueError:
|
||||
cli_name = knob_cls.cli_name or e.name.replace('_', '-')
|
||||
desc = "option --{0}".format(cli_name)
|
||||
else:
|
||||
|
@ -459,7 +459,7 @@ class LDAPEntry(collections.MutableMapping):
|
||||
def __delitem__(self, name):
|
||||
name = self._get_attr_name(name)
|
||||
|
||||
for (altname, keyname) in self._names.items():
|
||||
for (altname, keyname) in list(self._names.items()):
|
||||
if keyname == name:
|
||||
del self._names[altname]
|
||||
|
||||
@ -816,7 +816,7 @@ class LDAPClient(object):
|
||||
If there is a problem loading the schema or the attribute is
|
||||
not in the schema return None
|
||||
"""
|
||||
if isinstance(name_or_oid, unicode):
|
||||
if six.PY2 and isinstance(name_or_oid, unicode):
|
||||
name_or_oid = name_or_oid.encode('utf-8')
|
||||
|
||||
if name_or_oid in self._SINGLE_VALUE_OVERRIDE:
|
||||
@ -1492,7 +1492,7 @@ class LDAPClient(object):
|
||||
|
||||
# pass arguments to python-ldap
|
||||
with self.error_handler():
|
||||
modlist = [(a, self.encode(b), self.encode(c))
|
||||
modlist = [(a, str(b), self.encode(c))
|
||||
for a, b, c in modlist]
|
||||
self.conn.modify_s(str(entry.dn), modlist)
|
||||
|
||||
|
@ -51,7 +51,6 @@ from ipapython import ipavalidate
|
||||
from ipapython import config
|
||||
from ipaplatform.paths import paths
|
||||
from ipapython.dn import DN
|
||||
from ipapython.dnsutil import DNSName
|
||||
|
||||
SHARE_DIR = paths.USR_SHARE_IPA_DIR
|
||||
PLUGINS_SHARE_DIR = paths.IPA_PLUGINS
|
||||
@ -87,111 +86,132 @@ def get_domain_name():
|
||||
|
||||
return domain_name
|
||||
|
||||
class CheckedIPAddress(netaddr.IPAddress):
|
||||
|
||||
class UnsafeIPAddress(netaddr.IPAddress):
|
||||
"""Any valid IP address with or without netmask."""
|
||||
|
||||
# Use inet_pton() rather than inet_aton() for IP address parsing. We
|
||||
# will use the same function in IPv4/IPv6 conversions + be stricter
|
||||
# and don't allow IP addresses such as '1.1.1' in the same time
|
||||
netaddr_ip_flags = netaddr.INET_PTON
|
||||
|
||||
def __init__(self, addr):
|
||||
if isinstance(addr, UnsafeIPAddress):
|
||||
self._net = addr._net
|
||||
super(UnsafeIPAddress, self).__init__(addr,
|
||||
flags=self.netaddr_ip_flags)
|
||||
return
|
||||
|
||||
elif isinstance(addr, netaddr.IPAddress):
|
||||
self._net = None # no information about netmask
|
||||
super(UnsafeIPAddress, self).__init__(addr,
|
||||
flags=self.netaddr_ip_flags)
|
||||
return
|
||||
|
||||
elif isinstance(addr, netaddr.IPNetwork):
|
||||
self._net = addr
|
||||
super(UnsafeIPAddress, self).__init__(self._net.ip,
|
||||
flags=self.netaddr_ip_flags)
|
||||
return
|
||||
|
||||
# option of last resort: parse it as string
|
||||
self._net = None
|
||||
addr = str(addr)
|
||||
try:
|
||||
try:
|
||||
addr = netaddr.IPAddress(addr, flags=self.netaddr_ip_flags)
|
||||
except netaddr.AddrFormatError:
|
||||
# netaddr.IPAddress doesn't handle zone indices in textual
|
||||
# IPv6 addresses. Try removing zone index and parse the
|
||||
# address again.
|
||||
addr, sep, foo = addr.partition('%')
|
||||
if sep != '%':
|
||||
raise
|
||||
addr = netaddr.IPAddress(addr, flags=self.netaddr_ip_flags)
|
||||
if addr.version != 6:
|
||||
raise
|
||||
except ValueError:
|
||||
self._net = netaddr.IPNetwork(addr, flags=self.netaddr_ip_flags)
|
||||
addr = self._net.ip
|
||||
super(UnsafeIPAddress, self).__init__(addr,
|
||||
flags=self.netaddr_ip_flags)
|
||||
|
||||
|
||||
class CheckedIPAddress(UnsafeIPAddress):
|
||||
"""IPv4 or IPv6 address with additional constraints.
|
||||
|
||||
Reserved or link-local addresses are never accepted.
|
||||
"""
|
||||
def __init__(self, addr, match_local=False, parse_netmask=True,
|
||||
allow_network=False, allow_loopback=False,
|
||||
allow_broadcast=False, allow_multicast=False):
|
||||
|
||||
super(CheckedIPAddress, self).__init__(addr)
|
||||
if isinstance(addr, CheckedIPAddress):
|
||||
super(CheckedIPAddress, self).__init__(addr, flags=self.netaddr_ip_flags)
|
||||
self.prefixlen = addr.prefixlen
|
||||
self.defaultnet = addr.defaultnet
|
||||
self.interface = addr.interface
|
||||
return
|
||||
|
||||
net = None
|
||||
iface = None
|
||||
defnet = False
|
||||
if not parse_netmask and self._net:
|
||||
raise ValueError(
|
||||
"netmask and prefix length not allowed here: {}".format(addr))
|
||||
|
||||
if isinstance(addr, netaddr.IPNetwork):
|
||||
net = addr
|
||||
addr = net.ip
|
||||
elif isinstance(addr, netaddr.IPAddress):
|
||||
pass
|
||||
else:
|
||||
try:
|
||||
try:
|
||||
addr = netaddr.IPAddress(str(addr), flags=self.netaddr_ip_flags)
|
||||
except netaddr.AddrFormatError:
|
||||
# netaddr.IPAddress doesn't handle zone indices in textual
|
||||
# IPv6 addresses. Try removing zone index and parse the
|
||||
# address again.
|
||||
if not isinstance(addr, six.string_types):
|
||||
raise
|
||||
addr, sep, foo = addr.partition('%')
|
||||
if sep != '%':
|
||||
raise
|
||||
addr = netaddr.IPAddress(str(addr), flags=self.netaddr_ip_flags)
|
||||
if addr.version != 6:
|
||||
raise
|
||||
except ValueError:
|
||||
net = netaddr.IPNetwork(str(addr), flags=self.netaddr_ip_flags)
|
||||
if not parse_netmask:
|
||||
raise ValueError("netmask and prefix length not allowed here")
|
||||
addr = net.ip
|
||||
if self.version not in (4, 6):
|
||||
raise ValueError("unsupported IP version {}".format(self.version))
|
||||
|
||||
if addr.version not in (4, 6):
|
||||
raise ValueError("unsupported IP version")
|
||||
if not allow_loopback and self.is_loopback():
|
||||
raise ValueError("cannot use loopback IP address {}".format(addr))
|
||||
if (not self.is_loopback() and self.is_reserved()) \
|
||||
or self in netaddr.ip.IPV4_6TO4:
|
||||
raise ValueError(
|
||||
"cannot use IANA reserved IP address {}".format(addr))
|
||||
|
||||
if not allow_loopback and addr.is_loopback():
|
||||
raise ValueError("cannot use loopback IP address")
|
||||
if (not addr.is_loopback() and addr.is_reserved()) \
|
||||
or addr in netaddr.ip.IPV4_6TO4:
|
||||
raise ValueError("cannot use IANA reserved IP address")
|
||||
|
||||
if addr.is_link_local():
|
||||
raise ValueError("cannot use link-local IP address")
|
||||
if not allow_multicast and addr.is_multicast():
|
||||
raise ValueError("cannot use multicast IP address")
|
||||
if self.is_link_local():
|
||||
raise ValueError(
|
||||
"cannot use link-local IP address {}".format(addr))
|
||||
if not allow_multicast and self.is_multicast():
|
||||
raise ValueError("cannot use multicast IP address {}".format(addr))
|
||||
|
||||
if match_local:
|
||||
if addr.version == 4:
|
||||
if self.version == 4:
|
||||
family = 'inet'
|
||||
elif addr.version == 6:
|
||||
elif self.version == 6:
|
||||
family = 'inet6'
|
||||
|
||||
result = run(
|
||||
[paths.IP, '-family', family, '-oneline', 'address', 'show'],
|
||||
capture_output=True)
|
||||
lines = result.output.split('\n')
|
||||
iface = None
|
||||
for line in lines:
|
||||
fields = line.split()
|
||||
if len(fields) < 4:
|
||||
continue
|
||||
|
||||
ifnet = netaddr.IPNetwork(fields[3])
|
||||
if ifnet == net or (net is None and ifnet.ip == addr):
|
||||
net = ifnet
|
||||
if ifnet == self._net or (self._net is None and ifnet.ip == self):
|
||||
self._net = ifnet
|
||||
iface = fields[1]
|
||||
break
|
||||
|
||||
if iface is None:
|
||||
raise ValueError('No network interface matches the provided IP address and netmask')
|
||||
raise ValueError('no network interface matches the IP address '
|
||||
'and netmask {}'.format(addr))
|
||||
|
||||
if net is None:
|
||||
defnet = True
|
||||
if addr.version == 4:
|
||||
net = netaddr.IPNetwork(netaddr.cidr_abbrev_to_verbose(str(addr)))
|
||||
elif addr.version == 6:
|
||||
net = netaddr.IPNetwork(str(addr) + '/64')
|
||||
if self._net is None:
|
||||
if self.version == 4:
|
||||
self._net = netaddr.IPNetwork(
|
||||
netaddr.cidr_abbrev_to_verbose(str(self)))
|
||||
elif self.version == 6:
|
||||
self._net = netaddr.IPNetwork(str(self) + '/64')
|
||||
|
||||
if not allow_network and addr == net.network:
|
||||
raise ValueError("cannot use IP network address")
|
||||
if not allow_broadcast and addr.version == 4 and addr == net.broadcast:
|
||||
raise ValueError("cannot use broadcast IP address")
|
||||
if not allow_network and self == self._net.network:
|
||||
raise ValueError("cannot use IP network address {}".format(addr))
|
||||
if not allow_broadcast and (self.version == 4 and
|
||||
self == self._net.broadcast):
|
||||
raise ValueError("cannot use broadcast IP address {}".format(addr))
|
||||
|
||||
super(CheckedIPAddress, self).__init__(addr, flags=self.netaddr_ip_flags)
|
||||
self.prefixlen = net.prefixlen
|
||||
self.defaultnet = defnet
|
||||
self.interface = iface
|
||||
self.prefixlen = self._net.prefixlen
|
||||
|
||||
def is_local(self):
|
||||
return self.interface is not None
|
||||
|
||||
def valid_ip(addr):
|
||||
return netaddr.valid_ipv4(addr) or netaddr.valid_ipv6(addr)
|
||||
@ -519,10 +539,14 @@ def dir_exists(filename):
|
||||
except:
|
||||
return False
|
||||
|
||||
|
||||
def install_file(fname, dest):
|
||||
# SELinux: use copy to keep the right context
|
||||
if file_exists(dest):
|
||||
os.rename(dest, dest + ".orig")
|
||||
shutil.move(fname, dest)
|
||||
shutil.copy(fname, dest)
|
||||
os.remove(fname)
|
||||
|
||||
|
||||
def backup_file(fname):
|
||||
if file_exists(fname):
|
||||
@ -1014,20 +1038,6 @@ def bind_port_responder(port, socket_type=socket.SOCK_STREAM, socket_timeout=Non
|
||||
raise last_socket_error # pylint: disable=E0702
|
||||
|
||||
|
||||
def host_exists(host):
|
||||
"""
|
||||
Resolve the host to see if it exists.
|
||||
|
||||
Returns True/False
|
||||
"""
|
||||
try:
|
||||
socket.getaddrinfo(host, 80)
|
||||
except socket.gaierror:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
|
||||
def reverse_record_exists(ip_address):
|
||||
"""
|
||||
Checks if IP address have some reverse record somewhere.
|
||||
@ -1044,96 +1054,6 @@ def reverse_record_exists(ip_address):
|
||||
return True
|
||||
|
||||
|
||||
def check_zone_overlap(zone, raise_on_error=True):
|
||||
root_logger.info("Checking DNS domain %s, please wait ..." % zone)
|
||||
if not isinstance(zone, DNSName):
|
||||
zone = DNSName(zone).make_absolute()
|
||||
|
||||
# automatic empty zones always exist so checking them is pointless,
|
||||
# do not report them to avoid meaningless error messages
|
||||
if is_auto_empty_zone(zone):
|
||||
return
|
||||
|
||||
try:
|
||||
containing_zone = resolver.zone_for_name(zone)
|
||||
except DNSException as e:
|
||||
msg = ("DNS check for domain %s failed: %s." % (zone, e))
|
||||
if raise_on_error:
|
||||
raise ValueError(msg)
|
||||
else:
|
||||
root_logger.warning(msg)
|
||||
return
|
||||
|
||||
if containing_zone == zone:
|
||||
try:
|
||||
ns = [ans.to_text() for ans in resolver.query(zone, 'NS')]
|
||||
except DNSException as e:
|
||||
root_logger.debug("Failed to resolve nameserver(s) for domain"
|
||||
" {0}: {1}".format(zone, e))
|
||||
ns = []
|
||||
|
||||
msg = u"DNS zone {0} already exists in DNS".format(zone)
|
||||
if ns:
|
||||
msg += u" and is handled by server(s): {0}".format(', '.join(ns))
|
||||
raise ValueError(msg)
|
||||
|
||||
|
||||
def is_auto_empty_zone(zone):
|
||||
assert isinstance(zone, DNSName)
|
||||
|
||||
automatic_empty_zones = [DNSName(aez).make_absolute() for aez in [
|
||||
# RFC 1918
|
||||
"10.IN-ADDR.ARPA", "16.172.IN-ADDR.ARPA", "17.172.IN-ADDR.ARPA",
|
||||
"18.172.IN-ADDR.ARPA", "19.172.IN-ADDR.ARPA", "20.172.IN-ADDR.ARPA",
|
||||
"21.172.IN-ADDR.ARPA", "22.172.IN-ADDR.ARPA", "23.172.IN-ADDR.ARPA",
|
||||
"24.172.IN-ADDR.ARPA", "25.172.IN-ADDR.ARPA", "26.172.IN-ADDR.ARPA",
|
||||
"27.172.IN-ADDR.ARPA", "28.172.IN-ADDR.ARPA", "29.172.IN-ADDR.ARPA",
|
||||
"30.172.IN-ADDR.ARPA", "31.172.IN-ADDR.ARPA", "168.192.IN-ADDR.ARPA",
|
||||
# RFC 6598
|
||||
"64.100.IN-ADDR.ARPA", "65.100.IN-ADDR.ARPA", "66.100.IN-ADDR.ARPA",
|
||||
"67.100.IN-ADDR.ARPA", "68.100.IN-ADDR.ARPA", "69.100.IN-ADDR.ARPA",
|
||||
"70.100.IN-ADDR.ARPA", "71.100.IN-ADDR.ARPA", "72.100.IN-ADDR.ARPA",
|
||||
"73.100.IN-ADDR.ARPA", "74.100.IN-ADDR.ARPA", "75.100.IN-ADDR.ARPA",
|
||||
"76.100.IN-ADDR.ARPA", "77.100.IN-ADDR.ARPA", "78.100.IN-ADDR.ARPA",
|
||||
"79.100.IN-ADDR.ARPA", "80.100.IN-ADDR.ARPA", "81.100.IN-ADDR.ARPA",
|
||||
"82.100.IN-ADDR.ARPA", "83.100.IN-ADDR.ARPA", "84.100.IN-ADDR.ARPA",
|
||||
"85.100.IN-ADDR.ARPA", "86.100.IN-ADDR.ARPA", "87.100.IN-ADDR.ARPA",
|
||||
"88.100.IN-ADDR.ARPA", "89.100.IN-ADDR.ARPA", "90.100.IN-ADDR.ARPA",
|
||||
"91.100.IN-ADDR.ARPA", "92.100.IN-ADDR.ARPA", "93.100.IN-ADDR.ARPA",
|
||||
"94.100.IN-ADDR.ARPA", "95.100.IN-ADDR.ARPA", "96.100.IN-ADDR.ARPA",
|
||||
"97.100.IN-ADDR.ARPA", "98.100.IN-ADDR.ARPA", "99.100.IN-ADDR.ARPA",
|
||||
"100.100.IN-ADDR.ARPA", "101.100.IN-ADDR.ARPA",
|
||||
"102.100.IN-ADDR.ARPA", "103.100.IN-ADDR.ARPA",
|
||||
"104.100.IN-ADDR.ARPA", "105.100.IN-ADDR.ARPA",
|
||||
"106.100.IN-ADDR.ARPA", "107.100.IN-ADDR.ARPA",
|
||||
"108.100.IN-ADDR.ARPA", "109.100.IN-ADDR.ARPA",
|
||||
"110.100.IN-ADDR.ARPA", "111.100.IN-ADDR.ARPA",
|
||||
"112.100.IN-ADDR.ARPA", "113.100.IN-ADDR.ARPA",
|
||||
"114.100.IN-ADDR.ARPA", "115.100.IN-ADDR.ARPA",
|
||||
"116.100.IN-ADDR.ARPA", "117.100.IN-ADDR.ARPA",
|
||||
"118.100.IN-ADDR.ARPA", "119.100.IN-ADDR.ARPA",
|
||||
"120.100.IN-ADDR.ARPA", "121.100.IN-ADDR.ARPA",
|
||||
"122.100.IN-ADDR.ARPA", "123.100.IN-ADDR.ARPA",
|
||||
"124.100.IN-ADDR.ARPA", "125.100.IN-ADDR.ARPA",
|
||||
"126.100.IN-ADDR.ARPA", "127.100.IN-ADDR.ARPA",
|
||||
# RFC 5735 and RFC 5737
|
||||
"0.IN-ADDR.ARPA", "127.IN-ADDR.ARPA", "254.169.IN-ADDR.ARPA",
|
||||
"2.0.192.IN-ADDR.ARPA", "100.51.198.IN-ADDR.ARPA",
|
||||
"113.0.203.IN-ADDR.ARPA", "255.255.255.255.IN-ADDR.ARPA",
|
||||
# Local IPv6 Unicast Addresses
|
||||
"0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.IP6.ARPA",
|
||||
"1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.IP6.ARPA",
|
||||
# LOCALLY ASSIGNED LOCAL ADDRESS SCOPE
|
||||
"D.F.IP6.ARPA", "8.E.F.IP6.ARPA", "9.E.F.IP6.ARPA", "A.E.F.IP6.ARPA",
|
||||
"B.E.F.IP6.ARPA",
|
||||
# Example Prefix, RFC 3849.
|
||||
"8.B.D.0.1.0.0.2.IP6.ARPA",
|
||||
# RFC 7534
|
||||
"EMPTY.AS112.ARPA",
|
||||
]]
|
||||
return zone in automatic_empty_zones
|
||||
|
||||
|
||||
def config_replace_variables(filepath, replacevars=dict(), appendvars=dict()):
|
||||
"""
|
||||
Take a key=value based configuration file, and write new version
|
||||
@ -1557,3 +1477,22 @@ if six.PY2:
|
||||
type(value).__name__))
|
||||
else:
|
||||
fsdecode = os.fsdecode #pylint: disable=no-member
|
||||
|
||||
|
||||
def is_fips_enabled():
|
||||
"""
|
||||
Checks whether this host is FIPS-enabled.
|
||||
|
||||
Returns a boolean indicating if the host is FIPS-enabled, i.e. if the
|
||||
file /proc/sys/crypto/fips_enabled contains a non-0 value. Otherwise,
|
||||
or if the file /proc/sys/crypto/fips_enabled does not exist,
|
||||
the function returns False.
|
||||
"""
|
||||
try:
|
||||
with open(paths.PROC_FIPS_ENABLED, 'r') as f:
|
||||
if f.read().strip() != '0':
|
||||
return True
|
||||
except IOError:
|
||||
# Consider that the host is not fips-enabled if the file does not exist
|
||||
pass
|
||||
return False
|
||||
|
@ -18,6 +18,7 @@
|
||||
#
|
||||
|
||||
import os
|
||||
import six
|
||||
|
||||
from ipapython.ipautil import run
|
||||
|
||||
@ -45,7 +46,7 @@ def get_real_key(key):
|
||||
One cannot request a key based on the description it was created with
|
||||
so find the one we're looking for.
|
||||
"""
|
||||
assert isinstance(key, str)
|
||||
assert isinstance(key, six.string_types)
|
||||
result = run(['keyctl', 'search', KEYRING, KEYTYPE, key],
|
||||
raiseonerr=False, capture_output=True)
|
||||
if result.returncode:
|
||||
@ -53,7 +54,7 @@ def get_real_key(key):
|
||||
return result.raw_output.rstrip()
|
||||
|
||||
def get_persistent_key(key):
|
||||
assert isinstance(key, str)
|
||||
assert isinstance(key, six.string_types)
|
||||
result = run(['keyctl', 'get_persistent', KEYRING, key],
|
||||
raiseonerr=False, capture_output=True)
|
||||
if result.returncode:
|
||||
@ -73,7 +74,7 @@ def has_key(key):
|
||||
"""
|
||||
Returns True/False whether the key exists in the keyring.
|
||||
"""
|
||||
assert isinstance(key, str)
|
||||
assert isinstance(key, six.string_types)
|
||||
try:
|
||||
get_real_key(key)
|
||||
return True
|
||||
@ -86,7 +87,7 @@ def read_key(key):
|
||||
|
||||
Use pipe instead of print here to ensure we always get the raw data.
|
||||
"""
|
||||
assert isinstance(key, str)
|
||||
assert isinstance(key, six.string_types)
|
||||
real_key = get_real_key(key)
|
||||
result = run(['keyctl', 'pipe', real_key], raiseonerr=False,
|
||||
capture_output=True)
|
||||
@ -99,7 +100,7 @@ def update_key(key, value):
|
||||
"""
|
||||
Update the keyring data. If they key doesn't exist it is created.
|
||||
"""
|
||||
assert isinstance(key, str)
|
||||
assert isinstance(key, six.string_types)
|
||||
assert isinstance(value, bytes)
|
||||
if has_key(key):
|
||||
real_key = get_real_key(key)
|
||||
@ -114,7 +115,7 @@ def add_key(key, value):
|
||||
"""
|
||||
Add a key to the kernel keyring.
|
||||
"""
|
||||
assert isinstance(key, str)
|
||||
assert isinstance(key, six.string_types)
|
||||
assert isinstance(value, bytes)
|
||||
if has_key(key):
|
||||
raise ValueError('key %s already exists' % key)
|
||||
@ -127,7 +128,7 @@ def del_key(key):
|
||||
"""
|
||||
Remove a key from the keyring
|
||||
"""
|
||||
assert isinstance(key, str)
|
||||
assert isinstance(key, six.string_types)
|
||||
real_key = get_real_key(key)
|
||||
result = run(['keyctl', 'unlink', real_key, KEYRING],
|
||||
raiseonerr=False)
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
from __future__ import print_function
|
||||
from ipaplatform.paths import paths
|
||||
import ConfigParser
|
||||
from six.moves.configparser import ConfigParser
|
||||
from cryptography.hazmat.backends import default_backend
|
||||
from cryptography.hazmat.primitives import serialization
|
||||
from cryptography.hazmat.primitives.asymmetric import rsa, ec
|
||||
@ -154,7 +154,7 @@ class IPAKEMKeys(KEMKeysStore):
|
||||
|
||||
def __init__(self, config=None, ipaconf=paths.IPA_DEFAULT_CONF):
|
||||
super(IPAKEMKeys, self).__init__(config)
|
||||
conf = ConfigParser.ConfigParser()
|
||||
conf = ConfigParser()
|
||||
conf.read(ipaconf)
|
||||
self.host = conf.get('global', 'host')
|
||||
self.realm = conf.get('global', 'realm')
|
||||
|
@ -348,7 +348,7 @@ class StateFile:
|
||||
"""
|
||||
root_logger.debug("Saving StateFile to '%s'", self._path)
|
||||
|
||||
for module in self.modules.keys():
|
||||
for module in list(self.modules.keys()):
|
||||
if len(self.modules[module]) == 0:
|
||||
del self.modules[module]
|
||||
|
||||
|
@ -32,12 +32,14 @@ import six
|
||||
|
||||
from ipaserver.install import installutils
|
||||
from ipaserver.install import service
|
||||
from ipaserver.install.cainstance import IPA_CA_RECORD
|
||||
from ipaserver.install import sysupgrade
|
||||
from ipapython import sysrestore, ipautil, ipaldap
|
||||
from ipapython.ipa_log_manager import *
|
||||
from ipapython import dnsutil
|
||||
from ipapython.dn import DN
|
||||
import ipalib
|
||||
from ipalib import api, errors
|
||||
from ipalib.constants import IPA_CA_RECORD
|
||||
from ipaplatform import services
|
||||
from ipaplatform.constants import constants
|
||||
from ipaplatform.paths import paths
|
||||
@ -48,7 +50,7 @@ from ipalib.util import (validate_zonemgr_str, normalize_zonemgr,
|
||||
normalize_zone, get_reverse_zone_default,
|
||||
zone_is_reverse, validate_dnssec_global_forwarder,
|
||||
DNSSECSignatureMissingError, EDNS0UnsupportedError,
|
||||
UnresolvableRecordError, verify_host_resolvable)
|
||||
UnresolvableRecordError)
|
||||
from ipalib.constants import CACERT
|
||||
|
||||
if six.PY3:
|
||||
@ -294,7 +296,7 @@ def read_reverse_zone(default, ip_address, allow_zone_overlap=False):
|
||||
continue
|
||||
if not allow_zone_overlap:
|
||||
try:
|
||||
ipautil.check_zone_overlap(zone, raise_on_error=False)
|
||||
dnsutil.check_zone_overlap(zone, raise_on_error=False)
|
||||
except ValueError as e:
|
||||
root_logger.error("Reverse zone %s will not be used: %s"
|
||||
% (zone, e))
|
||||
@ -314,7 +316,7 @@ def get_auto_reverse_zones(ip_addresses):
|
||||
continue
|
||||
default_reverse = get_reverse_zone_default(ip)
|
||||
try:
|
||||
ipautil.check_zone_overlap(default_reverse)
|
||||
dnsutil.check_zone_overlap(default_reverse)
|
||||
except ValueError:
|
||||
root_logger.info("Reverse zone %s for IP address %s already exists"
|
||||
% (default_reverse, ip))
|
||||
@ -461,7 +463,7 @@ def check_reverse_zones(ip_addresses, reverse_zones, options, unattended,
|
||||
# isn't the zone managed by someone else
|
||||
if not options.allow_zone_overlap:
|
||||
try:
|
||||
ipautil.check_zone_overlap(rz)
|
||||
dnsutil.check_zone_overlap(rz)
|
||||
except ValueError as e:
|
||||
msg = "Reverse zone %s will not be used: %s" % (rz, e)
|
||||
if unattended:
|
||||
@ -610,8 +612,9 @@ class BindInstance(service.Service):
|
||||
|
||||
suffix = ipautil.dn_attribute_property('_suffix')
|
||||
|
||||
def setup(self, fqdn, ip_addresses, realm_name, domain_name, forwarders, ntp,
|
||||
reverse_zones, named_user=constants.NAMED_USER, zonemgr=None,
|
||||
def setup(self, fqdn, ip_addresses, realm_name, domain_name, forwarders,
|
||||
forward_policy, ntp, reverse_zones,
|
||||
named_user=constants.NAMED_USER, zonemgr=None,
|
||||
ca_configured=None, no_dnssec_validation=False):
|
||||
self.named_user = named_user
|
||||
self.fqdn = fqdn
|
||||
@ -619,6 +622,7 @@ class BindInstance(service.Service):
|
||||
self.realm = realm_name
|
||||
self.domain = domain_name
|
||||
self.forwarders = forwarders
|
||||
self.forward_policy = forward_policy
|
||||
self.host = fqdn.split(".")[0]
|
||||
self.suffix = ipautil.realm_to_suffix(self.realm)
|
||||
self.ntp = ntp
|
||||
@ -776,6 +780,7 @@ class BindInstance(service.Service):
|
||||
REALM=self.realm,
|
||||
SERVER_ID=installutils.realm_to_serverid(self.realm),
|
||||
FORWARDERS=fwds,
|
||||
FORWARD_POLICY=self.forward_policy,
|
||||
SUFFIX=self.suffix,
|
||||
OPTIONAL_NTP=optional_ntp,
|
||||
ZONEMGR=self.zonemgr,
|
||||
@ -872,14 +877,6 @@ class BindInstance(service.Service):
|
||||
add_rr(self.domain, rname, "SRV", rdata, self.dns_backup,
|
||||
api=self.api)
|
||||
|
||||
if not dns_zone_exists(zone, self.api):
|
||||
# check if master hostname is resolvable
|
||||
try:
|
||||
verify_host_resolvable(fqdn, root_logger)
|
||||
except errors.DNSNotARecordError:
|
||||
root_logger.warning("Master FQDN (%s) is not resolvable.",
|
||||
fqdn)
|
||||
|
||||
# Add forward and reverse records to self
|
||||
for addr in addrs:
|
||||
try:
|
||||
@ -905,7 +902,7 @@ class BindInstance(service.Service):
|
||||
if fqdn == self.fqdn:
|
||||
continue
|
||||
|
||||
addrs = installutils.resolve_host(fqdn)
|
||||
addrs = installutils.resolve_ip_addresses_nss(fqdn)
|
||||
|
||||
root_logger.debug("Adding DNS records for master %s" % fqdn)
|
||||
self.__add_master_records(fqdn, addrs)
|
||||
@ -961,7 +958,9 @@ class BindInstance(service.Service):
|
||||
if dns_zone_exists(zone, self.api):
|
||||
addrs = get_fwd_rr(zone, host, api=self.api)
|
||||
else:
|
||||
addrs = installutils.resolve_host(fqdn)
|
||||
addrs = dnsutil.resolve_ip_addresses(fqdn)
|
||||
# hack, will go away with locations
|
||||
addrs = [str(addr) for addr in addrs]
|
||||
|
||||
self.__add_ipa_ca_records(fqdn, addrs, True)
|
||||
|
||||
@ -1031,6 +1030,12 @@ class BindInstance(service.Service):
|
||||
section=NAMED_SECTION_OPTIONS,
|
||||
str_val=False)
|
||||
|
||||
# prevent repeated upgrade on new installs
|
||||
sysupgrade.set_upgrade_state(
|
||||
'named.conf',
|
||||
'forward_policy_conflict_with_empty_zones_handled', True
|
||||
)
|
||||
|
||||
def __setup_resolv_conf(self):
|
||||
if not self.fstore.has_file(RESOLV_CONF):
|
||||
self.fstore.backup_file(RESOLV_CONF)
|
||||
@ -1077,11 +1082,16 @@ class BindInstance(service.Service):
|
||||
self.__add_ipa_ca_record()
|
||||
|
||||
def add_ipa_ca_dns_records(self, fqdn, domain_name, ca_configured=True):
|
||||
if not self.api.Backend.ldap2.isconnected():
|
||||
self.api.Backend.ldap2.connect(autobind=True)
|
||||
|
||||
host, zone = fqdn.split(".", 1)
|
||||
if dns_zone_exists(zone, self.api):
|
||||
addrs = get_fwd_rr(zone, host, api=self.api)
|
||||
else:
|
||||
addrs = installutils.resolve_host(fqdn)
|
||||
addrs = dnsutil.resolve_ip_addresses(fqdn)
|
||||
# hack, will go away with locations
|
||||
addrs = [str(addr) for addr in addrs]
|
||||
|
||||
self.domain = domain_name
|
||||
|
||||
@ -1169,7 +1179,9 @@ class BindInstance(service.Service):
|
||||
if dns_zone_exists(zone, self.api):
|
||||
addrs = get_fwd_rr(zone, host, api=self.api)
|
||||
else:
|
||||
addrs = installutils.resolve_host(fqdn)
|
||||
addrs = dnsutil.resolve_ip_addresses(fqdn)
|
||||
# hack, will go away with locations
|
||||
addrs = [str(addr) for addr in addrs]
|
||||
|
||||
for addr in addrs:
|
||||
del_fwd_rr(domain_name, IPA_CA_RECORD, addr, api=self.api)
|
||||
|
@ -186,7 +186,11 @@ def install_step_1(standalone, replica_config, options):
|
||||
ca.stop('pki-tomcat')
|
||||
|
||||
# We need to ldap_enable the CA now that DS is up and running
|
||||
ca.ldap_enable('CA', host_name, dm_password, basedn, ['caRenewalMaster'])
|
||||
if replica_config is None:
|
||||
config = ['caRenewalMaster']
|
||||
else:
|
||||
config = []
|
||||
ca.ldap_enable('CA', host_name, dm_password, basedn, config)
|
||||
|
||||
# This is done within stopped_service context, which restarts CA
|
||||
ca.enable_client_auth_to_db(paths.CA_CS_CFG_PATH)
|
||||
|
@ -43,6 +43,7 @@ import pipes
|
||||
from six.moves import urllib
|
||||
from six.moves.configparser import ConfigParser, RawConfigParser
|
||||
|
||||
import ipalib.constants
|
||||
from ipalib import api
|
||||
from ipalib import pkcs10, x509
|
||||
from ipalib import errors
|
||||
@ -62,6 +63,7 @@ from ipapython.ipa_log_manager import log_mgr,\
|
||||
standard_logging_setup, root_logger
|
||||
|
||||
from ipaserver.install import certs
|
||||
from ipaserver.install import bindinstance
|
||||
from ipaserver.install import dsinstance
|
||||
from ipaserver.install import installutils
|
||||
from ipaserver.install import ldapupdate
|
||||
@ -79,10 +81,6 @@ except ImportError:
|
||||
import http.client as httplib
|
||||
|
||||
|
||||
# When IPA is installed with DNS support, this CNAME should hold all IPA
|
||||
# replicas with CA configured
|
||||
IPA_CA_RECORD = "ipa-ca"
|
||||
|
||||
# We need to reset the template because the CA uses the regular boot
|
||||
# information
|
||||
INF_TEMPLATE = """
|
||||
@ -311,7 +309,7 @@ class CAInstance(DogtagInstance):
|
||||
server_cert_name = 'Server-Cert cert-pki-ca'
|
||||
|
||||
def __init__(self, realm=None, ra_db=None, host_name=None,
|
||||
dm_password=None, ldapi=True):
|
||||
dm_password=None, ldapi=True, api=api):
|
||||
super(CAInstance, self).__init__(
|
||||
realm=realm,
|
||||
subsystem="CA",
|
||||
@ -327,6 +325,7 @@ class CAInstance(DogtagInstance):
|
||||
self.cert_file = None
|
||||
self.cert_chain_file = None
|
||||
self.create_ra_agent_db = True
|
||||
self.api = api
|
||||
|
||||
if realm is not None:
|
||||
self.canickname = get_ca_nickname(realm)
|
||||
@ -1289,7 +1288,15 @@ class CAInstance(DogtagInstance):
|
||||
|
||||
def __enable_instance(self):
|
||||
basedn = ipautil.realm_to_suffix(self.realm)
|
||||
self.ldap_enable('CA', self.fqdn, None, basedn, ['caRenewalMaster'])
|
||||
self.ldap_enable('CA', self.fqdn, None, basedn)
|
||||
|
||||
def __update_ca_records(self):
|
||||
# Install CA DNS records
|
||||
if bindinstance.dns_container_exists(
|
||||
api.env.host, api.env.basedn, ldapi=True, realm=api.env.realm
|
||||
):
|
||||
bind = bindinstance.BindInstance(ldapi=True, api=self.api)
|
||||
bind.add_ipa_ca_dns_records(api.env.host, api.env.domain)
|
||||
|
||||
def configure_replica(self, master_host, subject_base=None,
|
||||
ca_cert_bundle=None, ca_signing_algorithm=None,
|
||||
@ -1359,6 +1366,7 @@ class CAInstance(DogtagInstance):
|
||||
self.__restart_http_instance)
|
||||
|
||||
self.step("enabling CA instance", self.__enable_instance)
|
||||
self.step("Updating DNS CA records", self.__update_ca_records)
|
||||
|
||||
self.start_creation(runtime=210)
|
||||
|
||||
@ -1619,14 +1627,18 @@ def configure_profiles_acl():
|
||||
conn.disconnect()
|
||||
return updated
|
||||
|
||||
def import_included_profiles():
|
||||
|
||||
def __get_profile_config(profile_id):
|
||||
sub_dict = dict(
|
||||
DOMAIN=ipautil.format_netloc(api.env.domain),
|
||||
IPA_CA_RECORD=IPA_CA_RECORD,
|
||||
IPA_CA_RECORD=ipalib.constants.IPA_CA_RECORD,
|
||||
CRL_ISSUER='CN=Certificate Authority,o=ipaca',
|
||||
SUBJECT_DN_O=dsinstance.DsInstance().find_subject_base(),
|
||||
)
|
||||
return ipautil.template_file(
|
||||
'/usr/share/ipa/profiles/{}.cfg'.format(profile_id), sub_dict)
|
||||
|
||||
def import_included_profiles():
|
||||
server_id = installutils.realm_to_serverid(api.env.realm)
|
||||
dogtag_uri = 'ldapi://%%2fvar%%2frun%%2fslapd-%s.socket' % server_id
|
||||
conn = ldap2.ldap2(api, ldap_uri=dogtag_uri)
|
||||
@ -1663,15 +1675,56 @@ def import_included_profiles():
|
||||
ipacertprofilestoreissued=['TRUE' if store_issued else 'FALSE'],
|
||||
)
|
||||
conn.add_entry(entry)
|
||||
profile_data = ipautil.template_file(
|
||||
'/usr/share/ipa/profiles/{}.cfg'.format(profile_id), sub_dict)
|
||||
_create_dogtag_profile(profile_id, profile_data)
|
||||
|
||||
# Create the profile, replacing any existing profile of same name
|
||||
profile_data = __get_profile_config(profile_id)
|
||||
_create_dogtag_profile(profile_id, profile_data, overwrite=True)
|
||||
root_logger.info("Imported profile '%s'", profile_id)
|
||||
|
||||
api.Backend.ra_certprofile.override_port = None
|
||||
conn.disconnect()
|
||||
|
||||
|
||||
def repair_profile_caIPAserviceCert():
|
||||
"""
|
||||
A regression caused replica installation to replace the FreeIPA
|
||||
version of caIPAserviceCert with the version shipped by Dogtag.
|
||||
|
||||
This function detects and repairs occurrences of this problem.
|
||||
|
||||
"""
|
||||
api.Backend.ra_certprofile._read_password()
|
||||
api.Backend.ra_certprofile.override_port = 8443
|
||||
|
||||
profile_id = 'caIPAserviceCert'
|
||||
|
||||
with api.Backend.ra_certprofile as profile_api:
|
||||
try:
|
||||
cur_config = profile_api.read_profile(profile_id).splitlines()
|
||||
except errors.RemoteRetrieveError as e:
|
||||
# no profile there to check/repair
|
||||
api.Backend.ra_certprofile.override_port = None
|
||||
return
|
||||
|
||||
indicators = [
|
||||
"policyset.serverCertSet.1.default.params.name="
|
||||
"CN=$request.req_subject_name.cn$, OU=pki-ipa, O=IPA ",
|
||||
"policyset.serverCertSet.9.default.params.crlDistPointsPointName_0="
|
||||
"https://ipa.example.com/ipa/crl/MasterCRL.bin",
|
||||
]
|
||||
need_repair = all(l in cur_config for l in indicators)
|
||||
|
||||
if need_repair:
|
||||
root_logger.debug(
|
||||
"Detected that profile '{}' has been replaced with "
|
||||
"incorrect version; begin repair.".format(profile_id))
|
||||
_create_dogtag_profile(
|
||||
profile_id, __get_profile_config(profile_id), overwrite=True)
|
||||
root_logger.debug("Repair of profile '{}' complete.".format(profile_id))
|
||||
|
||||
api.Backend.ra_certprofile.override_port = None
|
||||
|
||||
|
||||
def migrate_profiles_to_ldap():
|
||||
"""Migrate profiles from filesystem to LDAP.
|
||||
|
||||
@ -1717,12 +1770,17 @@ def migrate_profiles_to_ldap():
|
||||
profile_data += '\n'
|
||||
profile_data += 'profileId={}\n'.format(profile_id)
|
||||
profile_data += 'classId={}\n'.format(class_id)
|
||||
_create_dogtag_profile(profile_id, profile_data)
|
||||
|
||||
# Import the profile, but do not replace it if it already exists.
|
||||
# This prevents replicas from replacing IPA-managed profiles with
|
||||
# Dogtag default profiles of same name.
|
||||
#
|
||||
_create_dogtag_profile(profile_id, profile_data, overwrite=False)
|
||||
|
||||
api.Backend.ra_certprofile.override_port = None
|
||||
|
||||
|
||||
def _create_dogtag_profile(profile_id, profile_data):
|
||||
def _create_dogtag_profile(profile_id, profile_data, overwrite):
|
||||
with api.Backend.ra_certprofile as profile_api:
|
||||
# import the profile
|
||||
try:
|
||||
@ -1733,9 +1791,8 @@ def _create_dogtag_profile(profile_id, profile_data):
|
||||
root_logger.debug("Error migrating '{}': {}".format(
|
||||
profile_id, e))
|
||||
|
||||
# conflicting profile; replace it if we are
|
||||
# installing IPA, but keep it for upgrades
|
||||
if api.env.context == 'installer':
|
||||
# profile already exists
|
||||
if overwrite:
|
||||
try:
|
||||
profile_api.disable_profile(profile_id)
|
||||
except errors.RemoteRetrieveError:
|
||||
|
@ -655,7 +655,7 @@ class CertDB(object):
|
||||
subject=host,
|
||||
passwd_fname=self.passwd_fname)
|
||||
# Now wait for the cert to appear. Check three times then abort
|
||||
certmonger.wait_for_request(reqid, timeout=15)
|
||||
certmonger.wait_for_request(reqid, timeout=60)
|
||||
|
||||
|
||||
class _CrossProcessLock(object):
|
||||
|
@ -4,7 +4,7 @@ from ipapython.secrets.kem import IPAKEMKeys
|
||||
from ipapython.secrets.client import CustodiaClient
|
||||
from ipaplatform.paths import paths
|
||||
from ipaplatform.constants import constants
|
||||
from service import SimpleServiceInstance
|
||||
from ipaserver.install.service import SimpleServiceInstance
|
||||
from ipapython import ipautil
|
||||
from ipapython.ipa_log_manager import root_logger
|
||||
from ipaserver.install import installutils
|
||||
|
@ -118,7 +118,7 @@ def install_check(standalone, api, replica, options, hostname):
|
||||
domain = dnsutil.DNSName(util.normalize_zone(api.env.domain))
|
||||
print("Checking DNS domain %s, please wait ..." % domain)
|
||||
try:
|
||||
ipautil.check_zone_overlap(domain, raise_on_error=False)
|
||||
dnsutil.check_zone_overlap(domain, raise_on_error=False)
|
||||
except ValueError as e:
|
||||
if options.force or options.allow_zone_overlap:
|
||||
root_logger.warning("%s Please make sure that the domain is "
|
||||
@ -129,7 +129,7 @@ def install_check(standalone, api, replica, options, hostname):
|
||||
|
||||
for reverse_zone in options.reverse_zones:
|
||||
try:
|
||||
ipautil.check_zone_overlap(reverse_zone)
|
||||
dnsutil.check_zone_overlap(reverse_zone)
|
||||
except ValueError as e:
|
||||
if options.force or options.allow_zone_overlap:
|
||||
root_logger.warning(e.message)
|
||||
@ -259,6 +259,17 @@ def install_check(standalone, api, replica, options, hostname):
|
||||
ip_addresses = get_server_ip_address(hostname, options.unattended,
|
||||
True, options.ip_addresses)
|
||||
|
||||
if not options.forward_policy:
|
||||
# user did not specify policy, derive it: default is 'first' but
|
||||
# if any of local IP addresses belongs to private ranges use 'only'
|
||||
options.forward_policy = 'first'
|
||||
for ip in ip_addresses:
|
||||
if dnsutil.inside_auto_empty_zone(dnsutil.DNSName(ip.reverse_dns)):
|
||||
options.forward_policy = 'only'
|
||||
root_logger.debug('IP address %s belongs to a private range, '
|
||||
'using forward policy only', ip)
|
||||
break
|
||||
|
||||
if options.no_forwarders:
|
||||
options.forwarders = []
|
||||
elif options.forwarders or options.auto_forwarders:
|
||||
@ -318,8 +329,8 @@ def install(standalone, replica, options, api=api):
|
||||
bind = bindinstance.BindInstance(fstore, ldapi=True, api=api,
|
||||
autobind=AUTOBIND_ENABLED)
|
||||
bind.setup(api.env.host, ip_addresses, api.env.realm, api.env.domain,
|
||||
options.forwarders, conf_ntp, reverse_zones,
|
||||
zonemgr=options.zonemgr,
|
||||
options.forwarders, options.forward_policy, conf_ntp,
|
||||
reverse_zones, zonemgr=options.zonemgr,
|
||||
no_dnssec_validation=options.no_dnssec_validation,
|
||||
ca_configured=options.setup_ca)
|
||||
|
||||
|
@ -57,7 +57,7 @@ from ipaplatform import services
|
||||
from ipaplatform.paths import paths
|
||||
from ipaplatform.tasks import tasks
|
||||
from ipapython import certmonger
|
||||
|
||||
from ipapython import dnsutil
|
||||
|
||||
if six.PY3:
|
||||
unicode = str
|
||||
@ -447,23 +447,39 @@ def create_keytab(path, principal):
|
||||
|
||||
kadmin("ktadd -k " + path + " " + principal)
|
||||
|
||||
def resolve_host(host_name):
|
||||
def resolve_ip_addresses_nss(fqdn):
|
||||
"""Get list of IP addresses for given host (using NSS/getaddrinfo).
|
||||
:returns:
|
||||
list of IP addresses as UnsafeIPAddress objects
|
||||
"""
|
||||
# make sure the name is fully qualified
|
||||
# so search path from resolv.conf does not apply
|
||||
fqdn = str(dnsutil.DNSName(fqdn).make_absolute())
|
||||
try:
|
||||
addrinfos = socket.getaddrinfo(host_name, None,
|
||||
addrinfos = socket.getaddrinfo(fqdn, None,
|
||||
socket.AF_UNSPEC, socket.SOCK_STREAM)
|
||||
except socket.error as ex:
|
||||
if ex.errno == socket.EAI_NODATA or ex.errno == socket.EAI_NONAME:
|
||||
root_logger.debug('Name %s does not have any address: %s',
|
||||
fqdn, ex)
|
||||
return set()
|
||||
else:
|
||||
raise
|
||||
|
||||
ip_list = []
|
||||
|
||||
for ai in addrinfos:
|
||||
ip = ai[4][0]
|
||||
if ip == "127.0.0.1" or ip == "::1":
|
||||
raise HostnameLocalhost("The hostname resolves to the localhost address")
|
||||
|
||||
ip_list.append(ip)
|
||||
|
||||
return ip_list
|
||||
except socket.error:
|
||||
return []
|
||||
# accept whatever we got from NSS
|
||||
ip_addresses = set()
|
||||
for ai in addrinfos:
|
||||
try:
|
||||
ip = ipautil.UnsafeIPAddress(ai[4][0])
|
||||
except ValueError as ex:
|
||||
# getaddinfo may return link-local address other similar oddities
|
||||
# which are not accepted by CheckedIPAddress - skip these
|
||||
root_logger.warning('Name %s resolved to an unacceptable IP '
|
||||
'address %s: %s', fqdn, ai[4][0], ex)
|
||||
else:
|
||||
ip_addresses.add(ip)
|
||||
root_logger.debug('Name %s resolved to %s', fqdn, ip_addresses)
|
||||
return ip_addresses
|
||||
|
||||
def get_host_name(no_host_dns):
|
||||
"""
|
||||
@ -479,10 +495,9 @@ def get_host_name(no_host_dns):
|
||||
return hostname
|
||||
|
||||
def get_server_ip_address(host_name, unattended, setup_dns, ip_addresses):
|
||||
# Check we have a public IP that is associated with the hostname
|
||||
try:
|
||||
hostaddr = resolve_host(host_name)
|
||||
except HostnameLocalhost:
|
||||
hostaddr = resolve_ip_addresses_nss(host_name)
|
||||
if hostaddr.intersection(
|
||||
{ipautil.UnsafeIPAddress(ip) for ip in ['127.0.0.1', '::1']}):
|
||||
print("The hostname resolves to the localhost address (127.0.0.1/::1)", file=sys.stderr)
|
||||
print("Please change your /etc/hosts file so that the hostname", file=sys.stderr)
|
||||
print("resolves to the ip address of your network interface.", file=sys.stderr)
|
||||
|
@ -520,6 +520,11 @@ class ReplicaPrepare(admintool.AdminTool):
|
||||
|
||||
if not options.no_reverse:
|
||||
reverse_zone = bindinstance.find_reverse_zone(ip)
|
||||
if reverse_zone is None:
|
||||
self.log.warning(
|
||||
"Could not find any IPA managed reverse zone. "
|
||||
"Not creating PTR records")
|
||||
return
|
||||
try:
|
||||
add_ptr_rr(reverse_zone, ip_address, self.replica_fqdn)
|
||||
except errors.PublicError as e:
|
||||
|
@ -42,6 +42,7 @@ class update_ca_renewal_master(Updater):
|
||||
ldap = self.api.Backend.ldap2
|
||||
base_dn = DN(('cn', 'masters'), ('cn', 'ipa'), ('cn', 'etc'),
|
||||
self.api.env.basedn)
|
||||
dn = DN(('cn', 'CA'), ('cn', self.api.env.host), base_dn)
|
||||
filter = '(&(cn=CA)(ipaConfigString=caRenewalMaster))'
|
||||
try:
|
||||
entries = ldap.get_entries(base_dn=base_dn, filter=filter,
|
||||
@ -50,7 +51,27 @@ class update_ca_renewal_master(Updater):
|
||||
pass
|
||||
else:
|
||||
self.debug("found CA renewal master %s", entries[0].dn[1].value)
|
||||
return False, []
|
||||
|
||||
master = False
|
||||
updates = []
|
||||
|
||||
for entry in entries:
|
||||
if entry.dn == dn:
|
||||
master = True
|
||||
continue
|
||||
|
||||
updates.append({
|
||||
'dn': entry.dn,
|
||||
'updates': [
|
||||
dict(action='remove', attr='ipaConfigString',
|
||||
value='caRenewalMaster')
|
||||
],
|
||||
})
|
||||
|
||||
if master:
|
||||
return False, updates
|
||||
else:
|
||||
return False, []
|
||||
|
||||
criteria = {
|
||||
'cert-database': paths.HTTPD_ALIAS_DIR,
|
||||
@ -95,7 +116,6 @@ class update_ca_renewal_master(Updater):
|
||||
"assuming local CA is renewal slave", config)
|
||||
return (False, False, [])
|
||||
|
||||
dn = DN(('cn', 'CA'), ('cn', self.api.env.host), base_dn)
|
||||
update = {
|
||||
'dn': dn,
|
||||
'updates': [
|
||||
|
@ -27,10 +27,129 @@ from ldif import LDIFWriter
|
||||
from ipalib import api, errors, util
|
||||
from ipalib import Updater
|
||||
from ipapython.dn import DN
|
||||
from ipapython import dnsutil
|
||||
from ipalib.plugins.dns import dns_container_exists
|
||||
from ipapython.ipa_log_manager import *
|
||||
|
||||
|
||||
class DNSUpdater(Updater):
|
||||
backup_dir = u'/var/lib/ipa/backup/'
|
||||
# override backup_filename in subclass, it will be mangled by strftime
|
||||
backup_filename = None
|
||||
|
||||
def __init__(self, api):
|
||||
super(DNSUpdater, self).__init__(api)
|
||||
backup_path = u'%s%s' % (self.backup_dir, self.backup_filename)
|
||||
self.backup_path = time.strftime(backup_path)
|
||||
self._ldif_writer = None
|
||||
self._saved_privileges = set() # store privileges only once
|
||||
self.saved_zone_to_privilege = {}
|
||||
|
||||
def version_update_needed(self, target_version):
|
||||
"""Test if IPA DNS version is smaller than target version."""
|
||||
assert isinstance(target_version, int)
|
||||
|
||||
try:
|
||||
return int(self.api.Command['dnsconfig_show'](
|
||||
all=True)['result']['ipadnsversion'][0]) < target_version
|
||||
except errors.NotFound:
|
||||
# IPA DNS is not configured
|
||||
return False
|
||||
|
||||
@property
|
||||
def ldif_writer(self):
|
||||
if not self._ldif_writer:
|
||||
self.log.info('Original zones will be saved in LDIF format in '
|
||||
'%s file' % self.backup_path)
|
||||
self._ldif_writer = LDIFWriter(open(self.backup_path, 'w'))
|
||||
return self._ldif_writer
|
||||
|
||||
def backup_zone(self, zone):
|
||||
"""Backup zone object, its records, permissions, and privileges.
|
||||
|
||||
Mapping from zone to privilege (containing zone's permissions)
|
||||
will be stored in saved_zone_to_privilege dict for further usage.
|
||||
"""
|
||||
dn = str(zone['dn'])
|
||||
del zone['dn'] # dn shouldn't be as attribute in ldif
|
||||
self.ldif_writer.unparse(dn, zone)
|
||||
|
||||
ldap = self.api.Backend.ldap2
|
||||
if 'managedBy' in zone:
|
||||
permission = ldap.get_entry(DN(zone['managedBy'][0]))
|
||||
self.ldif_writer.unparse(str(permission.dn), dict(permission.raw))
|
||||
for privilege_dn in permission.get('member', []):
|
||||
# privileges can be shared by multiples zones
|
||||
if privilege_dn not in self._saved_privileges:
|
||||
self._saved_privileges.add(privilege_dn)
|
||||
privilege = ldap.get_entry(privilege_dn)
|
||||
self.ldif_writer.unparse(str(privilege.dn),
|
||||
dict(privilege.raw))
|
||||
|
||||
# remember privileges referened by permission
|
||||
if 'member' in permission:
|
||||
self.saved_zone_to_privilege[
|
||||
zone['idnsname'][0]
|
||||
] = permission['member']
|
||||
|
||||
if 'idnszone' in zone['objectClass']:
|
||||
# raw values are required to store into ldif
|
||||
records = self.api.Command['dnsrecord_find'](zone['idnsname'][0],
|
||||
all=True,
|
||||
raw=True,
|
||||
sizelimit=0)['result']
|
||||
for record in records:
|
||||
if record['idnsname'][0] == u'@':
|
||||
# zone record was saved before
|
||||
continue
|
||||
dn = str(record['dn'])
|
||||
del record['dn']
|
||||
self.ldif_writer.unparse(dn, record)
|
||||
|
||||
|
||||
class update_ipaconfigstring_dnsversion_to_ipadnsversion(Updater):
|
||||
"""
|
||||
IPA <= 4.3.1 used ipaConfigString "DNSVersion 1" on DNS container.
|
||||
This was hard to deal with in API so from IPA 4.3.2 we are using
|
||||
new ipaDNSVersion attribute with integer syntax.
|
||||
Old ipaConfigString is left there for now so if someone accidentally
|
||||
executes upgrade on an old replica again it will not re-upgrade the data.
|
||||
"""
|
||||
def execute(self, **options):
|
||||
ldap = self.api.Backend.ldap2
|
||||
dns_container_dn = DN(self.api.env.container_dns, self.api.env.basedn)
|
||||
try:
|
||||
container_entry = ldap.get_entry(dns_container_dn)
|
||||
except errors.NotFound:
|
||||
# DNS container not found, nothing to upgrade
|
||||
return False, []
|
||||
|
||||
if 'ipadnscontainer' in [
|
||||
o.lower() for o in container_entry['objectclass']
|
||||
]:
|
||||
# version data are already migrated
|
||||
return False, []
|
||||
|
||||
self.log.debug('Migrating DNS ipaConfigString to ipaDNSVersion')
|
||||
container_entry['objectclass'].append('ipadnscontainer')
|
||||
version = 0
|
||||
for config_option in container_entry.get("ipaConfigString", []):
|
||||
matched = re.match("^DNSVersion\s+(?P<version>\d+)$",
|
||||
config_option, flags=re.I)
|
||||
if matched:
|
||||
version = int(matched.group("version"))
|
||||
else:
|
||||
self.log.error(
|
||||
'Failed to parse DNS version from ipaConfigString, '
|
||||
'defaulting to version %s', version)
|
||||
container_entry['ipadnsversion'] = version
|
||||
ldap.update_entry(container_entry)
|
||||
self.log.debug('ipaDNSVersion = %s', version)
|
||||
return False, []
|
||||
|
||||
api.register(update_ipaconfigstring_dnsversion_to_ipadnsversion)
|
||||
|
||||
|
||||
class update_dnszones(Updater):
|
||||
"""
|
||||
Update all zones to meet requirements in the new FreeIPA versions
|
||||
@ -141,7 +260,7 @@ class update_dns_limits(Updater):
|
||||
api.register(update_dns_limits)
|
||||
|
||||
|
||||
class update_master_to_dnsforwardzones(Updater):
|
||||
class update_master_to_dnsforwardzones(DNSUpdater):
|
||||
"""
|
||||
Update all zones to meet requirements in the new FreeIPA versions
|
||||
|
||||
@ -149,35 +268,22 @@ class update_master_to_dnsforwardzones(Updater):
|
||||
than none, will be tranformed to forward zones.
|
||||
Original masters zone will be backed up to ldif file.
|
||||
|
||||
This should be applied only once, and only if original version was lower than 4.0
|
||||
This should be applied only once,
|
||||
and only if original version was lower than 4.0
|
||||
"""
|
||||
backup_dir = u'/var/lib/ipa/backup/'
|
||||
backup_filename = u'dns-forward-zones-backup-%Y-%m-%d-%H-%M-%S.ldif'
|
||||
backup_path = u'%s%s' % (backup_dir, backup_filename)
|
||||
backup_filename = u'dns-master-to-forward-zones-%Y-%m-%d-%H-%M-%S.ldif'
|
||||
|
||||
def execute(self, **options):
|
||||
ldap = self.api.Backend.ldap2
|
||||
# check LDAP if forwardzones already uses new semantics
|
||||
dns_container_dn = DN(self.api.env.container_dns, self.api.env.basedn)
|
||||
try:
|
||||
container_entry = ldap.get_entry(dns_container_dn)
|
||||
except errors.NotFound:
|
||||
# DNS container not found, nothing to upgrade
|
||||
if not self.version_update_needed(target_version=1):
|
||||
# forwardzones already uses new semantics,
|
||||
# no upgrade is required
|
||||
return False, []
|
||||
|
||||
for config_option in container_entry.get("ipaConfigString", []):
|
||||
matched = re.match("^DNSVersion\s+(?P<version>\d+)$",
|
||||
config_option, flags=re.I)
|
||||
if matched and int(matched.group("version")) >= 1:
|
||||
# forwardzones already uses new semantics,
|
||||
# no upgrade is required
|
||||
return False, []
|
||||
|
||||
self.log.debug('Updating forward zones')
|
||||
# update the DNSVersion, following upgrade can be executed only once
|
||||
container_entry.setdefault(
|
||||
'ipaConfigString', []).append(u"DNSVersion 1")
|
||||
ldap.update_entry(container_entry)
|
||||
self.api.Command['dnsconfig_mod'](ipadnsversion=1)
|
||||
|
||||
# Updater in IPA version from 4.0 to 4.1.2 doesn't work well, this
|
||||
# should detect if update in past has been executed, and set proper
|
||||
@ -217,77 +323,18 @@ class update_master_to_dnsforwardzones(Updater):
|
||||
zones_to_transform.append(zone)
|
||||
|
||||
if zones_to_transform:
|
||||
# add time to filename
|
||||
self.backup_path = time.strftime(self.backup_path)
|
||||
|
||||
# DNs of privileges which contain dns managed permissions
|
||||
privileges_to_ldif = set() # store priviledges only once
|
||||
zone_to_privileges = {} # zone: [privileges cn]
|
||||
|
||||
self.log.info('Zones with specified forwarders with policy different'
|
||||
' than none will be transformed to forward zones.')
|
||||
self.log.info('Original zones will be saved in LDIF format in '
|
||||
'%s file' % self.backup_path)
|
||||
try:
|
||||
|
||||
with open(self.backup_path, 'w') as f:
|
||||
writer = LDIFWriter(f)
|
||||
for zone in zones_to_transform:
|
||||
# save backup to ldif
|
||||
try:
|
||||
|
||||
dn = str(zone['dn'])
|
||||
del zone['dn'] # dn shouldn't be as attribute in ldif
|
||||
writer.unparse(dn, zone)
|
||||
|
||||
if 'managedBy' in zone:
|
||||
entry = ldap.get_entry(DN(zone['managedBy'][0]))
|
||||
for privilege_member_dn in entry.get('member', []):
|
||||
privileges_to_ldif.add(privilege_member_dn)
|
||||
writer.unparse(str(entry.dn), dict(entry.raw))
|
||||
|
||||
# privileges where permission is used
|
||||
if entry.get('member'):
|
||||
zone_to_privileges[zone['idnsname'][0]] = entry['member']
|
||||
|
||||
# raw values are required to store into ldif
|
||||
records = self.api.Command['dnsrecord_find'](
|
||||
zone['idnsname'][0],
|
||||
all=True,
|
||||
raw=True,
|
||||
sizelimit=0)['result']
|
||||
for record in records:
|
||||
if record['idnsname'][0] == u'@':
|
||||
# zone record was saved before
|
||||
continue
|
||||
dn = str(record['dn'])
|
||||
del record['dn']
|
||||
writer.unparse(dn, record)
|
||||
|
||||
except Exception as e:
|
||||
self.log.error('Unable to backup zone %s' %
|
||||
zone['idnsname'][0])
|
||||
self.log.error(traceback.format_exc())
|
||||
return False, []
|
||||
|
||||
for privilege_dn in privileges_to_ldif:
|
||||
try:
|
||||
entry = ldap.get_entry(privilege_dn)
|
||||
writer.unparse(str(entry.dn), dict(entry.raw))
|
||||
except Exception as e:
|
||||
self.log.error('Unable to backup privilege %s' %
|
||||
privilege_dn)
|
||||
self.log.error(traceback.format_exc())
|
||||
return False, []
|
||||
|
||||
f.close()
|
||||
except Exception:
|
||||
self.log.error('Unable to create backup file')
|
||||
self.log.error(traceback.format_exc())
|
||||
return False, []
|
||||
|
||||
# update
|
||||
for zone in zones_to_transform:
|
||||
try:
|
||||
self.backup_zone(zone)
|
||||
except Exception:
|
||||
self.log.error('Unable to create backup for zone, '
|
||||
'terminating zone upgrade')
|
||||
self.log.error(traceback.format_exc())
|
||||
return False, []
|
||||
|
||||
# delete master zone
|
||||
try:
|
||||
self.api.Command['dnszone_del'](zone['idnsname'])
|
||||
@ -303,7 +350,9 @@ class update_master_to_dnsforwardzones(Updater):
|
||||
try:
|
||||
kw = {
|
||||
'idnsforwarders': zone.get('idnsforwarders', []),
|
||||
'idnsforwardpolicy': zone.get('idnsforwardpolicy', [u'first'])[0]
|
||||
'idnsforwardpolicy': zone.get('idnsforwardpolicy',
|
||||
[u'first'])[0],
|
||||
'skip_overlap_check': True,
|
||||
}
|
||||
self.api.Command['dnsforwardzone_add'](zone['idnsname'][0], **kw)
|
||||
except Exception as e:
|
||||
@ -329,9 +378,9 @@ class update_master_to_dnsforwardzones(Updater):
|
||||
continue
|
||||
|
||||
else:
|
||||
if zone['idnsname'][0] in zone_to_privileges:
|
||||
if zone['idnsname'][0] in self.saved_zone_to_privilege:
|
||||
privileges = [
|
||||
dn[0].value for dn in zone_to_privileges[zone['idnsname'][0]]
|
||||
dn[0].value for dn in self.saved_zone_to_privilege[zone['idnsname'][0]]
|
||||
]
|
||||
try:
|
||||
self.api.Command['permission_add_member'](perm_name,
|
||||
@ -352,3 +401,97 @@ class update_master_to_dnsforwardzones(Updater):
|
||||
return False, []
|
||||
|
||||
api.register(update_master_to_dnsforwardzones)
|
||||
|
||||
|
||||
class update_dnsforward_emptyzones(DNSUpdater):
|
||||
"""
|
||||
Migrate forward policies which conflict with automatic empty zones
|
||||
(RFC 6303) to use forward policy = only.
|
||||
|
||||
BIND ignores conflicting forwarding configuration
|
||||
when forwarding policy != only.
|
||||
bind-dyndb-ldap 9.0+ will do the same so we have to adjust FreeIPA zones
|
||||
accordingly.
|
||||
"""
|
||||
backup_filename = u'dns-forwarding-empty-zones-%Y-%m-%d-%H-%M-%S.ldif'
|
||||
|
||||
def update_zones(self):
|
||||
try:
|
||||
fwzones = self.api.Command.dnsforwardzone_find(all=True,
|
||||
raw=True)['result']
|
||||
except errors.NotFound:
|
||||
# No forwardzones found, we are done
|
||||
return
|
||||
|
||||
logged_once = False
|
||||
for zone in fwzones:
|
||||
if not (
|
||||
dnsutil.related_to_auto_empty_zone(
|
||||
dnsutil.DNSName(zone.get('idnsname')[0]))
|
||||
and zone.get('idnsforwardpolicy', [u'first'])[0] != u'only'
|
||||
and zone.get('idnsforwarders', []) != []
|
||||
):
|
||||
# this zone does not conflict with automatic empty zone
|
||||
continue
|
||||
|
||||
if not logged_once:
|
||||
self.log.info('Forward policy for zones conflicting with '
|
||||
'automatic empty zones will be changed to '
|
||||
'"only"')
|
||||
logged_once = True
|
||||
|
||||
# backup
|
||||
try:
|
||||
self.backup_zone(zone)
|
||||
except Exception:
|
||||
self.log.error('Unable to create backup for zone %s, '
|
||||
'terminating zone upgrade', zone['idnsname'][0])
|
||||
self.log.error(traceback.format_exc())
|
||||
continue
|
||||
|
||||
# change forward policy
|
||||
try:
|
||||
self.api.Command['dnsforwardzone_mod'](
|
||||
zone['idnsname'][0],
|
||||
idnsforwardpolicy=u'only'
|
||||
)
|
||||
except Exception as e:
|
||||
self.log.error('Forward policy update for zone %s failed '
|
||||
'(%s)' % (zone['idnsname'][0], e))
|
||||
self.log.error(traceback.format_exc())
|
||||
continue
|
||||
|
||||
self.log.debug('Zone %s was sucessfully modified to use '
|
||||
'forward policy "only"', zone['idnsname'][0])
|
||||
|
||||
def update_global_ldap_forwarder(self):
|
||||
config = self.api.Command['dnsconfig_show'](all=True,
|
||||
raw=True)['result']
|
||||
if (
|
||||
config.get('idnsforwardpolicy', [u'first'])[0] == u'first'
|
||||
and config.get('idnsforwarders', [])
|
||||
):
|
||||
self.log.info('Global forward policy in LDAP for all servers will '
|
||||
'be changed to "only" to avoid conflicts with '
|
||||
'automatic empty zones')
|
||||
self.backup_zone(config)
|
||||
self.api.Command['dnsconfig_mod'](idnsforwardpolicy=u'only')
|
||||
|
||||
def execute(self, **options):
|
||||
# check LDAP if DNS subtree already uses new semantics
|
||||
if not self.version_update_needed(target_version=2):
|
||||
# forwardzones already use new semantics, no upgrade is required
|
||||
return False, []
|
||||
|
||||
self.log.debug('Updating forwarding policies in LDAP '
|
||||
'to avoid conflicts with automatic empty zones')
|
||||
# update the DNSVersion, following upgrade can be executed only once
|
||||
self.api.Command['dnsconfig_mod'](ipadnsversion=2)
|
||||
|
||||
self.update_zones()
|
||||
if dnsutil.has_empty_zone_addresses(self.api.env.host):
|
||||
self.update_global_ldap_forwarder()
|
||||
|
||||
return False, []
|
||||
|
||||
api.register(update_dnsforward_emptyzones)
|
||||
|
@ -1354,6 +1354,7 @@ class ReplicationManager(object):
|
||||
'cn': ['clean %d' % replicaId],
|
||||
'replica-base-dn': [self.db_suffix],
|
||||
'replica-id': [replicaId],
|
||||
'replica-force-cleaning': ['yes'],
|
||||
}
|
||||
)
|
||||
try:
|
||||
|
@ -14,8 +14,7 @@ from ipapython.install import common, core
|
||||
from ipapython.install.core import Knob
|
||||
from ipalib.util import validate_domain_name
|
||||
from ipaserver.install import bindinstance
|
||||
from ipapython.ipautil import check_zone_overlap
|
||||
from ipapython.dnsutil import DNSName
|
||||
from ipapython.dnsutil import check_zone_overlap
|
||||
|
||||
if six.PY3:
|
||||
unicode = str
|
||||
@ -170,6 +169,11 @@ class BaseServerDNS(common.Installable, core.Group, core.Composite):
|
||||
cli_name='forwarder',
|
||||
)
|
||||
|
||||
forward_policy = Knob(
|
||||
{'only', 'first'}, None,
|
||||
description=("DNS forwarding policy for global forwarders"),
|
||||
)
|
||||
|
||||
auto_forwarders = Knob(
|
||||
bool, False,
|
||||
description="Use DNS forwarders configured in /etc/resolv.conf",
|
||||
@ -432,6 +436,10 @@ class BaseServer(common.Installable, common.Interactive, core.Composite):
|
||||
raise RuntimeError(
|
||||
"You cannot specify a --no-forwarders option without the "
|
||||
"--setup-dns option")
|
||||
if self.dns.forward_policy:
|
||||
raise RuntimeError(
|
||||
"You cannot specify a --forward-policy option without the "
|
||||
"--setup-dns option")
|
||||
if self.dns.reverse_zones:
|
||||
raise RuntimeError(
|
||||
"You cannot specify a --reverse-zone option without the "
|
||||
|
@ -23,7 +23,8 @@ from ipapython.install.common import step
|
||||
from ipapython.install.core import Knob
|
||||
from ipapython.ipa_log_manager import root_logger
|
||||
from ipapython.ipautil import (
|
||||
decrypt_file, format_netloc, ipa_generate_password, run, user_input)
|
||||
decrypt_file, format_netloc, ipa_generate_password, run, user_input,
|
||||
is_fips_enabled)
|
||||
from ipaplatform import services
|
||||
from ipaplatform.paths import paths
|
||||
from ipaplatform.tasks import tasks
|
||||
@ -437,6 +438,10 @@ def install_check(installer):
|
||||
external_ca_file = installer._external_ca_file
|
||||
http_ca_cert = installer._ca_cert
|
||||
|
||||
if is_fips_enabled():
|
||||
raise RuntimeError(
|
||||
"Installing IPA server in FIPS mode is not supported")
|
||||
|
||||
tasks.check_selinux_status()
|
||||
|
||||
if options.master_password:
|
||||
@ -759,10 +764,11 @@ def install_check(installer):
|
||||
|
||||
if options.setup_dns:
|
||||
print("BIND DNS server will be configured to serve IPA domain with:")
|
||||
print("Forwarders: %s" % (
|
||||
print("Forwarders: %s" % (
|
||||
"No forwarders" if not options.forwarders
|
||||
else ", ".join([str(ip) for ip in options.forwarders])
|
||||
))
|
||||
print('Forward policy: %s' % options.forward_policy)
|
||||
print("Reverse zone(s): %s" % (
|
||||
"No reverse zone" if options.no_reverse or not dns.reverse_zones
|
||||
else ", ".join(str(rz) for rz in dns.reverse_zones)
|
||||
@ -992,7 +998,7 @@ def install(installer):
|
||||
# Create a BIND instance
|
||||
bind = bindinstance.BindInstance(fstore, dm_password)
|
||||
bind.setup(host_name, ip_addresses, realm_name,
|
||||
domain_name, (), not options.no_ntp, (),
|
||||
domain_name, (), 'first', not options.no_ntp, (),
|
||||
zonemgr=options.zonemgr, ca_configured=setup_ca,
|
||||
no_dnssec_validation=options.no_dnssec_validation)
|
||||
bind.create_sample_bind_zone()
|
||||
@ -1400,6 +1406,7 @@ class Server(BaseServer):
|
||||
int, constants.MAX_DOMAIN_LEVEL,
|
||||
description="IPA domain level",
|
||||
cli_name='domain-level',
|
||||
deprecated=True,
|
||||
)
|
||||
|
||||
@domainlevel.validator
|
||||
|
@ -497,6 +497,10 @@ def install_check(installer):
|
||||
options = installer
|
||||
filename = installer.replica_file
|
||||
|
||||
if ipautil.is_fips_enabled():
|
||||
raise RuntimeError(
|
||||
"Installing IPA server in FIPS mode is not supported")
|
||||
|
||||
tasks.check_selinux_status()
|
||||
|
||||
if is_ipa_configured():
|
||||
@ -939,6 +943,33 @@ def ensure_enrolled(installer):
|
||||
except Exception:
|
||||
sys.exit("Configuration of client side components failed!")
|
||||
|
||||
|
||||
def promotion_check_ipa_domain(master_ldap_conn, basedn):
|
||||
entry = master_ldap_conn.get_entry(basedn, ['associatedDomain'])
|
||||
if not 'associatedDomain' in entry:
|
||||
raise RuntimeError('IPA domain not found in LDAP.')
|
||||
|
||||
if len(entry['associatedDomain']) > 1:
|
||||
root_logger.critical(
|
||||
"Multiple IPA domains found. We are so sorry :-(, you are "
|
||||
"probably experiencing this bug "
|
||||
"https://fedorahosted.org/freeipa/ticket/5976. Please contact us "
|
||||
"for help.")
|
||||
raise RuntimeError(
|
||||
'Multiple IPA domains found in LDAP database ({domains}). '
|
||||
'Only one domain is allowed.'.format(
|
||||
domains=u', '.join(entry['associatedDomain'])
|
||||
))
|
||||
|
||||
if entry['associatedDomain'][0] != api.env.domain:
|
||||
raise RuntimeError(
|
||||
"Cannot promote this client to a replica. Local domain "
|
||||
"'{local}' does not match IPA domain '{ipadomain}'. ".format(
|
||||
local=api.env.domain,
|
||||
ipadomain=entry['associatedDomain'][0]
|
||||
))
|
||||
|
||||
|
||||
@common_cleanup
|
||||
@preserve_enrollment_state
|
||||
def promote_check(installer):
|
||||
@ -1137,6 +1168,8 @@ def promote_check(installer):
|
||||
conn.disconnect()
|
||||
conn.connect(ccache=ccache)
|
||||
|
||||
promotion_check_ipa_domain(conn, remote_api.env.basedn)
|
||||
|
||||
# Check that we don't already have a replication agreement
|
||||
try:
|
||||
(acn, adn) = replman.agreement_dn(config.host_name)
|
||||
@ -1444,7 +1477,8 @@ def promote(installer):
|
||||
|
||||
ca = cainstance.CAInstance(config.realm_name, certs.NSS_DIR,
|
||||
host_name=config.host_name,
|
||||
dm_password=config.dirman_password)
|
||||
dm_password=config.dirman_password,
|
||||
api=remote_api)
|
||||
ca.configure_replica(config.ca_host_name,
|
||||
subject_base=config.subject_base,
|
||||
ca_cert_bundle=ca_data)
|
||||
|
@ -24,6 +24,7 @@ from ipapython import ipautil, sysrestore, version, certdb
|
||||
from ipapython import ipaldap
|
||||
from ipapython.ipa_log_manager import *
|
||||
from ipapython import certmonger
|
||||
from ipapython import dnsutil
|
||||
from ipapython.dn import DN
|
||||
from ipaplatform.constants import constants
|
||||
from ipaplatform.paths import paths
|
||||
@ -793,6 +794,50 @@ def named_root_key_include():
|
||||
return True
|
||||
|
||||
|
||||
def named_update_global_forwarder_policy():
|
||||
bind = bindinstance.BindInstance()
|
||||
if not bindinstance.named_conf_exists() or not bind.is_configured():
|
||||
# DNS service may not be configured
|
||||
root_logger.info('DNS is not configured')
|
||||
return False
|
||||
|
||||
root_logger.info('[Checking global forwarding policy in named.conf '
|
||||
'to avoid conflicts with automatic empty zones]')
|
||||
if sysupgrade.get_upgrade_state(
|
||||
'named.conf', 'forward_policy_conflict_with_empty_zones_handled'
|
||||
):
|
||||
# upgrade was done already
|
||||
return False
|
||||
|
||||
sysupgrade.set_upgrade_state(
|
||||
'named.conf',
|
||||
'forward_policy_conflict_with_empty_zones_handled',
|
||||
True
|
||||
)
|
||||
if not dnsutil.has_empty_zone_addresses(api.env.host):
|
||||
# guess: local server does not have IP addresses from private ranges
|
||||
# so hopefully automatic empty zones are not a problem
|
||||
return False
|
||||
|
||||
if bindinstance.named_conf_get_directive(
|
||||
'forward',
|
||||
section=bindinstance.NAMED_SECTION_OPTIONS,
|
||||
str_val=False
|
||||
) == 'only':
|
||||
return False
|
||||
|
||||
root_logger.info('Global forward policy in named.conf will '
|
||||
'be changed to "only" to avoid conflicts with '
|
||||
'automatic empty zones')
|
||||
bindinstance.named_conf_set_directive(
|
||||
'forward',
|
||||
'only',
|
||||
section=bindinstance.NAMED_SECTION_OPTIONS,
|
||||
str_val=False
|
||||
)
|
||||
return True
|
||||
|
||||
|
||||
def certificate_renewal_update(ca, ds, http):
|
||||
"""
|
||||
Update certmonger certificate renewal configuration.
|
||||
@ -1464,6 +1509,7 @@ def upgrade_configuration():
|
||||
sub_dict['SUBJECT_BASE'] = subject_base
|
||||
|
||||
ca = cainstance.CAInstance(api.env.realm, certs.NSS_DIR)
|
||||
ca_running = ca.is_running()
|
||||
|
||||
with installutils.stopped_service('pki-tomcatd', 'pki-tomcat'):
|
||||
# Dogtag must be stopped to be able to backup CS.cfg config
|
||||
@ -1497,6 +1543,12 @@ def upgrade_configuration():
|
||||
)
|
||||
upgrade_pki(ca, fstore)
|
||||
|
||||
# several upgrade steps require running CA. If CA is configured,
|
||||
# always run ca.start() because we need to wait until CA is really ready
|
||||
# by checking status using http
|
||||
if ca.is_configured():
|
||||
ca.start('pki-tomcat')
|
||||
|
||||
certmonger_service = services.knownservices.certmonger
|
||||
if ca.is_configured() and not certmonger_service.is_running():
|
||||
certmonger_service.start()
|
||||
@ -1616,6 +1668,7 @@ def upgrade_configuration():
|
||||
named_bindkey_file_option(),
|
||||
named_managed_keys_dir_option(),
|
||||
named_root_key_include(),
|
||||
named_update_global_forwarder_policy(),
|
||||
mask_named_regular(),
|
||||
fix_dyndb_ldap_workdir_permissions(),
|
||||
)
|
||||
@ -1658,6 +1711,9 @@ def upgrade_configuration():
|
||||
ca_import_included_profiles(ca)
|
||||
add_default_caacl(ca)
|
||||
|
||||
if ca.is_configured():
|
||||
cainstance.repair_profile_caIPAserviceCert()
|
||||
|
||||
set_sssd_domain_option('ipa_server_mode', 'True')
|
||||
|
||||
if ds_running and not ds.is_running():
|
||||
@ -1665,6 +1721,12 @@ def upgrade_configuration():
|
||||
elif not ds_running and ds.is_running():
|
||||
ds.stop(ds_serverid)
|
||||
|
||||
if ca.is_configured():
|
||||
if ca_running and not ca.is_running():
|
||||
ca.start('pki-tomcat')
|
||||
elif not ca_running and ca.is_running():
|
||||
ca.stop('pki-tomcat')
|
||||
|
||||
|
||||
def upgrade_check(options):
|
||||
try:
|
||||
|
@ -171,6 +171,8 @@ def fix_apache_semaphores(master):
|
||||
def unapply_fixes(host):
|
||||
restore_files(host)
|
||||
restore_hostname(host)
|
||||
# Clean ccache to prevent issues like 5741
|
||||
host.run_command(['kdestroy', '-A'], raiseonerr=False)
|
||||
|
||||
# Clean up the test directory
|
||||
host.run_command(['rm', '-rvf', host.config.test_dir])
|
||||
@ -1062,7 +1064,7 @@ def add_a_records_for_hosts_in_master_domain(master):
|
||||
# We don't need to take care of the zone creation since it is master
|
||||
# domain
|
||||
try:
|
||||
verify_host_resolvable(host.hostname, log)
|
||||
verify_host_resolvable(host.hostname)
|
||||
log.debug("The host (%s) is resolvable." % host.domain.name)
|
||||
except errors.DNSNotARecordError:
|
||||
log.debug("Hostname (%s) does not have A/AAAA record. Adding new one.",
|
||||
|
@ -6,6 +6,7 @@ import dns.dnssec
|
||||
import dns.resolver
|
||||
import dns.name
|
||||
import time
|
||||
import pytest
|
||||
|
||||
from ipatests.test_integration.base import IntegrationTest
|
||||
from ipatests.test_integration import tasks
|
||||
@ -71,6 +72,14 @@ def wait_until_record_is_signed(nameserver, record, log, rtype="SOA",
|
||||
return False
|
||||
|
||||
|
||||
def restart_named(*args):
|
||||
# A workaround for ticket N 5348
|
||||
time.sleep(20) # wait till dnssec key is exported to named
|
||||
for host in args:
|
||||
host.run_command(["systemctl", "restart",
|
||||
"named-pkcs11.service"])
|
||||
|
||||
|
||||
class TestInstallDNSSECLast(IntegrationTest):
|
||||
"""Simple DNSSEC test
|
||||
|
||||
@ -105,6 +114,7 @@ class TestInstallDNSSECLast(IntegrationTest):
|
||||
]
|
||||
self.master.run_command(args)
|
||||
|
||||
restart_named(self.master, self.replicas[0])
|
||||
# test master
|
||||
assert wait_until_record_is_signed(
|
||||
self.master.ip, test_zone, self.log, timeout=100
|
||||
@ -125,6 +135,7 @@ class TestInstallDNSSECLast(IntegrationTest):
|
||||
]
|
||||
self.replicas[0].run_command(args)
|
||||
|
||||
restart_named(self.replicas[0])
|
||||
# test replica
|
||||
assert wait_until_record_is_signed(
|
||||
self.replicas[0].ip, test_zone_repl, self.log, timeout=300
|
||||
@ -170,8 +181,7 @@ class TestInstallDNSSECLast(IntegrationTest):
|
||||
]
|
||||
self.master.run_command(args)
|
||||
|
||||
time.sleep(20) # sleep a bit until LDAP changes are applied to DNS
|
||||
|
||||
restart_named(self.master)
|
||||
# test master
|
||||
assert wait_until_record_is_signed(
|
||||
self.master.ip, test_zone, self.log, timeout=100
|
||||
@ -219,7 +229,7 @@ class TestInstallDNSSECLast(IntegrationTest):
|
||||
]
|
||||
self.master.run_command(args)
|
||||
|
||||
time.sleep(20) # sleep a bit until LDAP changes are applied to DNS
|
||||
restart_named(self.master, self.replicas[0])
|
||||
|
||||
# test master
|
||||
assert wait_until_record_is_signed(
|
||||
@ -235,6 +245,78 @@ class TestInstallDNSSECLast(IntegrationTest):
|
||||
self.log, rtype="DNSKEY").rrset
|
||||
assert dnskey_old != dnskey_new, "DNSKEY should be different"
|
||||
|
||||
|
||||
class TestZoneSigningWithoutNamedRestart(IntegrationTest):
|
||||
"""Test whether https://fedorahosted.org/freeipa/ticket/5348 is already
|
||||
fixed. If the issue is not fixed, the test will expectedly fail. When
|
||||
fixed, it will pass, which will cause the whole run to become "red"
|
||||
"""
|
||||
num_replicas = 1
|
||||
topology = 'star'
|
||||
|
||||
@classmethod
|
||||
def install(cls, mh):
|
||||
tasks.install_master(cls.master, setup_dns=False)
|
||||
args = [
|
||||
"ipa-dns-install",
|
||||
"--dnssec-master",
|
||||
"--forwarder", cls.master.config.dns_forwarder,
|
||||
"-U",
|
||||
]
|
||||
cls.master.run_command(args)
|
||||
|
||||
tasks.install_replica(cls.master, cls.replicas[0], setup_dns=True)
|
||||
|
||||
# backup trusted key
|
||||
tasks.backup_file(cls.master, paths.DNSSEC_TRUSTED_KEY)
|
||||
tasks.backup_file(cls.replicas[0], paths.DNSSEC_TRUSTED_KEY)
|
||||
|
||||
@classmethod
|
||||
def uninstall(cls, mh):
|
||||
# restore trusted key
|
||||
tasks.restore_files(cls.master)
|
||||
tasks.restore_files(cls.replicas[0])
|
||||
|
||||
super(TestZoneSigningWithoutNamedRestart, cls).uninstall(mh)
|
||||
|
||||
@pytest.mark.xfail(strict=True)
|
||||
def test_sign_root_zone_no_named_restart(self):
|
||||
args = [
|
||||
"ipa", "dnszone-add", root_zone, "--dnssec", "true",
|
||||
"--skip-overlap-check",
|
||||
]
|
||||
self.master.run_command(args)
|
||||
|
||||
# make BIND happy: add the glue record and delegate zone
|
||||
args = [
|
||||
"ipa", "dnsrecord-add", root_zone, self.master.hostname,
|
||||
"--a-rec=" + self.master.ip
|
||||
]
|
||||
self.master.run_command(args)
|
||||
args = [
|
||||
"ipa", "dnsrecord-add", root_zone, self.replicas[0].hostname,
|
||||
"--a-rec=" + self.replicas[0].ip
|
||||
]
|
||||
self.master.run_command(args)
|
||||
|
||||
time.sleep(10) # sleep a bit until data are provided by bind-dyndb-ldap
|
||||
|
||||
args = [
|
||||
"ipa", "dnsrecord-add", root_zone, self.master.domain.name,
|
||||
"--ns-rec=" + self.master.hostname
|
||||
]
|
||||
self.master.run_command(args)
|
||||
# test master
|
||||
assert wait_until_record_is_signed(
|
||||
self.master.ip, root_zone, self.log, timeout=100
|
||||
), "Zone %s is not signed (master)" % root_zone
|
||||
|
||||
# test replica
|
||||
assert wait_until_record_is_signed(
|
||||
self.replicas[0].ip, root_zone, self.log, timeout=300
|
||||
), "Zone %s is not signed (replica)" % root_zone
|
||||
|
||||
|
||||
class TestInstallDNSSECFirst(IntegrationTest):
|
||||
"""Simple DNSSEC test
|
||||
|
||||
@ -288,7 +370,7 @@ class TestInstallDNSSECFirst(IntegrationTest):
|
||||
"--ns-rec=" + self.master.hostname
|
||||
]
|
||||
self.master.run_command(args)
|
||||
|
||||
restart_named(self.master, self.replicas[0])
|
||||
# test master
|
||||
assert wait_until_record_is_signed(
|
||||
self.master.ip, root_zone, self.log, timeout=100
|
||||
@ -319,7 +401,7 @@ class TestInstallDNSSECFirst(IntegrationTest):
|
||||
"--ns-rec=" + self.master.hostname
|
||||
]
|
||||
self.master.run_command(args)
|
||||
|
||||
restart_named(self.master, self.replicas[0])
|
||||
# wait until zone is signed
|
||||
assert wait_until_record_is_signed(
|
||||
self.master.ip, example_test_zone, self.log, timeout=100
|
||||
@ -457,6 +539,7 @@ class TestMigrateDNSSECMaster(IntegrationTest):
|
||||
|
||||
self.master.run_command(args)
|
||||
|
||||
restart_named(self.master, self.replicas[0])
|
||||
# wait until zone is signed
|
||||
assert wait_until_record_is_signed(
|
||||
self.master.ip, example_test_zone, self.log, timeout=100
|
||||
@ -513,7 +596,7 @@ class TestMigrateDNSSECMaster(IntegrationTest):
|
||||
"--skip-overlap-check",
|
||||
]
|
||||
self.replicas[0].run_command(args)
|
||||
|
||||
restart_named(self.master, self.replicas[0])
|
||||
# wait until zone is signed
|
||||
assert wait_until_record_is_signed(
|
||||
self.replicas[0].ip, example2_test_zone, self.log, timeout=100
|
||||
@ -546,7 +629,7 @@ class TestMigrateDNSSECMaster(IntegrationTest):
|
||||
"--skip-overlap-check",
|
||||
]
|
||||
self.replicas[1].run_command(args)
|
||||
|
||||
restart_named(self.replicas[0], self.replicas[1])
|
||||
# wait until zone is signed
|
||||
assert wait_until_record_is_signed(
|
||||
self.replicas[1].ip, example3_test_zone, self.log, timeout=200
|
||||
|
@ -174,6 +174,18 @@ class TestReplicaPromotionLevel1(ReplicaPromotionBase):
|
||||
" to generate replica file\n"
|
||||
"is supported only in 0-level IPA domain", 1)
|
||||
|
||||
@replicas_cleanup
|
||||
def test_one_command_installation(self):
|
||||
"""
|
||||
TestCase:
|
||||
http://www.freeipa.org/page/V4/Replica_Promotion/Test_plan
|
||||
#Test_case:_Replica_can_be_installed_using_one_command
|
||||
"""
|
||||
self.replicas[0].run_command(['ipa-replica-install', '-w',
|
||||
self.master.config.admin_password,
|
||||
'-n', self.master.domain.name,
|
||||
'-r', self.master.domain.realm])
|
||||
|
||||
|
||||
class TestReplicaManageCommands(IntegrationTest):
|
||||
topology = "star"
|
||||
@ -211,7 +223,7 @@ class TestReplicaManageCommands(IntegrationTest):
|
||||
' deprecated with managed IPA replication'
|
||||
' topology. Please use `ipa topologysegment-*`'
|
||||
' commands to manage the topology', 1)
|
||||
tasks.create_segment(master, replica1, replica2)
|
||||
segment = tasks.create_segment(master, replica1, replica2)
|
||||
result4 = master.run_command(["ipa-replica-manage",
|
||||
"disconnect",
|
||||
replica1.hostname,
|
||||
@ -221,3 +233,199 @@ class TestReplicaManageCommands(IntegrationTest):
|
||||
' deprecated with managed IPA replication'
|
||||
' topology. Please use `ipa topologysegment-*`'
|
||||
' commands to manage the topology', 1)
|
||||
|
||||
# http://www.freeipa.org/page/V4/Replica_Promotion/Test_plan
|
||||
#Test_case:_ipa-csreplica-manage_connect_is_deprecated
|
||||
#_in_domain_level_1
|
||||
|
||||
result5 = master.run_command(['ipa-csreplica-manage', 'del',
|
||||
replica1.hostname,
|
||||
'-p', master.config.dirman_password],
|
||||
raiseonerr=False)
|
||||
assert_error(result5, "Removal of IPA CS replication agreement"
|
||||
" and replication data is deprecated with"
|
||||
" managed IPA replication topology", 1)
|
||||
|
||||
tasks.destroy_segment(master, segment[0]['name'])
|
||||
result6 = master.run_command(["ipa-csreplica-manage",
|
||||
"connect",
|
||||
replica1.hostname,
|
||||
replica2.hostname,
|
||||
'-p', master.config.dirman_password],
|
||||
raiseonerr=False)
|
||||
assert_error(result6, "Creation of IPA CS replication agreement is"
|
||||
" deprecated with managed IPA replication"
|
||||
" topology", 1)
|
||||
tasks.create_segment(master, replica1, replica2)
|
||||
result7 = master.run_command(["ipa-csreplica-manage",
|
||||
"disconnect",
|
||||
replica1.hostname,
|
||||
replica2.hostname,
|
||||
'-p', master.config.dirman_password],
|
||||
raiseonerr=False)
|
||||
assert_error(result7, "Removal of IPA CS replication agreement is"
|
||||
" deprecated with managed IPA"
|
||||
" replication topology", 1)
|
||||
|
||||
|
||||
class TestUnprivilegedUserPermissions(IntegrationTest):
|
||||
"""
|
||||
TestCase:
|
||||
http://www.freeipa.org/page/V4/Replica_Promotion/Test_plan
|
||||
#Test_case:_Unprivileged_users_are_not_allowed_to_enroll
|
||||
_and_promote_clients
|
||||
"""
|
||||
num_replicas = 1
|
||||
domain_level = DOMAIN_LEVEL_1
|
||||
|
||||
@classmethod
|
||||
def install(cls, mh):
|
||||
cls.username = 'testuser'
|
||||
tasks.install_master(cls.master, domain_level=cls.domain_level)
|
||||
password = cls.master.config.dirman_password
|
||||
cls.new_password = '$ome0therPaaS'
|
||||
adduser_stdin_text = "%s\n%s\n" % (cls.master.config.admin_password,
|
||||
cls.master.config.admin_password)
|
||||
user_kinit_stdin_text = "%s\n%s\n%s\n" % (password, cls.new_password,
|
||||
cls.new_password)
|
||||
tasks.kinit_admin(cls.master)
|
||||
cls.master.run_command(['ipa', 'user-add', cls.username, '--password',
|
||||
'--first', 'John', '--last', 'Donn'],
|
||||
stdin_text=adduser_stdin_text)
|
||||
# Now we need to change the password for the user
|
||||
cls.master.run_command(['kinit', cls.username],
|
||||
stdin_text=user_kinit_stdin_text)
|
||||
# And again kinit admin
|
||||
tasks.kinit_admin(cls.master)
|
||||
|
||||
def test_client_enrollment_by_unprivileged_user(self):
|
||||
replica = self.replicas[0]
|
||||
result1 = replica.run_command(['ipa-client-install',
|
||||
'-p', self.username,
|
||||
'-w', self.new_password,
|
||||
'--domain', replica.domain.name,
|
||||
'--realm', replica.domain.realm, '-U'],
|
||||
raiseonerr=False)
|
||||
assert_error(result1, "No permission to join this host", 1)
|
||||
|
||||
def test_replica_promotion_by_unprivileged_user(self):
|
||||
replica = self.replicas[0]
|
||||
tasks.install_client(self.master, replica)
|
||||
result2 = replica.run_command(['ipa-replica-install',
|
||||
'-P', self.username,
|
||||
'-p', self.new_password,
|
||||
'-n', self.master.domain.name,
|
||||
'-r', self.master.domain.realm],
|
||||
raiseonerr=False)
|
||||
assert_error(result2,
|
||||
"Insufficient privileges to promote the server", 1)
|
||||
|
||||
def test_replica_promotion_after_adding_to_admin_group(self):
|
||||
self.master.run_command(['ipa', 'group-add-member', 'admins',
|
||||
'--users=%s' % self.username])
|
||||
|
||||
self.replicas[0].run_command(['ipa-replica-install',
|
||||
'-P', self.username,
|
||||
'-p', self.new_password,
|
||||
'-n', self.master.domain.name,
|
||||
'-r', self.master.domain.realm])
|
||||
|
||||
|
||||
class TestProhibitReplicaUninstallation(IntegrationTest):
|
||||
topology = 'line'
|
||||
num_replicas = 2
|
||||
domain_level = DOMAIN_LEVEL_1
|
||||
|
||||
def test_replica_uninstallation_prohibited(self):
|
||||
"""
|
||||
http://www.freeipa.org/page/V4/Replica_Promotion/Test_plan
|
||||
#Test_case:_Prohibit_ipa_server_uninstallation_from_disconnecting
|
||||
_topology_segment
|
||||
"""
|
||||
result = self.replicas[0].run_command(['ipa-server-install',
|
||||
'--uninstall', '-U'],
|
||||
raiseonerr=False)
|
||||
assert(result.returncode == 0), ("The replica was removed without "
|
||||
"'--ignore-topology-disconnect' option")
|
||||
assert("Uninstallation leads to disconnected topology"
|
||||
in result.stdout_text), ("Expected error message was not found")
|
||||
self.replicas[0].run_command(['ipa-server-install', '--uninstall',
|
||||
'-U', '--ignore-topology-disconnect'])
|
||||
|
||||
|
||||
class TestOldReplicaWorksAfterDomainUpgrade(IntegrationTest):
|
||||
topology = 'star'
|
||||
num_replicas = 1
|
||||
domain_level = DOMAIN_LEVEL_0
|
||||
username = 'testuser'
|
||||
|
||||
def test_replica_after_domain_upgrade(self):
|
||||
tasks.kinit_admin(self.master)
|
||||
tasks.kinit_admin(self.replicas[0])
|
||||
self.master.run_command(['ipa', 'user-add', self.username,
|
||||
'--first', 'test',
|
||||
'--last', 'user'])
|
||||
tasks.wait_for_replication(self.replicas[0].ldap_connect())
|
||||
self.master.run_command(['ipa', 'domainlevel-set',
|
||||
str(DOMAIN_LEVEL_1)])
|
||||
result = self.replicas[0].run_command(['ipa', 'user-show',
|
||||
self.username])
|
||||
assert("User login: %s" % self.username in result.stdout_text), (
|
||||
"A testuser was not found on replica after domain upgrade")
|
||||
self.replicas[0].run_command(['ipa', 'user-del', self.username])
|
||||
tasks.wait_for_replication(self.master.ldap_connect())
|
||||
result1 = self.master.run_command(['ipa', 'user-show', self.username],
|
||||
raiseonerr=False)
|
||||
assert_error(result1, "%s: user not found" % self.username, 2)
|
||||
|
||||
|
||||
class TestWrongClientDomain(IntegrationTest):
|
||||
topology = "star"
|
||||
num_clients = 1
|
||||
domain_name = 'exxample.test'
|
||||
|
||||
@classmethod
|
||||
def install(cls, mh):
|
||||
tasks.install_master(cls.master, domain_level=cls.domain_level)
|
||||
|
||||
def teardown_method(self, method):
|
||||
self.clients[0].run_command(['ipa-client-install',
|
||||
'--uninstall', '-U'],
|
||||
raiseonerr=False)
|
||||
tasks.kinit_admin(self.master)
|
||||
self.master.run_command(['ipa', 'host-del',
|
||||
self.clients[0].hostname],
|
||||
raiseonerr=False)
|
||||
|
||||
def test_wrong_client_domain(self):
|
||||
client = self.clients[0]
|
||||
client.run_command(['ipa-client-install', '-U',
|
||||
'--domain', self.domain_name,
|
||||
'--realm', self.master.domain.realm,
|
||||
'-p', 'admin',
|
||||
'-w', self.master.config.admin_password,
|
||||
'--server', self.master.hostname,
|
||||
'--force-join'])
|
||||
result = client.run_command(['ipa-replica-install', '-U', '-w',
|
||||
self.master.config.dirman_password],
|
||||
raiseonerr=False)
|
||||
assert_error(result,
|
||||
"Cannot promote this client to a replica. Local domain "
|
||||
"'%s' does not match IPA domain "
|
||||
"'%s'" % (self.domain_name, self.master.domain.name))
|
||||
|
||||
def test_upcase_client_domain(self):
|
||||
client = self.clients[0]
|
||||
result = client.run_command(['ipa-client-install', '-U', '--domain',
|
||||
self.master.domain.name.upper(), '-w',
|
||||
self.master.config.admin_password,
|
||||
'-p', 'admin',
|
||||
'--server', self.master.hostname,
|
||||
'--force-join'], raiseonerr=False)
|
||||
assert(result.returncode == 0), (
|
||||
'Failed to setup client with the upcase domain name')
|
||||
result1 = client.run_command(['ipa-replica-install', '-U', '-w',
|
||||
self.master.config.dirman_password],
|
||||
raiseonerr=False)
|
||||
assert(result1.returncode == 0), (
|
||||
'Failed to promote the client installed with the upcase domain name')
|
||||
|
@ -3,7 +3,6 @@
|
||||
#
|
||||
|
||||
import re
|
||||
import time
|
||||
|
||||
import pytest
|
||||
|
||||
@ -11,6 +10,7 @@ from ipatests.test_integration.base import IntegrationTest
|
||||
from ipatests.test_integration import tasks
|
||||
from ipatests.test_integration.env_config import get_global_config
|
||||
from ipalib.constants import DOMAIN_SUFFIX_NAME
|
||||
from ipatests.util import assert_deepequal
|
||||
|
||||
config = get_global_config()
|
||||
reasoning = "Topology plugin disabled due to domain level 0"
|
||||
@ -61,15 +61,16 @@ class TestTopologyOptions(IntegrationTest):
|
||||
"""
|
||||
tasks.kinit_admin(self.master)
|
||||
result1 = self.master.run_command(['ipa', 'topologysegment-find',
|
||||
DOMAIN_SUFFIX_NAME])
|
||||
DOMAIN_SUFFIX_NAME]).stdout_text
|
||||
first_segment_name = "%s-to-%s" % (self.master.hostname,
|
||||
self.replicas[0].hostname)
|
||||
output1 = result1.stdout_text
|
||||
firstsegment = self.tokenize_topologies(output1)[0]
|
||||
assert(firstsegment['name'] == first_segment_name)
|
||||
assert(self.noentries_re.search(output1).group(1) == "1")
|
||||
assert(firstsegment['leftnode'] == self.master.hostname)
|
||||
assert(firstsegment['rightnode'] == self.replicas[0].hostname)
|
||||
expected_segment = {
|
||||
'connectivity': 'both',
|
||||
'leftnode': self.master.hostname,
|
||||
'name': first_segment_name,
|
||||
'rightnode': self.replicas[0].hostname}
|
||||
firstsegment = self.tokenize_topologies(result1)[0]
|
||||
assert_deepequal(expected_segment, firstsegment)
|
||||
tasks.install_replica(self.master, self.replicas[1], setup_ca=False,
|
||||
setup_dns=False)
|
||||
# We need to make sure topology information is consistent across all
|
||||
@ -81,16 +82,17 @@ class TestTopologyOptions(IntegrationTest):
|
||||
result4 = self.replicas[1].run_command(['ipa', 'topologysegment-find',
|
||||
DOMAIN_SUFFIX_NAME])
|
||||
segments = self.tokenize_topologies(result2.stdout_text)
|
||||
assert(len(segments) == 2)
|
||||
assert(result2.stdout_text == result3.stdout_text)
|
||||
assert(result3.stdout_text == result4.stdout_text)
|
||||
assert(len(segments) == 2), "Unexpected number of segments found"
|
||||
assert_deepequal(result2.stdout_text, result3.stdout_text)
|
||||
assert_deepequal(result3.stdout_text, result4.stdout_text)
|
||||
# Now let's check that uninstalling the replica will update the topology
|
||||
# info on the rest of replicas.
|
||||
tasks.uninstall_master(self.replicas[1])
|
||||
tasks.clean_replication_agreement(self.master, self.replicas[1])
|
||||
result5 = self.master.run_command(['ipa', 'topologysegment-find',
|
||||
DOMAIN_SUFFIX_NAME])
|
||||
assert(self.noentries_re.search(result5.stdout_text).group(1) == "1")
|
||||
num_entries = self.noentries_re.search(result5.stdout_text).group(1)
|
||||
assert(num_entries == "1"), "Incorrect number of entries displayed"
|
||||
|
||||
def test_add_remove_segment(self):
|
||||
"""
|
||||
@ -110,27 +112,31 @@ class TestTopologyOptions(IntegrationTest):
|
||||
assert err == "", err
|
||||
# Make sure the new segment is shown by `ipa topologysegment-find`
|
||||
result1 = self.master.run_command(['ipa', 'topologysegment-find',
|
||||
DOMAIN_SUFFIX_NAME])
|
||||
assert(result1.stdout_text.find(segment['name']) > 0)
|
||||
DOMAIN_SUFFIX_NAME]).stdout_text
|
||||
assert(segment['name'] in result1), (
|
||||
"%s: segment not found" % segment['name'])
|
||||
# Remove master <-> replica2 segment and make sure that the changes get
|
||||
# there through replica1
|
||||
deleteme = "%s-to-%s" % (self.master.hostname,
|
||||
self.replicas[1].hostname)
|
||||
returncode, error = tasks.destroy_segment(self.master, deleteme)
|
||||
assert returncode == 0, error
|
||||
# make sure replica1 does not have segment that was deleted on master
|
||||
# Wait till replication ends and make sure replica1 does not have
|
||||
# segment that was deleted on master
|
||||
replica1_ldap = self.replicas[0].ldap_connect()
|
||||
tasks.wait_for_replication(replica1_ldap)
|
||||
result3 = self.replicas[0].run_command(['ipa', 'topologysegment-find',
|
||||
DOMAIN_SUFFIX_NAME])
|
||||
assert(result3.stdout_text.find(deleteme) < 0)
|
||||
DOMAIN_SUFFIX_NAME]).stdout_text
|
||||
assert(deleteme not in result3), "%s: segment still exists" % deleteme
|
||||
# Create test data on master and make sure it gets all the way down to
|
||||
# replica2 through replica1
|
||||
self.master.run_command(['ipa', 'user-add', 'someuser',
|
||||
'--first', 'test',
|
||||
'--last', 'user'])
|
||||
time.sleep(60) # replication requires some time
|
||||
users_on_replica2 = self.replicas[1].run_command(['ipa',
|
||||
'user-find'])
|
||||
assert(users_on_replica2.find('someuser') > 0)
|
||||
dest_ldap = self.replicas[1].ldap_connect()
|
||||
tasks.wait_for_replication(dest_ldap)
|
||||
result4 = self.replicas[1].run_command(['ipa', 'user-find'])
|
||||
assert('someuser' in result4.stdout_text), 'User not found: someuser'
|
||||
# We end up having a line topology: master <-> replica1 <-> replica2
|
||||
|
||||
def test_remove_the_only_connection(self):
|
||||
|
@ -28,6 +28,7 @@ import pytest
|
||||
pytestmark = pytest.mark.tier0
|
||||
|
||||
TEST_KEY = 'ipa_test'
|
||||
TEST_UNICODEKEY = u'ipa_unicode'
|
||||
TEST_VALUE = b'abc123'
|
||||
UPDATE_VALUE = b'123abc'
|
||||
|
||||
@ -49,6 +50,10 @@ class test_keyring(object):
|
||||
kernel_keyring.del_key(SIZE_256)
|
||||
except ValueError:
|
||||
pass
|
||||
try:
|
||||
kernel_keyring.del_key(TEST_UNICODEKEY)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
def test_01(self):
|
||||
"""
|
||||
@ -150,3 +155,13 @@ class test_keyring(object):
|
||||
assert(result == SIZE_1024.encode('ascii'))
|
||||
|
||||
kernel_keyring.del_key(TEST_KEY)
|
||||
|
||||
def test_10(self):
|
||||
"""
|
||||
Test a unicode key
|
||||
"""
|
||||
kernel_keyring.add_key(TEST_UNICODEKEY, TEST_VALUE)
|
||||
result = kernel_keyring.read_key(TEST_UNICODEKEY)
|
||||
assert(result == TEST_VALUE)
|
||||
|
||||
kernel_keyring.del_key(TEST_UNICODEKEY)
|
||||
|
@ -184,9 +184,15 @@ class test_LDAPEntry(object):
|
||||
assert u'cn' in e
|
||||
assert u'cn' in e.keys()
|
||||
assert 'CN' in e
|
||||
assert 'CN' not in e.keys()
|
||||
if six.PY2:
|
||||
assert 'CN' not in e.keys()
|
||||
else:
|
||||
assert 'CN' in e.keys()
|
||||
assert 'commonName' in e
|
||||
assert 'commonName' not in e.keys()
|
||||
if six.PY2:
|
||||
assert 'commonName' not in e.keys()
|
||||
else:
|
||||
assert 'commonName' in e.keys()
|
||||
assert e['CN'] is self.cn1
|
||||
assert e['CN'] is e[u'cn']
|
||||
|
||||
@ -199,9 +205,15 @@ class test_LDAPEntry(object):
|
||||
assert u'cn' in e
|
||||
assert u'cn' in e.keys()
|
||||
assert 'CN' in e
|
||||
assert 'CN' not in e.keys()
|
||||
if six.PY2:
|
||||
assert 'CN' not in e.keys()
|
||||
else:
|
||||
assert 'CN' in e.keys()
|
||||
assert 'commonName' in e
|
||||
assert 'commonName' not in e.keys()
|
||||
if six.PY2:
|
||||
assert 'commonName' not in e.keys()
|
||||
else:
|
||||
assert 'commonName' in e.keys()
|
||||
assert e['CN'] is self.cn2
|
||||
assert e['CN'] is e[u'cn']
|
||||
|
||||
@ -281,33 +293,33 @@ class test_LDAPEntry(object):
|
||||
assert e['test'] is nice
|
||||
|
||||
raw = e.raw['test']
|
||||
assert raw == ['1', '2', '3']
|
||||
assert raw == [b'1', b'2', b'3']
|
||||
|
||||
nice.remove(1)
|
||||
assert e.raw['test'] is raw
|
||||
assert raw == ['2', '3']
|
||||
assert raw == [b'2', b'3']
|
||||
|
||||
raw.append('4')
|
||||
raw.append(b'4')
|
||||
assert e['test'] is nice
|
||||
assert nice == [2, 3, u'4']
|
||||
|
||||
nice.remove(2)
|
||||
raw.append('5')
|
||||
raw.append(b'5')
|
||||
assert nice == [3, u'4']
|
||||
assert raw == ['2', '3', '4', '5']
|
||||
assert raw == [b'2', b'3', b'4', b'5']
|
||||
assert e['test'] is nice
|
||||
assert e.raw['test'] is raw
|
||||
assert nice == [3, u'4', u'5']
|
||||
assert raw == ['3', '4', '5']
|
||||
assert raw == [b'3', b'4', b'5']
|
||||
|
||||
nice.insert(0, 2)
|
||||
raw.remove('4')
|
||||
raw.remove(b'4')
|
||||
assert nice == [2, 3, u'4', u'5']
|
||||
assert raw == ['3', '5']
|
||||
assert raw == [b'3', b'5']
|
||||
assert e.raw['test'] is raw
|
||||
assert e['test'] is nice
|
||||
assert nice == [2, 3, u'5']
|
||||
assert raw == ['3', '5', '2']
|
||||
assert raw == [b'3', b'5', b'2']
|
||||
|
||||
raw = [b'a', b'b']
|
||||
e.raw['test'] = raw
|
||||
@ -319,5 +331,5 @@ class test_LDAPEntry(object):
|
||||
assert e['test'] is nice
|
||||
assert e.raw['test'] == [b'not list']
|
||||
|
||||
e.raw['test'].append('second')
|
||||
e.raw['test'].append(b'second')
|
||||
assert e['test'] == ['not list', u'second']
|
||||
|
@ -214,7 +214,10 @@ class test_jsonserver(PluginTester):
|
||||
# Test with invalid JSON-data:
|
||||
e = raises(errors.JSONError, o.unmarshal, 'this wont work')
|
||||
assert isinstance(e.error, ValueError)
|
||||
assert unicode(e.error) == 'No JSON object could be decoded'
|
||||
if six.PY2:
|
||||
assert unicode(e.error) == 'No JSON object could be decoded'
|
||||
else:
|
||||
assert str(e.error).startswith('Expecting value: ')
|
||||
|
||||
# Test with non-dict type:
|
||||
e = raises(errors.JSONError, o.unmarshal, json.dumps([1, 2, 3]))
|
||||
|
@ -72,4 +72,4 @@ class TestTopologyPlugin(object):
|
||||
entry = self.conn.get_entry(topoplugindn)
|
||||
assert(set(entry.keys()) == set(pluginattrs.keys()))
|
||||
for i in checkvalues:
|
||||
assert(pluginattrs[i] == entry[i])
|
||||
assert(set(pluginattrs[i]) == set(entry[i]))
|
||||
|
@ -68,3 +68,13 @@ DATA5 = {
|
||||
('textarea', 'description', 'test-group5 desc'),
|
||||
]
|
||||
}
|
||||
|
||||
PKEY6 = 'itest-group6'
|
||||
DATA6 = {
|
||||
'pkey': PKEY6,
|
||||
'add': [
|
||||
('textbox', 'cn', PKEY6),
|
||||
('textarea', 'description', 'test-group6 desc'),
|
||||
('textbox', 'gidnumber', '77777'),
|
||||
]
|
||||
}
|
||||
|
@ -17,7 +17,6 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
ENTITY = 'user'
|
||||
|
||||
PKEY = 'itest-user'
|
||||
@ -63,3 +62,26 @@ DATA2 = {
|
||||
('textbox', 'sn', 'OtherSurname2'),
|
||||
],
|
||||
}
|
||||
|
||||
PKEY3 = 'itest-user3'
|
||||
DATA3 = {
|
||||
'pkey': PKEY3,
|
||||
'add': [
|
||||
('textbox', 'uid', PKEY3),
|
||||
('textbox', 'givenname', 'Name3'),
|
||||
('textbox', 'sn', 'Surname3'),
|
||||
('checkbox', 'noprivate', None),
|
||||
]
|
||||
}
|
||||
|
||||
PKEY4 = 'itest-user4'
|
||||
DATA4 = {
|
||||
'pkey': PKEY4,
|
||||
'add': [
|
||||
('textbox', 'uid', PKEY4),
|
||||
('textbox', 'givenname', 'Name4'),
|
||||
('textbox', 'sn', 'Surname4'),
|
||||
('checkbox', 'noprivate', None),
|
||||
('combobox', 'gidnumber', '77777'),
|
||||
]
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user