Skip to content

Commit

Permalink
CFY-3532-run-services-as-non-root
Browse files Browse the repository at this point in the history
  • Loading branch information
Madda committed Dec 16, 2015
1 parent 2d75ca3 commit 8a3e0e5
Show file tree
Hide file tree
Showing 8 changed files with 114 additions and 7 deletions.
2 changes: 2 additions & 0 deletions components/mgmtworker/config/cloudify-mgmtworker.service
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ TimeoutStartSec=0
Restart=on-failure
EnvironmentFile=-/etc/sysconfig/cloudify-mgmtworker
WorkingDirectory=/opt/mgmtworker/work
User={{ ctx.instance.runtime_properties.mgmtworker_user }}
Group={{ ctx.instance.runtime_properties.mgmtworker_group }}
ExecStart=/opt/mgmtworker/env/bin/celery worker \
-Ofair \
--include=cloudify_system_workflows.snapshot,cloudify_system_workflows.deployment_environment,cloudify_agent.operations,cloudify_agent.installer.operations,riemann_controller.tasks,cloudify.plugins.workflows \
Expand Down
40 changes: 39 additions & 1 deletion components/mgmtworker/scripts/create.sh
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,22 @@ export PLUGINS_COMMON_SOURCE_URL=$(ctx node properties plugins_common_module_sou
export SCRIPT_PLUGIN_SOURCE_URL=$(ctx node properties script_plugin_module_source_url)
export REST_SERVICE_SOURCE_URL=$(ctx node properties rest_service_module_source_url)
export AGENT_SOURCE_URL=$(ctx node properties agent_module_source_url)
export SSH_KEY_FILE=$(ctx node properties ssh_key_filename)
export AGENT_USER=$(ctx node properties agents_user)

# This will only be used if the management worker is not installed via an RPM
export CELERY_VERSION="3.1.17"

export MGMTWORKER_USER='cloudifymgmtworker'
export MGMTWORKER_GROUP='cloudifymgmtworker'
export MGMTWORKER_HOME="/opt/mgmtworker"

# these must all be exported as part of the start operation. they will not persist, so we should use the new agent
# don't forget to change all localhosts to the relevant ips
export MGMTWORKER_HOME="/opt/mgmtworker"
export MGMTWORKER_VIRTUALENV_DIR="${MGMTWORKER_HOME}/env"
export CELERY_WORK_DIR="${MGMTWORKER_HOME}/work"
export CELERY_LOG_DIR="/var/log/cloudify/mgmtworker"
export MGMTWORKER_RIEMANN_POLICY_DIR="/opt/riemann"

# Set broker port for rabbit
export BROKER_PORT_SSL="5671"
Expand All @@ -28,6 +34,8 @@ export RABBITMQ_SSL_ENABLED="$(ctx -j node properties rabbitmq_ssl_enabled)"
export RABBITMQ_CERT_PUBLIC="$(ctx node properties rabbitmq_cert_public)"

ctx instance runtime_properties rabbitmq_endpoint_ip "$(get_rabbitmq_endpoint_ip)"
ctx instance runtime_properties mgmtworker_user ${MGMTWORKER_USER}
ctx instance runtime_properties mgmtworker_group ${MGMTWORKER_GROUP}

# Fix possible injections in json of rabbit credentials
# See json.org for string spec
Expand All @@ -51,6 +59,12 @@ create_dir ${MGMTWORKER_HOME}/config
create_dir ${CELERY_LOG_DIR}
create_dir ${CELERY_WORK_DIR}

create_service_user ${MGMTWORKER_USER} ${MGMTWORKER_HOME} /bin/bash

# This directory is populated when deployments are created- counterintuitively it does need to live here rather than the riemann component
create_dir ${MGMTWORKER_RIEMANN_POLICY_DIR}
set_directory_tree_ownership ${MGMTWORKER_USER} ${MGMTWORKER_GROUP} ${MGMTWORKER_RIEMANN_POLICY_DIR}

# this create the MGMTWORKER_VIRTUALENV_DIR and installs the relevant modules into it.
yum_install ${MANAGEMENT_WORKER_RPM_SOURCE_URL}

Expand Down Expand Up @@ -98,4 +112,28 @@ for python_path in ${MGMTWORKER_VIRTUALENV_DIR}/lib/python*; do
# The config contains credentials, do not let the world read it
chmod 440 "${BROKER_CONF_PATH}"
done

# Copy or move key files to appropriate locations
if sudo test -f /root/.ssh/agent_key.pem; then
# We have to copy this at the moment as we still depend on it being in that location for something in non simple-manager bootstrapping
sudo cp /root/.ssh/agent_key.pem /opt/mgmtworker
else
# If the key file wasn't where we expected then we're probably running a simple-manager-blueprint
# If this fails, the management worker wouldn't be able to ssh into any compute nodes, so failing is acceptable
# Using this ugly approach because tilde expansion isn't working here. This should be fixed when we standardise
# key locations.
sudo cp ${SSH_KEY_FILE/\~/\/home\/${AGENT_USER}} /opt/mgmtworker/agent_key.pem
fi

# Set ownership
set_directory_tree_ownership ${MGMTWORKER_USER} ${MGMTWORKER_GROUP} ${MGMTWORKER_HOME}
set_directory_tree_ownership ${MGMTWORKER_USER} ${MGMTWORKER_GROUP} ${MGMTWORKER_VIRTUALENV_DIR}
set_directory_tree_ownership ${MGMTWORKER_USER} ${MGMTWORKER_GROUP} ${CELERY_WORK_DIR}
set_directory_tree_ownership ${MGMTWORKER_USER} ${MGMTWORKER_GROUP} ${CELERY_LOG_DIR}

# Management worker has to create services
# TODO: It would be better if we made a specific script and allowed sudo only for that script to allow the services to be created.
# This requires modifications to the agent as well, and may require modification to the agent installer.
allow_sudo_for_user ${MGMTWORKER_USER}

configure_systemd_service "mgmtworker"
6 changes: 4 additions & 2 deletions components/restservice/config/cloudify-restservice.service
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,16 @@ Description=Cloudify REST Service
PIDFile=/var/run/gunicorn.pid
TimeoutStartSec=0
Restart=on-failure
User={{ ctx.source.instance.runtime_properties.rest_service_user }}
Group={{ ctx.source.instance.runtime_properties.rest_service_group }}
EnvironmentFile=-/etc/sysconfig/cloudify-restservice
ExecStart=/bin/sh -c '/opt/manager/env/bin/gunicorn \
--pid /var/run/gunicorn.pid \
--pid {{ ctx.source.instance.runtime_properties.rest_service_pid_dir }}/gunicorn.pid \
-w $(($(nproc)*2+1)) \
-b 0.0.0.0:${REST_SERVICE_PORT} \
--timeout 300 manager_rest.server:app \
--log-file /var/log/cloudify/rest/gunicorn.log \
--access-logfile /var/log/cloudify/rest/gunicorn-access.log'

[Install]
WantedBy=multi-user.target
WantedBy=multi-user.target
24 changes: 22 additions & 2 deletions components/restservice/scripts/create.sh
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,23 @@ export PLUGINS_COMMON_SOURCE_URL=$(ctx node properties plugins_common_module_sou
export SCRIPT_PLUGIN_SOURCE_URL=$(ctx node properties script_plugin_module_source_url)
export AGENT_SOURCE_URL=$(ctx node properties agent_module_source_url)

# TODO: change to /opt/cloudify-rest-service
export REST_SERVICE_HOME="/opt/manager"
export REST_SERVICE_USER='cloudifyrest'
export REST_SERVICE_GROUP='cloudifyrest'
export REST_SERVICE_PID_DIR='/var/run/rest'

# injected as an input to the script
ctx instance runtime_properties es_endpoint_ip ${ES_ENDPOINT_IP}

ctx instance runtime_properties rabbitmq_endpoint_ip "$(get_rabbitmq_endpoint_ip)"
ctx instance runtime_properties rest_service_user ${REST_SERVICE_USER}
ctx instance runtime_properties rest_service_group ${REST_SERVICE_GROUP}
ctx instance runtime_properties rest_service_pid_dir ${REST_SERVICE_PID_DIR}

export RABBITMQ_SSL_ENABLED="$(ctx -j node properties rabbitmq_ssl_enabled)"
export RABBITMQ_CERT_PUBLIC="$(ctx node properties rabbitmq_cert_public)"

# TODO: change to /opt/cloudify-rest-service
export REST_SERVICE_HOME="/opt/manager"
export MANAGER_RESOURCES_HOME="/opt/manager/resources"
export RESTSERVICE_VIRTUALENV="${REST_SERVICE_HOME}/env"
# guni.conf currently contains localhost for all endpoints. We need to change that.
Expand All @@ -45,6 +52,9 @@ copy_notice "restservice"
create_dir ${REST_SERVICE_HOME}
create_dir ${REST_SERVICE_LOG_PATH}
create_dir ${MANAGER_RESOURCES_HOME}
create_dir ${REST_SERVICE_PID_DIR}

create_service_user ${REST_SERVICE_USER} ${REST_SERVICE_HOME}

# Add certificate and select port, as applicable
if [[ "${RABBITMQ_SSL_ENABLED}" == 'true' ]]; then
Expand Down Expand Up @@ -96,3 +106,13 @@ deploy_logrotate_config "restservice"
ctx logger info "Deploying REST Service Configuration file..."
# rest service ports are set as runtime properties in nginx/scripts/create.sh
deploy_blueprint_resource "${CONFIG_REL_PATH}/cloudify-rest.conf" "${REST_SERVICE_HOME}/cloudify-rest.conf"

# Set ownership
set_directory_tree_ownership ${REST_SERVICE_USER} ${REST_SERVICE_GROUP} ${REST_SERVICE_HOME}
set_directory_tree_ownership root ${REST_SERVICE_USER} ${REST_SERVICE_HOME}/env
set_directory_tree_ownership ${REST_SERVICE_USER} ${REST_SERVICE_GROUP} ${REST_SERVICE_LOG_PATH}
set_directory_tree_ownership ${REST_SERVICE_USER} ${REST_SERVICE_GROUP} ${REST_SERVICE_PID_DIR}

# Improve permissions
# Nothing in the env should be writable by the rest service- that's the site code
sudo find ${REST_SERVICE_HOME}/env -exec chmod g-w {} \;
4 changes: 3 additions & 1 deletion components/riemann/config/cloudify-riemann.service
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@ Description=Riemann Service
[Service]
TimeoutStartSec=0
Restart=always
User={{ ctx.instance.runtime_properties.riemann_user }}
Group={{ ctx.instance.runtime_properties.riemann_group }}
EnvironmentFile=-/etc/sysconfig/cloudify-riemann
ExecStart=/usr/bin/riemann -a ${RIEMANN_CONFIG_PATH}/main.clj

[Install]
WantedBy=multi-user.target
WantedBy=multi-user.target
14 changes: 14 additions & 0 deletions components/riemann/scripts/create.sh
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ export EXTRA_CLASSPATH="${LANGOHR_HOME}/langohr.jar"
export RABBITMQ_USERNAME="$(ctx node properties rabbitmq_username)"
export RABBITMQ_PASSWORD="$(ctx node properties rabbitmq_password)"

export RIEMANN_USER='riemann'
export RIEMANN_GROUP='riemann'
export RIEMANN_HOME='/var/lib/riemann'

# Confirm username and password have been supplied for broker before continuing
# Components other than logstash and riemann have this handled in code already
# Note that these are not directly used in this script, but are used by the deployed resources, hence the check here.
Expand All @@ -28,6 +32,8 @@ if [[ -z "${RABBITMQ_USERNAME}" ]] ||
fi

ctx instance runtime_properties rabbitmq_endpoint_ip "$(get_rabbitmq_endpoint_ip)"
ctx instance runtime_properties riemann_user ${RIEMANN_USER}
ctx instance runtime_properties riemann_group ${RIEMANN_GROUP}

ctx logger info "Installing Riemann..."
set_selinux_permissive
Expand All @@ -38,6 +44,14 @@ create_dir ${LANGOHR_HOME}
create_dir ${RIEMANN_CONFIG_PATH}
create_dir ${RIEMANN_CONFIG_PATH}/conf.d

create_service_user ${RIEMANN_USER} ${RIEMANN_HOME}
create_dir ${RIEMANN_HOME}

# Set ownership
set_directory_tree_ownership ${RIEMANN_USER} ${RIEMANN_GROUP} ${RIEMANN_HOME}
set_directory_tree_ownership ${RIEMANN_USER} ${RIEMANN_GROUP} ${RIEMANN_LOG_PATH}
set_directory_tree_ownership root ${RIEMANN_GROUP} ${RIEMANN_CONFIG_PATH}

langohr=$(download_cloudify_resource ${LANGOHR_SOURCE_URL})
sudo cp ${langohr} ${EXTRA_CLASSPATH}
ctx logger info "Applying Langohr permissions..."
Expand Down
23 changes: 22 additions & 1 deletion components/utils
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,7 @@ function create_service_user() {
###
user=$1
home=$2
shell=${3:-/sbin/nologin}

ctx logger info "Checking if ${user} exists..."
ret=false
Expand All @@ -385,7 +386,7 @@ function create_service_user() {
ctx logger info "User ${user} already exists..."
else
ctx logger info "Creating user ${user} with home ${home}..."
sudo useradd --shell /sbin/nologin --home-dir "${home}" --no-create-home --system "${user}"
sudo useradd --shell ${shell} --home-dir "${home}" --no-create-home --system "${user}"
fi
}

Expand Down Expand Up @@ -526,4 +527,24 @@ function get_rabbitmq_endpoint_ip() {
echo "${RABBITMQ_ENDPOINT_IP}"
}

function allow_sudo_for_user() {
###
# Allow a given user passwordless sudo
###
username=${1}

sudo bash -c "echo '${username} ALL=(ALL) NOPASSWD: ALL' > /etc/sudoers.d/${username} && visudo -c -f /etc/sudoers || (rm /etc/sudoers.d/${username} && false)" || sys_error "Failed to allow sudo for ${username}"
}

function set_directory_tree_ownership() {
###
# Set the ownership of a directory tree.
###
user=${1}
group=${2}
directory=${3}

sudo chown -R ${user}.${group} ${directory}
}

CLOUDIFY_SOURCES_PATH="/opt/cloudify/sources"
8 changes: 8 additions & 0 deletions types/manager-types.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -581,6 +581,14 @@ node_types:
manager.nodes.ManagementWorker:
derived_from: cloudify.nodes.ApplicationModule
properties:
agents_user:
description: User for agents.
type: string
default: { get_input: agents_user }
ssh_key_filename:
description: SSH key file for accessing agents
type: string
default: { get_input: ssh_key_filename }
management_worker_rpm_source_url:
description: Management Worker RPM Source URL
type: string
Expand Down

0 comments on commit 8a3e0e5

Please sign in to comment.