user-data.sh•10.4 kB
#!/bin/bash
#
# AWS EC2 User Data Script for Asterisk SIP Trunk Installation
# This script runs on first boot to install and configure Asterisk
#
set -euo pipefail
# Error handling
trap 'echo "Error on line $LINENO"; exit 1' ERR
# Logging
LOG_FILE="/var/log/asterisk-setup.log"
exec 1> >(tee -a "$LOG_FILE")
exec 2>&1
echo "=== Asterisk SIP Trunk Installation Started: $(date) ==="
# Variables from Terraform
export AWS_REGION="${aws_region}"
export ELASTIC_IP="${elastic_ip}"
export ELEVENLABS_PHONE_E164="${elevenlabs_phone_e164}"
export PROJECT_NAME="${project_name}"
export ENABLE_CALL_RECORDINGS="${enable_call_recordings}"
export S3_BUCKET_RECORDINGS="${s3_bucket_recordings}"
export ASTERISK_LOG_LEVEL="${asterisk_log_level}"
export RTP_PORT_START="${rtp_port_start}"
export RTP_PORT_END="${rtp_port_end}"
export ENABLE_CLOUDWATCH="${enable_cloudwatch}"
# Get instance metadata
INSTANCE_ID=$(ec2-metadata --instance-id | cut -d " " -f 2)
PRIVATE_IP=$(ec2-metadata --local-ipv4 | cut -d " " -f 2)
AVAILABILITY_ZONE=$(ec2-metadata --availability-zone | cut -d " " -f 2)
echo "Instance ID: $INSTANCE_ID"
echo "Private IP: $PRIVATE_IP"
echo "Elastic IP: $ELASTIC_IP"
# Update system
echo "=== Updating system packages ==="
yum update -y
# Install development tools
echo "=== Installing development tools ==="
yum groupinstall -y "Development Tools"
yum install -y \
wget \
ncurses-devel \
libuuid-devel \
jansson-devel \
libxml2-devel \
sqlite-devel \
openssl-devel \
kernel-devel \
libedit-devel \
libsrtp-devel \
pjproject-devel \
unixODBC-devel \
libtool-ltdl-devel \
git \
vim \
tcpdump \
nmap \
fail2ban \
awscli \
amazon-cloudwatch-agent
# Download and compile Asterisk 21
echo "=== Downloading Asterisk 21 ==="
cd /usr/src
ASTERISK_VERSION="21.5.0"
wget "https://downloads.asterisk.org/pub/telephony/asterisk/asterisk-$ASTERISK_VERSION.tar.gz"
tar xvfz "asterisk-$ASTERISK_VERSION.tar.gz"
cd "asterisk-$ASTERISK_VERSION"
echo "=== Configuring Asterisk ==="
./configure \
--with-pjproject-bundled \
--with-jansson-bundled \
--libdir=/usr/lib64
# Install required modules
echo "=== Selecting Asterisk modules ==="
make menuselect.makeopts
menuselect/menuselect \
--enable chan_pjsip \
--enable res_pjsip \
--enable res_pjsip_nat \
--enable res_pjsip_session \
--enable res_pjsip_outbound_registration \
--enable app_dial \
--enable app_playback \
--enable app_voicemail \
--enable codec_ulaw \
--enable codec_alaw \
--enable codec_gsm \
--enable format_wav \
--enable format_pcm \
menuselect.makeopts
echo "=== Compiling Asterisk (this may take 10-15 minutes) ==="
make -j$(nproc)
make install
make samples
make config
# Create Asterisk user
echo "=== Creating Asterisk user ==="
groupadd asterisk 2>/dev/null || true
useradd -r -d /var/lib/asterisk -g asterisk asterisk 2>/dev/null || true
chown -R asterisk:asterisk /etc/asterisk /var/{lib,log,spool}/asterisk /usr/lib64/asterisk
# Retrieve credentials from Parameter Store
echo "=== Retrieving credentials from Parameter Store ==="
ELEVENLABS_PASSWORD=$(aws ssm get-parameter \
--name "/$PROJECT_NAME/elevenlabs/sip_password" \
--with-decryption \
--query 'Parameter.Value' \
--output text \
--region "$AWS_REGION")
# Configure PJSIP
echo "=== Configuring PJSIP ==="
cat > /etc/asterisk/pjsip.conf <<EOF
;
; PJSIP Configuration for ElevenLabs SIP Trunk
; Auto-generated by AWS deployment
;
[global]
max_forwards=70
user_agent=Asterisk-AWS-$INSTANCE_ID
default_realm=aws.internal
debug=no
[transport-tcp]
type=transport
protocol=tcp
bind=0.0.0.0:5060
external_media_address=$ELASTIC_IP
external_signaling_address=$ELASTIC_IP
local_net=$PRIVATE_IP/16
[elevenlabs]
type=endpoint
context=from-elevenlabs
transport=transport-tcp
aors=elevenlabs
outbound_auth=elevenlabs-auth
allow=!all,ulaw,alaw
direct_media=no
from_user=$ELEVENLABS_PHONE_E164
callerid=$ELEVENLABS_PHONE_E164
rtp_symmetric=yes
force_rport=yes
rewrite_contact=yes
dtmf_mode=rfc4733
trust_id_inbound=yes
trust_id_outbound=yes
[elevenlabs]
type=aor
contact=sip:sip.elevenlabs.io:5060;transport=tcp
qualify_frequency=60
qualify_timeout=3
[elevenlabs-auth]
type=auth
auth_type=userpass
username=$ELEVENLABS_PHONE_E164
password=$ELEVENLABS_PASSWORD
[elevenlabs]
type=identify
endpoint=elevenlabs
match=sip.elevenlabs.io
EOF
# Configure RTP
echo "=== Configuring RTP ==="
cat > /etc/asterisk/rtp.conf <<EOF
;
; RTP Configuration
;
[general]
rtpstart=$RTP_PORT_START
rtpend=$RTP_PORT_END
rtpchecksums=no
dtmftimeout=3000
rtcpinterval=5000
strictrtp=yes
icesupport=no
stunaddr=
EOF
# Configure Extensions (Dialplan)
echo "=== Configuring dialplan ==="
cat > /etc/asterisk/extensions.conf <<EOF
;
; Asterisk Dialplan for ElevenLabs Integration
;
[general]
static=yes
writeprotect=no
clearglobalvars=no
[globals]
ELEVENLABS_PHONE=$ELEVENLABS_PHONE_E164
RECORDINGS_PATH=/var/spool/asterisk/recordings
[from-elevenlabs]
; Incoming calls from ElevenLabs agent
exten => _X.,1,NoOp(Incoming call from ElevenLabs: \${CALLERID(all)})
same => n,Set(CDR(accountcode)=elevenlabs)
same => n,Answer()
same => n,Wait(1)
same => n,Playback(hello-world)
same => n,Echo() ; Echo test for audio verification
same => n,Hangup()
[outbound-to-elevenlabs]
; Outgoing calls to ElevenLabs agent
exten => _X.,1,NoOp(Dialing ElevenLabs Agent: \${EXTEN})
same => n,Set(CALLERID(num)=\${ELEVENLABS_PHONE})
same => n,Set(CALLERID(name)=AWS-Asterisk)
same => n,Set(CDR(accountcode)=elevenlabs)
same => n,Dial(PJSIP/\${EXTEN}@elevenlabs,60,tT)
same => n,Hangup()
[default]
; Default context for safety
exten => _X.,1,NoOp(Unauthorized call attempt)
same => n,Hangup()
EOF
# Configure Asterisk logging
echo "=== Configuring logging ==="
cat > /etc/asterisk/logger.conf <<EOF
;
; Logger Configuration
;
[general]
dateformat=%F %T
[logfiles]
console => notice,warning,error
messages => notice,warning,error
full => $ASTERISK_LOG_LEVEL,notice,warning,error,verbose,dtmf
[syslog]
facility = local0
EOF
# Configure systemd service
echo "=== Configuring systemd service ==="
cat > /etc/systemd/system/asterisk.service <<EOF
[Unit]
Description=Asterisk PBX
After=network.target
[Service]
Type=forking
User=asterisk
Group=asterisk
ExecStart=/usr/sbin/asterisk -f -U asterisk -G asterisk
ExecReload=/usr/sbin/asterisk -rx 'core reload'
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable asterisk
# Configure Fail2Ban for SIP security
echo "=== Configuring Fail2Ban ==="
cat > /etc/fail2ban/jail.d/asterisk.conf <<EOF
[asterisk]
enabled = true
port = 5060
filter = asterisk
logpath = /var/log/asterisk/full
maxretry = 5
bantime = 3600
findtime = 600
EOF
systemctl enable fail2ban
systemctl start fail2ban
# Configure CloudWatch Agent (if enabled)
if [ "$ENABLE_CLOUDWATCH" = "true" ]; then
echo "=== Configuring CloudWatch Agent ==="
cat > /opt/aws/amazon-cloudwatch-agent/etc/config.json <<EOF
{
"agent": {
"metrics_collection_interval": 60,
"run_as_user": "cwagent"
},
"logs": {
"logs_collected": {
"files": {
"collect_list": [
{
"file_path": "/var/log/asterisk/full",
"log_group_name": "/aws/ec2/$PROJECT_NAME/asterisk",
"log_stream_name": "{instance_id}-asterisk-full"
},
{
"file_path": "/var/log/asterisk/messages",
"log_group_name": "/aws/ec2/$PROJECT_NAME/asterisk",
"log_stream_name": "{instance_id}-asterisk-messages"
}
]
}
}
},
"metrics": {
"namespace": "Asterisk/$PROJECT_NAME",
"metrics_collected": {
"cpu": {
"measurement": [
{"name": "cpu_usage_idle", "rename": "CPU_IDLE", "unit": "Percent"},
{"name": "cpu_usage_iowait", "rename": "CPU_IOWAIT", "unit": "Percent"}
],
"totalcpu": false
},
"disk": {
"measurement": [
{"name": "used_percent", "rename": "DISK_USED", "unit": "Percent"}
],
"resources": ["/", "/var/spool/asterisk"]
},
"mem": {
"measurement": [
{"name": "mem_used_percent", "rename": "MEM_USED", "unit": "Percent"}
]
}
}
}
}
EOF
systemctl enable amazon-cloudwatch-agent
systemctl start amazon-cloudwatch-agent
fi
# Create recordings directory (if enabled)
if [ "$ENABLE_CALL_RECORDINGS" = "true" ]; then
mkdir -p /var/spool/asterisk/recordings
chown -R asterisk:asterisk /var/spool/asterisk/recordings
fi
# Start Asterisk
echo "=== Starting Asterisk ==="
systemctl start asterisk
# Wait for Asterisk to initialize
sleep 10
# Verify installation
echo "=== Verifying Asterisk installation ==="
asterisk -rx "core show version"
asterisk -rx "pjsip show endpoints"
asterisk -rx "pjsip show transports"
# Create health check script
cat > /usr/local/bin/asterisk-health-check.sh <<'EOF'
#!/bin/bash
# Health check script for Asterisk SIP trunk
if ! systemctl is-active --quiet asterisk; then
echo "ERROR: Asterisk service is not running"
exit 1
fi
# Check if PJSIP endpoint is registered
ENDPOINT_STATUS=$(asterisk -rx "pjsip show endpoint elevenlabs" | grep -c "Avail")
if [ "$ENDPOINT_STATUS" -eq 0 ]; then
echo "WARNING: ElevenLabs endpoint not available"
exit 1
fi
echo "OK: Asterisk is healthy"
exit 0
EOF
chmod +x /usr/local/bin/asterisk-health-check.sh
# Setup cron for periodic health checks
echo "*/5 * * * * /usr/local/bin/asterisk-health-check.sh >> /var/log/asterisk-health.log 2>&1" | crontab -
# Installation complete
echo "=== Asterisk SIP Trunk Installation Complete: $(date) ==="
echo ""
echo "Deployment Summary:"
echo "==================="
echo "Instance ID: $INSTANCE_ID"
echo "Private IP: $PRIVATE_IP"
echo "Public IP (Elastic IP): $ELASTIC_IP"
echo "SIP Endpoint: sip:$ELASTIC_IP:5060"
echo "ElevenLabs Phone: $ELEVENLABS_PHONE_E164"
echo "RTP Port Range: $RTP_PORT_START-$RTP_PORT_END"
echo ""
echo "Useful Commands:"
echo "================"
echo "Asterisk CLI: asterisk -rx 'command'"
echo "View logs: tail -f /var/log/asterisk/full"
echo "Check endpoint: asterisk -rx 'pjsip show endpoints'"
echo "Enable debug: asterisk -rx 'pjsip set logger on'"
echo ""
echo "Health check: /usr/local/bin/asterisk-health-check.sh"
echo ""