Skip to content

Commit

Permalink
Add TLS plugin to onedocker dockerfile (#2245)
Browse files Browse the repository at this point in the history
Summary:
Pull Request resolved: #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
  • Loading branch information
YigeZhu authored and facebook-github-bot committed Mar 27, 2023
1 parent f2693d1 commit c08a437
Show file tree
Hide file tree
Showing 5 changed files with 145 additions and 2 deletions.
14 changes: 13 additions & 1 deletion docker/onedocker/prod/Dockerfile.ubuntu
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
11 changes: 11 additions & 0 deletions docker/onedocker/prod/opa_workflows/tls_workflow.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"StartAt": "State1",
"States": {
"State1": {
"PluginName": "python3 /home/onedocker/package/tls_cert_installer.py",
"CmdArgsList": [],
"Timeout": null,
"IsEnd": true
}
}
}
113 changes: 113 additions & 0 deletions docker/onedocker/prod/plugins/tls_cert_installer.py
Original file line number Diff line number Diff line change
@@ -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()
7 changes: 7 additions & 0 deletions docker/onedocker/prod/plugins/write_routing.sh
Original file line number Diff line number Diff line change
@@ -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
2 changes: 1 addition & 1 deletion fbpcs/private_computation/service/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"

0 comments on commit c08a437

Please sign in to comment.