2023-03-28 23:41:39 +02:00
AWSTemplateFormatVersion : 2010-09-09
Description : External TURN server for OpenVidu Server.
Parameters :
PublicElasticIP :
Description : "Previously created AWS Elastic IP to associate it to the EC2 instance. Example 13.33.145.23."
Type : String
AllowedPattern : ^([01]?\d{1,2}|2[0-4]\d|25[0-5])\.([01]?\d{1,2}|2[0-4]\d|25[0-5])\.([01]?\d{1,2}|2[0-4]\d|25[0-5])\.([01]?\d{1,2}|2[0-4]\d|25[0-5])$
ConstraintDescription : The public Elastic IP does not have a valid IPv4 format and it is mandatory
MyDomainName :
Description : "Valid domain name pointing to previous IP. Example: openvidu.company.com"
Type : String
AllowedPattern : ^$|^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z0-9][a-z0-9-]{0,61}[a-z0-9]$
ConstraintDescription : The domain name does not have a valid domain name format
LetsEncryptEmail :
Description : "This email will be used for Let's Encrypt notifications"
AllowedPattern : ^(.+)@(.+)$
Type : String
TurnStaticAuthSecret :
Description : "Shared secret for the TURN server to generate credentials for clients"
Type : String
NoEcho : true
AllowedPattern : .+$
ConstraintDescription : Turn secret is mandatory.
InstanceType :
2023-03-28 23:44:18 +02:00
Description : "Specifies the EC2 instance type for your TURN instance"
2023-03-28 23:41:39 +02:00
Type : String
Default : c5.xlarge
AllowedValues :
- t2.large
- t2.xlarge
- t2.2xlarge
- t3.large
- t3.xlarge
- t3.2xlarge
- m4.large
- m4.xlarge
- m4.2xlarge
- m4.4xlarge
- m4.10xlarge
- m4.16xlarge
- m5.large
- m5.xlarge
- m5.2xlarge
- m5.4xlarge
- m5.8xlarge
- m5.12xlarge
- m5.16xlarge
- m5.24xlarge
- c4.large
- c4.xlarge
- c4.2xlarge
- c4.4xlarge
- c4.8xlarge
- c5.large
- c5.xlarge
- c5.2xlarge
- c5.4xlarge
- c5.9xlarge
- c5.12xlarge
- c5.18xlarge
- c5.24xlarge
2023-10-03 10:45:53 +02:00
- c6a.large
- c6a.xlarge
- c6a.2xlarge
- c6a.4xlarge
- c6a.8xlarge
- c6a.12xlarge
- c6a.16xlarge
- c6a.24xlarge
- c6a.32xlarge
- c6a.48xlarge
- c6a.metal
2023-03-28 23:41:39 +02:00
ConstraintDescription : "Must be a valid EC2 instance type"
KeyName :
2023-03-28 23:44:18 +02:00
Description : "Name of an existing EC2 KeyPair to enable SSH access to the instance. It is mandatory to perform some administrative tasks to the TURN server."
2023-03-28 23:41:39 +02:00
Type : 'AWS::EC2::KeyPair::KeyName'
ConstraintDescription : "must be the name of an existing EC2 KeyPair"
Metadata :
'AWS::CloudFormation::Interface' :
ParameterGroups :
- Label :
default : Domain and SSL certificate configuration
Parameters :
- PublicElasticIP
- MyDomainName
- LetsEncryptEmail
- Label :
default : Turn server configuration
Parameters :
- TurnStaticAuthSecret
- Label :
default : EC2 Instance configuration
Parameters :
- InstanceType
- KeyName
ParameterLabels :
# SSL certificate configuration
PublicElasticIP :
default : "AWS Elastic IP (EIP)"
MyDomainName :
default : "Domain Name pointing to Elastic IP"
LetsEncryptEmail :
default : "Email for Let's Encrypt (letsencrypt)"
# OpenVidu configuration
TurnStaticAuthSecret :
default : >
Turn shared secret. This is used to generate credentials and should be in OpenVidu Server configuration.
# EC2 Instance configuration
InstanceType :
default : "Instance type"
KeyName :
default : "SSH Key"
# Other configuration
Resources :
DescribeImagesRole :
Type : AWS::IAM::Role
Properties :
AssumeRolePolicyDocument :
Version : '2012-10-17'
Statement :
- Action : sts:AssumeRole
Effect : Allow
Principal :
Service : lambda.amazonaws.com
ManagedPolicyArns :
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
Policies :
- PolicyName : DescribeImages
PolicyDocument :
Version : '2012-10-17'
Statement :
- Action : ec2:DescribeImages
Effect : Allow
Resource : "*"
GetLatestUbuntuAmi :
Type : AWS::Lambda::Function
Properties :
Handler : index.handler
Role : !Sub ${DescribeImagesRole.Arn}
Timeout : 60
2023-09-08 18:06:42 +02:00
Runtime : python3.11
2023-03-28 23:41:39 +02:00
Code :
ZipFile : |
import boto3
import cfnresponse
import json
import traceback
def handler(event, context) :
try :
2025-07-03 18:19:00 +02:00
response = boto3.client('ec2').describe_images(IncludeDeprecated=True, Filters=[
2023-04-06 17:26:11 +02:00
{'Name': 'name', 'Values' : [ event['ResourceProperties']['Name']]},
{'Name': 'owner-alias', 'Values' : [ 'amazon' ] }
2023-03-28 23:41:39 +02:00
] )
amis = sorted(response['Images'], key=lambda x : x['CreationDate'], reverse=True)
id = amis[0]['ImageId']
cfnresponse.send(event, context, cfnresponse.SUCCESS, {}, id)
except :
traceback.print_last()
cfnresponse.send(event, context, cfnresponse.FAIL, {}, "ok")
UbuntuAmi :
Type : Custom::FindAMI
Properties :
ServiceToken : !Sub ${GetLatestUbuntuAmi.Arn}
2025-01-08 12:34:28 +01:00
Name : "*ubuntu/images/hvm-ssd/ubuntu-noble-24.04-amd64-server-*"
2023-03-28 23:41:39 +02:00
TurnServerInstance :
Type : 'AWS::EC2::Instance'
Metadata :
Comment : 'Install External TURN server for OpenVidu Server'
AWS::CloudFormation::Init :
config :
files :
'/usr/local/bin/install_docker.sh' :
content : |
#!/bin/bash
# Check if docker is already installed
if ! command -v docker &> /dev/null; then
# Install docker
apt-get -y update
apt-get -y install \
ca-certificates \
curl \
gnupg
mkdir -m 0755 -p /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg
echo \
"deb [arch=" $(dpkg --print-architecture)" signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
"$(. /etc/os-release && echo " $VERSION_CODENAME")" stable" | \
tee /etc/apt/sources.list.d/docker.list > /dev/null
apt-get -y update
apt-get -y install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
fi
mode : "000755"
owner : "root"
group : "root"
'/usr/local/bin/coturn.sh' :
content : !Sub |
#!/bin/bash -x
WORKIND_DIR=/opt/coturn
cd /opt
# Check if directory /opt/coturn exists
# If it does not exist, it means it is the first time we run this script
# and we need to install coturn and fill the .env file
if [ ! -d "$WORKIND_DIR" ]; then
# This means it is the first time we run this script
2023-06-27 15:14:30 +02:00
curl https://s3.eu-west-1.amazonaws.com/aws.openvidu.io/external-turn/4.6.2/install_openvidu_external_coturn.sh | bash
2023-03-28 23:41:39 +02:00
cd "$WORKIND_DIR"
# Replace environment variables
sed -i "s|TURN_DOMAIN_NAME=.*|TURN_DOMAIN_NAME=${MyDomainName}|" .env
sed -i "s|LETSENCRYPT_EMAIL=.*|LETSENCRYPT_EMAIL=${LetsEncryptEmail}|" .env
sed -i "s|TURN_STATIC_AUTH_SECRET=.*|TURN_STATIC_AUTH_SECRET=${TurnStaticAuthSecret}|" .env
fi
cd "$WORKIND_DIR"
docker compose down
docker compose up -d
mode : "000755"
owner : "root"
group : "root"
'/usr/local/bin/wait_for_coturn.sh' :
content : !Sub |
#!/bin/bash -x
# Configuration
SERVER="${MyDomainName}"
PORT="443"
TIMEOUT=600 # 10 minutes in seconds
INTERVAL=10 # Time interval between attempts in seconds
TARGET_EXIT_CODE=0
start_time=$(date +%s)
while true; do
current_time=$(date +%s)
elapsed_time=$((current_time - start_time))
if [ $elapsed_time -ge $TIMEOUT ]; then
echo "Time limit reached: $TIMEOUT seconds"
exit 1
fi
# Run the command and get the exit code
docker exec coturn turnutils_stunclient -p $PORT $SERVER
exit_code=$?
if [ $exit_code -eq $TARGET_EXIT_CODE ]; then
echo "Coturn is ready"
exit 0
else
echo "Coturn is not ready yet. Waiting $INTERVAL seconds before the next attempt..."
sleep $INTERVAL
fi
done
mode : "000755"
owner : "root"
group : "root"
Properties :
ImageId : !Ref UbuntuAmi
InstanceType : !Ref InstanceType
SecurityGroups :
- !Ref WebServerSecurityGroup
KeyName : !Ref KeyName
Tags :
- Key : Name
Value : !Ref 'AWS::StackName'
UserData :
Fn::Base64 : !Sub |
#!/bin/bash -x
set -eu -o pipefail
apt-get update && apt-get install -y \
2025-01-11 03:44:59 +01:00
python3-setuptools \
python3-pip \
ec2-instance-connect
pip3 install https://s3.amazonaws.com/cloudformation-examples/aws-cfn-bootstrap-py3-latest.tar.gz --break-system-packages
2023-03-28 23:41:39 +02:00
cfn-init --region ${AWS::Region} --stack ${AWS::StackId} --resource TurnServerInstance
/usr/local/bin/install_docker.sh
export HOME="/root"
# Check if crontab has already been configured
if ! crontab -l | grep -q "coturn.sh"; then
# Configure crontab
echo "@reboot /usr/local/bin/coturn.sh" | crontab
fi
# Run coturn
/usr/local/bin/coturn.sh
# Wait for coturn to be ready
/usr/local/bin/wait_for_coturn.sh
# sending the finish call
/usr/local/bin/cfn-signal -e $? --stack ${AWS::StackId} --resource WaitCondition --region ${AWS::Region}
BlockDeviceMappings :
- DeviceName : /dev/sda1
Ebs :
2025-01-08 12:34:28 +01:00
VolumeType : gp3
2023-03-28 23:41:39 +02:00
DeleteOnTermination : true
VolumeSize : 25
MyEIP :
Type : 'AWS::EC2::EIPAssociation'
Properties :
InstanceId : !Ref TurnServerInstance
EIP : !Ref PublicElasticIP
WaitCondition :
Type : 'AWS::CloudFormation::WaitCondition'
CreationPolicy :
ResourceSignal :
Timeout : PT30M
Count : '1'
WebServerSecurityGroup :
Type : 'AWS::EC2::SecurityGroup'
Properties :
2023-03-28 23:44:18 +02:00
GroupDescription : SSH, Proxy and Turn necessaty ports
2023-03-28 23:41:39 +02:00
SecurityGroupIngress :
- IpProtocol : tcp
FromPort : 22
ToPort : 22
CidrIp : 0.0 .0 .0 /0
- IpProtocol : tcp
FromPort : 22
ToPort : 22
CidrIpv6 : : : /0
- IpProtocol : tcp
FromPort : 80
ToPort : 80
CidrIp : 0.0 .0 .0 /0
- IpProtocol : tcp
FromPort : 80
ToPort : 80
CidrIpv6 : : : /0
- IpProtocol : tcp
FromPort : 443
ToPort : 443
CidrIp : 0.0 .0 .0 /0
- IpProtocol : tcp
FromPort : 443
ToPort : 443
CidrIpv6 : : : /0
- IpProtocol : udp
FromPort : 443
ToPort : 443
CidrIp : 0.0 .0 .0 /0
- IpProtocol : udp
FromPort : 443
ToPort : 443
CidrIpv6 : : : /0
Outputs :
TurnServerURI :
2023-03-28 23:44:18 +02:00
Description : Use this URL to connect the TURN Server
2023-03-28 23:41:39 +02:00
Value : !Sub 'turns://${MyDomainName}:443?transport=tcp'