AWSTemplateFormatVersion: 2010-09-09 Description: OpenVidu Community - Single Node Parameters: CertificateType: Description: | [selfsigned] Not recommended for production use. If you don't have a FQDN, (DomainName parameter) you can use this option to generate a self-signed certificate. [owncert] Valid for productions environments. If you have a FQDN, (DomainName parameter) and an Elastic IP, you can use this option to use your own certificate. [letsencrypt] Valid for production environments. If you have a FQDN, (DomainName parameter) and an Elastic IP, you can use this option to generate a Let's Encrypt certificate. Type: String AllowedValues: - selfsigned - owncert - letsencrypt Default: selfsigned PublicElasticIP: Type: String Description: Previously created Elastic IP for the OpenVidu Deployment. 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 DomainName: Type: String Description: Domain name for the OpenVidu Deployment. 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 OwnPublicCertificate: Description: "If certificate type is 'owncert', this parameter will be used to specify the public certificate" Type: String OwnPrivateCertificate: Description: "If certificate type is 'owncert', this parameter will be used to specify the private certificate" Type: String LetsEncryptEmail: Description: "If certificate type is 'letsencrypt', this email will be used for Let's Encrypt notifications" Type: String AdditionalInstallFlags: Description: Additional optional flags to pass to the OpenVidu installer (comma-separated, e.g., "--flag1=value, --flag2"). Type: String Default: "" AllowedPattern: '^[A-Za-z0-9, =_.\-]*$' # Allows letters, numbers, comma, space, underscore, dot, equals, and hyphen ConstraintDescription: Must be a comma-separated list of flags (for example, --flag=value, --bool-flag). TurnDomainName: Description: '(Optional) Domain name for the TURN server with TLS. Only needed if your users are behind restrictive firewalls' Type: String Default: '' TurnOwnPublicCertificate: Description: "(Optional) This setting is applicable if the certificate type is set to 'owncert' and the TurnDomainName is specified." Type: String Default: '' TurnOwnPrivateCertificate: Description: "(Optional) This setting is applicable if the certificate type is set to 'owncert' and the TurnDomainName is specified." Type: String Default: '' # EC2 Instance configuration InstanceType: Description: "Specifies the EC2 instance type for your OpenVidu instance" Type: String Default: c6a.xlarge AllowedValues: - t2.large - t2.xlarge - t2.2xlarge - t3.medium - 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 - m6i.large - m6i.xlarge - m6i.2xlarge - m6i.4xlarge - m6i.8xlarge - m6i.12xlarge - m6i.16xlarge - m6i.24xlarge - m6i.32xlarge - m6i.metal - 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 - c6a.large - c6a.xlarge - c6a.2xlarge - c6a.4xlarge - c6a.8xlarge - c6a.12xlarge - c6a.16xlarge - c6a.24xlarge - c6a.32xlarge - c6a.48xlarge - c6a.metal - c6i.large - c6i.xlarge - c6i.2xlarge - c6i.4xlarge - c6i.8xlarge - c6i.12xlarge - c6i.16xlarge - c6i.24xlarge - c6i.32xlarge - c6i.metal - c7a.medium - c7a.large - c7a.xlarge - c7a.2xlarge - c7a.4xlarge - c7a.8xlarge - c7a.12xlarge - c7a.16xlarge - c7a.24xlarge - c7a.32xlarge - c7a.48xlarge - c7a.metal-48xl - c7i.large - c7i.xlarge - c7i.2xlarge - c7i.4xlarge - c7i.8xlarge - c7i.12xlarge - c7i.16xlarge - c7i.24xlarge - c7i.48xlarge - c7i.metal-24xl - c7i.metal-48xl - c5n.large - c5n.xlarge - c5n.2xlarge - c5n.4xlarge - c5n.9xlarge - c5n.18xlarge - m5n.large - m5n.xlarge - m5n.2xlarge - m5n.4xlarge - m5n.8xlarge - m5n.12xlarge - m5n.16xlarge - m5n.24xlarge - m6in.large - m6in.xlarge - m6in.2xlarge - m6in.4xlarge - m6in.8xlarge - m6in.12xlarge - m6in.16xlarge - m6in.24xlarge - m6in.32xlarge - r5n.large - r5n.xlarge - r5n.2xlarge - r5n.4xlarge - r5n.8xlarge - r5n.12xlarge - r5n.16xlarge - r5n.24xlarge ConstraintDescription: "Must be a valid EC2 instance type" KeyName: Type: AWS::EC2::KeyPair::KeyName Description: Name of an existing EC2 KeyPair to enable SSH access to the Deployment. AllowedPattern: ^.+$ ConstraintDescription: must be the name of an existing EC2 KeyPair. AmiId: Type: AWS::SSM::Parameter::Value Default: /aws/service/canonical/ubuntu/server/jammy/stable/current/amd64/hvm/ebs-gp2/ami-id Description: AMI ID for the EC2 instances S3AppDataBucketName: Type: String Description: Name of the S3 bucket to store data and recordings. If empty, a bucket will be created Metadata: 'AWS::CloudFormation::Interface': ParameterGroups: - Label: default: Domain and SSL certificate configuration Parameters: - CertificateType - PublicElasticIP - DomainName - OwnPublicCertificate - OwnPrivateCertificate - LetsEncryptEmail - Label: default: EC2 Instance configuration Parameters: - InstanceType - KeyName - AmiId - Label: default: S3 bucket for application data and recordings Parameters: - S3AppDataBucketName - Label: default: "(Optional) Additional Installer Flags" Parameters: - AdditionalInstallFlags - Label: default: (Optional) TURN server configuration with TLS Parameters: - TurnDomainName - TurnOwnPublicCertificate - TurnOwnPrivateCertificate Conditions: PublicElasticIPPresent: !Not [ !Equals [!Ref PublicElasticIP, ""] ] CreateRecordingsBucket: !Equals [!Ref S3AppDataBucketName, ""] Resources: OpenViduSharedInfo: Type: AWS::SecretsManager::Secret UpdateReplacePolicy: Retain DeletionPolicy: Delete Properties: Name: !Sub openvidu-${AWS::Region}-${AWS::StackName} Description: Secret for OpenVidu to store deployment info and seed secrets SecretString: | { "DOMAIN_NAME": "none", "LIVEKIT_TURN_DOMAIN_NAME": "none", "LETSENCRYPT_EMAIL": "none", "REDIS_PASSWORD": "none", "MONGO_ADMIN_USERNAME": "none", "MONGO_ADMIN_PASSWORD": "none", "MONGO_REPLICA_SET_KEY": "none", "MINIO_URL": "none", "MINIO_ACCESS_KEY": "none", "MINIO_SECRET_KEY": "none", "DASHBOARD_URL": "none", "DASHBOARD_ADMIN_USERNAME": "none", "DASHBOARD_ADMIN_PASSWORD": "none", "GRAFANA_URL": "none", "GRAFANA_ADMIN_USERNAME": "none", "GRAFANA_ADMIN_PASSWORD": "none", "LIVEKIT_API_KEY": "none", "LIVEKIT_API_SECRET": "none", "MEET_ADMIN_USER": "none", "MEET_ADMIN_SECRET": "none", "MEET_API_KEY": "none", "ENABLED_MODULES": "none" } S3AppDataBucketResource: Type: 'AWS::S3::Bucket' Properties: ### Unique bucket name using Stack ID BucketName: !Join ["-" , [ 'openvidu-appdata', !Select [0, !Split ["-", !Select [2, !Split [/, !Ref AWS::StackId ]]]]]] AccessControl: Private PublicAccessBlockConfiguration: BlockPublicAcls: true BlockPublicPolicy: true IgnorePublicAcls : true RestrictPublicBuckets: true DeletionPolicy: Retain UpdateReplacePolicy: Retain Condition: CreateRecordingsBucket OpenViduServerRole: Type: 'AWS::IAM::Role' Properties: AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Principal: Service: - ec2.amazonaws.com Action: - 'sts:AssumeRole' Path: / Policies: - PolicyName: !Sub openvidu-policy-${AWS::Region}-${AWS::StackName} PolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Action: - secretsmanager:GetSecretValue - secretsmanager:UpdateSecret Resource: !Ref OpenViduSharedInfo - Fn::If: - CreateRecordingsBucket - Effect: Allow Action: - s3:DeleteObject - s3:GetObject - s3:PutObject Resource: !Sub ${S3AppDataBucketResource.Arn}/* - Effect: Allow Action: - s3:DeleteObject - s3:GetObject - s3:PutObject Resource: !Sub arn:${AWS::Partition}:s3:::${S3AppDataBucketName}/* - Fn::If: - CreateRecordingsBucket - Effect: Allow Action: - s3:ListBucket - s3:GetBucketLocation Resource: !GetAtt S3AppDataBucketResource.Arn - Effect: Allow Action: - s3:ListBucket - s3:GetBucketLocation Resource: !Sub arn:${AWS::Partition}:s3:::${S3AppDataBucketName} RoleName: Fn::Join: # Generate a not too long and unique role name # Getting a unique identifier from the stack id - '' - - openvidu-role- - !Select [4, !Split ['-', !Select [2, !Split ['/', !Ref AWS::StackId]]]] OpenViduServerInstanceProfile: Type: 'AWS::IAM::InstanceProfile' Properties: Roles: - !Ref OpenViduServerRole InstanceProfileName: !Sub openvidu-instance-profile-${AWS::Region}-${AWS::StackName} OpenviduServer: Type: 'AWS::EC2::Instance' Metadata: Comment: 'Install and configure OpenVidu Community - Single Node' AWS::CloudFormation::Init: config: files: '/usr/local/bin/install.sh': content: !Sub | #!/bin/bash -x OPENVIDU_VERSION=main DOMAIN= YQ_VERSION=v4.44.5 apt-get update && apt-get install -y \ curl \ unzip \ jq \ wget wget https://github.com/mikefarah/yq/releases/download/${!YQ_VERSION}/yq_linux_amd64.tar.gz -O - |\ tar xz && mv yq_linux_amd64 /usr/bin/yq # Install aws-cli curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip" unzip -qq awscliv2.zip ./aws/install rm -rf awscliv2.zip aws # Token for IMDSv2 TOKEN=$(curl -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600") # Configure Domain if [[ "${DomainName}" == '' ]]; then [ ! -d "/usr/share/openvidu" ] && mkdir -p /usr/share/openvidu PublicHostname=$(curl -s -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/public-hostname) DOMAIN=$PublicHostname echo $PublicHostname > /usr/share/openvidu/old-host-name else DOMAIN=${DomainName} fi DOMAIN="$(/usr/local/bin/store_secret.sh save DOMAIN_NAME "$DOMAIN")" # Store usernames and generate random passwords REDIS_PASSWORD="$(/usr/local/bin/store_secret.sh generate REDIS_PASSWORD)" MONGO_ADMIN_USERNAME="$(/usr/local/bin/store_secret.sh save MONGO_ADMIN_USERNAME "mongoadmin")" MONGO_ADMIN_PASSWORD="$(/usr/local/bin/store_secret.sh generate MONGO_ADMIN_PASSWORD)" MONGO_REPLICA_SET_KEY="$(/usr/local/bin/store_secret.sh generate MONGO_REPLICA_SET_KEY)" MINIO_ACCESS_KEY="$(/usr/local/bin/store_secret.sh save MINIO_ACCESS_KEY "minioadmin")" MINIO_SECRET_KEY="$(/usr/local/bin/store_secret.sh generate MINIO_SECRET_KEY)" DASHBOARD_ADMIN_USERNAME="$(/usr/local/bin/store_secret.sh save DASHBOARD_ADMIN_USERNAME "dashboardadmin")" DASHBOARD_ADMIN_PASSWORD="$(/usr/local/bin/store_secret.sh generate DASHBOARD_ADMIN_PASSWORD)" GRAFANA_ADMIN_USERNAME="$(/usr/local/bin/store_secret.sh save GRAFANA_ADMIN_USERNAME "grafanaadmin")" GRAFANA_ADMIN_PASSWORD="$(/usr/local/bin/store_secret.sh generate GRAFANA_ADMIN_PASSWORD)" MEET_ADMIN_USER="$(/usr/local/bin/store_secret.sh save MEET_ADMIN_USER "meetadmin")" MEET_ADMIN_SECRET="$(/usr/local/bin/store_secret.sh generate MEET_ADMIN_SECRET)" MEET_API_KEY="$(/usr/local/bin/store_secret.sh generate MEET_API_KEY)" ENABLED_MODULES="$(/usr/local/bin/store_secret.sh save ENABLED_MODULES "observability,openviduMeet")" LIVEKIT_API_KEY="$(/usr/local/bin/store_secret.sh generate LIVEKIT_API_KEY "API" 12)" LIVEKIT_API_SECRET="$(/usr/local/bin/store_secret.sh generate LIVEKIT_API_SECRET)" # Base command INSTALL_COMMAND="sh <(curl -fsSL http://get.openvidu.io/community/singlenode/$OPENVIDU_VERSION/install.sh)" # Common arguments COMMON_ARGS=( "--no-tty" "--install" "--environment=aws" "--deployment-type=single_node" "--domain-name=$DOMAIN" "--enabled-modules='$ENABLED_MODULES'" "--redis-password=$REDIS_PASSWORD" "--mongo-admin-user=$MONGO_ADMIN_USERNAME" "--mongo-admin-password=$MONGO_ADMIN_PASSWORD" "--mongo-replica-set-key=$MONGO_REPLICA_SET_KEY" "--minio-access-key=$MINIO_ACCESS_KEY" "--minio-secret-key=$MINIO_SECRET_KEY" "--dashboard-admin-user=$DASHBOARD_ADMIN_USERNAME" "--dashboard-admin-password=$DASHBOARD_ADMIN_PASSWORD" "--grafana-admin-user=$GRAFANA_ADMIN_USERNAME" "--grafana-admin-password=$GRAFANA_ADMIN_PASSWORD" "--meet-admin-user=$MEET_ADMIN_USER" "--meet-admin-password=$MEET_ADMIN_SECRET" "--meet-api-key=$MEET_API_KEY" "--livekit-api-key=$LIVEKIT_API_KEY" "--livekit-api-secret=$LIVEKIT_API_SECRET" ) # Include additional installer flags provided by the user if [[ "${AdditionalInstallFlags}" != "" ]]; then IFS=',' read -ra EXTRA_FLAGS <<< "${AdditionalInstallFlags}" for extra_flag in "${!EXTRA_FLAGS[@]}"; do # Trim whitespace around each flag extra_flag="$(echo -e "${!extra_flag}" | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')" if [[ "$extra_flag" != "" ]]; then COMMON_ARGS+=("$extra_flag") fi done fi # Turn with TLS if [[ "${TurnDomainName}" != '' ]]; then LIVEKIT_TURN_DOMAIN_NAME=$(/usr/local/bin/store_secret.sh save LIVEKIT_TURN_DOMAIN_NAME "${TurnDomainName}") COMMON_ARGS+=( "--turn-domain-name=$LIVEKIT_TURN_DOMAIN_NAME" ) fi # Certificate arguments if [[ "${CertificateType}" == "selfsigned" ]]; then CERT_ARGS=( "--certificate-type=selfsigned" ) elif [[ "${CertificateType}" == "letsencrypt" ]]; then LETSENCRYPT_EMAIL=$(/usr/local/bin/store_secret.sh save LETSENCRYPT_EMAIL "${LetsEncryptEmail}") CERT_ARGS=( "--certificate-type=letsencrypt" "--letsencrypt-email=$LETSENCRYPT_EMAIL" ) else # Download owncert files mkdir -p /tmp/owncert wget -O /tmp/owncert/fullchain.pem ${OwnPublicCertificate} wget -O /tmp/owncert/privkey.pem ${OwnPrivateCertificate} # Convert to base64 OWN_CERT_CRT=$(base64 -w 0 /tmp/owncert/fullchain.pem) OWN_CERT_KEY=$(base64 -w 0 /tmp/owncert/privkey.pem) CERT_ARGS=( "--certificate-type=owncert" "--owncert-public-key=$OWN_CERT_CRT" "--owncert-private-key=$OWN_CERT_KEY" ) # Turn with TLS and own certificate if [[ "${TurnDomainName}" != '' ]]; then # Download owncert files mkdir -p /tmp/owncert-turn wget -O /tmp/owncert-turn/fullchain.pem ${TurnOwnPublicCertificate} wget -O /tmp/owncert-turn/privkey.pem ${TurnOwnPrivateCertificate} # Convert to base64 OWN_CERT_CRT_TURN=$(base64 -w 0 /tmp/owncert-turn/fullchain.pem) OWN_CERT_KEY_TURN=$(base64 -w 0 /tmp/owncert-turn/privkey.pem) CERT_ARGS+=( "--turn-owncert-private-key=$OWN_CERT_KEY_TURN" "--turn-owncert-public-key=$OWN_CERT_CRT_TURN" ) fi fi # Construct the final command with all arguments FINAL_COMMAND="$INSTALL_COMMAND $(printf "%s " "${!COMMON_ARGS[@]}") $(printf "%s " "${!CERT_ARGS[@]}")" # Install OpenVidu exec bash -c "$FINAL_COMMAND" mode: "000755" owner: "root" group: "root" '/usr/local/bin/config_s3.sh': content: !Sub - | #!/bin/bash set -e # Install dir and config dir INSTALL_DIR="/opt/openvidu" CONFIG_DIR="${!INSTALL_DIR}/config" # Config S3 bucket EXTERNAL_S3_ENDPOINT="https://s3.${AWS::Region}.amazonaws.com" EXTERNAL_S3_REGION="${AWS::Region}" EXTERNAL_S3_PATH_STYLE_ACCESS="false" EXTERNAL_S3_BUCKET_APP_DATA=${S3RecordingsBucketResourceName} sed -i "s|EXTERNAL_S3_ENDPOINT=.*|EXTERNAL_S3_ENDPOINT=$EXTERNAL_S3_ENDPOINT|" "${!CONFIG_DIR}/openvidu.env" sed -i "s|EXTERNAL_S3_REGION=.*|EXTERNAL_S3_REGION=$EXTERNAL_S3_REGION|" "${!CONFIG_DIR}/openvidu.env" sed -i "s|EXTERNAL_S3_PATH_STYLE_ACCESS=.*|EXTERNAL_S3_PATH_STYLE_ACCESS=$EXTERNAL_S3_PATH_STYLE_ACCESS|" "${!CONFIG_DIR}/openvidu.env" sed -i "s|EXTERNAL_S3_BUCKET_APP_DATA=.*|EXTERNAL_S3_BUCKET_APP_DATA=$EXTERNAL_S3_BUCKET_APP_DATA|" "${!CONFIG_DIR}/openvidu.env" - S3RecordingsBucketResourceName: !If - CreateRecordingsBucket - !Ref S3AppDataBucketResource - !Ref S3AppDataBucketName mode: "000755" owner: "root" group: "root" '/usr/local/bin/after_install.sh': content: !Sub | #!/bin/bash set -e # Get current shared secret SHARED_SECRET=$(aws secretsmanager get-secret-value \ --region ${AWS::Region} \ --secret-id openvidu-${AWS::Region}-${AWS::StackName} \ --query SecretString --output text) # Token for IMDSv2 TOKEN=$(curl -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600") if [[ "${DomainName}" == '' ]]; then PublicHostname=$(curl -s -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/public-hostname) DOMAIN=$PublicHostname else DOMAIN=${DomainName} fi # Generate URLs DASHBOARD_URL="https://${!DOMAIN}/dashboard/" GRAFANA_URL="https://${!DOMAIN}/grafana/" MINIO_URL="https://${!DOMAIN}/minio-console/" # Update shared secret SHARED_SECRET="$(echo "$SHARED_SECRET" | jq '. + {"DOMAIN_NAME": "'"$DOMAIN"'"}')" SHARED_SECRET="$(echo "$SHARED_SECRET" | jq '. + {"DASHBOARD_URL": "'"$DASHBOARD_URL"'" }')" SHARED_SECRET="$(echo "$SHARED_SECRET" | jq '. + {"GRAFANA_URL": "'"$GRAFANA_URL"'" }')" SHARED_SECRET="$(echo "$SHARED_SECRET" | jq '. + {"MINIO_URL": "'"$MINIO_URL"'" }')" # Update shared secret aws secretsmanager update-secret \ --region ${AWS::Region} \ --secret-id openvidu-${AWS::Region}-${AWS::StackName} \ --secret-string "$SHARED_SECRET" mode: "000755" owner: "root" group: "root" '/usr/local/bin/update_config_from_secret.sh': content: !Sub | #!/bin/bash set -e # Token for IMDSv2 TOKEN=$(curl -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600") # Get current shared secret SHARED_SECRET=$(aws secretsmanager get-secret-value \ --region ${AWS::Region} \ --secret-id openvidu-${AWS::Region}-${AWS::StackName} \ --query SecretString --output text) # Installation directory INSTALL_DIR="/opt/openvidu" CONFIG_DIR="${!INSTALL_DIR}/config" # Replace DOMAIN_NAME export DOMAIN=$(echo $SHARED_SECRET | jq -r .DOMAIN_NAME) if [[ $DOMAIN == *"compute.amazonaws.com"* ]] || [[ -z $DOMAIN ]]; then PublicHostname=$(curl -s -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/public-hostname) DOMAIN=$PublicHostname fi if [[ -n "$DOMAIN" ]]; then sed -i "s/DOMAIN_NAME=.*/DOMAIN_NAME=$DOMAIN/" "${!CONFIG_DIR}/openvidu.env" else exit 1 fi # Replace LIVEKIT_TURN_DOMAIN_NAME export LIVEKIT_TURN_DOMAIN_NAME=$(echo $SHARED_SECRET | jq -r .LIVEKIT_TURN_DOMAIN_NAME) if [[ -n "$LIVEKIT_TURN_DOMAIN_NAME" ]]; then sed -i "s/LIVEKIT_TURN_DOMAIN_NAME=.*/LIVEKIT_TURN_DOMAIN_NAME=$LIVEKIT_TURN_DOMAIN_NAME/" "${!CONFIG_DIR}/openvidu.env" fi if [[ ${CertificateType} == "letsencrypt" ]]; then export LETSENCRYPT_EMAIL=$(echo $SHARED_SECRET | jq -r .LETSENCRYPT_EMAIL) sed -i "s/LETSENCRYPT_EMAIL=.*/LETSENCRYPT_EMAIL=$LETSENCRYPT_EMAIL/" "${!CONFIG_DIR}/openvidu.env" fi # Replace rest of the values sed -i "s/REDIS_PASSWORD=.*/REDIS_PASSWORD=$(echo $SHARED_SECRET | jq -r .REDIS_PASSWORD)/" "${!CONFIG_DIR}/openvidu.env" sed -i "s/MONGO_ADMIN_USERNAME=.*/MONGO_ADMIN_USERNAME=$(echo $SHARED_SECRET | jq -r .MONGO_ADMIN_USERNAME)/" "${!CONFIG_DIR}/openvidu.env" sed -i "s/MONGO_ADMIN_PASSWORD=.*/MONGO_ADMIN_PASSWORD=$(echo $SHARED_SECRET | jq -r .MONGO_ADMIN_PASSWORD)/" "${!CONFIG_DIR}/openvidu.env" sed -i "s/MONGO_REPLICA_SET_KEY=.*/MONGO_REPLICA_SET_KEY=$(echo $SHARED_SECRET | jq -r .MONGO_REPLICA_SET_KEY)/" "${!CONFIG_DIR}/openvidu.env" sed -i "s/DASHBOARD_ADMIN_USERNAME=.*/DASHBOARD_ADMIN_USERNAME=$(echo $SHARED_SECRET | jq -r .DASHBOARD_ADMIN_USERNAME)/" "${!CONFIG_DIR}/openvidu.env" sed -i "s/DASHBOARD_ADMIN_PASSWORD=.*/DASHBOARD_ADMIN_PASSWORD=$(echo $SHARED_SECRET | jq -r .DASHBOARD_ADMIN_PASSWORD)/" "${!CONFIG_DIR}/openvidu.env" sed -i "s/MINIO_ACCESS_KEY=.*/MINIO_ACCESS_KEY=$(echo $SHARED_SECRET | jq -r .MINIO_ACCESS_KEY)/" "${!CONFIG_DIR}/openvidu.env" sed -i "s/MINIO_SECRET_KEY=.*/MINIO_SECRET_KEY=$(echo $SHARED_SECRET | jq -r .MINIO_SECRET_KEY)/" "${!CONFIG_DIR}/openvidu.env" sed -i "s/GRAFANA_ADMIN_USERNAME=.*/GRAFANA_ADMIN_USERNAME=$(echo $SHARED_SECRET | jq -r .GRAFANA_ADMIN_USERNAME)/" "${!CONFIG_DIR}/openvidu.env" sed -i "s/GRAFANA_ADMIN_PASSWORD=.*/GRAFANA_ADMIN_PASSWORD=$(echo $SHARED_SECRET | jq -r .GRAFANA_ADMIN_PASSWORD)/" "${!CONFIG_DIR}/openvidu.env" sed -i "s/LIVEKIT_API_KEY=.*/LIVEKIT_API_KEY=$(echo $SHARED_SECRET | jq -r .LIVEKIT_API_KEY)/" "${!CONFIG_DIR}/openvidu.env" sed -i "s/LIVEKIT_API_SECRET=.*/LIVEKIT_API_SECRET=$(echo $SHARED_SECRET | jq -r .LIVEKIT_API_SECRET)/" "${!CONFIG_DIR}/openvidu.env" sed -i "s/MEET_ADMIN_USER=.*/MEET_ADMIN_USER=$(echo $SHARED_SECRET | jq -r .MEET_ADMIN_USER)/" "${!CONFIG_DIR}/meet.env" sed -i "s/MEET_ADMIN_SECRET=.*/MEET_ADMIN_SECRET=$(echo $SHARED_SECRET | jq -r .MEET_ADMIN_SECRET)/" "${!CONFIG_DIR}/meet.env" sed -i "s/MEET_API_KEY=.*/MEET_API_KEY=$(echo $SHARED_SECRET | jq -r .MEET_API_KEY)/" "${!CONFIG_DIR}/meet.env" sed -i "s/ENABLED_MODULES=.*/ENABLED_MODULES=$(echo $SHARED_SECRET | jq -r .ENABLED_MODULES)/" "${!CONFIG_DIR}/openvidu.env" # Update URLs in secret DASHBOARD_URL="https://${!DOMAIN}/dashboard/" GRAFANA_URL="https://${!DOMAIN}/grafana/" MINIO_URL="https://${!DOMAIN}/minio-console/" SHARED_SECRET="$(echo "$SHARED_SECRET" | jq '. + {"DOMAIN_NAME": "'"$DOMAIN"'" }')" SHARED_SECRET="$(echo "$SHARED_SECRET" | jq '. + {"DASHBOARD_URL": "'"$DASHBOARD_URL"'" }')" SHARED_SECRET="$(echo "$SHARED_SECRET" | jq '. + {"GRAFANA_URL": "'"$GRAFANA_URL"'" }')" SHARED_SECRET="$(echo "$SHARED_SECRET" | jq '. + {"MINIO_URL": "'"$MINIO_URL"'" }')" aws secretsmanager update-secret \ --region ${AWS::Region} \ --secret-id openvidu-${AWS::Region}-${AWS::StackName} \ --secret-string "$SHARED_SECRET" mode: "000755" owner: "root" group: "root" '/usr/local/bin/update_secret_from_config.sh': content: !Sub | #!/bin/bash set -e # Get current shared secret SHARED_SECRET=$(aws secretsmanager get-secret-value \ --region ${AWS::Region} \ --secret-id openvidu-${AWS::Region}-${AWS::StackName} \ --query SecretString --output text) # Installation directory INSTALL_DIR="/opt/openvidu" CONFIG_DIR="${!INSTALL_DIR}/config" if [[ ${CertificateType} == "letsencrypt" ]]; then SHARED_SECRET="$(echo "$SHARED_SECRET" | jq '. + {"LETSENCRYPT_EMAIL": "'"$(/usr/local/bin/get_value_from_config.sh LETSENCRYPT_EMAIL "${!CONFIG_DIR}/openvidu.env")"'"}')" fi # Update shared secret SHARED_SECRET="$(echo "$SHARED_SECRET" | jq '. + {"REDIS_PASSWORD": "'"$(/usr/local/bin/get_value_from_config.sh REDIS_PASSWORD "${!CONFIG_DIR}/openvidu.env")"'"}')" SHARED_SECRET="$(echo "$SHARED_SECRET" | jq '. + {"DOMAIN_NAME": "'"$(/usr/local/bin/get_value_from_config.sh DOMAIN_NAME "${!CONFIG_DIR}/openvidu.env")"'"}')" SHARED_SECRET="$(echo "$SHARED_SECRET" | jq '. + {"LIVEKIT_TURN_DOMAIN_NAME": "'"$(/usr/local/bin/get_value_from_config.sh LIVEKIT_TURN_DOMAIN_NAME "${!CONFIG_DIR}/openvidu.env")"'"}')" SHARED_SECRET="$(echo "$SHARED_SECRET" | jq '. + {"MONGO_ADMIN_USERNAME": "'"$(/usr/local/bin/get_value_from_config.sh MONGO_ADMIN_USERNAME "${!CONFIG_DIR}/openvidu.env")"'"}')" SHARED_SECRET="$(echo "$SHARED_SECRET" | jq '. + {"MONGO_ADMIN_PASSWORD": "'"$(/usr/local/bin/get_value_from_config.sh MONGO_ADMIN_PASSWORD "${!CONFIG_DIR}/openvidu.env")"'"}')" SHARED_SECRET="$(echo "$SHARED_SECRET" | jq '. + {"MONGO_REPLICA_SET_KEY": "'"$(/usr/local/bin/get_value_from_config.sh MONGO_REPLICA_SET_KEY "${!CONFIG_DIR}/openvidu.env")"'"}')" SHARED_SECRET="$(echo "$SHARED_SECRET" | jq '. + {"MINIO_ACCESS_KEY": "'"$(/usr/local/bin/get_value_from_config.sh MINIO_ACCESS_KEY "${!CONFIG_DIR}/openvidu.env")"'"}')" SHARED_SECRET="$(echo "$SHARED_SECRET" | jq '. + {"MINIO_SECRET_KEY": "'"$(/usr/local/bin/get_value_from_config.sh MINIO_SECRET_KEY "${!CONFIG_DIR}/openvidu.env")"'"}')" SHARED_SECRET="$(echo "$SHARED_SECRET" | jq '. + {"DASHBOARD_ADMIN_USERNAME": "'"$(/usr/local/bin/get_value_from_config.sh DASHBOARD_ADMIN_USERNAME "${!CONFIG_DIR}/openvidu.env")"'"}')" SHARED_SECRET="$(echo "$SHARED_SECRET" | jq '. + {"DASHBOARD_ADMIN_PASSWORD": "'"$(/usr/local/bin/get_value_from_config.sh DASHBOARD_ADMIN_PASSWORD "${!CONFIG_DIR}/openvidu.env")"'"}')" SHARED_SECRET="$(echo "$SHARED_SECRET" | jq '. + {"GRAFANA_ADMIN_USERNAME": "'"$(/usr/local/bin/get_value_from_config.sh GRAFANA_ADMIN_USERNAME "${!CONFIG_DIR}/openvidu.env")"'"}')" SHARED_SECRET="$(echo "$SHARED_SECRET" | jq '. + {"GRAFANA_ADMIN_PASSWORD": "'"$(/usr/local/bin/get_value_from_config.sh GRAFANA_ADMIN_PASSWORD "${!CONFIG_DIR}/openvidu.env")"'"}')" SHARED_SECRET="$(echo "$SHARED_SECRET" | jq '. + {"LIVEKIT_API_KEY": "'"$(/usr/local/bin/get_value_from_config.sh LIVEKIT_API_KEY "${!CONFIG_DIR}/openvidu.env")"'"}')" SHARED_SECRET="$(echo "$SHARED_SECRET" | jq '. + {"LIVEKIT_API_SECRET": "'"$(/usr/local/bin/get_value_from_config.sh LIVEKIT_API_SECRET "${!CONFIG_DIR}/openvidu.env")"'"}')" SHARED_SECRET="$(echo "$SHARED_SECRET" | jq '. + {"MEET_ADMIN_USER": "'"$(/usr/local/bin/get_value_from_config.sh MEET_ADMIN_USER "${!CONFIG_DIR}/meet.env")"'"}')" SHARED_SECRET="$(echo "$SHARED_SECRET" | jq '. + {"MEET_ADMIN_SECRET": "'"$(/usr/local/bin/get_value_from_config.sh MEET_ADMIN_SECRET "${!CONFIG_DIR}/meet.env")"'"}')" SHARED_SECRET="$(echo "$SHARED_SECRET" | jq '. + {"MEET_API_KEY": "'"$(/usr/local/bin/get_value_from_config.sh MEET_API_KEY "${!CONFIG_DIR}/meet.env")"'"}')" SHARED_SECRET="$(echo "$SHARED_SECRET" | jq '. + {"ENABLED_MODULES": "'"$(/usr/local/bin/get_value_from_config.sh ENABLED_MODULES "${!CONFIG_DIR}/openvidu.env")"'"}')" # Update shared secret aws secretsmanager update-secret \ --region ${AWS::Region} \ --secret-id openvidu-${AWS::Region}-${AWS::StackName} \ --secret-string "$SHARED_SECRET" mode: "000755" owner: "root" group: "root" '/usr/local/bin/get_value_from_config.sh': content: | #!/bin/bash set -e # Function to get the value of a given key from the environment file get_value() { local key="$1" local file_path="$2" # Use grep to find the line with the key, ignoring lines starting with # # Use awk to split on '=' and print the second field, which is the value local value=$(grep -E "^\s*$key\s*=" "$file_path" | awk -F= '{print $2}' | sed 's/#.*//; s/^\s*//; s/\s*$//') # If the value is empty, return "none" if [ -z "$value" ]; then echo "none" else echo "$value" fi } # Check if the correct number of arguments are supplied if [ "$#" -ne 2 ]; then echo "Usage: $0 " exit 1 fi # Get the key and file path from the arguments key="$1" file_path="$2" # Get and print the value get_value "$key" "$file_path" mode: "000755" owner: "root" group: "root" '/usr/local/bin/store_secret.sh': content: !Sub | #!/bin/bash set -e # Modes: save, generate # save mode: save the secret in the secret manager # generate mode: generate a random password and save it in the secret manager MODE="$1" SHARED_SECRET="$(aws secretsmanager get-secret-value \ --region ${AWS::Region} \ --secret-id ${OpenViduSharedInfo} \ --query SecretString --output text)" if [[ "$MODE" == "generate" ]]; then SECRET_KEY_NAME="$2" PREFIX="${!3:-}" LENGTH="${!4:-44}" RANDOM_PASSWORD="$(openssl rand -base64 64 | tr -d '+/=\n' | cut -c -${!LENGTH})" RANDOM_PASSWORD="${!PREFIX}${!RANDOM_PASSWORD}" SHARED_SECRET="$(echo "$SHARED_SECRET" | jq '. + {"'"$SECRET_KEY_NAME"'": "'"$RANDOM_PASSWORD"'"}')" aws secretsmanager update-secret \ --region ${AWS::Region} \ --secret-id ${OpenViduSharedInfo} \ --secret-string "$SHARED_SECRET" > /dev/null 2>&1 echo "$RANDOM_PASSWORD" elif [[ "$MODE" == "save" ]]; then SECRET_KEY_NAME="$2" SECRET_VALUE="$3" SHARED_SECRET="$(echo "$SHARED_SECRET" | jq '. + {"'"$SECRET_KEY_NAME"'": "'"$SECRET_VALUE"'"}')" aws secretsmanager update-secret \ --region ${AWS::Region} \ --secret-id ${OpenViduSharedInfo} \ --secret-string "$SHARED_SECRET" > /dev/null 2>&1 echo "$SECRET_VALUE" else exit 1 fi mode: "000755" owner: "root" group: "root" '/usr/local/bin/check_app_ready.sh': content: | #!/bin/bash while true; do HTTP_STATUS=$(curl -Ik http://localhost:7880 | head -n1 | awk '{print $2}') if [ $HTTP_STATUS == 200 ]; then break fi sleep 5 done mode: "000755" owner: "root" group: "root" '/usr/local/bin/restart.sh': content: | #!/bin/bash set -e # Stop all services systemctl stop openvidu # Update config from secret /usr/local/bin/update_config_from_secret.sh # Start all services systemctl start openvidu mode: "000755" owner: "root" group: "root" Properties: ImageId: !Ref AmiId LaunchTemplate: # Enable IMDSv2 by default LaunchTemplateName: IMDSV2 Version: !GetAtt IMDSv2LaunchTemplate.DefaultVersionNumber InstanceType: !Ref InstanceType IamInstanceProfile: !Ref OpenViduServerInstanceProfile 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 OpenviduServer export HOME="/root" # Install OpenVidu /usr/local/bin/install.sh || { echo "[OpenVidu] error installing OpenVidu"; exit 1; } # Config S3 bucket /usr/local/bin/config_s3.sh || { echo "[OpenVidu] error configuring S3 bucket"; exit 1; } # Start OpenVidu systemctl start openvidu || { echo "[OpenVidu] error starting OpenVidu"; exit 1; } # Update shared secret /usr/local/bin/after_install.sh || { echo "[OpenVidu] error updating shared secret"; exit 1; } # Launch on reboot echo "@reboot /usr/local/bin/restart.sh" | crontab # Wait for the app /usr/local/bin/check_app_ready.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: 200 MyEIP: Type: 'AWS::EC2::EIPAssociation' Condition: PublicElasticIPPresent Properties: InstanceId: !Ref OpenviduServer EIP: !Ref PublicElasticIP IMDSv2LaunchTemplate: Type: AWS::EC2::LaunchTemplate Properties: LaunchTemplateName: IMDSV2 LaunchTemplateData: MetadataOptions: HttpEndpoint: enabled HttpPutResponseHopLimit: 1 HttpTokens: required WaitCondition: Type: 'AWS::CloudFormation::WaitCondition' CreationPolicy: ResourceSignal: Timeout: PT10M 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 - IpProtocol: tcp FromPort: 1935 ToPort: 1935 CidrIp: 0.0.0.0/0 - IpProtocol: tcp FromPort: 1935 ToPort: 1935 CidrIpv6: ::/0 - IpProtocol: tcp FromPort: 7881 ToPort: 7881 CidrIp: 0.0.0.0/0 - IpProtocol: tcp FromPort: 7881 ToPort: 7881 CidrIpv6: ::/0 - IpProtocol: udp FromPort: 7885 ToPort: 7885 CidrIp: 0.0.0.0/0 - IpProtocol: udp FromPort: 7885 ToPort: 7885 CidrIpv6: ::/0 - IpProtocol: udp FromPort: 50000 ToPort: 60000 CidrIp: 0.0.0.0/0 - IpProtocol: udp FromPort: 50000 ToPort: 60000 CidrIpv6: ::/0 Outputs: ServicesAndCredentials: Description: Services and credentials Value: !Sub https://${AWS::Region}.console.aws.amazon.com/secretsmanager/home?region=${AWS::Region}#!/secret?name=openvidu-${AWS::Region}-${AWS::StackName}