diff --git a/openvidu-server/deployments/external-turn/aws/CF-External-Turn.yml b/openvidu-server/deployments/external-turn/aws/CF-External-Turn.yml new file mode 100644 index 00000000..1c189bd8 --- /dev/null +++ b/openvidu-server/deployments/external-turn/aws/CF-External-Turn.yml @@ -0,0 +1,363 @@ +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: + Description: "Specifies the EC2 instance type for your OpenVidu instance" + 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 + ConstraintDescription: "Must be a valid EC2 instance type" + + KeyName: + Description: "Name of an existing EC2 KeyPair to enable SSH access to the instance. It is mandatory to perform some administrative tasks of OpenVidu." + 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 + Runtime: python3.9 + Code: + ZipFile: | + import boto3 + import cfnresponse + import json + import traceback + + def handler(event, context): + try: + response = boto3.client('ec2').describe_images(Filters=[ + {'Name': 'name', 'Values': [event['ResourceProperties']['Name']]} + ]) + 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} + Name: "*ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*" + + 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 + curl https://s3.eu-west-1.amazonaws.com/aws.openvidu.io/external-turn/4.5.2/install_openvidu_external_coturn.sh | bash + 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 \ + python3-pip \ + ec2-instance-connect + pip3 install https://s3.amazonaws.com/cloudformation-examples/aws-cfn-bootstrap-py3-latest.tar.gz + + 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: + VolumeType: gp2 + 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: + GroupDescription: SSH, Proxy and OpenVidu WebRTC Ports + 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: + Description: Use this URL to connect OpenVidu Server + Value: !Sub 'turns://${MyDomainName}:443?transport=tcp' diff --git a/openvidu-server/deployments/external-turn/.env b/openvidu-server/deployments/external-turn/docker-compose/.env similarity index 100% rename from openvidu-server/deployments/external-turn/.env rename to openvidu-server/deployments/external-turn/docker-compose/.env diff --git a/openvidu-server/deployments/external-turn/certbot.sh b/openvidu-server/deployments/external-turn/docker-compose/certbot.sh similarity index 100% rename from openvidu-server/deployments/external-turn/certbot.sh rename to openvidu-server/deployments/external-turn/docker-compose/certbot.sh diff --git a/openvidu-server/deployments/external-turn/docker-compose.yml b/openvidu-server/deployments/external-turn/docker-compose/docker-compose.yml similarity index 100% rename from openvidu-server/deployments/external-turn/docker-compose.yml rename to openvidu-server/deployments/external-turn/docker-compose/docker-compose.yml diff --git a/openvidu-server/deployments/external-turn/install_openvidu_external_coturn.sh b/openvidu-server/deployments/external-turn/docker-compose/install_openvidu_external_coturn.sh similarity index 100% rename from openvidu-server/deployments/external-turn/install_openvidu_external_coturn.sh rename to openvidu-server/deployments/external-turn/docker-compose/install_openvidu_external_coturn.sh