mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2025-02-25 18:55:28 -06:00
Require that hosts be resolvable in DNS. Use --force to ignore warnings.
This also requires a resolvable hostname on services as well. I want people to think long and hard about adding things that aren't resolvable. The cert plugin can automatically create services on the user's behalf when issuing a cert. It will always set the force flag to True. We use a lot of made-up host names in the test system, all of which require the force flag now. ticket #25
This commit is contained in:
@@ -1054,6 +1054,22 @@ class DefaultGroupError(ExecutionError):
|
|||||||
errno = 4018
|
errno = 4018
|
||||||
format = _('The default users group cannot be removed')
|
format = _('The default users group cannot be removed')
|
||||||
|
|
||||||
|
class DNSNotARecordError(ExecutionError):
|
||||||
|
"""
|
||||||
|
**4019** Raised when a hostname is not a DNS A record
|
||||||
|
|
||||||
|
For example:
|
||||||
|
|
||||||
|
>>> raise DNSNotARecordError()
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
DNSNotARecordError: Host does not have corresponding DNS A record
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
errno = 4019
|
||||||
|
format = _('Host does not have corresponding DNS A record')
|
||||||
|
|
||||||
class BuiltinError(ExecutionError):
|
class BuiltinError(ExecutionError):
|
||||||
"""
|
"""
|
||||||
**4100** Base class for builtin execution errors (*4100 - 4199*).
|
**4100** Base class for builtin execution errors (*4100 - 4199*).
|
||||||
|
|||||||
@@ -269,7 +269,7 @@ class cert_request(VirtualCommand):
|
|||||||
if not add:
|
if not add:
|
||||||
raise errors.NotFound(reason="The service principal for this request doesn't exist.")
|
raise errors.NotFound(reason="The service principal for this request doesn't exist.")
|
||||||
try:
|
try:
|
||||||
service = api.Command['service_add'](principal, **{})['result']
|
service = api.Command['service_add'](principal, **{'force': True})['result']
|
||||||
dn = service['dn']
|
dn = service['dn']
|
||||||
except errors.ACIError:
|
except errors.ACIError:
|
||||||
raise errors.ACIError(info='You need to be a member of the serviceadmin role to add services')
|
raise errors.ACIError(info='You need to be a member of the serviceadmin role to add services')
|
||||||
|
|||||||
@@ -84,7 +84,6 @@ def validate_host(ugettext, fqdn):
|
|||||||
return _('Fully-qualified hostname required')
|
return _('Fully-qualified hostname required')
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
class host(LDAPObject):
|
class host(LDAPObject):
|
||||||
"""
|
"""
|
||||||
Host object.
|
Host object.
|
||||||
@@ -196,8 +195,15 @@ class host_add(LDAPCreate):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
msg_summary = _('Added host "%(value)s"')
|
msg_summary = _('Added host "%(value)s"')
|
||||||
|
takes_options = (
|
||||||
|
Flag('force',
|
||||||
|
doc=_('force host name even if not in DNS'),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options):
|
def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options):
|
||||||
|
if not options.get('force', False):
|
||||||
|
util.validate_host_dns(self.log, keys[-1])
|
||||||
if 'locality' in entry_attrs:
|
if 'locality' in entry_attrs:
|
||||||
entry_attrs['l'] = entry_attrs['locality']
|
entry_attrs['l'] = entry_attrs['locality']
|
||||||
del entry_attrs['locality']
|
del entry_attrs['locality']
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ EXAMPLES:
|
|||||||
"""
|
"""
|
||||||
import base64
|
import base64
|
||||||
|
|
||||||
from ipalib import api, errors
|
from ipalib import api, errors, util
|
||||||
from ipalib import Str, Flag, Bytes
|
from ipalib import Str, Flag, Bytes
|
||||||
from ipalib.plugins.baseldap import *
|
from ipalib.plugins.baseldap import *
|
||||||
from ipalib import x509
|
from ipalib import x509
|
||||||
@@ -183,19 +183,11 @@ class service_add(LDAPCreate):
|
|||||||
entry_attrs['usercertificate'] = base64.b64decode(cert)
|
entry_attrs['usercertificate'] = base64.b64decode(cert)
|
||||||
# FIXME: shouldn't we request signing at this point?
|
# FIXME: shouldn't we request signing at this point?
|
||||||
|
|
||||||
# TODO: once DNS client is done (code below for reference only!)
|
if not options.get('force', False):
|
||||||
# if not kw['force']:
|
# We know the host exists if we've gotten this far but we
|
||||||
# fqdn = hostname + '.'
|
# really want to discourage creating services for hosts that
|
||||||
# rs = dnsclient.query(fqdn, dnsclient.DNS_C_IN, dnsclient.DNS_T_A)
|
# don't exist in DNS.
|
||||||
# if len(rs) == 0:
|
util.validate_host_dns(self.log, hostname)
|
||||||
# self.log.debug(
|
|
||||||
# 'IPA: DNS A record lookup failed for '%s'" % hostname
|
|
||||||
# )
|
|
||||||
# raise ipaerror.gen_exception(ipaerror.INPUT_NOT_DNS_A_RECORD)
|
|
||||||
# else:
|
|
||||||
# self.log.debug(
|
|
||||||
# 'IPA: found %d records for '%s'" % (len(rs), hostname)
|
|
||||||
# )
|
|
||||||
|
|
||||||
return dn
|
return dn
|
||||||
|
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ import time
|
|||||||
import krbV
|
import krbV
|
||||||
import socket
|
import socket
|
||||||
from ipalib import errors
|
from ipalib import errors
|
||||||
|
from ipapython import dnsclient
|
||||||
|
|
||||||
|
|
||||||
def get_current_principal():
|
def get_current_principal():
|
||||||
@@ -113,3 +114,18 @@ def realm_to_suffix(realm_name):
|
|||||||
s = realm_name.split(".")
|
s = realm_name.split(".")
|
||||||
terms = ["dc=" + x.lower() for x in s]
|
terms = ["dc=" + x.lower() for x in s]
|
||||||
return ",".join(terms)
|
return ",".join(terms)
|
||||||
|
|
||||||
|
def validate_host_dns(log, fqdn):
|
||||||
|
"""
|
||||||
|
See if the hostname has a DNS A record.
|
||||||
|
"""
|
||||||
|
rs = dnsclient.query(fqdn + '.', dnsclient.DNS_C_IN, dnsclient.DNS_T_A)
|
||||||
|
if len(rs) == 0:
|
||||||
|
log.debug(
|
||||||
|
'IPA: DNS A record lookup failed for %s' % fqdn
|
||||||
|
)
|
||||||
|
raise errors.DNSNotARecordError()
|
||||||
|
else:
|
||||||
|
log.debug(
|
||||||
|
'IPA: found %d records for %s' % (len(rs), fqdn)
|
||||||
|
)
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ class test_ipagetkeytab(cmdline_test):
|
|||||||
"""
|
"""
|
||||||
# Create the service
|
# Create the service
|
||||||
try:
|
try:
|
||||||
api.Command['host_add'](self.host_fqdn)
|
api.Command['host_add'](self.host_fqdn, force=True)
|
||||||
except errors.DuplicateEntry:
|
except errors.DuplicateEntry:
|
||||||
# it already exists, no problem
|
# it already exists, no problem
|
||||||
pass
|
pass
|
||||||
@@ -93,7 +93,7 @@ class test_ipagetkeytab(cmdline_test):
|
|||||||
"""
|
"""
|
||||||
# Create the service
|
# Create the service
|
||||||
try:
|
try:
|
||||||
api.Command['service_add'](self.service_princ)
|
api.Command['service_add'](self.service_princ, force=True)
|
||||||
except errors.DuplicateEntry:
|
except errors.DuplicateEntry:
|
||||||
# it already exists, no problem
|
# it already exists, no problem
|
||||||
pass
|
pass
|
||||||
|
|||||||
@@ -101,7 +101,7 @@ class test_cert(XMLRPC_test):
|
|||||||
This should fail because the service principal doesn't exist
|
This should fail because the service principal doesn't exist
|
||||||
"""
|
"""
|
||||||
# First create the host that will use this policy
|
# First create the host that will use this policy
|
||||||
res = api.Command['host_add'](self.host_fqdn)['result']
|
res = api.Command['host_add'](self.host_fqdn, force= True)['result']
|
||||||
|
|
||||||
csr = unicode(self.generateCSR(self.subject))
|
csr = unicode(self.generateCSR(self.subject))
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -149,19 +149,19 @@ class test_hbac(XMLRPC_test):
|
|||||||
self.test_group, description=u'description'
|
self.test_group, description=u'description'
|
||||||
)
|
)
|
||||||
self.failsafe_add(api.Object.host,
|
self.failsafe_add(api.Object.host,
|
||||||
self.test_host
|
self.test_host, force=True
|
||||||
)
|
)
|
||||||
self.failsafe_add(api.Object.hostgroup,
|
self.failsafe_add(api.Object.hostgroup,
|
||||||
self.test_hostgroup, description=u'description'
|
self.test_hostgroup, description=u'description'
|
||||||
)
|
)
|
||||||
self.failsafe_add(api.Object.host,
|
self.failsafe_add(api.Object.host,
|
||||||
self.test_sourcehost
|
self.test_sourcehost, force=True
|
||||||
)
|
)
|
||||||
self.failsafe_add(api.Object.hostgroup,
|
self.failsafe_add(api.Object.hostgroup,
|
||||||
self.test_sourcehostgroup, description=u'desc'
|
self.test_sourcehostgroup, description=u'desc'
|
||||||
)
|
)
|
||||||
self.failsafe_add(api.Object.hbacsvc,
|
self.failsafe_add(api.Object.hbacsvc,
|
||||||
self.test_service, description=u'desc'
|
self.test_service, description=u'desc', force=True
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_8_hbac_add_user(self):
|
def test_8_hbac_add_user(self):
|
||||||
|
|||||||
@@ -32,12 +32,15 @@ short1 = u'testhost1'
|
|||||||
dn1 = u'fqdn=%s,cn=computers,cn=accounts,%s' % (fqdn1, api.env.basedn)
|
dn1 = u'fqdn=%s,cn=computers,cn=accounts,%s' % (fqdn1, api.env.basedn)
|
||||||
service1 = u'dns/%s@%s' % (fqdn1, api.env.realm)
|
service1 = u'dns/%s@%s' % (fqdn1, api.env.realm)
|
||||||
service1dn = u'krbprincipalname=%s,cn=services,cn=accounts,%s' % (service1.lower(), api.env.basedn)
|
service1dn = u'krbprincipalname=%s,cn=services,cn=accounts,%s' % (service1.lower(), api.env.basedn)
|
||||||
|
fqdn2 = u'shouldnotexist.%s' % api.env.domain
|
||||||
|
dn2 = u'fqdn=%s,cn=computers,cn=accounts,%s' % (fqdn2, api.env.basedn)
|
||||||
|
|
||||||
|
|
||||||
class test_host(Declarative):
|
class test_host(Declarative):
|
||||||
|
|
||||||
cleanup_commands = [
|
cleanup_commands = [
|
||||||
('host_del', [fqdn1], {}),
|
('host_del', [fqdn1], {}),
|
||||||
|
('host_del', [fqdn2], {}),
|
||||||
('service_del', [service1], {}),
|
('service_del', [service1], {}),
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -70,6 +73,7 @@ class test_host(Declarative):
|
|||||||
dict(
|
dict(
|
||||||
description=u'Test host 1',
|
description=u'Test host 1',
|
||||||
l=u'Undisclosed location 1',
|
l=u'Undisclosed location 1',
|
||||||
|
force=True,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
expected=dict(
|
expected=dict(
|
||||||
@@ -94,6 +98,7 @@ class test_host(Declarative):
|
|||||||
dict(
|
dict(
|
||||||
description=u'Test host 1',
|
description=u'Test host 1',
|
||||||
localityname=u'Undisclosed location 1',
|
localityname=u'Undisclosed location 1',
|
||||||
|
force=True,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
expected=errors.DuplicateEntry(),
|
expected=errors.DuplicateEntry(),
|
||||||
@@ -267,6 +272,7 @@ class test_host(Declarative):
|
|||||||
dict(
|
dict(
|
||||||
description=u'Test host 1',
|
description=u'Test host 1',
|
||||||
l=u'Undisclosed location 1',
|
l=u'Undisclosed location 1',
|
||||||
|
force=True,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
expected=dict(
|
expected=dict(
|
||||||
@@ -286,7 +292,7 @@ class test_host(Declarative):
|
|||||||
|
|
||||||
dict(
|
dict(
|
||||||
desc='Add a service to host %r' % fqdn1,
|
desc='Add a service to host %r' % fqdn1,
|
||||||
command=('service_add', [service1], {}),
|
command=('service_add', [service1], {'force': True}),
|
||||||
expected=dict(
|
expected=dict(
|
||||||
value=service1,
|
value=service1,
|
||||||
summary=u'Added service "%s"' % service1,
|
summary=u'Added service "%s"' % service1,
|
||||||
@@ -321,4 +327,36 @@ class test_host(Declarative):
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
|
|
||||||
|
dict(
|
||||||
|
desc='Try to add host not in DNS %r without force' % fqdn2,
|
||||||
|
command=('host_add', [fqdn2], {}),
|
||||||
|
expected=errors.DNSNotARecordError(reason='Host does not have corresponding DNS A record'),
|
||||||
|
),
|
||||||
|
|
||||||
|
|
||||||
|
dict(
|
||||||
|
desc='Try to add host not in DNS %r with force' % fqdn2,
|
||||||
|
command=('host_add', [fqdn2],
|
||||||
|
dict(
|
||||||
|
description=u'Test host 2',
|
||||||
|
l=u'Undisclosed location 2',
|
||||||
|
force=True,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
expected=dict(
|
||||||
|
value=fqdn2,
|
||||||
|
summary=u'Added host "%s"' % fqdn2,
|
||||||
|
result=dict(
|
||||||
|
dn=dn2,
|
||||||
|
fqdn=[fqdn2],
|
||||||
|
description=[u'Test host 2'],
|
||||||
|
l=[u'Undisclosed location 2'],
|
||||||
|
krbprincipalname=[u'host/%s@%s' % (fqdn2, api.env.realm)],
|
||||||
|
objectclass=objectclasses.host,
|
||||||
|
ipauniqueid=[fuzzy_uuid],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -99,6 +99,7 @@ class test_hostgroup(Declarative):
|
|||||||
dict(
|
dict(
|
||||||
description=u'Test host 1',
|
description=u'Test host 1',
|
||||||
l=u'Undisclosed location 1',
|
l=u'Undisclosed location 1',
|
||||||
|
force=True,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
expected=dict(
|
expected=dict(
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ class test_netgroup(XMLRPC_test):
|
|||||||
host_fqdn = u'ipatesthost.%s' % api.env.domain
|
host_fqdn = u'ipatesthost.%s' % api.env.domain
|
||||||
host_description = u'Test host'
|
host_description = u'Test host'
|
||||||
host_localityname = u'Undisclosed location'
|
host_localityname = u'Undisclosed location'
|
||||||
host_kw = {'fqdn': host_fqdn, 'description': host_description, 'localityname': host_localityname, 'raw': True}
|
host_kw = {'fqdn': host_fqdn, 'description': host_description, 'localityname': host_localityname, 'raw': True, 'force': True}
|
||||||
|
|
||||||
hg_cn = u'hg1'
|
hg_cn = u'hg1'
|
||||||
hg_description = u'Netgroup'
|
hg_description = u'Netgroup'
|
||||||
|
|||||||
@@ -40,8 +40,8 @@ class test_service(XMLRPC_test):
|
|||||||
"""
|
"""
|
||||||
Test adding a HTTP principal using the `xmlrpc.service_add` method.
|
Test adding a HTTP principal using the `xmlrpc.service_add` method.
|
||||||
"""
|
"""
|
||||||
self.failsafe_add(api.Object.host, self.host)
|
self.failsafe_add(api.Object.host, self.host, force=True)
|
||||||
entry = self.failsafe_add(api.Object.service, self.principal)['result']
|
entry = self.failsafe_add(api.Object.service, self.principal, force=True)['result']
|
||||||
assert_attr_equal(entry, 'krbprincipalname', self.principal)
|
assert_attr_equal(entry, 'krbprincipalname', self.principal)
|
||||||
assert_attr_equal(entry, 'objectclass', 'ipaobject')
|
assert_attr_equal(entry, 'objectclass', 'ipaobject')
|
||||||
|
|
||||||
@@ -50,11 +50,6 @@ class test_service(XMLRPC_test):
|
|||||||
Test adding a host principal using `xmlrpc.service_add`. Host
|
Test adding a host principal using `xmlrpc.service_add`. Host
|
||||||
services are not allowed.
|
services are not allowed.
|
||||||
"""
|
"""
|
||||||
# FIXME: Are host principals not allowed still? Running this test gives
|
|
||||||
# this error:
|
|
||||||
#
|
|
||||||
# NotFound: The host 'ipatest.example.com' does not exist to add a service to.
|
|
||||||
|
|
||||||
kw = {'krbprincipalname': self.hostprincipal}
|
kw = {'krbprincipalname': self.hostprincipal}
|
||||||
try:
|
try:
|
||||||
api.Command['service_add'](**kw)
|
api.Command['service_add'](**kw)
|
||||||
@@ -67,7 +62,7 @@ class test_service(XMLRPC_test):
|
|||||||
"""
|
"""
|
||||||
Test adding a malformed principal ('foo').
|
Test adding a malformed principal ('foo').
|
||||||
"""
|
"""
|
||||||
kw = {'krbprincipalname': u'foo'}
|
kw = {'krbprincipalname': u'foo', 'force': True}
|
||||||
try:
|
try:
|
||||||
api.Command['service_add'](**kw)
|
api.Command['service_add'](**kw)
|
||||||
except errors.MalformedServicePrincipal:
|
except errors.MalformedServicePrincipal:
|
||||||
@@ -79,7 +74,7 @@ class test_service(XMLRPC_test):
|
|||||||
"""
|
"""
|
||||||
Test adding a malformed principal ('HTTP/foo@FOO.NET').
|
Test adding a malformed principal ('HTTP/foo@FOO.NET').
|
||||||
"""
|
"""
|
||||||
kw = {'krbprincipalname': u'HTTP/foo@FOO.NET'}
|
kw = {'krbprincipalname': u'HTTP/foo@FOO.NET', 'force': True}
|
||||||
try:
|
try:
|
||||||
api.Command['service_add'](**kw)
|
api.Command['service_add'](**kw)
|
||||||
except errors.RealmMismatch:
|
except errors.RealmMismatch:
|
||||||
@@ -115,3 +110,5 @@ class test_service(XMLRPC_test):
|
|||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
assert False
|
assert False
|
||||||
|
|
||||||
|
api.Command['host_del'](self.host)
|
||||||
|
|||||||
Reference in New Issue
Block a user