From 232992676a3a5bff9d4e3e98c42fd1b5c6d6d578 Mon Sep 17 00:00:00 2001 From: Martin Kulhavy Date: Mon, 21 Aug 2017 00:30:40 +0300 Subject: [PATCH] Add a script to create proxies to dashboards A script to create virtual hosts in Apache2 to proxy communication to the web dashboards/consoles which might be on private networks. In case of frequent access to these services, this approach is simpler than using SSH tunneling each time. Additionally, this script creates a customized homepage for the jumphost with links to the dashboards and information about the credentials. Change-Id: I2c9e17a95fa480095a4baba6bd990628bc73e053 Signed-off-by: Martin Kulhavy --- ci/setupproxy.sh | 526 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 526 insertions(+) create mode 100644 ci/setupproxy.sh diff --git a/ci/setupproxy.sh b/ci/setupproxy.sh new file mode 100644 index 00000000..61eabcd9 --- /dev/null +++ b/ci/setupproxy.sh @@ -0,0 +1,526 @@ +#!/bin/bash +############################################################################## +# Copyright (c) 2017 Nokia and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## +# A script to create virtual hosts in Apache2 to proxy communication +# to the web dashboards/consoles which might be on private networks. In case +# of frequent access to these services, this approach is simpler than using +# SSH tunneling each time. +# Additionally, this script creates a customized homepage for the jumphost +# with links to the dashboards and information about the credentials. +# +# Note that this script is meant for test deployments and might pose +# security risks for other uses (the SSL certificates are not validated, +# passwords are displayed in plaintext etc). +# +# Usage: ./setupproxy.sh [-v] openstack +# ./setupproxy.sh [-v] kubernetes +# ./setupproxy.sh --help +# Options: +# -v verbose (xtrace) +# +# Author: Martin Kulhavy +############################################################################## + +# Imports +source tools.sh + +# Halt on error +set -e + +# CONFIGURATION + +## JOID +JOID_CONFIG_DIR=../../joid_config + +## Apache config directories +A2_DIR=/etc/apache2 +A2_SSL_DIR=$A2_DIR/ssl/joid +A2_SITES_ENABLED_DIR=$A2_DIR/sites-enabled + +## Juju +JUJU_LOCAL_PORT=17070 + +## OpenStack +OS_LOCAL_PORT=17080 +OS_LOCAL_PORT_SSL=17443 + +# Kubernetes +KUBE_LOCAL_PORT=17080 + +# end of CONFIGURATION + +# Other global vars +VERBOSE=false +MAAS_WUI_PATH='/MAAS' +MAAS_CREDENTIALS=('ubuntu' 'ubuntu') +SETUP_JUJU=true +SETUP_OPENSTACK=false +SETUP_KUBERNETES=false +JUJU_GUI_PATH='/gui' +JUJU_GUI_CREDENTIALS=() +OS_DB_CREDENTIALS=() +KUBE_DB_PATH='/ui' +KUBE_DB_CREDENTIALS=() +EXTERNAL_HOST=jumphost + + +# Print out usage information and exit. +# $1 - exit code [optional, default 0] +usage() { + # no xtrace output + { set +x; } 2> /dev/null + + echo "Usage: $0 [-v] openstack" + echo " $0 [-v] kubernetes" + echo " $0 --help" + echo "Options:" + echo " -v verbose (xtrace)" + echo "" + echo "Sets up Apache proxy to the Juju and OpenStack or Kubernetes " + echo "dashboards, so that they are accessible through the jumphost, " + echo "even when on private networks." + exit ${1-0} +} + +# Parse the arguments of the script +# $@ - script arguments +parse_args() { + # Print usage help message if requested + if [ "$1" = "help" ] || [ "$1" = "-h" ] || [ "$1" = "--help" ]; then + usage + fi + + # Parse args + if [ "-v" = "$1" ]; then + VERBOSE=true + shift + set -x + fi + + if [ "openstack" = "$1" ]; then + SETUP_OPENSTACK=true + elif [ "kubernetes" = "$1" ]; then + SETUP_KUBERNETES=true + else + usage 1 + fi +} + + +# Get a value from a script exporting variables, i.e. consisting of lines +# in format `export VAR=value`. +# $1 - filename +# $2 - variable name +get_export_var_value() { + value=$(cat $1 | grep -Px "export $2=.+" | cut -d '=' -f 2) + echo "$value" +} + + +# Attempt to find the external IP address. +# Takes the source address for traffic on default route. +get_external_ip() { + # Look for the source IP when trying to request outside address + ext_ip=$(ip route get 8.8.8.8 | awk '/src/{print $7}') + if [ -n "ext_ip" ]; then + EXTERNAL_HOST=$ext_ip + fi +} + + +# Enable Apache mods needed for the proxy. +enable_mods() { + sudo a2enmod proxy + sudo a2enmod proxy_http + sudo a2enmod rewrite + sudo a2enmod deflate + sudo a2enmod headers + sudo a2enmod ssl +} + + +# Generate SSL keys and certificate to allow serving content over https. +generate_ssl_keys_cert() { + if [ ! -e $A2_SSL_DIR ]; then + sudo mkdir -p $A2_SSL_DIR + fi + sudo openssl genrsa -out $A2_SSL_DIR/ca.key 2048 + sudo openssl req -nodes -new \ + -subj "/C=OS/ST=None/L=None/O=OS/CN=localhost" \ + -key $A2_SSL_DIR/ca.key -out $A2_SSL_DIR/ca.csr + sudo openssl x509 -req -days 365 \ + -in $A2_SSL_DIR/ca.csr -signkey $A2_SSL_DIR/ca.key \ + -out $A2_SSL_DIR/ca.crt +} + + +# Remove the Apache configuration file for the default virtual host. +remove_default_site() { + def_site_conf=$A2_SITES_ENABLED_DIR/000-default.conf + if [ -e $def_site_conf ]; then + sudo rm $def_site_conf + fi +} + + +# Add a port for Apache to listen on. Only added if not yet present +# $1 - port number +add_listening_port() { + if [ -z "$1" ]; then + echo_error "No port to add specified" + exit 1 + fi + + # Add the port only if not already added + if [ $(cat $A2_DIR/ports.conf | grep -Fx "Listen $1" | wc -l) -eq 0 ]; then + echo "Listen $1" | sudo tee -a $A2_DIR/ports.conf + fi +} + + +# Setup a proxy for requests to the Juju GUI. +setup_juju_gui_proxy() { + # Get Juju GUI info + juju_gui_info=$(juju gui 2>&1) + juju_gui_url=$(echo "$juju_gui_info" | grep -Po 'https://[^\s]+') + juju_socket=$(echo "$juju_gui_url" | grep -Po 'https://\K[^/]+') + JUJU_GUI_PATH=$(echo "$juju_gui_url" | grep -Po 'https://[^/]+\K/.+') + juju_gui_username=$(echo "$juju_gui_info" | grep -Po 'username: .+' \ + | cut -d ' ' -f 2) + juju_gui_password=$(echo "$juju_gui_info" | grep -Po 'password: .+' \ + | cut -d ' ' -f 2) + JUJU_GUI_CREDENTIALS=("$juju_gui_username" "$juju_gui_password") + + # Virtual host settings + sudo tee "${A2_DIR}/sites-enabled/juju-gui.conf" > /dev/null <<-EOF + + ServerName localhost + ServerAlias * + SSLEngine On + SSLCertificateFile ${A2_SSL_DIR}/ca.crt + SSLCertificateKeyFile ${A2_SSL_DIR}/ca.key + RewriteEngine On + RewriteCond %{HTTP:Connection} Upgrade [NC] + RewriteCond %{HTTP:Upgrade} websocket [NC] + RewriteRule /(.*) wss://${juju_socket}/\$1 [P,L] + SSLProxyEngine on + SSLProxyVerify none + SSLProxyCheckPeerCN off + SSLProxyCheckPeerName off + SSLProxyCheckPeerExpire off + ProxyPass / https://${juju_socket}/ + ProxyPassReverse / https://${juju_socket}/ + +EOF + + # Add the local port to listen on + add_listening_port ${JUJU_LOCAL_PORT} +} + + +# Setup a proxy for requests to the OpenStack dashboard. +setup_openstack_dashboard_proxy() { + # Get OpenStack dashboard info + os_ip=$(juju status | awk '/openstack-dashboard\/0/ {print $5}') + if [ -z "$os_ip" ]; then + echo_error "Unable to find unit openstack-dashboard/0. Is this an OpenStack deployment?" + exit 1 + fi + + # Virtual host settings + sudo tee "${A2_DIR}/sites-enabled/openstack-dashboard.conf" > /dev/null \ + <<-EOF + + ServerName localhost + ServerAlias * + ProxyPass / http://${os_ip}/ + ProxyPassReverse / http://${os_ip}/ + + + ServerName localhost + ServerAlias * + SSLEngine On + SSLCertificateFile ${A2_SSL_DIR}/ca.crt + SSLCertificateKeyFile ${A2_SSL_DIR}/ca.key + SSLProxyEngine on + SSLProxyVerify none + SSLProxyCheckPeerCN off + SSLProxyCheckPeerName off + SSLProxyCheckPeerExpire off + ProxyPass / https://${os_ip}/ + ProxyPassReverse / https://${os_ip}/ + +EOF + + # Add the local ports to listen on + add_listening_port ${OS_LOCAL_PORT} + add_listening_port ${OS_LOCAL_PORT_SSL} + + # Collect login credentials + openrc=${JOID_CONFIG_DIR}/admin-openrc + OS_DB_CREDENTIALS[0]=$(get_export_var_value $openrc 'OS_USERNAME') + OS_DB_CREDENTIALS[1]=$(get_export_var_value $openrc 'OS_PASSWORD') + OS_DB_CREDENTIALS[2]=$(get_export_var_value $openrc 'OS_USER_DOMAIN_NAME') +} + + +# Attempt to start the Kubernetes Web UI (Dashboard) +start_kubernetes_dashboard() { + # See docs: https://kubernetes.io/docs/tasks/access-application-cluster/web-ui-dashboard/ + + machine_num=$(juju status | awk '/kubernetes-master\/0/ {print $4}') + if [ -z "$machine_num" ]; then + echo_error "Unable to find unit kubernetes-master/0. Is this a Kubernetes deployment?" + exit 1 + fi + + echo "Attempting to start Kubernetes Web UI proxy. A timeout error can be expected here." + juju run --machine="$machine_num" --timeout=5s "kubectl proxy --address='' --accept-hosts='' &" || true +} + + +# Setup a proxy for requests to the Kubernetes dashboard. +setup_kubernetes_dashboard_proxy() { + + # Get Kubernetes master ip (where dashboard is running) + kube_ip=$(juju status | awk '/kubernetes-master\/0/ {print $5}') + # Note: Maybe the port discovery can be automated. Port 8001 is default. + kube_socket="$kube_ip:8001" + + # Virtual host settings + sudo tee "${A2_DIR}/sites-enabled/kubernetes-dashboard.conf" > /dev/null \ + <<-EOF + + ServerName localhost + ServerAlias * + ProxyPass / http://${kube_socket}/ + ProxyPassReverse / http://${kube_socket}/ + +EOF + + # Add the local port to listen on + add_listening_port ${KUBE_LOCAL_PORT} +} + + +print_info_message() { + # no xtrace output + { set +x; } 2> /dev/null + + echo '' + echo_info -n "JOID deployment overview page"; + echo " is now accessible on the following url (jumphost):" + echo -n " Address: "; echo_info "http://${EXTERNAL_HOST}/" + echo '' + + if [ "$SETUP_JUJU" = true ]; then + echo_info -n "Juju GUI"; + echo " is now accessible with the following url and credentials:" + echo -n " Address: "; echo_info "https://${EXTERNAL_HOST}:${JUJU_LOCAL_PORT}${JUJU_GUI_PATH}" + echo -n " Username: "; echo_info "${JUJU_GUI_CREDENTIALS[0]}" + echo -n " Password: "; echo_info "${JUJU_GUI_CREDENTIALS[1]}" + echo '' + fi + if [ "$SETUP_OPENSTACK" = true ]; then + echo_info -n "OpenStack dashboard" + echo " is now accessible with the following url and credentials:" + echo -n " Address: "; echo_info -n "https://${EXTERNAL_HOST}:${OS_LOCAL_PORT_SSL}/"; + echo -n " or "; echo_info "http://${EXTERNAL_HOST}:${OS_LOCAL_PORT}/" + echo -n " Domain: "; echo_info "${OS_DB_CREDENTIALS[2]}" + echo -n " User Name: "; echo_info "${OS_DB_CREDENTIALS[0]}" + echo -n " Password: "; echo_info "${OS_DB_CREDENTIALS[1]}" + echo '' + fi + if [ "$SETUP_KUBERNETES" = true ]; then + echo_info -n "Kubernetes dashboard" + echo " is now accessible with the following url and credentials:" + echo -n " Address: "; echo_info "http://${EXTERNAL_HOST}:${KUBE_LOCAL_PORT}${KUBE_DB_PATH}"; + echo " No credentials needed if started on kubernetes-master/0 with command:" + echo " kubectl proxy --address='' --accept-hosts='' &" + echo '' + fi + +} + + +# Create a homepage for the jumphost with links to the dashboards +create_homepage() { + # Note: If this function is about to get any more complicated, + # it might be worth using template rendering instead. + + juju_origin="10.21.19.100:17070" + juju_url="https://10.21.19.100:17070/gui/u/admin/default" + os_origin="https://10.21.19.100:17443/" + os_url="10.21.19.100:17443" + kube_origin="https://10.21.19.100:17443/" + kube_url="10.21.19.100:17443" + + sudo tee "/var/www/html/index.html" > /dev/null < + + + + OPNFV - deployed with JOID + + + + + + +

Deployed with JOID

+EOF + + # MAAS info box + origin="${EXTERNAL_HOST}:80" + url="http://${origin}${MAAS_WUI_PATH}" + user="${MAAS_CREDENTIALS[0]}" + pass="${MAAS_CREDENTIALS[1]}" + sudo tee -a "/var/www/html/index.html" > /dev/null <MAAS dashboard + ${origin} + + +
Username:
Password:
+ +EOF + + if [ "$SETUP_JUJU" = true ]; then + origin="${EXTERNAL_HOST}:${JUJU_LOCAL_PORT}" + url="https://${origin}${JUJU_GUI_PATH}" + user="${JUJU_GUI_CREDENTIALS[0]}" + pass="${JUJU_GUI_CREDENTIALS[1]}" + sudo tee -a "/var/www/html/index.html" > /dev/null <Juju GUI + ${origin} + + +
Username:
Password:
+ +EOF + fi + + if [ "$SETUP_OPENSTACK" = true ]; then + origin="${EXTERNAL_HOST}:${OS_LOCAL_PORT_SSL}" + url="https://${origin}/" + user="${OS_DB_CREDENTIALS[0]}" + pass="${OS_DB_CREDENTIALS[1]}" + domain="${OS_DB_CREDENTIALS[2]}" + sudo tee -a "/var/www/html/index.html" > /dev/null <OpenStack dashboard + ${origin} + + + +
Domain:
User Name:
Password:
+ +EOF + fi + + if [ "$SETUP_KUBERNETES" = true ]; then + origin="${EXTERNAL_HOST}:${KUBE_LOCAL_PORT}" + url="http://${origin}${KUBE_DB_PATH}" + user="${KUBE_DB_CREDENTIALS[0]}" + pass="${KUBE_DB_CREDENTIALS[1]}" + sudo tee -a "/var/www/html/index.html" > /dev/null <Kubernetes dashboard + ${origin} +
+

No credentials needed if started with command

+
kubectl proxy --address='' --accept-hosts='' &
+
+ +EOF + fi + + sudo tee -a "/var/www/html/index.html" > /dev/null <new Clipboard('button.copy'); + + +EOF +} + + +main() { + # Do not run script as root (causes later permission issues with Juju) + if [ "$(id -u)" == "0" ]; then + echo_error "Must not be run with sudo or by root" + exit 77 + fi + + parse_args "$@" + + get_external_ip + + echo_info "Enabling Apache mods" + enable_mods + + echo_info "Generating SSL keys and certificates" + generate_ssl_keys_cert + + remove_default_site + + if [ "$SETUP_JUJU" = true ]; then + echo_info "Setting up proxy configuration for Juju GUI" + setup_juju_gui_proxy + fi + if [ "$SETUP_OPENSTACK" = true ]; then + echo_info "Setting up proxy configuration for OpenStack dashboard" + setup_openstack_dashboard_proxy + fi + if [ "$SETUP_KUBERNETES" = true ]; then + echo_info "Starting Kubernetes dashboard" + start_kubernetes_dashboard + echo_info "Setting up proxy configuration for Kubernetes dashboard" + setup_kubernetes_dashboard_proxy + fi + + echo_info "Creating the homepage for jumphost" + create_homepage + + + echo_info "Restarting HTTP server" + sudo service apache2 restart + + # Print info message + echo_info "Setup finished." + print_info_message +} + +# Start the script with the main() function +main "$@" -- 2.16.6