gfch100: more peer cleanup
* pass args to html and js to let them know they're a peer
* use that when contacting craftui
* add 2nd device to simulator, to test peer control
* add proxy to peer
Change-Id: Icc752b6ff87e7f1d2c94f57ccc9c0a734563de80
diff --git a/craftui/.gitignore b/craftui/.gitignore
index 5d9b5ae..7dd7a03 100644
--- a/craftui/.gitignore
+++ b/craftui/.gitignore
@@ -1,7 +1,9 @@
*.pyo
*.swp
.started
-.sim.extracted
-sim
+.sim1.extracted
+.sim2.extracted
+sim1
+sim2
LOG
tmp-certs
diff --git a/craftui/HOW.restart_if_changed b/craftui/HOW.restart_if_changed
index 0bfd11f..2a26b18 100644
--- a/craftui/HOW.restart_if_changed
+++ b/craftui/HOW.restart_if_changed
@@ -4,21 +4,26 @@
export PATH="$(pwd)/../../../../out.gfch100_defconfig/host/usr/bin:$PATH"
-pid=
+pid1=
+pid2=
restart() {
- [ -n "$pid" ] && kill $pid
+ [ -n "$pid1" ] && kill $pid1
+ [ -n "$pid2" ] && kill $pid2
echo "######################################################################"
echo "# starting craftui"
gpylint *.py
make test
./craftui &
- pid=$!
+ pid1=$!
+ ./craftui -2 &
+ pid2=$!
touch .started
}
onExit() {
- [ -n "$pid" ] && kill $pid
+ [ -n "$pid1" ] && kill $pid1
+ [ -n "$pid2" ] && kill $pid2
exit 1
}
@@ -26,7 +31,7 @@
restart
while sleep 1; do
- if ! kill -0 $pid; then
+ if ! kill -0 $pid1 || ! kill -0 $pid2; then
restart
continue
fi
diff --git a/craftui/HOW.updatesim b/craftui/HOW.updatesim
index d5f2e6a..f01d964 100644
--- a/craftui/HOW.updatesim
+++ b/craftui/HOW.updatesim
@@ -1,27 +1,31 @@
#! /bin/sh
-ssh chimera '
- rm -rf /tmp/sim;
- mkdir -p /tmp/sim/proc && cat /proc/uptime > /tmp/sim/proc/uptime;
- for n in /sys/class/net/*/statistics/*; do
- mkdir -p /tmp/sim/$(dirname $n);
- test ! -d $n && cat $n > /tmp/sim/$n;
- done;
- ip -o -d link > /tmp/sim/ip.link.txt;
- ip -o addr > /tmp/sim/ip.addr.txt;
- presterastats > /tmp/sim/presterastats.json;
- '
+for suffix in 1 2; do
-ssh chimera cd / "&&" tar czf - -C / \
- config/settings \
- etc/platform \
- etc/serial \
- etc/softwaredate \
- etc/version \
- tmp/glaukus \
- tmp/serial \
- tmp/ssl \
- tmp/platform \
- tmp/gpio \
- tmp/sim \
- > sim.tgz
+ ssh chimera$suffix '
+ rm -rf /tmp/sim;
+ mkdir -p /tmp/sim/proc && cat /proc/uptime > /tmp/sim/proc/uptime;
+ for n in /sys/class/net/*/statistics/*; do
+ mkdir -p /tmp/sim/$(dirname $n);
+ test ! -d $n && cat $n > /tmp/sim/$n;
+ done;
+ ip -o -d link > /tmp/sim/ip.link.txt;
+ ip -o addr > /tmp/sim/ip.addr.txt;
+ presterastats > /tmp/sim/presterastats.json;
+ '
+
+ ssh chimera$suffix cd / "&&" tar czf - -C / \
+ config/settings \
+ etc/platform \
+ etc/serial \
+ etc/softwaredate \
+ etc/version \
+ tmp/glaukus \
+ tmp/serial \
+ tmp/ssl \
+ tmp/platform \
+ tmp/gpio \
+ tmp/sim \
+ > sim$suffix.tgz
+
+done
diff --git a/craftui/Makefile b/craftui/Makefile
index 782ca22..dbb2082 100644
--- a/craftui/Makefile
+++ b/craftui/Makefile
@@ -15,14 +15,14 @@
install-libs:
@echo "No libs to install."
-.sim.extracted: sim.tgz
- -chmod -R +w sim
- rm -rf sim
- rsync -av sim-tools/ sim
- tar xf sim.tgz -C sim
+.sim%.extracted: sim%.tgz
+ -chmod -R +w sim$*
+ rm -rf sim$*
+ rsync -av sim-tools/ sim$*
+ tar xf sim$*.tgz -C sim$*
touch $@
-test: .sim.extracted lint
+test: .sim1.extracted .sim2.extracted lint
set -e; \
for n in $(wildcard ./*_test.*); do \
echo; \
diff --git a/craftui/craftui b/craftui/craftui
index 527eba7..9d2a17a 100755
--- a/craftui/craftui
+++ b/craftui/craftui
@@ -7,15 +7,22 @@
# in developer environment if vendor/google/catawapus is above us
if [ -d "$devcw" ]; then
- isdev=1
+ sim=1
+fi
+
+if [ -n "$sim" ] && [ "$1" = "-2" ]; then
+ sim=2
+ shift
fi
# if running from developer desktop, use simulated data
-if [ "$isdev" = 1 ]; then
+if [ -n "$sim" ]; then
cw="$devcw"
- args="$args --http-port=8888 --https-port=8889 --sim=./sim"
+ args="$args --http-port=$((8888+2*($sim-1)))"
+ args="$args --https-port=$((8889+2*($sim-1)))"
+ args="$args --sim=./sim$sim"
pycode=./craftui_fortesting.py
- export PATH="$PWD/sim/bin:$PATH"
+ export PATH="$PWD/sim1/bin:$PATH"
fi
# for debugging on the device, use the local (/tmp/www?) web tree
@@ -32,13 +39,6 @@
continue
fi
- # enable https
- if [ "$1" = -S ]; then
- httpsmode="-S"
- shift
- continue
- fi
-
echo "$0: '$1': unknown command line option" >&2
exit 1
done
diff --git a/craftui/craftui.py b/craftui/craftui.py
index 10f5e91..9afe28d 100755
--- a/craftui/craftui.py
+++ b/craftui/craftui.py
@@ -262,7 +262,7 @@
print 'Glaukus: ', url, payload
try:
- fd = urllib2.urlopen(url, payload)
+ fd = urllib2.urlopen(url, payload, timeout=2)
except urllib2.URLError as ex:
print 'Connection to %s failed: %s' % (url, ex.reason)
raise ConfigError('failed to contact glaukus')
@@ -381,16 +381,18 @@
'tx_errors',
'tx_dropped'
]
- realm = 'gfch100'
def __init__(self, wwwroot, http_port, https_port, sim):
- """initialize."""
+ """Initialize."""
self.wwwroot = wwwroot
self.http_port = http_port
self.https_port = https_port
self.sim = sim
self.data = {}
self.data['refreshCount'] = 0
+ platform = self.ReadFile(sim + '/etc/platform')
+ serial = self.ReadFile(sim + '/etc/serial')
+ self.realm = '%s-%s' % (platform, serial)
def ApplyChanges(self, changes):
"""Apply changes to system."""
@@ -556,6 +558,7 @@
return response
def GetUserCreds(self, user):
+ """Create a dict with the requested password."""
if user not in ('admin', 'guest'):
return None
b64 = self.ReadFile('%s/config/settings/password_%s' % (self.sim, user))
@@ -583,32 +586,110 @@
"""Common class to add args to html template."""
auth = 'unset'
+ def IsProxy(self):
+ """Check if this request was proxied, (ie, we are the peer)."""
+ return self.request.headers.get('craftui-proxy', 0) == '1'
+
+ def IsPeer(self):
+ """Check args to see if this is a request for the peer."""
+ return self.get_argument('peer', default='0') == '1'
+
+ def IsHttps(self):
+ """See if https:// was used."""
+ return (self.request.protocol == 'https' or
+ self.request.headers.get('craftui-https', 0) == '1')
+
def TemplateArgs(self):
+ """Build template args to dynamically adjust html file."""
+ is_https = self.IsHttps()
+ is_proxy = self.IsProxy()
+
+ peer_arg = '?peer=1'
+
args = {}
- args['hidepeer'] = ''
- args['hidehttps'] = ''
- args['peer'] = ''
- if self.request.protocol is 'https':
- args['hidehttps'] = 'hidden'
- if self.get_argument('peer', default='0') == '1':
- args['peer'] = '?peer=1'
- args['hidepeer'] = 'hidden'
- print args
+ args['hidden_on_https'] = 'hidden' if is_https else ''
+ args['hidden_on_peer'] = 'hidden' if is_proxy else ''
+ args['shown_on_peer'] = 'hidden' if not is_proxy else ''
+ args['peer_arg'] = peer_arg
+ args['peer_arg_on_peer'] = peer_arg if is_proxy else ''
return args
- def get(self):
- ui = self.settings['ui']
- if self.auth is 'any':
- if not ui.Authenticate(self):
- return
- elif self.auth is 'admin':
- if not ui.AuthenticateAdmin(self):
- return
- elif self.auth is not 'none':
- raise Exception('unknown authentication type "%s"' % self.auth)
+ def TryProxy(self):
+ """Check if we should proxy this request to the peer."""
+ if not self.IsPeer() or self.IsProxy():
+ return False
+ self.Proxy()
+ return True
+ class ErrorHandler(urllib2.HTTPDefaultErrorHandler):
+ """Catch the error, don't raise exception."""
+ error = {}
+
+ def http_error_default(self, req, fd, code, msg, hdrs):
+ self.error = {
+ 'request': req,
+ 'fd': fd,
+ 'code': code,
+ 'msg': msg,
+ 'hdrs': hdrs
+ }
+
+ def Proxy(self):
+ """Proxy to the peer."""
+ ui = self.settings['ui']
+ r = self.request
+ cs = '/config/settings/'
+ peer_ipaddr = ui.ReadFile(ui.sim + cs + 'peer_ipaddr')
+ peer_ipaddr = re.sub(r'/\d+$', '', peer_ipaddr)
+ if ui.sim:
+ peer_ipaddr = 'localhost:8890'
+ url = 'http://' + peer_ipaddr + r.uri
+ print 'proxy: ', url
+
+ eh = self.ErrorHandler()
+ opener = urllib2.build_opener(eh)
+
+ body = None
+ if r.method == 'POST':
+ body = '' if r.body is None else r.body
+ req = urllib2.Request(url, body, r.headers)
+ req.add_header('CraftUI-Proxy', 1)
+ req.add_header('CraftUI-Https', int(self.IsHttps()))
+ fd = opener.open(req, timeout=2)
+ if eh.error:
+ fd = eh.error['fd']
+ self.set_status(eh.error['code'])
+ hdrs = eh.error['hdrs']
+ for h in hdrs:
+ v = hdrs.get(h)
+ self.set_header(h, v)
+
+ response = fd.read()
+ if response:
+ self.write(response)
+ self.finish()
+
+ def Authenticated(self):
+ """Authenticate the user per the required auth type."""
+ ui = self.settings['ui']
+ if self.auth == 'any':
+ if not ui.Authenticate(self):
+ return False
+ elif self.auth == 'admin':
+ if not ui.AuthenticateAdmin(self):
+ return False
+ elif self.auth != 'none':
+ raise Exception('unknown authentication type "%s"' % self.auth)
+ return True
+
+ def get(self):
+ if self.TryProxy():
+ return
+ if not self.Authenticated():
+ return
+ ui = self.settings['ui']
path = ui.wwwroot + '/' + self.page + '.thtml'
- print 'GET %s page' % self.page
+ print '%s %s page (%s)' % (self.request.method, self.page, ui.sim)
self.render(path, **self.TemplateArgs())
class WelcomeHandler(CraftHandler):
@@ -625,22 +706,29 @@
class JsonHandler(CraftHandler):
"""Provides JSON-formatted content to be displayed in the UI."""
+ page = 'json'
def get(self):
- ui = self.settings['ui']
- if not ui.Authenticate(self):
+ if self.TryProxy():
return
- print 'GET json data'
+ self.auth = 'any'
+ if not self.Authenticated():
+ return
+ ui = self.settings['ui']
+ print '%s %s page (%s)' % (self.request.method, self.page, ui.sim)
jsonstring = ui.GetData()
self.set_header('Content-Type', 'application/json')
self.write(jsonstring)
self.finish()
def post(self):
- ui = self.settings['ui']
- if not ui.AuthenticateAdmin(self):
+ if self.TryProxy():
return
- print 'POST JSON data for craft page'
+ self.auth = 'admin'
+ if not self.Authenticated():
+ return
+ ui = self.settings['ui']
+ print '%s %s page (%s)' % (self.request.method, self.page, ui.sim)
request = self.request.body
result = {}
result['error'] = 0
diff --git a/craftui/craftui_test.sh b/craftui/craftui_test.sh
index 5fc11ce..677f719 100755
--- a/craftui/craftui_test.sh
+++ b/craftui/craftui_test.sh
@@ -52,7 +52,7 @@
onexit() {
testname "process not running at exit"
- kill -0 $pid
+ kill -0 $pid1
check_failure
testname "end of script reached"
@@ -86,15 +86,19 @@
# add a signed cert (using our fake CA)
sh HOW.cert
- chmod 750 sim/tmp/ssl/*
- cp tmp-certs/localhost.pem sim/tmp/ssl/certs/craftui.pem
- cp tmp-certs/localhost.key sim/tmp/ssl/private/craftui.key
+ for n in sim1 sim2; do
+ chmod 750 $n/tmp/ssl/*
+ cp tmp-certs/localhost.pem $n/tmp/ssl/certs/craftui.pem
+ cp tmp-certs/localhost.key $n/tmp/ssl/private/craftui.key
+ done
./craftui &
- pid=$!
+ pid1=$!
+ ./craftui -2 &
+ pid2=$!
testname "process running"
- kill -0 $pid
+ kill -0 $pid1
check_success
sleep 1
@@ -225,15 +229,36 @@
$curl $admin_auth -d "$d" $url/content.json |& grep '"error": 1}'
check_success
+ testname proxy read from peer
+ $curl $admin_auth $url/content.json'?peer=1' |& grep '"platform": "GFCH100"'
+ check_success
+
+ testname proxy write to peer
+ d='{"config":[{"peer_ipaddr":"192.168.99.99/24"}]}'
+ $curl $admin_auth -d $d $url/content.json'?peer=1' |& grep '"error": 0}'
+ check_success
+
+ done
+
+ # verify insecure message is hidden on https and not on http
+ for peer in '' '?peer=1'; do
+ testname http warning $peer
+ $curl http://localhost:$http$peer |& grep 'hidden_on_https value=""'
+ check_success
+
+ testname no https warning $peer
+ $curl https://localhost:$https$peer |& grep 'hidden_on_https value="hidden"'
+ check_success
done
testname "process still running at end of test sequence"
- kill -0 $pid
+ kill -0 $pid1
check_success
# cleanup
t0=$(date +%s)
- kill $pid
+ kill $pid1
+ kill $pid2
wait
t1=$(date +%s)
dt=$((t1 - t0))
diff --git a/craftui/sim.tgz b/craftui/sim.tgz
deleted file mode 100644
index d50297f..0000000
--- a/craftui/sim.tgz
+++ /dev/null
Binary files differ
diff --git a/craftui/sim1.tgz b/craftui/sim1.tgz
new file mode 100644
index 0000000..04f1d51
--- /dev/null
+++ b/craftui/sim1.tgz
Binary files differ
diff --git a/craftui/sim2.tgz b/craftui/sim2.tgz
new file mode 100644
index 0000000..14e5547
--- /dev/null
+++ b/craftui/sim2.tgz
Binary files differ
diff --git a/craftui/www/config.thtml b/craftui/www/config.thtml
index 1227ad0..7d95d8a 100644
--- a/craftui/www/config.thtml
+++ b/craftui/www/config.thtml
@@ -14,16 +14,23 @@
<h1><img src=static/logo.png alt="Google Fiber"></h1>
<nav>
<ul>
- <li ><a href=/{{peer}}>Welcome</a></li>
- <li ><a href=/status{{peer}}>Status</a></li>
- <li class=active><a href=/config{{peer}}>Configuration</a></li>
- <li ><a {{hidepeer}} href="/?peer=1" target=_blank>Peer</a></li>
+ <li ><a href=/{{peer_arg_on_peer}}>Welcome</a></li>
+ <li ><a href=/status{{peer_arg_on_peer}}>Status</a></li>
+ <li class=active><a href=/config{{peer_arg_on_peer}}>Configuration</a></li>
+ <li ><a {{hidden_on_peer}} href="/{{peer_arg}}" target=_blank>Peer</a></li>
</ul>
</nav>
</section>
</header>
<br>
-
+ <div hidden>
+ <input id=hidden_on_https value="{{hidden_on_https}}">
+ <input id=hidden_on_peer value="{{hidden_on_peer}}">
+ <input id=shown_on_peer value="{{shown_on_peer}}">
+ <input id=peer_arg value="{{peer_arg}}">
+ <input id=peer_arg_on_peer value="{{peer_arg_on_peer}}">
+ </div>
+ <div {{shown_on_peer}}><font color="red"><b>This is the Peer</b></font></div>
<div class="tabs">
<div class="tab">
<input type="radio" id="tab-1" name="tab-group-1" checked>
diff --git a/craftui/www/static/craft.js b/craftui/www/static/craft.js
index fe7d9bc..7b539ba 100644
--- a/craftui/www/static/craft.js
+++ b/craftui/www/static/craft.js
@@ -64,6 +64,7 @@
if (CraftUI.am_sending) {
return;
}
+ var peer_arg_on_peer = document.getElementById("peer_arg_on_peer").value;
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
self.unhandled = '';
@@ -74,20 +75,18 @@
CraftUI.updateField('unhandled', self.unhandled);
CraftUI.am_sending = false
};
- var payload = [];
- payload.push('checksum=' + encodeURIComponent(CraftUI.info.checksum));
- payload.push('_=' + encodeURIComponent((new Date()).getTime()));
- xhr.open('get', 'content.json?' + payload.join('&'), true);
+ xhr.open('get', '/content.json' + peer_arg_on_peer, true);
CraftUI.am_sending = true
xhr.send();
};
CraftUI.config = function(key, activate, is_password) {
// POST as json
+ var peer_arg_on_peer = document.getElementById("peer_arg_on_peer").value;
var el = document.getElementById(key);
var xhr = new XMLHttpRequest();
var action = "Configured";
- xhr.open('post', 'content.json');
+ xhr.open('post', '/content.json' + peer_arg_on_peer);
xhr.setRequestHeader('Content-Type', 'application/json; charset=UTF-8');
var data;
if (is_password) {
diff --git a/craftui/www/status.thtml b/craftui/www/status.thtml
index 28b5027..adb4673 100644
--- a/craftui/www/status.thtml
+++ b/craftui/www/status.thtml
@@ -14,15 +14,23 @@
<h1><img src=static/logo.png alt="Google Fiber"></h1>
<nav>
<ul>
- <li ><a href=/?{{peer}}>Welcome</a></li>
- <li class=active><a href=/status{{peer}}>Status</a></li>
- <li ><a href=/config{{peer}}>Configuration</a></li>
- <li ><a {{hidepeer}} href="/?peer=1" target=_blank>Peer</a></li>
+ <li ><a href=/{{peer_arg_on_peer}}>Welcome</a></li>
+ <li class=active><a href=/status{{peer_arg_on_peer}}>Status</a></li>
+ <li ><a href=/config{{peer_arg_on_peer}}>Configuration</a></li>
+ <li ><a {{hidden_on_peer}} href="/{{peer_arg}}" target=_blank>Peer</a></li>
</ul>
</nav>
</section>
</header>
<br>
+ <div hidden>
+ <input id=hidden_on_https value="{{hidden_on_https}}">
+ <input id=hidden_on_peer value="{{hidden_on_peer}}">
+ <input id=shown_on_peer value="{{shown_on_peer}}">
+ <input id=peer_arg value="{{peer_arg}}">
+ <input id=peer_arg_on_peer value="{{peer_arg_on_peer}}">
+ </div>
+ <div {{shown_on_peer}}><font color="red"><b>This is the Peer</b></font></div>
<div class="tabs">
<div class="tab">
<input type="radio" id="tab-1" name="tab-group-1" checked>
diff --git a/craftui/www/welcome.thtml b/craftui/www/welcome.thtml
index de7c098..43966ff 100644
--- a/craftui/www/welcome.thtml
+++ b/craftui/www/welcome.thtml
@@ -14,21 +14,29 @@
<h1><img src=static/logo.png alt="Google Fiber"></h1>
<nav>
<ul>
- <li class=active><a href=/{{peer}}>Welcome</a></li>
- <li ><a href=/status{{peer}}>Status</a></li>
- <li ><a href=/config{{peer}}>Configuration</a></li>
- <li ><a {{hidepeer}} href="/?peer=1" target=_blank>Peer</a></li>
+ <li class=active><a href=/{{peer_arg_on_peer}}>Welcome</a></li>
+ <li ><a href=/status{{peer_arg_on_peer}}>Status</a></li>
+ <li ><a href=/config{{peer_arg_on_peer}}>Configuration</a></li>
+ <li ><a {{hidden_on_peer}} href="/{{peer_arg}}" target=_blank>Peer</a></li>
</ul>
</nav>
</section>
</header>
<br>
+ <div hidden>
+ <input id=hidden_on_https value="{{hidden_on_https}}">
+ <input id=hidden_on_peer value="{{hidden_on_peer}}">
+ <input id=shown_on_peer value="{{shown_on_peer}}">
+ <input id=peer_arg value="{{peer_arg}}">
+ <input id=peer_arg_on_peer value="{{peer_arg_on_peer}}">
+ </div>
+ <div {{shown_on_peer}}><font color="red"><b>This is the Peer</b></font></div>
<div class="tabs">
<div class="tab">
<input type="radio" id="tab-1" name="tab-group-1" checked>
<label for="tab-1">Authorized Use Only</label>
<div class="content">
- <div {{hidehttps}}>
+ <div {{hidden_on_https}}>
<b>
Warning: You are not connected securely. Consider https://...
<br>
@@ -40,9 +48,6 @@
<p>
Unauthorized access to this system is forbidden and will be
prosecuted by law.
- <br>
- By accessing this system, you agree that your actions may be
- monitored if unauthorized usage is suspected.
</div>
</div>
</div>