Blog

  • PowerShell script to find users with OWA and Mobile App enabled – Office 365

    I was tasked to create a PowerShell script to quickly find out how many users have OWA enabled and how many have Mobile App (ActiveSync) enabled. Complicating this was the deployment is on Office 365/Exchange 365 setup. Following is the end result of the endeavor which will quickly give you the list you want.

    # Created by Asanka Gunasekara - 2/14/2020
    # This script generates list of users who has OWA enabled and Mobile/Active Sync Enabled.
    # User needs special permission: https://docs.microsoft.com/en-us/exchange/troubleshoot/connecting-to-the-service/access-denied-connect-powershell
    # User needs to enter their full username: [email protected]
    
    $UserCredential = Get-Credential
    $Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri https://outlook.office365.com/powershell-liveid/ -Credential $UserCredential -Authentication Basic -AllowRedirection
    Import-PSSession $Session -DisableNameChecking
    
    Write-Output ""
    Write-Output "Users with OWA Enabled"
    Get-CASMailbox -ResultSize Unlimited | where { $_.OWAEnabled } | sort DisplayName | ft DisplayName, PrimarySmtpAddress, OWAEnabled –autosize
    Write-Output "Users with Mobile APP Enabled"
    Get-CASMailbox -ResultSize Unlimited | where { $_.ActiveSyncEnabled} | sort DisplayName | ft DisplayName, PrimarySmtpAddress, activesyncenabled –autosize
    
    Remove-PSSession $Session
  • Traditional Reverse Proxy with Traefik

    So the other day I needed to setup a reverse proxy server for a client of mine who wanted some internal servers accessible from the internet. I tested this with nginx but had issues with LetsEncrypt as well as some pages timing out.

    I finally ended up with Traefik but boy did it take along time to get it configured as I was not familiar with Docker and most guides are Docker with other Docker Containers.

    Following the guide below will get you up and running in no time with

    • Integrated LetsEncrypt SSL certificates, which will auto generate and renew.
    • Standalone systemd service for ease of use/management
    • A hardened/restricted Traefik service for added security.

    This guide assumes the following has already been done:

    • Latest Debian/Ubuntu server has been installed/setup.
    • No other services are using ports 80/443
    • If server is behind NAT, ports 80 and 443 has been forwarded correctly.
    • DNS records have been correctly updated to point to Traefic Server.

    Let’s start with setting up the working directories and files required to run Traefik and grabbing the Traefik binary.

    mkdir /etc/traefik
    touch /etc/traefik/traefik.toml
    touch /etc/traefik/acme.json
    chmod 0600 /etc/traefik/acme.json
    wget https://github.com/containous/traefik/releases/download/v1.7.9/traefik
    chmod +x traefik
    mv traefik /usr/local/bin/

    Next, we need to edit traefik.toml which holds configuration for Traefik. In the following sample configuration, it is configured to

    • Listen on HTTP and HTTPS (80/443)
    • Redirect HTTP to HTTPS
    • Enabled LetsEncrypt integration
    • Created 2x backend services (Plex and ERPNext)
    • Mapped frontends (URL you would enter on a browser) to backends
    nano /etc/traefik/traefik.toml
    debug = false
    
    #Uncomment below if you selfsigned backends
    #insecureSkipVerify = true
    
    logLevel = "ERROR"
    defaultEntryPoints = ["https","http"]
    
    [entryPoints]
    	[entryPoints.http]
    	address = ":80"
    		[entryPoints.http.redirect]
    		entryPoint = "https"
    	[entryPoints.https]
    	address = ":443"
    	[entryPoints.https.tls]
    
    [retry]
    
    [api]
    
    [acme]
    	email = "[email protected]"
    	storage = "/etc/traefik/acme.json"
    	entryPoint = "https"
    	onHostRule = true
    
    [acme.httpChallenge]
    	entryPoint = "http"
    	
    [file]
    
    [backends]
    	[backends.plex]
    		[backends.plex.servers.server1]
    			url = "https://172.16.1.2"
    	[backends.erpnext]
    		[backends.erpnext.servers.server1]
    			url = "http://172.16.1.1"
    
    [frontends]
    		[frontends.plex]
    			backend = "plex"
    			passHostHeader = true
    		[frontends.plex.routes.test_1]
    			rule = "Host:media.domain.com"
    		[frontends.erpnext]
    			backend = "erpnext"
    			passHostHeader = true
    		[frontends.erpnext.routes.test_1]
    			rule = "Host:erp.domain.com"
    

    Next we need to setup the systemd service. The following will setup the restricted systemd file

    nano /etc/systemd/system/traefik.service
    [Unit]
    Description=Traefik
    Documentation=https://docs.traefik.io
    After=network-online.target
    AssertFileIsExecutable=/usr/local/bin/traefik
    AssertPathExists=/etc/traefik
    
    [Service]
    Type=notify
    ExecStart=/usr/local/bin/traefik -c /etc/traefik/traefik.toml
    Restart=always
    WatchdogSec=1s
    ProtectSystem=strict
    ReadWritePaths=/etc/traefik/acme.json
    ReadOnlyPaths=/etc/traefik/traefik.toml
    PrivateTmp=true
    ProtectHome=true
    PrivateDevices=true
    ProtectKernelTunables=true
    ProtectControlGroups=true
    LimitNPROC=1
    
    [Install]
    WantedBy=multi-user.target
    

    Finally, reload the systemd service, enable the traefik service to auto start and start the actual service.

    systemctl daemon-reload
    systemctl enable traefik.service
    systemctl start traefik.service

    Sources:

    https://blog.ssdnodes.com/blog/traefik-multiple-ssl-websites/
    https://blog.cubieserver.de/2018/locking-down-traefik-systemd-service/

  • PowerDNS Recursor – Hiding/Private Zones via LUA

    When deploying PowerDNS Recursor for our customers, we had an issue with how to hide some responses/zones to external IPs. The way we implemented this solution is via LUA scripting.

    -- Set Private Zone
    myDomain = newDN("private.com")
    
    -- Whitelist IPs
    myNetblock = newNMG()
    myNetblock:addMask("192.168.0.0/24")
    myNetblock:addMask("192.168.9.0/24")
    
    function preresolve(dq)
    	if dq.qname:isPartOf(myDomain) and myNetblock:match(dq.remoteaddr) and dq.qtype == pdns.A then
    		-- if the dns requests is for the private zone by trusted IP, proceed to respond.
    		return false
    	elseif dq.qname:isPartOf(myDomain) and dq.qtype == pdns.A then
    		-- if the dns requests is for the private zone by UNtrusted IP, do not respond for A record requests
    		pdnslog ("Remote IP " .. dq.remoteaddr:toString() .. " for domain=" .. dq.qname:toString() .. ", type=" .. tostring(dq.qtype) )
    		return true
    	else
    		-- function normally for everything else.
    		return false
    	end
    end

    In the above example, we set the zone/domain that needs to be private, next select which IP addresses are allowed to see the records and finally the logic via the preresolve() function.

  • [FreePBX/Asterisk]Multiple / Rotary Call Flow Toggles

    We got a customer the other day who wanted to manually override their normal call flow. This could have been easily achieved by a single toggle if it was to a single destination. But this customer wanted wanted to be able select multiple destination and also only wanted to push one button to do so.

    This meant we had to write custom code to get this done and it was achieved by writing a code to manipulate call flow toggles.

    What you need to add is the following to the extensions_custom.conf

    [from-internal-custom]
    include => enable-switch
    
    [check-active-switch]
    exten => s,1,NoOp("This context loops through all the switches and checks which one is ON. Than it follows its destination")
    exten => s,n,GoSub(sub-get-nr-of-switches,s,1)
    exten => s,n,Set(nrofs=${GOSUB_RETVAL})
    exten => s,n,Set(i=0)
    exten => s,n,Set(default-dest=app-daynight,4,1)
    exten => s,n,Set(switchnr="NULL")
    exten => s,n,While($[$[${i} < ${nrofs}] & $[${switchnr} = "NULL"]])
    exten => s,n,Set(switchnr=${IF($["${DB(DAYNIGHT/C${i})}" = "NIGHT"]?${i}:"NULL")})
    exten => s,n,Set(i=$[${i} + 1])
    exten => s,n,EndWhile
    exten => s,n,GotoIf($[${switchnr}!="NULL"]?app-daynight,${switchnr},1:${default-dest})
    exten => s,n,Hangup()
    
    [sub-get-nr-of-switches]
    exten => s,1,Set(nr_of_s=0)
    exten => s,n,Set(exten_state="NOT_ACQUIRED")
    exten => s,n,While($[${exten_state}!=0])
    exten => s,n,Set(exten_state=$[${VALID_EXTEN(app-daynight,${nr_of_s},1)}])
    exten => s,n,NoOp(Exten nr ${nr_of_s} of app-daynight is ${exten_state})
    exten => s,n,Set(nr_of_s=${IF($[${exten_state}=1]?$[${nr_of_s}+1]:${nr_of_s})})
    exten => s,n,EndWhile
    exten => s,n,NoOp(${nr_of_s} switches found)
    exten => s,n,Return(${nr_of_s})
    
    [enable-switch]
    exten => _*20X,1,NoOp("Enable selected Switch and disable others")
    same => n,GoSub(sub-get-nr-of-switches,s,1)
    same => n,Set(nrofs=${GOSUB_RETVAL})
    same => n,Set(switchtoactivate=${EXTEN:3})
    same => n,Set(CSTATE=${DB(DAYNIGHT/C${switchtoactivate})})
    same => n,NoOp(Activated Switch nr ${switchtoactivate})
    same => n,Set(i=0)
    same => n,While($[${i}<${nrofs}])
    same => n,GotoIf($["${CSTATE}" = "NIGHT"]?end1)
    same => n,Set(DB(DAYNIGHT/C${i})=DAY)
    same => n(end1),NoOp(Dialing the same toggle code. Switch it off)
    same => n,Set(DEVICE_STATE(Custom:AHTGL${i})=NOT_INUSE)
    same => n,Set(DEVICE_STATE(Custom:DAYNIGHT${i})=NOT_INUSE)
    same => n,Set(i=$[${i}+1])
    same => n,EndWhile
    same => n,Set(freepbx_toggle_dest=*28${switchtoactivate})
    same => n,GotoIf($["${CSTATE}" = "NIGHT"]?end2)
    same => n,Set(DEVICE_STATE(Custom:AHTGL${switchtoactivate})=INUSE)
    same => n(end2),NoOp(Skip setting BLF)
    same => n,Goto(app-daynight-toggle,${freepbx_toggle_dest},1)
    same => n,Hangup
    same => hint,Custom:AHTGL${EXTEN:3}

    Then from FreePBX add custom destination with check-active-switch,s,1 as the value and point the inbound route to this custom destination.

    Now, you create all the call flow toggles with their override mode set to the destinations you want to go and the normal mode is set identical on each toggle the normal operation you want to work.

    Finally, create BLF keys to see which switch is active using *20X as the value to dial/monitor.

    Modify the following line to point calls to a default destination if none of the toggles are on override  mode. On the below code it is set to app-daynight,4,1

    exten => s,n,Set(default-dest=app-daynight,4,1)

    The original code is via : https://community.freepbx.org/t/multiple-daynight-toggles-aka-call-flow-control/27518

    Modifications on this code helps you use BLF to monitor currently selected toggles as well as ability to turn off the the override.

  • Solving NAT related issues for Hosted PABXes

    If you come across a situation where a customer has multiple handsets usually when upwards of 20 units per site/location, you may have noticed that you get myriad of issues depending on the router/firewall being used.

    Some issues that I’ve noticed are, phones loosing registration randomly and unable to register again; Transfer/hold/BLF functions stops working correctly.

    The main culprit here is that the router/firewall is unable to cope with NATing table for same source port for this many number of devices and sometimes freezes. The side affect of this is that traffic from the hosted PABX does not reach the handset as the router stops forwarding the traffic inwards.

    There are two remedies that I have worked out. First being setting up a VPN, which opens a whole heap of other issues plus brings added complexity. The second being changing the listening port of the device to be unique one. This is much simpler and easier to maintain and troubleshoot.

    Now the hard part is that if you have a site with hundreds of provisioning files for each phone, this would be tedious and cumbersome. So here’s a simple script to help you save the day. This is designed for Yealink phones and can be adapted to any other phone model.

    #!/bin/bash
    
    #check old yealink phones are there
    if ls 001565* 1> /dev/null 2>&1; then
        LOOP=0
      FILES=001565*
    else
        LOOP=1
      FILES=805ec0*
    fi
    
    while [ $LOOP -lt 2 ]; do
      for f in $FILES
      do
        #check if sip.listen_port already exists. if not proceed to add following
        if ! grep -q '^[[:blank:]]*sip\.listen_port' "$f"; then				
          UNAME=$(awk '/user_name/{print $NF}' "$f")
          # take action on each file. $f store current file name
          # check the lenght of extension number and set the last 3 digits as listen port
          if [ ${#UNAME} -lt 2 ]; then
            echo "sip.listen_port = 900$UNAME" >> $f
          elif [ ${#UNAME} -lt 3 ]; then
            echo "sip.listen_port = 90$UNAME" >> $f
          elif [ ${#UNAME} -lt 4 ]; then
            echo "sip.listen_port = 9$UNAME" >> $f
          else
            UNAME=${UNAME:(-3)}
            echo "sip.listen_port = 9$UNAME" >> $f
          fi
        fi
      
      done
      #check new yealink phones are there
      if ls 805ec0* 1> /dev/null 2>&1; then
        FILES=805ec0*
        LOOP=$((LOOP+1))
      else
        LOOP=$((LOOP+2))
      fi
    done

     

  • [Asterisk/FreePBX]Set Inbound DID as Caller-ID for Forwarded Calls

    This is a snippet developed by a colleague of mine. This is for people who’s supplier’s doesn’t allow passing original Caller ID for forwarded calls. What this does is it will show the DID where the calling party called in as the caller ID instead of showing up your default trunk number for the caller ID.

    First add the below code to extensions_override_freepbx.conf, next restart/reload asterisk and you’re good to go.

    [macro-outbound-callerid]
    include => macro-outbound-callerid-custom
    exten => s,1,ExecIf($["${CHANNEL(channeltype)}" = "Local"]?Set(CALLERID(all)=${MASTER_CHANNEL(DEXTEN)}))
    exten => s,n,ExecIf($["${CHANNEL(channeltype)}" = "Local"]?Set(AMPUSER=${MASTER_CHANNEL(DEXTEN)}))
    exten => s,n,ExecIf($["${CALLINGNAMEPRES_SV}" != ""]?Set(CALLERPRES(name-pres)=${CALLINGNAMEPRES_SV}))
    exten => s,n,ExecIf($["${CALLINGNUMPRES_SV}" != ""]?Set(CALLERPRES(num-pres)=${CALLINGNUMPRES_SV}))
    exten => s,n,ExecIf($["${REALCALLERIDNUM:1:2}" = ""]?Set(REALCALLERIDNUM=${CALLERID(number)}))
    exten => s,n(start),GotoIf($[ $["${REALCALLERIDNUM}" = ""] | $["${KEEPCID}" != "TRUE"] | $["${OUTKEEPCID_${ARG1}}" = "on"] ]?normcid)
    exten => s,n,Set(USEROUTCID=${REALCALLERIDNUM})
    exten => s,n,GotoIf($["foo${DB(AMPUSER/${REALCALLERIDNUM}/device)}" = "foo"]?bypass)
    exten => s,n(normcid),Set(USEROUTCID=${DB(AMPUSER/${AMPUSER}/outboundcid)})
    exten => s,n(bypass),Set(EMERGENCYCID=${DB(DEVICE/${REALCALLERIDNUM}/emergency_cid)})
    exten => s,n,Set(TRUNKOUTCID=${OUTCID_${ARG1}})
    exten => s,n,GotoIf($["${EMERGENCYROUTE:1:2}" = "" | "${EMERGENCYCID:1:2}" = ""]?trunkcid)
    exten => s,n,Set(CALLERID(all)=${EMERGENCYCID})
    exten => s,n,Set(CDR(outbound_cnum)=${CALLERID(num)})
    exten => s,n,Set(CDR(outbound_cnam)=${CALLERID(name)})
    exten => s,n(exit),MacroExit()
    exten => s,n(trunkcid),ExecIf($[${LEN(${TRUNKOUTCID})} != 0]?Set(CALLERID(all)=${TRUNKOUTCID}))
    exten => s,n(usercid),ExecIf($[${LEN(${USEROUTCID})} != 0]?Set(CALLERID(all)=${USEROUTCID}))
    exten => s,n,ExecIf($[${LEN(${TRUNKCIDOVERRIDE})} != 0 | ${LEN(${FORCEDOUTCID_${ARG1}})} != 0]?Set(CALLERID(all)=${IF($[${LEN(${FORCEDOUTCID_${ARG1}})}=0]?${TRUNKCIDOVERRIDE}:${FORCEDOUTCID_${ARG1}})}))
    exten => s,n(hidecid),ExecIf($["${CALLERID(name)}"="hidden"]?Set(CALLERPRES(name-pres)=prohib_passed_screen))
    exten => s,n,ExecIf($["${CALLERID(name)}"="hidden"]?Set(CALLERPRES(num-pres)=prohib_passed_screen))
    exten => s,n,Set(CDR(outbound_cnum)=${CALLERID(num)})
    exten => s,n,Set(CDR(outbound_cnam)=${CALLERID(name)})
    

     

  • Connecting two Asterisk/FreePBX using SIP Trunks

    This was a project that I’ve been working on and off for some time and always ended up with failure. There are many documentations available on the net however the one that worked for me is using IP trunks and here’s how it is done.

    Lets take an example so it’s easy to identify. Let’s assume the the two PBXes are at Sydney & Melbourne.

    In Sydney PABX, create a new trunk, name it to-Melbourne. For the peer details set it up as follows:

    Trunk Name: melbPABX

    type=friend
    context=from-trunk
    host=Melbourne-PABX-IP-OR-FQDN
    username=sydPABX
    password=superstrongsecretpassword
    qualify=yes

    In the Melbourne PABX, create a new trunk with a name to-Sydney. For the peer details set it up as follows:

    Trunk Name: sydPABX

    type=friend
    context=from-trunk
    host=Sydney-PABX-IP-OR-FQDN
    username=melbPABX
    password=superstrongsecretpassword
    qualify=yes

    Points to remember:

    • Password common for both trunks.
    • Trunk names used under Peer Details acts as the usernames.
    • Extension numbers should differ on each PABX otherwise it would not be possible to route calls correctly.
  • [Asterisk/FreePBX] Blocking incoming calls to an extension

    You may have a scenario where you’ve a particular extension assigned to a special service and you want only a select group of people/extensions to be able to make calls to this extension.

    This can be easily achieved with the dial plan below. There’s just one file to edit : extensions_custom.conf

    [kick]
    exten => _X.,1,Playback(invalid)
    exten => _X.,n,Hangup

    [from-internal-custom]
    exten => 7572/7520,1,Answer()
    exten => 7572/7507,1,Answer()
    exten => 7572/7584,1,Answer()
    exten => 7572/4914,1,Answer()
    exten => 7572/7500,1,Answer()
    exten => 7572/_XXXX,1,Goto(kick,${EXTEN},1)
    exten => 7572/_X.,1,Answer()

    Let’s analyse what’s happening here.

    The first section [kick] tells Asterisk to play a message saying the dialed destination is invalid and then to hang up.

    The next section [from-internal-custom] defines what extension can connect/dial to this particular extension (in this example ext 7572 is the one needing incoming restrictions).
    The part before the “/” defines the destination extension and the part after the “/” defines the source extensions.
    So in this scenario extensions 7500, 7507, 7520, 7584 and 4914 will be able to call 7572. All other 4 digit extensions will be sent to [kick] which will terminate their calls.

    The last part will allow any other call to be picked up by the extension (for example, an external call).

     

    Original idea from : source

  • Asterisk/FreePBX Time Conditions with Multiple Time Zones / Daylight Saving

    This solution is for older Asterisk/FreePBX installations (v2.11 and below) where the Time Condition module doesn’t have the Time Zone dropdown.

    TZThis is based on a bash script which alters MySQL DB values to represent the change in DST.

    This script will help you to automate DST changes easily and and have peace of mind about not having to manually change every year.

    Scenario:

    PABX is for an Australian company which has offices in multiple states. Main office is in NSW which observes daylight saving whereas a branch office is in QLD which does not observe daylight saving. Server time is set for NSW timezone. (more…)

  • Garmin Vivosmart HR Review – First Impressions and General Use Review

    I’ve been hitting the gym more regularly in the last few months and I’ve been looking into getting my first fitness tracker for some time. The main reasons behind the push is that I work at odd hours and was wanting to keep track of my health and general fitness and see where I’m heading to. You could say this this review will encompass many points that someone who is about to take the first steps in getting into the fitness tracking bandwagon.

    So without further ado, let’s start with the review.

    Garmin Vivosmart HR

    What this review is about?

    There are plenty of reviews for this device available from review experts, athletes and other users who had been using some sort of fitness tracker. This review will be based on someone who would be thinking of getting a tracker, no prior experience with trackers and who had been working out in the gym for some time (say 6 months to 1 year).

    So you will see things that I liked, issues that I faced with this device and in general deciding what tracker to buy and why I went with Garmin’s Vivosmart HR.

    I will not be doing scientific measurements/comparisons as there are plenty of other sites that have done it. Just a regular use review.

    (more…)