Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
127 changes: 109 additions & 18 deletions irods_testing_environment/irods_setup.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
# grown-up modules
import concurrent.futures
import json
import logging
import os
import pathlib

# local modules
from . import context, database_setup, execute, irods_config, odbc_setup
from . import archive, context, database_setup, execute, irods_config, odbc_setup


class zone_info(object):
Expand Down Expand Up @@ -132,9 +131,12 @@ def __init__(self):
self.provides_local_storage = 'y'
self.resource_name = ''
self.vault_directory = ''
self.authentication_scheme = 'native'

self.catalog_service_provider_host = 'localhost'

self.use_tls = False

def setup(self,
irods_version,
service_account_name= None,
Expand Down Expand Up @@ -164,8 +166,10 @@ def setup(self,
provides_local_storage = None,
resource_name = None,
vault_directory = None,
authentication_scheme = None,
**kwargs):
"""Set values for the service account section of the setup script.
"""
Set values for the service account section of the setup script.

Returns this instance of the class.

Expand Down Expand Up @@ -204,6 +208,10 @@ def setup(self,
resource_name -- name used to identify the local storage
vault_directory -- storage location of the default unixfilesystem resource created
during installation
authentication_scheme: Authentication scheme to use for service account.

Returns:
This instance of setup_input_builder.
"""
self.irods_version = irods_version

Expand Down Expand Up @@ -239,9 +247,18 @@ def setup(self,
self.provides_local_storage = provides_local_storage or self.provides_local_storage
self.resource_name = resource_name or self.resource_name
self.vault_directory = vault_directory or self.vault_directory
self.authentication_scheme = authentication_scheme or self.authentication_scheme

self.do_unattended_install = kwargs.get('do_unattended_install', False)

self.use_tls = kwargs.get('use_tls', False)
self.certificate_chain_file = str(pathlib.Path(context.irods_config()) / "chain.pem")
self.certificate_key_file = str(pathlib.Path(context.irods_config()) / "server.key")
self.dh_params_file = str(pathlib.Path(context.irods_config()) / "dhparams.pem")
self.ca_certificate_file = str(pathlib.Path(context.irods_config()) / "server.crt")
self.ca_certificate_path = "" # This is optional but should be defined.
self.verify_server = "cert"

return self


Expand Down Expand Up @@ -290,6 +307,18 @@ def build_input_for_catalog_consumer(self):
input_args.insert(4, str(self.provides_local_storage))
input_args.insert(5, str(self.resource_name))
input_args.insert(6, str(self.vault_directory))

# Insert entries for TLS prompts (added in 5.1.0).
if self.irods_version >= (5, 0, 90) and self.use_tls:
# tls_server
input_args.insert(7, self.certificate_chain_file)
input_args.insert(8, self.certificate_key_file)
input_args.insert(9, self.dh_params_file)
# tls_client
input_args.insert(10, self.ca_certificate_file)
input_args.insert(11, self.ca_certificate_path)
input_args.insert(12, str(2 if self.verify_server == "cert" else 1))
input_args.insert(13, "") # confirmation
# Handle the difference between 4.2 servers and 4.3 servers.
elif self.irods_version >= (4, 3, 0):
input_args.insert(3, str(self.provides_local_storage))
Expand Down Expand Up @@ -364,6 +393,18 @@ def build_input_for_catalog_provider(self):
input_args.insert(12, str(self.provides_local_storage))
input_args.insert(13, str(self.resource_name))
input_args.insert(14, str(self.vault_directory))

# Insert entries for TLS prompts (added in 5.1.0).
if self.irods_version >= (5, 0, 90) and self.use_tls:
# tls_server
input_args.insert(15, self.certificate_chain_file)
input_args.insert(16, self.certificate_key_file)
input_args.insert(17, self.dh_params_file)
# tls_client
input_args.insert(18, self.ca_certificate_file)
input_args.insert(19, self.ca_certificate_path)
input_args.insert(20, str(2 if self.verify_server == "cert" else 1))
input_args.insert(21, "") # confirmation
# Handle the difference between 4.2 servers and 4.3 servers.
elif self.irods_version >= (4, 3, 0):
input_args.insert(11, str(self.provides_local_storage))
Expand Down Expand Up @@ -395,6 +436,7 @@ def build_unattended_install_input_for_catalog_consumer(self):
"service_account_group_name": 'irods'
},
"service_account_environment": {
"irods_authentication_scheme": self.authentication_scheme,
"irods_client_server_policy": "CS_NEG_REFUSE",
"irods_connection_pool_refresh_time_in_seconds": 300,
"irods_cwd": f"/{self.zone_name}/home/{self.admin_username}",
Expand Down Expand Up @@ -531,7 +573,7 @@ def build_unattended_install_input_for_catalog_consumer(self):
"schema_version": "v5",
"server_port_range_end": self.parallel_port_range_end,
"server_port_range_start": self.parallel_port_range_begin,
"zone_auth_scheme": "native",
"zone_auth_scheme": self.authentication_scheme,
"zone_key": self.zone_key,
"zone_name": self.zone_name,
"zone_port": self.zone_port,
Expand Down Expand Up @@ -570,6 +612,23 @@ def build_unattended_install_input_for_catalog_consumer(self):
"server_control_plane_timeout_milliseconds": 10000
})

elif self.use_tls:
json_input["server_config"]["client_server_policy"] = "CS_NEG_REQUIRE"
json_input["server_config"]["tls_server"] = {
"certificate_chain_file": self.certificate_chain_file,
"certificate_key_file": self.certificate_key_file,
"dh_params_file": self.dh_params_file,
}
json_input["server_config"]["tls_client"] = {"verify_server": self.verify_server}
if self.ca_certificate_file:
json_input["server_config"]["tls_client"]["ca_certificate_file"] = self.ca_certificate_file
json_input["service_account_environment"]["irods_ssl_ca_certificate_file"] = self.ca_certificate_file
if self.ca_certificate_path:
json_input["server_config"]["tls_client"]["ca_certificate_path"] = self.ca_certificate_path
json_input["service_account_environment"]["irods_ssl_ca_certificate_path"] = self.ca_certificate_path
json_input["service_account_environment"]["irods_client_server_policy"] = "CS_NEG_REQUIRE"
json_input["service_account_environment"]["irods_ssl_verify_server"] = self.verify_server

return json.dumps(json_input, sort_keys=True, indent=4)


Expand All @@ -593,6 +652,7 @@ def build_unattended_install_input_for_catalog_provider(self):
"service_account_group_name": 'irods'
},
"service_account_environment": {
"irods_authentication_scheme": self.authentication_scheme,
"irods_client_server_policy": "CS_NEG_REFUSE",
"irods_connection_pool_refresh_time_in_seconds": 300,
"irods_cwd": f"/{self.zone_name}/home/{self.admin_username}",
Expand Down Expand Up @@ -738,7 +798,7 @@ def build_unattended_install_input_for_catalog_provider(self):
"schema_version": "v5",
"server_port_range_end": self.parallel_port_range_end,
"server_port_range_start": self.parallel_port_range_begin,
"zone_auth_scheme": "native",
"zone_auth_scheme": self.authentication_scheme,
"zone_key": self.zone_key,
"zone_name": self.zone_name,
"zone_port": self.zone_port,
Expand Down Expand Up @@ -787,6 +847,22 @@ def build_unattended_install_input_for_catalog_provider(self):
"server_control_plane_port": 1248,
"server_control_plane_timeout_milliseconds": 10000
})
elif self.use_tls:
json_input["server_config"]["client_server_policy"] = "CS_NEG_REQUIRE"
json_input["server_config"]["tls_server"] = {
"certificate_chain_file": self.certificate_chain_file,
"certificate_key_file": self.certificate_key_file,
"dh_params_file": self.dh_params_file,
}
json_input["server_config"]["tls_client"] = {"verify_server": self.verify_server}
if self.ca_certificate_file:
json_input["server_config"]["tls_client"]["ca_certificate_file"] = self.ca_certificate_file
json_input["service_account_environment"]["irods_ssl_ca_certificate_file"] = self.ca_certificate_file
if self.ca_certificate_path:
json_input["server_config"]["tls_client"]["ca_certificate_path"] = self.ca_certificate_path
json_input["service_account_environment"]["irods_ssl_ca_certificate_path"] = self.ca_certificate_path
json_input["service_account_environment"]["irods_client_server_policy"] = "CS_NEG_REQUIRE"
json_input["service_account_environment"]["irods_ssl_verify_server"] = self.verify_server

return json.dumps(json_input, sort_keys=True, indent=4)

Expand Down Expand Up @@ -911,6 +987,24 @@ def setup_irods_server(container, setup_input, **kwargs):
from . import container_info
from . import irods_config

if kwargs.get("use_tls", False):
config_path = pathlib.Path(context.irods_config())
key_file = config_path / 'server.key'
dhparams_file = config_path / 'dhparams.pem'
chain_file = config_path / 'chain.pem'
cert_file = config_path / 'server.crt'

# Chain file and cert file use the same source for self-signed certs.
archive.copy_files_in_container(
container,
[
(kwargs.get("path_to_key_file_on_host"), key_file),
(kwargs.get("path_to_cert_file_on_host"), chain_file),
(kwargs.get("path_to_cert_file_on_host"), cert_file),
(kwargs.get("path_to_dhparams_file_on_host"), dhparams_file),
],
)

try:
if stop_irods(container) != 0:
logging.debug(f'[{container.name}] failed to stop iRODS server before setup')
Expand Down Expand Up @@ -939,8 +1033,13 @@ def setup_irods_server(container, setup_input, **kwargs):
run_setup_script = 'bash -c \'{} {} --json_configuration_file /input\''.format(
container_info.python(container), path_to_setup_script)
else:
run_setup_script = 'bash -c \'{} {} < /input\''.format(
container_info.python(container), path_to_setup_script)
args = []
if irods_config.get_irods_version(container) >= (5, 0, 90):
args = ['--auth-scheme', kwargs.get("authentication_scheme", "native")]
if kwargs.get("use_tls", False):
args.append("--tls")
args = " ".join(args)
run_setup_script = f'bash -c \'{container_info.python(container)} {path_to_setup_script} {args} < /input\''
ec = execute.execute_command(container, run_setup_script)
if ec != 0:
raise RuntimeError('failed to set up iRODS server [{}]'.format(container.name))
Expand Down Expand Up @@ -1003,9 +1102,7 @@ def setup_irods_catalog_provider(ctx,

logging.warning('setting up iRODS catalog provider [{}]'.format(csp_container.name))

setup_irods_server(csp_container,
setup_input,
do_unattended_install=kwargs.get('do_unattended_install', False))
setup_irods_server(csp_container, setup_input, **kwargs)


def setup_irods_catalog_consumer(ctx,
Expand Down Expand Up @@ -1052,9 +1149,7 @@ def setup_irods_catalog_consumer(ctx,

logging.warning('setting up iRODS catalog consumer [{}]'.format(csc_container.name))

setup_irods_server(csc_container,
setup_input,
do_unattended_install=kwargs.get('do_unattended_install', False))
setup_irods_server(csc_container, setup_input, **kwargs)


def setup_irods_catalog_consumers(ctx,
Expand All @@ -1072,8 +1167,6 @@ def setup_irods_catalog_consumers(ctx,
consumer service name in the Compose project will be
targeted. If an empty list is provided, nothing happens.
"""
import concurrent.futures

catalog_consumer_containers = ctx.compose_project.containers(
service_names=[context.irods_catalog_consumer_service()])

Expand Down Expand Up @@ -1170,8 +1263,6 @@ def setup_irods_zones(ctx,
zone_info_list,
odbc_driver=None,
**kwargs):
import concurrent.futures

rc = 0

with concurrent.futures.ThreadPoolExecutor() as executor:
Expand Down
32 changes: 27 additions & 5 deletions irods_testing_environment/services.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
# grown-up modules
"""Utility functions for managing Docker Compose services and setting up iRODS topologies."""

import logging
import os
import pathlib

# local modules
from . import context
from . import irods_setup
from . import context, irods_setup, tls_setup
from .install import install


def create_topologies(ctx,
zone_count,
externals_directory=None,
Expand Down Expand Up @@ -51,7 +52,28 @@ def create_topologies(ctx,
# This should generate a list of identical zone infos
zone_info_list = irods_setup.get_info_for_zones(ctx, zone_names, consumer_count)

irods_setup.setup_irods_zones(ctx, zone_info_list, odbc_driver=odbc_driver, **kwargs)
# Forcibly enable TLS usage if using an authentication scheme other than native.
if kwargs.get("authentication_scheme", "native") != "native":
kwargs["use_tls"] = True

if kwargs.get("use_tls", False):
# The testing environment is using a self-signed certificate, so the certificate, key, and dhparams should be
# generated ONCE and copied to each server.
key, key_file = tls_setup.generate_tls_certificate_key()
cert_file = tls_setup.generate_tls_self_signed_certificate(key)
dhparams_file = tls_setup.generate_tls_dh_params()
kwargs["path_to_key_file_on_host"] = key_file
kwargs["path_to_cert_file_on_host"] = cert_file
kwargs["path_to_dhparams_file_on_host"] = dhparams_file

try:
irods_setup.setup_irods_zones(ctx, zone_info_list, odbc_driver=odbc_driver, **kwargs)

finally:
if kwargs.get("use_tls", False):
pathlib.Path(key_file).unlink()
pathlib.Path(cert_file).unlink()
pathlib.Path(dhparams_file).unlink()


def create_topology(ctx,
Expand Down
55 changes: 38 additions & 17 deletions stand_it_up.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
# grown-up modules
import compose.cli.command
import docker
import logging
import os

# local modules
from irods_testing_environment import context
from irods_testing_environment import services
from irods_testing_environment import tls_setup
import docker

import compose.cli.command
from irods_testing_environment import context, irods_config, services, tls_setup

if __name__ == "__main__":
import argparse
Expand Down Expand Up @@ -35,6 +32,15 @@
If indicated, the iRODS servers will be set up using \
unattended installation.''')

parser.add_argument(
'--use-irods-auth',
dest='use_irods_auth',
action='store_true',
help=textwrap.dedent('''\
Indicates that irods auth scheme should be used instead \
of native. Implies --use-tls.'''),
)

args = parser.parse_args()

if not args.package_version and not args.install_packages:
Expand All @@ -45,6 +51,10 @@
print('--irods-package-directory and --irods-package-version are incompatible')
exit(1)

# Set use_tls option to True if using irods authentication as it is required.
if args.use_irods_auth:
args.use_tls = True

project_directory = os.path.abspath(args.project_directory or os.getcwd())

if not args.install_packages:
Expand All @@ -63,14 +73,25 @@

# Bring up the services
logging.debug('bringing up project [{}]'.format(ctx.compose_project.name))
services.create_topology(ctx,
externals_directory=args.irods_externals_package_directory,
package_directory=args.package_directory,
package_version=args.package_version,
odbc_driver=args.odbc_driver,
consumer_count=args.consumer_count,
install_packages=args.install_packages,
do_unattended_install=args.do_unattended_install)

if args.use_tls:
services.create_topology(
ctx,
externals_directory=args.irods_externals_package_directory,
package_directory=args.package_directory,
package_version=args.package_version,
odbc_driver=args.odbc_driver,
consumer_count=args.consumer_count,
install_packages=args.install_packages,
do_unattended_install=args.do_unattended_install,
use_tls=args.use_tls,
authentication_scheme="irods" if args.use_irods_auth else "native",
)

containers = [
ctx.docker_client.containers.get(
context.container_name(ctx.compose_project.name, context.irods_catalog_provider_service())
)
]

# TLS configuration happens in setup as of 5.1.0, so only do this for prior versions when requested.
if args.use_tls and irods_config.get_irods_version(containers[0]) < (5, 0, 90):
tls_setup.configure_tls_in_zone(ctx.docker_client, ctx.compose_project)
Loading