source/idea/idea-bootstrap/virtual-desktop-host-linux/configure.sh.jinja2 (395 lines of code) (raw):
#!/bin/bash
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance
# with the License. A copy of the License is located at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES
# OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions
# and limitations under the License.
set -x
SEMAPHORE_DIR="/root/bootstrap/semaphore"
CONFIG_FINISHED_LOCK="${SEMAPHORE_DIR}/configure_finished.lock"
INSTANCE_READY_LOCK="${SEMAPHORE_DIR}/instance_ready.lock"
SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
if [[ ! -f ${CONFIG_FINISHED_LOCK} ]]; then
curr_environment=$(echo -e "
## [BEGIN] RES Environment VDI Configuration - Do Not Delete
IDEA_MODULE_NAME={{ context.module_name }}
IDEA_MODULE_ID={{ context.module_id }}
IDEA_MODULE_SET={{ context.module_set }}
IDEA_MODULE_VERSION={{ context.module_version }}
IDEA_CLUSTER_HOME={{ context.cluster_home_dir }}
IDEA_APP_DEPLOY_DIR={{ context.app_deploy_dir }}
")
{% if context.https_proxy != '' %}
curr_environment+=$(echo -e "
IDEA_HTTPS_PROXY={{ context.https_proxy }}
IDEA_NO_PROXY={{ context.no_proxy }}
")
{% endif %}
curr_environment+=$(echo -e "
## [END] RES Environment VDI Configuration - Do Not Delete
## [BEGIN] RES VDI Session - Do Not Delete
IDEA_SESSION_ID="{{ context.vars.idea_session_id }}"
IDEA_SESSION_OWNER="{{ context.vars.session_owner }}"
## [END] RES VDI Session
")
curr_environment+=$(echo -e "
## [BEGIN] COGNITO VALUES - Do Not Delete
COGNITO_MIN_ID="{{ context.vars.cognito_min_id }}"
COGNITO_MAX_ID="{{ context.vars.cognito_max_id }}"
COGNITO_UID_ATTRIBUTE="{{ context.vars.cognito_uid_attribute }}"
COGNITO_DEFAULT_USER_GROUP="{{ context.vars.cognito_default_user_group }}"
USER_POOL_ID="{{ context.config.get_string('identity-provider.cognito.user_pool_id', required=True) }}"
VDI_CLIENT_ID="{{ context.config.get_string('identity-provider.cognito.vdi_client_id', required=True) }}"
## [END] COGNITO VALUES
")
# Merge Environments
/bin/bash "${SCRIPT_DIR}/../common/merge_environments.sh" -r "${curr_environment}" -o /etc/environment
source /etc/environment
if [[ -f /etc/profile.d/proxy.sh ]]; then
source /etc/profile.d/proxy.sh
fi
timestamp=$(date +%s)
exec > ${BOOTSTRAP_DIR}/logs/configure.log.${timestamp} 2>&1
echo -n "no" > ${BOOTSTRAP_DIR}/reboot_required.txt
source "${SCRIPT_DIR}/../common/bootstrap_common.sh"
# Begin: Configure AWS Systems Manager Agent
/bin/bash "${SCRIPT_DIR}/../common/configure_amazon_ssm_agent.sh" -o "${RES_BASE_OS}" -s "${SCRIPT_DIR}"
# End: Configure AWS Systems Manager Agent
# Begin: Enable executable on vdi-helper python scripts
chmod +x ${SCRIPT_DIR}/../vdi-helper/custom_credential_broker.py
chmod +x ${SCRIPT_DIR}/../vdi-helper/vdi_auto_stop.py
# End: Enable executable on vdi-helper python scripts
# Begin: Configure CloudWatch Agent
{%- set cloudwatch_agent_config = context.get_cloudwatch_agent_config(additional_log_files=additional_log_files) %}
{%- if cloudwatch_agent_config %}
echo '{{ context.utils.to_json(cloudwatch_agent_config, indent=True) }}' > /opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.json
/opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl -a fetch-config -m ec2 -s -c file:/opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.json
{%- else %}
log_warning "cloudwatch_agent_config not provided."
{%- endif %}
# End: Configure CloudWatch Agent
{% include '_templates/linux/idea_proxy.jinja2' %}
{% include '_templates/linux/idea_service_account.jinja2' %}
{% include '_templates/linux/mount_shared_storage.jinja2' %}
{%- include '_templates/linux/restrict_ssh_access_to_session_owner.jinja2' %}
{%- include '_templates/linux/disable_ssh.jinja2' %}
{%- with ebs_volume_tags = [
{'Key':'res:EnvironmentName', 'Value': context.cluster_name },
{'Key':'res:ModuleName', 'Value': context.module_name },
{'Key':'res:ModuleId', 'Value': context.module_id },
{'Key':'res:Project', 'Value': context.vars.project },
{'Key':'Name', 'Value': context.cluster_name + '/' + context.module_id + ' Root Volume' }
] %}
{% include '_templates/linux/tag_ebs_volumes.jinja2' %}
{%- endwith %}
{%- with network_interface_tags = [
{'Key':'res:EnvironmentName', 'Value': context.cluster_name },
{'Key':'res:ModuleName', 'Value': context.module_name },
{'Key':'res:ModuleId', 'Value': context.module_id },
{'Key':'Name', 'Value': context.cluster_name + '/' + context.module_id + ' Network Interface' }
] %}
{% include '_templates/linux/tag_network_interface.jinja2' %}
{%- endwith %}
{% include '_templates/linux/chronyd.jinja2' %}
{% include '_templates/linux/disable_ulimit.jinja2' %}
{% include '_templates/linux/disable_strict_host_check.jinja2' %}
{% include '_templates/linux/disable_motd_update.jinja2' %}
{%- with secure_path = '/bin:/usr/bin:/sbin:/usr/sbin:/usr/local/bin' %}
{% include '_templates/linux/sudoer_secure_path.jinja2' %}
{%- endwith %}
{%- with messages = [
context.module_name + ' (v'+context.module_version+'), Cluster: ' + context.cluster_name
] %}
{% include '_templates/linux/motd.jinja2' %}
{%- endwith %}
# TODO: enable virtual-desktop-app for ubuntu after resolve dependency issue
if [[ $RES_BASE_OS != "ubuntu2204" ]]; then
/bin/bash ${SCRIPT_DIR}/install_app.sh "{{context.vars.app_package_uri}}"
fi
{% if context.config.get_string('directoryservice.name', default='') != '' %}
{% include '_templates/linux/join_directoryservice.jinja2' %}
{% endif %}
{% if context.config.get_string('scheduler.provider') == 'openpbs' %}
{% include '_templates/linux/openpbs_client.jinja2' %}
{% endif %}
{% if context.config.get_bool('identity-provider.cognito.enable_native_user_login') %}
/bin/bash "${SCRIPT_DIR}/../common/configure_cognito_modules.sh" -o "${RES_BASE_OS}" -s "${SCRIPT_DIR}" -u "${IDEA_SESSION_OWNER}" -r "${AWS_REGION}" -i "${COGNITO_MIN_ID}" -a "${COGNITO_MAX_ID}" -d "${COGNITO_UID_ATTRIBUTE}" -p "${USER_POOL_ID}" -c "${VDI_CLIENT_ID}" -g "${COGNITO_DEFAULT_USER_GROUP}" -x "${https_proxy}"
{% endif %}
{% include '_templates/linux/set_sudoers.jinja2' %}
#Begin: Configure SSH Key Gen
/bin/bash "${SCRIPT_DIR}/../common/configure_ssh_keygen.sh" -o "${RES_BASE_OS}" -s "${SCRIPT_DIR}" -u "${IDEA_SESSION_OWNER}"
#End: Configure SSH Key Gen
BROKER_CERTIFICATE_LOCATION_LOCAL='/etc/dcv/dcv_broker/dcvsmbroker_ca.pem'
BROKER_HOSTNAME="{{ context.config.get_cluster_internal_endpoint().replace('https://', '') }}"
INTERNAL_ALB_ENDPOINT="{{ context.config.get_cluster_internal_endpoint() }}"
BROKER_AGENT_CONNECTION_PORT="{{ context.config.get_int('virtual-desktop-controller.dcv_broker.agent_communication_port', required=True) }}"
function download_broker_certificate() {
# local BROKER_CERTIFICATE_URL="{{ context.vars.broker_cert_url }}"
# if [[ "${BROKER_CERTIFICATE_URL}" == s3://* ]]; then
# local AWS=$(command -v aws)
# $AWS s3 cp "${BROKER_CERTIFICATE_URL}" "${BROKER_CERTIFICATE_LOCATION_LOCAL}"
# else
# cp "${BROKER_CERTIFICATE_URL}" "${BROKER_CERTIFICATE_LOCATION_LOCAL}"
# fi
echo "Downloading broker certificate logic goes here.. NO-OP"
}
function configure_dcv_host() {
local HOSTNAME=$(hostname -s)
local IDLE_TIMEOUT="{{ context.config.get_string('virtual-desktop-controller.dcv_session.idle_timeout', required=True) }}"
local IDLE_TIMEOUT_WARNING="{{ context.config.get_string('virtual-desktop-controller.dcv_session.idle_timeout_warning', required=True) }}"
echo "# configuring dcv host ..."
if [[ -f /etc/dcv/dcv.conf ]]; then
mv /etc/dcv/dcv.conf /etc/dcv/dcv.conf.${timestamp}
fi
local DCV_STORAGE_ROOT="/home/${IDEA_SESSION_OWNER}/storage-root"
if [[ -L ${DCV_STORAGE_ROOT} ]]; then
echo "something fishy is going on here. a sym-link should not exist. check with the session owner for bad actor or unwarranted usage of system."
exit 1
fi
if [[ ! -d ${DCV_STORAGE_ROOT} ]]; then
mkdir -p ${DCV_STORAGE_ROOT} # Create the storage root location if needed
chown ${IDEA_SESSION_OWNER} ${DCV_STORAGE_ROOT}
fi
local GL_DISPLAYS_VALUE=":0.0"
echo -e "[license]
[log]
level=debug
[session-management]
virtual-session-xdcv-args=\"-listen tcp\"
[session-management/defaults]
[session-management/automatic-console-session]
[display]
# add more if using an instance with more GPU
cuda-devices=[\"0\"]
[display/linux]
gl-displays = [\"${GL_DISPLAYS_VALUE}\"]
[display/linux]
use-glx-fallback-provider=true
[connectivity]
enable-quic-frontend=true
idle-timeout=${IDLE_TIMEOUT}
idle-timeout-warning=${IDLE_TIMEOUT_WARNING}
[security]
supervision-control=\"enforced\"
# ca-file=\"${BROKER_CERTIFICATE_LOCATION_LOCAL}\"
auth-token-verifier=\"${INTERNAL_ALB_ENDPOINT}:${BROKER_AGENT_CONNECTION_PORT}/agent/validate-authentication-token\"
no-tls-strict=true
os-auto-lock=false
administrators=[\"dcvsmagent\"]
[windows]
disable-display-sleep=true
" > /etc/dcv/dcv.conf
}
function configure_dcv_agent() {
if [[ -f "/etc/dcv-session-manager-agent/agent.conf" ]]; then
mv /etc/dcv-session-manager-agent/agent.conf /etc/dcv-session-manager-agent/agent.conf.${timestamp}
fi
echo -e "version = '0.1'
[agent]
# hostname or IP of the broker. This parameter is mandatory.
broker_host = '${BROKER_HOSTNAME}'
# The port of the broker. Default: 8445
broker_port = ${BROKER_AGENT_CONNECTION_PORT}
# CA used to validate the certificate of the broker.
# ca_file = '${BROKER_CERTIFICATE_LOCATION_LOCAL}'
tls_strict = false
# Folder on the file system from which the tag files are read.
tags_folder = '/etc/dcv-session-manager-agent/tags/'
broker_update_interval = 15
[log]
level = 'debug'
rotation = 'daily'
" > /etc/dcv-session-manager-agent/agent.conf
mkdir -p /etc/dcv-session-manager-agent/tags/
if [[ -f "/etc/dcv-session-manager-agent/tags/idea_tags.toml" ]]; then
mkdir -p /etc/dcv-session-manager-agent/archive-tags/
mv /etc/dcv-session-manager-agent/tags/idea_tags.toml /etc/dcv-session-manager-agent/archive-tags/idea_tags.toml.${timestamp}
fi
echo -e "idea_session_id=\"${IDEA_SESSION_ID}\"" > /etc/dcv-session-manager-agent/tags/idea_tags.toml
}
function configure_usb_remotization() {
echo "Searching for USB Remotization configurations..."
{%- for usb_info in context.config.get_list('vdc.server.usb_remotization', default=[]) %}
echo -en "{{ usb_info }}\n" >>/etc/dcv/usb-devices.conf
{%- endfor %}
}
function configure_x_server() {
# Configure the X server to start automatically when the Linux server boots
sudo systemctl set-default graphical.target
}
function start_x_server() {
local output=$(x_server_validation_command)
if [[ ! "$output" == "SI:localuser:dcv" ]]; then
echo "# start x server ..."
sudo systemctl isolate graphical.target
echo "# wait for x server to start ..."
fi
}
function restart_x_server() {
echo "# restart x server ..."
local NVIDIAXCONFIG=$(which nvidia-xconfig)
sudo $NVIDIAXCONFIG --preserve-busid --enable-all-gpus
sudo systemctl isolate multi-user.target
sudo systemctl isolate graphical.target
echo "# wait for x server to start ..."
}
function start_and_validate_x_server() {
start_x_server
verify_x_server_is_up
}
function start_and_configure_dcv_service() {
echo "# start dcv server ..."
sudo systemctl enable dcvserver
echo """
# This file is part of systemd.
# AGENT
# systemd is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation; either version 2.1 of the License, or
# (at your option) any later version.
[Unit]
Description=NICE DCV server daemon
DefaultDependencies=no
Conflicts=umount.target
After=network-online.target remote-fs.target
Before=umount.target
[Service]
PermissionsStartOnly=true
ExecStartPre=-/sbin/modprobe eveusb
ExecStart=/usr/bin/dcvserver -d --service
Restart=always
BusName=com.nicesoftware.DcvServer
User=dcv
Group=dcv
[Install]
WantedBy=multi-user.target
""" > /usr/lib/systemd/system/dcvserver.service
# Because we have modified the .service file we need to tell systemctl to reload and recreate the dependency tree again.
# This is necessary because we are introducing a dependency on network.targets.
# Refer - https://serverfault.com/questions/700862/do-systemd-unit-files-have-to-be-reloaded-when-modified
sudo systemctl daemon-reload
sudo systemctl restart dcvserver
}
function start_and_configure_dcv_agent_service() {
echo "# start dcv session manager agent ..."
sudo systemctl enable dcv-session-manager-agent
echo """
# This file is part of systemd.
# AGENT
# systemd is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation; either version 2.1 of the License, or
# (at your option) any later version.
[Unit]
Description=Agent component of DCV Session Manager
DefaultDependencies=no
Conflicts=umount.target
After=network-online.target remote-fs.target
Before=umount.target
[Service]
Type=simple
ExecStart=/usr/libexec/dcv-session-manager-agent/dcvsessionmanageragent
User=dcvsmagent
Group=dcvsmagent
[Install]
WantedBy=multi-user.target
""" > /usr/lib/systemd/system/dcv-session-manager-agent.service
# Because we have modified the .service file we need to tell systemctl to reload and recreate the dependency tree again.
# This is necessary because we are introducing a dependency on network.targets.
# Refer - https://serverfault.com/questions/700862/do-systemd-unit-files-have-to-be-reloaded-when-modified
sudo systemctl daemon-reload
sudo systemctl restart dcv-session-manager-agent
}
function x_server_validation_command() {
echo $(DISPLAY=:0 XAUTHORITY=$(ps aux | grep "X.*\-auth" | grep -v grep | sed -n 's/.*-auth \([^ ]\+\).*/\1/p') xhost | grep "SI:localuser:dcv$")
}
function verify_x_server_is_up() {
max_timeout_in_seconds=120
start_time=$(date +%s)
echo "# validating if x server is running ..."
sleep 10
local output=$(x_server_validation_command)
local count=0
while [[ ! "$output" == "SI:localuser:dcv" ]]
do
echo -ne "Waiting for X Server to come up.. sleeping for 10 more seconds; ${count} seconds already slept \033[0K\r"
count=$(($count+10))
sleep 10
output=$(x_server_validation_command)
if [[ $(expr $count % 50) == 0 ]]
then
echo "Waited 5 times in a row. Was unsuccessful. trying to restart x server again..."
restart_x_server
fi
current_time=$(date +%s)
elapsed=$((current_time - start_time))
if [[ $elapsed -ge $max_timeout_in_seconds ]]
then
echo "Max timeout for verify server reached after $elapsed seconds"
break
fi
done
current_time=$(date +%s)
elapsed=$((current_time - start_time))
if [[ $elapsed -lt $max_timeout_in_seconds ]]
then
echo "x server is up and running...."
fi
}
## -- DCV RELATED EXECUTION BEGINS HERE -- ##
/bin/bash "${SCRIPT_DIR}/../nice-dcv-linux/disable_wayland_protocol.sh" -o "${RES_BASE_OS}" -s "${SCRIPT_DIR}"
download_broker_certificate
configure_dcv_host
configure_dcv_agent
configure_usb_remotization
{% if context.is_gpu_instance_type() -%}
/bin/bash "${SCRIPT_DIR}/../nice-dcv-linux/configure_gl.sh" -o "${RES_BASE_OS}" -s "${SCRIPT_DIR}"
{% else %}
echo "OS is "${RES_BASE_OS}", BUT not a GL Machine. No need for this configuration. NO-OP..."
{% endif %}
machine=$(uname -m) #x86_64 or aarch64
if [[ $machine == "x86_64" ]]; then
configure_x_server
start_and_validate_x_server
fi
start_and_configure_dcv_service
start_and_configure_dcv_agent_service
## -- DCV RELATED EXECUTION ENDS HERE -- ##
# run user customizations if available
if [[ -f ${IDEA_CLUSTER_HOME}/dcv_host/userdata_customizations.sh ]]; then
/bin/bash ${IDEA_CLUSTER_HOME}/dcv_host/userdata_customizations.sh >> ${BOOTSTRAP_DIR}/logs/userdata_customizations.log 2>&1
fi
## run custom user configure scripts
source /etc/launch_script_environment
/bin/bash ${SCRIPT_DIR}/${ON_VDI_CONFIGURED_COMMANDS}
# notify controller
CONTROLLER_EVENTS_QUEUE_URL="{{ context.config.get_string('virtual-desktop-controller.events_sqs_queue_url', required=True) }}"
MESSAGE="{{ context.vars.dcv_host_ready_message }}"
AWS=$(command -v aws)
$AWS sqs send-message --queue-url ${CONTROLLER_EVENTS_QUEUE_URL} --message-body ${MESSAGE} --region ${AWS_REGION} --message-group-id ${IDEA_SESSION_ID}
mkdir -p ${SEMAPHORE_DIR}
echo $(date +%s) > ${CONFIG_FINISHED_LOCK}
REBOOT_REQUIRED=$(cat /root/bootstrap/reboot_required.txt)
if [[ "${REBOOT_REQUIRED}" == "yes" ]]; then
log_info "Triggering 3rd Reboot Required for RES Configuration for VDI"
echo -n "no" > /root/bootstrap/reboot_required.txt
reboot
# Exit to avoid running the rest of script before reboot is completed.
exit 0
fi
else
# Rerun script if RERUN_ON_REBOOT is true when reboot
source /etc/launch_script_environment
if [[ "${RERUN_ON_REBOOT}" == "True" && -f ${INSTANCE_READY_LOCK} ]]; then
echo "Rerunning ON_VDI_CONFIGURED_COMMANDS"
/bin/bash ${SCRIPT_DIR}/${ON_VDI_CONFIGURED_COMMANDS}
else
echo "Skipping running ON_VDI_CONFIGURED_COMMANDS"
fi
fi
/bin/bash ${SCRIPT_DIR}/configure_post_reboot.sh