Skip to content

Commit

Permalink
WIP: support multiple host aliases in HTTP_REFERER
Browse files Browse the repository at this point in the history
1 .Introduces --host-aliases option to the server installers

2. Host aliases stored in /etc/ipa/server.conf

3. Host aliases added to automatically generated certificates in
   internal IPA certificates for LDAP and HTTP services

4. Host aliases from /etc/ipa/server.conf checked by the RPC server to
   verify that a connection uses a valid referrer

TODO:
 - add syntax to support specifying a host address in --host-aliases to
   bootstrap installation with multiple network interfaces or derive
   them from the host's /etc/hosts

 - for host aliases, when integrated DNS is in use, add them as host
   entries with own addresses

 - add host aliased entries as managed by the primary host object to
   allow issuance of the certificates with dNS SAN records for aliases

The code is not tested at all.
  • Loading branch information
abbra committed Nov 6, 2022
1 parent 38728dd commit d471294
Show file tree
Hide file tree
Showing 9 changed files with 66 additions and 4 deletions.
3 changes: 3 additions & 0 deletions client/man/default.conf.5
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,9 @@ Specifies whether an IPA client should attempt to fall back and try other servic
.B host <hostname>
Specifies the local system hostname.
.TP
.B host_aliases <comma-separated list of hostnames>
Specifies the local system hostname aliases. Can be used for multi-homed deployment. Please note that certificates issued to LDAP and HTTP services must also contain these hostname aliases in the dNS SAN records.
.TP
.B http_timeout <seconds>
Timeout for HTTP blocking requests (e.g. connection). The default value is 30 seconds.
.TP
Expand Down
3 changes: 3 additions & 0 deletions install/tools/man/ipa-server-install.1
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ Create home directories for users on their first login.
\fB\-\-hostname\fR=\fIHOST_NAME\fR
The fully\-qualified DNS name of this server.
.TP
\fB\-\-host\-aliases\fR=\fI"HOST_NAME1, HOST_NAME2, .."\fR
The fully\-qualified DNS name aliases of this server. Can only be specified if the system uses multi-homed environment.
.TP
\fB\-\-ip\-address\fR=\fIIP_ADDRESS\fR
The IP address of this server. If this address does not match the address the host resolves to and \-\-setup\-dns is not selected, the installation will fail. If the server hostname is not resolvable, a record for the hostname and IP_ADDRESS is added to /etc/hosts.
This option can be used multiple times to specify more IP addresses of the server (e.g. multihomed and/or dualstacked server).
Expand Down
3 changes: 3 additions & 0 deletions ipalib/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,9 @@
# WebUI stuff:
('webui_prod', True),

# HTTP interface hostname aliases, comma- or space-separated
('host_aliases', None),

# Session stuff:
('kinit_lifetime', None),

Expand Down
10 changes: 10 additions & 0 deletions ipapython/ipautil.py
Original file line number Diff line number Diff line change
Expand Up @@ -1796,3 +1796,13 @@ def get_config_debug(context):
return False

return parser.get(CONFIG_SECTION, 'debug').lower() == 'true'


def split_string(value):
if value is None:
return list()
if ',' in value:
values = value.split(',')
else:
values = value.split(' ')
return list(v.strip() for v in values if v.strip())
7 changes: 6 additions & 1 deletion ipaserver/install/dsinstance.py
Original file line number Diff line number Diff line change
Expand Up @@ -864,6 +864,11 @@ def __enable_ssl(self):
prev_helper = None
try:
cmd = 'restart_dirsrv %s' % self.serverid

dns_san_record = [self.fqdn]
if api.env.host_aliases is not None:
dns_san_record += ipautil.split_string(api.env.host_aliases)

certmonger.request_and_wait_for_cert(
certpath=dirname,
storage='NSSDB',
Expand All @@ -873,7 +878,7 @@ def __enable_ssl(self):
subject=str(DN(('CN', self.fqdn), self.subject_base)),
ca='IPA',
profile=dogtag.DEFAULT_PROFILE,
dns=[self.fqdn],
dns=dns_san_record,
post_command=cmd,
resubmit_timeout=api.env.certmonger_wait_timeout
)
Expand Down
8 changes: 6 additions & 2 deletions ipaserver/install/httpinstance.py
Original file line number Diff line number Diff line change
Expand Up @@ -388,23 +388,27 @@ def __setup_ssl(self):
# have codepaths to support the ipa-ca.$DOMAIN dnsName in HTTP
# cert. Therefore if request fails, try again without the
# ipa-ca.$DOMAIN dnsName.
ipa_ca_record = f'{IPA_CA_RECORD}.{api.env.domain}'
args = dict(
certpath=(paths.HTTPD_CERT_FILE, paths.HTTPD_KEY_FILE),
principal=self.principal,
subject=str(DN(('CN', self.fqdn), self.subject_base)),
ca='IPA',
profile=dogtag.DEFAULT_PROFILE,
dns=[self.fqdn, f'{IPA_CA_RECORD}.{api.env.domain}'],
dns=[self.fqdn, ipa_ca_record],
post_command='restart_httpd',
storage='FILE',
passwd_fname=key_passwd_file,
resubmit_timeout=api.env.certmonger_wait_timeout,
stop_tracking_on_error=True,
)
if api.env.host_aliases is not None:
args['dns'] += ipautil.split_string(api.env.host_aliases)

try:
certmonger.request_and_wait_for_cert(**args)
except Exception:
args['dns'] = [self.fqdn] # remove ipa-ca.$DOMAIN
args['dns'].remove(ipa_ca_record) # remove ipa-ca.$DOMAIN
args['stop_tracking_on_error'] = False
certmonger.request_and_wait_for_cert(**args)
finally:
Expand Down
7 changes: 7 additions & 0 deletions ipaserver/install/server/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,13 @@ class ServerInstallInterface(ServerCertificateInstallInterface,
description="fully qualified name of this host",
)

host_aliases = knob(
list,
description="Additional fully qualified names of this host "
"(aliases for multi-homed setup)",
)
host_aliases = enroll_only(host_aliases)

ca_cert_files = extend_knob(
client.ClientInstallInterface.ca_cert_files,
description="File containing CA certificates for the service "
Expand Down
21 changes: 21 additions & 0 deletions ipaserver/install/server/install.py
Original file line number Diff line number Diff line change
Expand Up @@ -699,6 +699,24 @@ def install_check(installer):
# check addresses here, dns module is doing own check
no_matching_interface_for_ip_address_warning(ip_addresses)

if api.env.host_aliases is not None:
# create /etc/ipa/server.conf with host_aliases to use after installer
# context is not used
gopts = [
ipaconf.setOption('host_aliases', api.env.host_aliases)
]
opts = [
ipaconf.setSection('global', gopts),
{'name': 'empty', 'type': 'empty'}
]

target_fname = paths.IPA_SERVER_CONF
ipaconf.newConf(target_fname, opts)

# Must be readable for everyone
os.chmod(target_fname, 0o644)


instance_name = "-".join(realm_name.split("."))
dirsrv = services.knownservices.dirsrv
if (options.external_cert_files
Expand All @@ -723,6 +741,8 @@ def install_check(installer):
print()
print("The IPA Master Server will be configured with:")
print("Hostname: %s" % host_name)
if api.env.host_aliases is not None:
print("Host aliases: %s" % api.env.host_aliases)
print("IP address(es): %s" % ", ".join(str(ip) for ip in ip_addresses))
print("Domain name: %s" % domain_name)
print("Realm name: %s" % realm_name)
Expand Down Expand Up @@ -776,6 +796,7 @@ def install_check(installer):
options.admin_password = admin_password
options._host_name_overridden = bool(options.host_name)
options.host_name = host_name
options.host_aliases = ipautil.split_string(api.env.host_aliases)
options.ip_addresses = ip_addresses

installer._fstore = fstore
Expand Down
8 changes: 7 additions & 1 deletion ipaserver/rpcserver.py
Original file line number Diff line number Diff line change
Expand Up @@ -378,7 +378,13 @@ def wsgi_execute(self, environ):
e = None
if 'HTTP_REFERER' not in environ:
return self.marshal(result, RefererError(referer='missing'), _id)
if not environ['HTTP_REFERER'].startswith('https://%s/ipa' % self.api.env.host) and not self.env.in_tree:

host_aliases = [self.api.env.host]
host_aliases += ipautil.split_string(self.api.env.host_aliases)

referers = ['https://%s/ipa' % s for s in host_aliases]
is_any = any(environ['HTTP_REFERER'].startswith(s) for s in referers)
if not is_any and not self.env.in_tree:
return self.marshal(result, RefererError(referer=environ['HTTP_REFERER']), _id)
if self.api.env.debug:
time_start = time.perf_counter_ns()
Expand Down

0 comments on commit d471294

Please sign in to comment.