From c08a437911ee5fb8b4729b435f5acc235eb11fc8 Mon Sep 17 00:00:00 2001 From: Yige Zhu Date: Mon, 27 Mar 2023 11:42:39 -0700 Subject: [PATCH] Add TLS plugin to onedocker dockerfile (#2245) Summary: Pull Request resolved: https://github.com/facebookresearch/fbpcs/pull/2245 Context: We want to create a onedocker image such that we will be able to run the TLS script before running a given binary. We will integrate with OPA (OneDocker Plugin Architecture) to do that. Currently the OPA design only supports local plugins (plugins directly embedded in the image). Changes: 1. Create a plugins/ folder under fbpcs/docker/onedocker/prod to store plugins 2. Create a opa_workflows/ folder under fbpcs/docker/onedocker/prod to store OPA workflows 3. Updated the dockerfile to embed TLS related plugins and workflow in the image. Reviewed By: musebc Differential Revision: D44235501 fbshipit-source-id: 75891cdde7f27a51638fb3371d2c3ae251d7154d --- docker/onedocker/prod/Dockerfile.ubuntu | 14 ++- .../prod/opa_workflows/tls_workflow.json | 11 ++ .../prod/plugins/tls_cert_installer.py | 113 ++++++++++++++++++ .../onedocker/prod/plugins/write_routing.sh | 7 ++ .../private_computation/service/constants.py | 2 +- 5 files changed, 145 insertions(+), 2 deletions(-) create mode 100644 docker/onedocker/prod/opa_workflows/tls_workflow.json create mode 100644 docker/onedocker/prod/plugins/tls_cert_installer.py create mode 100644 docker/onedocker/prod/plugins/write_routing.sh diff --git a/docker/onedocker/prod/Dockerfile.ubuntu b/docker/onedocker/prod/Dockerfile.ubuntu index f8dbe7663..44bc0f28b 100644 --- a/docker/onedocker/prod/Dockerfile.ubuntu +++ b/docker/onedocker/prod/Dockerfile.ubuntu @@ -9,7 +9,9 @@ FROM ubuntu:${os_release} ENV TZ=America/Los_Angeles RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone -RUN useradd -ms /bin/bash onedocker +ARG caAdminGroup=ca-admins +RUN groupadd --gid 1001 ${caAdminGroup} +RUN useradd -ms /bin/bash onedocker -g ${caAdminGroup} USER onedocker RUN mkdir -p /home/onedocker/package @@ -44,11 +46,21 @@ RUN mkdir -p /home/onedocker/src ADD pip_requirements.txt /home/onedocker/src RUN python3.8 -m pip install -r /home/onedocker/src/pip_requirements.txt +# Add TLS Plugin +WORKDIR /home/onedocker/package +ADD plugins/tls_cert_installer.py . +ADD opa_workflows/tls_workflow.json . +ADD plugins/write_routing.sh . + # limit execution permission only to the directory where binaries are downloaded RUN chown -R onedocker /home/onedocker/package/ RUN chmod -R u-x ~/ RUN chmod -R u+rw /tmp RUN chmod -R u+rwx /home/onedocker/package/ +# Give runtime user special permission to run the install certificate binary +ENV WRITE_ROUTING_SCRIPT="/home/onedocker/package/write_routing.sh" +RUN echo "%${caAdminGroup} ALL=(ALL) NOPASSWD: ${WRITE_ROUTING_SCRIPT}" >> /etc/sudoers + CMD ["/bin/bash"] WORKDIR /home/onedocker diff --git a/docker/onedocker/prod/opa_workflows/tls_workflow.json b/docker/onedocker/prod/opa_workflows/tls_workflow.json new file mode 100644 index 000000000..5c678cf8d --- /dev/null +++ b/docker/onedocker/prod/opa_workflows/tls_workflow.json @@ -0,0 +1,11 @@ +{ + "StartAt": "State1", + "States": { + "State1": { + "PluginName": "python3 /home/onedocker/package/tls_cert_installer.py", + "CmdArgsList": [], + "Timeout": null, + "IsEnd": true + } + } +} diff --git a/docker/onedocker/prod/plugins/tls_cert_installer.py b/docker/onedocker/prod/plugins/tls_cert_installer.py new file mode 100644 index 000000000..191635357 --- /dev/null +++ b/docker/onedocker/prod/plugins/tls_cert_installer.py @@ -0,0 +1,113 @@ +#!/usr/bin/env python3 +# Copyright (c) Meta Platforms, Inc. and affiliates. +# +# This source code is licensed under the MIT license found in the +# LICENSE file in the root directory of this source tree. + +# pyre-strict + +import logging +import os +import sys + +from fbpcp.service.secrets_manager_aws import AWSSecretsManagerService + +# environment variables +SERVER_PRIVATE_KEY = "SERVER_PRIVATE_KEY" +SERVER_PRIVATE_KEY_REF = "SERVER_PRIVATE_KEY_REF" +SERVER_CERTIFICATE = "SERVER_CERTIFICATE" +ISSUER_CERTIFICATE = "ISSUER_CERTIFICATE" +SERVER_PRIVATE_KEY_PATH = "SERVER_PRIVATE_KEY_PATH" +SERVER_CERTIFICATE_PATH = "SERVER_CERTIFICATE_PATH" +ISSUER_CERTIFICATE_PATH = "ISSUER_CERTIFICATE_PATH" +HOME_DIR = "HOME" +HOSTALIASES = "HOSTALIASES" +IP_ADDRESS = "IP_ADDRESS" +SERVER_HOSTNAME = "SERVER_HOSTNAME" +REGION = "REGION" + +# other constants +HOST_FILE_PATH = "/etc/hosts" +DEFAULT_REGION = "us-west-2" + + +def _get_env_var_if_set(env_var: str, default_val: str) -> str: + val = os.getenv(env_var) + return val if val else default_val + + +def _write_content_to_file(full_path: str, content: str) -> None: + parent_path = "/".join(full_path.split("/")[:-1]) + os.makedirs(parent_path, exist_ok=True) + with open(full_path, "w") as fw: + fw.write(content) + + +def _get_secret(secret_id: str, region: str) -> str: + secret_svc = AWSSecretsManagerService(region) + return secret_svc.get_secret(secret_id).value + + +def main() -> None: + logger = logging.getLogger() + streamHandler = logging.StreamHandler(sys.stdout) + formatter = logging.Formatter("%(levelname)s:%(filename)s:%(message)s") + logger.setLevel(logging.DEBUG) + streamHandler.setFormatter(formatter) + logger.addHandler(streamHandler) + + logging.info("Reading certificate content from environment variables...") + server_certificate = _get_env_var_if_set(SERVER_CERTIFICATE, "") + server_certificate_path = _get_env_var_if_set(SERVER_CERTIFICATE_PATH, "") + issuer_certificate = _get_env_var_if_set(ISSUER_CERTIFICATE, "") + issuer_certificate_path = _get_env_var_if_set(ISSUER_CERTIFICATE_PATH, "") + private_key = _get_env_var_if_set(SERVER_PRIVATE_KEY, "") + private_key_ref = _get_env_var_if_set(SERVER_PRIVATE_KEY_REF, "") + private_key_path = _get_env_var_if_set(SERVER_PRIVATE_KEY_PATH, "") + home_dir = _get_env_var_if_set(HOME_DIR, "") + ip_address = _get_env_var_if_set(IP_ADDRESS, "") + server_hostname = _get_env_var_if_set(SERVER_HOSTNAME, "") + region = _get_env_var_if_set(REGION, DEFAULT_REGION) + + try: + logging.info("Starting writing certificates...") + if server_certificate_path and server_certificate: + full_server_cert_path = os.path.join(home_dir, server_certificate_path) + _write_content_to_file(full_server_cert_path, server_certificate) + logging.info(f"Wrote server certificate to {full_server_cert_path}") + + if issuer_certificate_path and issuer_certificate: + full_issuer_cert_path = os.path.join(home_dir, issuer_certificate_path) + _write_content_to_file(full_issuer_cert_path, issuer_certificate) + logging.info(f"Wrote issuer certificate to {full_issuer_cert_path}") + + if private_key_path: + full_private_key_path = os.path.join(home_dir, private_key_path) + if private_key_ref: + secret = _get_secret(private_key_ref, region) + _write_content_to_file(full_private_key_path, secret) + logging.info(f"Wrote private_key to {full_private_key_path}") + elif private_key: + _write_content_to_file(full_private_key_path, private_key) + logging.info(f"Wrote private_key to {full_private_key_path}") + + if ip_address and server_hostname: + logging.info("Start setting up routing config in the host file.") + # sudo permission to run this script is built into the dockerfile + # with specified user group. + os.system( + f"sudo /home/onedocker/package/write_routing.sh {ip_address} {server_hostname}" + ) + logging.info(f"Wrote IP address and host name to {HOST_FILE_PATH}") + else: + logging.info( + "Routing not configured because at least one of ip_address and server_hostname is not specified." + ) + except Exception as e: + raise Exception( + f"Caught an exception while executing the binary: {e.with_traceback(e.__traceback__)}" + ) + + +if __name__ == "__main__": + main() diff --git a/docker/onedocker/prod/plugins/write_routing.sh b/docker/onedocker/prod/plugins/write_routing.sh new file mode 100644 index 000000000..d84696525 --- /dev/null +++ b/docker/onedocker/prod/plugins/write_routing.sh @@ -0,0 +1,7 @@ +#!/bin/bash +# Copyright (c) Meta Platforms, Inc. and affiliates. +# +# This source code is licensed under the MIT license found in the +# LICENSE file in the root directory of this source tree. + +echo "$1 $2" >> /etc/hosts diff --git a/fbpcs/private_computation/service/constants.py b/fbpcs/private_computation/service/constants.py index 57bb9d1cc..28ef811f7 100644 --- a/fbpcs/private_computation/service/constants.py +++ b/fbpcs/private_computation/service/constants.py @@ -82,4 +82,4 @@ SERVER_IP_ADDRESS_ENV_VAR = "IP_ADDRESS" # OneDocker plugin architecture workflow paths -TLS_OPA_WORKFLOW_PATH = "/home/onedocker/tls_workflow.json" +TLS_OPA_WORKFLOW_PATH = "/home/onedocker/package/tls_workflow.json"