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 firefox

  5. sudo apt-get install firefox
    

  6. Download the firefox geckodriver 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.firefox.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
    
    # default parameter values - change them to reflect your environment if you wish
    default_orbi_ip = '192.168.1.1'
    default_orbi_user = 'admin'
    default_orbi_password = 'password'
    default_orbi_wait = 5
    default_gecko_driver = '/usr/local/bin/geckodriver'
    
    # 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))
    ap.add_argument("-g", "--gecko", required=False, default=default_gecko_driver,
            help="The geckodriver [default:{}]".format(default_gecko_driver))
    
    # store arguments as args
    args = vars(ap.parse_args())
    
    # Build Orbi URL based upon IP address - note: NOT HTTPS
    url = "http://{}/debug_detail.htm".format(args['orbi'])
    
    print "\nProcessing request to enable Orbi telnet function:"
    print "    - using url: {}".format(url)
    
    # Set headless option
    options = Options()
    options.set_headless()
    assert options.headless  # Make sure that headless setting worked
    
    print "    - initializing headless Firefox...."
    
    # gecko driver
    driver = webdriver.Firefox(options=options, executable_path=r'{}'.format(args['gecko']))
    driver.get(url)
    
    # Auth popup
    print "    - waiting for authorization popup....."
    try:
        wait(driver, 5).until(EC.alert_is_present())
    except:
        print
        print "      Authorization popup timed out."
        print "      Make sure you are not logged in another window in a browser."
        print "      If you are, then logout of that window, close it and try again."
        print
        sys.exit()
    
    print "    - processing authorization popup...."
    alert = driver.switch_to.alert
    
    # force ascii on popup text
    text = ''.join([i if ord(i) < 128 else ' ' for i in alert.text])
    print u"    - found alert text: {}".format(text)
    
    # send login
    alert.send_keys(args['user'] + Keys.TAB + args['password'] + Keys.TAB)
    alert.accept()
    
    # wait for page to load
    print u"    - waiting {} seconds for page to refresh".format(args['wait'])
    time.sleep(args['wait'])
    
    # 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
    
    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.