[lang]

Present Perfect

Personal
Projects
Packages
Patches
Presents
Linux

Picture Gallery
Present Perfect

Getting Things Done with CouchDB, part 3: Security in mushin

Filed under: couchdb,General,Hacking,Python — Thomas @ 23:26

2012-09-16
23:26

After piecing together the security story of CouchDB as it applies to mushin, I secured the mushin database on various machines. This serves as a quick setup guide for security for mushin, but I think it's useful for other people using CouchDB.

Stop using Admin Party

This is easy to do in Futon (link only works if you run couchdb locally on port 5984, as per default). Jan's blog post explains it perfectly, including screenshots.

Under the hood, couchdb will actually rewrite your local.ini file to add this user - all admin users are stored in the config files. (I'm sure there's an obvious reason for that)

Given that you most likely will use this password in Futon, make sure you pick a unique password - as far as I can tell this password goes over the wire.

Create a user object for your user

explains the basics. You need to create or update the _users database, which is a special couchdb database. You can get to it in Futon. If, like most people, you're still on a couchdb before 1.2.0, you have to fiddle yourself to calculate the password_sha field, but at least the page explains how to do it. Not the most user-friendly thing to do in the world, so I'm considering adding commands for this to a different application I'm working on.

Allow this user to read and write to the mushin database

Again, the best reference is the CouchDB wiki, but the information is easy to miss.
Every database has a _security object under the database name; in the case of mushin, you can get to it in Futon. _security is a special document that does not get versioned, and doesn't show up in listings either. In fact, it is so special that Futon doesn't let you change it; when you click save it just resets. So your only option is to PUT the document, for example:

curl -X PUT -d @security.json http://admin:sup3rs3kr3t@localhost:5984/mushin/_security

Oops, see what I did there ? I had to specify my admin password on the command line, and now it's in my shell history. I did tell you to choose a unique one because it's going to be all over the place, didn't I ?

security.json is just the contents of the _security document; just adapt the example on the wiki, and put your user under readers, and leave the role empty for now.

test denial

This one is simple; just try to GET the database:

$ curl http://localhost:5984/mushin
{"error":"unauthorized","reason":"You are not authorized to access this db."}

If you did it right, you should see the same error. If you're brave, you can retry the same curl command, but add your username and password. But you know how we feel about that.

Getting Things Done with CouchDB, part 2: Security

Filed under: couchdb,Hacking,mushin,Python — Thomas @ 16:26

2012-09-15
16:26

(... wherein I disappoint the readers who were planning to drop my database)

So, I assume you now know what mushin is.

The goal for my nine/eleven hack day was simply to add authentication everywhere to mushin, making it as user-friendly as possible, and as secure as couchdb was going to let me.

Now, CouchDB's security story has always been a little confusing to me. It's gotten better over the years, though, so it was time to revisit it and see how far we could get.

By default, CouchDB listens only on localhost, uses plaintext HTTP, and is in Admin Party mode. Which means, anyone is an admin, and anyone who can do a request on localhost can create and delete databases or documents. This is really useful for playing around with CouchDB, learning its REST API using curl. So easy in fact that it's hard to go away from that simplicity (I would not be surprised to find that there are companies out there running couchdb on localhost and unprotected).

Authorization

What can users do ? What type of users are there ?

In a nutshell, couchdb has three levels of permissions:

  • server admin: can do anything; create, delete databases, replicate, ... Is server wide. Think of it as root for couchdb.
  • database admin: can do anything to a database; including changing design documents
  • database reader: can read documents from the database, and (confusingly) write normal documents, but not design documents

CouchDB: The Definitive Guide sadly only mentions the server admin and admin party, and is not as definitive as its title suggests. A slightly better reference is (although I still haven't cleared up what roles are to be used for, beside the internal _admin role).
By far the clearest explanation of security-related concepts in CouchDB is in
Jan Lernhardt's CouchBase blog post.

I'll come back to how these different objects/permissions get configured in a later post.

Authentication

How do you tell CouchDB who you are, so it can decide what it lets you do ?

By default, CouchDB has the following authentication handlers:

  • OAuth
  • cookie authentication
  • HTTP basic authentication, RFC 2617

But wait a minute... To tell the database who I am, I have a choice between OAuth (which isn't documented anywhere, and there doesn't seem to be an actual working example of it, but I assume this was contributed by desktopcouch), cookie authentication (which creates a session and a cookie for later use, but to create the session you need to use a different authentication mechanism in the first place), or basic authentication (which is easy to sniff).

So, in practice, at some point the password is going to be sent over plaintext, and your choice basically is between once, a few times (every time you let your cookie time out, which happens after ten minutes, although you get a new cookie on every request), or every single time. I'm not a security expert, but that doesn't sound good enough.

And typically, my solution would be to switch to https:// and SSL to solve that part. Since CouchDB 1.1 this is included, although I haven't tried it yet (since I'm still on couchdb 1.0.3 because that's what I got working on my phone)

Now, my use case is a command-line application. This adds some additional security and usability concerns:

  • When invoking gtd (the mushin command-line client) to add a task, it would be great if I didn't have to specify my username and password every single time. Luckily, gtd can be started as a command line interpreter, so that helps.
  • It would be great if I didn't have to specify a password on the command line, either as part of a URL (for example, when replicating) or as an option. I really hate to see passwords either in the process list or in shell history or in a config file, and typically I will use my lowest-quality passwords for apps that force me to do this, and want to avoid writing software that has no other option.

The Plan

In the end, the plan of attack started to clear up:

  • Get away from Admin Party in CouchDB, add an admin user
  • Create a new user in the _users database in CouchDB, with the same name as my system username
  • Create a _security object on the mushin database, and allow my username as a reader.
  • to connect to couchdb from gtd, use the current OS user, and ask for the password on the terminal
  • Use Paisley's username/password and basic auth support. This means auth details still go over the network in plaintext. Add an Authenticator class that integrates with Paisley such that, when CouchDB refuses the operation, the authenticator can be asked to provide username and password to repeat the same request with. Together with a simple implementation that asks for the password on the terminal, this handles the security problem of passing the password to the application.
  • Use the cookie mechanism to avoid sending username/password every time. Create a session using the name and password, then store the cookie, and use that instead for the next request. Anytime you get a new cookie, use that from now on. This was relatively easy to do since paisley has changed to use the new

    twisted.web.agent.Agent

    and so it was easy to add a cookie-handling Agent together with the cookielib module.

  • A tricky bit was replication. When you replicate, you tell one CouchDB server to replicate to or from a database on another CouchDB server - from the point of view of the first one. On the one hand, CouchDB sometimes gives confusing response codes; for example, a 404 in the case where the remote database refuses access to the db, but a 401 in the case where the local database refuses access. On the other hand, we have to give our couchdb database the authentication information for the other one - again, we have to pass username and password, in plaintext, as part of the POST body for replication. I doubt there is a way to do this with cookies or oauth, although I don't know. And in any case, you're not even guaranteed that you can get an oauth token or cookie from the other database, since that database might not even be reachable by you (although this wouldn't be a common case). The best I could do here is, again, ask for the password on the terminal if username is given but password is not.
  • Don't log the password anywhere visibly; replace it with asterisks wherever it makes sense (Incidentally, later on I found out that couchdb does exactly the same on its console logging. Excellent.)
  • Upgrade to use CouchDB 1.1 everywhere, and do everything over SSL
  • Figure out OAuth, possibly stealing techniques from desktopcouch. For a command-line client, it would make sense that my os user is allowed to authenticate to a local couchdb instance only once per, say, X session, and a simple 'gtd add U:5 @home p:mushin add oauth support' would not ask for a password.

I made it pretty far down the list, stopping short at upgrading to couchdb 1.1

But at least tomorrow at work, people will not be able to get at my tasks on their first attempt. (Not that there's anything secret in there anyway, but I digress)

Getting Things Done with CouchDB, part 1: how did I get here?

Filed under: couchdb,Hacking,mushin — Thomas @ 20:47

2012-09-11
20:47

(... where I spend a whole post setting the scene)

Today is a day off in Barcelona - to some people it's a national holiday. To me, it's a big old opportunity to spend a whole day hacking on something I want to work on for myself.

And today, I wanted to go back to hack on mushin. What is mushin, you ask? I thought you'd never. It's going to take all of this post to explain, and I won't even get to today's hack part.

mushin is an application to implement the Getting Things Done approach. I follow this approach with varying degrees of success, but it's what has worked best for me so far.

I was never really happy with any of the tools available that claimed to implement it. For me, the basic requirements are:

  • available off-line (no online-only solutions)
  • data available across devices. I use my laptop, home desktop, and work desktop regularly; and when I'm in the store and I only have my phone with me I want to be able to see what I was supposed to buy whenever I'm in the @shopping context)
  • easy to add new tasks; preferably a command-line. I add tasks during meetings, when I'm on the phone, when I'm talking to someone, or simply typing them on my phone, so it has to be easy and quick.

This excluded many solutions at the time I first started looking for one. I recall RememberTheMilk was popular at the time, but since I was spending at least four hours a week on plane trips back then, and planes were excellent places to do GTD reviewing, it was simply not an option for me.

I don't know if Getting things GNOME already existed back then. When I first looked at it, it was basically a local-only application, but since then it's evolved to letting you synchronize tasks online, although it still looks like it's an added-on feature instead of an integral design choice. I should try it again someday.

Anyway, I ended up using yagtd, which is a command-line application operating on a text file. I put the text file in subversion, and then proceeded to use it across three computers (back then I did not have a real smartphone yet), and cursing every time I forgot to update from subversion or commit to subversion. At least the conflicts were usually easy to manage since yagtd basically stores one line per 'thing'.

And then I discovered CouchDB and I did what they told me to - I relaxed. I created a personal project called 'things' that took most of yagtd's functionality but put all the data in CouchDB. CouchDB solved two of the three requirements of my list above - it promised to make it possible to have my data available locally, even when offline, and to be able to synchronize it across devices. (Of course, I later figured out that it's nice in theory but not that simple in practice - but the basics are there)

Even though I really liked the name 'things', because, you know, if you're writing a GTD application, the things you are doing are actually, 'things'. But I realized it was a stupid ungoogleable name, so I ended up going for something Japanesey that was close enough to 'mind like water' and stumbled on the name 'mushin' - (bonus: it started with an m, and for some reason I'm accumulating personal projects that start with m)

So I happily hacked away on mushin, making it have the same featureset as yagtd, but with couchdb as the backend. Originally I used python-couchdb, since for a command-line application it's not strictly necessary to write non-blocking code. This was almost three years ago, and I've been using this application pretty much every day since then. (In fact, I have 2153 open things to do, and a well-rested mind that typically isn't too concerned about forgetting about stuff, because everything I need to worry about is *somewhere* in those 2153 open things. And some days that statement is truer than on others!)

I wonder how many people by now think I'm a classic case of NIH - surely lots of people are happily using tools for GTD already. The way I convinced myself that it made sense to create this tool is because I was incredibly excited about the promise of CouchDB (and I still am, although I 'm confused about what's going on in CouchDB land, but more on that in another post).

Maybe I was a rarity back then, with my work desktop in Barcelona, my laptop, and my home desktop in Belgium, and wanting my data in all three, all the time. In the back of my mind I was still planning to write the Ultimate Music Application, with automatic synchronization of tracks and ratings synchronized across devices, and I thought that a simple GTD application would be an excellent testing ground to see if CouchDB really could deliver on the promises it was making to my future self.

Over time, I adapted mushin. At some point, I got an N900, and I wanted mushin to work on it, and a command-line client didn't make that much sense. So I wrote a GUI frontend for it, and it was time to port mushin over to use Twisted, so that all calls could be done asynchronously and integrate nicely with the GUI. I switched from python-couchdb to Paisley, a CouchDB client from Twisted. (At one time I was even tricked into thinking I was the maintainer, appointed by the previous maintainer according to the last-touched rule, but then someone else happily forked Paisley from under me, and it now lives on github. I copied over the python-couchdb Document/Schema/Mapping code, because I liked how it worked.

And there I had a Maemo client, using most of the same code, and with access to the same data as all my desktops and laptop. I had reached my requirements, in a fashion.

It wasn't ideal: replication had to be triggered manually. I had a command to do so, but any restart of a couchdb server, or being offline too long (a few hours of my laptop not being online or even on, for example) would break the replication. In practice, you still somehow need to initiate the replication, or write code around that to do that for you. Especially with my phone, which I usually don't have online, it's easy to forget and find yourself at the store without having synced first and not remembering exactly what it was I was supposed to buy. But it was good enough.

But I never really mentioned anything about his project, and the reason was simple. I was using CouchDB in Admin Party mode (anyone can do anything), and to be able to replicate (without fiddling with tunnels and the like) I had couchdb listening on 0.0.0.0 So if anyone had known I was using this tool, it would have been very easy to take a look at all my tasks (bad), assign me some new tasks (even worse), or, you know, drop my whole database of things (I used to think that was bad, but today I'm not so convinced anymore?)

So I decided to rely on security by obscurity, and never write about my GTD application.

But now I did, so if you're still with me, you may be excited about the prospect of getting onto a network of mine and dropping my database, to release me from the mental pressure of 2153 things to be done?

Ah, well... That's what part 2 is going to be about. Stay tuned.

In the meantime, I converted my SVN repository to git and threw it on github. I only run it uninstalled at the moment, not ready yet to be packaged, but hey, if you're brave... have a go.

Python 2.7, JSON, and unicode

Filed under: couchdb,DAD,General,Python,Twisted — Thomas @ 17:28

2011-04-23
17:28

I have been hacking on Paisley more recently. I actually got to hack during work time on Paisley, because the guys needed a feature I had developed - change notification. But more on that later.

I eked out a four day hacking session over this easter weekend, and my primary goal is to make some good advances on my music database system using CouchDB. The code is still in prototype stage, and I wanted to start removing hacks, tightening things down, and adding tests. But suddenly I found myself having to add a bunch of unicode() calls on data coming back from paisley just because I was being stricter on the input to paisley functions.

I didn't want to have to deal with unicode, again, as it detracted me of the core of my application. But I didn't like paying the technical debt either of not understanding what was going wrong under the hood.

From my limited understanding, JSON is an object notation format used for exchange of information between processes and applications. A JSON string is in unicode, which is great. It would be pretty useless otherwise in today's world. So I should be able to send in unicode to JSON libraries and get unicode back out.

A recent change in Paisley by one of the maintainers prefers simplejson over the stdlib-json. I first thought this change was to blame for my problems.

And yes, when decoding a JSON object, text was returned as str instead of unicode objects. Now, this is only when the text is in fact ASCII and hence works fine both as str and as unicode. And I'm sure opinions will differ here - but I think that a JSON library should *always* deserialize text to the same type of object by default - ie, unicode.

Clearly, simplejson disagrees with me. But I didn't have this problem a few weeks ago, so something changed! What gives? And changing back to json over simplejson didn't fix it either!

After some googling, I stumbled upon this bug report. Apparently, in 2.7, the C-based implementation deserializes ASCII text as str instead of unicode. The Python-based one always returns unicode for text. And in previous Pythons, both always returned unicode for text.

In essence, my problem boiled down to this:


[thomas@ana ~]$ ipython
Python 2.7 (r27:82500, Sep 16 2010, 18:02:00)
Type "copyright", "credits" or "license" for more information.
IPython 0.10.2 -- An enhanced Interactive Python.
? -> Introduction and overview of IPython's features.
%quickref -> Quick reference.
help -> Python's own help system.
object? -> Details about 'object'. ?object also works, ?? prints more.
In [1]: from json import decoder
In [2]: decoder.py_scanstring('"str"', 1)
Out[2]: (u'str', 5)
In [3]: decoder.c_scanstring('"str"', 1)
Out[3]: ('str', 5)

versus


[py-2.6] [thomas@ana ~]$ python
Python 2.6.2 (r262:71600, Sep 29 2009, 21:49:07)
[GCC 4.4.1 20090725 (Red Hat 4.4.1-2)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> from json import decoder
>>> decoder.py_scanstring('"str"', 1)
(u'str', 5)
>>> decoder.c_scanstring('"str"', 1)
(u'str', 5)

Note how the last command returned a normal str object.

And yes, in the past few weeks since I last tested this, I did indeed upgrade my machine to Fedora 14, pulling in Python 2.7

simplejson seems to always deserialize to str when it can. I would consider that a bug - ie, 'be strict in what you produce'.

As for Paisley, I made a feature-unicode branch on github, and this commit introduces a compatibility pjson module. By default it is STRICT, ie it wants unicode back always, and tests for the buggy behaviour, and does an alternative loads implementation that falls back to the python one. I'm sure some Paisley devs will prefer simplejson still, so you can change STRICTness and prefer simplejson.

Now, back to the hack.

CouchDB python unittest setUp/tearDown

Filed under: couchdb,Hacking,Python,Twisted — Thomas @ 23:44

2011-04-03
23:44

I've been hacking on Paisley again recently since I found it I am not the only current maintainer. There is a branch on github from which a 0.3 release was recently made.

That's good news, because I didn't really need a new project to maintain. But I still have code I want to see land there, so I'm working on merging branches between launchpad, github, and some of my experimental svn branches here and there.

I had just implemented a cache for the object view mapping using couchdb-python's mapping.py and it turns out someone else was interested in adding memcache support to cache document lookups.

Some discussion started on a possible API, and I took a stab at a first draft over the past week.

Separately from that, I also took a CouchDB training course for work (together with Marek, one of our developers) ran by the Couchbase (company merger of CouchOne, formerly CouchIO (?) and Membase) people. That was a good training - but I digress.

At night Marek told me that they have some 300 lines of code that sadly reuses some classes from the current work codebase to set up and tear down test cases that work against an actual couchdb instance. He didn't feel like rewriting all that code to not use some of work's code just so that it could be contributed to Paisley for example. I felt I could do it in less than 100 lines, but he didn't seem to believe me.

So here I am after a magnificent Jose Gonzalez concert at the Palau de la Musica which is right around the corner from me, trying to write the caching code, and realizing I can't properly test it together with the change notification listener I wrote.

So while I was watching an episode of Breaking Bad, I wrote the setUp and tearDown code to do just that - start a couchdb instance on a random port, get the port, and connect to it.

It's probably not perfect yet (I do a busy loop for the creation and filling of the log file to read the port), but it worked for my simple test case. And it's 74 lines of code, including docstrings (which Marek for some reason does not believe in) and comments (which Marek also not believes in).

It's being worked on in this branch and I hope to land that in the paisley tree soon.

Next Page »
picture