#!/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 "$@"