D Script File To Validate Network Setup
You must create a script file to validate if the existing WebLogic Server
subnet and the database subnets meet the prerequisites to provision the WebLogic instance in Oracle WebLogic Server for
OKE. You can copy the following
scripts in Cloud Shell to perform the validation. For example, copy the scripts and save the
file as validateoke.sh
.
# Script to validate existing public, private and database subnets meet the prerequisites
# for provisioning and proper functioning of Oracle WebLogic Server for OKE.
#
version="1.0.0"
# Set Flags
# -----------------------------------
# Flags which can be overridden by user input.
# Default values are below
# -----------------------------------
DB_PORT=1521
SSH_PORT=22
BASTION_SUBNET_OCID=""
ADMIN_SUBNET_OCID=""
WORKER_SUBNET_OCID=""
FSS_SUBNET_OCID=""
LB_SUBNET_OCID=""
DB_SUBNET_OCID=""
BASTION_HOST_IP_CIDR=""
debug=false
args=()
function ip_to_int() {
local ip_addr="${1}"
local ip_1 ip_2 ip_3 ip_4
ip_1=$(echo "${ip_addr}" | cut -d'.' -f1)
ip_2=$(echo "${ip_addr}" | cut -d'.' -f2)
ip_3=$(echo "${ip_addr}" | cut -d'.' -f3)
ip_4=$(echo "${ip_addr}" | cut -d'.' -f4)
echo $(( ip_1 * 256**3 + ip_2 * 256**2 + ip_3 * 256 + ip_4 ))
}
####################################################
# Determine whether IP address is in the specified subnet.
#
# Args:
# cidr_subnet: Subnet, in CIDR notation.
# ip_addr: IP address to check.
#
# Returns:
# 0|1
####################################################
function in_cidr_range() {
local cidr_subnet="${1}"
local ip_addr="${2}"
local subnet_ip cidr_mask netmask ip_addr_subnet subnet rval
subnet_ip=$(echo "${cidr_subnet}" | cut -d'/' -f1)
cidr_mask=$(echo "${cidr_subnet}" | cut -d'/' -f2)
netmask=$(( 0xFFFFFFFF << $(( 32 - ${cidr_mask} )) ))
# Apply netmask to both the subnet IP and the given IP address
ip_addr_subnet=$(( netmask & $(ip_to_int ${ip_addr}) ))
subnet=$(( netmask & $(ip_to_int ${subnet_ip}) ))
# Subnet IPs will match if given IP address is in CIDR subnet
[ "${ip_addr_subnet}" == "${subnet}" ] && rval=0 || rval=1
return $rval
}
####################################################
# Validates if one of service or nat gateways exist in the specified private subnet.
#
# Returns:
# 0|1
####################################################
function validate_service_or_nat_gw_exist() {
local subnet_ocid=$1
local vcn_ocid=""
local vcn_compartment_ocid=""
is_private_subnet=$(oci network subnet get --subnet-id "${subnet_ocid}" | jq -r '.data["prohibit-public-ip-on-vnic"]')
if [[ $is_private_subnet = true ]]
then
vcn_ocid=$(oci network subnet get --subnet-id "${subnet_ocid}" | jq -r '.data["vcn-id"]')
vcn_compartment_ocid=$(oci network vcn get --vcn-id "${vcn_ocid}" | jq -r '.data["compartment-id"]')
# Check if NAT gateway exists in the VCN
res=$(oci network nat-gateway list --compartment-id ${vcn_compartment_ocid} --vcn-id ${vcn_ocid})
nat_gw_found=$(if [[ -n $res ]]; then echo 0; else echo 1; fi)
# Check if Service gateway exists in the VCN
res=$(oci network service-gateway list --compartment-id ${vcn_compartment_ocid} --vcn-id ${vcn_ocid})
svc_gw_found=$(if [[ -n $res ]]; then echo 0; else echo 1; fi)
# One of NAT or Service Gateway must exist
if [[ $nat_gw_found -ne 0 ]] && [[ $svc_gw_found -ne 0 ]]
then
echo 1
return
fi
# Admin subnet should be using either NAT or service gateway or both in its routetable
rt_ocid=$(oci network subnet get --subnet-id ${subnet_ocid} | jq -r '.data["route-table-id"]')
rt_rules=$(oci network route-table get --rt-id ${rt_ocid} | jq -r '.data["route-rules"]')
rt_rules_count=$(echo $rt_rules | jq '.|length')
nat=""
svc=""
nat_gw_id=""
svc_gw_id=""
for ((i = 0 ; i < $rt_rules_count ; i++))
do
network_entity_ocid=$(echo $rt_rules | jq -r --arg i "$i" '.[$i|tonumber]["network-entity-id"]')
nat_id=$(echo $network_entity_ocid | grep natgateway)
if [[ -n $nat_id ]]; then nat_gw_id=$nat_id; fi
svc_id=$(echo $network_entity_ocid | grep servicegateway)
if [[ -n $svc_id ]]; then svc_gw_id=$svc_id; fi
done
if [[ (-z $nat_gw_id && -z $svc_gw_id) ]]; then
echo 2
return
fi
# If WLS subnet route table has a rule to use service gateway then it should be using
# all-<region-code>-services-in-oracle-services-network destination
echo ""
if [[ -n $svc_gw_id ]]
then
is_all_services_name=$(oci network service-gateway get --service-gateway-id $svc_gw_id | jq -r '.data.services[0]["service-name"]' | grep -i "all.*services in oracle services network")
if [[ -z $is_all_services_name ]]
then
echo 3
return
fi
for ((i = 0 ; i < $rt_rules_count ; i++))
do
network_entity_ocid=$(echo $rt_rules | jq -r --arg i "$i" '.[$i|tonumber]["network-entity-id"]')
res=$(echo $network_entity_ocid | grep servicegateway)
if [[ -n $res ]]
then
all_services_destination=$(echo $rt_rules | jq -r --arg i "$i" '.[$i|tonumber].destination' | grep -i "all-.*-services-in-oracle-services-network")
if [[ -z $all_services_destination ]]
then
echo 4
return
fi
fi
done
fi
fi
echo 0
}
####################################################
# Validates if the internet gateway exists in the VCN of Admin subnet.
# Without Internet gateway in Admin Subnet VCN, SSH access from ORM will not work.
# When using terraform CLI from within private network, internet gateway is not required.
# Hence this check will give a warning and not an error.
#
# Returns:
# 0|1
####################################################
function validate_internet_gw_exist() {
local subnet_ocid=$1
local vcn_ocid=""
local vcn_compartment_ocid=""
vcn_ocid=$(oci network subnet get --subnet-id ${subnet_ocid} | jq -r '.data["vcn-id"]')
vcn_compartment_ocid=$(oci network vcn get --vcn-id ${vcn_ocid} | jq -r '.data["compartment-id"]')
# Check if Service gateway exists in the VCN
res=$(oci network internet-gateway list --compartment-id ${vcn_compartment_ocid} --vcn-id ${vcn_ocid})
if [[ -n $res ]]; then
echo 0
else
echo 1
fi
}
####################################################
# Checks if specified port is open to specified source CIDR in the specified seclist's ingress rules.
#
# Args:
# seclist_ocid: Security list OCID for the security list to check ingress rules for.
# port: destination port to check
# source: Source CIDR (either block/range of IPs or single IP (with /32 suffix)
#
# Returns:
# 0|1
####################################################
function check_tcp_port_open_in_seclist() {
local seclist_ocid=$1
local port=$2
local source=$3
local port_is_open=false
local tcp_protocol="6"
ingress_rules=$(oci network security-list get --security-list-id $seclist_ocid | jq -r '.data["ingress-security-rules"]')
ingress_rules_count=$(echo $ingress_rules | jq '.|length')
for ((i = 0 ; i < $ingress_rules_count ; i++))
do
ingress_protocol=$(echo $ingress_rules | jq -r --arg i "$i" '.[$i|tonumber].protocol')
ingress_source=$(echo $ingress_rules | jq -r --arg i "$i" '.[$i|tonumber].source')
tcp_options=$(echo $ingress_rules | jq -r --arg i "$i" '.[$i|tonumber]["tcp-options"]')
port_min=$(echo $ingress_rules | jq -r --arg i "$i" '.[$i|tonumber]["tcp-options"]["destination-port-range"].min')
port_max=$(echo $ingress_rules | jq -r --arg i "$i" '.[$i|tonumber]["tcp-options"]["destination-port-range"].max')
source_in_cidr_range=1
if [[ $source = "0.0.0.0/0" ]]
then
if [[ $ingress_source = $source ]]
then
source_in_cidr_range=0
else
source_in_cidr_range=1
fi
else
source_in_cidr_range=$(in_cidr_range $ingress_source $source ; echo $?)
fi
if [[ ($ingress_protocol = "all" || $ingress_protocol = $tcp_protocol ) && ( $tcp_options = "null" || ( $port -ge $port_min && $port -le $port_max ) ) && $source_in_cidr_range -eq 0 ]]
then
port_is_open=true
echo 0
return
fi
done
echo 1
}
####################################################
# Validates if the specified TCP port is open for the WLS subnet CIDR.
#
# Args:
# port: Destination port
# source_cidr: Source CIDR
#
# Returns:
# 0|1
####################################################
function validate_subnet_port_access() {
local port_found_open=1
local subnet=$1
local port=$2
local source_cidr=$3
local protocol=$4 # Default protocol is TCP, if it is UDP then need to pass this param
sec_lists=$(oci network subnet get --subnet-id ${subnet} | jq -c '.data["security-list-ids"]')
# Convert to bash array
declare -A seclists_array
while IFS="=" read -r key value
do
seclists_array[$key]="$value"
done < <(jq -r 'to_entries|map("\(.key)=\(.value|tostring)")|.[]' <<< "$sec_lists")
# Check the ingress rules for specified destination port is open for access by source CIDR
for seclist_ocid in "${seclists_array[@]}"
do
if [[ $port_found_open -ne 0 ]]; then
if [[ -z $protocol ]]; then # default is TCP
port_found_open=$(check_tcp_port_open_in_seclist $seclist_ocid "${port}" "$source_cidr")
else # protocol param is non empty then udp
port_found_open=$(check_udp_port_open_in_seclist $seclist_ocid "${port}" "$source_cidr")
fi
fi
done
echo $port_found_open
}
####################################################
# Checks if specified UDP port is open to specified source CIDR in the specified seclist's ingress rules.
#
# Args:
# seclist_ocid: Security list OCID for the security list to check ingress rules for.
# port: destination port to check
# source: Source CIDR (either block/range of IPs or single IP (with /32 suffix)
#
# Returns:
# 0|1
####################################################
function check_udp_port_open_in_seclist() {
local seclist_ocid=$1
local port=$2
local source=$3
local port_is_open=false
local udp_protocol="17"
ingress_rules=$(oci network security-list get --security-list-id $seclist_ocid | jq -r '.data["ingress-security-rules"]')
ingress_rules_count=$(echo $ingress_rules | jq '.|length')
for ((i = 0 ; i < $ingress_rules_count ; i++))
do
ingress_protocol=$(echo $ingress_rules | jq -r --arg i "$i" '.[$i|tonumber].protocol')
ingress_source=$(echo $ingress_rules | jq -r --arg i "$i" '.[$i|tonumber].source')
udp_options=$(echo $ingress_rules | jq -r --arg i "$i" '.[$i|tonumber]["udp-options"]')
port_min=$(echo $ingress_rules | jq -r --arg i "$i" '.[$i|tonumber]["udp-options"]["destination-port-range"].min')
port_max=$(echo $ingress_rules | jq -r --arg i "$i" '.[$i|tonumber]["udp-options"]["destination-port-range"].max')
source_in_cidr_range=1
if [[ $source = "0.0.0.0/0" ]]
then
if [[ $ingress_source = $source ]]
then
source_in_cidr_range=0
else
source_in_cidr_range=1
fi
else
source_in_cidr_range=$(in_cidr_range $ingress_source $source ; echo $?)
fi
if [[ ($ingress_protocol = "all" || $ingress_protocol = $udp_protocol ) && ( $udp_options = "null" || ( $port -ge $port_min && $port -le $port_max ) ) && $source_in_cidr_range -eq 0 ]]
then
port_is_open=true
echo 0
return
fi
done
echo 1
}
####################################################
# Validates if CIDR is a valid single host IP (must end with /32 suffix).
#
# Args:
# ip_cidr: Single host IPv4 Address in CIDR format
#
# Returns:
# 0|1
####################################################
function is_valid_ip_cidr() {
local ip_cidr=$1
is_valid=$(echo ${ip_cidr} | grep -E '^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/(32))$')
if [[ -n $is_valid ]]; then
echo 0
else
echo 1
fi
}
############## Begin Options and Usage ###################
# Print usage
usage() {
echo -n "$0 [OPTIONS]...
This script is used to validate existing subnets for OKE - Bastion, Admin, Worker, FSS, LB subnets (and optionally database subnets) are setup correctly.
${bold}Options:${reset}
-b, --bastionsubnet Bastion Subnet OCID (Required)
-a, --adminsubnet Admin Subnet OCID (Required)
-w --workersubnet Workers Subnet OCID (Required)
-f --fsssubnet FSS Subnet OCID (Required)
-l --lbsubnet LB Subnet OCID (Required)
-d, --dbsubnet DB Subnet OCID
-i, --bastionipcidr Bastion Host IP CIDR (should be suffixed with /32)
--debug Runs script in BASH debug mode (set -x)
-h, --help Display this help and exit
--version Output version information and exit
"
}
# Iterate over options breaking -ab into -a -b when needed and --foo=bar into
# --foo bar
optstring=h
unset options
while (($#)); do
case $1 in
# If option is of type -ab
-[!-]?*)
# Loop over each character starting with the second
for ((i=1; i < ${#1}; i++)); do
c=${1:i:1}
# Add current char to options
options+=("-$c")
# If option takes a required argument, and it's not the last char make
# the rest of the string its argument
if [[ $optstring = *"$c:"* && ${1:i+1} ]]; then
options+=("${1:i+1}")
break
fi
done
;;
# If option is of type --foo=bar
--?*=*) options+=("${1%%=*}" "${1#*=}") ;;
# add --endopts for --
--) options+=(--endopts) ;;
# Otherwise, nothing special
*) options+=("$1") ;;
esac
shift
done
set -- "${options[@]}"
unset options
# Print help if no arguments were passed.
[[ $# -eq 0 ]] && set -- "--help"
# Read the options and set stuff
while [[ $1 = -?* ]]; do
case $1 in
-h|--help) usage >&2; exit 0 ;;
--version) echo "$(basename $0) ${version}"; exit 0 ;;
-b|--bastionsubnet) shift; BASTION_SUBNET_OCID=${1} ;;
-a|--adminsubnet) shift; ADMIN_SUBNET_OCID=${1} ;;
-w|--workersubnet) shift; WORKER_SUBNET_OCID=${1} ;;
-f|--fsssubnet) shift; FSS_SUBNET_OCID=${1} ;;
-l|--lbsubnet) shift; LB_SUBNET_OCID=${1} ;;
-d|--dbsubnet) shift; DB_SUBNET_OCID=${1} ;;
-i|--bastionipcidr) shift; BASTION_HOST_IP_CIDR=${1} ;;
--debug) debug=true;;
--endopts) shift; break ;;
*) "invalid option: '$1'." ; usage >&2; exit 1 ;;
esac
shift
done
# Store the remaining part as arguments.
args+=("$@")
############## End Options and Usage ###################
# ############# ############# #############
# ## MAIN SCRIPT BODY ##
# ## ##
# ## ##
# ############# ############# #############
# Set IFS to preferred implementation
IFS=$'\n\t'
# Exit on error. Append '||true' when you run the script if you expect an error.
set -o errexit
# Run in debug mode, if set
if ${debug}; then set -x ; fi
# Bash will remember & return the highest exitcode in a chain of pipes.
# This way you can catch the error in case mysqldump fails in `mysqldump |gzip`, for example.
set -o pipefail
# Validate all required params are present
if [[ -z ${BASTION_SUBNET_OCID} || -z ${ADMIN_SUBNET_OCID} || -z ${WORKER_SUBNET_OCID} || -z ${FSS_SUBNET_OCID} || -z ${LB_SUBNET_OCID} ]]
then
echo "One or more required params are not specified. Please provide either bastion and Admin subnet OCIDs"
usage >&2
exit
fi
vcn_ocid=$(oci network subnet get --subnet-id "${BASTION_SUBNET_OCID}" | jq -r '.data["vcn-id"]')
vcn_cidr=$(oci network vcn get --vcn-id "${vcn_ocid}" | jq -r '.data["cidr-block"]')
# Check if SSH port - 22 is open for access by Bastion Subnet
if [[ -n ${BASTION_SUBNET_OCID} || -n ${BASTION_HOST_IP_CIDR} ]]
then
all_ips="0.0.0.0/0"
res=$(validate_subnet_port_access "${BASTION_SUBNET_OCID}" "${SSH_PORT}" "${all_ips}")
if [[ ${res} -ne 0 ]]
then
echo "ERROR: SSH port ${SSH_PORT} is not open for access by [$all_ips] in -- ${BASTION_SUBNET_OCID}"
fi
# Check if bastion host IP is valid CIDR
bastion_cidr_block=""
if [[ -n ${BASTION_HOST_IP_CIDR} ]]
then
is_valid_cidr=$(is_valid_ip_cidr "${BASTION_HOST_IP_CIDR}")
if [[ $is_valid_cidr -ne 0 ]]
then
echo "Bastion host IP CIDR is not valid: [${BASTION_HOST_IP_CIDR}]"
usage >&2
exit
fi
bastion_cidr_block=${BASTION_HOST_IP_CIDR}
else
bastion_cidr_block=$(oci network subnet get --subnet-id "${BASTION_SUBNET_OCID}" | jq -r '.data["cidr-block"]')
fi
# Check if bastion CIDR has access to SSH port on ADMIN subnet
res=$(validate_subnet_port_access "${ADMIN_SUBNET_OCID}" "${SSH_PORT}" "${bastion_cidr_block}")
if [[ $res -ne 0 ]]
then
echo "WARNING: SSH port ${SSH_PORT} is not open for access by Bastion Subnet CIDR [$bastion_cidr_block] in private Admin Subnet [$ADMIN_SUBNET_OCID]"
fi
fi
# Check if service or NAT gateway exists in ADMIN & WORKER subnet's VCN.
if [[ -n ${ADMIN_SUBNET_OCID} && -n ${WORKER_SUBNET_OCID} ]]
then
subnet_names=('ADMIN_SUBNET' 'WORKER_SUBNET')
i=0
for subnet_ocid in ${ADMIN_SUBNET_OCID} ${WORKER_SUBNET_OCID}; do
res=$(validate_service_or_nat_gw_exist "${subnet_ocid}")
if [[ $res -eq 1 ]]
then
echo "ERROR: Missing Service or NAT gateway in the VCN of the private ${subnet_names[i]} subnet ocid [$subnet_ocid]"
elif [[ $res -eq 2 ]]
then
echo "ERROR: Private ${subnet_names[i]} subnet [$subnet_ocid] does not use NAT or Service gateway"
elif [[ $res -eq 3 ]]
then
echo "ERROR: Service Gateway in VCN of private ${subnet_names[i]} subnet [$subnet_ocid] does not allow access to all services in Oracle services network"
elif [[ $res -eq 4 ]]
then
echo "ERROR: Route Rule of private ${subnet_names[i]} subnet [$subnet_ocid] does not use 'ALL Services in Oracle services network' destination"
fi
done
fi
# Check if internet gateway exists in BASTION & LB & FSS subnet's VCN.
subnet_names=('BASTION_SUBNET' 'LB_SUBNET' 'FSS_SUBNET_OCID')
i=0
for subnet_ocid in ${BASTION_SUBNET_OCID} ${LB_SUBNET_OCID} ${FSS_SUBNET_OCID}; do
res=$(validate_internet_gw_exist "${subnet_ocid}")
if [[ $res -ne 0 ]]
then
echo "WARNING: Missing internet gateway in the VCN of the ${subnet_names[i]} subnet [$subnet_ocid]"
fi
i=$((i+1))
done
# Check if LB Subnet ports are open 0.0.0.0/0 all, 443, 80
all_ips="0.0.0.0/0"
for port in 'all' '443' '80'; do
res=$(validate_subnet_port_access "${LB_SUBNET_OCID}" "${port}" "${all_ips}")
if [[ $res -ne 0 ]]
then
echo "WARNING: Port [$port] is not open for 0.0.0.0/0 in LB Subnet CIDR [${LB_SUBNET_OCID}]"
fi
done
# Check if Worker Subnet all protocols are open for workers subnet
worker_subnet_cidr=$(oci network subnet get --subnet-id "${WORKER_SUBNET_OCID}" | jq -r '.data["cidr-block"]')
res=$(validate_subnet_port_access "${WORKER_SUBNET_OCID}" "all" ${worker_subnet_cidr})
if [[ $res -ne 0 ]]
then
echo "ERROR: All Protocols are not open for WORKER's Subnet CIDR [${worker_subnet_cidr}]"
fi
# FSS subnet verification - Checking All TCP Ports are open in FSS SUBNET OCID for VCN CIDR
for port in '111' '2048' '2049' '2050'; do
res=$(validate_subnet_port_access "${FSS_SUBNET_OCID}" "${port}" "${vcn_cidr}")
if [[ $res -ne 0 ]]
then
echo "ERROR: TCP Port [${port}] is not open in FSS Subnet for VCN CIDR"
fi
done
# FSS subnet verification - UDP - '111' '2048' in FSS SUBNET OCID for VCN CIDR"
for port in '111' '2048'; do
res=$(validate_subnet_port_access "${FSS_SUBNET_OCID}" "${port}" "${vcn_cidr}" "UDP")
if [[ $res -ne 0 ]]
then
echo "ERROR: UDP Port [${port}] is not open in FSS Subnet for VCN CIDR"
fi
done
# Check if DB port is open for access by Worker's subnet CIDR in DB subnet (only if DB subnet is provided)
if [[ -n ${DB_SUBNET_OCID} ]]
then
res=$(validate_subnet_port_access ${DB_SUBNET_OCID} ${DB_PORT} ${vcn_cidr})
res1=$(validate_subnet_port_access ${DB_SUBNET_OCID} ${DB_PORT} ${worker_subnet_cidr})
if [[ (${res} -ne 0) || (${res1} -ne 0) ]]
then
echo "ERROR: DB port ${DB_PORT} is not open for access by VCN CIDR [$vcn_cidr] or Worker Subnet CIDR [$worker_subnet_cidr] in DB Subnet [$DB_SUBNET_OCID]"
fi
fi