Last active
December 21, 2022 10:18
-
-
Save lgaetz/066c655867b4e679da21dbd1567b29bd to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
; Latest version: https://gist.github.com/lgaetz/066c655867b4e679da21dbd1567b29bd | |
; | |
; Crude proof of concept of 2FA for a FreePBX extension | |
; Clients can register to the PBX, but are unable to dial until they successfully | |
; enter a random PIN sent via email. | |
; Once the PIN passes, the client IP is whitelisted and user can dial without | |
; restriction until the IP changes | |
; | |
; License: GNU GPL3+ | |
; | |
; usage: put this code in extensions_custom.conf and change context of extension to from-mac | |
; | |
; History | |
; 2021-10-14 First commit, NOT SUITABLE FOR PRODUCTION | |
; 2021-10-18 update notes, add emerg bypass, NOT SUITABLE FOR PRODUCTION | |
[from-mac] | |
; only local extensions should be in this context, so dialplan ASSUMES that no external callers will end up here. | |
; There is no check to confirm the call is from a local extension | |
exten => _.,1,Noop(Entering user defined context from-mac in extensions_custom.conf) | |
; Add bypass to allow all emergency call strings - required for some jurisdictions | |
exten => _.,n,GotoIf(($["${EXTEN}"="911"]?pass) | |
exten => _.,n,GotoIf(($["${EXTEN}"="933"]?pass) | |
exten => _.,n,GotoIf(($["${EXTEN}"="999"]?pass) | |
exten => _.,n,Set(mac_address=${PJSIP_HEADER(read,User-Agent):-12}})) | |
exten => _.,n,Set(ext_ip=${CUT(CHANNEL(pjsip,remote_addr),:,1)}) ; ip addess of dialing extension | |
; add check to confim string is an IP address | |
exten => _.,n,Macro(user-callerid) ; needed to set ampuser var | |
; check to see if the ip is recognized for this extension | |
exten => _.,n,gotoif($["${DB(ipwhite/${AMPUSER})}"="${ext_ip}"]?pass) | |
; calls here need to be challenged | |
exten => _.,n,set(goal=${RAND(0,9)}${RAND(0,9)}${RAND(0,9)}${RAND(0,9)}) ; generates a 4 digit PIN | |
; todo - can we hide PIN from being logged? | |
exten => _.,n,set(email=${VM_INFO(${AMPUSER}@default,email)}) ; get email for vm | |
; to do, AGI to get email address from userman instead of voicemail | |
exten => _.,n,TrySystem(echo "PIN ${goal}" | mail -s "2FA validation PIN" ${email}) ; email PIN to caller's vm | |
exten => _.,n,Read(dtmf_in,enter-password,4,,,60) ; caller gets 60 sec to read email and enter DTMF pin | |
exten => _.,n,gotoif($["${dtmf_in}"="${goal}"]?whitelist) ; if pin matches, add IP to astdb for whitelisting | |
exten => _.,n,Noop(challenge failed *************************) | |
; TODO after multiple failures, add IP to blacklist? send alert? | |
exten => _.,n,Hangup() | |
exten => _.,n(whitelist),Noop(Whitelist ip) | |
exten => _.,n,Set(DB(ipwhite/${AMPUSER})=${ext_ip}) ; add IP to astdb so they don't get challenged again | |
exten => _.,n(pass),Noop(passed *************************) | |
exten => _.,n,goto(from-internal,${EXTEN},1) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
; Different apporach to the same concept | |
; Extensions stay in from-internal context, which will allow them to dial local feature codes/extensions, but | |
; outbound calls are blocked until the calling extension's SIP User agent is known to the system | |
; Unknown UA will be challenged with PIN | |
; | |
; License: GNU GPL3+ | |
; | |
; usage: put this code in extensions_custom.conf and change context of extension to from-mac | |
; | |
; History | |
; 2021-10-15 First commit, NOT SUITABLE FOR PRODUCTION | |
; 2021-10-18 Update notes, add emerg bypass, NOT SUITABLE FOR PRODUCTION | |
[check-user-agent] | |
exten => s,1,Noop(Entering user defined context check-user-agent in extensions_custom.conf) | |
; TODO - add check and bypass to ensure this is a call from a local registered extension and NOT an external call being forwarded to an outbound | |
; route. Prob best done by confirming channel var AMPUSER matches a local extension number | |
exten => s,n,Set(user_agent=${PJSIP_HEADER(read,User-Agent)}})) ; UA of dialing ext | |
exten => s,n,Set(mac_address=${PJSIP_HEADER(read,User-Agent):-12}})) ; 12 rightmost chars from UA which could be a MAC | |
exten => s,n,Set(ext_ip=${CUT(CHANNEL(pjsip,remote_addr),:,1)}) ; ip addess of dialing extension | |
; check to see if the UA has been previously allowed for this extension | |
exten => s,n,gotoif($["${DB(ipwhite/${AMPUSER})}"="${user_agent}"]?pass) | |
; calls here need to be challenged | |
exten => s,n,set(goal=${RAND(0,9)}${RAND(0,9)}${RAND(0,9)}${RAND(0,9)}) ; generate random 4 digit PIN | |
; todo - can we hide PIN from being logged? | |
exten => s,n,set(email=${VM_INFO(${AMPUSER}@default,email)}) ; get email for vm | |
; to do, AGI to get email address from userman instead of voicemail | |
; to do, validate that we got a proper email address | |
exten => s,n,TrySystem(echo "PIN ${goal}" | mail -s "2FA validation PIN" ${email}) ; email PIN to caller's vm | |
exten => s,n,Read(dtmf_in,enter-password,4,,,120) ; caller gets 120 sec to read email and enter DTMF pin | |
exten => s,n,gotoif($["${dtmf_in}"="${goal}"]?whitelist) ; if pin matches, add UA to astdb for whitelisting | |
exten => s,n,Noop(challenge failed *************************) | |
exten => s,n,Hangup() | |
exten => s,n(whitelist),Noop(Code entered correctly) | |
exten => s,n,Playback(auth-thankyou) | |
exten => s,n,Set(DB(ipwhite/${AMPUSER})=${user_agent}) ; add IP to astdb so they don't get challenged again | |
exten => s,n(pass),Noop(passed *************************) | |
exten => s,n,Return() | |
[macro-dialout-trunk-predial-hook] | |
exten => s,1,Noop(Entering user defined context macro-dialout-trunk-predial-hook in extensions_custom.conf) | |
; add bypass for emergency calls. Calls thru outbound routes with EMERGENCY enabled will not be challenged | |
exten => s,n,GosubIf($["${EMERGENCYROUTE}"!="YES"]?check-user-agent,s,1) | |
exten => s,n,MacroExit |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Problems: