Skip to content

Instantly share code, notes, and snippets.

@mikesparr
Last active November 5, 2022 05:31
Show Gist options
  • Save mikesparr/2939a97fdfa24ff5fa4cad9400b067e0 to your computer and use it in GitHub Desktop.
Save mikesparr/2939a97fdfa24ff5fa4cad9400b067e0 to your computer and use it in GitHub Desktop.
Freeswitch example on Google Cloud Platform
#!/usr/bin/env bash
export PROJECT_ID=$(gcloud config get-value project)
export PROJECT_USER=$(gcloud config get-value core/account) # set current user
export PROJECT_NUMBER=$(gcloud projects describe $PROJECT_ID --format="value(projectNumber)")
export IDNS=${PROJECT_ID}.svc.id.goog # workflow identity domain
export GCP_REGION="us-central1" # CHANGEME (OPT)
export GCP_ZONE="us-central1-a" # CHANGEME (OPT)
export NETWORK_NAME="default"
# enable apis
gcloud services enable compute.googleapis.com \
iap.googleapis.com \
storage.googleapis.com
gcloud config set compute/region $GCP_REGION
gcloud config set compute/zone $GCP_ZONE
###########################################
# IP ADDRESSES
###########################################
export NAT_GW_IP="nat-gw-ip"
export LOAD_BALANCER_IP="load-balancer-ip"
gcloud compute addresses create $NAT_GW_IP --region $GCP_REGION
gcloud compute addresses create $LOAD_BALANCER_IP --region $GCP_REGION
###########################################
# NAT GATEWAY
###########################################
export CLOUD_ROUTER_NAME="router-1"
export CLOUD_ROUTER_ASN="64523"
export NAT_GW_NAME="nat-gateway-1"
# create cloud router and nat gateway
gcloud compute routers create $CLOUD_ROUTER_NAME \
--network $NETWORK_NAME \
--asn $CLOUD_ROUTER_ASN \
--region $GCP_REGION
gcloud compute routers nats create $NAT_GW_NAME \
--router=$CLOUD_ROUTER_NAME \
--region=$GCP_REGION \
--auto-allocate-nat-external-ips \
--nat-all-subnet-ip-ranges \
--enable-logging
# change to static IP (test)
gcloud compute routers nats update $NAT_GW_NAME \
--router=$CLOUD_ROUTER_NAME \
--nat-external-ip-pool=$NAT_GW_IP
###########################################
# TEMPLATE + MIG + STARTUP SCRIPT
#
# debug on VM:
# - re-run startup: sudo google_metadata_script_runner startup
# - debug: sudo journalctl -u google-startup-scripts.service
###########################################
export TEMPLATE_NAME="freeswitch"
export GROUP_NAME="freeswitch"
export NW_TAG="freeswitch"
export BUCKET_NAME="mike-test-freeswitch-bucket"
export SCRIPT_NAME="startup.sh"
export COMPUTE_SA="${PROJECT_NUMBER}-compute@developer.gserviceaccount.com"
# enable apis
gcloud services enable compute.googleapis.com \
storage.googleapis.com \
iap.googleapis.com
# create storage bucket
gsutil mb gs://$BUCKET_NAME
# add IAM permissions to bucket
gsutil iam ch serviceAccount:${COMPUTE_SA}:roles/storage.objectViewer gs://$BUCKET_NAME
# create startup script
# https://freeswitch.org/confluence/display/FREESWITCH/Debian+10+Buster
#
cat > startup.sh << EOF
#!/usr/bin/env bash
echo "[startup] Configuring FreeSWITCH server ..."
echo
echo "[startup] Updating packages ..."
apt-get update && apt-get install -y gnupg2 wget
wget -O - https://files.freeswitch.org/repo/deb/debian-release/fsstretch-archive-keyring.asc | apt-key add -
echo "deb http://files.freeswitch.org/repo/deb/debian-release/ buster main" > /etc/apt/sources.list.d/freeswitch.list
echo "deb-src http://files.freeswitch.org/repo/deb/debian-release/ buster main" >> /etc/apt/sources.list.d/freeswitch.list
echo "[startup] Checking sources file ..."
cat /etc/apt/sources.list.d/freeswitch.list
# if /etc/freeswitch does not exist, the standard vanilla configuration is deployed
echo "[startup] Installing FreeSWITCH ..."
apt-get update && apt-get install -y freeswitch-meta-all
# wait for config files to exist
echo "[startup] Waiting for config files ..."
while read i; do if [ "" = /etc/freeswitch/vars.xml ]; then break; fi; done < <(inotifywait -e create,open --format '%f' --quiet /tmp --monitor)
# TODO: edit config files to use correct IP addresses
# <X-PRE-PROCESS cmd="exec-set" data="external_rtp_ip=curl -X GET -H 'Metadata-Flavor: Google' 'http://metadata.google.internal/computeMetadata/v1/instance/network-interfaces/0/access-configs/0/external-ip'"/>
echo "[startup] Editing config files ..."
echo "[startup] Done!"
exit 0
EOF
# push script to bucket
gsutil cp ./$SCRIPT_NAME "gs://${BUCKET_NAME}/${SCRIPT_NAME}"
# create instance template w/ freeswitch install setup script
gcloud beta compute --project=$PROJECT_ID instance-templates create $TEMPLATE_NAME \
--machine-type=e2-medium \
--network=projects/mike-test-freeswitch/global/networks/default \
--network-tier=PREMIUM \
--metadata="startup-script-url=gs://${BUCKET_NAME}/${SCRIPT_NAME}" \
--can-ip-forward \
--no-address \
--service-account=733678248239-compute@developer.gserviceaccount.com \
--scopes=https://www.googleapis.com/auth/devstorage.read_only,https://www.googleapis.com/auth/logging.write,https://www.googleapis.com/auth/monitoring.write,https://www.googleapis.com/auth/servicecontrol,https://www.googleapis.com/auth/service.management.readonly,https://www.googleapis.com/auth/trace.append \
--tags=$NW_TAG \
--image=debian-10-buster-v20210420 \
--image-project=debian-cloud \
--boot-disk-size=10GB \
--boot-disk-type=pd-balanced \
--boot-disk-device-name=$GROUP_NAME \
--shielded-secure-boot \
--no-shielded-vtpm \
--no-shielded-integrity-monitoring \
--labels=role=pbx \
--reservation-affinity=any
# create instance group
gcloud compute --project=$PROJECT_ID instance-groups managed create $GROUP_NAME \
--base-instance-name=$GROUP_NAME \
--template=$GROUP_NAME \
--size=2 \
--zone=$GCP_ZONE
# configure autoscaler (can increase to 2 min for testing)
gcloud beta compute --project $PROJECT_ID instance-groups managed set-autoscaling $GROUP_NAME \
--zone $GCP_ZONE \
--cool-down-period "60" \
--max-num-replicas "5" \
--min-num-replicas "1" \
--target-cpu-utilization "0.85" \
--mode "on"
#################################################
# LOAD BALANCING
#################################################
# create health check
gcloud compute health-checks create tcp tcp-health-check \
--region $GCP_REGION \
--port 5060
# create firewall rules for health checks
gcloud compute firewall-rules create fw-allow-network-lb-health-checks \
--action=ALLOW \
--direction=INGRESS \
--source-ranges=35.191.0.0/16,209.85.152.0/22,209.85.204.0/22 \
--rules=tcp
# create firewall rules for FreeSwitch
export FW_PORTS="UDP:1719,TCP:1720,TCP:2855-2856,UDP:3478-3479,TCP:5002-5003,UDP:5060,TCP:5060,UDP:5070,TCP:5070,UDP:5080,TCP:5080,UDP:16384-32768,TCP:5066,TCP:7443,TCP:8081-8082"
gcloud compute firewall-rules create freeswitch-policy \
--allow $FW_PORTS \
--source-ranges=0.0.0.0/0 \
--target-tags=$NW_TAG
# create backend service
gcloud compute backend-services create network-lb-backend-service \
--protocol TCP \
--health-checks tcp-health-check \
--health-checks-region $GCP_REGION \
--region $GCP_REGION
# add instance group to backend
gcloud compute backend-services add-backend network-lb-backend-service \
--instance-group $GROUP_NAME \
--instance-group-zone $GCP_ZONE \
--region $GCP_REGION
# add forwarding rule
gcloud compute forwarding-rules create network-lb-forwarding-rule \
--load-balancing-scheme external \
--region $GCP_REGION \
--ports 1-65535 \
--address $LOAD_BALANCER_IP \
--backend-service network-lb-backend-service
#############################################
# IAP ACCESS
#############################################
# grant SSH access
gcloud compute firewall-rules create allow-ssh-ingress-from-iap \
--direction=INGRESS \
--action=allow \
--rules=tcp:22 \
--source-ranges=35.235.240.0/20
# grant user tunneling (one for each user or group [preferred])
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member=user:$PROJECT_USER \
--role=roles/iap.tunnelResourceAccessor
# TODO: fetch name of compute instance
# connect with IAP tunnel SSH into private VM
gcloud compute ssh freeswitch-private \
--zone $GCP_ZONE \
--tunnel-through-iap
# ...
@mikesparr
Copy link
Author

If I edit the instance template and deploy an instance group with external IPs, 2-way SIP + RTP (audio) works.

If the template has --no-address flag and internal IP only, using NAT, then SIP works but RTP does not (no audio).

@lifewins
Copy link

Hi Mike,
Have you tried to use TLS connections to FreeSWITCH  using Google's SSL LB?

@billybobilly
Copy link

Any luck with this? I've been struggling with it for the past week.

@mikesparr
Copy link
Author

mikesparr commented Apr 8, 2022

I haven't revisited the original experiment and I believe some used Kamailio or another proxy to solve the NAT issue. If someone was more of a FreeSwitch guru, they might know the levers in configs to address NAT issue. Unfortunately I didn't solve that part.

No I did not try HTTPS LB which supports ports 80/443, nor the SSL Proxy which I don't believe supports needed ports, only the L4 (TCP) load balancer.

@mikesparr
Copy link
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment