Add knob to limit hostname length

On Linux systems the length limit for hostnames is hardcoded
at 64 in MAXHOSTNAMELEN

Solaris, for example, allows 255 characters, and DNS allows the
total length to be up to 255 (with each label < 64).

Add a knob to allow configuring the maximum hostname length (FQDN)

The same validators are used between hosts and DNS to apply
the knob only when dealing with a FQDN as a hostname.

The maxlen option is included so installers can limit the length
of allowed hostnames when the --hostname option is used.

https://pagure.io/freeipa/issue/2018

Signed-off-by: Rob Crittenden <rcritten@redhat.com>
Reviewed-By: Florence Blanc-Renaud <frenaud@redhat.com>
Reviewed-By: Christian Heimes <cheimes@redhat.com>
This commit is contained in:
Rob Crittenden 2019-05-01 10:15:37 -04:00
parent 7fe10d9903
commit 6662e99e17
12 changed files with 79 additions and 14 deletions

View File

@ -61,7 +61,7 @@ aci: (targetattr = "cn || description || ipacertprofilestoreissued")(targetfilte
dn: cn=certprofiles,cn=ca,dc=ipa,dc=example
aci: (targetattr = "cn || createtimestamp || description || entryusn || ipacertprofilestoreissued || modifytimestamp || objectclass")(targetfilter = "(objectclass=ipacertprofile)")(version 3.0;acl "permission:System: Read Certificate Profiles";allow (compare,read,search) userdn = "ldap:///all";)
dn: cn=ipaconfig,cn=etc,dc=ipa,dc=example
aci: (targetattr = "cn || createtimestamp || entryusn || ipacertificatesubjectbase || ipaconfigstring || ipacustomfields || ipadefaultemaildomain || ipadefaultloginshell || ipadefaultprimarygroup || ipadomainresolutionorder || ipagroupobjectclasses || ipagroupsearchfields || ipahomesrootdir || ipakrbauthzdata || ipamaxusernamelength || ipamigrationenabled || ipapwdexpadvnotify || ipasearchrecordslimit || ipasearchtimelimit || ipaselinuxusermapdefault || ipaselinuxusermaporder || ipauserauthtype || ipauserobjectclasses || ipausersearchfields || modifytimestamp || objectclass")(targetfilter = "(objectclass=ipaguiconfig)")(version 3.0;acl "permission:System: Read Global Configuration";allow (compare,read,search) userdn = "ldap:///all";)
aci: (targetattr = "cn || createtimestamp || entryusn || ipacertificatesubjectbase || ipaconfigstring || ipacustomfields || ipadefaultemaildomain || ipadefaultloginshell || ipadefaultprimarygroup || ipadomainresolutionorder || ipagroupobjectclasses || ipagroupsearchfields || ipahomesrootdir || ipakrbauthzdata || ipamaxhostnamelength || ipamaxusernamelength || ipamigrationenabled || ipapwdexpadvnotify || ipasearchrecordslimit || ipasearchtimelimit || ipaselinuxusermapdefault || ipaselinuxusermaporder || ipauserauthtype || ipauserobjectclasses || ipausersearchfields || modifytimestamp || objectclass")(targetfilter = "(objectclass=ipaguiconfig)")(version 3.0;acl "permission:System: Read Global Configuration";allow (compare,read,search) userdn = "ldap:///all";)
dn: cn=costemplates,cn=accounts,dc=ipa,dc=example
aci: (targetfilter = "(objectclass=costemplate)")(version 3.0;acl "permission:System: Add Group Password Policy costemplate";allow (add) groupdn = "ldap:///cn=System: Add Group Password Policy costemplate,cn=permissions,cn=pbac,dc=ipa,dc=example";)
dn: cn=costemplates,cn=accounts,dc=ipa,dc=example

View File

@ -1075,7 +1075,7 @@ args: 0,1,1
option: Str('version?')
output: Output('result')
command: config_mod/1
args: 0,27,3
args: 0,28,3
option: Str('addattr*', cli_name='addattr')
option: Flag('all', autofill=True, cli_name='all', default=False)
option: Str('ca_renewal_master_server?', autofill=False)
@ -1089,6 +1089,7 @@ option: Str('ipagroupobjectclasses*', autofill=False, cli_name='groupobjectclass
option: IA5Str('ipagroupsearchfields?', autofill=False, cli_name='groupsearch')
option: IA5Str('ipahomesrootdir?', autofill=False, cli_name='homedirectory')
option: StrEnum('ipakrbauthzdata*', autofill=False, cli_name='pac_type', values=[u'MS-PAC', u'PAD', u'nfs:NONE'])
option: Int('ipamaxhostnamelength?', autofill=False, cli_name='maxhostname')
option: Int('ipamaxusernamelength?', autofill=False, cli_name='maxusername')
option: Bool('ipamigrationenabled?', autofill=False, cli_name='enable_migration')
option: Int('ipapwdexpadvnotify?', autofill=False, cli_name='pwdexpnotify')

View File

@ -83,8 +83,8 @@ define(IPA_DATA_VERSION, 20100614120000)
# #
########################################################
define(IPA_API_VERSION_MAJOR, 2)
define(IPA_API_VERSION_MINOR, 231)
# Last change: Added admin creds to trust-fetch-domains
define(IPA_API_VERSION_MINOR, 232)
# Last change: Add ipamaxhostnamelength to config
########################################################

View File

@ -43,11 +43,13 @@ attributeTypes: ( 2.16.840.1.113730.3.8.3.23 NAME 'ipaCertificateSubjectBase' SY
attributeTypes: (2.16.840.1.113730.3.8.3.16 NAME 'ipaConfigString' DESC 'Generic configuration stirng' EQUALITY caseIgnoreMatch ORDERING caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-ORIGIN 'IPA v2' )
attributeTypes: ( 2.16.840.1.113730.3.8.3.26 NAME 'ipaSELinuxUserMapDefault' DESC 'Default SELinux user' EQUALITY caseIgnoreMatch ORDERING caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE X-ORIGIN 'IPA v3')
attributeTypes: ( 2.16.840.1.113730.3.8.3.27 NAME 'ipaSELinuxUserMapOrder' DESC 'Available SELinux user context ordering' EQUALITY caseIgnoreMatch ORDERING caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE X-ORIGIN 'IPA v3')
## ipaMaxHostnameLength - maximum hostname length to allow
attributeTypes: ( 2.16.840.1.113730.3.8.1.28 NAME 'ipaMaxHostnameLength' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE)
###############################################
##
## ObjectClasses
##
## ipaGuiConfig - GUI config parameters objectclass
objectClasses: ( 2.16.840.1.113730.3.8.2.1 NAME 'ipaGuiConfig' AUXILIARY MAY ( ipaUserSearchFields $ ipaGroupSearchFields $ ipaSearchTimeLimit $ ipaSearchRecordsLimit $ ipaCustomFields $ ipaHomesRootDir $ ipaDefaultLoginShell $ ipaDefaultPrimaryGroup $ ipaMaxUsernameLength $ ipaPwdExpAdvNotify $ ipaUserObjectClasses $ ipaGroupObjectClasses $ ipaDefaultEmailDomain $ ipaMigrationEnabled $ ipaCertificateSubjectBase $ ipaSELinuxUserMapDefault $ ipaSELinuxUserMapOrder $ ipaKrbAuthzData ) )
objectClasses: ( 2.16.840.1.113730.3.8.2.1 NAME 'ipaGuiConfig' AUXILIARY MAY ( ipaUserSearchFields $ ipaGroupSearchFields $ ipaSearchTimeLimit $ ipaSearchRecordsLimit $ ipaCustomFields $ ipaHomesRootDir $ ipaDefaultLoginShell $ ipaDefaultPrimaryGroup $ ipaMaxUsernameLength $ ipaPwdExpAdvNotify $ ipaUserObjectClasses $ ipaGroupObjectClasses $ ipaDefaultEmailDomain $ ipaMigrationEnabled $ ipaCertificateSubjectBase $ ipaSELinuxUserMapDefault $ ipaSELinuxUserMapOrder $ ipaKrbAuthzData $ ipaMaxHostnameLength) )
## ipaConfigObject - Generic config strings object holder
objectClasses: (2.16.840.1.113730.3.8.4.13 NAME 'ipaConfigObject' DESC 'generic config object for IPA' AUXILIARY MAY ( ipaConfigString ) X-ORIGIN 'IPA v2' )

View File

@ -405,6 +405,7 @@ ipaHomesRootDir: /home
ipaDefaultLoginShell: /bin/sh
ipaDefaultPrimaryGroup: ipausers
ipaMaxUsernameLength: 32
ipaMaxHostnameLength: 64
ipaPwdExpAdvNotify: 4
ipaGroupObjectClasses: top
ipaGroupObjectClasses: groupofnames

View File

@ -5,3 +5,4 @@ add:ipaUserObjectClasses: ipasshuser
remove:ipaConfigString:AllowLMhash
add:objectClass: ipaUserAuthTypeClass
add:objectClass: ipaNameResolutionData
addifnew:ipamaxhostnamelength: 64

View File

@ -33,7 +33,7 @@ from configparser import RawConfigParser
from urllib.parse import urlparse, urlunparse
from ipalib import api, errors, x509
from ipalib.constants import IPAAPI_USER
from ipalib.constants import IPAAPI_USER, MAXHOSTNAMELEN
from ipalib.install import certmonger, certstore, service, sysrestore
from ipalib.install import hostname as hostname_
from ipalib.install.kinit import kinit_keytab, kinit_password
@ -42,6 +42,7 @@ from ipalib.rpc import delete_persistent_client_session_data
from ipalib.util import (
normalize_hostname,
no_matching_interface_for_ip_address_warning,
validate_hostname,
verify_host_resolvable,
)
from ipaplatform import services
@ -2119,6 +2120,13 @@ def install_check(options):
"Invalid hostname, '{}' must not be used.".format(hostname),
rval=CLIENT_INSTALL_ERROR)
try:
validate_hostname(hostname, maxlen=MAXHOSTNAMELEN)
except ValueError as e:
raise ScriptError(
'invalid hostname: {}'.format(e),
rval=CLIENT_INSTALL_ERROR)
# --no-sssd is not supported any more for rhel-based distros
if not tasks.is_nosssd_supported() and not options.sssd:
raise ScriptError(

View File

@ -289,6 +289,9 @@ RENEWAL_REUSE_CA_NAME = 'dogtag-ipa-ca-renew-agent-reuse'
# How long dbus clients should wait for CA certificate RPCs [seconds]
CA_DBUS_TIMEOUT = 120
# Maximum hostname length in Linux
MAXHOSTNAMELEN = 64
# regexp definitions
PATTERN_GROUPUSER_NAME = (
'(?!^[0-9]+$)^[a-zA-Z0-9_.][a-zA-Z0-9_.-]*[a-zA-Z0-9_.$-]?$'

View File

@ -434,14 +434,28 @@ def validate_zonemgr_str(zonemgr):
zonemgr = DNSName(zonemgr)
return validate_zonemgr(zonemgr)
def validate_hostname(hostname, check_fqdn=True, allow_underscore=False, allow_slash=False):
def validate_hostname(hostname, check_fqdn=True, allow_underscore=False,
allow_slash=False, maxlen=255):
""" See RFC 952, 1123
Length limit of 64 imposed by MAXHOSTNAMELEN on Linux.
DNS and other operating systems has a max length of 255. Default to
the theoretical max unless explicitly told to limit. The cases
where a limit would be set might include:
* *-install --hostname
* ipa host-add
The *-install commands by definition are executed on Linux hosts so
the maximum length needs to be limited.
:param hostname Checked value
:param check_fqdn Check if hostname is fully qualified
"""
if len(hostname) > 255:
raise ValueError(_('cannot be longer that 255 characters'))
if len(hostname) > maxlen:
raise ValueError(_('cannot be longer that {} characters'.format(
maxlen)))
if hostname.endswith('.'):
hostname = hostname[:-1]
@ -1029,9 +1043,16 @@ def normalize_hostname(hostname):
return hostname
def hostname_validator(ugettext, value):
def hostname_validator(ugettext, value, maxlen=255):
"""Validator used by plugins to ensure hostname compliance.
In Linux the maximum hostname length is 64. In DNS and
other operaring systems (Solaris) it is 255. If not explicitly
checking a Linux hostname (e.g. the server) use the DNS
default.
"""
try:
validate_hostname(value)
validate_hostname(value, maxlen=maxlen)
except ValueError as e:
return _('invalid domain-name: %s') % unicode(e)

View File

@ -50,6 +50,7 @@ import ipaplatform
from ipapython import ipautil, admintool, version, ipaldap
from ipapython.admintool import ScriptError, SERVER_NOT_CONFIGURED # noqa: E402
from ipapython.certdb import EXTERNAL_CA_TRUST_FLAGS
from ipalib.constants import MAXHOSTNAMELEN
from ipalib.util import validate_hostname
from ipalib import api, errors, x509
from ipapython.dn import DN
@ -154,7 +155,7 @@ def verify_fqdn(host_name, no_host_dns=False, local_hostname=True):
try:
# make sure that the host name meets the requirements in ipalib
validate_hostname(host_name)
validate_hostname(host_name, maxlen=MAXHOSTNAMELEN)
except ValueError as e:
raise BadHostError("Invalid hostname '%s', %s" % (host_name, unicode(e)))

View File

@ -21,6 +21,7 @@
from ipalib import api
from ipalib import Bool, Int, Str, IA5Str, StrEnum, DNParam
from ipalib import errors
from ipalib.constants import MAXHOSTNAMELEN
from ipalib.plugable import Registry
from ipalib.util import validate_domain_name
from .baseldap import (
@ -59,6 +60,12 @@ Password plug-in features: currently defines additional hashes that the
When setting the order list for mapping SELinux users you may need to
quote the value so it isn't interpreted by the shell.
The maximum length of a hostname in Linux is controlled by
MAXHOSTNAMELEN in the kernel and defaults to 64. Some other operating
systems, Solaris for example, allows hostnames up to 255 characters.
This option will allow flexibility in length but by default limiting
to the Linux maximum length.
EXAMPLES:
Show basic server configuration:
@ -70,6 +77,9 @@ EXAMPLES:
Change maximum username length to 99 characters:
ipa config-mod --maxusername=99
Change maximum host name length to 255 characters:
ipa config-mod --maxhostname=255
Increase default time and size limits for maximum IPA server search:
ipa config-mod --searchtimelimit=10 --searchrecordslimit=2000
@ -110,7 +120,7 @@ class config(LDAPObject):
'ipamigrationenabled', 'ipacertificatesubjectbase',
'ipapwdexpadvnotify', 'ipaselinuxusermaporder',
'ipaselinuxusermapdefault', 'ipaconfigstring', 'ipakrbauthzdata',
'ipauserauthtype', 'ipadomainresolutionorder'
'ipauserauthtype', 'ipadomainresolutionorder', 'ipamaxhostnamelength',
]
container_dn = DN(('cn', 'ipaconfig'), ('cn', 'etc'))
permission_filter_objectclasses = ['ipaguiconfig']
@ -132,6 +142,7 @@ class config(LDAPObject):
'ipasearchrecordslimit', 'ipasearchtimelimit',
'ipauserauthtype', 'ipauserobjectclasses',
'ipausersearchfields', 'ipacustomfields',
'ipamaxhostnamelength',
},
},
}
@ -146,6 +157,11 @@ class config(LDAPObject):
minvalue=1,
maxvalue=255,
),
Int('ipamaxhostnamelength',
cli_name='maxhostname',
label=_('Maximum hostname length'),
minvalue=MAXHOSTNAMELEN,
maxvalue=255,),
IA5Str('ipahomesrootdir',
cli_name='homedirectory',
label=_('Home directory base'),

View File

@ -653,6 +653,15 @@ class host_add(LDAPCreate):
def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options):
assert isinstance(dn, DN)
config = ldap.get_ipa_config()
if 'ipamaxhostnamelength' in config:
maxlen = int(config.get('ipamaxhostnamelength')[0])
if len(keys[-1]) > maxlen:
raise errors.ValidationError(
name=self.obj.primary_key.cli_name,
error=_('can be at most %(len)d characters' %
dict(len=maxlen))
)
if options.get('ip_address') and dns_container_exists(ldap):
parts = keys[-1].split('.')
host = parts[0]
@ -762,7 +771,9 @@ class host_del(LDAPDelete):
def pre_callback(self, ldap, dn, *keys, **options):
assert isinstance(dn, DN)
# If we aren't given a fqdn, find it
if hostname_validator(None, keys[-1]) is not None:
config = ldap.get_ipa_config()
maxlen = int(config.get('ipamaxhostnamelength')[0])
if hostname_validator(None, keys[-1], maxlen=maxlen) is not None:
hostentry = api.Command['host_show'](keys[-1])['result']
fqdn = hostentry['fqdn'][0]
else: