Wake on LAN for Google Apps

Google Apps logoGoogle Apps is great framework for your business and provides ready-to-use application to cover most of the business flows in your organization. However there is no way to fulfill every single need of even small organization and custom solutions are often required.

Most of business face two questions when using Google Apps:

  • How we can integrate Google Apps with our organization internal systems?
  • How we can add custom features to Google Apps services to better suite our need?

The answer to both of the questions  is Google Apps Script. In short Google describes it as:

Google Apps Script is a JavaScript cloud scripting language that provides easy ways to automate tasks across Google products and third party services.

We are going to test Apps script on the real business case that will reduce your electricity bills. Organization that develops software consumes a lot of electricity mostly due high number of powerful workstations that developers prefer to keep online 24/7. This is good thing to be able to connect to workplace during non-working hours to solve some critical problem your customer is facing or push some code to source control to unblock your team members.

Employees will not put workstations in standby mode unless they have convenient and reliable way to power them up. Optimal solution will be some web application with single button – “Wake my workstation”. It doesn’t required to any software to be installed and can be accessed from mobile devices. Surely you don’t want to expose application of this type to then entire world and want to make sure that it’s only accessible by your team members.

We have build solution to wake workstations in your network using Google Apps Script and Google Sites. It consists of three parts:

  • Web site hosted on Google Sites available only to Google Apps domain users. We have used existing intranet website and published new Apps Script there.
  • Apps script service deployed as web application on Google servers. Accessible only by domain users. This application send requests to wake workstation to another application deployed in your office. All communication between Google servers and you office is protected by cryptography.
  • Web application hosted in your office that accepts HTTP requests from Google and sends WOL (Wake On LAN) packets to workstations.

Diagram below shows relationships between all services.

Wake on Lan deployment network diagram

Source code for Google Apps service and simple NodeJS HTTP wake service are available here - wake-on-lan-for-google-apps GitHub repository.

We will review in details each part of the solution in next sections.

Google Apps Script service

There are couple of advantages using Apps Script service as fronted for Wake On LAN solution:

  1. You don’t need to build custom authentication system, you can setup access to the service using Google Apps permissions management.
  2. Apps Script services come with simple key-value storage for settings persistence, so you don’t need to use database for storing MAC addresses.

Building User Interface

Google provides two options for building UI interface of your Apps Script application – UI Service and HTML service.

UI Service

UI Service is server side framework that is similar to ASP.NET web forms. User interface is set up  on the server side and is rendered to JavaScript and HTML in run time  You can assign event handlers to UI controls and you code will be executed on Google servers when user presses a button or selects value from drop down list. When using UI Service your application consists of only server side JavaScript. As experimental feature Google offers UI graphical designer which allows to escape creating UI in the code. One issue when using designer is that you can’t see source of UI component created so can’t store it in source control.

HTML Service

Alternative approach for building UI is using HTML, CSS and client side JavaScript. There are few constraints here as well, mostly for client side JavaScript. User browser will run your JavaScript, server from Google’s domain and this can lead to cross-site scripting security problems. To close this gap in security Google parses all scripts and sanitizes them. Google uses own Caja compiler to strip down unsafe parts of your HTML, CSS and client side JavaScript. For example you can’t extend built in JavaScript types using prototype nor dynamically add new script to the page. These JavaScript constraints limits selection of available JavaScript frameworks to use on client side – AngularJS is not compatible with Caja, however JQuery and JQueryUI are compatible.  CSS3 animations and even html marquee tag will be striped down. You are not allowed to store images along with HTML files and they should be hosted elsewhere. Google uses proxy to server images to the user.

We have selected HTML Service despite all constraints for being able to version UI files and use HTML + CSS.

We have build simple form in HTML that allows user to enter MAC address and send wake request. Google provides access to google.script.run object in client side JavaScript to call server side code.

 google.script.run.withSuccessHandler(function(status) {}).withFailureHandler(function(error) {}).wake();

This will execute wake function on server. Call is asynchronous and works in the same way as AJAX does. One thing to note here is function naming in you server side code – if you want to make function private you add underscore to the end of it’s name (somePrivateFunction_), this will make it invisible for client side JavaScript.

Using Grunt to prepare you HTML and JavaScript for Google Apps Script

Currently Google’s online script editor does not allows you to create separate files for css, js and html. All files should have .gs or .html extension so you have to use script and style tags right in your html. Another option is to use some build tools to assemble UI before deploying to the Google Apps Script editor. For this solution we are using Grunt – JavaScript task runner.

Its much faster to debug non sanitized version of HTML and Javascript in you browser, it takes some time (5-9 seconds) for Google to render sanitized UI after page is loaded. To test non sanitized version you need to mock google.script.run object in your JavaScript. We use Grunt with grunt-preprocess plugin  to conditionally remove or add some pieces of JavaScript and HTML.  You will be able to do things like this:

    // @exclude
    /*
    // @endexclude
    var delegate = google.script.run;
    // @exclude
    */
    // @endexclude

    // @exclude
    var delegate = {};
    // @endexclude

Exclude directive marks block of code that will be removed by Grunt during build process. This allows you to mock missing dependencies  during development. Plugin works with both HTML and JavaScript files.

The same plugins allows to include one file into another with @include directive. We use this to merge js, css and html file into single html before deployment.

<style type="text/css">
     <!--@include ui.css-->
 </style>
<script type="text/javascript">
    <!--@include ui.deploy.js-->
</script>

After ruining Grunt you will get two file backend.gs and ui.html with all testing code removed.

Back-end using Apps Script

Back-end for Wake On LAN is simple and is used to store workstation MAC address and send HTTP requests to wake service. Google provides as simple key-value storage service – ScriptProperties.  To store some value as script property you can use this code:

var currentUser = Session.getActiveUser();
ScriptProperties.setProperty(currentUser.getUserLoginId(), mac.toUpperCase());

We use current user email as a key and can retrieve value with this code:

function getMac_() {
    var currentUser = Session.getActiveUser();
    return ScriptProperties.getProperty(currentUser.getUserLoginId());
}

To call wake service over HTTP we use Apps Script object UrlFetchApp:

var options = {
        "method": "post",
        "payload": getWakePayload_(mac)
    };
var result = UrlFetchApp.fetch(url, options);

 Secure communication

Wake service is exposed to internet and should be protected from unauthorized access. To protect each request we use standard technique with nonce, timestamp and hash token, this will prevent anyone except Google to send requests to wake service or duplicate them.

function getWakePayload_(mac) {
    var secret = ScriptProperties.getProperty("wake_service_secret");
    var nonce = Math.floor(Math.random() * 1000000000);
    var timestamp = (new Date().getTime());
    var dataForHash = mac + nonce.toString() + timestamp.toString();
    var token = Utilities.base64Encode(Utilities.computeHmacSha256Signature(dataForHash, secret));

    return {
        "mac": mac,
        "nonce": nonce.toString(),
        "timestamp": timestamp.toString(),
        "token": urlEncode_(token)
    };
}

This code creates secure token using shared secret stored in Apps Script property called wake_service_secret.

Wake service on NodeJS

Wake service can be implemented using any technology that allows to send TCP packets and accept HTTP requests. NodeJS feels right for this task because all necessary modules are available in npm repository and ready-to-use application can be build in couple of hours.

We use following npm modules:

  • express – wraps NodeJS http module and allows to handle request with less “plumbing” code.
  • crypto – used for hash token verification.
  • wake_on_lan – used to send WOL magic packets.
  • memory-cache – used to cache request nonces and prevent duplicate requests.

You can use Forever to run NodeJS app as a daemon on Linux box – forever on GitHub. If you prefer Windows hosting you can use IISNode – iisnode on GitHub.

You can get source code for all parts of the application here - wake-on-lan-for-google-apps GitHub repository.