Imported Upstream version 4.6.2

This commit is contained in:
Mario Fetka
2021-07-25 07:32:41 +02:00
commit 8ff3be4216
1788 changed files with 1900965 additions and 0 deletions

View File

@@ -0,0 +1,23 @@
# Authors:
# Petr Viktorin <pviktori@redhat.com>
#
# Copyright (C) 2013 Red Hat
# see file 'COPYING' for use and warranty information
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import ipatests.util
ipatests.util.check_ipaclient_unittests()
ipatests.util.check_no_ipaapi()

View File

@@ -0,0 +1,83 @@
# Authors:
# Petr Viktorin <pviktori@redhat.com>
#
# Copyright (C) 2013 Red Hat
# see file 'COPYING' for use and warranty information
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""Base class for FreeIPA integration tests"""
import pytest
from ipatests.pytest_plugins.integration import tasks
from pytest_sourceorder import ordered
@ordered
@pytest.mark.usefixtures('mh')
@pytest.mark.usefixtures('integration_logs')
class IntegrationTest(object):
num_replicas = 0
num_clients = 0
num_ad_domains = 0
required_extra_roles = []
topology = None
domain_level = None
@classmethod
def setup_class(cls):
pass
@classmethod
def host_by_role(cls, role):
for domain in cls.get_domains():
try:
return domain.host_by_role(role)
except LookupError:
pass
raise LookupError(role)
@classmethod
def get_all_hosts(cls):
return ([cls.master] + cls.replicas + cls.clients +
[cls.host_by_role(r) for r in cls.required_extra_roles])
@classmethod
def get_domains(cls):
return [cls.domain] + cls.ad_domains
@classmethod
def install(cls, mh):
if cls.domain_level is not None:
domain_level = cls.domain_level
else:
domain_level = cls.master.config.domain_level
if cls.topology is None:
return
else:
tasks.install_topo(cls.topology,
cls.master, cls.replicas,
cls.clients, domain_level)
@classmethod
def teardown_class(cls):
pass
@classmethod
def uninstall(cls, mh):
tasks.uninstall_master(cls.master)
for replica in cls.replicas:
tasks.uninstall_master(replica)
for client in cls.clients:
tasks.uninstall_client(client)

View File

@@ -0,0 +1,558 @@
# Copyright (c) 2015-2017, Jan Cholasta <jcholast@redhat.com>
#
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
import collections
import datetime
import itertools
import os
import os.path
import six
from cryptography import x509
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.x509.oid import NameOID
from pyasn1.type import univ, char, namedtype, tag
from pyasn1.codec.der import encoder as der_encoder
from pyasn1.codec.native import decoder as native_decoder
if six.PY3:
unicode = str
DAY = datetime.timedelta(days=1)
YEAR = 365 * DAY
# we get the variables from ca_less test
domain = None
realm = None
server1 = None
server2 = None
client = None
password = None
cert_dir = None
CertInfo = collections.namedtuple('CertInfo', 'nick key cert counter')
class PrincipalName(univ.Sequence):
'''See RFC 4120 for details'''
componentType = namedtype.NamedTypes(
namedtype.NamedType(
'name-type',
univ.Integer().subtype(
explicitTag=tag.Tag(
tag.tagClassContext,
tag.tagFormatSimple,
0,
),
),
),
namedtype.NamedType(
'name-string',
univ.SequenceOf(char.GeneralString()).subtype(
explicitTag=tag.Tag(
tag.tagClassContext,
tag.tagFormatSimple,
1,
),
),
),
)
class KRB5PrincipalName(univ.Sequence):
'''See RFC 4556 for details'''
componentType = namedtype.NamedTypes(
namedtype.NamedType(
'realm',
char.GeneralString().subtype(
explicitTag=tag.Tag(
tag.tagClassContext,
tag.tagFormatSimple,
0,
),
),
),
namedtype.NamedType(
'principalName',
PrincipalName().subtype(
explicitTag=tag.Tag(
tag.tagClassContext,
tag.tagFormatSimple,
1,
),
),
),
)
def profile_ca(builder, ca_nick, ca):
now = datetime.datetime.utcnow()
builder = builder.not_valid_before(now)
builder = builder.not_valid_after(now + 10 * YEAR)
crl_uri = u'file://{}.crl'.format(os.path.join(cert_dir, ca_nick))
builder = builder.add_extension(
x509.KeyUsage(
digital_signature=True,
content_commitment=True,
key_encipherment=False,
data_encipherment=False,
key_agreement=False,
key_cert_sign=True,
crl_sign=True,
encipher_only=False,
decipher_only=False,
),
critical=True,
)
builder = builder.add_extension(
x509.BasicConstraints(ca=True, path_length=None),
critical=True,
)
builder = builder.add_extension(
x509.CRLDistributionPoints([
x509.DistributionPoint(
full_name=[x509.UniformResourceIdentifier(crl_uri)],
relative_name=None,
crl_issuer=None,
reasons=None,
),
]),
critical=False,
)
public_key = builder._public_key
builder = builder.add_extension(
x509.SubjectKeyIdentifier.from_public_key(public_key),
critical=False,
)
# here we get "ca" only for "ca1/subca" CA
if not ca:
builder = builder.add_extension(
x509.AuthorityKeyIdentifier.from_issuer_public_key(public_key),
critical=False,
)
else:
ski = ca.cert.extensions.get_extension_for_class(
x509.SubjectKeyIdentifier)
builder = builder.add_extension(
x509.AuthorityKeyIdentifier
.from_issuer_subject_key_identifier(ski),
critical=False,
)
return builder
def profile_server(builder, ca_nick, ca,
warp=datetime.timedelta(days=0), dns_name=None,
badusage=False, wildcard=False):
now = datetime.datetime.utcnow() + warp
builder = builder.not_valid_before(now)
builder = builder.not_valid_after(now + YEAR)
crl_uri = u'file://{}.crl'.format(os.path.join(cert_dir, ca_nick))
builder = builder.add_extension(
x509.CRLDistributionPoints([
x509.DistributionPoint(
full_name=[x509.UniformResourceIdentifier(crl_uri)],
relative_name=None,
crl_issuer=None,
reasons=None,
),
]),
critical=False,
)
if dns_name is not None:
builder = builder.add_extension(
x509.SubjectAlternativeName([x509.DNSName(dns_name)]),
critical=False,
)
if badusage:
builder = builder.add_extension(
x509.KeyUsage(
digital_signature=False,
content_commitment=False,
key_encipherment=False,
data_encipherment=True,
key_agreement=True,
key_cert_sign=False,
crl_sign=False,
encipher_only=False,
decipher_only=False
),
critical=False
)
if wildcard:
names = [x509.DNSName(u'*.' + domain)]
server_split = server1.split('.', 1)
if len(server_split) == 2 and domain != server_split[1]:
names.append(x509.DNSName(u'*.' + server_split[1]))
builder = builder.add_extension(
x509.SubjectAlternativeName(names),
critical=False,
)
return builder
def profile_kdc(builder, ca_nick, ca,
warp=datetime.timedelta(days=0), dns_name=None,
badusage=False):
now = datetime.datetime.utcnow() + warp
builder = builder.not_valid_before(now)
builder = builder.not_valid_after(now + YEAR)
crl_uri = u'file://{}.crl'.format(os.path.join(cert_dir, ca_nick))
builder = builder.add_extension(
x509.ExtendedKeyUsage([x509.ObjectIdentifier('1.3.6.1.5.2.3.5')]),
critical=False,
)
name = {
'realm': realm,
'principalName': {
'name-type': 2,
'name-string': ['krbtgt', realm],
},
}
name = native_decoder.decode(name, asn1Spec=KRB5PrincipalName())
name = der_encoder.encode(name)
names = [x509.OtherName(x509.ObjectIdentifier('1.3.6.1.5.2.2'), name)]
if dns_name is not None:
names += [x509.DNSName(dns_name)]
builder = builder.add_extension(
x509.SubjectAlternativeName(names),
critical=False,
)
builder = builder.add_extension(
x509.CRLDistributionPoints([
x509.DistributionPoint(
full_name=[x509.UniformResourceIdentifier(crl_uri)],
relative_name=None,
crl_issuer=None,
reasons=None,
),
]),
critical=False,
)
if badusage:
builder = builder.add_extension(
x509.KeyUsage(
digital_signature=False,
content_commitment=False,
key_encipherment=False,
data_encipherment=True,
key_agreement=True,
key_cert_sign=False,
crl_sign=False,
encipher_only=False,
decipher_only=False
),
critical=False
)
return builder
def gen_cert(profile, nick_base, subject, ca=None, **kwargs):
key = rsa.generate_private_key(
public_exponent=65537,
key_size=2048,
backend=default_backend(),
)
public_key = key.public_key()
counter = itertools.count(1)
if ca is not None:
ca_nick, ca_key, ca_cert, ca_counter = ca
nick = os.path.join(ca_nick, nick_base)
issuer = ca_cert.subject
else:
nick = ca_nick = nick_base
ca_key = key
ca_counter = counter
issuer = subject
serial = next(ca_counter)
builder = x509.CertificateBuilder()
builder = builder.serial_number(serial)
builder = builder.issuer_name(issuer)
builder = builder.subject_name(subject)
builder = builder.public_key(public_key)
builder = profile(builder, ca_nick, ca, **kwargs)
cert = builder.sign(
private_key=ca_key,
algorithm=hashes.SHA256(),
backend=default_backend(),
)
key_pem = key.private_bytes(
serialization.Encoding.PEM,
serialization.PrivateFormat.PKCS8,
serialization.BestAvailableEncryption(password.encode()),
)
cert_pem = cert.public_bytes(serialization.Encoding.PEM)
try:
os.makedirs(os.path.dirname(os.path.join(cert_dir, nick)))
except OSError:
pass
with open(os.path.join(cert_dir, nick + '.key'), 'wb') as f:
f.write(key_pem)
with open(os.path.join(cert_dir, nick + '.crt'), 'wb') as f:
f.write(cert_pem)
return CertInfo(nick, key, cert, counter)
def revoke_cert(ca, serial):
now = datetime.datetime.utcnow()
crl_builder = x509.CertificateRevocationListBuilder()
crl_builder = crl_builder.issuer_name(ca.cert.subject)
crl_builder = crl_builder.last_update(now)
crl_builder = crl_builder.next_update(now + DAY)
crl_filename = os.path.join(cert_dir, ca.nick + '.crl')
try:
f = open(crl_filename, 'rb')
except IOError:
pass
else:
with f:
crl_pem = f.read()
crl = x509.load_pem_x509_crl(crl_pem, default_backend())
for revoked_cert in crl:
crl_builder = crl_builder.add_revoked_certificate(revoked_cert)
builder = x509.RevokedCertificateBuilder()
builder = builder.serial_number(serial)
builder = builder.revocation_date(now)
revoked_cert = builder.build(default_backend())
crl_builder = crl_builder.add_revoked_certificate(revoked_cert)
crl = crl_builder.sign(
private_key=ca.key,
algorithm=hashes.SHA256(),
backend=default_backend(),
)
crl_pem = crl.public_bytes(serialization.Encoding.PEM)
with open(crl_filename, 'wb') as f:
f.write(crl_pem)
def gen_server_certs(nick_base, hostname, org, ca=None):
gen_cert(profile_server, nick_base,
x509.Name([
x509.NameAttribute(NameOID.ORGANIZATION_NAME, org),
x509.NameAttribute(NameOID.COMMON_NAME, hostname)
]),
ca
)
gen_cert(profile_server, nick_base + u'-badname',
x509.Name([
x509.NameAttribute(NameOID.ORGANIZATION_NAME, org),
x509.NameAttribute(NameOID.COMMON_NAME, u'not-' + hostname)
]),
ca
)
gen_cert(profile_server, nick_base + u'-altname',
x509.Name([
x509.NameAttribute(NameOID.ORGANIZATION_NAME, org),
x509.NameAttribute(NameOID.COMMON_NAME, u'alt-' + hostname)
]),
ca, dns_name=hostname
)
gen_cert(profile_server, nick_base + u'-expired',
x509.Name([
x509.NameAttribute(NameOID.ORGANIZATION_NAME, org),
x509.NameAttribute(NameOID.ORGANIZATIONAL_UNIT_NAME,
u'Expired'),
x509.NameAttribute(NameOID.COMMON_NAME, hostname)
]),
ca, warp=-2 * YEAR
)
gen_cert(profile_server, nick_base + u'-badusage',
x509.Name([
x509.NameAttribute(NameOID.ORGANIZATION_NAME, org),
x509.NameAttribute(NameOID.ORGANIZATIONAL_UNIT_NAME,
u'Bad Usage'),
x509.NameAttribute(NameOID.COMMON_NAME, hostname)
]),
ca, badusage=True
)
revoked = gen_cert(profile_server, nick_base + u'-revoked',
x509.Name([
x509.NameAttribute(NameOID.ORGANIZATION_NAME, org),
x509.NameAttribute(NameOID.ORGANIZATIONAL_UNIT_NAME,
u'Revoked'),
x509.NameAttribute(NameOID.COMMON_NAME, hostname)
]),
ca
)
revoke_cert(ca, revoked.cert.serial_number)
def gen_kdc_certs(nick_base, hostname, org, ca=None):
gen_cert(profile_kdc, nick_base + u'-kdc',
x509.Name([
x509.NameAttribute(NameOID.ORGANIZATION_NAME, org),
x509.NameAttribute(NameOID.ORGANIZATIONAL_UNIT_NAME, u'KDC'),
x509.NameAttribute(NameOID.COMMON_NAME, hostname)
]),
ca
)
gen_cert(profile_kdc, nick_base + u'-kdc-badname',
x509.Name([
x509.NameAttribute(NameOID.ORGANIZATION_NAME, org),
x509.NameAttribute(NameOID.ORGANIZATIONAL_UNIT_NAME, u'KDC'),
x509.NameAttribute(NameOID.COMMON_NAME, u'not-' + hostname)
]),
ca
)
gen_cert(profile_kdc, nick_base + u'-kdc-altname',
x509.Name([
x509.NameAttribute(NameOID.ORGANIZATION_NAME, org),
x509.NameAttribute(NameOID.ORGANIZATIONAL_UNIT_NAME, u'KDC'),
x509.NameAttribute(NameOID.COMMON_NAME, u'alt-' + hostname)
]),
ca, dns_name=hostname
)
gen_cert(profile_kdc, nick_base + u'-kdc-expired',
x509.Name([
x509.NameAttribute(NameOID.ORGANIZATION_NAME, org),
x509.NameAttribute(NameOID.ORGANIZATIONAL_UNIT_NAME,
u'Expired KDC'),
x509.NameAttribute(NameOID.COMMON_NAME, hostname)
]),
ca, warp=-2 * YEAR
)
gen_cert(profile_kdc, nick_base + u'-kdc-badusage',
x509.Name([
x509.NameAttribute(NameOID.ORGANIZATION_NAME, org),
x509.NameAttribute(NameOID.ORGANIZATIONAL_UNIT_NAME,
u'Bad Usage KDC'),
x509.NameAttribute(NameOID.COMMON_NAME, hostname)
]),
ca, badusage=True
)
revoked = gen_cert(profile_kdc, nick_base + u'-kdc-revoked',
x509.Name([
x509.NameAttribute(NameOID.ORGANIZATION_NAME, org),
x509.NameAttribute(NameOID.ORGANIZATIONAL_UNIT_NAME,
u'Revoked KDC'),
x509.NameAttribute(NameOID.COMMON_NAME, hostname)
]),
ca
)
revoke_cert(ca, revoked.cert.serial_number)
def gen_subtree(nick_base, org, ca=None):
subca = gen_cert(profile_ca, nick_base,
x509.Name([
x509.NameAttribute(NameOID.ORGANIZATION_NAME, org),
x509.NameAttribute(NameOID.COMMON_NAME, u'CA')
]),
ca
)
gen_cert(profile_server, u'wildcard',
x509.Name([
x509.NameAttribute(NameOID.ORGANIZATION_NAME, org),
x509.NameAttribute(NameOID.COMMON_NAME, u'*.' + domain)
]),
subca, wildcard=True
)
gen_server_certs(u'server', server1, org, subca)
gen_server_certs(u'replica', server2, org, subca)
gen_server_certs(u'client', client, org, subca)
gen_cert(profile_kdc, u'kdcwildcard',
x509.Name([
x509.NameAttribute(NameOID.ORGANIZATION_NAME, org),
x509.NameAttribute(NameOID.COMMON_NAME, u'*.' + domain)
]),
subca
)
gen_kdc_certs(u'server', server1, org, subca)
gen_kdc_certs(u'replica', server2, org, subca)
gen_kdc_certs(u'client', client, org, subca)
return subca
def create_pki():
gen_cert(profile_server, u'server-selfsign',
x509.Name([
x509.NameAttribute(NameOID.ORGANIZATION_NAME, u'Self-signed'),
x509.NameAttribute(NameOID.COMMON_NAME, server1)
])
)
gen_cert(profile_server, u'replica-selfsign',
x509.Name([
x509.NameAttribute(NameOID.ORGANIZATION_NAME, u'Self-signed'),
x509.NameAttribute(NameOID.COMMON_NAME, server2)
])
)
gen_cert(profile_server, u'noca',
x509.Name([
x509.NameAttribute(NameOID.ORGANIZATION_NAME, u'No-CA'),
x509.NameAttribute(NameOID.COMMON_NAME, server1)
])
)
gen_cert(profile_kdc, u'server-kdc-selfsign',
x509.Name([
x509.NameAttribute(NameOID.ORGANIZATION_NAME, u'Self-signed'),
x509.NameAttribute(NameOID.ORGANIZATIONAL_UNIT_NAME, u'KDC'),
x509.NameAttribute(NameOID.COMMON_NAME, server1)
])
)
gen_cert(profile_kdc, u'replica-kdc-selfsign',
x509.Name([
x509.NameAttribute(NameOID.ORGANIZATION_NAME, u'Self-signed'),
x509.NameAttribute(NameOID.ORGANIZATIONAL_UNIT_NAME, u'KDC'),
x509.NameAttribute(NameOID.COMMON_NAME, server2)
])
)
ca1 = gen_subtree(u'ca1', u'Example Organization')
gen_subtree(u'subca', u'Subsidiary Example Organization', ca1)
gen_subtree(u'ca2', u'Other Example Organization')
ca3 = gen_subtree(u'ca3', u'Unknown Organization')
os.unlink(os.path.join(cert_dir, ca3.nick + '.key'))
os.unlink(os.path.join(cert_dir, ca3.nick + '.crt'))

View File

@@ -0,0 +1,155 @@
#
# Copyright (C) 2017 FreeIPA Contributors see COPYING for license
#
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from cryptography import x509
from cryptography.x509.oid import NameOID
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import serialization
import datetime
import six
class ExternalCA(object):
"""
Provide external CA for testing
"""
def create_ca(self, cn='example.test'):
"""Create root CA.
:returns: bytes -- Root CA in PEM format.
"""
self.ca_key = rsa.generate_private_key(
public_exponent=65537,
key_size=2048,
backend=default_backend(),
)
self.ca_public_key = self.ca_key.public_key()
subject = self.issuer = x509.Name([
x509.NameAttribute(NameOID.COMMON_NAME, six.text_type(cn)),
])
builder = x509.CertificateBuilder()
builder = builder.subject_name(subject)
builder = builder.issuer_name(self.issuer)
builder = builder.public_key(self.ca_public_key)
builder = builder.serial_number(x509.random_serial_number())
builder = builder.not_valid_before(datetime.datetime.utcnow())
builder = builder.not_valid_after(
datetime.datetime.utcnow() + datetime.timedelta(days=365)
)
builder = builder.add_extension(
x509.KeyUsage(
digital_signature=False,
content_commitment=False,
key_encipherment=False,
data_encipherment=False,
key_agreement=False,
key_cert_sign=True,
crl_sign=True,
encipher_only=False,
decipher_only=False,
),
critical=True,
)
builder = builder.add_extension(
x509.BasicConstraints(ca=True, path_length=None),
critical=True,
)
builder = builder.add_extension(
x509.SubjectKeyIdentifier.from_public_key(self.ca_public_key),
critical=False,
)
builder = builder.add_extension(
x509.AuthorityKeyIdentifier.from_issuer_public_key(
self.ca_public_key
),
critical=False,
)
cert = builder.sign(self.ca_key, hashes.SHA256(), default_backend())
return cert.public_bytes(serialization.Encoding.PEM)
def sign_csr(self, ipa_csr):
"""Sign certificate CSR.
:param ipa_csr: CSR in PEM format.
:type ipa_csr: bytes.
:returns: bytes -- Signed CA in PEM format.
"""
csr_tbs = x509.load_pem_x509_csr(ipa_csr, default_backend())
csr_public_key = csr_tbs.public_key()
csr_subject = csr_tbs.subject
builder = x509.CertificateBuilder()
builder = builder.public_key(csr_public_key)
builder = builder.subject_name(csr_subject)
builder = builder.serial_number(x509.random_serial_number())
builder = builder.issuer_name(self.issuer)
builder = builder.not_valid_before(datetime.datetime.utcnow())
builder = builder.not_valid_after(
datetime.datetime.utcnow() + datetime.timedelta(days=365))
builder = builder.add_extension(
x509.KeyUsage(
digital_signature=False,
content_commitment=False,
key_encipherment=False,
data_encipherment=False,
key_agreement=False,
key_cert_sign=True,
crl_sign=True,
encipher_only=False,
decipher_only=False,
),
critical=True,
)
builder = builder.add_extension(
x509.SubjectKeyIdentifier.from_public_key(csr_public_key),
critical=False,
)
builder = builder.add_extension(
x509.AuthorityKeyIdentifier.from_issuer_public_key(
self.ca_public_key
),
critical=False,
)
builder = builder.add_extension(
x509.BasicConstraints(ca=True, path_length=1),
critical=True,
)
cert = builder.sign(
private_key=self.ca_key,
algorithm=hashes.SHA256(),
backend=default_backend(),
)
return cert.public_bytes(serialization.Encoding.PEM)

View File

@@ -0,0 +1,135 @@
# Authors:
# Gabe Alford <redhatrises@gmail.com>
#
# Copyright (C) 2013 Red Hat
# see file 'COPYING' for use and warranty information
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# FIXME: Pylint errors
# pylint: disable=no-member
import re
from ipatests.pytest_plugins.integration import tasks
from ipatests.test_integration.base import IntegrationTest
def run_advice(master, advice_id, advice_regex, raiseerr):
# Obtain the advice from the server
tasks.kinit_admin(master)
result = master.run_command(['ipa-advise', advice_id],
raiseonerr=raiseerr)
if not result.stdout_text:
advice = result.stderr_text
else:
advice = result.stdout_text
assert re.search(advice_regex, advice, re.S)
class TestAdvice(IntegrationTest):
"""
Tests ipa-advise output.
"""
advice_id = None
raiseerr = None
advice_regex = ''
topology = 'line'
def test_invalid_advice(self):
advice_id = 'invalid-advise-param'
advice_regex = "invalid[\s]+\'advice\'.*"
raiseerr = False
run_advice(self.master, advice_id, advice_regex, raiseerr)
def test_advice_FedoraAuthconfig(self):
advice_id = 'config-fedora-authconfig'
advice_regex = "\#\!\/bin\/sh.*" \
"authconfig[\s]+\-\-enableldap[\s]+" \
"\-\-ldapserver\=.*[\s]+\-\-enablerfc2307bis[\s]+" \
"\-\-enablekrb5"
raiseerr = True
run_advice(self.master, advice_id, advice_regex, raiseerr)
def test_advice_FreeBSDNSSPAM(self):
advice_id = 'config-freebsd-nss-pam-ldapd'
advice_regex = "\#\!\/bin\/sh.*" \
"pkg_add[\s]+\-r[\s]+nss\-pam\-ldapd[\s]+curl.*" \
"\/usr\/local\/etc\/rc\.d\/nslcd[\s]+restart"
raiseerr = True
run_advice(self.master, advice_id, advice_regex, raiseerr)
def test_advice_GenericNSSPAM(self):
advice_id = 'config-generic-linux-nss-pam-ldapd'
advice_regex = "\#\!\/bin\/sh.*" \
"apt\-get[\s]+\-y[\s]+install[\s]+curl[\s]+openssl[\s]+" \
"libnss\-ldapd[\s]+libpam\-ldapd[\s]+nslcd.*" \
"service[\s]+nscd[\s]+stop[\s]+\&\&[\s]+service[\s]+" \
"nslcd[\s]+restart"
raiseerr = True
run_advice(self.master, advice_id, advice_regex, raiseerr)
def test_advice_GenericSSSDBefore19(self):
advice_id = 'config-generic-linux-sssd-before-1-9'
advice_regex = "\#\!\/bin\/sh.*" \
"apt\-get[\s]+\-y[\s]+install sssd curl openssl.*" \
"service[\s]+sssd[\s]+start"
raiseerr = True
run_advice(self.master, advice_id, advice_regex, raiseerr)
def test_advice_RedHatNSS(self):
advice_id = 'config-redhat-nss-ldap'
advice_regex = "\#\!\/bin\/sh.*" \
"yum[\s]+install[\s]+\-y[\s]+curl[\s]+openssl[\s]+nss_ldap" \
"[\s]+authconfig.*authconfig[\s]+\-\-updateall" \
"[\s]+\-\-enableldap[\s]+\-\-enableldaptls"\
"[\s]+\-\-enableldapauth[\s]+" \
"\-\-ldapserver=.*[\s]+\-\-ldapbasedn=.*"
raiseerr = True
run_advice(self.master, advice_id, advice_regex, raiseerr)
def test_advice_RedHatNSSPAM(self):
advice_id = 'config-redhat-nss-pam-ldapd'
advice_regex = "\#\!\/bin\/sh.*" \
"yum[\s]+install[\s]+\-y[\s]+curl[\s]+openssl[\s]+" \
"nss\-pam\-ldapd[\s]+pam_ldap[\s]+authconfig.*" \
"authconfig[\s]+\-\-updateall[\s]+\-\-enableldap"\
"[\s]+\-\-enableldaptls[\s]+\-\-enableldapauth[\s]+" \
"\-\-ldapserver=.*[\s]+\-\-ldapbasedn=.*"
raiseerr = True
run_advice(self.master, advice_id, advice_regex, raiseerr)
def test_advice_RedHatSSSDBefore19(self):
advice_id = 'config-redhat-sssd-before-1-9'
advice_regex = "\#\!\/bin\/sh.*" \
"yum[\s]+install[\s]+\-y[\s]+sssd[\s]+authconfig[\s]+" \
"curl[\s]+openssl.*service[\s]+sssd[\s]+start"
raiseerr = True
run_advice(self.master, advice_id, advice_regex, raiseerr)

View File

@@ -0,0 +1,415 @@
# Authors:
# Petr Viktorin <pviktori@redhat.com>
#
# Copyright (C) 2014 Red Hat
# see file 'COPYING' for use and warranty information
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from __future__ import print_function
import logging
import os
import re
import contextlib
from ipapython.dn import DN
from ipatests.test_integration.base import IntegrationTest
from ipatests.pytest_plugins.integration import tasks
from ipatests.test_integration.test_dnssec import wait_until_record_is_signed
from ipatests.util import assert_deepequal
logger = logging.getLogger(__name__)
def assert_entries_equal(a, b):
assert_deepequal(a.dn, b.dn)
assert_deepequal(dict(a), dict(b))
def assert_results_equal(a, b):
def to_dict(r):
return {
'stdout': r.stdout_text,
'stderr': r.stderr_text,
'returncode': r.returncode,
}
assert_deepequal(to_dict(a), to_dict(b))
def check_admin_in_ldap(host):
ldap = host.ldap_connect()
basedn = host.domain.basedn
user_dn = DN(('uid', 'admin'), ('cn', 'users'), ('cn', 'accounts'), basedn)
entry = ldap.get_entry(user_dn)
print(entry)
assert entry.dn == user_dn
assert entry['uid'] == ['admin']
entry.pop('krbLastSuccessfulAuth', None)
return entry
def check_admin_in_cli(host):
result = host.run_command(['ipa', 'user-show', 'admin'])
assert 'User login: admin' in result.stdout_text, result.stdout_text
return result
def check_admin_in_id(host):
result = host.run_command(['id', 'admin'])
assert 'admin' in result.stdout_text, result.stdout_text
return result
def check_certs(host):
result = host.run_command(['ipa', 'cert-find'])
assert re.search('^Number of entries returned [1-9]\d*$',
result.stdout_text, re.MULTILINE), result.stdout_text
return result
def check_dns(host):
result = host.run_command(['host', host.hostname, 'localhost'])
return result
def check_kinit(host):
result = host.run_command(['kinit', 'admin'],
stdin_text=host.config.admin_password)
return result
CHECKS = [
(check_admin_in_ldap, assert_entries_equal),
(check_admin_in_cli, assert_results_equal),
(check_admin_in_id, assert_results_equal),
(check_certs, assert_results_equal),
(check_dns, assert_results_equal),
(check_kinit, assert_results_equal),
]
@contextlib.contextmanager
def restore_checker(host):
"""Check that the IPA at host works the same at context enter and exit"""
tasks.kinit_admin(host)
results = []
for check, assert_func in CHECKS:
logger.info('Storing result for %s', check)
results.append(check(host))
yield
for (check, assert_func), expected in zip(CHECKS, results):
logger.info('Checking result for %s', check)
got = check(host)
assert_func(expected, got)
def backup(host):
"""Run backup on host, return the path to the backup directory"""
result = host.run_command(['ipa-backup', '-v'])
# Get the backup location from the command's output
for line in result.stderr_text.splitlines():
prefix = ('ipa.ipaserver.install.ipa_backup.Backup: '
'INFO: Backed up to ')
if line.startswith(prefix):
backup_path = line[len(prefix):].strip()
logger.info('Backup path for %s is %s', host, backup_path)
return backup_path
else:
raise AssertionError('Backup directory not found in output')
class TestBackupAndRestore(IntegrationTest):
topology = 'star'
def test_full_backup_and_restore(self):
"""backup, uninstall, restore"""
with restore_checker(self.master):
backup_path = backup(self.master)
self.master.run_command(['ipa-server-install',
'--uninstall',
'-U'])
dirman_password = self.master.config.dirman_password
self.master.run_command(['ipa-restore', backup_path],
stdin_text=dirman_password + '\nyes')
def test_full_backup_and_restore_with_removed_users(self):
"""regression test for https://fedorahosted.org/freeipa/ticket/3866"""
with restore_checker(self.master):
backup_path = backup(self.master)
logger.info('Backup path for %s is %s', self.master, backup_path)
self.master.run_command(['ipa-server-install',
'--uninstall',
'-U'])
homedir = os.path.join(self.master.config.test_dir,
'testuser_homedir')
self.master.run_command(['useradd', 'ipatest_user1',
'--system',
'-d', homedir])
try:
dirman_password = self.master.config.dirman_password
self.master.run_command(['ipa-restore', backup_path],
stdin_text=dirman_password + '\nyes')
finally:
self.master.run_command(['userdel', 'ipatest_user1'])
def test_full_backup_and_restore_with_selinux_booleans_off(self):
"""regression test for https://fedorahosted.org/freeipa/ticket/4157"""
with restore_checker(self.master):
backup_path = backup(self.master)
logger.info('Backup path for %s is %s', self.master, backup_path)
self.master.run_command(['ipa-server-install',
'--uninstall',
'-U'])
self.master.run_command([
'setsebool', '-P',
'httpd_can_network_connect=off',
'httpd_manage_ipa=off',
])
dirman_password = self.master.config.dirman_password
self.master.run_command(['ipa-restore', backup_path],
stdin_text=dirman_password + '\nyes')
result = self.master.run_command([
'getsebool',
'httpd_can_network_connect',
'httpd_manage_ipa',
])
assert 'httpd_can_network_connect --> on' in result.stdout_text
assert 'httpd_manage_ipa --> on' in result.stdout_text
class BaseBackupAndRestoreWithDNS(IntegrationTest):
"""
Abstract class for DNS restore tests
"""
topology = 'star'
example_test_zone = "example.test."
example2_test_zone = "example2.test."
@classmethod
def install(cls, mh):
tasks.install_master(cls.master, setup_dns=True)
def _full_backup_restore_with_DNS_zone(self, reinstall=False):
"""backup, uninstall, restore"""
with restore_checker(self.master):
self.master.run_command([
'ipa', 'dnszone-add',
self.example_test_zone,
])
tasks.resolve_record(self.master.ip, self.example_test_zone)
backup_path = backup(self.master)
self.master.run_command(['ipa-server-install',
'--uninstall',
'-U'])
if reinstall:
tasks.install_master(self.master, setup_dns=True)
dirman_password = self.master.config.dirman_password
self.master.run_command(['ipa-restore', backup_path],
stdin_text=dirman_password + '\nyes')
tasks.resolve_record(self.master.ip, self.example_test_zone)
tasks.kinit_admin(self.master)
self.master.run_command([
'ipa', 'dnszone-add',
self.example2_test_zone,
])
tasks.resolve_record(self.master.ip, self.example2_test_zone)
class TestBackupAndRestoreWithDNS(BaseBackupAndRestoreWithDNS):
def test_full_backup_and_restore_with_DNS_zone(self):
"""backup, uninstall, restore"""
self._full_backup_restore_with_DNS_zone(reinstall=False)
class TestBackupReinstallRestoreWithDNS(BaseBackupAndRestoreWithDNS):
def test_full_backup_reinstall_restore_with_DNS_zone(self):
"""backup, uninstall, reinstall, restore"""
self._full_backup_restore_with_DNS_zone(reinstall=True)
class BaseBackupAndRestoreWithDNSSEC(IntegrationTest):
"""
Abstract class for DNSSEC restore tests
"""
topology = 'star'
example_test_zone = "example.test."
example2_test_zone = "example2.test."
@classmethod
def install(cls, mh):
tasks.install_master(cls.master, setup_dns=True)
args = [
"ipa-dns-install",
"--dnssec-master",
"--forwarder", cls.master.config.dns_forwarder,
"-U",
]
cls.master.run_command(args)
def _full_backup_and_restore_with_DNSSEC_zone(self, reinstall=False):
with restore_checker(self.master):
self.master.run_command([
'ipa', 'dnszone-add',
self.example_test_zone,
'--dnssec', 'true',
])
assert (
wait_until_record_is_signed(
self.master.ip, self.example_test_zone)
), "Zone is not signed"
backup_path = backup(self.master)
self.master.run_command(['ipa-server-install',
'--uninstall',
'-U'])
if reinstall:
tasks.install_master(self.master, setup_dns=True)
dirman_password = self.master.config.dirman_password
self.master.run_command(['ipa-restore', backup_path],
stdin_text=dirman_password + '\nyes')
assert (
wait_until_record_is_signed(
self.master.ip, self.example_test_zone)
), "Zone is not signed after restore"
tasks.kinit_admin(self.master)
self.master.run_command([
'ipa', 'dnszone-add',
self.example2_test_zone,
'--dnssec', 'true',
])
assert (
wait_until_record_is_signed(
self.master.ip, self.example2_test_zone)
), "A new zone is not signed"
class TestBackupAndRestoreWithDNSSEC(BaseBackupAndRestoreWithDNSSEC):
def test_full_backup_and_restore_with_DNSSEC_zone(self):
"""backup, uninstall, restore"""
self._full_backup_and_restore_with_DNSSEC_zone(reinstall=False)
class TestBackupReinstallRestoreWithDNSSEC(BaseBackupAndRestoreWithDNSSEC):
def test_full_backup_reinstall_restore_with_DNSSEC_zone(self):
"""backup, uninstall, install, restore"""
self._full_backup_and_restore_with_DNSSEC_zone(reinstall=True)
class BaseBackupAndRestoreWithKRA(IntegrationTest):
"""
Abstract class for KRA restore tests
"""
topology = 'star'
vault_name = "ci_test_vault"
vault_password = "password"
vault_data = "SSBsb3ZlIENJIHRlc3RzCg=="
@classmethod
def install(cls, mh):
tasks.install_master(cls.master, setup_dns=True, setup_kra=True)
def _full_backup_restore_with_vault(self, reinstall=False):
with restore_checker(self.master):
# create vault
self.master.run_command([
"ipa", "vault-add",
self.vault_name,
"--password", self.vault_password,
"--type", "symmetric",
])
# archive secret
self.master.run_command([
"ipa", "vault-archive",
self.vault_name,
"--password", self.vault_password,
"--data", self.vault_data,
])
# retrieve secret
self.master.run_command([
"ipa", "vault-retrieve",
self.vault_name,
"--password", self.vault_password,
])
backup_path = backup(self.master)
self.master.run_command(['ipa-server-install',
'--uninstall',
'-U'])
if reinstall:
tasks.install_master(self.master, setup_dns=True)
dirman_password = self.master.config.dirman_password
self.master.run_command(['ipa-restore', backup_path],
stdin_text=dirman_password + '\nyes')
tasks.kinit_admin(self.master)
# retrieve secret after restore
self.master.run_command([
"ipa", "vault-retrieve",
self.vault_name,
"--password", self.vault_password,
])
class TestBackupAndRestoreWithKRA(BaseBackupAndRestoreWithKRA):
def test_full_backup_restore_with_vault(self):
"""backup, uninstall, restore"""
self._full_backup_restore_with_vault(reinstall=False)
class TestBackupReinstallRestoreWithKRA(BaseBackupAndRestoreWithKRA):
def test_full_backup_reinstall_restore_with_vault(self):
"""backup, uninstall, reinstall, restore"""
self._full_backup_restore_with_vault(reinstall=True)

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,83 @@
from ipatests.test_integration.base import IntegrationTest
from ipatests.pytest_plugins.integration import tasks
DIRSRV_CONFIG_MODS = """
# https://fedorahosted.org/freeipa/ticket/4949
dn: cn=config,cn=ldbm database,cn=plugins,cn=config
changetype: modify
replace: nsslapd-db-locks
nsslapd-db-locks: 100000
# https://fedorahosted.org/freeipa/ticket/1930
dn: cn=config
changetype: modify
replace: nsslapd-allow-unauthenticated-binds
nsslapd-allow-unauthenticated-binds: off
-
replace: nsslapd-require-secure-binds
nsslapd-require-secure-binds: off
-
replace: nsslapd-allow-anonymous-access
nsslapd-allow-anonymous-access: off
-
replace: nsslapd-minssf
nsslapd-minssf: 0
# https://fedorahosted.org/freeipa/ticket/4048
dn: cn=config
changetype: modify
replace: nssslapd-maxbersize
nssslapd-maxbersize: 209715201
dn: cn=userRoot,cn=ldbm database,cn=plugins,cn=config
changetype: modify
replace: nsslapd-cachememsize
nsslapd-cachememsize: 10485761
dn: cn=config,cn=ldbm database,cn=plugins,cn=config
changetype: modify
replace: nsslapd-import_cachesize
nsslapd-import_cachesize: 20000001
-
replace: nsslapd-dbcachesize
nsslapd-dbcachesize: 10000001
"""
CONFIG_LDIF_PATH = "/root/dirsrv-config-mod.ldif"
class TestCustomInstallMaster(IntegrationTest):
"""
Install master with customized DS config
"""
topology = 'star'
@classmethod
def install(cls, mh):
# just prepare LDIF file on both master and replica
cls.master.put_file_contents(CONFIG_LDIF_PATH, DIRSRV_CONFIG_MODS)
def test_customized_ds_install_master(self):
tasks.install_master(self.master, setup_dns=False, extra_args=[
'--dirsrv-config-file', CONFIG_LDIF_PATH
])
class TestCustomInstallReplica(IntegrationTest):
"""
Install replica with customized DS config
"""
topology = 'star'
num_replicas = 1
@classmethod
def install(cls, mh):
# just prepare LDIF file on both master and replica
cls.replicas[0].put_file_contents(CONFIG_LDIF_PATH, DIRSRV_CONFIG_MODS)
tasks.install_master(cls.master)
def test_customized_ds_install_replica(self):
tasks.install_replica(
self.master, self.replicas[0], setup_ca=False,
extra_args=['--dirsrv-config-file', CONFIG_LDIF_PATH])

View File

@@ -0,0 +1,451 @@
#
# Copyright (C) 2016 FreeIPA Contributors see COPYING for license
#
import logging
import time
import pytest
import six
import dns.resolver
import dns.rrset
import dns.rdatatype
import dns.rdataclass
from ipatests.test_integration.base import IntegrationTest
from ipatests.pytest_plugins.integration import tasks
from ipapython.dnsutil import DNSName
from ipalib.constants import IPA_CA_RECORD
logger = logging.getLogger(__name__)
IPA_DEFAULT_MASTER_SRV_REC = (
# srv record name, port
(DNSName(u'_ldap._tcp'), 389),
(DNSName(u'_kerberos._tcp'), 88),
(DNSName(u'_kerberos._udp'), 88),
(DNSName(u'_kerberos-master._tcp'), 88),
(DNSName(u'_kerberos-master._udp'), 88),
(DNSName(u'_kpasswd._tcp'), 464),
(DNSName(u'_kpasswd._udp'), 464),
)
IPA_DEFAULT_ADTRUST_SRV_REC = (
# srv record name, port
(DNSName(u'_ldap._tcp.Default-First-Site-Name._sites.dc._msdcs'), 389),
(DNSName(u'_ldap._tcp.dc._msdcs'), 389),
(DNSName(u'_kerberos._tcp.Default-First-Site-Name._sites.dc._msdcs'), 88),
(DNSName(u'_kerberos._udp.Default-First-Site-Name._sites.dc._msdcs'), 88),
(DNSName(u'_kerberos._tcp.dc._msdcs'), 88),
(DNSName(u'_kerberos._udp.dc._msdcs'), 88),
)
# we keep NTP SRV record separated as NTP is "optional"
IPA_DEFAULT_NTP_SRV_REC = (
# srv record name, port
(DNSName("_ntp._udp"), 123),
)
IPA_CA_A_REC = (
(DNSName(six.text_type(IPA_CA_RECORD))),
)
def resolve_records_from_server(rname, rtype, nameserver):
error = None
res = dns.resolver.Resolver()
res.nameservers = [nameserver]
res.lifetime = 30
logger.info("Query: %s %s, nameserver %s", rname, rtype, nameserver)
# lets try to query 3x
for _i in range(3):
try:
ans = res.query(rname, rtype)
logger.info("Answer: %s", ans.rrset)
return ans.rrset
except (dns.resolver.NXDOMAIN, dns.resolver.Timeout) as e:
error = e
time.sleep(10)
pytest.fail("Query: {} {}, nameserver {} failed due to {}".format(
rname, rtype, nameserver, error))
def _gen_expected_srv_rrset(rname, port, servers, ttl=86400):
rdata_list = [
"{prio} {weight} {port} {servername}".format(
prio=prio,
weight=weight,
port=port,
servername=servername.make_absolute()
)
for prio, weight, servername in servers
]
return dns.rrset.from_text_list(
rname, ttl, dns.rdataclass.IN, dns.rdatatype.SRV, rdata_list
)
def _gen_expected_a_rrset(rname, servers, ttl=86400):
return dns.rrset.from_text_list(rname, ttl, dns.rdataclass.IN,
dns.rdatatype.A, servers)
class TestDNSLocations(IntegrationTest):
"""Simple test if SRV DNS records for IPA locations are generated properly
Topology:
* 3 servers (replica0 --- master --- replica1)
replica0 with NTP/no CA, master with ADtrust installed later,
replica1 without NTP/with CA
* 2 locations (prague, paris)
"""
num_replicas = 2
topology = 'star'
LOC_PRAGUE = u'prague'
LOC_PARIS = u'paris'
PRIO_HIGH = 0
PRIO_LOW = 50
WEIGHT = 100
@classmethod
def install(cls, mh):
cls.domain = DNSName(cls.master.domain.name).make_absolute()
tasks.install_master(cls.master, setup_dns=True)
tasks.install_replica(cls.master, cls.replicas[0], setup_dns=True,
setup_ca=False)
tasks.install_replica(cls.master, cls.replicas[1], setup_dns=True,
setup_ca=True, extra_args=(['--no-ntp']))
for host in (cls.master, cls.replicas[0], cls.replicas[1]):
ldap = host.ldap_connect()
tasks.wait_for_replication(ldap)
# give time to named to retrieve new records
time.sleep(20)
@classmethod
def delete_update_system_records(cls, rnames):
filepath = '/tmp/ipa.nsupdate'
cls.master.run_command([
'ipa', 'dns-update-system-records', '--dry-run', '--out', filepath
])
for name in rnames:
cls.master.run_command([
'ipa', 'dnsrecord-del', str(cls.domain), str(name),
'--del-all'])
time.sleep(15)
# allow unauthenticates nsupdate (no need to testing authentication)
cls.master.run_command([
'ipa', 'dnszone-mod', str(cls.domain),
'--update-policy=grant * wildcard *;'
], raiseonerr=False)
cls.master.run_command(['nsupdate', '-g', filepath])
time.sleep(15)
def _test_A_rec_against_server(self, server_ip, domain, expected_servers,
rec_list=IPA_CA_A_REC):
for rname in rec_list:
name_abs = rname.derelativize(domain)
expected = _gen_expected_a_rrset(name_abs, expected_servers)
query = resolve_records_from_server(
name_abs, 'A', server_ip)
assert expected == query, (
"Expected and received DNS data do not match on server "
"with IP: '{}' for name '{}' (expected:\n{}\ngot:\n{})".
format(server_ip, name_abs, expected, query))
def _test_SRV_rec_against_server(self, server_ip, domain, expected_servers,
rec_list=IPA_DEFAULT_MASTER_SRV_REC):
for rname, port in rec_list:
name_abs = rname.derelativize(domain)
expected = _gen_expected_srv_rrset(
name_abs, port, expected_servers)
query = resolve_records_from_server(
name_abs, 'SRV', server_ip)
assert expected == query, (
"Expected and received DNS data do not match on server "
"with IP: '{}' for name '{}' (expected:\n{}\ngot:\n{})".
format(server_ip, name_abs, expected, query))
def test_without_locations(self):
"""Servers are not in locations, this tests if basic system records
are generated properly"""
expected_servers = (
(self.PRIO_HIGH, self.WEIGHT, DNSName(self.master.hostname)),
(self.PRIO_HIGH, self.WEIGHT, DNSName(self.replicas[0].hostname)),
(self.PRIO_HIGH, self.WEIGHT, DNSName(self.replicas[1].hostname)),
)
for ip in (self.master.ip, self.replicas[0].ip, self.replicas[1].ip):
self._test_SRV_rec_against_server(ip, self.domain,
expected_servers)
def test_nsupdate_without_locations(self):
"""Test nsupdate file generated by dns-update-system-records
Remove all records and the use nsupdate to restore state and test if
all record are there as expected"""
self.delete_update_system_records(rnames=(r[0] for r in
IPA_DEFAULT_MASTER_SRV_REC))
self.test_without_locations()
def test_one_replica_in_location(self):
"""Put one replica to location and test if records changed properly
"""
# create location prague, replica0 --> location prague
self.master.run_command([
'ipa', 'location-add', self.LOC_PRAGUE
])
self.master.run_command([
'ipa', 'server-mod', self.replicas[0].hostname,
'--location', self.LOC_PRAGUE
])
tasks.restart_named(self.replicas[0])
servers_without_loc = (
(self.PRIO_HIGH, self.WEIGHT, DNSName(self.master.hostname)),
(self.PRIO_HIGH, self.WEIGHT, DNSName(self.replicas[0].hostname)),
(self.PRIO_HIGH, self.WEIGHT, DNSName(self.replicas[1].hostname)),
)
domain_without_loc = DNSName(self.master.domain.name).make_absolute()
servers_prague_loc = (
(self.PRIO_LOW, self.WEIGHT, DNSName(self.master.hostname)),
(self.PRIO_HIGH, self.WEIGHT, DNSName(self.replicas[0].hostname)),
(self.PRIO_LOW, self.WEIGHT, DNSName(self.replicas[1].hostname)),
)
domain_prague_loc = (
DNSName('{}._locations'.format(self.LOC_PRAGUE)) +
DNSName(self.master.domain.name).make_absolute()
)
self._test_SRV_rec_against_server(
self.replicas[0].ip, domain_prague_loc, servers_prague_loc)
for ip in (self.master.ip, self.replicas[1].ip):
self._test_SRV_rec_against_server(
ip, domain_without_loc, servers_without_loc)
def test_two_replicas_in_location(self):
"""Put second replica to location and test if records changed properly
"""
# create location paris, replica1 --> location prague
self.master.run_command(['ipa', 'location-add', self.LOC_PARIS])
self.master.run_command([
'ipa', 'server-mod', self.replicas[1].hostname, '--location',
self.LOC_PARIS])
tasks.restart_named(self.replicas[1])
servers_without_loc = (
(self.PRIO_HIGH, self.WEIGHT, DNSName(self.master.hostname)),
(self.PRIO_HIGH, self.WEIGHT, DNSName(self.replicas[0].hostname)),
(self.PRIO_HIGH, self.WEIGHT, DNSName(self.replicas[1].hostname)),
)
domain_without_loc = DNSName(self.master.domain.name).make_absolute()
servers_prague_loc = (
(self.PRIO_LOW, self.WEIGHT, DNSName(self.master.hostname)),
(self.PRIO_HIGH, self.WEIGHT, DNSName(self.replicas[0].hostname)),
(self.PRIO_LOW, self.WEIGHT, DNSName(self.replicas[1].hostname)),
)
domain_prague_loc = (
DNSName('{}._locations'.format(self.LOC_PRAGUE)) + DNSName(
self.master.domain.name).make_absolute())
servers_paris_loc = (
(self.PRIO_LOW, self.WEIGHT, DNSName(self.master.hostname)),
(self.PRIO_LOW, self.WEIGHT, DNSName(self.replicas[0].hostname)),
(self.PRIO_HIGH, self.WEIGHT, DNSName(self.replicas[1].hostname)),
)
domain_paris_loc = (
DNSName('{}._locations'.format(self.LOC_PARIS)) + DNSName(
self.master.domain.name).make_absolute())
self._test_SRV_rec_against_server(
self.replicas[0].ip, domain_prague_loc, servers_prague_loc)
self._test_SRV_rec_against_server(
self.replicas[1].ip, domain_paris_loc, servers_paris_loc)
self._test_SRV_rec_against_server(
self.master.ip, domain_without_loc, servers_without_loc)
def test_all_servers_in_location(self):
"""Put master (as second server) to location and test if records
changed properly
"""
# master --> location paris
self.master.run_command([
'ipa', 'server-mod', self.master.hostname, '--location',
self.LOC_PARIS])
tasks.restart_named(self.master)
servers_prague_loc = (
(self.PRIO_LOW, self.WEIGHT, DNSName(self.master.hostname)),
(self.PRIO_HIGH, self.WEIGHT, DNSName(self.replicas[0].hostname)),
(self.PRIO_LOW, self.WEIGHT, DNSName(self.replicas[1].hostname)),
)
domain_prague_loc = (
DNSName('{}._locations'.format(self.LOC_PRAGUE)) + DNSName(
self.master.domain.name).make_absolute())
servers_paris_loc = (
(self.PRIO_HIGH, self.WEIGHT, DNSName(self.master.hostname)),
(self.PRIO_LOW, self.WEIGHT, DNSName(self.replicas[0].hostname)),
(self.PRIO_HIGH, self.WEIGHT, DNSName(self.replicas[1].hostname)),
)
domain_paris_loc = (
DNSName('{}._locations'.format(self.LOC_PARIS)) + DNSName(
self.master.domain.name).make_absolute())
self._test_SRV_rec_against_server(
self.replicas[0].ip, domain_prague_loc, servers_prague_loc)
for ip in (self.replicas[1].ip, self.master.ip):
self._test_SRV_rec_against_server(ip, domain_paris_loc,
servers_paris_loc)
def test_change_weight(self):
"""Change weight of master and test if records changed properly
"""
new_weight = 2000
self.master.run_command([
'ipa', 'server-mod', self.master.hostname, '--service-weight',
str(new_weight)
])
# all servers must be restarted
tasks.restart_named(self.master, self.replicas[0], self.replicas[1])
servers_prague_loc = (
(self.PRIO_LOW, new_weight, DNSName(self.master.hostname)),
(self.PRIO_HIGH, self.WEIGHT, DNSName(self.replicas[0].hostname)),
(self.PRIO_LOW, self.WEIGHT, DNSName(self.replicas[1].hostname)),
)
domain_prague_loc = (
DNSName('{}._locations'.format(self.LOC_PRAGUE)) + DNSName(
self.master.domain.name).make_absolute())
servers_paris_loc = (
(self.PRIO_HIGH, new_weight, DNSName(self.master.hostname)),
(self.PRIO_LOW, self.WEIGHT, DNSName(self.replicas[0].hostname)),
(self.PRIO_HIGH, self.WEIGHT, DNSName(self.replicas[1].hostname)),
)
domain_paris_loc = (
DNSName('{}._locations'.format(self.LOC_PARIS)) + DNSName(
self.master.domain.name).make_absolute())
self._test_SRV_rec_against_server(
self.replicas[0].ip, domain_prague_loc, servers_prague_loc)
for ip in (self.replicas[1].ip, self.master.ip):
self._test_SRV_rec_against_server(ip, domain_paris_loc,
servers_paris_loc)
def test_restore_locations_and_weight(self):
"""Restore locations and weight. Not just for test purposes but also
for the following tests"""
for hostname in (self.master.hostname, self.replicas[0].hostname,
self.replicas[1].hostname):
self.master.run_command(['ipa', 'server-mod', hostname,
'--location='''])
self.master.run_command(['ipa', 'location-del', self.LOC_PRAGUE])
self.master.run_command(['ipa', 'location-del', self.LOC_PARIS])
self.master.run_command([
'ipa', 'server-mod', self.master.hostname, '--service-weight',
str(self.WEIGHT)
])
tasks.restart_named(self.master, self.replicas[0], self.replicas[1])
time.sleep(5)
def test_ipa_ca_records(self):
""" Test ipa-ca dns records with firstly removing the records and then
using the nsupdate generated by dns-update-system-records"""
self.delete_update_system_records(rnames=IPA_CA_A_REC)
expected_servers = (self.master.ip, self.replicas[1].ip)
for ip in (self.master.ip, self.replicas[0].ip, self.replicas[1].ip):
self._test_A_rec_against_server(ip, self.domain, expected_servers)
def test_ntp_srv_records(self):
""" Test NTP dns records with firstly removing the records and then
using the nsupdate generated by dns-update-system-records."""
self.delete_update_system_records(rnames=(r[0] for r in
IPA_DEFAULT_NTP_SRV_REC))
# we installed NTP only on master and replica[0]
expected_servers = (
(self.PRIO_HIGH, self.WEIGHT, DNSName(self.master.hostname)),
(self.PRIO_HIGH, self.WEIGHT, DNSName(self.replicas[0].hostname)),
)
for ip in (self.master.ip, self.replicas[0].ip, self.replicas[1].ip):
self._test_SRV_rec_against_server(ip, self.domain,
expected_servers,
rec_list=IPA_DEFAULT_NTP_SRV_REC)
def test_adtrust_system_records(self):
""" Test ADTrust dns records with firstly installing a trust then
removing the records and using the nsupdate generated by
dns-update-system-records."""
self.master.run_command(['ipa-adtrust-install', '-U',
'--enable-compat', '--netbios-name', 'IPA',
'-a', self.master.config.admin_password,
'--add-sids'])
# lets re-kinit after adtrust-install and restart named
tasks.kinit_admin(self.master)
tasks.restart_named(self.master)
time.sleep(5)
self.delete_update_system_records(rnames=(r[0] for r in
IPA_DEFAULT_ADTRUST_SRV_REC))
expected_servers = (
(self.PRIO_HIGH, self.WEIGHT, DNSName(self.master.hostname)),
)
for ip in (self.master.ip, self.replicas[0].ip, self.replicas[1].ip):
self._test_SRV_rec_against_server(
ip, self.domain, expected_servers,
rec_list=IPA_DEFAULT_ADTRUST_SRV_REC)
def test_remove_replica_with_ca(self):
"""Test ipa-ca dns records after removing the replica with CA"""
tasks.uninstall_replica(self.master, self.replicas[1])
self.delete_update_system_records(rnames=IPA_CA_A_REC)
expected_servers = (self.master.ip,)
self._test_A_rec_against_server(self.master.ip, self.domain,
expected_servers)
def test_remove_replica_with_ntp(self):
"""Test NTP dns records after removing the replica with NTP"""
tasks.uninstall_replica(self.master, self.replicas[0])
self.delete_update_system_records(rnames=(r[0] for r in
IPA_DEFAULT_NTP_SRV_REC))
expected_servers = (
(self.PRIO_HIGH, self.WEIGHT, DNSName(self.master.hostname)),
)
self._test_SRV_rec_against_server(self.master.ip, self.domain,
expected_servers,
rec_list=IPA_DEFAULT_NTP_SRV_REC)

View File

@@ -0,0 +1,566 @@
#
# Copyright (C) 2015 FreeIPA Contributors see COPYING for license
#
import logging
import dns.dnssec
import dns.resolver
import dns.name
import time
from ipatests.test_integration.base import IntegrationTest
from ipatests.pytest_plugins.integration import tasks
from ipaplatform.paths import paths
logger = logging.getLogger(__name__)
test_zone = "dnssec.test."
test_zone_repl = "dnssec-replica.test."
root_zone = "."
example_test_zone = "example.test."
example2_test_zone = "example2.test."
example3_test_zone = "example3.test."
def resolve_with_dnssec(nameserver, query, rtype="SOA"):
res = dns.resolver.Resolver()
res.nameservers = [nameserver]
res.lifetime = 10 # wait max 10 seconds for reply
# enable Authenticated Data + Checking Disabled flags
res.set_flags(dns.flags.AD | dns.flags.CD)
# enable EDNS v0 + enable DNSSEC-Ok flag
res.use_edns(0, dns.flags.DO, 0)
ans = res.query(query, rtype)
return ans
def get_RRSIG_record(nameserver, query, rtype="SOA"):
ans = resolve_with_dnssec(nameserver, query, rtype=rtype)
return ans.response.find_rrset(
ans.response.answer, dns.name.from_text(query),
dns.rdataclass.IN, dns.rdatatype.RRSIG,
dns.rdatatype.from_text(rtype))
def is_record_signed(nameserver, query, rtype="SOA"):
try:
get_RRSIG_record(nameserver, query, rtype=rtype)
except KeyError:
return False
except dns.exception.DNSException:
return False
return True
def wait_until_record_is_signed(nameserver, record, rtype="SOA",
timeout=100):
"""
Returns True if record is signed, or False on timeout
:param nameserver: nameserver to query
:param record: query
:param log: logger
:param rtype: record type
:param timeout:
:return: True if records is signed, False if timeout
"""
logger.info("Waiting for signed %s record of %s from server %s (timeout "
"%s sec)", rtype, record, nameserver, timeout)
wait_until = time.time() + timeout
while time.time() < wait_until:
if is_record_signed(nameserver, record, rtype=rtype):
return True
time.sleep(1)
return False
class TestInstallDNSSECLast(IntegrationTest):
"""Simple DNSSEC test
Install a server and a replica with DNS, then reinstall server
as DNSSEC master
"""
num_replicas = 1
topology = 'star'
@classmethod
def install(cls, mh):
tasks.install_master(cls.master, setup_dns=True)
tasks.install_replica(cls.master, cls.replicas[0], setup_dns=True)
def test_install_dnssec_master(self):
"""Both master and replica have DNS installed"""
args = [
"ipa-dns-install",
"--dnssec-master",
"--forwarder", self.master.config.dns_forwarder,
"-U",
]
self.master.run_command(args)
def test_if_zone_is_signed_master(self):
# add zone with enabled DNSSEC signing on master
args = [
"ipa",
"dnszone-add", test_zone,
"--skip-overlap-check",
"--dnssec", "true",
]
self.master.run_command(args)
# test master
assert wait_until_record_is_signed(
self.master.ip, test_zone, timeout=100
), "Zone %s is not signed (master)" % test_zone
# test replica
assert wait_until_record_is_signed(
self.replicas[0].ip, test_zone, timeout=200
), "DNS zone %s is not signed (replica)" % test_zone
def test_if_zone_is_signed_replica(self):
# add zone with enabled DNSSEC signing on replica
args = [
"ipa",
"dnszone-add", test_zone_repl,
"--skip-overlap-check",
"--dnssec", "true",
]
self.replicas[0].run_command(args)
# test replica
assert wait_until_record_is_signed(
self.replicas[0].ip, test_zone_repl, timeout=300
), "Zone %s is not signed (replica)" % test_zone_repl
# we do not need to wait, on master zones should be singed faster
# than on replicas
assert wait_until_record_is_signed(
self.master.ip, test_zone_repl, timeout=5
), "DNS zone %s is not signed (master)" % test_zone
def test_disable_reenable_signing_master(self):
dnskey_old = resolve_with_dnssec(self.master.ip, test_zone,
rtype="DNSKEY").rrset
# disable DNSSEC signing of zone on master
args = [
"ipa",
"dnszone-mod", test_zone,
"--dnssec", "false",
]
self.master.run_command(args)
time.sleep(20) # sleep a bit until LDAP changes are applied to DNS
# test master
assert not is_record_signed(
self.master.ip, test_zone
), "Zone %s is still signed (master)" % test_zone
# test replica
assert not is_record_signed(
self.replicas[0].ip, test_zone
), "DNS zone %s is still signed (replica)" % test_zone
# reenable DNSSEC signing
args = [
"ipa",
"dnszone-mod", test_zone,
"--dnssec", "true",
]
self.master.run_command(args)
# test master
assert wait_until_record_is_signed(
self.master.ip, test_zone, timeout=100
), "Zone %s is not signed (master)" % test_zone
# test replica
assert wait_until_record_is_signed(
self.replicas[0].ip, test_zone, timeout=200
), "DNS zone %s is not signed (replica)" % test_zone
dnskey_new = resolve_with_dnssec(self.master.ip, test_zone,
rtype="DNSKEY").rrset
assert dnskey_old != dnskey_new, "DNSKEY should be different"
def test_disable_reenable_signing_replica(self):
dnskey_old = resolve_with_dnssec(self.replicas[0].ip, test_zone_repl,
rtype="DNSKEY").rrset
# disable DNSSEC signing of zone on replica
args = [
"ipa",
"dnszone-mod", test_zone_repl,
"--dnssec", "false",
]
self.master.run_command(args)
time.sleep(20) # sleep a bit until LDAP changes are applied to DNS
# test master
assert not is_record_signed(
self.master.ip, test_zone_repl
), "Zone %s is still signed (master)" % test_zone_repl
# test replica
assert not is_record_signed(
self.replicas[0].ip, test_zone_repl
), "DNS zone %s is still signed (replica)" % test_zone_repl
# reenable DNSSEC signing
args = [
"ipa",
"dnszone-mod", test_zone_repl,
"--dnssec", "true",
]
self.master.run_command(args)
# test master
assert wait_until_record_is_signed(
self.master.ip, test_zone_repl, timeout=100
), "Zone %s is not signed (master)" % test_zone_repl
# test replica
assert wait_until_record_is_signed(
self.replicas[0].ip, test_zone_repl, timeout=200
), "DNS zone %s is not signed (replica)" % test_zone_repl
dnskey_new = resolve_with_dnssec(self.replicas[0].ip, test_zone_repl,
rtype="DNSKEY").rrset
assert dnskey_old != dnskey_new, "DNSKEY should be different"
class TestInstallDNSSECFirst(IntegrationTest):
"""Simple DNSSEC test
Install the server with DNSSEC and then install the replica with DNS
"""
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(TestInstallDNSSECFirst, cls).uninstall(mh)
def test_sign_root_zone(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, timeout=100
), "Zone %s is not signed (master)" % root_zone
# test replica
assert wait_until_record_is_signed(
self.replicas[0].ip, root_zone, timeout=300
), "Zone %s is not signed (replica)" % root_zone
def test_chain_of_trust(self):
"""
Validate signed DNS records, using our own signed root zone
:return:
"""
# add test zone
args = [
"ipa", "dnszone-add", example_test_zone, "--dnssec", "true",
"--skip-overlap-check",
]
self.master.run_command(args)
# delegation
args = [
"ipa", "dnsrecord-add", root_zone, example_test_zone,
"--ns-rec=" + self.master.hostname
]
self.master.run_command(args)
# wait until zone is signed
assert wait_until_record_is_signed(
self.master.ip, example_test_zone, timeout=100
), "Zone %s is not signed (master)" % example_test_zone
# wait until zone is signed
assert wait_until_record_is_signed(
self.replicas[0].ip, example_test_zone, timeout=200
), "Zone %s is not signed (replica)" % example_test_zone
# GET DNSKEY records from zone
ans = resolve_with_dnssec(self.master.ip, example_test_zone,
rtype="DNSKEY")
dnskey_rrset = ans.response.get_rrset(
ans.response.answer,
dns.name.from_text(example_test_zone),
dns.rdataclass.IN,
dns.rdatatype.DNSKEY)
assert dnskey_rrset, "No DNSKEY records received"
logger.debug("DNSKEY records returned: %s", dnskey_rrset.to_text())
# generate DS records
ds_records = []
for key_rdata in dnskey_rrset:
if key_rdata.flags != 257:
continue # it is not KSK
ds_records.append(dns.dnssec.make_ds(example_test_zone, key_rdata,
'sha256'))
assert ds_records, ("No KSK returned from the %s zone" %
example_test_zone)
logger.debug("DS records for %s created: %r", example_test_zone,
ds_records)
# add DS records to root zone
args = [
"ipa", "dnsrecord-add", root_zone, example_test_zone,
# DS record requires to coexists with NS
"--ns-rec", self.master.hostname,
]
for ds in ds_records:
args.append("--ds-rec")
args.append(ds.to_text())
self.master.run_command(args)
# wait until DS records it replicated
assert wait_until_record_is_signed(
self.replicas[0].ip, example_test_zone, timeout=100,
rtype="DS"
), "No DS record of '%s' returned from replica" % example_test_zone
# extract DSKEY from root zone
ans = resolve_with_dnssec(self.master.ip, root_zone,
rtype="DNSKEY")
dnskey_rrset = ans.response.get_rrset(ans.response.answer,
dns.name.from_text(root_zone),
dns.rdataclass.IN,
dns.rdatatype.DNSKEY)
assert dnskey_rrset, "No DNSKEY records received"
logger.debug("DNSKEY records returned: %s", dnskey_rrset.to_text())
# export trust keys for root zone
root_key_rdatas = []
for key_rdata in dnskey_rrset:
if key_rdata.flags != 257:
continue # it is not KSK
root_key_rdatas.append(key_rdata)
assert root_key_rdatas, "No KSK returned from the root zone"
root_keys_rrset = dns.rrset.from_rdata_list(dnskey_rrset.name,
dnskey_rrset.ttl,
root_key_rdatas)
logger.debug("Root zone trusted key: %s", root_keys_rrset.to_text())
# set trusted key for our root zone
self.master.put_file_contents(paths.DNSSEC_TRUSTED_KEY,
root_keys_rrset.to_text() + '\n')
self.replicas[0].put_file_contents(paths.DNSSEC_TRUSTED_KEY,
root_keys_rrset.to_text() + '\n')
# verify signatures
time.sleep(1)
args = [
"drill", "@localhost", "-k",
paths.DNSSEC_TRUSTED_KEY, "-S",
example_test_zone, "SOA"
]
# test if signature chains are valid
self.master.run_command(args)
self.replicas[0].run_command(args)
class TestMigrateDNSSECMaster(IntegrationTest):
"""test DNSSEC master migration
Install a server and a replica with DNS, then reinstall server
as DNSSEC master
Test:
* migrate dnssec master to replica
* create new zone
* verify if zone is signed on all replicas
* add new replica
* add new zone
* test if new zone is signed on all replicas
"""
num_replicas = 2
topology = 'star'
@classmethod
def install(cls, mh):
tasks.install_master(cls.master, setup_dns=True)
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)
def test_migrate_dnssec_master(self):
"""Both master and replica have DNS installed"""
backup_filename = "/var/lib/ipa/ipa-kasp.db.backup"
replica_backup_filename = "/tmp/ipa-kasp.db.backup"
# add test zone
args = [
"ipa", "dnszone-add", example_test_zone, "--dnssec", "true",
"--skip-overlap-check",
]
self.master.run_command(args)
# wait until zone is signed
assert wait_until_record_is_signed(
self.master.ip, example_test_zone, timeout=100
), "Zone %s is not signed (master)" % example_test_zone
# wait until zone is signed
assert wait_until_record_is_signed(
self.replicas[0].ip, example_test_zone, timeout=200
), "Zone %s is not signed (replica)" % example_test_zone
dnskey_old = resolve_with_dnssec(self.master.ip, example_test_zone,
rtype="DNSKEY").rrset
# migrate dnssec master to replica
args = [
"ipa-dns-install",
"--disable-dnssec-master",
"--forwarder", self.master.config.dns_forwarder,
"--force",
"-U",
]
self.master.run_command(args)
# move content of "ipa-kasp.db.backup" to replica
kasp_db_backup = self.master.get_file_contents(backup_filename)
self.replicas[0].put_file_contents(replica_backup_filename,
kasp_db_backup)
args = [
"ipa-dns-install",
"--dnssec-master",
"--kasp-db", replica_backup_filename,
"--forwarder", self.master.config.dns_forwarder,
"-U",
]
self.replicas[0].run_command(args)
# wait until zone is signed
assert wait_until_record_is_signed(
self.master.ip, example_test_zone, timeout=100
), "Zone %s is not signed after migration (master)" % example_test_zone
# wait until zone is signed
assert wait_until_record_is_signed(
self.replicas[0].ip, example_test_zone, timeout=200
), "Zone %s is not signed after migration (replica)" % example_test_zone
# test if dnskey are the same
dnskey_new = resolve_with_dnssec(self.master.ip, example_test_zone,
rtype="DNSKEY").rrset
assert dnskey_old == dnskey_new, "DNSKEY should be the same"
# add test zone
args = [
"ipa", "dnszone-add", example2_test_zone, "--dnssec", "true",
"--skip-overlap-check",
]
self.replicas[0].run_command(args)
# wait until zone is signed
assert wait_until_record_is_signed(
self.replicas[0].ip, example2_test_zone, timeout=100
), ("Zone %s is not signed after migration (replica - dnssec master)"
% example2_test_zone)
# wait until zone is signed
assert wait_until_record_is_signed(
self.master.ip, example2_test_zone, timeout=200
), ("Zone %s is not signed after migration (master)"
% example2_test_zone)
# add new replica
tasks.install_replica(self.master, self.replicas[1], setup_dns=True)
# test if originial zones are signed on new replica
# wait until zone is signed
assert wait_until_record_is_signed(
self.replicas[1].ip, example_test_zone, timeout=200
), ("Zone %s is not signed (new replica)"
% example_test_zone)
# wait until zone is signed
assert wait_until_record_is_signed(
self.replicas[1].ip, example2_test_zone, timeout=200
), ("Zone %s is not signed (new replica)"
% example2_test_zone)
# add new zone to new replica
args = [
"ipa", "dnszone-add", example3_test_zone, "--dnssec", "true",
"--skip-overlap-check",
]
self.replicas[1].run_command(args)
# wait until zone is signed
assert wait_until_record_is_signed(
self.replicas[1].ip, example3_test_zone, timeout=200
), ("Zone %s is not signed (new replica)"
% example3_test_zone)
assert wait_until_record_is_signed(
self.replicas[0].ip, example3_test_zone, timeout=200
), ("Zone %s is not signed (replica)"
% example3_test_zone)
# wait until zone is signed
assert wait_until_record_is_signed(
self.master.ip, example3_test_zone, timeout=200
), ("Zone %s is not signed (master)"
% example3_test_zone)

View File

@@ -0,0 +1,73 @@
#
# Copyright (C) 2017 FreeIPA Contributors see COPYING for license
#
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os
from ipatests.pytest_plugins.integration import tasks
from ipatests.test_integration.base import IntegrationTest
from ipatests.test_integration.create_external_ca import ExternalCA
class TestExternalCA(IntegrationTest):
"""
Test of FreeIPA server installation with exernal CA
"""
@tasks.collect_logs
def test_external_ca(self):
# Step 1 of ipa-server-install
self.master.run_command([
'ipa-server-install', '-U',
'-a', self.master.config.admin_password,
'-p', self.master.config.dirman_password,
'--setup-dns', '--no-forwarders',
'-n', self.master.domain.name,
'-r', self.master.domain.realm,
'--domain-level=%i' % self.master.config.domain_level,
'--external-ca'
])
test_dir = self.master.config.test_dir
# Get IPA CSR as bytes
ipa_csr = self.master.get_file_contents('/root/ipa.csr')
external_ca = ExternalCA()
# Create root CA
root_ca = external_ca.create_ca()
# Sign CSR
ipa_ca = external_ca.sign_csr(ipa_csr)
root_ca_fname = os.path.join(test_dir, 'root_ca.crt')
ipa_ca_fname = os.path.join(test_dir, 'ipa_ca.crt')
# Transport certificates (string > file) to master
self.master.put_file_contents(root_ca_fname, root_ca)
self.master.put_file_contents(ipa_ca_fname, ipa_ca)
# Step 2 of ipa-server-install
self.master.run_command([
'ipa-server-install',
'-a', self.master.config.admin_password,
'-p', self.master.config.dirman_password,
'--external-cert-file', ipa_ca_fname,
'--external-cert-file', root_ca_fname
])
# Make sure IPA server is working properly
tasks.kinit_admin(self.master)
result = self.master.run_command(['ipa', 'user-show', 'admin'])
assert 'User login: admin' in result.stdout_text

View File

@@ -0,0 +1,311 @@
# Authors:
# Ana Krivokapic <akrivoka@redhat.com>
#
# Copyright (C) 2013 Red Hat
# see file 'COPYING' for use and warranty information
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import logging
import os
import subprocess
from ipaplatform.paths import paths
import pytest
from ipatests.test_integration.base import IntegrationTest
from ipatests.pytest_plugins.integration import tasks
logger = logging.getLogger(__name__)
CLIENT_KEYTAB = paths.KRB5_KEYTAB
class TestForcedClientReenrollment(IntegrationTest):
"""
Forced client re-enrollment
http://www.freeipa.org/page/V3/Forced_client_re-enrollment#Test_Plan
"""
num_replicas = 1
num_clients = 1
@classmethod
def install(cls, mh):
super(TestForcedClientReenrollment, cls).install(mh)
tasks.install_master(cls.master)
cls.client_dom = cls.clients[0].hostname.split('.', 1)[1]
if cls.client_dom != cls.master.domain.name:
# In cases where client is managed by upstream DNS server we
# overlap its zone so we can save DNS records (e.g. SSHFP) for
# comparison.
servers = [cls.master] + cls.replicas
tasks.add_dns_zone(cls.master, cls.client_dom,
skip_overlap_check=True,
dynamic_update=True,
add_a_record_hosts=servers
)
tasks.install_replica(cls.master, cls.replicas[0], setup_ca=False)
cls.BACKUP_KEYTAB = os.path.join(
cls.master.config.test_dir,
'krb5.keytab'
)
def test_reenroll_with_force_join(self, client):
"""
Client re-enrollment using admin credentials (--force-join)
"""
sshfp_record_pre = self.get_sshfp_record()
self.restore_client()
self.check_client_host_entry()
self.reenroll_client(force_join=True)
sshfp_record_post = self.get_sshfp_record()
assert sshfp_record_pre == sshfp_record_post
def test_reenroll_with_keytab(self, client):
"""
Client re-enrollment using keytab
"""
self.backup_keytab()
sshfp_record_pre = self.get_sshfp_record()
self.restore_client()
self.check_client_host_entry()
self.restore_keytab()
self.reenroll_client(keytab=self.BACKUP_KEYTAB)
sshfp_record_post = self.get_sshfp_record()
assert sshfp_record_pre == sshfp_record_post
def test_reenroll_with_both_force_join_and_keytab(self, client):
"""
Client re-enrollment using both --force-join and --keytab options
"""
self.backup_keytab()
sshfp_record_pre = self.get_sshfp_record()
self.restore_client()
self.check_client_host_entry()
self.restore_keytab()
self.reenroll_client(force_join=True, keytab=self.BACKUP_KEYTAB)
sshfp_record_post = self.get_sshfp_record()
assert sshfp_record_pre == sshfp_record_post
def test_reenroll_to_replica(self, client):
"""
Client re-enrollment using keytab, to a replica
"""
self.backup_keytab()
sshfp_record_pre = self.get_sshfp_record()
self.restore_client()
self.check_client_host_entry()
self.restore_keytab()
self.reenroll_client(keytab=self.BACKUP_KEYTAB, to_replica=True)
sshfp_record_post = self.get_sshfp_record()
assert sshfp_record_pre == sshfp_record_post
def test_try_to_reenroll_with_disabled_host(self, client):
"""
Client re-enrollment using keytab, with disabled host
"""
self.backup_keytab()
self.disable_client_host_entry()
self.restore_client()
self.check_client_host_entry(enabled=False)
self.restore_keytab()
self.reenroll_client(keytab=self.BACKUP_KEYTAB, expect_fail=True)
def test_try_to_reenroll_with_uninstalled_host(self, client):
"""
Client re-enrollment using keytab, with uninstalled host
"""
self.backup_keytab()
self.uninstall_client()
self.restore_client()
self.check_client_host_entry(enabled=False)
self.restore_keytab()
self.reenroll_client(keytab=self.BACKUP_KEYTAB, expect_fail=True)
def test_try_to_reenroll_with_deleted_host(self, client):
"""
Client re-enrollment using keytab, with deleted host
"""
self.backup_keytab()
self.delete_client_host_entry()
self.restore_client()
self.check_client_host_entry(not_found=True)
self.restore_keytab()
self.reenroll_client(keytab=self.BACKUP_KEYTAB, expect_fail=True)
def test_try_to_reenroll_with_incorrect_keytab(self, client):
"""
Client re-enrollment using keytab, with incorrect keytab file
"""
EMPTY_KEYTAB = os.path.join(
self.clients[0].config.test_dir,
'empty.keytab'
)
self.restore_client()
self.check_client_host_entry()
self.clients[0].run_command(['touch', EMPTY_KEYTAB])
self.reenroll_client(keytab=EMPTY_KEYTAB, expect_fail=True)
def uninstall_client(self):
self.clients[0].run_command(
['ipa-client-install', '--uninstall', '-U'],
set_env=False,
raiseonerr=False
)
def restore_client(self):
client = self.clients[0]
client.run_command([
'iptables',
'-A', 'INPUT',
'-j', 'ACCEPT',
'-p', 'tcp',
'--dport', '22'
])
for host in [self.master] + self.replicas:
client.run_command([
'iptables',
'-A', 'INPUT',
'-j', 'REJECT',
'-p', 'all',
'--source', host.ip
])
self.uninstall_client()
client.run_command(['iptables', '-F'])
def reenroll_client(self, keytab=None, to_replica=False, force_join=False,
expect_fail=False):
server = self.replicas[0] if to_replica else self.master
client = self.clients[0]
self.fix_resolv_conf(client, server)
args = [
'ipa-client-install', '-U',
'--server', server.hostname,
'--domain', server.domain.name
]
if force_join:
args.append('--force-join')
if keytab:
args.extend(['--keytab', keytab])
else:
args.extend([
'-p', client.config.admin_name,
'-w', client.config.admin_password
])
result = client.run_command(
args,
set_env=False,
raiseonerr=not expect_fail
)
assert 'IPA Server: %s' % server.hostname in result.stderr_text
if expect_fail:
err_msg = "Kerberos authentication failed: "
assert result.returncode == 1
assert err_msg in result.stderr_text
elif force_join and keytab:
warn_msg = ("Option 'force-join' has no additional effect "
"when used with together with option 'keytab'.")
assert warn_msg in result.stderr_text
def check_client_host_entry(self, enabled=True, not_found=False):
result = self.master.run_command(
['ipa', 'host-show', self.clients[0].hostname],
raiseonerr=not not_found
)
if not_found:
assert result.returncode == 2
assert 'host not found' in result.stderr_text
elif enabled:
assert 'Certificate:' not in result.stdout_text
assert 'Keytab: True' in result.stdout_text
else:
assert 'Certificate:' not in result.stdout_text
assert 'Keytab: False' in result.stdout_text
def disable_client_host_entry(self):
self.master.run_command(
['ipa', 'host-disable', self.clients[0].hostname]
)
@classmethod
def delete_client_host_entry(cls):
try:
cls.master.run_command(
['ipa', 'host-del', cls.clients[0].hostname]
)
except subprocess.CalledProcessError as e:
if e.returncode != 2:
raise
def get_sshfp_record(self):
sshfp_record = ''
client_host = self.clients[0].hostname.split('.')[0]
result = self.master.run_command(
['ipa', 'dnsrecord-show', self.client_dom, client_host]
)
lines = result.stdout_text.splitlines()
for line in lines:
if 'SSHFP record:' in line:
sshfp_record = line.replace('SSHFP record:', '').strip()
assert sshfp_record, 'SSHFP record not found'
sshfp_record = set(sshfp_record.split(', '))
logger.debug("SSHFP record for host %s: %s",
client_host, str(sshfp_record))
return sshfp_record
def backup_keytab(self):
contents = self.clients[0].get_file_contents(CLIENT_KEYTAB)
self.master.put_file_contents(self.BACKUP_KEYTAB, contents)
def restore_keytab(self):
contents = self.master.get_file_contents(self.BACKUP_KEYTAB)
self.clients[0].put_file_contents(self.BACKUP_KEYTAB, contents)
@classmethod
def fix_resolv_conf(cls, client, server):
"""
Put server's ip address at the top of resolv.conf
"""
contents = client.get_file_contents(paths.RESOLV_CONF,
encoding='utf-8')
nameserver = 'nameserver %s\n' % server.ip
if not contents.startswith(nameserver):
contents = nameserver + contents.replace(nameserver, '')
client.put_file_contents(paths.RESOLV_CONF, contents)
@pytest.fixture()
def client(request):
# Here we call "fix_resolv_conf" method before every ipa-client-install so
# we get the client pointing to ipa master as DNS server.
request.cls.fix_resolv_conf(request.cls.clients[0], request.cls.master)
tasks.install_client(request.cls.master, request.cls.clients[0])
def teardown_client():
tasks.uninstall_client(request.cls.clients[0])
request.cls.delete_client_host_entry()
request.addfinalizer(teardown_client)

View File

@@ -0,0 +1,58 @@
#
# Copyright (C) 2016 FreeIPA Contributors see COPYING for license
#
import six
from ipatests.pytest_plugins.integration import tasks
from ipatests.test_integration.base import IntegrationTest
from ipaplatform.paths import paths
if six.PY3:
unicode = str
class TestHttpKdcProxy(IntegrationTest):
topology = "line"
num_clients = 1
@classmethod
def install(cls, mh):
super(TestHttpKdcProxy, cls).install(mh)
# Block access from client to master's port 88
cls.clients[0].run_command([
'iptables', '-A', 'OUTPUT', '-p', 'tcp',
'--dport', '88', '-j', 'DROP'])
cls.clients[0].run_command([
'iptables', '-A', 'OUTPUT', '-p', 'udp',
'--dport', '88', '-j', 'DROP'])
cls.clients[0].run_command([
'ip6tables', '-A', 'OUTPUT', '-p', 'tcp',
'--dport', '88', '-j', 'DROP'])
cls.clients[0].run_command([
'ip6tables', '-A', 'OUTPUT', '-p', 'udp',
'--dport', '88', '-j', 'DROP'])
# configure client
cls.clients[0].run_command(
"sed -i 's/ kdc = .*$/ kdc = https:\/\/%s\/KdcProxy/' %s" % (
cls.master.hostname, paths.KRB5_CONF)
)
cls.clients[0].run_command(
"sed -i 's/master_kdc = .*$/master_kdc"
" = https:\/\/%s\/KdcProxy/' %s" % (
cls.master.hostname, paths.KRB5_CONF)
)
# Workaround for https://fedorahosted.org/freeipa/ticket/6443
cls.clients[0].run_command(['systemctl', 'restart', 'sssd.service'])
# End of workaround
@classmethod
def uninstall(cls, mh):
super(TestHttpKdcProxy, cls).uninstall(mh)
cls.clients[0].run_command(['iptables', '-F'])
def test_http_kdc_proxy_works(self):
result = tasks.kinit_admin(self.clients[0], raiseonerr=False)
assert(result.returncode == 0), (
"Unable to kinit using KdcProxy: %s" % result.stderr_text
)

View File

@@ -0,0 +1,238 @@
#
# Copyright (C) 2016 FreeIPA Contributors see COPYING for license
#
import os
import re
import string
from ipatests.pytest_plugins.integration import tasks
from ipatests.test_integration.base import IntegrationTest
from ipatests.pytest_plugins.integration.env_config import get_global_config
from ipaplatform.paths import paths
config = get_global_config()
class TestCertsInIDOverrides(IntegrationTest):
topology = "line"
num_ad_domains = 1
adview = 'Default Trust View'
cert_re = re.compile('Certificate: (?P<cert>.*?)\\s+.*')
adcert1 = 'MyCert1'
adcert2 = 'MyCert2'
adcert1_file = adcert1 + '.crt'
adcert2_file = adcert2 + '.crt'
@classmethod
def uninstall(cls, mh):
super(TestCertsInIDOverrides, cls).uninstall(mh)
cls.master.run_command(['rm', '-rf', cls.reqdir], raiseonerr=False)
@classmethod
def install(cls, mh):
super(TestCertsInIDOverrides, cls).install(mh)
cls.ad = config.ad_domains[0].ads[0]
cls.ad_domain = cls.ad.domain.name
cls.aduser = "testuser@%s" % cls.ad_domain
master = cls.master
# A setup for test_dbus_user_lookup
master.run_command(['dnf', 'install', '-y', 'sssd-dbus'],
raiseonerr=False)
# The tasks.modify_sssd_conf way did not work because
# sssd_domain.set_option knows nothing about 'services' parameter of
# the sssd config file. Therefore I am using sed approach
master.run_command(
"sed -i '/^services/ s/$/, ifp/' %s" % paths.SSSD_CONF)
master.run_command(
"sed -i 's/= 7/= 0xFFF0/' %s" % paths.SSSD_CONF, raiseonerr=False)
master.run_command(['systemctl', 'restart', 'sssd.service'])
# End of setup for test_dbus_user_lookup
# AD-related stuff
tasks.install_adtrust(master)
tasks.sync_time(master, cls.ad)
tasks.establish_trust_with_ad(cls.master, cls.ad_domain,
extra_args=['--range-type',
'ipa-ad-trust'])
cls.reqdir = os.path.join(master.config.test_dir, "certs")
cls.reqfile1 = os.path.join(cls.reqdir, "test1.csr")
cls.reqfile2 = os.path.join(cls.reqdir, "test2.csr")
cls.pwname = os.path.join(cls.reqdir, "pwd")
# Create a NSS database folder
master.run_command(['mkdir', cls.reqdir], raiseonerr=False)
# Create an empty password file
master.run_command(["touch", cls.pwname], raiseonerr=False)
# Initialize NSS database
tasks.run_certutil(master, ["-N", "-f", cls.pwname], cls.reqdir)
# Now generate self-signed certs for a windows user
stdin_text = string.digits+string.ascii_letters[2:] + '\n'
tasks.run_certutil(master, ['-S', '-s',
"cn=%s,dc=ad,dc=test" % cls.adcert1, '-n',
cls.adcert1, '-x', '-t', 'CT,C,C', '-v',
'120', '-m', '1234'],
cls.reqdir, stdin=stdin_text)
tasks.run_certutil(master, ['-S', '-s',
"cn=%s,dc=ad,dc=test" % cls.adcert2, '-n',
cls.adcert2, '-x', '-t', 'CT,C,C', '-v',
'120', '-m', '1234'],
cls.reqdir, stdin=stdin_text)
# Export the previously generated cert
tasks.run_certutil(master, ['-L', '-n', cls.adcert1, '-a', '>',
cls.adcert1_file], cls.reqdir)
tasks.run_certutil(master, ['-L', '-n', cls.adcert2, '-a', '>',
cls.adcert2_file], cls.reqdir)
cls.cert1_base64 = cls.master.run_command(
"openssl x509 -outform der -in %s | base64 -w 0" % cls.adcert1_file
).stdout_text
cls.cert2_base64 = cls.master.run_command(
"openssl x509 -outform der -in %s | base64 -w 0" % cls.adcert2_file
).stdout_text
cls.cert1_pem = cls.master.run_command(
"openssl x509 -in %s -outform pem" % cls.adcert1_file
).stdout_text
cls.cert2_pem = cls.master.run_command(
"openssl x509 -in %s -outform pem" % cls.adcert2_file
).stdout_text
def test_certs_in_idoverrides_ad_users(self):
"""
http://www.freeipa.org/page/V4/Certs_in_ID_overrides/Test_Plan
#Test_case:_Manipulate_certificate_in_ID_override_entry
"""
master = self.master
master.run_command(['ipa', 'idoverrideuser-add',
self.adview, self.aduser])
master.run_command(['ipa', 'idoverrideuser-add-cert',
self.adview, self.aduser,
"--certificate=%s" % self.cert1_base64])
master.run_command(['ipa', 'idoverrideuser-add-cert',
self.adview, self.aduser,
"--certificate=%s" % self.cert2_base64])
result = master.run_command(['ipa', 'idoverrideuser-show',
self.adview, self.aduser])
assert(self.cert1_base64 in result.stdout_text and
self.cert2_base64 in result.stdout_text), (
"idoverrideuser-show does not show all user certificates")
master.run_command(['ipa', 'idoverrideuser-remove-cert',
self.adview, self.aduser,
"--certificate=%s" % self.cert2_base64])
def test_dbus_user_lookup(self):
"""
http://www.freeipa.org/page/V4/Certs_in_ID_overrides/Test_Plan
#Test_case:_User_lookup_by_certificate
"""
master = self.master
userpath_re = re.compile('.*object path "(.*?)".*')
result0 = master.run_command([
'dbus-send', '--system', '--print-reply',
'--dest=org.freedesktop.sssd.infopipe',
'/org/freedesktop/sssd/infopipe/Users',
'org.freedesktop.sssd.infopipe.Users.FindByCertificate',
"string:%s" % self.cert1_pem])
assert("object path" in result0.stdout_text), (
"command output did not contain expected"
"string:\n\n%s" % result0.stdout_text)
userpath = userpath_re.findall(result0.stdout_text)[0]
result1 = master.run_command(
"dbus-send --system --print-reply"
" --dest=org.freedesktop.sssd.infopipe"
" %s org.freedesktop.DBus.Properties.Get"
" string:\"org.freedesktop.sssd.infopipe.Users.User\""
" string:\"name\"" % userpath, raiseonerr=False)
assert(self.aduser in result1.stdout_text)
result2 = master.run_command(
"dbus-send --system --print-reply"
" --dest=org.freedesktop.sssd.infopipe"
" %s org.freedesktop.DBus.Properties.GetAll"
" string:\"org.freedesktop.sssd.infopipe.Users.User\"" % userpath
)
assert('dict entry' in result2.stdout_text)
class TestRulesWithServicePrincipals(IntegrationTest):
"""
https://fedorahosted.org/freeipa/ticket/6146
"""
topology = 'star'
num_replicas = 0
service_certprofile = 'caIPAserviceCert'
caacl = 'test_caacl'
keytab = "replica.keytab"
csr = "my.csr"
csr_conf = "replica.cnf"
@classmethod
def prepare_config(cls):
template = """
req_extensions = v3_req
distinguished_name = req_distinguished_name
[req_distinguished_name]
commonName = %s
[ v3_req ]
# Extensions to add to a certificate request
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
subjectAltName = @alt_names
[alt_names]
DNS.1 = %s
DNS.2 = %s
EOF
"""
contents = template % (cls.replica, cls.replica, cls.master.hostname)
cls.master.run_command("cat <<EOF > %s\n%s" % (cls.csr_conf, contents))
@classmethod
def install(cls, mh):
super(TestRulesWithServicePrincipals, cls).install(mh)
master = cls.master
tasks.kinit_admin(master)
cls.replica = "replica.%s" % master.domain.name
master.run_command(['ipa', 'host-add', cls.replica, '--force'])
cls.service_name = "svc/%s" % master.hostname
cls.replica_service_name = "svc/%s" % cls.replica
master.run_command("ipa service-add %s" % cls.service_name)
master.run_command("ipa service-add %s --force" %
cls.replica_service_name)
master.run_command("ipa service-add-host %s --hosts %s" % (
cls.service_name, cls.replica))
master.run_command("ipa caacl-add %s --desc \"test\"" % cls.caacl)
master.run_command("ipa caacl-add-host %s --hosts %s" % (cls.caacl,
cls.replica))
master.run_command("ipa caacl-add-service %s --services"
" svc/`hostname`" % cls.caacl)
master.run_command("ipa-getkeytab -p host/%s@%s -k %s" % (
cls.replica, master.domain.realm, cls.keytab))
master.run_command("kinit -kt %s host/%s" % (cls.keytab, cls.replica))
# Prepare a CSR
cls.prepare_config()
stdin_text = "qwerty\nqwerty\n%s\n" % cls.replica
master.run_command(['openssl', 'req', '-config', cls.csr_conf, '-new',
'-out', cls.csr], stdin_text=stdin_text)
def test_rules_with_service_principals(self):
result = self.master.run_command(['ipa', 'cert-request', self.csr,
'--principal', "svc/%s@%s" % (
self.replica,
self.master.domain.realm),
'--profile-id',
self.service_certprofile],
raiseonerr=False)
assert(result.returncode == 0), (
'Failed to add a cert to custom certprofile')

View File

@@ -0,0 +1,318 @@
#
# Copyright (C) 2015 FreeIPA Contributors see COPYING for license
#
"""
Module provides tests which testing ability of various subsystems to be
installed.
"""
import pytest
from ipalib.constants import DOMAIN_LEVEL_0
from ipatests.pytest_plugins.integration.env_config import get_global_config
from ipatests.test_integration.base import IntegrationTest
from ipatests.pytest_plugins.integration import tasks
config = get_global_config()
class InstallTestBase1(IntegrationTest):
num_replicas = 3
topology = 'star'
@classmethod
def install(cls, mh):
tasks.install_master(cls.master, setup_dns=False)
def test_replica0_ca_less_install(self):
tasks.install_replica(self.master, self.replicas[0], setup_ca=False)
def test_replica0_ipa_ca_install(self):
tasks.install_ca(self.replicas[0])
def test_replica0_ipa_kra_install(self):
tasks.install_kra(self.replicas[0], first_instance=True)
def test_replica0_ipa_dns_install(self):
tasks.install_dns(self.replicas[0])
def test_replica1_with_ca_install(self):
tasks.install_replica(self.master, self.replicas[1], setup_ca=True)
def test_replica1_ipa_kra_install(self):
tasks.install_kra(self.replicas[1])
def test_replica1_ipa_dns_install(self):
tasks.install_dns(self.replicas[1])
def test_replica2_with_ca_kra_install(self):
tasks.install_replica(self.master, self.replicas[2], setup_ca=True,
setup_kra=True)
def test_replica2_ipa_dns_install(self):
tasks.install_dns(self.replicas[2])
class InstallTestBase2(IntegrationTest):
num_replicas = 3
topology = 'star'
@classmethod
def install(cls, mh):
tasks.install_master(cls.master, setup_dns=False)
def test_replica0_with_ca_kra_dns_install(self):
tasks.install_replica(self.master, self.replicas[0], setup_ca=True,
setup_kra=True, setup_dns=True)
def test_replica1_with_ca_dns_install(self):
tasks.install_replica(self.master, self.replicas[1], setup_ca=True,
setup_dns=True)
def test_replica1_ipa_kra_install(self):
tasks.install_kra(self.replicas[1])
def test_replica2_with_dns_install(self):
tasks.install_replica(self.master, self.replicas[2], setup_ca=False,
setup_dns=True)
def test_replica2_ipa_ca_install(self):
tasks.install_ca(self.replicas[2])
def test_replica2_ipa_kra_install(self):
tasks.install_kra(self.replicas[2])
class ADTrustInstallTestBase(IntegrationTest):
"""
Base test for builtin AD trust installation im combination with other
components
"""
num_replicas = 2
topology = 'star'
@classmethod
def install(cls, mh):
tasks.install_master(cls.master, setup_dns=False)
def install_replica(self, replica, **kwargs):
tasks.install_replica(self.master, replica, setup_adtrust=True,
**kwargs)
def test_replica0_only_adtrust(self):
self.install_replica(self.replicas[0], setup_ca=False)
def test_replica1_all_components_adtrust(self):
self.install_replica(self.replicas[1], setup_ca=True)
##
# Master X Replicas installation tests
##
@pytest.mark.xfail(reason="FreeIPA ticket 7008")
class TestInstallWithCA1(InstallTestBase1):
@classmethod
def install(cls, mh):
tasks.install_master(cls.master, setup_dns=False)
@pytest.mark.skipif(config.domain_level == DOMAIN_LEVEL_0,
reason='does not work on DOMAIN_LEVEL_0 by design')
def test_replica1_ipa_kra_install(self):
super(TestInstallWithCA1, self).test_replica1_ipa_kra_install()
@pytest.mark.skipif(config.domain_level == DOMAIN_LEVEL_0,
reason='does not work on DOMAIN_LEVEL_0 by design')
def test_replica2_with_ca_kra_install(self):
super(TestInstallWithCA1, self).test_replica2_with_ca_kra_install()
@pytest.mark.skipif(config.domain_level == DOMAIN_LEVEL_0,
reason='does not work on DOMAIN_LEVEL_0 by design')
def test_replica2_ipa_dns_install(self):
super(TestInstallWithCA1, self).test_replica2_ipa_dns_install()
@pytest.mark.xfail(reason="FreeIPA ticket 7008")
class TestInstallWithCA2(InstallTestBase2):
@classmethod
def install(cls, mh):
tasks.install_master(cls.master, setup_dns=False)
@pytest.mark.skipif(config.domain_level == DOMAIN_LEVEL_0,
reason='does not work on DOMAIN_LEVEL_0 by design')
def test_replica0_with_ca_kra_dns_install(self):
super(TestInstallWithCA2, self).test_replica0_with_ca_kra_dns_install()
@pytest.mark.skipif(config.domain_level == DOMAIN_LEVEL_0,
reason='does not work on DOMAIN_LEVEL_0 by design')
def test_replica1_ipa_kra_install(self):
super(TestInstallWithCA2, self).test_replica1_ipa_kra_install()
@pytest.mark.skipif(config.domain_level == DOMAIN_LEVEL_0,
reason='does not work on DOMAIN_LEVEL_0 by design')
def test_replica2_ipa_kra_install(self):
super(TestInstallWithCA2, self).test_replica2_ipa_kra_install()
class TestInstallWithCA_KRA1(InstallTestBase1):
@classmethod
def install(cls, mh):
tasks.install_master(cls.master, setup_dns=False, setup_kra=True)
def test_replica0_ipa_kra_install(self):
tasks.install_kra(self.replicas[0], first_instance=False)
class TestInstallWithCA_KRA2(InstallTestBase2):
@classmethod
def install(cls, mh):
tasks.install_master(cls.master, setup_dns=False, setup_kra=True)
@pytest.mark.xfail(reason="FreeIPA ticket 7008")
class TestInstallWithCA_DNS1(InstallTestBase1):
@classmethod
def install(cls, mh):
tasks.install_master(cls.master, setup_dns=True)
@pytest.mark.skipif(config.domain_level == DOMAIN_LEVEL_0,
reason='does not work on DOMAIN_LEVEL_0 by design')
def test_replica1_ipa_kra_install(self):
super(TestInstallWithCA_DNS1, self).test_replica1_ipa_kra_install()
@pytest.mark.skipif(config.domain_level == DOMAIN_LEVEL_0,
reason='does not work on DOMAIN_LEVEL_0 by design')
def test_replica2_with_ca_kra_install(self):
super(TestInstallWithCA_DNS1, self).test_replica2_with_ca_kra_install()
@pytest.mark.skipif(config.domain_level == DOMAIN_LEVEL_0,
reason='does not work on DOMAIN_LEVEL_0 by design')
def test_replica2_ipa_dns_install(self):
super(TestInstallWithCA_DNS1, self).test_replica2_ipa_dns_install()
@pytest.mark.xfail(reason="FreeIPA ticket 7008")
class TestInstallWithCA_DNS2(InstallTestBase2):
@classmethod
def install(cls, mh):
tasks.install_master(cls.master, setup_dns=True)
@pytest.mark.skipif(config.domain_level == DOMAIN_LEVEL_0,
reason='does not work on DOMAIN_LEVEL_0 by design')
def test_replica0_with_ca_kra_dns_install(self):
super(
TestInstallWithCA_DNS2, self
).test_replica0_with_ca_kra_dns_install()
@pytest.mark.skipif(config.domain_level == DOMAIN_LEVEL_0,
reason='does not work on DOMAIN_LEVEL_0 by design')
def test_replica1_ipa_kra_install(self):
super(TestInstallWithCA_DNS2, self).test_replica1_ipa_kra_install()
@pytest.mark.skipif(config.domain_level == DOMAIN_LEVEL_0,
reason='does not work on DOMAIN_LEVEL_0 by design')
def test_replica2_ipa_kra_install(self):
super(TestInstallWithCA_DNS2, self).test_replica2_ipa_kra_install()
@pytest.mark.cs_acceptance
class TestInstallWithCA_KRA_DNS1(InstallTestBase1):
@classmethod
def install(cls, mh):
tasks.install_master(cls.master, setup_dns=True, setup_kra=True)
def test_replica0_ipa_kra_install(self):
tasks.install_kra(self.replicas[0], first_instance=False)
class TestInstallWithCA_KRA_DNS2(InstallTestBase2):
@classmethod
def install(cls, mh):
tasks.install_master(cls.master, setup_dns=True, setup_kra=True)
class TestADTrustInstall(ADTrustInstallTestBase):
"""
Tests built-in AD trust installation in various combinations (see the base
class for more details) against plain IPA master (no DNS, no KRA, no AD
trust)
"""
pass
class TestADTrustInstallWithDNS_KRA_ADTrust(ADTrustInstallTestBase):
"""
Tests built-in AD trust installation in various combinations (see the base
class for more details) against fully equipped (DNS, CA, KRA, ADtrust)
master. Additional two test cases were added to test interplay including
KRA installer
"""
@classmethod
def install(cls, mh):
tasks.install_master(cls.master, setup_dns=True, setup_kra=True,
setup_adtrust=True)
def test_replica1_all_components_adtrust(self):
self.install_replica(self.replicas[1], setup_ca=True, setup_kra=True)
##
# Rest of master installation tests
##
class TestInstallMaster(IntegrationTest):
num_replicas = 0
@classmethod
def install(cls, mh):
pass
def test_install_master(self):
tasks.install_master(self.master, setup_dns=False)
def test_install_kra(self):
tasks.install_kra(self.master, first_instance=True)
def test_install_dns(self):
tasks.install_dns(self.master)
class TestInstallMasterKRA(IntegrationTest):
num_replicas = 0
@classmethod
def install(cls, mh):
pass
def test_install_master(self):
tasks.install_master(self.master, setup_dns=False, setup_kra=True)
def test_install_dns(self):
tasks.install_dns(self.master)
class TestInstallMasterDNS(IntegrationTest):
num_replicas = 0
@classmethod
def install(cls, mh):
pass
def test_install_master(self):
tasks.install_master(self.master, setup_dns=True)
def test_install_kra(self):
tasks.install_kra(self.master, first_instance=True)

View File

@@ -0,0 +1,191 @@
# Authors:
# Ana Krivokapic <akrivoka@redhat.com>
#
# Copyright (C) 2013 Red Hat
# see file 'COPYING' for use and warranty information
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from ipatests.test_integration.base import IntegrationTest
from ipatests.pytest_plugins.integration import tasks
class TestKerberosFlags(IntegrationTest):
"""
Test Kerberos Flags
http://www.freeipa.org/page/V3/Kerberos_Flags#Test_Plan
"""
topology = 'line'
num_clients = 1
def test_set_flag_with_host_add(self):
host = 'host.example.com'
host_service = 'host/%s' % host
host_keytab = '/tmp/host.keytab'
for trusted in (True, False, None):
self.add_object('host', host, trusted=trusted, force=True)
self.check_flag_cli('host', host, trusted=trusted)
self.rekinit()
self.getkeytab(host_service, host_keytab)
self.kvno(host_service)
self.check_flag_klist(host_service, trusted=trusted)
self.del_object('host', host)
def test_set_and_clear_flag_with_host_mod(self):
client_hostname = self.clients[0].hostname
host_service = 'host/%s' % client_hostname
self.kvno(host_service)
self.check_flag_cli('host', client_hostname, trusted=False)
self.check_flag_klist(host_service, trusted=False)
for trusted in (True, False):
self.mod_object_cli('host', client_hostname, trusted=trusted)
self.check_flag_cli('host', client_hostname, trusted=trusted)
self.rekinit()
self.kvno(host_service)
self.check_flag_klist(host_service, trusted=trusted)
for trusted in (True, False):
self.mod_service_kadmin_local(host_service, trusted=trusted)
self.check_flag_cli('host', client_hostname, trusted=trusted)
self.rekinit()
self.kvno(host_service)
self.check_flag_klist(host_service, trusted=trusted)
def test_set_flag_with_service_add(self):
ftp_service = 'ftp/%s' % self.master.hostname
ftp_keytab = '/tmp/ftp.keytab'
for trusted in (True, False, None):
self.add_object('service', ftp_service, trusted=trusted)
self.check_flag_cli('service', ftp_service, trusted=trusted)
self.rekinit()
self.getkeytab(ftp_service, ftp_keytab)
self.kvno(ftp_service)
self.check_flag_klist(ftp_service, trusted=trusted)
self.del_object('service', ftp_service)
def test_set_and_clear_flag_with_service_mod(self):
http_service = 'HTTP/%s' % self.master.hostname
self.kvno(http_service)
self.check_flag_cli('service', http_service, trusted=False)
self.check_flag_klist(http_service, trusted=False)
for trusted in (True, False):
self.mod_object_cli('service', http_service, trusted=trusted)
self.check_flag_cli('service', http_service, trusted=trusted)
self.rekinit()
self.kvno(http_service)
self.check_flag_klist(http_service, trusted=trusted)
for trusted in (True, False):
self.mod_service_kadmin_local(http_service, trusted=trusted)
self.check_flag_cli('service', http_service, trusted=trusted)
self.rekinit()
self.kvno(http_service)
self.check_flag_klist(http_service, trusted=trusted)
def test_try_to_set_flag_using_unexpected_values(self):
http_service = 'HTTP/%s' % self.master.hostname
invalid_values = ['blah', 'yes', 'y', '2', '1.0', '$']
for v in invalid_values:
self.mod_object_cli('service', http_service, trusted=v,
expect_fail=True)
def add_object(self, object_type, object_id, trusted=None, force=False):
args = ['ipa', '%s-add' % object_type, object_id]
if trusted is True:
args.extend(['--ok-as-delegate', '1'])
elif trusted is False:
args.extend(['--ok-as-delegate', '0'])
if force:
args.append('--force')
self.master.run_command(args)
def del_object(self, object_type, object_id):
self.master.run_command(['ipa', '%s-del' % object_type, object_id])
def mod_object_cli(self, object_type, object_id, trusted,
expect_fail=False):
args = ['ipa', '%s-mod' % object_type, object_id]
if trusted is True:
args.extend(['--ok-as-delegate', '1'])
elif trusted is False:
args.extend(['--ok-as-delegate', '0'])
else:
args.extend(['--ok-as-delegate', trusted])
result = self.master.run_command(args, raiseonerr=not expect_fail)
if expect_fail:
stderr_text = "invalid 'ipakrbokasdelegate': must be True or False"
assert result.returncode == 1
assert stderr_text in result.stderr_text
def mod_service_kadmin_local(self, service, trusted):
sign = '+' if trusted else '-'
stdin_text = '\n'.join([
'modify_principal %sok_as_delegate %s' % (sign, service),
'q',
''
])
self.master.run_command('kadmin.local', stdin_text=stdin_text)
def check_flag_cli(self, object_type, object_id, trusted):
result = self.master.run_command(
['ipa', '%s-show' % object_type, object_id, '--all']
)
if trusted:
assert 'Trusted for delegation: True' in result.stdout_text
else:
assert 'Trusted for delegation: False' in result.stdout_text
def check_flag_klist(self, service, trusted):
result = self.master.run_command(['klist', '-f'])
output_lines = result.stdout_text.split('\n')
flags = ''
for line, next_line in zip(output_lines, output_lines[1:]):
if service in line:
flags = next_line.replace('Flags:', '').strip()
if trusted:
assert 'O' in flags
else:
assert 'O' not in flags
def rekinit(self):
self.master.run_command(['kdestroy'])
tasks.kinit_admin(self.master)
def getkeytab(self, service, keytab):
result = self.master.run_command([
'ipa-getkeytab',
'-s', self.master.hostname,
'-p', service,
'-k', keytab
])
assert 'Keytab successfully retrieved' in result.stderr_text
def kvno(self, service):
self.master.run_command(['kvno', service])

View File

@@ -0,0 +1,566 @@
# Authors:
# Tomas Babej <tbabej@redhat.com>
#
# Copyright (C) 2013 Red Hat
# see file 'COPYING' for use and warranty information
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# FIXME: Pylint errors
# pylint: disable=no-member
import os
import re
import nose
from ipaplatform.paths import paths
from ipatests.pytest_plugins.integration import tasks
# importing test_trust under different name to avoid nose executing the test
# base class imported from this module
from ipatests.test_integration import test_trust as trust_tests
class BaseTestLegacyClient(object):
"""
Tests legacy client support.
"""
advice_id = None
backup_files = ['/etc/sysconfig/authconfig',
'/etc/pam.d',
'/etc/openldap/cacerts',
'/etc/openldap/ldap.conf',
'/etc/nsswitch.conf',
paths.SSSD_CONF]
homedir_template = "/home/{domain}/{username}"
required_extra_roles = ()
optional_extra_roles = ()
# Actual test classes need to override these attributes to set the expected
# values on the UID and GID results, since this varies with the usage of the
# POSIX and non-POSIX ID ranges
testuser_uid_regex = None
testuser_gid_regex = None
subdomain_testuser_uid_regex = None
subdomain_testuser_gid_regex = None
treedomain_testuser_uid_regex = None
treedomain_testuser_gid_regex = None
# To allow custom validation dependent on the trust type
posix_trust = False
def test_apply_advice(self):
# Obtain the advice from the server
tasks.kinit_admin(self.master)
result = self.master.run_command(['ipa-advise', self.advice_id])
advice = result.stdout_text
# Apply the advice on the legacy client
advice_path = os.path.join(self.legacy_client.config.test_dir,
'advice.sh')
self.legacy_client.put_file_contents(advice_path, advice)
result = self.legacy_client.run_command(['bash', '-x', '-e',
advice_path])
# Restart SSHD to load new PAM configuration
self.legacy_client.run_command([paths.SBIN_SERVICE, 'sshd', 'restart'])
def clear_sssd_caches(self):
tasks.clear_sssd_cache(self.master)
tasks.clear_sssd_cache(self.legacy_client)
def test_getent_ipa_user(self):
self.clear_sssd_caches()
result = self.legacy_client.run_command(['getent', 'passwd', 'admin'])
admin_regex = "admin:\*:(\d+):(\d+):"\
"Administrator:/home/admin:/bin/bash"
assert re.search(admin_regex, result.stdout_text)
def test_getent_ipa_group(self):
self.clear_sssd_caches()
result = self.legacy_client.run_command(['getent', 'group', 'admins'])
admin_group_regex = "admins:\*:(\d+):admin"
assert re.search(admin_group_regex, result.stdout_text)
def test_id_ipa_user(self):
self.clear_sssd_caches()
result = self.legacy_client.run_command(['id', 'admin'])
uid_regex = "uid=(\d+)\(admin\)"
gid_regex = "gid=(\d+)\(admins\)"
groups_regex = "groups=(\d+)\(admins\)"
assert re.search(uid_regex, result.stdout_text)
assert re.search(gid_regex, result.stdout_text)
assert re.search(groups_regex, result.stdout_text)
def test_getent_ad_user(self):
self.clear_sssd_caches()
testuser = 'testuser@%s' % self.ad.domain.name
result = self.legacy_client.run_command(['getent', 'passwd', testuser])
testuser_regex = "testuser@%s:\*:%s:%s:"\
"Test User:%s:/bin/sh"\
% (re.escape(self.ad.domain.name),
self.testuser_uid_regex,
self.testuser_gid_regex,
self.homedir_template.format(
username='testuser',
domain=re.escape(self.ad.domain.name))
)
assert re.search(testuser_regex, result.stdout_text)
def test_getent_ad_group(self):
self.clear_sssd_caches()
testgroup = 'testgroup@%s' % self.ad.domain.name
result = self.legacy_client.run_command(['getent', 'group', testgroup])
testgroup_regex = "%s:\*:%s:" % (testgroup, self.testuser_gid_regex)
assert re.search(testgroup_regex, result.stdout_text)
def test_id_ad_user(self):
self.clear_sssd_caches()
testuser = 'testuser@%s' % self.ad.domain.name
testgroup = 'testgroup@%s' % self.ad.domain.name
result = self.legacy_client.run_command(['id', testuser])
# Only for POSIX trust testing does the testuser belong to the
# testgroup
group_name = '\(%s\)' % testgroup if self.posix_trust else ''
uid_regex = "uid=%s\(%s\)" % (self.testuser_uid_regex, testuser)
gid_regex = "gid=%s%s" % (self.testuser_gid_regex, group_name)
groups_regex = "groups=%s%s" % (self.testuser_gid_regex, group_name)
assert re.search(uid_regex, result.stdout_text)
assert re.search(gid_regex, result.stdout_text)
assert re.search(groups_regex, result.stdout_text)
def test_login_ipa_user(self):
if not self.master.transport.file_exists('/usr/bin/sshpass'):
raise nose.SkipTest('Package sshpass not available on %s'
% self.master.hostname)
result = self.master.run_command(
'sshpass -p %s '
'ssh '
'-o StrictHostKeyChecking=no '
'-l admin '
'%s '
'"echo test"' %
(self.legacy_client.config.admin_password,
self.legacy_client.hostname))
assert "test" in result.stdout_text
def test_login_ad_user(self):
if not self.master.transport.file_exists('/usr/bin/sshpass'):
raise nose.SkipTest('Package sshpass not available on %s'
% self.master.hostname)
testuser = 'testuser@%s' % self.ad.domain.name
result = self.master.run_command(
'sshpass -p Secret123 '
'ssh '
'-o StrictHostKeyChecking=no '
'-l %s '
'%s '
'"echo test"' %
(testuser, self.legacy_client.hostname))
assert "test" in result.stdout_text
def test_login_disabled_ipa_user(self):
if not self.master.transport.file_exists('/usr/bin/sshpass'):
raise nose.SkipTest('Package sshpass not available on %s'
% self.master.hostname)
self.clear_sssd_caches()
result = self.master.run_command(
'sshpass -p %s '
'ssh '
'-o StrictHostKeyChecking=no '
'-l disabledipauser '
'%s '
'"echo test"'
% (self.legacy_client.config.admin_password,
self.legacy_client.external_hostname),
raiseonerr=False)
assert result.returncode != 0
def test_login_disabled_ad_user(self):
if not self.master.transport.file_exists('/usr/bin/sshpass'):
raise nose.SkipTest('Package sshpass not available on %s'
% self.master.hostname)
testuser = 'disabledaduser@%s' % self.ad.domain.name
result = self.master.run_command(
'sshpass -p Secret123 '
'ssh '
'-o StrictHostKeyChecking=no '
'-l %s '
'%s '
'"echo test"' %
(testuser, self.legacy_client.external_hostname),
raiseonerr=False)
assert result.returncode != 0
def test_getent_subdomain_ad_user(self):
if not self.ad_subdomain:
raise nose.SkipTest('AD for the subdomain is not available.')
self.clear_sssd_caches()
testuser = 'subdomaintestuser@%s' % self.ad_subdomain
result = self.legacy_client.run_command(['getent', 'passwd', testuser])
testuser_regex = "subdomaintestuser@%s:\*:%s:%s:"\
"Subdomaintest User:%s:"\
"/bin/sh"\
% (re.escape(self.ad_subdomain),
self.subdomain_testuser_uid_regex,
self.subdomain_testuser_gid_regex,
self.homedir_template.format(
username='subdomaintestuser',
domain=re.escape(self.ad_subdomain))
)
assert re.search(testuser_regex, result.stdout_text)
def test_getent_subdomain_ad_group(self):
if not self.ad_subdomain:
raise nose.SkipTest('AD for the subdomain is not available.')
self.clear_sssd_caches()
testgroup = 'subdomaintestgroup@%s' % self.ad_subdomain
result = self.legacy_client.run_command(['getent', 'group', testgroup])
testgroup_stdout = "%s:\*:%s:" % (testgroup,
self.subdomain_testuser_gid_regex)
assert re.search(testgroup_stdout, result.stdout_text)
def test_id_subdomain_ad_user(self):
if not self.ad_subdomain:
raise nose.SkipTest('AD for the subdomain is not available.')
self.clear_sssd_caches()
testuser = 'subdomaintestuser@%s' % self.ad_subdomain
testgroup = 'subdomaintestgroup@%s' % self.ad_subdomain
result = self.legacy_client.run_command(['id', testuser])
# Only for POSIX trust testing does the testuser belong to the
# testgroup
group_name = '\(%s\)' % testgroup if self.posix_trust else ''
uid_regex = "uid=%s\(%s\)" % (self.subdomain_testuser_uid_regex,
testuser)
gid_regex = "gid=%s%s" % (self.subdomain_testuser_gid_regex,
group_name)
groups_regex = "groups=%s%s" % (self.subdomain_testuser_gid_regex,
group_name)
assert re.search(uid_regex, result.stdout_text)
assert re.search(gid_regex, result.stdout_text)
assert re.search(groups_regex, result.stdout_text)
def test_login_subdomain_ad_user(self):
if not self.ad_subdomain:
raise nose.SkipTest('AD for the subdomain is not available.')
if not self.master.transport.file_exists('/usr/bin/sshpass'):
raise nose.SkipTest('Package sshpass not available on %s'
% self.master.hostname)
testuser = 'subdomaintestuser@%s' % self.ad_subdomain
result = self.master.run_command(
'sshpass -p Secret123 '
'ssh '
'-o StrictHostKeyChecking=no '
'-l %s '
'%s '
'"echo test"' %
(testuser, self.legacy_client.external_hostname))
assert "test" in result.stdout_text
def test_login_disabled_subdomain_ad_user(self):
if not self.ad_subdomain:
raise nose.SkipTest('AD for the subdomain is not available.')
if not self.master.transport.file_exists('/usr/bin/sshpass'):
raise nose.SkipTest('Package sshpass not available on %s'
% self.master.hostname)
testuser = 'subdomaindisabledaduser@%s' % self.ad_subdomain
result = self.master.run_command(
'sshpass -p Secret123 '
'ssh '
'-o StrictHostKeyChecking=no '
'-l %s '
'%s '
'"echo test"' %
(testuser, self.legacy_client.external_hostname),
raiseonerr=False)
assert result.returncode != 0
def test_getent_treedomain_ad_user(self):
if not self.ad_treedomain:
raise nose.SkipTest('AD tree root domain is not available.')
self.clear_sssd_caches()
testuser = 'treetestuser@{0}'.format(self.ad_treedomain)
result = self.legacy_client.run_command(['getent', 'passwd', testuser])
testuser_regex = ("treetestuser@{0}:\*:{1}:{2}:TreeTest User:"
"/home/{0}/treetestuser:/bin/sh".format(
re.escape(self.ad_treedomain),
self.treedomain_testuser_uid_regex,
self.treedomain_testuser_gid_regex))
assert re.search(testuser_regex, result.stdout_text)
def test_getent_treedomain_ad_group(self):
if not self.ad_treedomain:
raise nose.SkipTest('AD tree root domain is not available')
self.clear_sssd_caches()
testgroup = 'treetestgroup@{0}'.format(self.ad_treedomain)
result = self.legacy_client.run_command(['getent', 'group', testgroup])
testgroup_stdout = "{0}:\*:{1}:".format(
testgroup, self.treedomain_testuser_gid_regex)
assert re.search(testgroup_stdout, result.stdout_text)
def test_id_treedomain_ad_user(self):
if not self.ad_treedomain:
raise nose.SkipTest('AD tree root domain is not available')
self.clear_sssd_caches()
testuser = 'treetestuser@{0}'.format(self.ad_treedomain)
testgroup = 'treetestgroup@{0}'.format(self.ad_treedomain)
result = self.legacy_client.run_command(['id', testuser])
# Only for POSIX trust testing does the testuser belong to the
# testgroup
group_name = '\({}\)'.format(testgroup) if self.posix_trust else ''
uid_regex = "uid={0}\({1}\)".format(
self.treedomain_testuser_uid_regex, testuser)
gid_regex = "gid={0}{1}".format(
self.treedomain_testuser_gid_regex, group_name)
group_regex = "groups={0}{1}".format(
self.treedomain_testuser_gid_regex, group_name)
assert re.search(uid_regex, result.stdout_text)
assert re.search(gid_regex, result.stdout_text)
assert re.search(group_regex, result.stdout_text)
def test_login_treedomain_ad_user(self):
if not self.ad_treedomain:
raise nose.SkipTest('AD tree root domain is not available.')
if not self.master.transport.file_exists('/usr/bin/sshpass'):
raise nose.SkipTest('Package sshpass not available on {}'.format(
self.master.hostname))
result = self.master.run_command(
'sshpass -p {0} ssh -o StrictHostKeyChecking=no '
'-l admin {1} "echo test"'.format(
self.legacy_client.config.admin_password,
self.legacy_client.external_hostname))
assert "test" in result.stdout_text
@classmethod
def install(cls, mh):
super(BaseTestLegacyClient, cls).install(mh)
tasks.kinit_admin(cls.master)
password_confirmation = (
cls.master.config.admin_password +
'\n' +
cls.master.config.admin_password
)
cls.master.run_command(['ipa', 'user-add', 'disabledipauser',
'--first', 'disabled',
'--last', 'ipauser',
'--password'],
stdin_text=password_confirmation)
cls.master.run_command(['ipa', 'user-disable', 'disabledipauser'])
cls.ad = cls.ad_domains[0].ads[0]
cls.legacy_client = cls.host_by_role(cls.required_extra_roles[0])
# Determine whether the subdomain AD is available
try:
child_ad = cls.host_by_role(cls.optional_extra_roles[0])
cls.ad_subdomain = '.'.join(
child_ad.hostname.split('.')[1:])
except LookupError:
cls.ad_subdomain = None
# Determine whether the tree domain AD is available
try:
cls.tree_ad = cls.host_by_role(cls.optional_extra_roles[1])
cls.ad_treedomain = '.'.join(
cls.tree_ad.hostname.split('.')[1:])
except LookupError:
cls.ad_treedomain = None
tasks.apply_common_fixes(cls.legacy_client)
for f in cls.backup_files:
tasks.backup_file(cls.legacy_client, f)
@classmethod
def uninstall(cls, mh):
cls.master.run_command(['ipa', 'user-del', 'disabledipauser'],
raiseonerr=False)
# Remove information about trust from AD, if domain was defined
if hasattr(cls, 'ad_domain'):
tasks.remove_trust_info_from_ad(cls.master, cls.ad_domain)
# Also unapply fixes on the legacy client, if defined
if hasattr(cls, 'legacy_client'):
tasks.unapply_fixes(cls.legacy_client)
super(BaseTestLegacyClient, cls).uninstall(mh)
# Base classes with attributes that are specific for each legacy client test
class BaseTestLegacySSSDBefore19RedHat(object):
advice_id = 'config-redhat-sssd-before-1-9'
required_extra_roles = ['legacy_client_sssd_redhat']
optional_extra_roles = ['ad_subdomain', 'ad_treedomain']
class BaseTestLegacyNssPamLdapdRedHat(object):
advice_id = 'config-redhat-nss-pam-ldapd'
required_extra_roles = ['legacy_client_nss_pam_ldapd_redhat']
optional_extra_roles = ['ad_subdomain', 'ad_treedomain']
def clear_sssd_caches(self):
tasks.clear_sssd_cache(self.master)
class BaseTestLegacyNssLdapRedHat(object):
advice_id = 'config-redhat-nss-ldap'
required_extra_roles = ['legacy_client_nss_ldap_redhat']
optional_extra_roles = ['ad_subdomain', 'ad_treedomain']
def clear_sssd_caches(self):
tasks.clear_sssd_cache(self.master)
# Base classes that join legacy client specific steps with steps required
# to setup IPA with trust (both with and without using the POSIX attributes)
class BaseTestLegacyClientPosix(BaseTestLegacyClient,
trust_tests.TestEnforcedPosixADTrust):
testuser_uid_regex = '10042'
testuser_gid_regex = '10047'
subdomain_testuser_uid_regex = '10142'
subdomain_testuser_gid_regex = '10147'
treedomain_testuser_uid_regex = '10242'
treedomain_testuser_gid_regex = '10247'
posix_trust = True
def test_remove_trust_with_posix_attributes(self):
pass
class BaseTestLegacyClientNonPosix(BaseTestLegacyClient,
trust_tests.TestBasicADTrust):
testuser_uid_regex = '(?!10042)(\d+)'
testuser_gid_regex = '(?!10047)(\d+)'
subdomain_testuser_uid_regex = '(?!10142)(\d+)'
subdomain_testuser_gid_regex = '(?!10147)(\d+)'
treedomain_testuser_uid_regex = '(?!10242)(\d+)'
treedomain_testuser_gid_regex = '(?!10247)(\d+)'
def test_remove_nonposix_trust(self):
pass
class BaseTestSSSDMixin(object):
def test_apply_advice(self):
super(BaseTestSSSDMixin, self).test_apply_advice()
tasks.setup_sssd_debugging(self.legacy_client)
# Tests definitions themselves. Beauty. Just pure beauty.
class TestLegacySSSDBefore19RedHatNonPosix(BaseTestSSSDMixin,
BaseTestLegacySSSDBefore19RedHat,
BaseTestLegacyClientNonPosix):
pass
class TestLegacyNssPamLdapdRedHatNonPosix(BaseTestLegacyNssPamLdapdRedHat,
BaseTestLegacyClientNonPosix):
pass
class TestLegacyNssLdapRedHatNonPosix(BaseTestLegacyNssLdapRedHat,
BaseTestLegacyClientNonPosix):
pass
class TestLegacySSSDBefore19RedHatPosix(BaseTestSSSDMixin,
BaseTestLegacySSSDBefore19RedHat,
BaseTestLegacyClientPosix):
pass
class TestLegacyNssPamLdapdRedHatPosix(BaseTestLegacyNssPamLdapdRedHat,
BaseTestLegacyClientPosix):
pass
class TestLegacyNssLdapRedHatPosix(BaseTestLegacyNssLdapRedHat,
BaseTestLegacyClientPosix):
pass

View File

@@ -0,0 +1,169 @@
#
# Copyright (C) 2017 FreeIPA Contributors see COPYING for license
#
import pytest
from ipatests.test_integration.base import IntegrationTest
from ipatests.pytest_plugins.integration.tasks import clear_sssd_cache
test_data = []
for i in range(3):
data = {
'user': {
'login': 'testuser_{}'.format(i),
'first': 'Test_{}'.format(i),
'last': 'User_{}'.format(i),
},
'netgroup': 'testgroup_{}'.format(i),
'nested_netgroup': 'testgroup_{}'.format(i-1) if i > 0 else None
}
test_data.append(data)
members = [d['user']['login'] for d in test_data]
test_data[-1]['netgroup_nested_members'] = members
@pytest.fixture()
def three_netgroups(request):
"""Prepare basic netgroups with users"""
for d in test_data:
request.cls.master.run_command(['ipa', 'user-add', d['user']['login'],
'--first', d['user']['first'],
'--last', d['user']['last']],
raiseonerr=False)
request.cls.master.run_command(['ipa', 'netgroup-add', d['netgroup']],
raiseonerr=False)
user_opt = '--users={u[login]}'.format(u=d['user'])
request.cls.master.run_command(['ipa', 'netgroup-add-member', user_opt,
d['netgroup']], raiseonerr=False)
def teardown_three_netgroups():
"""Clean basic netgroups with users"""
for d in test_data:
request.cls.master.run_command(['ipa', 'user-del',
d['user']['login']],
raiseonerr=False)
request.cls.master.run_command(['ipa', 'netgroup-del',
d['netgroup']],
raiseonerr=False)
request.addfinalizer(teardown_three_netgroups)
class TestNetgroups(IntegrationTest):
"""
Test Netgroups
"""
topology = 'line'
def check_users_in_netgroups(self):
"""Check if users are in groups, no nested things"""
master = self.master
clear_sssd_cache(master)
for d in test_data:
result = master.run_command(['getent', 'passwd',
d['user']['login']], raiseonerr=False)
assert result.returncode == 0
user = '{u[first]} {u[last]}'.format(u=d['user'])
assert user in result.stdout_text
result = master.run_command(['getent', 'netgroup',
d['netgroup']], raiseonerr=False)
assert result.returncode == 0
netgroup = '(-,{},{})'.format(d['user']['login'],
self.master.domain.name)
assert netgroup in result.stdout_text
def check_nested_netgroup_hierarchy(self):
"""Check if nested netgroups hierarchy is complete"""
master = self.master
clear_sssd_cache(master)
for d in test_data:
result = master.run_command(['getent', 'netgroup', d['netgroup']],
raiseonerr=False)
assert result.returncode == 0
for member in d['netgroup_nested_members']:
if not member:
continue
netgroup = '(-,{},{})'.format(member, self.master.domain.name)
assert netgroup in result.stdout_text
def prepare_nested_netgroup_hierarchy(self):
"""Prepares nested netgroup hierarchy from basic netgroups"""
for d in test_data:
if not d['nested_netgroup']:
continue
netgroups_opt = '--netgroups={}'.format(d['nested_netgroup'])
self.master.run_command(['ipa', 'netgroup-add-member',
netgroups_opt, d['netgroup']])
def test_add_nested_netgroup(self, three_netgroups):
"""Test of adding nested groups"""
self.check_users_in_netgroups()
self.prepare_nested_netgroup_hierarchy()
self.check_nested_netgroup_hierarchy()
def test_remove_nested_netgroup(self, three_netgroups):
"""Test of removing nested groups"""
master = self.master
trinity = ['(-,{},{})'.format(d['user']['login'],
self.master.domain.name)
for d in test_data]
self.check_users_in_netgroups()
self.prepare_nested_netgroup_hierarchy()
self.check_nested_netgroup_hierarchy()
# Removing of testgroup_1 from testgroup_2
netgroups_opt = '--netgroups={n[netgroup]}'.format(n=test_data[0])
result = self.master.run_command(['ipa', 'netgroup-remove-member',
netgroups_opt,
test_data[1]['netgroup']],
raiseonerr=False)
assert result.returncode == 0
clear_sssd_cache(master)
result = master.run_command(['getent', 'netgroup',
test_data[1]['netgroup']],
raiseonerr=False)
assert result.returncode == 0
assert trinity[1] in result.stdout_text
result = master.run_command(['getent', 'netgroup',
test_data[2]['netgroup']],
raiseonerr=False)
assert result.returncode == 0
assert trinity[0] not in result.stdout_text
assert trinity[1] in result.stdout_text
assert trinity[2] in result.stdout_text
# Removing of testgroup_2 from testgroup_3
netgroups_opt = '--netgroups={n[netgroup]}'.format(n=test_data[1])
result = self.master.run_command(['ipa', 'netgroup-remove-member',
netgroups_opt,
test_data[2]['netgroup']],
raiseonerr=False)
assert result.returncode == 0
clear_sssd_cache(master)
result = master.run_command(['getent', 'netgroup',
test_data[2]['netgroup']],
raiseonerr=False)
assert result.returncode == 0
assert trinity[0] not in result.stdout_text
assert trinity[1] not in result.stdout_text
assert trinity[2] in result.stdout_text

View File

@@ -0,0 +1,54 @@
# Authors:
# Petr Viktorin <pviktori@redhat.com>
#
# Copyright (C) 2014 Red Hat
# see file 'COPYING' for use and warranty information
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""Test the ordering of tests
IPA integration tests, marked with `@ordered`, require tests to be run
in a specific order:
- Base classes first
- Within a class, test methods are ordered according to source line
"""
from pytest_sourceorder import ordered
@ordered
class TestBase(object):
@classmethod
def setup_class(cls):
cls.value = 'unchanged'
def test_d_first(self):
type(self).value = 'changed once'
class TestChild(TestBase):
def test_b_third(self):
assert type(self).value == 'changed twice'
type(self).value = 'changed thrice'
def test_a_fourth(self):
assert type(self).value == 'changed thrice'
def test_c_second(self):
assert type(self).value == 'changed once'
type(self).value = 'changed twice'
TestBase.test_c_second = test_c_second
del test_c_second

View File

@@ -0,0 +1,529 @@
#
# Copyright (C) 2016 FreeIPA Contributors see COPYING for license
#
import time
from tempfile import NamedTemporaryFile
import textwrap
import pytest
from ipatests.test_integration.base import IntegrationTest
from ipatests.pytest_plugins.integration import tasks
from ipatests.pytest_plugins.integration.tasks import (
assert_error, replicas_cleanup)
from ipalib.constants import DOMAIN_LEVEL_0
from ipalib.constants import DOMAIN_LEVEL_1
from ipalib.constants import DOMAIN_SUFFIX_NAME
class ReplicaPromotionBase(IntegrationTest):
@classmethod
def install(cls, mh):
tasks.install_master(cls.master, domain_level=cls.domain_level)
def test_kra_install_master(self):
result1 = tasks.install_kra(self.master,
first_instance=True,
raiseonerr=False)
assert result1.returncode == 0, result1.stderr_text
tasks.kinit_admin(self.master)
result2 = self.master.run_command(["ipa", "vault-find"],
raiseonerr=False)
found = result2.stdout_text.find("0 vaults matched")
assert(found > 0), result2.stdout_text
class TestReplicaPromotionLevel0(ReplicaPromotionBase):
topology = 'star'
num_replicas = 1
domain_level = DOMAIN_LEVEL_0
@replicas_cleanup
def test_promotion_disabled(self):
"""
Testcase:
http://www.freeipa.org/page/V4/Replica_Promotion/Test_plan#Test_case:
_Make_sure_the_feature_is_unavailable_under_domain_level_0
"""
client = self.replicas[0]
tasks.install_client(self.master, client)
args = ['ipa-replica-install', '-U',
'-p', self.master.config.dirman_password,
'-w', self.master.config.admin_password,
'--ip-address', client.ip]
result = client.run_command(args, raiseonerr=False)
assert_error(result,
'You must provide a file generated by ipa-replica-prepare'
' to create a replica when the domain is at level 0', 1)
@pytest.mark.xfail(reason="Ticket N 6274")
@replicas_cleanup
def test_backup_restore(self):
"""
TestCase:
http://www.freeipa.org/page/V4/Replica_Promotion/Test_plan#Test_case:
_ipa-restore_after_domainlevel_raise_restores_original_domain_level
"""
command = ["ipa", "topologysegment-find", DOMAIN_SUFFIX_NAME]
tasks.install_replica(self.master, self.replicas[0])
backup_file = tasks.ipa_backup(self.master)
self.master.run_command(["ipa", "domainlevel-set", str(DOMAIN_LEVEL_1)])
# We need to give the server time to merge 2 one-way segments into one
time.sleep(10)
result = self.master.run_command(command)
found1 = result.stdout_text.rfind("1 segment matched")
assert(found1 > 0), result.stdout_text
tasks.ipa_restore(self.master, backup_file)
result2 = self.master.run_command(command, raiseonerr=False)
found2 = result2.stdout_text.rfind("0 segments matched")
assert(found2 > 0), result2.stdout_text
@pytest.mark.xfail(reason="Ticket N 6274")
class TestKRAInstall(IntegrationTest):
"""
TestCase: http://www.freeipa.org/page/V4/Replica_Promotion/Test_plan
#Test_case:_ipa-kra-install_with_replica_file_works_only_on_domain_level_0
"""
topology = 'star'
domain_level = DOMAIN_LEVEL_0
num_replicas = 2
@classmethod
def install(cls, mh):
tasks.install_master(cls.master, domain_level=cls.domain_level)
def test_kra_install_without_replica_file(self):
master = self.master
replica1 = self.replicas[0]
replica2 = self.replicas[1]
tasks.install_kra(master, first_instance=True)
tasks.install_replica(master, replica1)
result1 = tasks.install_kra(replica1,
domain_level=DOMAIN_LEVEL_1,
raiseonerr=False)
assert_error(result1, "A replica file is required", 1)
tasks.install_kra(replica1,
domain_level=DOMAIN_LEVEL_0,
raiseonerr=True)
# Now prepare the replica file, copy it to the client and raise
# domain level on master to test the reverse situation
tasks.replica_prepare(master, replica2)
master.run_command(["ipa", "domainlevel-set", str(DOMAIN_LEVEL_1)])
tasks.install_replica(master, replica2)
result2 = tasks.install_kra(replica2,
domain_level=DOMAIN_LEVEL_0,
raiseonerr=False)
assert_error(result2, "No replica file is required", 1)
tasks.install_kra(replica2)
@pytest.mark.xfail(reason="Ticket N 6274")
class TestCAInstall(IntegrationTest):
topology = 'star'
domain_level = DOMAIN_LEVEL_0
num_replicas = 2
@classmethod
def install(cls, mh):
tasks.install_master(cls.master, domain_level=cls.domain_level)
def test_ca_install_without_replica_file(self):
"""
TestCase:
http://www.freeipa.org/page/V4/Replica_Promotion/Test_plan#Test_case:
_ipa-ca-install_with_replica_file_works_only_on_domain_level_0
"""
master = self.master
replica1 = self.replicas[0]
replica2 = self.replicas[1]
for replica in self.replicas:
tasks.install_replica(master, replica, setup_ca=False,
setup_dns=True)
result1 = tasks.install_ca(replica1,
domain_level=DOMAIN_LEVEL_1,
raiseonerr=False)
assert_error(result1, "If you wish to replicate CA to this host,"
" please re-run 'ipa-ca-install'\nwith a"
" replica file generated on an existing CA"
" master as argument.", 1)
tasks.install_ca(replica1, domain_level=DOMAIN_LEVEL_0)
# Now prepare the replica file, copy it to the client and raise
# domain level on master to test the reverse situation
master.run_command(["ipa", "domainlevel-set", str(DOMAIN_LEVEL_1)])
result2 = tasks.install_ca(replica2,
domain_level=DOMAIN_LEVEL_0,
raiseonerr=False)
assert_error(result2, 'Too many parameters provided.'
' No replica file is required', 1)
tasks.install_ca(replica2, domain_level=DOMAIN_LEVEL_1)
class TestReplicaPromotionLevel1(ReplicaPromotionBase):
"""
TestCase: http://www.freeipa.org/page/V4/Replica_Promotion/Test_plan#
Test_case:_Make_sure_the_old_workflow_is_disabled_at_domain_level_1
"""
topology = 'star'
num_replicas = 1
domain_level = DOMAIN_LEVEL_1
@replicas_cleanup
def test_replica_prepare_disabled(self):
replica = self.replicas[0]
args = ['ipa-replica-prepare',
'-p', self.master.config.dirman_password,
'--ip-address', replica.ip,
replica.hostname]
result = self.master.run_command(args, raiseonerr=False)
assert_error(result, "Replica creation using 'ipa-replica-prepare'"
" 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,
'--server', self.master.hostname,
'-U'])
@pytest.mark.xfail(reason="Ticket N 6274")
class TestReplicaManageCommands(IntegrationTest):
topology = "star"
num_replicas = 2
domain_level = DOMAIN_LEVEL_0
def test_replica_manage_commands(self):
"""
TestCase: http://www.freeipa.org/page/V4/Replica_Promotion/Test_plan
#Test_case:_ipa-replica-manage_connect_is_deprecated_in_domain_level_1
"""
master = self.master
replica1 = self.replicas[0]
replica2 = self.replicas[1]
result1 = master.run_command(["ipa-replica-manage",
"connect",
replica1.hostname,
replica2.hostname],
raiseonerr=False)
assert result1.returncode == 0, result1.stderr_text
result2 = master.run_command(["ipa-replica-manage",
"disconnect",
replica1.hostname,
replica2.hostname],
raiseonerr=False)
assert result2.returncode == 0, result2.stderr_text
master.run_command(["ipa", "domainlevel-set", str(DOMAIN_LEVEL_1)])
result3 = master.run_command(["ipa-replica-manage",
"connect",
replica1.hostname,
replica2.hostname],
raiseonerr=False)
assert_error(result3, 'Creation of IPA replication agreement is'
' deprecated with managed IPA replication'
' topology. Please use `ipa topologysegment-*`'
' commands to manage the topology', 1)
segment = tasks.create_segment(master, replica1, replica2)
result4 = master.run_command(["ipa-replica-manage",
"disconnect",
replica1.hostname,
replica2.hostname],
raiseonerr=False)
assert_error(result4, 'Removal of IPA replication agreement is'
' 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',
'--server', self.master.hostname],
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,
'-U'])
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_error(result, "Removal of '%s' leads to disconnected"
" topology" % self.replicas[0].hostname, 1)
self.replicas[0].run_command(['ipa-server-install', '--uninstall',
'-U', '--ignore-topology-disconnect'])
@pytest.mark.xfail(reason="Ticket N 6274")
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_replicas = 1
domain_name = 'exxample.test'
domain_level = DOMAIN_LEVEL_1
@classmethod
def install(cls, mh):
tasks.install_master(cls.master, domain_level=cls.domain_level)
def teardown_method(self, method):
self.replicas[0].run_command(['ipa-client-install',
'--uninstall', '-U'],
raiseonerr=False)
tasks.kinit_admin(self.master)
self.master.run_command(['ipa', 'host-del',
self.replicas[0].hostname],
raiseonerr=False)
def test_wrong_client_domain(self):
client = self.replicas[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.replicas[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')
class TestRenewalMaster(IntegrationTest):
topology = 'star'
num_replicas = 1
@classmethod
def uninstall(cls, mh):
super(TestRenewalMaster, cls).uninstall(mh)
def test_replica_not_marked_as_renewal_master(self):
"""
https://fedorahosted.org/freeipa/ticket/5902
"""
master = self.master
replica = self.replicas[0]
result = master.run_command(["ipa", "config-show"]).stdout_text
assert("IPA CA renewal master: %s" % master.hostname in result), (
"Master hostname not found among CA renewal masters"
)
assert("IPA CA renewal master: %s" % replica.hostname not in result), (
"Replica hostname found among CA renewal masters"
)
def test_manual_renewal_master_transfer(self):
replica = self.replicas[0]
replica.run_command(['ipa', 'config-mod',
'--ca-renewal-master-server', replica.hostname])
result = self.master.run_command(["ipa", "config-show"]).stdout_text
assert("IPA CA renewal master: %s" % replica.hostname in result), (
"Replica hostname not found among CA renewal masters"
)
def test_automatic_renewal_master_transfer_ondelete(self):
# Test that after master uninstallation, replica overtakes the cert
# renewal master role
tasks.uninstall_master(self.replicas[0])
result = self.master.run_command(['ipa', 'config-show']).stdout_text
assert("IPA CA renewal master: %s" % self.master.hostname in result), (
"Master hostname not found among CA renewal masters"
)
class TestReplicaInstallWithExistingEntry(IntegrationTest):
"""replica install might fail because of existing entry for replica like
`cn=ipa-http-delegation,cn=s4u2proxy,cn=etc,$SUFFIX` etc. The situation
may arise due to incorrect uninstall of replica.
https://pagure.io/freeipa/issue/7174"""
num_replicas = 1
def test_replica_install_with_existing_entry(self):
master = self.master
tasks.install_master(master)
replica = self.replicas[0]
tf = NamedTemporaryFile()
ldif_file = tf.name
base_dn = "dc=%s" % (",dc=".join(replica.domain.name.split(".")))
# adding entry for replica on master so that master will have it before
# replica installtion begins and creates a situation for pagure-7174
entry_ldif = textwrap.dedent("""
dn: cn=ipa-http-delegation,cn=s4u2proxy,cn=etc,{base_dn}
changetype: modify
add: memberPrincipal
memberPrincipal: HTTP/{hostname}@{realm}
dn: cn=ipa-ldap-delegation-targets,cn=s4u2proxy,cn=etc,{base_dn}
changetype: modify
add: memberPrincipal
memberPrincipal: ldap/{hostname}@{realm}""").format(
base_dn=base_dn, hostname=replica.hostname,
realm=replica.domain.name.upper())
master.put_file_contents(ldif_file, entry_ldif)
arg = ['ldapmodify',
'-h', master.hostname,
'-p', '389', '-D',
str(master.config.dirman_dn), # pylint: disable=no-member
'-w', master.config.dirman_password,
'-f', ldif_file]
master.run_command(arg)
tasks.install_replica(master, replica)

View File

@@ -0,0 +1,180 @@
#
# Copyright (C) 2017 FreeIPA Contributors see COPYING for license
#
import time
import pytest
from ipalib.constants import DOMAIN_LEVEL_0
from ipatests.pytest_plugins.integration.env_config import get_global_config
from ipatests.test_integration.base import IntegrationTest
from ipatests.pytest_plugins.integration import tasks
config = get_global_config()
class LayoutsBaseTest(IntegrationTest):
@classmethod
def install(cls, mh):
# tests use custom installation
pass
def replication_is_working(self):
test_user = 'replication-testuser'
self.master.run_command(
['ipa', 'user-add', test_user, '--first', 'test', '--last', 'user']
)
time.sleep(60) # make sure the replication of user is done
for r in self.replicas:
r.run_command(['ipa', 'user-show', test_user])
@pytest.mark.skipif(config.domain_level == DOMAIN_LEVEL_0,
reason='does not work on DOMAIN_LEVEL_0 by design')
class TestLineTopologyWithoutCA(LayoutsBaseTest):
num_replicas = 3
def test_line_topology_without_ca(self):
tasks.install_topo('line', self.master, self.replicas, [],
setup_replica_cas=False)
self.replication_is_working()
class TestLineTopologyWithCA(LayoutsBaseTest):
num_replicas = 3
def test_line_topology_with_ca(self):
tasks.install_topo('line', self.master, self.replicas, [],
setup_replica_cas=True)
self.replication_is_working()
class TestLineTopologyWithCAKRA(LayoutsBaseTest):
num_replicas = 3
def test_line_topology_with_ca_kra(self):
tasks.install_topo('line', self.master, self.replicas, [],
setup_replica_cas=True, setup_replica_kras=True)
self.replication_is_working()
class TestStarTopologyWithoutCA(LayoutsBaseTest):
num_replicas = 3
def test_star_topology_without_ca(self):
tasks.install_topo('star', self.master, self.replicas, [],
setup_replica_cas=False)
self.replication_is_working()
class TestStarTopologyWithCA(LayoutsBaseTest):
num_replicas = 3
def test_star_topology_with_ca(self):
tasks.install_topo('star', self.master, self.replicas, [],
setup_replica_cas=True)
self.replication_is_working()
class TestStarTopologyWithCAKRA(LayoutsBaseTest):
num_replicas = 3
def test_star_topology_with_ca_kra(self):
tasks.install_topo('star', self.master, self.replicas, [],
setup_replica_cas=True, setup_replica_kras=True)
self.replication_is_working()
class TestCompleteTopologyWithoutCA(LayoutsBaseTest):
num_replicas = 3
def test_complete_topology_without_ca(self):
tasks.install_topo('complete', self.master, self.replicas, [],
setup_replica_cas=False)
self.replication_is_working()
class TestCompleteTopologyWithCA(LayoutsBaseTest):
num_replicas = 3
def test_complete_topology_with_ca(self):
tasks.install_topo('complete', self.master, self.replicas, [],
setup_replica_cas=True)
self.replication_is_working()
class TestCompleteTopologyWithCAKRA(LayoutsBaseTest):
num_replicas = 3
def test_complete_topology_with_ca_kra(self):
tasks.install_topo('complete', self.master, self.replicas, [],
setup_replica_cas=True, setup_replica_kras=True)
self.replication_is_working()
@pytest.mark.skipif(config.domain_level == DOMAIN_LEVEL_0,
reason='does not work on DOMAIN_LEVEL_0 by design')
class Test2ConnectedTopologyWithoutCA(LayoutsBaseTest):
num_replicas = 33
def test_2_connected_topology_without_ca(self):
tasks.install_topo('2-connected', self.master, self.replicas, [],
setup_replica_cas=False)
self.replication_is_working()
class Test2ConnectedTopologyWithCA(LayoutsBaseTest):
num_replicas = 33
def test_2_connected_topology_with_ca(self):
tasks.install_topo('2-connected', self.master, self.replicas, [],
setup_replica_cas=True)
self.replication_is_working()
class Test2ConnectedTopologyWithCAKRA(LayoutsBaseTest):
num_replicas = 33
def test_2_connected_topology_with_ca_kra(self):
tasks.install_topo('2-connected', self.master, self.replicas, [],
setup_replica_cas=True, setup_replica_kras=True)
self.replication_is_working()
@pytest.mark.skipif(config.domain_level == DOMAIN_LEVEL_0,
reason='does not work on DOMAIN_LEVEL_0 by design')
class TestDoubleCircleTopologyWithoutCA(LayoutsBaseTest):
num_replicas = 29
def test_2_connected_topology_with_ca(self):
tasks.install_topo('double-circle', self.master, self.replicas, [],
setup_replica_cas=False)
self.replication_is_working()
class TestDoubleCircleTopologyWithCA(LayoutsBaseTest):
num_replicas = 29
def test_2_connected_topology_with_ca(self):
tasks.install_topo('double-circle', self.master, self.replicas, [],
setup_replica_cas=True)
self.replication_is_working()
class TestDoubleCircleTopologyWithCAKRA(LayoutsBaseTest):
num_replicas = 29
def test_2_connected_topology_with_ca_kra(self):
tasks.install_topo('double-circle', self.master, self.replicas, [],
setup_replica_cas=True, setup_replica_kras=True)
self.replication_is_working()

View File

@@ -0,0 +1,302 @@
#
# Copyright (C) 2016 FreeIPA Contributors see COPYING for license
#
from itertools import permutations
from ipatests.test_integration.base import IntegrationTest
from ipatests.pytest_plugins.integration import tasks
from ipalib.constants import DOMAIN_LEVEL_1, DOMAIN_SUFFIX_NAME, CA_SUFFIX_NAME
REMOVAL_ERR_TEMPLATE = ("Removal of '{hostname}' leads to disconnected "
"topology in suffix '{suffix}'")
def check_master_removal(host, hostname_to_remove,
force=False,
ignore_topology_disconnect=False,
ignore_last_of_role=False):
result = tasks.run_server_del(
host,
hostname_to_remove,
force=force,
ignore_topology_disconnect=ignore_topology_disconnect,
ignore_last_of_role=ignore_last_of_role)
assert result.returncode == 0
if force:
assert ("Forcing removal of {hostname}".format(
hostname=hostname_to_remove) in result.stderr_text)
if ignore_topology_disconnect:
assert "Ignoring topology connectivity errors." in result.stderr_text
if ignore_last_of_role:
assert ("Ignoring these warnings and proceeding with removal" in
result.stderr_text)
tasks.assert_error(
host.run_command(
['ipa', 'server-show', hostname_to_remove], raiseonerr=False
),
"{}: server not found".format(hostname_to_remove),
returncode=2
)
def check_removal_disconnects_topology(
host, hostname_to_remove,
affected_suffixes=(DOMAIN_SUFFIX_NAME,)):
result = tasks.run_server_del(host, hostname_to_remove)
assert len(affected_suffixes) <= 2
err_messages_by_suffix = {
CA_SUFFIX_NAME: REMOVAL_ERR_TEMPLATE.format(
hostname=hostname_to_remove,
suffix=CA_SUFFIX_NAME
),
DOMAIN_SUFFIX_NAME: REMOVAL_ERR_TEMPLATE.format(
hostname=hostname_to_remove,
suffix=DOMAIN_SUFFIX_NAME
)
}
for suffix in err_messages_by_suffix:
if suffix in affected_suffixes:
tasks.assert_error(
result, err_messages_by_suffix[suffix], returncode=1)
else:
assert err_messages_by_suffix[suffix] not in result.stderr_text
class ServerDelBase(IntegrationTest):
num_replicas = 2
num_clients = 1
domain_level = DOMAIN_LEVEL_1
topology = 'star'
@classmethod
def install(cls, mh):
super(ServerDelBase, cls).install(mh)
cls.client = cls.clients[0]
cls.replica1 = cls.replicas[0]
cls.replica2 = cls.replicas[1]
class TestServerDel(ServerDelBase):
@classmethod
def install(cls, mh):
super(TestServerDel, cls).install(mh)
# prepare topologysegments for negative test cases
# it should look like this for DOMAIN_SUFFIX_NAME:
# master
# /
# /
# /
# replica1------- replica2
# and like this for CA_SUFFIX_NAME
# master
# \
# \
# \
# replica1------- replica2
tasks.create_segment(cls.client, cls.replica1, cls.replica2)
tasks.create_segment(cls.client, cls.replica1, cls.replica2,
suffix=CA_SUFFIX_NAME)
# try to delete all relevant segment connecting master and replica1/2
segment_name_fmt = '{p[0].hostname}-to-{p[1].hostname}'
for domain_pair in permutations((cls.master, cls.replica2)):
tasks.destroy_segment(
cls.client, segment_name_fmt.format(p=domain_pair))
for ca_pair in permutations((cls.master, cls.replica1)):
tasks.destroy_segment(
cls.client, segment_name_fmt.format(p=ca_pair),
suffix=CA_SUFFIX_NAME)
def test_removal_of_nonexistent_master_raises_error(self):
"""
tests that removal of non-existent master raises an error
"""
hostname = u'bogus-master.bogus.domain'
err_message = "{}: server not found".format(hostname)
tasks.assert_error(
tasks.run_server_del(self.client, hostname),
err_message,
returncode=2
)
def test_forced_removal_of_nonexistent_master(self):
"""
tests that removal of non-existent master with '--force' does not raise
an error
"""
hostname = u'bogus-master.bogus.domain'
result = tasks.run_server_del(self.client, hostname, force=True)
assert result.returncode == 0
assert ('Deleted IPA server "{}"'.format(hostname) in
result.stdout_text)
assert ("Server has already been deleted" in result.stderr_text)
def test_removal_of_replica1_disconnects_domain_topology(self):
"""
tests that given the used topology, attempted removal of replica1 fails
with disconnected DOMAIN topology but not CA
"""
check_removal_disconnects_topology(
self.client,
self.replica1.hostname,
affected_suffixes=(DOMAIN_SUFFIX_NAME,)
)
def test_removal_of_replica2_disconnects_ca_topology(self):
"""
tests that given the used topology, attempted removal of replica2 fails
with disconnected CA topology but not DOMAIN
"""
check_removal_disconnects_topology(
self.client,
self.replica2.hostname,
affected_suffixes=(CA_SUFFIX_NAME,)
)
def test_ignore_topology_disconnect_replica1(self):
"""
tests that removal of replica1 with '--ignore-topology-disconnect'
destroys master for good
"""
check_master_removal(
self.client,
self.replica1.hostname,
ignore_topology_disconnect=True
)
# reinstall the replica
tasks.uninstall_master(self.replica1)
tasks.install_replica(self.master, self.replica1, setup_ca=True)
def test_ignore_topology_disconnect_replica2(self):
"""
tests that removal of replica2 with '--ignore-topology-disconnect'
destroys master for good
"""
check_master_removal(
self.client,
self.replica2.hostname,
ignore_topology_disconnect=True
)
# reinstall the replica
tasks.uninstall_master(self.replica2)
tasks.install_replica(self.master, self.replica2, setup_ca=True)
def test_removal_of_master_disconnects_both_topologies(self):
"""
tests that master removal will now raise errors in both suffixes.
"""
check_removal_disconnects_topology(
self.client,
self.master.hostname,
affected_suffixes=(CA_SUFFIX_NAME, DOMAIN_SUFFIX_NAME)
)
def test_removal_of_replica1(self):
"""
tests the removal of replica1 which should now pass without errors
"""
check_master_removal(
self.client,
self.replica1.hostname
)
def test_removal_of_replica2(self):
"""
tests the removal of replica2 which should now pass without errors
"""
check_master_removal(
self.client,
self.replica2.hostname
)
class TestLastServices(ServerDelBase):
"""
Test the checks for last services during server-del and their bypassing
using when forcing the removal
"""
num_replicas = 1
domain_level = DOMAIN_LEVEL_1
topology = 'line'
@classmethod
def install(cls, mh):
tasks.install_topo(
cls.topology, cls.master, cls.replicas, [],
domain_level=cls.domain_level, setup_replica_cas=False)
def test_removal_of_master_raises_error_about_last_ca(self):
"""
test that removal of master fails on the last
"""
tasks.assert_error(
tasks.run_server_del(self.replicas[0], self.master.hostname),
"Deleting this server is not allowed as it would leave your "
"installation without a CA.",
1
)
def test_install_ca_on_replica1(self):
"""
Install CA on replica so that we can test DNS-related checks
"""
tasks.install_ca(self.replicas[0], domain_level=self.domain_level)
def test_removal_of_master_raises_error_about_last_dns(self):
"""
Now server-del should complain about the removal of last DNS server
"""
tasks.assert_error(
tasks.run_server_del(self.replicas[0], self.master.hostname),
"Deleting this server will leave your installation "
"without a DNS.",
1
)
def test_install_dns_on_replica1_and_dnssec_on_master(self):
"""
install DNS server on replica and DNSSec on master
"""
tasks.install_dns(self.replicas[0])
args = [
"ipa-dns-install",
"--dnssec-master",
"--forwarder", self.master.config.dns_forwarder,
"-U",
]
self.master.run_command(args)
def test_removal_of_master_raises_error_about_dnssec(self):
tasks.assert_error(
tasks.run_server_del(self.replicas[0], self.master.hostname),
"Replica is active DNSSEC key master. Uninstall "
"could break your DNS system. Please disable or replace "
"DNSSEC key master first.",
1
)
def test_forced_removal_of_master(self):
"""
Tests that we can still force remove the master using
'--ignore-last-of-role'
"""
check_master_removal(
self.replicas[0], self.master.hostname,
ignore_last_of_role=True
)

View File

@@ -0,0 +1,138 @@
# Authors:
# Petr Viktorin <pviktori@redhat.com>
#
# Copyright (C) 2014 Red Hat
# see file 'COPYING' for use and warranty information
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os
from ipatests.test_integration.base import IntegrationTest
from ipatests.pytest_plugins.integration import tasks
class TestServicePermissions(IntegrationTest):
topology = 'star'
def test_service_as_user_admin(self):
"""Test that a service in User Administrator role can manage users"""
service_name = 'testservice/%s@%s' % (self.master.hostname,
self.master.domain.realm)
keytab_file = os.path.join(self.master.config.test_dir,
'testservice_keytab')
# Prepare a service
self.master.run_command(['ipa', 'service-add', service_name])
self.master.run_command(['ipa-getkeytab',
'-p', service_name,
'-k', keytab_file,
'-s', self.master.hostname])
# Check that the service cannot add a user
self.master.run_command(['kdestroy'])
self.master.run_command(['kinit', '-k', service_name,
'-t', keytab_file])
result = self.master.run_command(['ipa', 'role-add-member',
'User Administrator',
'--service', service_name],
raiseonerr=False)
assert result.returncode > 0
# Add service to User Administrator role
self.master.run_command(['kdestroy'])
tasks.kinit_admin(self.master)
self.master.run_command(['ipa', 'role-add-member',
'User Administrator',
'--service', service_name])
# Check that the service now can add a user
self.master.run_command(['kdestroy'])
self.master.run_command(['kinit', '-k', service_name,
'-t', keytab_file])
self.master.run_command(['ipa', 'user-add', 'tuser',
'--first', 'a', '--last', 'b', '--random'])
# Clean up
self.master.run_command(['kdestroy'])
tasks.kinit_admin(self.master)
self.master.run_command(['ipa', 'service-del', service_name])
self.master.run_command(['ipa', 'user-del', 'tuser'])
class TestServiceAuthenticationIndicators(IntegrationTest):
topology = 'star'
def test_service_access(self):
""" Test that user is granted access when authenticated using
credentials that are sufficient for a service, and denied access
when using insufficient credentials"""
service_name = 'testservice/%s@%s' % (self.master.hostname,
self.master.domain.realm)
keytab_file = os.path.join(self.master.config.test_dir,
'testservice_keytab')
# Prepare a service without authentication indicator
self.master.run_command(['ipa', 'service-add', service_name])
self.master.run_command(['ipa-getkeytab',
'-p', service_name,
'-k', keytab_file])
# Set authentication-type for admin user
self.master.run_command(['ipa', 'user-mod', 'admin',
'--user-auth-type=password',
'--user-auth-type=otp'])
# Authenticate
self.master.run_command(['kinit', '-k', service_name,
'-t', keytab_file])
# Verify access to service is granted
result = self.master.run_command(['kvno', service_name],
raiseonerr=False)
assert result.returncode == 0
# Obtain admin ticket to be able to update service
tasks.kinit_admin(self.master)
# Modify service to have authentication indicator
self.master.run_command(['ipa', 'service-mod', service_name,
'--auth-ind=otp'])
self.master.run_command(['ipa-getkeytab',
'-p', service_name,
'-k', keytab_file])
# Authenticate
self.master.run_command(['kinit', '-k', service_name,
'-t', keytab_file])
# Verify access to service is rejected
result = self.master.run_command(['kvno', service_name],
raiseonerr=False)
assert result.returncode > 0

View File

@@ -0,0 +1,79 @@
# Authors:
# Petr Viktorin <pviktori@redhat.com>
#
# Copyright (C) 2013 Red Hat
# see file 'COPYING' for use and warranty information
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from __future__ import print_function
import pytest
from ipapython.dn import DN
from ipatests.test_integration.base import IntegrationTest
from ipatests.pytest_plugins.integration import tasks
@pytest.mark.ds_acceptance
class TestSimpleReplication(IntegrationTest):
"""Simple replication test
Install a server and a replica, then add an user on one host and ensure
it is also present on the other one.
"""
num_replicas = 1
topology = 'star'
def check_replication(self, source_host, dest_host, login):
source_host.run_command(['ipa', 'user-add', login,
'--first', 'test',
'--last', 'user'])
source_ldap = source_host.ldap_connect()
tasks.wait_for_replication(source_ldap)
ldap = dest_host.ldap_connect()
tasks.wait_for_replication(ldap)
# Check using LDAP
basedn = dest_host.domain.basedn
user_dn = DN(('uid', login), ('cn', 'users'), ('cn', 'accounts'),
basedn)
entry = ldap.get_entry(user_dn)
print(entry)
assert entry.dn == user_dn
assert entry['uid'] == [login]
# Check using CLI
result = dest_host.run_command(['ipa', 'user-show', login])
assert 'User login: %s' % login in result.stdout_text
def test_user_replication_to_replica(self):
"""Test user replication master -> replica"""
self.check_replication(self.master, self.replicas[0], 'testuser1')
def test_user_replication_to_master(self):
"""Test user replication replica -> master"""
self.check_replication(self.replicas[0], self.master, 'testuser2')
def test_replica_removal(self):
"""Test replica removal"""
result = self.master.run_command(['ipa-replica-manage', 'list'])
assert self.replicas[0].hostname in result.stdout_text
# has to be run with --force, there is no --unattended
self.master.run_command(['ipa-replica-manage', 'del',
self.replicas[0].hostname, '--force'])
result = self.master.run_command(['ipa-replica-manage', 'list'])
assert self.replicas[0].hostname not in result.stdout_text

View File

@@ -0,0 +1,682 @@
# Authors:
# Tomas Babej <tbabej@redhat.com>
#
# Copyright (C) 2014 Red Hat
# see file 'COPYING' for use and warranty information
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import pytest
from ipatests.test_integration.base import IntegrationTest
from ipatests.pytest_plugins.integration.tasks import (
clear_sssd_cache, get_host_ip_with_hostmask, modify_sssd_conf)
class TestSudo(IntegrationTest):
"""
Test Sudo
http://www.freeipa.org/page/V4/Sudo_Integration#Test_Plan
"""
num_clients = 1
topology = 'line'
@classmethod
def install(cls, mh):
super(TestSudo, cls).install(mh)
cls.client = cls.clients[0]
cls.clientname = cls.client.run_command(
['hostname', '-s']).stdout_text.strip()
for i in range(1, 3):
# Add 1. and 2. testing user
cls.master.run_command(['ipa', 'user-add',
'testuser%d' % i,
'--first', 'Test',
'--last', 'User%d' % i])
# Add 1. and 2. testing groups
cls.master.run_command(['ipa', 'group-add',
'testgroup%d' % i,
'--desc', '"%d. testing group"' % i])
# Add respective members to each group
cls.master.run_command(['ipa', 'group-add-member',
'testgroup%d' % i,
'--users', 'testuser%d' % i])
# Add hostgroup containing the client
cls.master.run_command(['ipa', 'hostgroup-add',
'testhostgroup',
'--desc', '"Contains client"'])
# Add the client to the host group
cls.master.run_command(['ipa', 'hostgroup-add-member',
'testhostgroup',
'--hosts', cls.client.hostname])
# Create local user and local group he's member of
cls.client.run_command(['groupadd', 'localgroup'])
cls.client.run_command(['useradd',
'-M',
'-G', 'localgroup',
'localuser'])
# Create sudorule 'defaults' for not requiring authentication
cls.master.run_command(['ipa', 'sudorule-add', 'defaults'])
cls.master.run_command(['ipa', 'sudorule-add-option',
'defaults',
'--sudooption', "!authenticate"])
@classmethod
def uninstall(cls, mh):
cls.client.run_command(['groupdel', 'localgroup'], raiseonerr=False)
cls.client.run_command(['userdel', 'localuser'], raiseonerr=False)
super(TestSudo, cls).uninstall(mh)
def list_sudo_commands(self, user, raiseonerr=False, verbose=False):
clear_sssd_cache(self.client)
list_flag = '-ll' if verbose else '-l'
return self.client.run_command(
'su -c "sudo %s -n" %s' % (list_flag, user),
raiseonerr=raiseonerr)
def reset_rule_categories(self, safe_delete=True):
if safe_delete:
# Remove and then add the rule back, since the deletion of some
# entries might cause setting categories to ALL to fail
# and therefore cause false negatives in the tests
self.master.run_command(['ipa', 'sudorule-del', 'testrule'])
self.master.run_command(['ipa', 'sudorule-add', 'testrule'])
self.master.run_command(['ipa', 'sudorule-add-option',
'testrule',
'--sudooption', "!authenticate"])
# Reset testrule to allow everything
result = self.master.run_command(['ipa', 'sudorule-mod',
'testrule',
'--usercat=all',
'--hostcat=all',
'--cmdcat=all',
'--runasusercat=all',
'--runasgroupcat=all'],
raiseonerr=False)
return result
def test_nisdomainname(self):
result = self.client.run_command('nisdomainname')
assert self.client.domain.name in result.stdout_text
def test_add_sudo_commands(self):
# Group: Readers
self.master.run_command(['ipa', 'sudocmd-add', '/usr/bin/cat'])
self.master.run_command(['ipa', 'sudocmd-add', '/usr/bin/tail'])
# No group
self.master.run_command(['ipa', 'sudocmd-add', '/usr/bin/yum'])
def test_add_sudo_command_groups(self):
self.master.run_command(['ipa', 'sudocmdgroup-add', 'readers',
'--desc', '"Applications that read"'])
self.master.run_command(['ipa', 'sudocmdgroup-add-member', 'readers',
'--sudocmds', '/usr/bin/cat'])
self.master.run_command(['ipa', 'sudocmdgroup-add-member', 'readers',
'--sudocmds', '/usr/bin/tail'])
def test_create_allow_all_rule(self):
# Create rule that allows everything
self.master.run_command(['ipa', 'sudorule-add',
'testrule',
'--usercat=all',
'--hostcat=all',
'--cmdcat=all',
'--runasusercat=all',
'--runasgroupcat=all'])
# Add !authenticate option
self.master.run_command(['ipa', 'sudorule-add-option',
'testrule',
'--sudooption', "!authenticate"])
def test_add_sudo_rule(self):
result1 = self.list_sudo_commands("testuser1")
assert "(ALL : ALL) NOPASSWD: ALL" in result1.stdout_text
result2 = self.list_sudo_commands("testuser2")
assert "(ALL : ALL) NOPASSWD: ALL" in result2.stdout_text
def test_sudo_rule_restricted_to_one_user_setup(self):
# Configure the rule to not apply to anybody
self.master.run_command(['ipa', 'sudorule-mod',
'testrule',
'--usercat='])
# Add the testuser1 to the rule
self.master.run_command(['ipa', 'sudorule-add-user',
'testrule',
'--users', 'testuser1'])
def test_sudo_rule_restricted_to_one_user(self):
result1 = self.list_sudo_commands("testuser1")
assert "(ALL : ALL) NOPASSWD: ALL" in result1.stdout_text
result2 = self.list_sudo_commands("testuser2", raiseonerr=False)
assert result2.returncode != 0
assert "Sorry, user testuser2 may not run sudo on {}.".format(
self.clientname) in result2.stderr_text
def test_sudo_rule_restricted_to_one_user_without_defaults_rule(self):
# Verify password is requested with the 'defaults' sudorule disabled
self.master.run_command(['ipa', 'sudorule-disable', 'defaults'])
result3 = self.list_sudo_commands("testuser2", raiseonerr=False)
assert result3.returncode != 0
assert "sudo: a password is required" in result3.stderr_text
def test_setting_category_to_all_with_valid_entries_user(self):
result = self.reset_rule_categories(safe_delete=False)
assert result.returncode != 0
def test_sudo_rule_restricted_to_one_user_teardown(self):
# Remove the testuser1 from the rule
self.master.run_command(['ipa', 'sudorule-remove-user',
'testrule',
'--users', 'testuser1'])
self.master.run_command(['ipa', 'sudorule-enable', 'defaults'])
def test_sudo_rule_restricted_to_one_group_setup(self):
# Add the testgroup2 to the rule
self.master.run_command(['ipa', 'sudorule-add-user',
'testrule',
'--groups', 'testgroup2'])
def test_sudo_rule_restricted_to_one_group(self):
result1 = self.list_sudo_commands("testuser1", raiseonerr=False)
assert result1.returncode != 0
assert "Sorry, user testuser1 may not run sudo on {}.".format(
self.clientname) in result1.stderr_text
result2 = self.list_sudo_commands("testuser2")
assert "(ALL : ALL) NOPASSWD: ALL" in result2.stdout_text
def test_setting_category_to_all_with_valid_entries_user_group(self):
result = self.reset_rule_categories(safe_delete=False)
assert result.returncode != 0
def test_sudo_rule_restricted_to_one_group_teardown(self):
# Remove the testgroup2 from the rule
self.master.run_command(['ipa', 'sudorule-remove-user',
'testrule',
'--groups', 'testgroup2'])
def test_sudo_rule_restricted_to_one_host_negative_setup(self):
# Reset testrule configuration
self.reset_rule_categories()
# Configure the rule to not apply anywhere
self.master.run_command(['ipa', 'sudorule-mod',
'testrule',
'--hostcat='])
# Add the master to the rule
self.master.run_command(['ipa', 'sudorule-add-host',
'testrule',
'--hosts', self.master.hostname])
def test_sudo_rule_restricted_to_one_host_negative(self):
result1 = self.list_sudo_commands("testuser1", raiseonerr=False)
assert result1.returncode != 0
assert "Sorry, user testuser1 may not run sudo on {}.".format(
self.clientname) in result1.stderr_text
def test_sudo_rule_restricted_to_one_host_negative_teardown(self):
# Remove the master from the rule
self.master.run_command(['ipa', 'sudorule-remove-host',
'testrule',
'--hosts', self.master.hostname])
def test_sudo_rule_restricted_to_one_host_setup(self):
# Configure the rulle to not apply anywhere
self.master.run_command(['ipa', 'sudorule-mod',
'testrule',
'--hostcat='], raiseonerr=False)
# Add the master to the rule
self.master.run_command(['ipa', 'sudorule-add-host',
'testrule',
'--hosts', self.client.hostname])
def test_sudo_rule_restricted_to_one_host(self):
result1 = self.list_sudo_commands("testuser1", raiseonerr=False)
assert "(ALL : ALL) NOPASSWD: ALL" in result1.stdout_text
def test_setting_category_to_all_with_valid_entries_host(self):
result = self.reset_rule_categories(safe_delete=False)
assert result.returncode != 0
def test_sudo_rule_restricted_to_one_host_teardown(self):
# Remove the master from the rule
self.master.run_command(['ipa', 'sudorule-remove-host',
'testrule',
'--hosts', self.client.hostname])
def test_sudo_rule_restricted_to_one_hostgroup_setup(self):
# Add the testhostgroup to the rule
self.master.run_command(['ipa', 'sudorule-add-host',
'testrule',
'--hostgroups', 'testhostgroup'])
def test_sudo_rule_restricted_to_one_hostgroup(self):
result1 = self.list_sudo_commands("testuser1")
assert "(ALL : ALL) NOPASSWD: ALL" in result1.stdout_text
def test_setting_category_to_all_with_valid_entries_host_group(self):
result = self.reset_rule_categories(safe_delete=False)
assert result.returncode != 0
def test_sudo_rule_restricted_to_one_hostgroup_teardown(self):
# Remove the testhostgroup from the rule
self.master.run_command(['ipa', 'sudorule-remove-host',
'testrule',
'--hostgroups', 'testhostgroup'])
def test_sudo_rule_restricted_to_one_hostmask_setup(self):
# We need to detect the hostmask first
full_ip = get_host_ip_with_hostmask(self.client)
# Make a note for the next test, which needs to be skipped
# if hostmask detection failed
self.__class__.skip_hostmask_based = False
if not full_ip:
self.__class__.skip_hostmask_based = True
raise pytest.skip("Hostmask could not be detected")
self.master.run_command(['ipa', '-n', 'sudorule-add-host',
'testrule',
'--hostmask', full_ip])
# SSSD >= 1.13.3-3 uses native IPA schema instead of compat entries to
# pull in sudoers. Since native schema does not (yet) support
# hostmasks, we need to point ldap_sudo_search_base to the old schema
domain = self.client.domain
modify_sssd_conf(
self.client,
domain.name,
{
'ldap_sudo_search_base': 'ou=sudoers,{}'.format(domain.basedn)
},
provider_subtype='sudo'
)
def test_sudo_rule_restricted_to_one_hostmask(self):
if self.__class__.skip_hostmask_based:
raise pytest.skip("Hostmask could not be detected")
result1 = self.list_sudo_commands("testuser1")
assert "(ALL : ALL) NOPASSWD: ALL" in result1.stdout_text
def test_setting_category_to_all_with_valid_entries_host_mask(self):
result = self.reset_rule_categories(safe_delete=False)
assert result.returncode != 0
def test_sudo_rule_restricted_to_one_hostmask_teardown(self):
if self.__class__.skip_hostmask_based:
raise pytest.skip("Hostmask could not be detected")
# Detect the hostmask first to delete the hostmask based rule
full_ip = get_host_ip_with_hostmask(self.client)
# Remove the client's hostmask from the rule
self.master.run_command(['ipa', '-n', 'sudorule-remove-host',
'testrule',
'--hostmask', full_ip])
def test_sudo_rule_restricted_to_one_hostmask_negative_setup(self):
# Add the master's hostmask to the rule
ip = self.master.ip
self.master.run_command(['ipa', '-n', 'sudorule-add-host',
'testrule',
'--hostmask', '%s/32' % ip])
def test_sudo_rule_restricted_to_one_hostmask_negative(self):
result1 = self.list_sudo_commands("testuser1")
assert result1.returncode != 0
assert "Sorry, user testuser1 may not run sudo on {}.".format(
self.clientname) in result1.stderr_text
def test_sudo_rule_restricted_to_one_hostmask_negative_teardown(self):
# Remove the master's hostmask from the rule
ip = self.master.ip
self.master.run_command(['ipa', '-n', 'sudorule-remove-host',
'testrule',
'--hostmask', '%s/32' % ip])
# reset ldap_sudo_search_base back to the default value, the old
# schema is not needed for the upcoming tests
domain = self.client.domain
modify_sssd_conf(
self.client,
domain.name,
{
'ldap_sudo_search_base': None
},
provider_subtype='sudo'
)
def test_sudo_rule_restricted_to_one_command_setup(self):
# Reset testrule configuration
self.reset_rule_categories()
# Configure the rule to not allow any command
self.master.run_command(['ipa', 'sudorule-mod',
'testrule',
'--cmdcat='])
# Add the yum command to the rule
self.master.run_command(['ipa', 'sudorule-add-allow-command',
'testrule',
'--sudocmds', '/usr/bin/yum'])
def test_sudo_rule_restricted_to_one_command(self):
result1 = self.list_sudo_commands("testuser1")
assert "(ALL : ALL) NOPASSWD: /usr/bin/yum" in result1.stdout_text
def test_sudo_rule_restricted_to_command_and_command_group_setup(self):
# Add the readers command group to the rule
self.master.run_command(['ipa', 'sudorule-add-allow-command',
'testrule',
'--sudocmdgroups', 'readers'])
def test_sudo_rule_restricted_to_command_and_command_group(self):
result1 = self.list_sudo_commands("testuser1")
assert "(ALL : ALL) NOPASSWD:" in result1.stdout_text
assert "/usr/bin/yum" in result1.stdout_text
assert "/usr/bin/tail" in result1.stdout_text
assert "/usr/bin/cat" in result1.stdout_text
def test_setting_category_to_all_with_valid_entries_command(self):
result = self.reset_rule_categories(safe_delete=False)
assert result.returncode != 0
def test_sudo_rule_restricted_to_command_and_command_group_teardown(self):
# Remove the yum command from the rule
self.master.run_command(['ipa', 'sudorule-remove-allow-command',
'testrule',
'--sudocmds', '/usr/bin/yum'])
# Remove the readers command group from the rule
self.master.run_command(['ipa', 'sudorule-remove-allow-command',
'testrule',
'--sudocmdgroups', 'readers'])
def test_sudo_rule_restricted_to_running_as_single_user_setup(self):
# Reset testrule configuration
self.reset_rule_categories()
# Configure the rule to not allow running commands as anybody
self.master.run_command(['ipa', 'sudorule-mod',
'testrule',
'--runasusercat='])
self.master.run_command(['ipa', 'sudorule-mod',
'testrule',
'--runasgroupcat='])
# Allow running commands as testuser2
self.master.run_command(['ipa', 'sudorule-add-runasuser',
'testrule',
'--users', 'testuser2'])
def test_sudo_rule_restricted_to_running_as_single_user(self):
result1 = self.list_sudo_commands("testuser1", verbose=True)
assert "RunAsUsers: testuser2" in result1.stdout_text
assert "RunAsGroups:" not in result1.stdout_text
def test_setting_category_to_all_with_valid_entries_runasuser(self):
result = self.reset_rule_categories(safe_delete=False)
assert result.returncode != 0
def test_sudo_rule_restricted_to_running_as_single_user_teardown(self):
# Remove permission to run commands as testuser2
self.master.run_command(['ipa', 'sudorule-remove-runasuser',
'testrule',
'--users', 'testuser2'])
def test_sudo_rule_restricted_to_running_as_single_local_user_setup(self):
# Allow running commands as testuser2
self.master.run_command(['ipa', 'sudorule-add-runasuser',
'testrule',
'--users', 'localuser'])
def test_sudo_rule_restricted_to_running_as_single_local_user(self):
result1 = self.list_sudo_commands("testuser1", verbose=True)
assert "RunAsUsers: localuser" in result1.stdout_text
assert "RunAsGroups:" not in result1.stdout_text
def test_setting_category_to_all_with_valid_entries_runasuser_local(self):
result = self.reset_rule_categories(safe_delete=False)
assert result.returncode != 0
def test_sudo_rule_restricted_to_running_as_single_user_local_tear(self):
# Remove permission to run commands as testuser2
self.master.run_command(['ipa', 'sudorule-remove-runasuser',
'testrule',
'--users', 'localuser'])
def test_sudo_rule_restricted_to_running_as_users_from_group_setup(self):
# Allow running commands as users from testgroup2
self.master.run_command(['ipa', 'sudorule-add-runasuser',
'testrule',
'--groups', 'testgroup2'])
def test_sudo_rule_restricted_to_running_as_users_from_group(self):
result1 = self.list_sudo_commands("testuser1", verbose=True)
assert "RunAsUsers: %testgroup2" in result1.stdout_text
assert "RunAsGroups:" not in result1.stdout_text
def test_setting_category_to_all_with_valid_entries_runasuser_group(self):
result = self.reset_rule_categories(safe_delete=False)
assert result.returncode != 0
def test_sudo_rule_restricted_to_running_as_users_from_group_teardown(self):
# Remove permission to run commands as testuser2
self.master.run_command(['ipa', 'sudorule-remove-runasuser',
'testrule',
'--groups', 'testgroup2'])
def test_sudo_rule_restricted_to_run_as_users_from_local_group_setup(self):
# Allow running commands as users from localgroup
self.master.run_command(['ipa', 'sudorule-add-runasuser',
'testrule',
'--groups', 'localgroup'])
def test_sudo_rule_restricted_to_run_as_users_from_local_group(self):
result1 = self.list_sudo_commands("testuser1", verbose=True)
assert "RunAsUsers: %localgroup" in result1.stdout_text
assert "RunAsGroups:" not in result1.stdout_text
def test_set_category_to_all_with_valid_entries_runasuser_group_local(self):
result = self.reset_rule_categories(safe_delete=False)
assert result.returncode != 0
def test_sudo_rule_restricted_to_run_as_users_from_local_group_tear(self):
# Remove permission to run commands as testuser2
self.master.run_command(['ipa', 'sudorule-remove-runasuser',
'testrule',
'--groups', 'localgroup'])
def test_sudo_rule_restricted_to_running_as_single_group_setup(self):
# Allow running commands as testgroup2
self.master.run_command(['ipa', 'sudorule-add-runasgroup',
'testrule',
'--groups', 'testgroup2'])
def test_sudo_rule_restricted_to_running_as_single_group(self):
result1 = self.list_sudo_commands("testuser1", verbose=True)
assert "RunAsUsers: testuser1" in result1.stdout_text
assert "RunAsGroups: testgroup2" in result1.stdout_text
def test_setting_category_to_all_with_valid_entries_runasgroup(self):
result = self.reset_rule_categories(safe_delete=False)
assert result.returncode != 0
def test_sudo_rule_restricted_to_running_as_single_group_teardown(self):
# Remove permission to run commands as testgroup2
self.master.run_command(['ipa', 'sudorule-remove-runasgroup',
'testrule',
'--groups', 'testgroup2'])
def test_sudo_rule_restricted_to_running_as_single_local_group_setup(self):
# Allow running commands as testgroup2
self.master.run_command(['ipa', 'sudorule-add-runasgroup',
'testrule',
'--groups', 'localgroup'])
def test_sudo_rule_restricted_to_running_as_single_local_group(self):
result1 = self.list_sudo_commands("testuser1", verbose=True)
assert "RunAsUsers: testuser1" in result1.stdout_text
assert "RunAsGroups: localgroup" in result1.stdout_text
def test_setting_category_to_all_with_valid_entries_runasgroup_local(self):
result = self.reset_rule_categories(safe_delete=False)
assert result.returncode != 0
def test_sudo_rule_restricted_to_running_as_single_local_group_tear(self):
# Remove permission to run commands as testgroup2
self.master.run_command(['ipa', 'sudorule-remove-runasgroup',
'testrule',
'--groups', 'localgroup'])
def test_category_all_validation_setup(self):
# Reset testrule configuration
self.reset_rule_categories()
def test_category_all_validation_user(self):
# Add the testuser1 to the rule
result = self.master.run_command(['ipa', 'sudorule-add-user',
'testrule',
'--users', 'testuser1'],
raiseonerr=False)
assert result.returncode != 0
def test_category_all_validation_user_group(self):
# Try to add the testgroup2 to the rule
result = self.master.run_command(['ipa', 'sudorule-add-user',
'testrule',
'--groups', 'testgroup2'],
raiseonerr=False)
assert result.returncode != 0
def test_category_all_validation_user_local(self):
# Try to add the local user to the rule
result = self.master.run_command(['ipa', 'sudorule-add-user',
'testrule',
'--users', 'localuser'],
raiseonerr=False)
assert result.returncode != 0
def test_category_all_validation_host(self):
# Try to add the master to the rule
result = self.master.run_command(['ipa', 'sudorule-add-host',
'testrule',
'--hosts', self.master.hostname],
raiseonerr=False)
assert result.returncode != 0
def test_category_all_validation_host_group(self):
# Try to add the testhostgroup to the rule
result = self.master.run_command(['ipa', 'sudorule-add-host',
'testrule',
'--hostgroups', 'testhostgroup'],
raiseonerr=False)
assert result.returncode != 0
def test_category_all_validation_host_mask(self):
# Try to add the client's /24 hostmask to the rule
ip = self.client.ip
result = self.master.run_command(['ipa', '-n', 'sudorule-add-host',
'testrule',
'--hostmask', '%s/24' % ip],
raiseonerr=False)
assert result.returncode != 0
def test_category_all_validation_command_allow(self):
# Try to add the yum command to the rule
result = self.master.run_command(['ipa', 'sudorule-add-allow-command',
'testrule',
'--sudocmds', '/usr/bin/yum'],
raiseonerr=False)
assert result.returncode != 0
def test_category_all_validation_command_allow_group(self):
# Try to add the readers command group to the rule
result = self.master.run_command(['ipa', 'sudorule-add-allow-command',
'testrule',
'--sudocmdgroups', 'readers'],
raiseonerr=False)
assert result.returncode != 0
def test_category_all_validation_command_deny(self):
# Try to add the yum command to the rule
# This SHOULD be allowed
self.master.run_command(['ipa', 'sudorule-add-deny-command',
'testrule',
'--sudocmds', '/usr/bin/yum'],
raiseonerr=False)
self.master.run_command(['ipa', 'sudorule-remove-deny-command',
'testrule',
'--sudocmds', '/usr/bin/yum'],
raiseonerr=False)
def test_category_all_validation_command_deny_group(self):
# Try to add the readers command group to the rule
# This SHOULD be allowed
self.master.run_command(['ipa', 'sudorule-add-deny-command',
'testrule',
'--sudocmdgroups', 'readers'])
self.master.run_command(['ipa', 'sudorule-remove-deny-command',
'testrule',
'--sudocmdgroups', 'readers'])
def test_category_all_validation_runasuser(self):
# Try to allow running commands as testuser2
result = self.master.run_command(['ipa', 'sudorule-add-runasuser',
'testrule',
'--users', 'testuser2'],
raiseonerr=False)
assert result.returncode != 0
def test_category_all_validation_runasuser_group(self):
# Try to allow running commands as users from testgroup2
result = self.master.run_command(['ipa', 'sudorule-add-runasuser',
'testrule',
'--groups', 'testgroup2'],
raiseonerr=False)
assert result.returncode != 0
def test_category_all_validation_runasgroup(self):
# Try to allow running commands as testgroup2
result = self.master.run_command(['ipa', 'sudorule-add-runasgroup',
'testrule',
'--groups', 'testgroup2'],
raiseonerr=False)
assert result.returncode != 0

View File

@@ -0,0 +1,448 @@
# Authors:
# Petr Viktorin <pviktori@redhat.com>
#
# Copyright (C) 2014 Red Hat
# see file 'COPYING' for use and warranty information
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import json
import copy
from ipatests.pytest_plugins.integration import config
from ipapython.ipautil import write_tmp_file
from ipatests.util import assert_deepequal
from ipalib.constants import MAX_DOMAIN_LEVEL
DEFAULT_OUTPUT_DICT = {
"nis_domain": "ipatest",
"test_dir": "/root/ipatests",
"ad_admin_name": "Administrator",
"ipv6": False,
"ssh_key_filename": "~/.ssh/id_rsa",
"ssh_username": "root",
"admin_name": "admin",
"ad_admin_password": "Secret123",
"ssh_password": None,
"dns_forwarder": "8.8.8.8",
"domains": [],
"dirman_dn": "cn=Directory Manager",
"dirman_password": "Secret123",
"ntp_server": "ntp.clock.test",
"admin_password": "Secret123",
"domain_level": MAX_DOMAIN_LEVEL,
"log_journal_since": "-1h",
}
DEFAULT_OUTPUT_ENV = {
"IPATEST_DIR": "/root/ipatests",
"IPA_ROOT_SSH_KEY": "~/.ssh/id_rsa",
"IPA_ROOT_SSH_PASSWORD": "",
"ADMINID": "admin",
"ADMINPW": "Secret123",
"ROOTDN": "cn=Directory Manager",
"ROOTDNPWD": "Secret123",
"DNSFORWARD": "8.8.8.8",
"NISDOMAIN": "ipatest",
"NTPSERVER": "ntp.clock.test",
"ADADMINID": "Administrator",
"ADADMINPW": "Secret123",
"IPv6SETUP": "",
"IPADEBUG": "",
"DOMAINLVL": str(MAX_DOMAIN_LEVEL),
"LOG_JOURNAL_SINCE": "-1h",
}
DEFAULT_INPUT_ENV = {
'NTPSERVER': 'ntp.clock.test',
}
DEFAULT_INPUT_DICT = {
'ntp_server': 'ntp.clock.test',
'domains': [],
}
def extend_dict(defaults, *others, **kwargs):
result = dict(defaults)
for other in others:
result.update(other)
result.update(kwargs)
return copy.deepcopy(result)
class CheckConfig(object):
def check_config(self, conf):
pass
def get_input_env(self):
return extend_dict(DEFAULT_INPUT_ENV, self.extra_input_env)
def get_output_env(self):
return extend_dict(DEFAULT_OUTPUT_ENV, self.extra_output_env)
def get_input_dict(self):
return extend_dict(DEFAULT_INPUT_DICT, self.extra_input_dict)
def get_output_dict(self):
return extend_dict(DEFAULT_OUTPUT_DICT, self.extra_output_dict)
def test_env_to_dict(self):
conf = config.Config.from_env(self.get_input_env())
assert_deepequal(self.get_output_dict(), conf.to_dict())
self.check_config(conf)
def test_env_to_env(self):
conf = config.Config.from_env(self.get_input_env())
assert_deepequal(self.get_output_env(), dict(conf.to_env()))
self.check_config(conf)
def test_dict_to_env(self):
conf = config.Config.from_dict(self.get_input_dict())
assert_deepequal(self.get_output_env(), dict(conf.to_env()))
self.check_config(conf)
def test_dict_to_dict(self):
conf = config.Config.from_dict(self.get_input_dict())
assert_deepequal(self.get_output_dict(), conf.to_dict())
self.check_config(conf)
def test_env_roundtrip(self):
conf = config.Config.from_env(self.get_output_env())
assert_deepequal(self.get_output_env(), dict(conf.to_env()))
self.check_config(conf)
def test_dict_roundtrip(self):
conf = config.Config.from_dict(self.get_output_dict())
assert_deepequal(self.get_output_dict(), conf.to_dict())
self.check_config(conf)
def test_from_json_file(self):
file = write_tmp_file(json.dumps(self.get_input_dict()))
conf = config.Config.from_env({'IPATEST_JSON_CONFIG': file.name})
assert_deepequal(self.get_output_dict(), conf.to_dict())
self.check_config(conf)
# Settings to override:
extra_input_dict = {}
extra_input_env = {}
extra_output_dict = {}
extra_output_env = {}
class TestEmptyConfig(CheckConfig):
extra_input_dict = {}
extra_input_env = {}
extra_output_dict = {}
extra_output_env = {}
class TestMinimalConfig(CheckConfig):
extra_input_dict = dict(
domains=[
dict(name='ipadomain.test', type='IPA', hosts=[
dict(name='master', ip='192.0.2.1'),
]),
],
)
extra_input_env = dict(
MASTER='master.ipadomain.test',
BEAKERMASTER1_IP_env1='192.0.2.1',
)
extra_output_dict = dict(
domains=[
dict(
type="IPA",
name="ipadomain.test",
hosts=[
dict(
name='master.ipadomain.test',
ip="192.0.2.1",
external_hostname="master.ipadomain.test",
role="master",
),
],
),
],
)
extra_output_env = dict(
DOMAIN_env1="ipadomain.test",
RELM_env1="IPADOMAIN.TEST",
BASEDN_env1="dc=ipadomain,dc=test",
MASTER_env1="master.ipadomain.test",
BEAKERMASTER_env1="master.ipadomain.test",
BEAKERMASTER_IP_env1="192.0.2.1",
MASTER1_env1="master.ipadomain.test",
BEAKERMASTER1_env1="master.ipadomain.test",
BEAKERMASTER1_IP_env1="192.0.2.1",
MASTER="master.ipadomain.test",
BEAKERMASTER="master.ipadomain.test",
MASTERIP="192.0.2.1",
)
def check_config(self, conf):
assert len(conf.domains) == 1
assert conf.domains[0].name == 'ipadomain.test'
assert conf.domains[0].type == 'IPA'
assert len(conf.domains[0].hosts) == 1
master = conf.domains[0].master
assert master == conf.domains[0].hosts[0]
assert master.hostname == 'master.ipadomain.test'
assert master.role == 'master'
assert conf.domains[0].replicas == []
assert conf.domains[0].clients == []
assert conf.domains[0].hosts_by_role('replica') == []
assert conf.domains[0].host_by_role('master') == master
class TestComplexConfig(CheckConfig):
extra_input_dict = dict(
domains=[
dict(name='ipadomain.test', type='IPA', hosts=[
dict(name='master', ip='192.0.2.1', role='master'),
dict(name='replica1', ip='192.0.2.2', role='replica'),
dict(name='replica2', ip='192.0.2.3', role='replica',
external_hostname='r2.ipadomain.test'),
dict(name='client1', ip='192.0.2.4', role='client'),
dict(name='client2', ip='192.0.2.5', role='client',
external_hostname='c2.ipadomain.test'),
dict(name='extra', ip='192.0.2.6', role='extrarole'),
dict(name='extram1', ip='192.0.2.7', role='extrarolem'),
dict(name='extram2', ip='192.0.2.8', role='extrarolem',
external_hostname='e2.ipadomain.test'),
]),
dict(name='addomain.test', type='AD', hosts=[
dict(name='ad', ip='192.0.2.33', role='ad'),
]),
dict(name='ipadomain2.test', type='IPA', hosts=[
dict(name='master.ipadomain2.test', ip='192.0.2.65'),
]),
],
)
extra_input_env = dict(
MASTER='master.ipadomain.test',
BEAKERMASTER1_IP_env1='192.0.2.1',
REPLICA='replica1.ipadomain.test replica2.ipadomain.test',
BEAKERREPLICA1_IP_env1='192.0.2.2',
BEAKERREPLICA2_IP_env1='192.0.2.3',
BEAKERREPLICA2_env1='r2.ipadomain.test',
CLIENT='client1.ipadomain.test client2.ipadomain.test',
BEAKERCLIENT1_IP_env1='192.0.2.4',
BEAKERCLIENT2_IP_env1='192.0.2.5',
BEAKERCLIENT2_env1='c2.ipadomain.test',
TESTHOST_EXTRAROLE_env1='extra.ipadomain.test',
BEAKEREXTRAROLE1_IP_env1='192.0.2.6',
TESTHOST_EXTRAROLEM_env1='extram1.ipadomain.test extram2.ipadomain.test',
BEAKEREXTRAROLEM1_IP_env1='192.0.2.7',
BEAKEREXTRAROLEM2_IP_env1='192.0.2.8',
BEAKEREXTRAROLEM2_env1='e2.ipadomain.test',
AD_env2='ad.addomain.test',
BEAKERAD1_IP_env2='192.0.2.33',
MASTER_env3='master.ipadomain2.test',
BEAKERMASTER1_IP_env3='192.0.2.65',
)
extra_output_dict = dict(
domains=[
dict(
type="IPA",
name="ipadomain.test",
hosts=[
dict(
name='master.ipadomain.test',
ip="192.0.2.1",
external_hostname="master.ipadomain.test",
role="master",
),
dict(
name='replica1.ipadomain.test',
ip="192.0.2.2",
external_hostname="replica1.ipadomain.test",
role="replica",
),
dict(
name='replica2.ipadomain.test',
ip="192.0.2.3",
external_hostname="r2.ipadomain.test",
role="replica",
),
dict(
name='client1.ipadomain.test',
ip="192.0.2.4",
external_hostname="client1.ipadomain.test",
role="client",
),
dict(
name='client2.ipadomain.test',
ip="192.0.2.5",
external_hostname="c2.ipadomain.test",
role="client",
),
dict(
name='extra.ipadomain.test',
ip="192.0.2.6",
external_hostname="extra.ipadomain.test",
role="extrarole",
),
dict(
name='extram1.ipadomain.test',
ip="192.0.2.7",
external_hostname="extram1.ipadomain.test",
role="extrarolem",
),
dict(
name='extram2.ipadomain.test',
ip="192.0.2.8",
external_hostname="e2.ipadomain.test",
role="extrarolem",
),
],
),
dict(
type="AD",
name="addomain.test",
hosts=[
dict(
name='ad.addomain.test',
ip="192.0.2.33",
external_hostname="ad.addomain.test",
role="ad",
),
],
),
dict(
type="IPA",
name="ipadomain2.test",
hosts=[
dict(
name='master.ipadomain2.test',
ip="192.0.2.65",
external_hostname="master.ipadomain2.test",
role="master",
),
],
),
],
)
extra_output_env = extend_dict(extra_input_env,
DOMAIN_env1="ipadomain.test",
RELM_env1="IPADOMAIN.TEST",
BASEDN_env1="dc=ipadomain,dc=test",
MASTER_env1="master.ipadomain.test",
BEAKERMASTER_env1="master.ipadomain.test",
BEAKERMASTER_IP_env1="192.0.2.1",
MASTER="master.ipadomain.test",
BEAKERMASTER="master.ipadomain.test",
MASTERIP="192.0.2.1",
MASTER1_env1="master.ipadomain.test",
BEAKERMASTER1_env1="master.ipadomain.test",
BEAKERMASTER1_IP_env1="192.0.2.1",
REPLICA_env1="replica1.ipadomain.test replica2.ipadomain.test",
BEAKERREPLICA_env1="replica1.ipadomain.test r2.ipadomain.test",
BEAKERREPLICA_IP_env1="192.0.2.2 192.0.2.3",
REPLICA="replica1.ipadomain.test replica2.ipadomain.test",
REPLICA1_env1="replica1.ipadomain.test",
BEAKERREPLICA1_env1="replica1.ipadomain.test",
BEAKERREPLICA1_IP_env1="192.0.2.2",
REPLICA2_env1="replica2.ipadomain.test",
BEAKERREPLICA2_env1="r2.ipadomain.test",
BEAKERREPLICA2_IP_env1="192.0.2.3",
SLAVE="replica1.ipadomain.test replica2.ipadomain.test",
BEAKERSLAVE="replica1.ipadomain.test r2.ipadomain.test",
SLAVEIP="192.0.2.2 192.0.2.3",
CLIENT_env1="client1.ipadomain.test client2.ipadomain.test",
BEAKERCLIENT_env1="client1.ipadomain.test c2.ipadomain.test",
BEAKERCLIENT='client1.ipadomain.test',
BEAKERCLIENT2='c2.ipadomain.test',
BEAKERCLIENT_IP_env1="192.0.2.4 192.0.2.5",
CLIENT="client1.ipadomain.test",
CLIENT2="client2.ipadomain.test",
CLIENT1_env1="client1.ipadomain.test",
BEAKERCLIENT1_env1="client1.ipadomain.test",
BEAKERCLIENT1_IP_env1="192.0.2.4",
CLIENT2_env1="client2.ipadomain.test",
BEAKERCLIENT2_env1="c2.ipadomain.test",
BEAKERCLIENT2_IP_env1="192.0.2.5",
TESTHOST_EXTRAROLE_env1="extra.ipadomain.test",
BEAKEREXTRAROLE_env1="extra.ipadomain.test",
BEAKEREXTRAROLE_IP_env1="192.0.2.6",
TESTHOST_EXTRAROLE1_env1="extra.ipadomain.test",
BEAKEREXTRAROLE1_env1="extra.ipadomain.test",
BEAKEREXTRAROLE1_IP_env1="192.0.2.6",
TESTHOST_EXTRAROLEM_env1="extram1.ipadomain.test extram2.ipadomain.test",
BEAKEREXTRAROLEM_env1="extram1.ipadomain.test e2.ipadomain.test",
BEAKEREXTRAROLEM_IP_env1="192.0.2.7 192.0.2.8",
TESTHOST_EXTRAROLEM1_env1="extram1.ipadomain.test",
BEAKEREXTRAROLEM1_env1="extram1.ipadomain.test",
BEAKEREXTRAROLEM1_IP_env1="192.0.2.7",
TESTHOST_EXTRAROLEM2_env1="extram2.ipadomain.test",
BEAKEREXTRAROLEM2_env1="e2.ipadomain.test",
BEAKEREXTRAROLEM2_IP_env1="192.0.2.8",
DOMAIN_env2="addomain.test",
RELM_env2="ADDOMAIN.TEST",
BASEDN_env2="dc=addomain,dc=test",
AD_env2="ad.addomain.test",
BEAKERAD_env2="ad.addomain.test",
BEAKERAD_IP_env2="192.0.2.33",
AD1_env2="ad.addomain.test",
BEAKERAD1_env2="ad.addomain.test",
BEAKERAD1_IP_env2="192.0.2.33",
DOMAIN_env3="ipadomain2.test",
RELM_env3="IPADOMAIN2.TEST",
BASEDN_env3="dc=ipadomain2,dc=test",
MASTER_env3="master.ipadomain2.test",
BEAKERMASTER_env3="master.ipadomain2.test",
BEAKERMASTER_IP_env3="192.0.2.65",
MASTER1_env3="master.ipadomain2.test",
BEAKERMASTER1_env3="master.ipadomain2.test",
BEAKERMASTER1_IP_env3="192.0.2.65",
)
def check_config(self, conf):
assert len(conf.domains) == 3
main_dom = conf.domains[0]
(client1, client2, extra, extram1, extram2, _master,
replica1, replica2) = sorted(main_dom.hosts, key=lambda h: h.role)
assert main_dom.name == 'ipadomain.test'
assert main_dom.type == 'IPA'
assert sorted(main_dom.roles) == ['client', 'extrarole', 'extrarolem',
'master', 'replica']
assert main_dom.static_roles == ('master', 'replica', 'client', 'other')
assert sorted(main_dom.extra_roles) == ['extrarole', 'extrarolem']
assert main_dom.replicas == [replica1, replica2]
assert main_dom.clients == [client1, client2]
assert main_dom.hosts_by_role('replica') == [replica1, replica2]
assert main_dom.hosts_by_role('extrarolem') == [extram1, extram2]
assert main_dom.host_by_role('extrarole') == extra
assert extra.ip == '192.0.2.6'
assert extram2.hostname == 'extram2.ipadomain.test'
assert extram2.external_hostname == 'e2.ipadomain.test'
ad_dom = conf.domains[1]
assert ad_dom.roles == ['ad']
assert ad_dom.static_roles == ('ad',)
assert ad_dom.extra_roles == []

View File

@@ -0,0 +1,181 @@
# Authors:
# Petr Viktorin <pviktori@redhat.com>
#
# Copyright (C) 2013 Red Hat
# see file 'COPYING' for use and warranty information
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from ipatests.pytest_plugins.integration import tasks
def test_topology_star():
topo = tasks.get_topo('star')
assert topo == tasks.star_topo
assert list(topo('M', [1, 2, 3, 4, 5])) == [
('M', 1),
('M', 2),
('M', 3),
('M', 4),
('M', 5),
]
assert list(topo('M', [])) == []
def test_topology_line():
topo = tasks.get_topo('line')
assert topo == tasks.line_topo
assert list(topo('M', [1, 2, 3, 4, 5])) == [
('M', 1),
(1, 2),
(2, 3),
(3, 4),
(4, 5),
]
assert list(topo('M', [])) == []
def test_topology_tree():
topo = tasks.get_topo('tree')
assert topo == tasks.tree_topo
assert list(topo('M', [1, 2, 3, 4, 5])) == [
('M', 1),
('M', 2),
(1, 3),
(1, 4),
(2, 5),
]
assert list(topo('M', [1, 2, 3, 4, 5, 6, 7, 8, 9, 10])) == [
('M', 1),
('M', 2),
(1, 3),
(1, 4),
(2, 5),
(2, 6),
(3, 7),
(3, 8),
(4, 9),
(4, 10),
]
assert list(topo('M', [])) == []
def test_topology_tree2():
topo = tasks.get_topo('tree2')
assert topo == tasks.tree2_topo
assert list(topo('M', [1, 2, 3, 4, 5])) == [
('M', 1),
('M', 2),
(2, 3),
(3, 4),
(4, 5),
]
assert list(topo('M', [])) == []
def test_topology_complete():
topo = tasks.get_topo('complete')
assert topo == tasks.complete_topo
assert list(topo('M', [1, 2, 3])) == [
('M', 1),
('M', 2),
('M', 3),
(1, 2),
(1, 3),
(2, 3),
]
assert list(topo('M', [])) == []
def test_topology_two_connected():
topo = tasks.get_topo('2-connected')
assert topo == tasks.two_connected_topo
assert list(topo('M', [1, 2, 3, 4, 5, 6, 7, 8])) == [
('M', 1),
('M', 2),
(2, 3),
(1, 3),
('M', 4),
('M', 5),
(4, 6),
(5, 6),
(2, 4),
(2, 7),
(4, 8),
(7, 8),
]
assert list(topo('M', [])) == []
def test_topology_double_circle_topo():
topo = tasks.get_topo('double-circle')
assert topo == tasks.double_circle_topo
assert list(topo('M', list(range(1, 30)))) == [
('M', 1),
(1, 6),
(1, 12),
(6, 7),
(7, 12),
(7, 18),
(12, 13),
(13, 18),
(13, 24),
(18, 19),
(19, 24),
(19, 'M'),
(24, 25),
(25, 'M'),
(25, 6),
('M', 2),
(2, 3),
(2, 4),
(2, 5),
(3, 4),
(3, 5),
(4, 5),
(1, 5),
(6, 8),
(8, 9),
(8, 10),
(8, 11),
(9, 10),
(9, 11),
(10, 11),
(7, 11),
(12, 14),
(14, 15),
(14, 16),
(14, 17),
(15, 16),
(15, 17),
(16, 17),
(13, 17),
(18, 20),
(20, 21),
(20, 22),
(20, 23),
(21, 22),
(21, 23),
(22, 23),
(19, 23),
(24, 26),
(26, 27),
(26, 28),
(26, 29),
(27, 28),
(27, 29),
(28, 29),
(25, 29),
]
assert list(topo('M', [])) == []

View File

@@ -0,0 +1,338 @@
#
# Copyright (C) 2015 FreeIPA Contributors see COPYING for license
#
import re
import pytest
from ipatests.test_integration.base import IntegrationTest
from ipatests.pytest_plugins.integration import tasks
from ipatests.pytest_plugins.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"
def find_segment(master, replica):
result = master.run_command(['ipa', 'topologysegment-find',
DOMAIN_SUFFIX_NAME]).stdout_text
segment_re = re.compile('Left node: (?P<left>\S+)\n.*Right node: '
'(?P<right>\S+)\n')
allsegments = segment_re.findall(result)
for segment in allsegments:
if master.hostname in segment and replica.hostname in segment:
return '-to-'.join(segment)
def remove_segment(master, host1, host2):
"""
This removes a segment between host1 and host2 on master. The function is
needed because test_add_remove_segment expects only one segment, but due to
track tickete N 6250, the test_topology_updated_on_replica_install_remove
leaves 2 topology segments
"""
def wrapper(func):
def wrapped(*args, **kwargs):
try:
func(*args, **kwargs)
finally:
segment = find_segment(host1, host2)
master.run_command(['ipa', 'topologysegment-del',
DOMAIN_SUFFIX_NAME, segment],
raiseonerr=False)
return wrapped
return wrapper
@pytest.mark.skipif(config.domain_level == 0, reason=reasoning)
class TestTopologyOptions(IntegrationTest):
num_replicas = 2
topology = 'star'
rawsegment_re = ('Segment name: (?P<name>.*?)',
'\s+Left node: (?P<lnode>.*?)',
'\s+Right node: (?P<rnode>.*?)',
'\s+Connectivity: (?P<connectivity>\S+)')
segment_re = re.compile("\n".join(rawsegment_re))
noentries_re = re.compile("Number of entries returned (\d+)")
segmentnames_re = re.compile('.*Segment name: (\S+?)\n.*')
@classmethod
def install(cls, mh):
tasks.install_topo(cls.topology, cls.master,
cls.replicas[:-1],
cls.clients)
def tokenize_topologies(self, command_output):
"""
takes an output of `ipa topologysegment-find` and returns an array of
segment hashes
"""
segments = command_output.split("-----------------")[2]
raw_segments = segments.split('\n\n')
result = []
for i in raw_segments:
matched = self.segment_re.search(i)
if matched:
result.append({'leftnode': matched.group('lnode'),
'rightnode': matched.group('rnode'),
'name': matched.group('name'),
'connectivity': matched.group('connectivity')
}
)
return result
@pytest.mark.xfail(reason="Trac 6250", strict=True)
@remove_segment(config.domains[0].master,
config.domains[0].master,
config.domains[0].replicas[1])
def test_topology_updated_on_replica_install_remove(self):
"""
Install and remove a replica and make sure topology information is
updated on all other replicas
Testcase: http://www.freeipa.org/page/V4/Manage_replication_topology/
Test_plan#Test_case:
_Replication_topology_should_be_saved_in_the_LDAP_tree
"""
tasks.kinit_admin(self.master)
result1 = self.master.run_command(['ipa', 'topologysegment-find',
DOMAIN_SUFFIX_NAME]).stdout_text
segment_name = self.segmentnames_re.findall(result1)[0]
assert(self.master.hostname in segment_name), (
"Segment %s does not contain master hostname" % segment_name)
assert(self.replicas[0].hostname in segment_name), (
"Segment %s does not contain replica hostname" % segment_name)
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
# replicas
result2 = self.master.run_command(['ipa', 'topologysegment-find',
DOMAIN_SUFFIX_NAME])
result3 = self.replicas[0].run_command(['ipa', 'topologysegment-find',
DOMAIN_SUFFIX_NAME])
result4 = self.replicas[1].run_command(['ipa', 'topologysegment-find',
DOMAIN_SUFFIX_NAME])
segments = self.tokenize_topologies(result2.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])
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):
"""
Make sure a topology segment can be manually created and deleted
with the influence on the real topology
Testcase http://www.freeipa.org/page/V4/Manage_replication_topology/
Test_plan#Test_case:_Basic_CRUD_test
"""
tasks.kinit_admin(self.master)
# Install the second replica
tasks.install_replica(self.master, self.replicas[1], setup_ca=False,
setup_dns=False)
# turn a star into a ring
segment, err = tasks.create_segment(self.master,
self.replicas[0],
self.replicas[1])
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]).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
# Since segment name can be one of master-to-replica2 or
# replica2-to-master, we need to determine the segment name dynamically
deleteme = find_segment(self.master, self.replicas[1])
returncode, error = tasks.destroy_segment(self.master, deleteme)
assert returncode == 0, error
# 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]).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'])
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):
"""
Testcase: http://www.freeipa.org/page/V4/Manage_replication_topology/
Test_plan#Test_case:
_Removal_of_a_topology_segment_is_allowed_only_if_there_is_at_least_one_more_segment_connecting_the_given_replica
"""
text = "Removal of Segment disconnects topology"
error1 = "The system should not have let you remove the segment"
error2 = "Wrong error message thrown during segment removal: \"%s\""
replicas = (self.replicas[0].hostname, self.replicas[1].hostname)
returncode, error = tasks.destroy_segment(self.master, "%s-to-%s" % replicas)
assert returncode != 0, error1
assert error.count(text) == 1, error2 % error
_newseg, err = tasks.create_segment(
self.master, self.master, self.replicas[1])
assert err == "", err
returncode, error = tasks.destroy_segment(self.master, "%s-to-%s" % replicas)
assert returncode == 0, error
@pytest.mark.skipif(config.domain_level == 0, reason=reasoning)
class TestCASpecificRUVs(IntegrationTest):
num_replicas = 2
topology = 'star'
username = 'testuser'
user_firstname = 'test'
user_lastname = 'user'
def test_delete_ruvs(self):
"""
http://www.freeipa.org/page/V4/Manage_replication_topology_4_4/
Test_Plan#Test_case:_clean-ruv_subcommand
"""
replica = self.replicas[0]
master = self.master
res1 = master.run_command(['ipa-replica-manage', 'list-ruv', '-p',
master.config.dirman_password])
assert(res1.stdout_text.count(replica.hostname) == 2 and
"Certificate Server Replica"
" Update Vectors" in res1.stdout_text), (
"CA-specific RUVs are not displayed")
ruvid_re = re.compile(".*%s:389: (\d+).*" % replica.hostname)
replica_ruvs = ruvid_re.findall(res1.stdout_text)
# Find out the number of RUVids
assert(len(replica_ruvs) == 2), (
"The output should display 2 RUV ids of the selected replica")
# Block replication to preserve replica-specific RUVs
dashed_domain = master.domain.realm.replace(".", '-')
dirsrv_service = "dirsrv@%s.service" % dashed_domain
replica.run_command(['systemctl', 'stop', dirsrv_service])
try:
master.run_command(['ipa-replica-manage', 'clean-ruv',
replica_ruvs[1], '-p',
master.config.dirman_password, '-f'])
res2 = master.run_command(['ipa-replica-manage',
'list-ruv', '-p',
master.config.dirman_password])
assert(res2.stdout_text.count(replica.hostname) == 1), (
"CA RUV of the replica is still displayed")
master.run_command(['ipa-replica-manage', 'clean-ruv',
replica_ruvs[0], '-p',
master.config.dirman_password, '-f'])
res3 = master.run_command(['ipa-replica-manage', 'list-ruv', '-p',
master.config.dirman_password])
assert(replica.hostname not in res3.stdout_text), (
"replica's RUV is still displayed")
finally:
replica.run_command(['systemctl', 'start', dirsrv_service])
def test_replica_uninstall_deletes_ruvs(self):
"""
http://www.freeipa.org/page/V4/Manage_replication_topology_4_4/Test_Plan
#Test_case:_.2A-ruv_subcommands_of_ipa-replica-manage_are_extended
_to_handle_CA-specific_RUVs
"""
master = self.master
replica = self.replicas[1]
res1 = master.run_command(['ipa-replica-manage', 'list-ruv', '-p',
master.config.dirman_password]).stdout_text
assert(res1.count(replica.hostname) == 2), (
"Did not find proper number of replica hostname (%s) occurrencies"
" in the command output: %s" % (replica.hostname, res1))
tasks.uninstall_master(replica)
res2 = master.run_command(['ipa-replica-manage', 'list-ruv', '-p',
master.config.dirman_password]).stdout_text
assert(replica.hostname not in res2), (
"Replica RUVs were not clean during replica uninstallation")
class TestReplicaManageDel(IntegrationTest):
domain_level = 0
topology = 'star'
num_replicas = 3
def test_replica_managed_del_domlevel0(self):
"""
http://www.freeipa.org/page/V4/Manage_replication_topology_4_4/
Test_Plan#Test_case:_ipa-replica-manage_del_with_turned_off_replica
_under_domain_level_0_keeps_ca-related_RUVs
"""
master = self.master
replica = self.replicas[0]
replica.run_command(['ipactl', 'stop'])
master.run_command(['ipa-replica-manage', 'del', '-f', '-p',
master.config.dirman_password, replica.hostname])
result = master.run_command(['ipa-replica-manage', 'list-ruv',
'-p', master.config.dirman_password])
num_ruvs = result.stdout_text.count(replica.hostname)
assert(num_ruvs == 1), ("Expected to find 1 replica's RUV, found %s" %
num_ruvs)
ruvid_re = re.compile(".*%s:389: (\d+).*" % replica.hostname)
replica_ruvs = ruvid_re.findall(result.stdout_text)
master.run_command(['ipa-replica-manage', 'clean-ruv', '-f',
'-p', master.config.dirman_password,
replica_ruvs[0]])
result2 = master.run_command(['ipa-replica-manage', 'list-ruv',
'-p', master.config.dirman_password])
assert(replica.hostname not in result2.stdout_text), (
"Replica's RUV was not properly removed")
def test_clean_dangling_ruv_multi_ca(self):
"""
http://www.freeipa.org/page/V4/Manage_replication_topology_4_4/
Test_Plan#Test_case:_ipa-replica-manage_clean-dangling-ruv_in_a
_multi-CA_setup
"""
master = self.master
replica = self.replicas[1]
replica.run_command(['ipa-server-install', '--uninstall', '-U'])
master.run_command(['ipa-replica-manage', 'del', '-f', '-p',
master.config.dirman_password, replica.hostname])
result1 = master.run_command(['ipa-replica-manage', 'list-ruv', '-p',
master.config.dirman_password])
ruvid_re = re.compile(".*%s:389: (\d+).*" % replica.hostname)
assert(ruvid_re.search(result1.stdout_text)), (
"Replica's RUV should not be removed under domain level 0")
master.run_command(['ipa-replica-manage', 'clean-dangling-ruv', '-p',
master.config.dirman_password], stdin_text="yes\n")
result2 = master.run_command(['ipa-replica-manage', 'list-ruv', '-p',
master.config.dirman_password])
assert(replica.hostname not in result2.stdout_text), (
"Replica's RUV was not removed by a clean-dangling-ruv command")
def test_replica_managed_del_domlevel1(self):
"""
http://www.freeipa.org/page/V4/Manage_replication_topology_4_4/
Test_Plan#Test_case:_ipa-replica-manage_del_with_turned_off_replica
_under_domain_level_1_removes_ca-related_RUVs
"""
master = self.master
replica = self.replicas[2]
master.run_command(['ipa', 'domainlevel-set', '1'])
replica.run_command(['ipactl', 'stop'])
master.run_command(['ipa-replica-manage', 'del', '-f', '-p',
master.config.dirman_password, replica.hostname])
result = master.run_command(['ipa-replica-manage', 'list-ruv',
'-p', master.config.dirman_password])
assert(replica.hostname not in result.stdout_text), (
"Replica's RUV was not properly removed")

View File

@@ -0,0 +1,491 @@
# Authors:
# Tomas Babej <tbabej@redhat.com>
#
# Copyright (C) 2013 Red Hat
# see file 'COPYING' for use and warranty information
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import nose
import re
from ipatests.test_integration.base import IntegrationTest
from ipatests.pytest_plugins.integration import tasks
from ipaplatform.paths import paths
class ADTrustBase(IntegrationTest):
"""Provides common checks for the AD trust integration testing."""
topology = 'line'
num_ad_domains = 1
optional_extra_roles = ['ad_subdomain', 'ad_treedomain']
@classmethod
def install(cls, mh):
if not cls.master.transport.file_exists('/usr/bin/rpcclient'):
raise nose.SkipTest("Package samba-client not available "
"on {}".format(cls.master.hostname))
super(ADTrustBase, cls).install(mh)
cls.ad = cls.ad_domains[0].ads[0]
cls.ad_domain = cls.ad.domain.name
cls.install_adtrust()
cls.check_sid_generation()
# Determine whether the subdomain AD is available
try:
cls.child_ad = cls.host_by_role(cls.optional_extra_roles[0])
cls.ad_subdomain = '.'.join(
cls.child_ad.hostname.split('.')[1:])
except LookupError:
cls.ad_subdomain = None
# Determine whether the tree domain AD is available
try:
cls.tree_ad = cls.host_by_role(cls.optional_extra_roles[1])
cls.ad_treedomain = '.'.join(
cls.tree_ad.hostname.split('.')[1:])
except LookupError:
cls.ad_treedomain = None
cls.configure_dns_and_time()
@classmethod
def install_adtrust(cls):
"""Test adtrust support installation"""
tasks.install_adtrust(cls.master)
@classmethod
def check_sid_generation(cls):
"""Test SID generation"""
command = ['ipa', 'user-show', 'admin', '--all', '--raw']
# TODO: remove duplicate definition and import from common module
_sid_identifier_authority = '(0x[0-9a-f]{1,12}|[0-9]{1,10})'
sid_regex = 'S-1-5-21-%(idauth)s-%(idauth)s-%(idauth)s'\
% dict(idauth=_sid_identifier_authority)
stdout_re = re.escape(' ipaNTSecurityIdentifier: ') + sid_regex
tasks.run_repeatedly(cls.master, command,
test=lambda x: re.search(stdout_re, x))
@classmethod
def configure_dns_and_time(cls):
tasks.configure_dns_for_trust(cls.master, cls.ad_domain)
tasks.sync_time(cls.master, cls.ad)
def test_establish_trust(self):
"""Tests establishing trust with Active Directory"""
tasks.establish_trust_with_ad(self.master, self.ad_domain,
extra_args=['--range-type', 'ipa-ad-trust'])
def test_all_trustdomains_found(self):
"""
Tests that all trustdomains can be found.
"""
if self.ad_subdomain is None:
raise nose.SkipTest('AD subdomain is not available.')
result = self.master.run_command(['ipa',
'trustdomain-find',
self.ad_domain])
# Check that all trustdomains appear in the result
assert self.ad_domain in result.stdout_text
assert self.ad_subdomain in result.stdout_text
assert self.ad_treedomain in result.stdout_text
class ADTrustSubdomainBase(ADTrustBase):
"""
Base class for tests involving subdomains of trusted forests
"""
@classmethod
def configure_dns_and_time(cls):
tasks.configure_dns_for_trust(cls.master, cls.ad_subdomain)
tasks.sync_time(cls.master, cls.child_ad)
@classmethod
def install(cls, mh):
super(ADTrustSubdomainBase, cls).install(mh)
if not cls.ad_subdomain:
raise nose.SkipTest('AD subdomain is not available.')
class ADTrustTreedomainBase(ADTrustBase):
"""
Base class for tests involving tree root domains of trusted forests
"""
@classmethod
def configure_dns_and_time(cls):
tasks.configure_dns_for_trust(cls.master, cls.ad_treedomain)
tasks.sync_time(cls.master, cls.tree_ad)
@classmethod
def install(cls, mh):
super(ADTrustTreedomainBase, cls).install(mh)
if not cls.ad_treedomain:
raise nose.SkipTest('AD tree root domain is not available.')
class TestBasicADTrust(ADTrustBase):
"""Basic Integration test for Active Directory"""
def test_range_properties_in_nonposix_trust(self):
"""Check the properties of the created range"""
range_name = self.ad_domain.upper() + '_id_range'
result = self.master.run_command(['ipa', 'idrange-show', range_name,
'--all', '--raw'])
iparangetype_regex = r'ipaRangeType: ipa-ad-trust'
iparangesize_regex = r'ipaIDRangeSize: 200000'
assert re.search(iparangetype_regex, result.stdout_text, re.IGNORECASE)
assert re.search(iparangesize_regex, result.stdout_text, re.IGNORECASE)
def test_user_gid_uid_resolution_in_nonposix_trust(self):
"""Check that user has SID-generated UID"""
# Using domain name since it is lowercased realm name for AD domains
testuser = 'testuser@%s' % self.ad_domain
result = self.master.run_command(['getent', 'passwd', testuser])
# This regex checks that Test User does not have UID 10042 nor belongs
# to the group with GID 10047
testuser_regex = "^testuser@%s:\*:(?!10042)(\d+):(?!10047)(\d+):"\
"Test User:/home/%s/testuser:/bin/sh$"\
% (re.escape(self.ad_domain),
re.escape(self.ad_domain))
assert re.search(testuser_regex, result.stdout_text)
def test_ipauser_authentication(self):
ipauser = u'tuser'
original_passwd = 'Secret123'
new_passwd = 'userPasswd123'
# create an ipauser for this test
self.master.run_command(['ipa', 'user-add', ipauser, '--first=Test',
'--last=User', '--password'],
stdin_text=original_passwd)
# change password for the user to be able to kinit
tasks.ldappasswd_user_change(ipauser, original_passwd, new_passwd,
self.master)
# try to kinit as ipauser
self.master.run_command(
['kinit', '-E', '{0}@{1}'.format(ipauser,
self.master.domain.name)],
stdin_text=new_passwd)
def test_remove_nonposix_trust(self):
tasks.remove_trust_with_ad(self.master, self.ad_domain)
tasks.clear_sssd_cache(self.master)
class TestPosixADTrust(ADTrustBase):
"""Integration test for Active Directory with POSIX support"""
def test_establish_trust(self):
# Not specifying the --range-type directly, it should be detected
tasks.establish_trust_with_ad(self.master, self.ad_domain)
def test_range_properties_in_posix_trust(self):
# Check the properties of the created range
range_name = self.ad_domain.upper() + '_id_range'
result = self.master.run_command(['ipa', 'idrange-show', range_name,
'--all', '--raw'])
# Check the range type and size
iparangetype_regex = r'ipaRangeType: ipa-ad-trust-posix'
iparangesize_regex = r'ipaIDRangeSize: 200000'
assert re.search(iparangetype_regex, result.stdout_text, re.IGNORECASE)
assert re.search(iparangesize_regex, result.stdout_text, re.IGNORECASE)
def test_user_uid_gid_resolution_in_posix_trust(self):
# Check that user has AD-defined UID
# Using domain name since it is lowercased realm name for AD domains
testuser = 'testuser@%s' % self.ad_domain
result = self.master.run_command(['getent', 'passwd', testuser])
testuser_stdout = "testuser@%s:*:10042:10047:"\
"Test User:/home/%s/testuser:/bin/sh"\
% (self.ad_domain, self.ad_domain)
assert testuser_stdout in result.stdout_text
def test_user_without_posix_attributes_not_visible(self):
# Check that user has AD-defined UID
# Using domain name since it is lowercased realm name for AD domains
nonposixuser = 'nonposixuser@%s' % self.ad_domain
result = self.master.run_command(['getent', 'passwd', nonposixuser],
raiseonerr=False)
# Getent exits with 2 for non-existent user
assert result.returncode == 2
def test_remove_trust_with_posix_attributes(self):
tasks.remove_trust_with_ad(self.master, self.ad_domain)
tasks.clear_sssd_cache(self.master)
class TestEnforcedPosixADTrust(TestPosixADTrust):
"""
This test is intented to copycat PosixADTrust, since enforcing the POSIX
trust type should not make a difference.
"""
def test_establish_trust_with_posix_attributes(self):
tasks.establish_trust_with_ad(self.master, self.ad_domain,
extra_args=['--range-type', 'ipa-ad-trust-posix'])
class TestInvalidRangeTypes(ADTrustBase):
"""
Tests invalid values being put into trust-add command.
"""
def test_invalid_range_types(self):
invalid_range_types = ['ipa-local',
'ipa-ad-winsync',
'ipa-ipa-trust',
'random-invalid',
're@ll%ybad12!']
for range_type in invalid_range_types:
tasks.kinit_admin(self.master)
result = self.master.run_command(
['ipa', 'trust-add', '--type', 'ad', self.ad_domain, '--admin',
'Administrator', '--range-type', range_type, '--password'],
raiseonerr=False,
stdin_text=self.master.config.ad_admin_password)
# The trust-add command is supposed to fail
assert result.returncode == 1
class TestExternalTrustWithSubdomain(ADTrustSubdomainBase):
"""
Test establishing external trust with subdomain
"""
def test_establish_trust(self):
""" Tests establishing external trust with Active Directory """
tasks.establish_trust_with_ad(
self.master, self.ad_subdomain,
extra_args=['--range-type', 'ipa-ad-trust', '--external=True'])
def test_all_trustdomains_found(self):
""" Test that only one trustdomain is found """
result = self.master.run_command(['ipa', 'trustdomain-find',
self.ad_subdomain])
assert self.ad_subdomain in result.stdout_text
assert "Number of entries returned 1" in result.stdout_text
def test_user_gid_uid_resolution_in_nonposix_trust(self):
""" Check that user has SID-generated UID """
testuser = 'subdomaintestuser@{0}'.format(self.ad_subdomain)
result = self.master.run_command(['getent', 'passwd', testuser])
testuser_regex = ("^subdomaintestuser@{0}:\*:(?!10142)(\d+):"
"(?!10147)(\d+):Subdomaintest User:"
"/home/{1}/subdomaintestuser:/bin/sh$".format(
re.escape(self.ad_subdomain),
re.escape(self.ad_subdomain)))
assert re.search(testuser_regex, result.stdout_text)
def test_remove_nonposix_trust(self):
tasks.remove_trust_with_ad(self.master, self.ad_subdomain)
tasks.clear_sssd_cache(self.master)
class TestNonexternalTrustWithSubdomain(ADTrustSubdomainBase):
"""
Tests that a non-external trust to a subdomain cannot be established
"""
def test_establish_trust(self):
""" Tests establishing non-external trust with Active Directory """
self.master.run_command(['kinit', '-kt', paths.HTTP_KEYTAB,
'HTTP/%s' % self.master.hostname])
self.master.run_command(['systemctl', 'restart', 'krb5kdc.service'])
self.master.run_command(['kdestroy', '-A'])
tasks.kinit_admin(self.master)
self.master.run_command(['klist'])
self.master.run_command(['smbcontrol', 'all', 'debug', '100'])
result = self.master.run_command([
'ipa', 'trust-add', '--type', 'ad', self.ad_subdomain, '--admin',
'Administrator', '--password', '--range-type', 'ipa-ad-trust'
], stdin_text=self.master.config.ad_admin_password,
raiseonerr=False)
assert result != 0
assert ("Domain '{0}' is not a root domain".format(
self.ad_subdomain) in result.stderr_text)
def test_all_trustdomains_found(self):
raise nose.SkipTest(
'Test case unapplicable, present for inheritance reason only')
class TestExternalTrustWithTreedomain(ADTrustTreedomainBase):
"""
Test establishing external trust with tree root domain
"""
def test_establish_trust(self):
""" Tests establishing external trust with Active Directory """
tasks.establish_trust_with_ad(
self.master, self.ad_treedomain,
extra_args=['--range-type', 'ipa-ad-trust', '--external=True'])
def test_all_trustdomains_found(self):
""" Test that only one trustdomain is found """
result = self.master.run_command(['ipa', 'trustdomain-find',
self.ad_treedomain])
assert self.ad_treedomain in result.stdout_text
assert "Number of entries returned 1" in result.stdout_text
def test_user_gid_uid_resolution_in_nonposix_trust(self):
""" Check that user has SID-generated UID """
testuser = 'treetestuser@{0}'.format(self.ad_treedomain)
result = self.master.run_command(['getent', 'passwd', testuser])
testuser_regex = ("^treetestuser@{0}:\*:(?!10242)(\d+):"
"(?!10247)(\d+):TreeTest User:"
"/home/{1}/treetestuser:/bin/sh$".format(
re.escape(self.ad_treedomain),
re.escape(self.ad_treedomain)))
assert re.search(testuser_regex, result.stdout_text)
def test_remove_nonposix_trust(self):
tasks.remove_trust_with_ad(self.master, self.ad_treedomain)
tasks.clear_sssd_cache(self.master)
class TestNonexternalTrustWithTreedomain(ADTrustTreedomainBase):
"""
Tests that a non-external trust to a tree root domain cannot be established
"""
def test_establish_trust(self):
""" Tests establishing non-external trust with Active Directory """
self.master.run_command(['kinit', '-kt', paths.HTTP_KEYTAB,
'HTTP/%s' % self.master.hostname])
self.master.run_command(['systemctl', 'restart', 'krb5kdc.service'])
self.master.run_command(['kdestroy', '-A'])
tasks.kinit_admin(self.master)
self.master.run_command(['klist'])
self.master.run_command(['smbcontrol', 'all', 'debug', '100'])
result = self.master.run_command([
'ipa', 'trust-add', '--type', 'ad', self.ad_treedomain, '--admin',
'Administrator', '--password', '--range-type', 'ipa-ad-trust'
], stdin_text=self.master.config.ad_admin_password,
raiseonerr=False)
assert result != 0
assert ("Domain '{0}' is not a root domain".format(
self.ad_treedomain) in result.stderr_text)
def test_all_trustdomains_found(self):
raise nose.SkipTest(
'Test case unapplicable, present for inheritance reason only')
class TestExternalTrustWithRootDomain(ADTrustSubdomainBase):
"""
Test establishing external trust with root domain
Main purpose of this test is to verify that subdomains are not
associated with the external trust, hence all tests are skipped
if no subdomain is specified.
"""
def test_establish_trust(self):
""" Tests establishing external trust with Active Directory """
tasks.establish_trust_with_ad(
self.master, self.ad_domain,
extra_args=['--range-type', 'ipa-ad-trust', '--external=True'])
def test_all_trustdomains_found(self):
""" Test that only one trustdomain is found """
result = self.master.run_command(['ipa', 'trustdomain-find',
self.ad_domain])
assert self.ad_domain in result.stdout_text
assert "Number of entries returned 1" in result.stdout_text
def test_remove_nonposix_trust(self):
tasks.remove_trust_with_ad(self.master, self.ad_domain)
tasks.clear_sssd_cache(self.master)
class TestTrustWithUPN(ADTrustBase):
"""
Test support of UPN for trusted domains
"""
upn_suffix = 'UPNsuffix.com'
upn_username = 'upnuser'
upn_name = 'UPN User'
upn_principal = '{}@{}'.format(upn_username, upn_suffix)
upn_password = 'Secret123456'
def test_upn_in_nonposix_trust(self):
""" Check that UPN is listed as trust attribute """
result = self.master.run_command(['ipa', 'trust-show', self.ad_domain,
'--all', '--raw'])
assert ("ipantadditionalsuffixes: {}".format(self.upn_suffix) in
result.stdout_text)
def test_upn_user_resolution_in_nonposix_trust(self):
""" Check that user with UPN can be resolved """
result = self.master.run_command(['getent', 'passwd',
self.upn_principal])
# result will contain AD domain, not UPN
upnuser_regex = "^{}@{}:\*:(\d+):(\d+):{}:/home/{}/{}:/bin/sh$".format(
self.upn_username, self.ad_domain, self.upn_name,
self.ad_domain, self.upn_username)
assert re.search(upnuser_regex, result.stdout_text)
def test_upn_user_authentication(self):
""" Check that AD user with UPN can authenticate in IPA """
self.master.run_command(['kinit', '-C', '-E', self.upn_principal],
stdin_text=self.upn_password)
def test_remove_nonposix_trust(self):
tasks.remove_trust_with_ad(self.master, self.ad_domain)
tasks.clear_sssd_cache(self.master)

View File

@@ -0,0 +1,142 @@
#
# Copyright (C) 2015 FreeIPA Contributors see COPYING for license
#
import time
from ipatests.test_integration.base import IntegrationTest
from ipatests.pytest_plugins.integration import tasks
WAIT_AFTER_ARCHIVE = 45 # give some time to replication
class TestInstallKRA(IntegrationTest):
"""
Test if vault feature behaves as expected, when KRA is installed or not
installed on replica
"""
num_replicas = 1
topology = 'star'
vault_password = "password"
vault_data = "SSBsb3ZlIENJIHRlc3RzCg=="
vault_name_master = "ci_test_vault_master"
vault_name_master2 = "ci_test_vault_master2"
vault_name_master3 = "ci_test_vault_master3"
vault_name_replica_without_KRA = "ci_test_vault_replica_without_kra"
vault_name_replica_with_KRA = "ci_test_vault_replica_with_kra"
vault_name_replica_KRA_uninstalled = "ci_test_vault_replica_KRA_uninstalled"
@classmethod
def install(cls, mh):
tasks.install_master(cls.master, setup_kra=True)
# do not install KRA on replica, it is part of test
tasks.install_replica(cls.master, cls.replicas[0], setup_kra=False)
def _retrieve_secret(self, vault_names=[]):
# try to retrieve secret from vault on both master and replica
for vault_name in vault_names:
self.master.run_command([
"ipa", "vault-retrieve",
vault_name,
"--password", self.vault_password,
])
self.replicas[0].run_command([
"ipa", "vault-retrieve",
vault_name,
"--password", self.vault_password,
])
def test_create_and_retrieve_vault_master(self):
# create vault
self.master.run_command([
"ipa", "vault-add",
self.vault_name_master,
"--password", self.vault_password,
"--type", "symmetric",
])
# archive secret
self.master.run_command([
"ipa", "vault-archive",
self.vault_name_master,
"--password", self.vault_password,
"--data", self.vault_data,
])
time.sleep(WAIT_AFTER_ARCHIVE)
self._retrieve_secret([self.vault_name_master])
def test_create_and_retrieve_vault_replica_without_kra(self):
# create vault
self.replicas[0].run_command([
"ipa", "vault-add",
self.vault_name_replica_without_KRA,
"--password", self.vault_password,
"--type", "symmetric",
])
# archive secret
self.replicas[0].run_command([
"ipa", "vault-archive",
self.vault_name_replica_without_KRA,
"--password", self.vault_password,
"--data", self.vault_data,
])
time.sleep(WAIT_AFTER_ARCHIVE)
self._retrieve_secret([self.vault_name_replica_without_KRA])
def test_create_and_retrieve_vault_replica_with_kra(self):
# install KRA on replica
tasks.install_kra(self.replicas[0], first_instance=False)
# create vault
self.replicas[0].run_command([
"ipa", "vault-add",
self.vault_name_replica_with_KRA,
"--password", self.vault_password,
"--type", "symmetric",
])
# archive secret
self.replicas[0].run_command([
"ipa", "vault-archive",
self.vault_name_replica_with_KRA,
"--password", self.vault_password,
"--data", self.vault_data,
])
time.sleep(WAIT_AFTER_ARCHIVE)
self._retrieve_secret([self.vault_name_replica_with_KRA])
################# master #################
# test master again after KRA was installed on replica
# create vault
self.master.run_command([
"ipa", "vault-add",
self.vault_name_master2,
"--password", self.vault_password,
"--type", "symmetric",
])
# archive secret
self.master.run_command([
"ipa", "vault-archive",
self.vault_name_master2,
"--password", self.vault_password,
"--data", self.vault_data,
])
time.sleep(WAIT_AFTER_ARCHIVE)
self._retrieve_secret([self.vault_name_master2])
################ old vaults ###############
# test if old vaults are still accessible
self._retrieve_secret([
self.vault_name_master,
self.vault_name_replica_without_KRA,
])