Security - ending the Admin Party


@gordonb This is a start at defining minimum security around OpenAg.
Background: Couchdb installs with an “Admin Party”; there are no defined users and anybody logging in can do anything (literally anything!!). The only security is not having the Couchdb port open to the internet.
Couchdb (and hence OpenAg) are federated databases, each one is independent, but only serve the greater good when information is shared (Couchdb is open to the internet).
Hence, the need for some security architecture.
Goals are:

  1. Protection of OpenAg brain data
  2. Minimal administrative overhead
  3. Easy sharing of selective data

With a federated system I am assuming that most data sharing will by by “pull” (rather than by push). This is the way that much of the web works. If somebody wants my data, I tell them where it is located and they pull a copy.
The one exception to this might be the central OpenAg database, where people may ‘push’ recipes and data to a common public repository.

The following three steps should accomplish these goals.

  1. Create an admin user - this blocks unauthorized creation/deletion of databases, and modification of _design documents.
  2. There are two options here, and I have yet to decide which I like best (though leaning toward the role). a) create a user and add this user to each database, or b) create a role and add it to each database (and the admin user). This prevents unauthorized users from deleting and editing documents. An unauthorized user can cause way more damage by small, unnoticed changes to critical data than by obvious attacks like deleting databases.
  3. The third step is to set up replication of tables that are to be shared. These tables have no users or roles, so are open to the ‘wild’ for reading. There is a risk of them being deleted or modified, but the replication process assures its relative health. I would propose adding some ‘administrative’ fields to all documents to help with replication.

Required fields:
From my past database work, I am a big fan of having document meta-data. I would propose adding five fields to each _design document:
*author - who created the document, useful when sharing documents to know where it came from
*created_dt - when first created
*last_update_dt - when last modified
*maturity (development, test, production) - it is nice to know if this is something being played around with and ‘not ready for prime time’, or something that has been thoroughly vetted.
*sharing (private, share, public) - this allows setting up a filter on the replication script so that only documents that are intended to be shared get out for public access.

After setting up an admin user (via command line), Futon has some heart-burn giving access. There needs to be a modification to \etc\couchdb\local.ini require_valid_user=false
in order to get a login. This may be taken care of if the admin user is created through Futon.

The following are the commands I was playing around with to test this:

#Access with no authorization

#Create dabatases
curl -X PUT $NO_AUTH/crop

#Create server admin
#No more admin party:
curl -X PUT $NO_AUTH/_config/admins/grower -d ‘“greens”’

#Try to create database without authorization (should fail)
curl -X PUT $NO_AUTH/non_database

#Access with admin authorization

#Create public database
curl -X PUT $GROWER/crop_public

#get list of all databases
curl -X GET $GROWER/_all_dbs

#Create user:helper
curl -HContent-Type:application/json
-vXPUT $GROWER/_users/org.couchdb.user:helper
–data-binary ‘{"_id": “org.couchdb.user:helper”,“name”: “helper”,“roles”: [],“type”: “user”,“password”: “thumb”}’

#Add user:helper to database:crop
curl -vX PUT $GROWER/crop/_security
–data-binary ‘{“admins”:{“names”:[],“roles”:[]},“members”:{“names”:[“helper”],“roles”:[]}}’

#create a dummy document
curl -X PUT $GROWER/crop/“crop001” -d ‘{}’

#query for the document
curl -X GET $GROWER/crop/crop001
curl -X GET $HELPER/crop/crop001
#should not have authorization
curl -X GET $NO_AUTH/crop/crop001

#replicate database:crop to database:crop_public
curl -X POST $GROWER/_replicate/“crop_rep” -d ‘{ “source”: “crop” , “target”: “crop_public” }’ -H "Content-Type: application/json"
curl -X POST $GROWER/_replicate/“crop_rep” -d ‘{ “source”: “recipes” , “target”: “recipes_public” , “continuous”:true, “create_target”:true }’ -H “Content-Type: application/json”
#query for the replicated document
curl -X GET $GROWER/crop_public/crop001
#grower create document in database:crop_public
curl -X POST $GROWER/crop_public -d ‘{}’ -H “Content-Type: application/json”
#query for all document
curl -X GET $NO_AUTH/crop_public/_all_docs

#helper create document in database:crop
curl -X POST $GROWER/crop_public -d ‘{}’ -H “Content-Type: application/json”
#helper create document in database:crop_public
curl -X POST $HELPER/crop_public -d ‘{}’ -H “Content-Type: application/json”

#helper reads database:crop (should fail?)
curl -X GET $HELPER/crop/_all_docs
#helper reads database:crop_public
curl -X GET $HELPER/crop_public/_all_docs

#unknown create document in database:crop (should fail)
curl -X $NO_AUTH/crop_public -d ‘{}’ -H “Content-Type: application/json”
#unknown create document in database:crop_public
curl -X POST $NO_AUTH/crop_public -d ‘{}’ -H “Content-Type: application/json”
#unknown read document in database:crop (should fail)
curl -X GET $NO_AUTH/crop_public/crop/_all_docs
#unknown read document in database:crop_public
curl -X GET $NO_AUTH/crop_public/_all_docs

Delete database:crop
$ curl -X DELETE GROWER/crop Delete database:crop_public curl -X DELETE $GROWER/crop_public
Delete user:helper
#Check list of users
curl -X GET $GROWER/_users/_all_docs
#Delete helper (note: you need the rev info to avoid a conflict error)
curl -X GET $GROWER/_users/org.couchdb.user:helper?revs_info=true
curl -X DELETE $GROWER/_users/org.couchdb.user:helper?rev="<9999999999999>"
Delete admin:grower
curl -X DELETE $GROWER/_config/admins/grower

#You should now be back to the starting state, with an Admin Party


HI Webbhm,

Thanks for pointing out this security issue. Can you elaborate on why you like securing the db via a role as opposed to creating a user the sole purpose of which is db access? Where i work the db admins create new users for each application db. I had never heard of using a role, although it sounds like it might be the way to go.




I suspect you work for a relatively small company and there is a lot of trust among people, and most of your applications are on an internal network.
I worked at large companies where paranoia was the norm.
If you build security around users, with users identified by application, they people end up with lists of user logins, and possibly multiple logins for a single application if different users have different levels of access (read only; read and update; read, update and delete).
Roles allow a person to have a single login for all systems, with access managed via roles. The two main strengths are:

  1. If you log application/login information, you know who is into what system at what time, and what they are doing. This gives clear audit tracking details.
  2. If you demote or fire a person, you just change their login or their roles; no impact to anyone else.
    I worked at a company where one internet application had a shared login. Months after I left the company, out of curiosity I tried logging into the application, and did so with no effort. I could have deleted all the data and they would not have known what happened. I suspect I could still access that application today.
    Role managed security takes more time to think through and set up initially, but I think it has more flexibility in the long run (especially for bigger systems). A single personal food computer doesn’t take much administration, but not knowing where things will grow I want to start with what I consider the best options.


I have tested out the role security and it works as expected, and is easier than user security. Futon is the easiest way to implement the roles; however, here is the code for those who are interested:

#Access with no authorization


#Create server admin
#No more admin party:
curl -X PUT $NO_AUTH/_config/admins/grower -d '“greens”'
curl -HContent-Type:application/json
-vXPUT $GROWER/_users/org.couchdb.user:helper
–data-binary ‘{"_id": “org.couchdb.user:helper”,“name”: “helper”,“roles”: [],“type”: “user”,“password”: “thumb”}’

#Create user:helper with role:mbr (this is optional):
curl -HContent-Type:application/json
-vXPUT $GROWER/_users/org.couchdb.user:helper
–data-binary ‘{"_id": “org.couchdb.user:helper”,“name”: “helper”,“roles”: [“mbr”],“type”: “user”,“password”: “thumb”}’

#Add role:mbr to database:recipes
curl -vX PUT $GROWER/recipes/_security
–data-binary ‘{“admins”:{“names”:[],“roles”:[]},“members”:{“names”:[],“roles”:[“mbr”]}}’
#Add role:mbr to databse:environment
curl -vX PUT $GROWER/environment/_security
–data-binary ‘{“admins”:{“names”:[],“roles”:[]},“members”:{“names”:[],“roles”:[“mbr”]}}’
#Add role:mbr to databse:environment_data_point
curl -vX PUT $GROWER/environment_data_point/_security
–data-binary ‘{“admins”:{“names”:[],“roles”:[]},“members”:{“names”:[],“roles”:[“mbr”]}}’
#Add role:mbr to databse:firmware_module
curl -vX PUT $GROWER/firmware_module/_security
–data-binary ‘{“admins”:{“names”:[],“roles”:[]},“members”:{“names”:[],“roles”:[“mbr”]}}’
#Add role:mbr to databse:firmware_module_type
curl -vX PUT $GROWER/firmware_module_type/_security
–data-binary ‘{“admins”:{“names”:[],“roles”:[]},“members”:{“names”:[],“roles”:[“mbr”]}}’
#Add role:mbr to databse:software_module
curl -vX PUT $GROWER/software_module/_security
–data-binary ‘{“admins”:{“names”:[],“roles”:[]},“members”:{“names”:[],“roles”:[“mbr”]}}’
#Add role:mbr to databse:software_module_type
curl -vX PUT $GROWER/software_module_type/_security
–data-binary ‘{“admins”:{“names”:[],“roles”:[]},“members”:{“names”:[],“roles”:[“mbr”]}}’

#replicate database:recipes to database:recipes_public
curl -X POST $GROWER/_replicate/“recipes_rep” -d ‘{ “source”: “recipes” , “target”: “recipes_public” , “continuous”:true, “create_target”:true }’ -H “Content-Type: application/json”
#replicate database:environment to database:environment_public
curl -X POST $GROWER/_replicate/“environment_rep” -d ‘{ “source”: “environment” , “target”: “environment_public” , “continuous”:true, “create_target”:true }’ -H “Content-Type: application/json”
#replicate database:firmware_module to database:firmware_module_public
curl -X POST $GROWER/_replicate/“firmware_module_rep” -d ‘{ “source”: “firmware_module” , “target”: “firmware_module_public” , “continuous”:true, “create_target”:true }’ -H “Content-Type: application/json”
#replicate database:software_module to database:software_module_public
curl -X POST $GROWER/_replicate/“software_module_rep” -d ‘{ “source”: “software_module” , “target”: “software_module_public” , “continuous”:true, “create_target”:true }’ -H “Content-Type: application/json”