Skip to content

rpodsada/edgeos-openvpn-status

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

14 Commits
 
 
 
 
 
 

Repository files navigation

EdgeOS OpenVPN Status Feature Wizard

This adds an OpenVPN Status Feature Wizard to your EdgeOS router in the Wizards tab. It does this by parsing and displaying the log file generated by OpenVPN with the --status option.

DISCLAIMER: This is a personal utility I made for myself and comes with no guarantees that it will work on every (or any) EdgeMAX router. I did not have any documentation or support to build this Wizard. I simply reverse-engineered the "VPN status" wizard that comes with the unit to create this. This has only been tested on an EdgeRouter X running EdgeOS 1.10.11 and 2.0.8. It should be considered beta and not ready for a production environment. Install, tinker and improve at your own risk.

Configuring OpenVPN

OpenVPN must be configured to generate a status log using the --status option.

Recommendations:

  1. Generate the log under the /config/ path. This folder tree survives firmware upgrades.
  2. Specify an absolute path to the log so you can be sure it's where you expect it to be.
  3. You could also use the admin users home folder, e.g. /home/ubnt/ as the log location if you prefer.
configure
set interfaces openvpn vtun0 openvpn-option "--status /config/openvpn/status.log"
commit;save
exit

Note: If you had previously set a --status path in your openvpn configuration, remove that old path.

Installing the Wizard

The next step is to install the Wizard on the router. Note: In the admin interface there is a + button beside the Feature wizards section, but I have not yet figured out what format it expects the files in (zip or individual file uploads did not work for me.) If somebody knows how to package files for this upload, I'd love to know.

For the wizard to work, its folder needs to end up in /config/wizard/feature. To get it there I copied the files to the router via scp to the admin user's home folder (replace your server's login credentials & IP as needed):

$ scp -r OpenVPN_status/ [email protected]:~/

Then I logged into the router, changed to root, moved the folder, and set permissions:

$ sudo su
# cp -a /home/ubnt/OpenVPN_status/ /config/wizard/feature/
# chown -R www-data:vyattacfg /config/wizard/feature/OpenVPN_status
# chmod +x /config/wizard/feature/OpenVPN_status/wizard-run

(Thanks to @r2munz for reminding me about the chmod +x ;)

Updating the path to the log file in the Wizard

The script is setup by default to look for the status file in /config/openvpn/status.log. If your path is different, edit the wizard-run script and change the STATUS_LOG_PATH:

sudo su
vi /config/wizard/feature/OpenVPN_status/wizard-run

You'll find it up top:

STATUS_LOG_PATH="/config/openvpn/status.log"

Verify it works

Login to your router and go to the Wizards area. You should see an OpenVPN status link under the Features area. Clicking on it should bring up a parsed version of the OpenVPN status log with two tables: the list of clients and the routing information below that. If this information comes up, you're done!

Background: Hacking Feature Wizards

When I noticed the + button beside Features in the Wizards area, I realized the intention was for the community to build their own wizards for the router. Of course my first instinct was to look for a repository of community (or official) wizards I could add. All I could find however were users asking if something like this exists in the Ubiquiti support forums. Apparently there is a beta testing program where some users have gotten acccess to preliminary docs / info on this, but it's not public yet.

Finding Where They Live

The router runs on Debian and this is a web interface. My first instinct was to see if anything lived in the /var/www folder, and viola, inside of it was a folder called wizard that contained status and feature sub-folders. I originally built my wizard and placed it under this structure. It worked fine... except when I updated from 1.10.11 to 2.0.8, my wizard was wiped out. Looks like these are system folders which are reset on firmware updates/changes and can't be used. I later discovered the /config/wizard/feature folder which I know will persist across firmware updates.

(Semi) Simple System Based on Conventions

Diving into the VPN_status folder to learn how they work, I was surprised to see how simple the structure of a Feature Wizard was. The system is very much designed around convention over configuration. You simply put things where they should be and name them a certain way, and the magic fairies will make the rest work. Systems like this can be easy and quick to set up, but can also be frustrating & limiting if you don't know how the system really works or what the available options are.

Folder/File Structure

Each system wizard lives in its own folder under /var/www/wizard/feature

The folder name of the wizard becomes its title in the menu. Underscores are converted to spaces. For example, /var/www/wizard/feature/VPN_status becomes VPN status in the web UI.

Each wizard folder has two files in it:

wizard-run    # bash script to process command
wizard.html   # html template 

Note: For wizards that take input, there is also a validator.json file with validation rules for the input. I won't cover this here since I haven't played with it yet. This feature wizard also doesn't use it.

The wizard-run file is nothing more than a bash script that takes a command and some input, and replies with a JSON response. Treat it as a bash-driven JSON API.

The wizard.html file is the view/template for the wizard. It takes input and displays output, and has a specific HTML structure that EdgeOS's javascript depends on.

Basic Operation

wizard-run

wizard-run takes two command-line input parameters:

  • $1 is the command/action the wizard should perform. This is a string, and the first command a wizard gets from EdgeOS seems to be load, which asks it to load any initial information required to be displayed.
  • $2 is any info submitted from the wizard by the user (via form submission). This is not used during the initial load command. Only wizards that take input use this. Since this wizard doesn't and I haven't played with this functionality, I won't comment on it here. (But looking at other wizards, and given the pattern across EdgeOS, I believe this parameter will receive a JSON object representing the form submission, which you'd use jq to parse. The DNS_host_names wizard seems to follow this pattern.)

Beyond this, it's nothing more than a regular bash script. You can run any available bash commands on the system, use tools like sed, grep, awk, etc. You can also use some API functions exposed via the command-line by EdgeOS / Vyatta.

Whatever you do with it, the script must then return a JSON object in this basic format:

{
    success: 1 | 0,
    data: { 
       key: ..., 
       key2: ...,    
    },
    readonly: 1 | 0
}

success should be 1 to indicate success of the query, or 0 for failure. data is an object with named properties. The names of the properties are used to bind and display data in wizard.html readonly seems to indicate whether the data returned should be rendered as read-only or not. Our script always returns 1 as it is read-only. See the wizard.html info below for more details.

wizard.html

This is the display template for the interface. I don't know a lot about how EdgeOS is built, but I would venture to guess this interface utilizes existing code/conventions used across other core EdgeOS screens.

It seems to employ a rudimentary Javascript data-binding system, where a JSON object is used to automatically populate/manipulate the HTML in the template. The HTML must be named/structure a certain way for this to work. You'll note there's no JavaScript in this file or in any of the Wizard folders. The JavaScript that runs this is elsewhere, and I'm guessing it's more core to EdgeOS as a whole and it's probably not meant to be changed.

The key things in the template which seem to affect how it runs are:

  1. The div.addable element contains a data-object attribute, and that value must match one of the keys for your data property in the JSON response. For example:
<div class="addable" data-object="clients" data-objectify="1">

The clients value tells the system to use data.clients in the JSON response from wizard-run:

{
    success: 1 | 0,
    data: { 
       clients: [
            { ... },
            { ... },
       ],
       routing: [
            { ... },
            { ... },
       ]
    },
    readonly: 1 | 0
}
  1. Inside of this element, is another div.addable-template element. The HTML inside of this element seems to be the template for rendering each record returned in the specified key in the JSON object (styles removed for clarity):
<div class="addable-template">
   <tr>
       <td><input type="text" disabled class="hostname" name="name" style="..."></td>
       <td><input type="text" disabled class="hostname" name="address" style="..."></td>
       <td><input type="text" disabled class="hostname" name="received" style="..."></td>
       <td><input type="text" disabled class="hostname" name="sent" style="..."></td>
       <td><input type="text" disabled class="hostname" name="connected" style="..."></td>
    </tr>
</div>

The key attribute here is name. The value of name sets which property in the JSON data object to populate the field with. In this wizard, we send back a data object that looks like this:

...
    data: { 
       clients: [
            { name: "client1", address: "x.x.x.x", received: "12345", sent: "12345", connected: "date"  },
            { name: "client2", address: "x.x.x.x", received: "12345", sent: "12345", connected: "date"  }           
       ],
...
}

If the name attribute matches the name of the property in the JSON response object, the system will automatically set the text field to that value.

  1. The table.addable-container element is where the rows are rendered using the template above. In our wizard.html, this element looks like so:
<table class="addable-container">
        <tr><th>Name</th><th style="width:130px">Address</th><th style="width:80px">Bytes in</th><th style="width:80px">Bytes out</th><th style="width:145px">Connected</th></tr>
</table>

It keeps the header row and appends the rows using the template below it (e.g. like jQuery's append() method.)

That's the basic structure of wizard.html. I have not extensively experimented with it, but I believe you can change from a <table> element to others using the same structure. The class names dictate what's what.

One thing I have not figured out, however, is how to get the system to render data into elements other than an input field with a name attribute. In my wizard I simply added some styles to make it look less like an input element, but there may be some mobile/usability issues with doing this.

Of course, if you're part of the beta program maybe there's a whole document about all of this and I'm wasting my time, but this is what I've discovered, and I hope it helps others.

Wishlist/TODOS

  1. Error handling - what if the status file is not where the script thinks it is? What if no users are connected? OpenVPN not setup? etc.
  2. Parsing - there's probably better ways to parse the file. I'm not a bash parsing expert.
  3. Output - there might be cleaner ways to output this information. Might be limited by the Wizard conventions.
  4. Date updated - also output the date last updated (for the file overall), found at the top of the status file.

About

OpenVPN Status Feature Wizard for EdgeOS

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published