From b050996b9f6df738a0f68ac36a5b5f17f8bb2bc2 Mon Sep 17 00:00:00 2001 From: Lingxian Kong Date: Fri, 18 Jun 2021 11:30:03 +1200 Subject: [PATCH] Use bridge network for db container - Changed the network mode of database container to "bridge" and exposed the service ports. - Use socket file to connect with the database. - Upgrade the backup container image for postgressql. Change-Id: Id5b119f8a474befc3a2cd6e061bbffc4ae5f7bb6 --- backup/Dockerfile | 2 +- backup/utils/postgresql.py | 12 +++++++++- .../guest-agent/package-installs.yaml | 2 +- .../notes/xena-container-bridge-network.yaml | 14 +++++++++++ trove/common/cfg.py | 2 +- .../datastore/mysql_common/manager.py | 2 ++ .../datastore/mysql_common/service.py | 11 +++++++-- .../guestagent/datastore/postgres/service.py | 23 ++++++++++++++++--- trove/guestagent/datastore/service.py | 6 ++++- .../strategies/replication/mysql_base.py | 1 + trove/guestagent/utils/docker.py | 7 +++++- 11 files changed, 71 insertions(+), 11 deletions(-) create mode 100644 releasenotes/notes/xena-container-bridge-network.yaml diff --git a/backup/Dockerfile b/backup/Dockerfile index f60149c70c..3827bfc7d9 100644 --- a/backup/Dockerfile +++ b/backup/Dockerfile @@ -18,7 +18,7 @@ WORKDIR /opt/trove/backup RUN ./install.sh $DATASTORE RUN apt-get update \ - && apt-get install $APTOPTS build-essential python3-setuptools python3-all python3-all-dev python3-pip libffi-dev libssl-dev libxml2-dev libxslt1-dev libyaml-dev \ + && apt-get install $APTOPTS build-essential python3-setuptools python3-all python3-all-dev python3-pip libffi-dev libssl-dev libxml2-dev libxslt1-dev libyaml-dev libpq-dev \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* \ && pip3 --no-cache-dir install -U -r requirements.txt \ diff --git a/backup/utils/postgresql.py b/backup/utils/postgresql.py index 033652f068..e43570e912 100644 --- a/backup/utils/postgresql.py +++ b/backup/utils/postgresql.py @@ -15,7 +15,17 @@ import psycopg2 class PostgresConnection(object): - def __init__(self, user, password='', host='localhost', port=5432): + def __init__(self, user, password='', host='/var/run/postgresql', + port=5432): + """Utility class to communicate with PostgreSQL. + + Connect with socket rather than IP or localhost address to avoid + manipulation of pg_hba.conf when the database is running inside + container with bridge network. + + This class is consistent with PostgresConnection in + trove/guestagent/datastore/postgres/service.py + """ self.user = user self.password = password self.host = host diff --git a/integration/scripts/files/elements/guest-agent/package-installs.yaml b/integration/scripts/files/elements/guest-agent/package-installs.yaml index 6f2e7892a0..5eccd1fe44 100644 --- a/integration/scripts/files/elements/guest-agent/package-installs.yaml +++ b/integration/scripts/files/elements/guest-agent/package-installs.yaml @@ -42,5 +42,5 @@ rsyslog: ubuntu-cloudimage-keyring: ureadahead: uuid-runtime: -vim-tiny: +vim: vlan: diff --git a/releasenotes/notes/xena-container-bridge-network.yaml b/releasenotes/notes/xena-container-bridge-network.yaml new file mode 100644 index 0000000000..b70d370e1b --- /dev/null +++ b/releasenotes/notes/xena-container-bridge-network.yaml @@ -0,0 +1,14 @@ +--- +security: + - | + Changed the network mode of database container to "bridge" and exposed the + service ports. Cloud operator could adjust the iptables to restrict network + access from the database container to the outside. An example:: + + iptables -t filter -I DOCKER-USER 1 -d [restricted-network-range] -i docker0 ! -o docker0 -j REJECT + +upgrade: + - The default value of the trove guest agent config option + ``[postgresql] backup_docker_image`` is changed to + ``openstacktrove/db-backup-postgresql:1.1.1``. There is nothing to do if + the option is not configured explicitly. diff --git a/trove/common/cfg.py b/trove/common/cfg.py index e4c918021f..49ea8b8286 100644 --- a/trove/common/cfg.py +++ b/trove/common/cfg.py @@ -1097,7 +1097,7 @@ postgresql_opts = [ ), cfg.StrOpt( 'backup_docker_image', - default='openstacktrove/db-backup-postgresql:1.1.0', + default='openstacktrove/db-backup-postgresql:1.1.1', help='The docker image used for backup and restore.' ), cfg.BoolOpt('icmp', default=False, diff --git a/trove/guestagent/datastore/mysql_common/manager.py b/trove/guestagent/datastore/mysql_common/manager.py index a81431a16c..e27b47ffaf 100644 --- a/trove/guestagent/datastore/mysql_common/manager.py +++ b/trove/guestagent/datastore/mysql_common/manager.py @@ -112,8 +112,10 @@ class MySqlManager(manager.Manager): """ LOG.info(f"Creating backup {backup_info['id']}") with EndNotification(context): + # Set /var/run/mysqld to allow localhost access. volumes_mapping = { '/var/lib/mysql': {'bind': '/var/lib/mysql', 'mode': 'rw'}, + "/var/run/mysqld": {"bind": "/var/run/mysqld", "mode": "ro"}, '/tmp': {'bind': '/tmp', 'mode': 'rw'} } self.app.create_backup(context, backup_info, diff --git a/trove/guestagent/datastore/mysql_common/service.py b/trove/guestagent/datastore/mysql_common/service.py index 1ab1d5fc31..64abe54204 100644 --- a/trove/guestagent/datastore/mysql_common/service.py +++ b/trove/guestagent/datastore/mysql_common/service.py @@ -586,13 +586,20 @@ class BaseMySqlApp(service.BaseDbApp): if extra_volumes: volumes.update(extra_volumes) + # Expose ports + ports = {} + tcp_ports = cfg.get_configuration_property('tcp_ports') + for port_range in tcp_ports: + for port in port_range: + ports[f'{port}/tcp'] = port + try: - LOG.info("Starting docker container, image: %s", image) docker_util.start_container( self.docker_client, image, volumes=volumes, - network_mode="host", + network_mode="bridge", + ports=ports, user=user, environment={ "MYSQL_ROOT_PASSWORD": root_pass, diff --git a/trove/guestagent/datastore/postgres/service.py b/trove/guestagent/datastore/postgres/service.py index 404db303ec..4c2435f039 100644 --- a/trove/guestagent/datastore/postgres/service.py +++ b/trove/guestagent/datastore/postgres/service.py @@ -190,13 +190,20 @@ class PgSqlApp(service.BaseDbApp): if extra_volumes: volumes.update(extra_volumes) + # Expose ports + ports = {} + tcp_ports = cfg.get_configuration_property('tcp_ports') + for port_range in tcp_ports: + for port in port_range: + ports[f'{port}/tcp'] = port + try: - LOG.info("Starting docker container, image: %s", image) docker_util.start_container( self.docker_client, image, volumes=volumes, - network_mode="host", + network_mode="bridge", + ports=ports, user=user, environment={ "POSTGRES_PASSWORD": postgres_pass, @@ -727,7 +734,17 @@ class PgSqlAdmin(object): class PostgresConnection(object): - def __init__(self, user, password=None, host='localhost', port=5432): + def __init__(self, user, password=None, host='/var/run/postgresql', + port=5432): + """Utility class to communicate with PostgreSQL. + + Connect with socket rather than IP or localhost address to avoid + manipulation of pg_hba.conf when the database is running inside + container with bridge network. + + This class is consistent with PostgresConnection in + backup/utils/postgresql.py + """ self.user = user self.password = password self.host = host diff --git a/trove/guestagent/datastore/service.py b/trove/guestagent/datastore/service.py index d016ab5d9f..fe30652d2a 100644 --- a/trove/guestagent/datastore/service.py +++ b/trove/guestagent/datastore/service.py @@ -395,6 +395,9 @@ class BaseDbApp(object): ): raise exception.TroveError("Failed to stop database") + def start_db(self, *args, **kwargs): + pass + def start_db_with_conf_changes(self, config_contents, ds_version): LOG.info(f"Starting database service with new configuration and " f"datastore version {ds_version}.") @@ -435,7 +438,8 @@ class BaseDbApp(object): db_userinfo = '' if need_dbuser: admin_pass = self.get_auth_password() - db_userinfo = (f"--db-host=127.0.0.1 --db-user=os_admin " + # Use localhost to avoid host access verification. + db_userinfo = (f"--db-host=localhost --db-user=os_admin " f"--db-password={admin_pass}") swift_metadata = ( diff --git a/trove/guestagent/strategies/replication/mysql_base.py b/trove/guestagent/strategies/replication/mysql_base.py index e6dfc3cc3f..744aec6b56 100644 --- a/trove/guestagent/strategies/replication/mysql_base.py +++ b/trove/guestagent/strategies/replication/mysql_base.py @@ -82,6 +82,7 @@ class MysqlReplicationBase(base.Replication): volumes_mapping = { '/var/lib/mysql': {'bind': '/var/lib/mysql', 'mode': 'rw'}, + "/var/run/mysqld": {"bind": "/var/run/mysqld", "mode": "ro"}, '/tmp': {'bind': '/tmp', 'mode': 'rw'} } service.create_backup(context, snapshot_info, diff --git a/trove/guestagent/utils/docker.py b/trove/guestagent/utils/docker.py index 0f254aa612..beff4f1c14 100644 --- a/trove/guestagent/utils/docker.py +++ b/trove/guestagent/utils/docker.py @@ -56,9 +56,14 @@ def start_container(client, image, name="database", """ try: container = client.containers.get(name) + LOG.info(f'Starting existing container {name}') container.start() except docker.errors.NotFound: - LOG.warning("Failed to get container %s", name) + LOG.info( + f"Creating docker container, image: {image}, " + f"volumes: {volumes}, ports: {ports}, user: {user}, " + f"network_mode: {network_mode}, environment: {environment}, " + f"command: {command}") container = client.containers.run( image, name=name,