Automating Orbi SSL Setup

In my previous post I described how to enable telnet mode and install your own SSL certs on your Orbi router. This allows the Orbi to be trusted within your own network, which is specially helpful when you use any of the modern (read annoying) browsers that automatically switch to https. When that happens the default self-signed cert inside the Orbi is rejected leaving you to figure out how to bypass it.

I prefer another way - install my own SSL certs that are signed by a trusted CA such as Letsencrypt. Netgear should have allowed this in the first place - heck my old Epson printer allows me to upload certs and properly use SSL. If only Netgear was a some kind of networking company that understood how security works……..

I also mentioned in my previous post that there remains a problem when the certs are updated. The change is not permanent. As a result each time the Orbi power cycles it resets the certs back to the original self-signed ones provided by Netgear.

To get around this I decided to write a small python application that leverages selenium and headless firefox to log into the Orbi router and enable the telnet capabilities on the debug page. Once that is done then the process from my previous post kicks in and the SSL certs are automaticaly updated.

For this to work you will need all of the items I mentioned before as well as python, firefox, the selenium python package and the firefox webdriver application.

Here in a nutshell is the process:

  1. Let's Encrypt certificates. Fetch the acme.sh application, figure out how to use it, create wildcard certificates for your domain, use either DNS validation or .well-known public server validation to verify the certs.

  2. I recommend setting up a python virtual environment on Ubuntu:

  3. sudo apt-get install python-virtualenv
    mkdir ~/orbi
    cd ~/orbi
    virtualenv vpython
    source vpython/bin/activate
    

  4. Install firefoxi chrome

  5. sudo apt-get install firefox chromium-browser
    

  6. Download the firefox geckodriver from here chromium webdriver that matches your version of the chromium-browser from here and extract the binary somewhere onto your path, such as /usr/local/bin/geckodriver

  7. Install the selenium python library:

  8. pip install selenium
    

  9. Take the following orbi.py script and adapt it to your environment:

  10. import time
    import argparse
    import sys
    from selenium import webdriver
    from selenium.webdriver.chrome.options import Options
    from selenium.webdriver.common.keys import Keys
    from selenium.webdriver.common.alert import Alert
    from selenium.webdriver.support.ui import WebDriverWait as wait
    from selenium.webdriver.support import expected_conditions as EC
    
    from selenium import webdriver
    
    
    
    # default parameter values
    default_orbi_ip = '192.168.1.1'
    default_orbi_user = 'admin'
    default_orbi_password = 'password'
    default_orbi_wait = 5
    
    # Parse command line arguments
    ap = argparse.ArgumentParser(description='Set Orbi certificate')
    ap.add_argument("-o", "--orbi", required=False, default=default_orbi_ip,
            help=u"Orbi IP address on your local lan [default:{}]".format(default_orbi_ip))
    ap.add_argument("-u", "--user", required=False, default=default_orbi_user,
            help=u"admin user [default:{}]".format(default_orbi_user))
    ap.add_argument("-p", "--password", required=False, default=default_orbi_password,
            help="admin password [default:{}]".format(default_orbi_password))
    ap.add_argument("-w", "--wait", required=False, default=default_orbi_wait,
            help="time to wait for orbi page to refresh [default:{}s]".format(default_orbi_wait))
    
    # store arguments as args
    args = vars(ap.parse_args())
    
    # Build URL based upon IP address - note: NOT HTTPS
    base_url = "http://{}/debug_detail.htm".format(args['orbi'])
    log_out_url = "http://log:out@{}/debug_detail.htm".format(args['orbi'])
    debug_url = "http://{}:{}@{}/debug_detail.htm".format(args['user'], args['password'],args['orbi'])
    
    print "\nProcessing request to enable Orbi telnet function:"
    
    # Set headless option
    chrome_options = Options()
    chrome_options.add_argument('--headless')
    chrome_options.add_argument('--no-sandbox')
    chrome_options.add_argument('--disable-dev-shm-usage')
    
    print "    - initializing headless Chrome...."
    driver = webdriver.Chrome(executable_path=r'/usr/local/bin/chromedriver',chrome_options=chrome_options)
    
    print "    - requesting logout: {}".format(base_url)
    driver.get(log_out_url)
    
    print "    - requesting debug url: {}".format(base_url)
    driver.get(debug_url)
    
    # there should only be one checkbox with enable_telnet
    print "    - retrieving enable_telnet checkbox...."
    checkboxes = driver.find_elements_by_xpath("//input[@name='enable_telnet']")
    
    assert len(checkboxes) == 1,"Incorrect number of checkboxes retrieved.  Expected: 1, found: {}".format(len(checkboxes))
    
    # Select the checkbox if not already selected.
    # This will trigger the page to submit the value to the Orbi
    if checkboxes[0].is_selected() != True:
        print "    - Enabling telnet"
        checkboxes[0].click()
    else:
        print "    - telnet already enabled"
    
    print "    - requesting logout: {}".format(log_out_url)
    driver.get(log_out_url)
    print
    
    driver.quit()
    

  11. Have a local web server that can serve your certs to the Orbi when it requests their download. nginx works well. Plus since you now have valid wildcard certs it can also work well with SSL. Just make sure to put the cert in a location that only allows the Orbi to access, not the Internet as a whole. Allow the Orbi IP address and deny all the others.

  12. Install expect

  13. sudo apt-get install expect
    

  14. Have an expect script that can log into the Orbi via telnet and install the scripts, such as the following orbi_login.sh:

  15. #!/usr/bin/expect
    
    spawn telnet -e ! YOUR_ORBI_HOST_NAME
    expect {*login:}
    send "admin\r"
    expect {*Password:}
    send "YOUR_ORBI_PASSWORD\r"
    expect {*root@RBR50:/#}
    send "curl -s -k https://YOUR_LOCAL_WEBSERVER/server.pem > /etc/lighttpd/certs/server.pem\r"
    expect {*root@RBR50:/#}
    send "/etc/init.d/lighttpd restart\r"
    expect {*root@RBR50:/#}
    send "!"
    expect {*telnet>}
    send "quit\r"
    

  16. Create a new script that will execute the orbi.py script, build and place the cert file, call the expect scripts and then cleanup. For example, orbi_setup.sh:

  17. #!/bin/bash
    
    # Log to syslog
    exec 3>&1 4>&2
    trap 'exec 2>&4 1>&3' 0 1 2 3
    exec 1> >(logger -s -t $(basename $0)) 2>&1
    
    KEY=/PATH_TO_YOUR_PRIVATE_KEY_WITH_NO_PASSWORD
    CERT=/PATH_TO_THE_FULLCHAIN_CERT_CONTAINING_YOUR_CERT_AND_LETSENCRYPT_INTERMEDIATE_CERT
    PEMFILE=/WHERE_YOU_WILL_PUT_THE_ORBI_FILE_ON_YOUR_WEBSERVER_TO_DOWNLOAD
    
    cd ~/orbi
    
    echo "Enable telnet on Orbi"
    source vpython/bin/activate
    
    # Either edit the orbi.py file defaults to match your values or input parameters
    python orbi.py -o 192.168.0.1 -p some_password
    
    echo
    echo "Place pem file in accessible location"
    cat ${KEY} ${CERT} | grep [A-Za-z] >  ${PEMFILE}
    
    echo
    echo "Wait 10s for telnet to start"
    sleep 10
    
    echo
    echo "Pull pem file to orbi and restart lighttpd"
    ./orbi_login.sh
    
    echo
    echo "Cleanup"
    rm -f ${PEMFILE}
    killall -9 firefox
    

On my system I then setup a cron job to run every few hours. That way if the cert gets updated (also a cronjob) or if the Orbi power cycles (with the storms recently it seems to be a more common occurance), the Orbi will eventually get updated and I won’t have to worry about SSL failures.

If this is all confusing and meaningless then I would recommend sticking to accessing your Orbi via its IP address. But for those of you that understand all this gibberish, Enjoy! I know that there are a number of you out there wanting to do this from the posts I’ve seen on the Netgear forums.


Update 2019-06-08: If you use firefox then you might have noticed it can no longer log into the Orbi web page due to it’s inability to negotiate HTTP Auth requests. From what I’ve read the excuse is that there is no specification, so it doesn’t work. The translation is, it used to work and we’re being shitheads to no longer support it. So go get the ESR version instead - it still works since it still has the old code.

Update 2019-07-11: Sick of the script failing due to the auht popup, even with the latest ESR, I rewrote it a bit to attempt simply logging out by sending false credentials. Hopefuly it works fine, seems to do so far. I also updated the python script to refelct this change.