From f94e74ddc76c4e1bb4f4a5d6f2b3f7efea77a97d Mon Sep 17 00:00:00 2001 From: Bart Van Der Meerssche Date: Sat, 16 Apr 2011 20:54:25 +0200 Subject: [PATCH] [heartbeat] update the heartbeat to use JSON/HTTPS --- .../package/flukso/luasrc/heartbeat.lua | 141 ++++++++++++++++++ 1 file changed, 141 insertions(+) create mode 100755 mote/v2/openwrt/package/flukso/luasrc/heartbeat.lua diff --git a/mote/v2/openwrt/package/flukso/luasrc/heartbeat.lua b/mote/v2/openwrt/package/flukso/luasrc/heartbeat.lua new file mode 100755 index 0000000..18e5ced --- /dev/null +++ b/mote/v2/openwrt/package/flukso/luasrc/heartbeat.lua @@ -0,0 +1,141 @@ +#!/usr/bin/env lua + +--[[ + + heartbeat.lua - send a heartbeat to the flukso server + + Copyright (C) 2008-2009 jokamajo.org + 2011 Bart Van Der Meerssche + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +]]-- + +if not arg[1] then + print ('Please pass the reset argument as a boolean to the script.') + os.exit(1) +end + +local dbg = require 'dbg' +local nixio = require 'nixio' +nixio.fs = require 'nixio.fs' +local uci = require 'luci.model.uci'.cursor() +local luci = require 'luci' +luci.sys = require 'luci.sys' +luci.json = require 'luci.json' +local httpclient = require 'luci.httpclient' + +-- WAN settings +local WAN_BASE_URL = 'https://api.flukso.net/device/' +local WAN_KEY = '0123456789abcdef0123456789abcdef' +uci:foreach('system', 'system', function(x) WAN_KEY = x.key end) -- quirky but it works + +local DEVICE = '0123456789abcdef0123456789abcdef' +uci:foreach('system', 'system', function(x) DEVICE = x.device end) + +local UPGRADE_URL = 'http://www.flukso.net/files/upgrade/' + +-- https header helpers +local FLUKSO_VERSION = '000' +uci:foreach('system', 'system', function(x) FLUKSO_VERSION = x.version end) + +local USER_AGENT = 'Fluksometer v' .. FLUKSO_VERSION +local CACERT = '/etc/ssl/certs/flukso.ca.crt' + +-- collect relevant monitoring points +function collect_mp() + local monitor = {} + + monitor.reset = tonumber(arg[1]) + monitor.version = tonumber(FLUKSO_VERSION) + monitor.uptime = math.floor(luci.sys.uptime()) + system, model, monitor.memtotal, monitor.memcached, monitor.membuffers, monitor.memfree = luci.sys.sysinfo() + + return monitor +end + +-- open the connection to the syslog deamon, specifying our identity +nixio.openlog('heartbeat', 'pid') + +local monitor = collect_mp() +local monitor_json = luci.json.encode(monitor) + + +-- phone home +local headers = {} +headers['Content-Type'] = 'application/json' +headers['X-Version'] = '1.0' +headers['User-Agent'] = USER_AGENT +headers['Connection'] = 'close' + +local options = {} +options.sndtimeo = 5 +options.rcvtimeo = 5 +-- We don't enable peer cert verification so we can still update/upgrade +-- the Fluksometer via the heartbeat call even when the cacert has expired. +-- Disabling validation does mean that the server has to include an hmac +-- digest in the reply that the Fluksometer needs to verify, this to prevent +-- man-in-the-middle attacks. +options.tls_context_set_verify = 'none' +-- options.cacert = CACERT +options.method = 'POST' +options.headers = headers +options.body = luci.json.encode(monitor) + +local hash = nixio.crypto.hmac('sha1', WAN_KEY) +hash:update(options.body) +options.headers['X-Digest'] = hash:final() + +local http_persist = httpclient.create_persistent() +local url = WAN_BASE_URL .. DEVICE +local response_json, code, call_info = http_persist(url, options) + +if code == 200 then + nixio.syslog('info', string.format('%s %s: %s', options.method, url, code)) +else + nixio.syslog('err', string.format('%s %s: %s', options.method, url, code)) + + -- if available, send additional error info to the syslog + if type(call_info) == 'string' then + nixio.syslog('err', call_info) + elseif type(call_info) == 'table' then + local auth_error = call_info.headers['WWW-Authenticate'] + + if auth_error then + nixio.syslog('err', string.format('WWW-Authenticate: %s', auth_error)) + end + end + + os.exit(2) +end + +-- verify the reply's digest +hash = nixio.crypto.hmac('sha1', WAN_KEY) +hash:update(response) +if call_info.headers['X-Digest'] ~= hash:final() then + nixio.syslog('err', 'Incorrect digest in the heartbeat reply. Discard response.') + os.exit(3) +end + +-- check whether we have to reset or upgrade +local response = luci.json.decode(response_json) + +if response.upgrade == monitor.version then + os.execute('reboot') +elseif response.upgrade > monitor.version then + os.execute('wget -P /tmp ' .. UPGRADE_URL .. 'upgrade.' .. response.upgrade) + os.execute('chmod a+x /tmp/upgrade.' .. response.upgrade) + os.execute('/tmp/upgrade.' .. response.upgrade) + os.execute('rm /tmp/upgrade.' .. response.upgrade) +end