Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# Build outputs (regenerated by CI)
build/
# ...but lab-vm/build/ is source: the scripts that create, verify and publish
# the lab appliance. Not an output, and not regenerable.
!lab-vm/build/

# Raw PDF extraction dumps (derivable from extract.py; bulky)
src/raw/
Expand All @@ -11,6 +14,10 @@ src/raw/
# Lab runtime
lab/**/*.log

# Lab VM appliance — 800 MB, over GitHub's file limit. Built by
# lab-vm/build/export-ova.sh and served from R2; only the checksum is tracked.
*.ova

# TLS keys (generate locally with lab/make-certs.sh)
lab/asterisk/etc/keys/

Expand Down
1 change: 1 addition & 0 deletions asterisk-lab-base-1.0.ova.sha256
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
4a3fe749cc4edad5eba7c8ce6ede07e2d9d1c188f3a871da5a0f19c692aef25c asterisk-lab-base-1.0.ova
62 changes: 62 additions & 0 deletions lab-vm/asterisk/etc/extensions.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
;
; Dialplan for the Asterisk lab VM — the starting point, not the finished PBX.
;
; This file gives you exactly what Lab 1 needs: two phones that can call each
; other, and an echo test. Every later lab adds to it — the IVR, voicemail,
; the queue, and trunk routing are yours to build.
;
; Generated from lab-vm/asterisk/etc/extensions.conf by provision.sh.
; `lab reset` restores this baseline if you paint yourself into a corner.
;

[globals]
; Where an unanswered call waits before giving up, in seconds. Used by the
; Dial() calls below so the timeout is stated once.
RINGTIME=20

;===========================================================================
; ${CTX_INTERNAL} — where your desk phones live
;===========================================================================
; Anything that can reach this context can dial anything in it. That is why
; calls from the PSTN gateway land in ${CTX_FROM_TRUNK} instead.
[${CTX_INTERNAL}]

; --- phone to phone ---
exten => ${EXT_A},1,Dial(PJSIP/${EXT_A},${RINGTIME})
same => n,Hangup()

exten => ${EXT_B},1,Dial(PJSIP/${EXT_B},${RINGTIME})
same => n,Hangup()

; --- the browser phone (Lab 6) ---
exten => ${EXT_WEBRTC},1,Dial(PJSIP/${WEBRTC_USER},${RINGTIME})
same => n,Hangup()

; --- echo test: the fastest way to prove audio works in both directions ---
; If you hear yourself, RTP is flowing. If you hear silence, it is not — and
; that distinction is the first question in every audio troubleshooting lab.
exten => ${EXT_ECHO},1,Answer()
same => n,Playback(demo-echotest)
same => n,Echo()
same => n,Playback(demo-echodone)
same => n,Hangup()

; --- headless verification target, called by sipp ---
; `lab verify` dials this to prove the PBX answers without needing a softphone.
exten => ${EXT_SIPP},1,Answer()
same => n,Playback(hello-world)
same => n,Echo()
same => n,Hangup()

;===========================================================================
; ${CTX_FROM_TRUNK} — calls arriving from the simulated PSTN
;===========================================================================
; Deliberately almost empty. It cannot dial out, it cannot reach your phones,
; and that is the point: an inbound context should only be able to do the one
; thing you decided it may do. Lab 5 gives it a real destination.
[${CTX_FROM_TRUNK}]

exten => _.,1,NoOp(Inbound from the PSTN gateway: ${CALLERID(num)} -> ${EXTEN})
same => n,Answer()
same => n,Playback(hello-world)
same => n,Hangup()
23 changes: 23 additions & 0 deletions lab-vm/asterisk/etc/http.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
;
; Asterisk's built-in HTTP server.
;
; It serves two things in this course: the ARI REST API (Lab 8) and the secure
; WebSocket the browser phone signals over (Lab 6). Both are off by default in
; a stock Asterisk; the lab switches them on so those labs have something to
; connect to on day one.
;
[general]
enabled=yes

; Bound to every interface, which is safe here because the only network that
; reaches this VM is the host-only one. Do not copy this to a public server.
bindaddr=0.0.0.0
bindport=${ARI_PORT}

; TLS, for wss:// — the browser will not open an insecure WebSocket from an
; https page. The certificate is self-signed and generated on first boot, so
; your browser will warn about it once; Lab 6 explains how to accept it.
tlsenable=yes
tlsbindaddr=0.0.0.0:${WSS_PORT}
tlscertfile=/etc/asterisk/keys/asterisk.pem
tlsprivatekey=/etc/asterisk/keys/asterisk.key
144 changes: 144 additions & 0 deletions lab-vm/asterisk/etc/pjsip.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
;
; PJSIP configuration for the Asterisk lab VM.
;
; Generated from lab-vm/asterisk/etc/pjsip.conf by provision.sh. Values such as
; ${EXT_A} come from lab-vm/lab.env — edit there, not here, if you rebuild.
; Editing this file directly on a running lab VM is expected and encouraged:
; that is what the labs ask you to do. `lab reset` puts it back.
;
; ${EXT_A} / ${EXT_B} desk phones (register your softphone from the host)
; ${WEBRTC_USER} browser phone over secure WebSocket
; sipp IP-identified, no auth, for headless call generation
; ${TRUNK_NAME} the simulated PSTN gateway
;

;=============================== transports ==============================
[transport-udp]
type=transport
protocol=udp
bind=0.0.0.0:5060

; Secure WebSocket, for the browser phone.
[transport-wss]
type=transport
protocol=wss
bind=0.0.0.0

;=============================== templates ===============================
; Templates keep the endpoint definitions below short enough to read at a
; glance. A template is applied with (name) after the section header.
[endpoint-internal](!)
type=endpoint
context=${CTX_INTERNAL}
disallow=all
allow=ulaw
allow=alaw

[auth-userpass](!)
type=auth
auth_type=userpass

[aor-single](!)
type=aor
max_contacts=1

;=============================== ${EXT_A} — ${EXT_A_NAME} ===================
[${EXT_A}](endpoint-internal)
auth=${EXT_A}
aors=${EXT_A}
callerid=${EXT_A_NAME} <${EXT_A}>
[${EXT_A}](auth-userpass)
username=${EXT_A}
password=${EXT_A_PASS}
[${EXT_A}](aor-single)

;=============================== ${EXT_B} — ${EXT_B_NAME} ===================
[${EXT_B}](endpoint-internal)
auth=${EXT_B}
aors=${EXT_B}
callerid=${EXT_B_NAME} <${EXT_B}>
[${EXT_B}](auth-userpass)
username=${EXT_B}
password=${EXT_B_PASS}
[${EXT_B}](aor-single)

;=============================== sipp ====================================
; Identified by source address rather than digest auth, so the SIPp scenarios
; used for headless verification stay simple. sipp runs on this VM, so it can
; only ever arrive from loopback — and loopback is the ONLY address matched
; here. On a bridged adapter, widening this to the local subnet would hand an
; unauthenticated endpoint to every machine on your network.
[sipp](endpoint-internal)
aors=sipp
[sipp](aor-single)

[sipp-identify]
type=identify
endpoint=sipp
match=127.0.0.1

;=============================== browser phone ===========================
; webrtc=yes is the convenience option that turns on everything a browser
; client needs: DTLS media encryption, an auto-generated certificate, ICE,
; AVPF and RTCP multiplexing.
[${WEBRTC_USER}](endpoint-internal)
aors=${WEBRTC_USER}
auth=${WEBRTC_USER}
webrtc=yes
transport=transport-wss
callerid=Browser <${EXT_WEBRTC}>
[${WEBRTC_USER}](auth-userpass)
username=${WEBRTC_USER}
password=${WEBRTC_PASS}
[${WEBRTC_USER}](aor-single)

;=============================== simulated PSTN ==========================
; ${TRUNK_HOST}:${TRUNK_PORT} is a real Asterisk server that stands in for the
; PSTN. Every student shares it, so each one registers a different account
; from the range ${TRUNK_ACCOUNT_RANGE}. Set TRUNK_ACCOUNT in lab.env to the
; number assigned to you.
;
; Calls arriving from the gateway land in the ${CTX_FROM_TRUNK} context — never
; in ${CTX_INTERNAL}. Keeping inbound trunk traffic out of the context that can
; dial out is the single most important toll-fraud defence you have.

[${TRUNK_NAME}]
type=endpoint
context=${CTX_FROM_TRUNK}
transport=transport-udp
disallow=all
allow=ulaw
allow=alaw
direct_media=no
outbound_auth=${TRUNK_NAME}
aors=${TRUNK_NAME}
from_user=${TRUNK_ACCOUNT}

[${TRUNK_NAME}]
type=aor
contact=sip:${TRUNK_HOST}:${TRUNK_PORT}
; No qualify_frequency: this gateway does not answer SIP OPTIONS, so qualifying
; it would leave the contact permanently "Unavailable" on a trunk that works.

[${TRUNK_NAME}]
type=auth
auth_type=userpass
username=${TRUNK_ACCOUNT}
password=${TRUNK_PASS}

[${TRUNK_NAME}]
type=registration
transport=transport-udp
outbound_auth=${TRUNK_NAME}
server_uri=sip:${TRUNK_HOST}:${TRUNK_PORT}
client_uri=sip:${TRUNK_ACCOUNT}@${TRUNK_HOST}:${TRUNK_PORT}
contact_user=${TRUNK_ACCOUNT}
retry_interval=60

; Match inbound requests from the gateway to the trunk endpoint by address.
; The hostname is resolved when the config loads, so this keeps working if the
; gateway's IP changes — which is why it is a hostname and not a literal IP.
[${TRUNK_NAME}]
type=identify
endpoint=${TRUNK_NAME}
match=${TRUNK_HOST}
19 changes: 19 additions & 0 deletions lab-vm/asterisk/etc/rtp.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
;
; RTP media ports for the lab VM.
;
; On the Docker lab this range had to be published to the host one port at a
; time, which made Docker Desktop crawl. In a VM there is nothing to publish:
; the VM owns its own address, so media flows straight to it.
;
[general]
rtpstart=${RTP_START}
rtpend=${RTP_END}

; Send RTCP so `rtp set stats on` and the quality figures in the
; troubleshooting labs have something to report.
rtcpinterval=5000

; ICE/STUN are off. Everything here is on one host-only network with no NAT
; between the softphone and Asterisk, so they would only add moving parts.
; Lab 5 turns ICE on for the browser phone, which genuinely needs it.
icesupport=no
Loading
Loading