Commit 35071d72 authored by James Vasile's avatar James Vasile

...

parents
*.pyc
*.tiny.css
data/*.log
data/cherrypy_sessions
data/store.sqlite3
doc/*.tex
doc/*.pdf
doc/*.html
doc/*.aux
doc/*.toc
doc/*.log
doc/*.out
doc/COPYING.mdwn
doc/INSTALL.mdwn
doc/README.mdwn
doc/TODO.mdwn
doc/oneline.txt
doc/plinth.1
templates/*.py
TODO
\ No newline at end of file
This diff is collapsed.
# Quick Start
## Installing Plinth
Unzip and untar the source into a directory. Change to the directory
containing the program. Run `./plinth.py` and point your web
browser at `localhost:8000`. The default username is "admin" and the
default password is "secret".
## Dependencies
* cherrypy - python web engine
* python - tested with version 2.6.6
* *GNU Make* is used to build the templates and such.
The documentation has some dependencies too.
* *Markdown* is used to format and style docs.
* *Pandoc* converts the markdown to different formats.
* *PDFLatex* generates pdf versions of the documentation.
* *GNU Make* processes /doc/Makefile.
## Building the Documentation
Documentation has been collected into a pdf that can be built using
`make doc`. It also gets built into smaller files and other formats,
including one suitable for install as a man page.
<a name="installing_systemwide" />
MAKE=make
#TODO: add install target
CSS=$(wildcard *.css)
CSS=$(subst .tiny,,$(shell find themes -type f -name '*.css'))
COMPRESSED_CSS := $(patsubst %.css,%.tiny.css,$(CSS))
## Catch-all tagets
default: template docs css
all: default
%.tiny.css: %.css
@cat $< | python -c 'import re,sys;print re.sub("\s*([{};,:])\s*", "\\1", re.sub("/\*.*?\*/", "", re.sub("\s+", " ", sys.stdin.read())))' > $@
css: $(COMPRESSED_CSS)
template:
@$(MAKE) -s -C templates
templates: template
docs:
@$(MAKE) -s -C doc
doc: docs
html:
@$(MAKE) -s -C doc html
clean:
@find themes -name "*.tiny.css" -exec rm {} \;
@find . -name "*~" -exec rm {} \;
@find . -name ".#*" -exec rm {} \;
@find . -name "#*" -exec rm {} \;
@find . -name "*.pyc" -exec rm {} \;
@find . -name "*.bak" -exec rm {} \;
@$(MAKE) -s -C doc clean
@$(MAKE) -s -C templates clean
% PLINTH(1) Version 0.1 | Plinth User Manual
%
% February 2011
# Introduction
## Name
plinth - a web front end for administering every aspect of a Freedom Box.
## Synopsis
plinth.py
## Description
The Freedom Box is a net appliance conceived by Eben Moglen. It
contains free software and is designed to allow you to interface with
the rest of the net under conditions of protected privacy and data
security.
The Plinth front end is a web interface to administer the functions of
the Freedom Box. For example, the Freedom Box is a wireless router,
and the front end is where you can adjust its settings.
## Overview
The front end is an extensible web platform for forms and menus. It
allows authenticated users to fill out forms. The interface saves the
form data and from them generates configuration files for the various
services running on the box.
The interface is extremely pluggable. Drop modules into place to add
new capabilities to Plinth and your Freedom Box. Replace existing modules to get
newer, better shinier functions. The modules will automatically
integrate into the existing menu system so you can control all of the
box's parts from one central location.
The interface has a 'basic' and an 'expert' mode. In basic mode, much
of Plinth's configuration and capability are hidden. Sane defaults
are chosen whenever possible. In expert mode, you can get down into
the details and configure things the average user never thinks about.
For example, experts can turn off ntp or switch ntp servers. Basic
users should never even know those options exist.
One caveat: expert mode is not as powerful as the commandline. We can
only do so much when translating free-form configuration files into
web forms. And if you manually change your configuration files, the
interface will overwrite those changes at first opportunity. This
interface is not a tool for super admins facing complex scenarios. It
is for home users to do a wide variety of basic tasks.
from menu import Menu
import os
file_root = os.path.dirname(os.path.realpath(__file__))
data_dir = os.path.join(file_root, "data")
store_file = os.path.join(data_dir, "store.sqlite3")
status_log_file = os.path.join(data_dir, "status.log")
access_log_file = os.path.join(data_dir, "access.log")
users_dir = os.path.join(data_dir, "users")
product_name = "Plinth"
box_name = "Freedom Plug"
html_root = None
main_menu = Menu()
[global]
server.socket_host = '0.0.0.0'
server.socket_port = 8000
server.thread_pool = 10
tools.staticdir.root = "/home/vasile/src/plinth"
tools.sessions.on = True
tools.auth.on = True
tools.sessions.storage_type = "file"
tools.sessions.storage_path = "/home/vasile/src/plinth/data/cherrypy_sessions"
tools.sessions.timeout = 90
[/static]
tools.staticdir.on = True
tools.staticdir.dir = "static"
[/favicon.ico]
tools.staticfile.on = True
tools.staticfile.filename = "/home/vasile/src/plinth/static/theme/favicon.ico"
{"username": "admin", "name": "Admin", "expert": "on", "groups": ["expert"], "passphrase": "5ebe2294ecd0e0f08eab7690d2a6ee69", "password": "", "email": "root@localhost"}
\ No newline at end of file
DOCDIR=../dist/doc
PANDOC=pandoc
PDFLATEX=pdflatex
# List text files in the order in which you want them to appear in the
# complete manual:
SOURCES=README.mdwn INSTALL.mdwn themes.mdwn hacking.mdwn TODO.mdwn modules.mdwn scripts.mdwn design.mdwn roadmap.mdwn faq.mdwn COPYING.mdwn colophon.mdwn
TODO_SOURCES=$(patsubst TODO.mdwn,,$(SOURCES))
MAN_SOURCES=$(patsubst COPYING.mdwn,copyright_notice00,$(SOURCES))
NEWLINE_SOURCES=$(patsubst %,% oneline.txt,$(SOURCES))
NEWLINE_MAN_SOURCES=$(patsubst %,% oneline.txt,$(MAN_SOURCES))
HTML=plinth.html $(patsubst %.mdwn,%.html,$(SOURCES))
HTML_PART=$(patsubst %.html,%.part.html,$(HTML))
LATEX=plinth.tex $(patsubst %.mdwn,%.tex,$(SOURCES))
PDF=plinth.pdf $(patsubst %.mdwn,%.pdf,$(SOURCES))
MAN=plinth.1
OUTPUTS=$(HTML) $(LATEX) $(MAN) $(PDF) $(HTML_PART)
DIST_OUTPUT=$(patsubst %,$(DOCDIR)/%,$(OUTPUTS))
# Yes, do it twice. TODO created during the process requires a second run
default:
make all
make all
all: oneline.txt $(OUTPUTS) Makefile
$(DOCDIR)/%: %
cp $< $@
dist: $(DIST_OUTPUT)
###############################################################################
oneline.txt: Makefile
perl -e 'print "\n"' > oneline.txt
$(SOURCES):
@rm -f $@
@ln -s ../$(patsubst %.mdwn,%,$@) $@
../TODO : $(TODO_SOURCES) ../*.py ../modules/*.py ../Makefile Makefile ../templates/Makefile
grep -ro --exclude=.git* --exclude=plinth.1 --exclude=*.tex --exclude=*.html \
--exclude=README.mdwn --exclude=INSTALL.mdwn \
--exclude=TODO.mdwn --exclude=COPYING.mdwn \
"TODO\:.*" ../* 2>/dev/null | \
sed -e "s/TODO\://g" | \
sed -e "s/^..\//* /g" | \
sed -e 's/"""$$//g' | \
sed -e 's/<\/p>$$//g' \
> ../TODO
###############################################################################
##
## MAN PAGES
##
$(MAN): $(SOURCES) ../TODO
@csplit -s -f copyright_notice COPYING.mdwn '/##/'
cat $(NEWLINE_MAN_SOURCES) | perl -pe 'BEGIN { $$/=undef } $$_ =~ s/\n\n#\s.*/\n/gm; $$_ =~ s/\n\n#/\n\n/gm; $$_ =~ s/(\n\n#\s.*)/uc($$1)/gme' > .make_man
$(PANDOC) -s -t man -o $@ .make_man
@rm -f copyright_notice0? .make_man
manpages: $(MAN)
###############################################################################
##
## LaTeX
##
%.tex: %.mdwn
$(PANDOC) -s --toc -f markdown --standalone -o $@ $<
hacking.tex: hacking.mdwn ../TODO
$(PANDOC) -s --toc -f markdown -o $@ hacking.mdwn ../TODO
plinth.tex: $(NEWLINE_SOURCES) ../TODO
$(PANDOC) -s --toc -f markdown -o $@ $(NEWLINE_SOURCES)
latex: $(LATEX)
###############################################################################
##
## HTML
##
# This gets us the html sections complete with TOC, but without the
# HTML and head section boilerplate. /help/view uses the parts.
%.part.html: %.html
csplit -s -f $@ $< '%.*<body>%'
sed '1d' $@00 > $@01
csplit -s -f $@ $@01 '/<\/body>/'
mv $@00 $@
rm $@01
%.html: %.mdwn header.html footer.html style.css Makefile
$(PANDOC) -s --toc -c style.css -f markdown -o $@ header.html $< footer.html
hacking.html: hacking.mdwn ../TODO style.css Makefile
$(PANDOC) -s --toc -c style.css -o $@ -f markdown hacking.mdwn ../TODO
#plinth.html: $(NEWLINE_SOURCES) ../TODO style.css Makefile
# $(PANDOC) -s --toc -c style.css -o $@ -f markdown $(NEWLINE_SOURCES)
plinth.html: $(NEWLINE_SOURCES) ../TODO style.css Makefile
@csplit -s -f copyright_notice COPYING.mdwn '/##/'
$(PANDOC) -s --toc -c style.css -o $@ -f markdown $(NEWLINE_MAN_SOURCES)
@rm -f copyright_notice0? .make_man
html: $(HTML) $(HTML_PART)
###############################################################################
%.pdf: %.tex
$(PDFLATEX) -interaction=batchmode $< >/dev/null
$(PDFLATEX) -interaction=batchmode $< >/dev/null # yes, do it twice so the toc works
pdf: $(PDF)
###############################################################################
clean-latex:
rm -f *.log *.out *.aux *.toc
clean: clean-latex
rm -f $(OUTPUTS) README.mdwn INSTALL.mdwn TODO.mdwn COPYING.mdwn \
copyright_notice0? \#*\# ../TODO oneline.txt
# Colophon
This manual was typed in emacs, formatted using markdown and converted
to pdf, html, troff and latex using pandoc and pdflatex.
The complete source code to this manual is available in the 'doc'
directory of the Freedom Box front end source distribution.
# Freedom Box Design and Architecture
This article describes and documents architectural considerations for
my vision of Freedom Box. It is not specific to the user interface
and instead contains thoughts about the Freedom Box as a whole.
The major immediate design problems that I see are authentication, ip
addressing, and backup. I'm sure there are others.
If the Freedom Box front end pulls together basic pieces and
configures them properly, we'll have the start of the freedom stack.
Then we can build things like free social networking applications on
top.
## Design Goals
The target hardware for this project is the
[GuruPlug](http://www.globalscaletechnologies.com/t-guruplugdetails.aspx).
It has low power consumption, two wired network interfaces, one
wireless hostapd-capable interface (802.11b), and two usb slots for
connecting external drives and peripherals. The hardware target might
change if a better unit becomes available (for example, with 802.11n
or USB 3 capability). The GuruPlug is reputed to have availability
problems involving shipping delays, and targetting a unit that cannot
satsify high demand might be an issue.
Freedom Boxes are not giant honking servers. They are small boxes
that do perform a lot of functions. They are not heavily resourced.
So keep things small and light.
By the same token, there is no need to scale up to thousands of users.
A box should happily serve a person and her family, maybe an extended
group of friends. Call it 100 people at the most.
The target user for this project is the home consumer of network
appliances. It ranges from the most basic user (the kind of person
who might have spent all of three minutes setting up her LinkSys
WRT54G) to the home enthusiast who wants a local file server for media
and backups, print server and dns service.
## Authentication
Authentication in the context of the Freedom Box is a diffiult
problem. You need to be able to trust your box, and it needs to be
able to trust you. What's more, security must withstand forgotten
passwords, server destruction, changing email addresses and any other
possible disaster scenario.
In addition, your friends (and their boxes) need to trust your box
when it acts on your behalf, even if it does so when you're not around
to enter passphrases or otherwise confirm agency. But even as it
needs to operate in your absence, it can't itself have authority to
access all your data, because you might lack exclusive physical
control of the box (e.g. if you have a roommate). If the box can act
as you without limits, anybody who takes control of the box takes
control of your online identity.
What's more, security should be high. Freedom Boxes might contain
emails or other sensitive documents. The box mediates network
traffic, which means rooting it would allow an attacker to spy on or
even change traffic. Demonstrating control of an email address should
not be enough to gain access to the box and its admin functions.
### Passphrases
Most users habitually think of passwords. We need to force them to
think of passphrases. It starts by using 'passphrase' in the
literature. Second, we need to encourage users to pick phrases, and
prompts should urge them to do so. We also need minimum password
lengths and maybe even a requirement of at least a few spaces.
Even better than passphrases are passfiles. We should keep a lookout
for ways to use files instead of phrases when securing the Freedom
Box.
### A Scheme for Secure Web Login
Passphrases should
[never be stored in plain text](http://www.codinghorror.com/blog/2007/09/rainbow-hash-cracking.html).
[MD5 is too fast for passphrases.](http://chargen.matasano.com/chargen/2007/9/7/enough-with-the-rainbow-tables-what-you-need-to-know-about-s.html)
Therefore, my current plan for secure website login involves bcrypt:
The server sends the client a permanent salt, PS, a random session
salt, SS and a number of rounds, R. It brcypts the password using PS
and stores the result, B.
The browser
[bcrypts the passphrase using javascript](https://code.google.com/p/javascript-bcrypt/)
and PS. Then, it bcrypts that result with SS. It does R rounds of
bcrypt with S. The browser then sends the result, C, back to the
server.
The server retrieves bcrypts its stored, hashed passphrase and does R
rounds of bcrypt on it with SS. If that matches C, the passphrase is
a match.
The server must be able to keep track of sessions, as it needs to
remember SS between giving it to the client and getting it back. The
Server cannot rely on the client to remind it of SS. This would allow
an attacker to dictate SS and leave the server vulnerable to replay
attacks.
This scheme has the dual advantage of not storing any cleartext
passphrases and also never sending any cleartext passphrases between
client and server.
TODO: consult a security expert as to the strength of this scheme
### Other Schemes
#### Monkeysphere
[Monkeysphere](http://web.monkeysphere.info/) is a promising project.
Monkeysphere's major win is that it avoids the clunkiness of SSL
certificate authorities. That's huge, except it replaces the
certificate authority structure with the PGP web of trust. Relying on
the web of trust is a good idea
([much better than relying on SSL certificate authorities](http://www.crypto.com/blog/spycerts/)),
except that new users might not have joined the web. It also requires
a bunch of GPG infrastructure that might be difficult to setup
automatically and with little user intervention.
As of this writing, it does not appear to support Windows or Macintosh
clients, making it unsuitable for this project.
#### Secure Remote Password
[SRP](http://srp.stanford.edu/) is an interesting technique. Patents
are a concern. What does it buy us that my scheme, above does not?
There is a
[python implementation](http://members.tripod.com/professor_tom/archives/srpsocket.html),
although it is not clear that it has been through the crucible. Even
if SRP is solid, this implementation might be flawed.
## Finding Each Other
### Dynamic DNS
Each box might need multiple dynamic DNS pointers. Every box is going
to require its own set of dynamic names. For example, my Freedom Box
might be known as both `fb.hackervisions.org` and
`james.softwarefreedom.org`. My work colleagues might know me as
`james@james.softwarefreedom.org` while my friends might reach me at
`james@fb.hackervisions.org`. By default, when contacts come in via
different names, my box might be smart enough to treat those contacts
differently.
If I share this box with my partner, Emily, she might be
`emily@jv187.fboxes.columbia.edu`, in which case my box will need
another dynamic DNS pointer.
### Mesh Networking
Freedom Boxes should be meshed. Rather than run in infrastructure
mode, they should route for each other and create strong local meshes
that will carry data for anybody that passes through.
There are some problems, though.
* I'm not sure how well nodes in ad-hoc mesh mode interact with
others in infrastructure mode.
* The Freedom Box only has one wireless radio, which will drastically
hurt speed.
* Routing can be difficult when connecting mesh network nodes to the WAN.
## Backup
Everybody should automatically and redundantly store encrypted backups
on their friend's freedom boxes. Recovery should not assume you kept
your key or know a password. We can recover the decryption key by
having a bunch of friends encrypt the key in various combinations. If
enough of my friends get together, they can decrypt it for me.
Optionally, a passphrase can serve to slow my friends down if they
turn against me. Maybe it takes 5 friends acting in concert to
recover my key, but only 3 to recover a version protected by the
passphrase.
## Push vs Pull
For a social network, real time communication is key. Asynch
communication is good, but sometimes people just want to bash comments
back and forth. For that, you need push, which is just a web fetch
meaning "pull my rss feed".
If, however, you have a lot of friends and they have a lot of friends,
your extended network can be large, resulting in many "pull my rss
feed" commands. We should only process push requests for friends to
reduce load. You really don't need real time updates of the social
networking activity of strangers. Friends of friends is as far out as
things should ever go.
# Plinth and Freedom Plug FAQ
## General Questions
### What is the Freedom Plug?
The Freedom Plug is .... insert links...
The Freedom Plug is based on the GNU/Debian operating system. It is
not a Linux distribution. It is a network appliance that depends on a
series of Debian packages that configure a plug computer to behave as
a Freedom Plug.
### What is Plinth?
Plinth is the web-based GUI administration front end for the Freedom Plug.
### On what hardware is the Freedom Plug based?
The current targets are the [Guru Plug](http://guruplug) and the
[Dream Plug](http://dreamplug).
## Accessing the Freedom Plug
### Why does ssh listen on port 2222 instead of 22?
If ssh listens on port 2222, bots and scripts will forever attempt to
guess your username and password. Maybe your password isn't so
strong. Maybe the bots get lucky. Either way, if you allow ssh
access on port 22, you're taking a chance that can be avoided quite
easily by moving your ssh activity to a slightly more obscure port.
Because ssh activity on these boxes will be limited to programs
configured to work specifically with Freedom Plugs as well relatively
few people generally using ssh, the coordination necessary to use a
non-standard port is easily achieved.
# Hacking
This codebase could really use a testing framework.
If you are interested in helping out, writing tests is a great place
to start-- you don't need to know much about the code or python to
write useful tests.
In addition to a testing framework, the code is in need of some
general cleanup. I've been inconsistent in capitalization conventions
as well as in my use of underscores.
The plugin interface could use some attention as well. Right now,
it's a a bit of a free-for-all until I see how the plugins actually
code up. Channeling all that into a few plugin interface grooves
would be a help.
If you're feeling more ambitious than that, the best way to improve
Plinth is to add modules. More functionality, especially in the
router section, could convert the Freedom Box from a good idea to a
must-have appliance.
Beyond that, we need to train some expert eyes on the interaction
between Freedom Boxes. Transparent, zero-config box-to-box backup is
possible. We just need to build an auth and dns layer. There are
lots of theories on how to do this well. The first theory reduced to
practice wins!
There is a list of TODO items below. Some of them are discrete pieces
that can be tackled without diving too deep into the code.
## Repository
Plinth is available from github at
`git://github.com/jvasile/plinth.git`. The [project page on
github](https://github.com/jvasile/plinth) is at
`https://github.com/jvasile/plinth`.
## Bugs
There are lots of bugs. We don't have a spec or tests, so a bug is
really just any unexpected behavior. I am not easily surprised, but
there are still lots of bugs.
<a name="hacking_code_practices" />
## Coding Practices
I try to stick to [PEP 8](http://www.python.org/dev/peps/pep-0008/)
recommendations. That's not to say I don't deviate, just that
deviations are usually bugs that should be fixed.
### Internationalization
Every module should `from gettext import gettext as _` and wrap
displayed strings with _(). We don't have the language stuff in place
yet (we have no translation files), but we need to put the
infrastructure in place for it from the start. Use it like this:
cfg.log.error(_("Couldn't import %s: %s") % (path, e))
### Variables and Data Stores
Plinth needs to keep information for short and long term
future use, and it can't just store all of that on the stack.
Global config information can be put in the `cfg` module namespace.
Keep it thread and session safe, though, or you'll get undefined
behavior as soon as multiple simultaneous users enter the picture.
Cherrpy has support for session variables. Use those for short term
user-specific data.
For long term storage, the Plinth needs a back end
storage solution. Databases are a bit opaque and can be hard for
third party software or shell users to manipulate. For now, I've
decided that persistent data should be placed in dicts and stored in
json format. We'll need a file locking solution too.
The `user_store.py` module implements the `UserStoreModule` interface
specified in `plugin_mount.py`. Any new system that respects that
interface can be used. The existing `user_store.py` holds entire user
files in memory and caches user files as it goes. This has two
downsides: first, if you have lots of users and store big things in
the user dict, you'll run out of memory. Second, it's not thread
safe. Maybe a database is a good idea after all.
We do not yet have a means of storing module data for long terms. My
current thinking is that modules can store data in their own
directories. That makes removal easy.
## Todo
Plinth has a number of open todo items. Please help!
* Implement the functions in the submenus of router.py
* Unify our logging and cherrypy's.
# Modules
Almost all of the front end's functionality is contained in small,
separate modules that reside in the directory tree beneath
`/modules/installed`. Some are installed by default, some are
required for operation, and some are entirely optional.
## Installing and Loading Modules
Eventually, the goal is for module to be separate Debian package so
installation is as simple as `aptitude install freedombox-foo`. As an
intermediate step, we'll start scripting module installation. But
until then, we can install them manually.
Modules are installed by copying them into the tree beneath the
`/modules/installed` directory. They are activated by linking their
.py files into the modules directory. (Freedom Box will not load
modules unless they are symlinks.) If the module provides other
resources, they can be linked from wherever in the working tree they
need to be. So if a module provides a template, that template can be
linked from `/templates`.
Modules can be organized into subdirectories beneath the installed
directory. As an initial matter, I've made separate directories for
each major tab. Modules that extend the functionality of the Freedom
Box code base (as opposed to being user-visible interface modules
under specific major tabs) go in `modules/installed/lib`. This
convention can be adjusted or ignored as needed. In fact, modules can
be installed anywhere in the file system as long as the symlinks are
in `/modules`.
The names of the symlinks in the `modules` directory can be arbitrary,
and if a name is already taken, (e.g. if `router/info.py` and
`apps/info.p`y both exist), append a counter to the end (e.g. link
`info.py` to `router/info.py` and `info1.py` to `apps/info.py`).
If a module cannot be imported for any reason (e.g. it's a dead
symlink), freedombox.py will log an error but will otherwise just keep
going.
TODO: automatically prune dead links to clear out old module installs.
This is something the install scripts should do.
## Pluggable Module Structure
Plugin interfaces are contained in `plugin_mount.py`. Each interface
is a metaclass. Classes that implement a given interface should
inherit the metaclass and then provide the indicated properties. New
metaclasses can be created to make new classes of plugins.
Any place that might be affected by arbitrary addition of modules
should have its own metaclass.
### FromPlugin
Each form displayed in the interface should inherit from FormPlugin.
This will allow the system to display multiple forms on the page in
the correct order. Forms will likely also want to inherit from
PagePlugin because after the user clicks submit, the plugin usually
displays a responsive message or an error (along with the form to fill
out again).
## Coding Practices for Modules
All the
[coding practices for hacking on the front end](#hacking_code_practices)
also apply to modules. In addition, I try to stick to the other
practices listed in this section.
* Every module should `from gettext import gettext as _` and wrap
displayed strings with _().
# Plinth Roadmap
## 0.1 Basic Wireless Access Point
* <strike>Basic/Expert mode toggle</strike>
* SSH access
* Connect to WAN via DHCP and static IP
* Offer DHCP on wired and wireless interfaces
* WEP
* WPA2
* Bridge WAN and LAN
## 0.2 Basic Wireless Router
* NAT
* DMZ
* Port forwarding and triggering
* dhcp server (on by default, expert can disable)
* dns server
* MAC address filtering
* NTP client
## 0.3 Advanced Wireless Router
* dynamic dns - This is special. See the notes in the section on
[dynamic DNS](#dynamic-dns) for details.
* UPnP
* Cron
* Tx power management
## 0.4 File Server, More Routing Features
* boxbackupd
* NFS
* Samba
* Auto mount usb volumes and create samba shares for them
* TOR