Update documentation

Split the README documentation into better section. Refresh the text,
so that it matches the current code. Ensure the sample config works
with the examples.

Closes-bug: 1437703
Change-Id: I1548892a97d82fdcd1a4fe53f24c0fcdb6e35f1f
This commit is contained in:
Stanisław Pitucha 2015-06-09 21:36:45 +10:00
parent 6b56a2a994
commit b61edc5020
5 changed files with 230 additions and 100 deletions

19
CONTRIBUTING.md Normal file
View File

@ -0,0 +1,19 @@
This project is part of the OpenStack / Stackforge family. If you would like to
contribute to the development, you must follow the steps in this page:
http://docs.openstack.org/infra/manual/developers.html
Once those steps have been completed, changes to OpenStack should be submitted
for review via the Gerrit tool, following the workflow documented at:
http://docs.openstack.org/infra/manual/developers.html#development-workflow
(in short - install git-review package, then submit changes via `git review`)
Pull requests submitted through GitHub will be ignored.
Bugs should be filed on Launchpad, not GitHub:
https://bugs.launchpad.net/anchor

226
README.md
View File

@ -5,89 +5,209 @@ Anchor is an ephemeral PKI service that, based on certain conditions,
automates the verification of CSRs and signs certificates for clients.
The validity period can be set in the config file with hour resolution.
Ideas behind Anchor
===================
A critical capability within PKI is to revoke a certificate - to ensure that it
is no longer trusted by any peer. Unfortunately research has demonstrated that
the two typical methods of revocation (Certificate Revocation Lists and Online
Certificate Status Protocol) both have failings that make them unreliable,
especially when attempting to leverage PKI outside of web-browser software.
Through the use of short-lifetime certificates Anchor introduces the concept of
"passive revocation". By issuing certificates with lifetimes measured in hours,
revocation can be achieved by simply not re-issuing certificates to clients.
The benefits of using Anchor instead of manual long-term certificates are:
* quick certificate revoking / rotation
* always tested certificate update mechanism (used daily)
* easy integration with certmonger for service restarting
* certificates are signed only when validation is passed
* signing certificates follows consistent process
Installation
============
This service requires either a python virtual environment and
python/ssl/ldap/sasl development system packages, or system
python-ldap, python-pecan packages.
In order to install Anchor from source, the following system dependencies need
to be present:
For virtual environment run:
* python 2.7
* python (dev files)
* libffi (dev)
* libssl (dev)
* ldap (dev, optional)
* sasl (dev, optional)
When everything is in place, Anchor can be installed in one of three ways. For
development, run:
virtualenv .venv
. .venv/bin/activate
To install a development version of Anchor, run:
python setup.py develop
pip install watchdog
Note that watchdog is needed only when running with the --reload option used
later. To install a production version, run:
For installing into virtualenv, run:
virtualenv path/to/environment
. path/to/environment/bin/activate
python setup.py install
For installing in production, either install a perpared system package, or
install globally in the system:
python setup.py install
The config file should be copied from `config.py` with any details updated.
Running the service
===================
Anchor requires you to provide a CA signing certificate and private key
which is stored in the CA subdirectory by default (as specified in
config.py). This can be generated using the certificate provider of
your choice, or a test signing certificate can be generated using
openssl:
In order to run the service, it needs to be started via the `pecan` application
server. The only extra parameter is a config file:
Create a private key with password 'x', and then decrypt it:
pecan serve config.py
cd CA
openssl genrsa -aes128 -passout pass:x -out ca.p.key 4096
openssl rsa -passin pass:x -in ca.p.key -out root-ca-unwrapped.key
For development, an additional `--reload` parameter may be used. It will cause
the service to reload every time a source file is changed, however it requires
installing an additional `watchdog` python module.
Then create a CSR from that key, specify 'Test Anchor CA' or similar as
the Common Name for the certificate:
In the default configuration, Anchor will wait for web requests on port 5000 on
local network interface. This can be adjusted in the `config.py` file.
openssl req -new -key root-ca-unwrapped.key -out ca.csr
Preparing a test environment
============================
Finally, sign the CSR to create a self-signed root certificate:
In order to test Anchor with the default configuration, the following can be
done to create a test CA. The test certificate can be then used to sign the new
certificates.
openssl x509 -req -days 365 -in ca.csr \
-signkey root-ca-unwrapped.key -out root-ca.crt
rm ca.p.key ca.csr
openssl req -out CA/root-ca.crt -keyout CA/root-ca-unwrapped.key \
-newkey rsa:4096 -subj "/CN=Anchor Test CA" -nodes -x509 -days 365
chmod 0400 CA/root-ca-unwrapped.key
The service can be run during development with:
Next, a new certificate request may be generated:
.venv/bin/pecan serve --reload config.py
openssl req -out anchor-test.example.com.csr -nodes \
-keyout anchor-test.example.com.key -newkey rsa:2048 \
-subj "/CN=anchor-test.example.com"
In production, the package should be instead installed using:
That reqest can be submitted using curl (while `pecan serve config.py` is
running):
pip install '.[production]'
curl http://0.0.0.0:5000/sign -F user='myusername' \
-F secret='simplepassword' -F encoding=pem \
-F 'csr=<anchor-test.example.com.csr'
And the debug option in `config.py` has to be turned off. Service can
be started via the uwsgi server, for example (with 4 processes):
This will result in the signed request being created in the `certs` directory.
uwsgi --http-socket :5000 --venv /path/to/the/virtualenv \
--pecan /path/to/config.py -p 4
Running Anchor in production
============================
To test the service, generate the certificate request using default
values and submit it using curl (change the user and secret if you have
changed them in config.py):
Anchor shouldn't be exposed directly to the network. It's running via an
application server (Pecan) and doesn't have all the features you'd normally
expect from a http proxy - for example dealing well with deliberately slow
connections, or using multiple workers. Anchor can however be run in production
using a better frontend.
openssl req -text -newkey rsa:4096 -nodes \
-out subdomain.example.org.csr
To run Anchor using uwsgi you can use the following command:
curl http://127.0.0.1:5000/sign -F user='woot' -F secret='woot' \
-F encoding=pem -F 'csr=<subdomain.example.org.csr'
uwsgi --http-socket :5000 --venv path/to/venv --pecan config.py -p 4
Assuming the installation is successful and the default config is
unchanged, this will fail validation, but should not give an OpenSSL or
other error. This is assuming you set the CN to be subdomain.example.org,
which is not permitted in the default config. Now generate a valid CSR
that should pass validation and check that it is issued, by specifying a
common name of 'anchor-test.example.com' when prompted:
In case a more complex scripted configuration is needed, for example to handle
custom headers, rate limiting, or source filtering a complete HTTP proxy like
Nginx may be needed. This is however out of scope for Anchor project. You can
read more about production deployment in
[Pecan documentation](http://pecan.readthedocs.org/en/latest/deployment.html).
openssl req -text -newkey rsa:4096 -nodes \
-out anchor-test.example.com.csr
Additionally, using an AppArmor profile for Anchor is a good idea to prevent
exploits relying on one of the native libraries used by Anchor (for example
OpenSSL). This can be done with sample profiles which you can find in the
`tools/apparmor.anchor_*` files. The used file needs to be reviewed and updated
with the right paths depending on the deployment location.
curl http://127.0.0.1:5000/sign -F user='woot' -F secret='woot' \
-F encoding=pem -F 'csr=<anchor-test.example.com.csr'
Validators
==========
If Anchor is correctly configured, the CA will return a certificate.
One of the main features of Anchor are the validators which make sure that all
requests match a given set of rules. They're configured in `config.json` and
the sample configuration includes a few of them.
Each validator takes a dictionary of options which provide the specific
matching conditions.
Currenly available validators are:
* `common_name` ensures CN matches one of names in `allowed_domains` or ranges
in `allowed_networks`
* `alternative_names` ensures alternative names match one of the names in
`allowed_domains`
* `alternative_names_ip` ensures alternative names match one of the names in
`allowed_domains` or IP ranges in `allowed_networks`
* `blacklist_names` ensures CN and alternative names do not contain any of the
configured `domains`
* `server_group` ensures the group the requester is contained within
`group_prefixes`
* `extensions` ensures only `allowed_extensions` are present in the request
* `key_usage` ensures only `allowed_usage` is requested for the certificate
* `ca_status` ensures the request does/doesn't require the CA flag
* `source_cidrs` ensures the request comes from one of the ranges in `cidrs`
A configuration entry for a validator might look like one from the sample
config:
"key_usage": {
"allowed_usage": [
"Digital Signature",
"Key Encipherment",
"Non Repudiation"
]
}
Authentication
==============
Anchor can use one of the following authentication modules: static, keystone,
ldap.
Static: Username and password are present in `config.json`. This mode should be
used only for development and testing.
"auth": {
"static": {
"secret": "simplepassword",
"user": "myusername"
}
}
Keystone: Username is ignored, but password is a token valid in the configured
keystone location.
"auth": {
"keystone": {
"url": "https://keystone.example.com"
}
}
LDAP: Username and password are used to bind to an LDAP user in a configured
domain. User's groups for the `server_group` filter are retrieved from
attribute `memberOf` in search for `(sAMAccountName=username@domain)`. The
search is done in the configured base.
"auth": {
"ldap": {
"host": "ldap.example.com",
"base": "ou=Users,dc=example,dc=com",
"domain": "example.com"
}
}
Reporting bugs and contributing
===============================
For bug reporting and contributing, please check the CONTRIBUTING.md file.

View File

@ -61,15 +61,6 @@
"127.0.0.0/8"
]
}
},
"ip": {
"common_name": {
"allowed_networks": ["93.184.216.34"]
},
"alternative_names": {
"allowed_networks": ["93.184.216.34"]
}
}
}
}

View File

@ -13,20 +13,20 @@
#include <tunables/global>
@{ECA_BASE}="/replace/with/path/to/anchor"
@{ECA_VENV}="@{ECA_BASE}/.venv"
@{ECA_CA_DIR}="@{ECA_BASE}/CA"
@{ECA_CERTS_DIR}="@{ECA_BASE}/certs"
@{ANCHOR_BASE}="/replace/with/path/to/anchor"
@{ANCHOR_VENV}="@{ANCHOR_BASE}/.venv"
@{ANCHOR_CA_DIR}="@{ANCHOR_BASE}/CA"
@{ANCHOR_CERTS_DIR}="@{ANCHOR_BASE}/certs"
/replace/with/path/to/anchor/bin/anchor_debug {
#include <abstractions/base>
#include <abstractions/bash>
/bin/dash ix,
@{ECA_BASE}/ r,
@{ECA_BASE}/bin/anchor_debug mixr,
@{ANCHOR_BASE}/ r,
@{ANCHOR_BASE}/bin/anchor_debug mixr,
@{ECA_VENV}/bin/pecan cix,
@{ANCHOR_VENV}/bin/pecan cix,
profile /replace/with/path/to/bin/pecan {
#include <abstractions/base>
@ -43,19 +43,19 @@
/sbin/ldconfig mUxr,
@{ECA_CA_DIR}/* r,
@{ECA_CERTS_DIR}/{,*.crt} w,
@{ANCHOR_CA_DIR}/* r,
@{ANCHOR_CERTS_DIR}/{,*.crt} w,
@{ECA_BASE}/ r,
@{ECA_BASE}/anchor/**.py{,c} r,
@{ECA_BASE}/anchor/{,**/} r,
@{ECA_BASE}/config.py r,
@{ANCHOR_BASE}/ r,
@{ANCHOR_BASE}/anchor/**.py{,c} r,
@{ANCHOR_BASE}/anchor/{,**/} r,
@{ANCHOR_BASE}/config.py r,
@{ECA_VENV}/bin/python mixr,
@{ECA_VENV}/bin/pecan mixr,
@{ECA_VENV}/bin/ r,
@{ECA_VENV}/lib/python2.7/ r,
@{ECA_VENV}/lib/python2.7/** r,
@{ECA_VENV}/lib/python2.7/**/*.so m,
@{ANCHOR_VENV}/bin/python mixr,
@{ANCHOR_VENV}/bin/pecan mixr,
@{ANCHOR_VENV}/bin/ r,
@{ANCHOR_VENV}/lib/python2.7/ r,
@{ANCHOR_VENV}/lib/python2.7/** r,
@{ANCHOR_VENV}/lib/python2.7/**/*.so m,
}
}

View File

@ -13,20 +13,20 @@
#include <tunables/global>
@{ECA_BASE}="/path/to/anchor"
@{ECA_VENV}="@{ECA_BASE}/.venv"
@{ECA_CA_DIR}="@{ECA_BASE}/CA"
@{ECA_CERTS_DIR}="@{ECA_BASE}/certs"
@{ANCHOR_BASE}="/path/to/anchor"
@{ANCHOR_VENV}="@{ANCHOR_BASE}/.venv"
@{ANCHOR_CA_DIR}="@{ANCHOR_BASE}/CA"
@{ANCHOR_CERTS_DIR}="@{ANCHOR_BASE}/certs"
/path/to/anchor/bin/anchor_production {
#include <abstractions/base>
#include <abstractions/bash>
/bin/dash ix,
@{ECA_BASE}/ r,
@{ECA_BASE}/bin/anchor_production mixr,
@{ANCHOR_BASE}/ r,
@{ANCHOR_BASE}/bin/anchor_production mixr,
@{ECA_VENV}/bin/uwsgi cix,
@{ANCHOR_VENV}/bin/uwsgi cix,
profile /path/to/anchor/.venv/bin/uwsgi {
#include <abstractions/base>
@ -44,19 +44,19 @@
@{PROC}/sys/net/core/somaxconn r,
@{ECA_CA_DIR}/* r,
@{ECA_CERTS_DIR}/{,*.crt} w,
@{ANCHOR_CA_DIR}/* r,
@{ANCHOR_CERTS_DIR}/{,*.crt} w,
@{ECA_BASE}/ r,
@{ECA_BASE}/anchor/**.py{,c} r,
@{ECA_BASE}/anchor/{,**/} r,
@{ECA_BASE}/config.py r,
@{ANCHOR_BASE}/ r,
@{ANCHOR_BASE}/anchor/**.py{,c} r,
@{ANCHOR_BASE}/anchor/{,**/} r,
@{ANCHOR_BASE}/config.py r,
@{ECA_VENV}/bin/python mixr,
@{ECA_VENV}/bin/uwsgi mixr,
@{ECA_VENV}/bin/ r,
@{ECA_VENV}/lib/python2.7/ r,
@{ECA_VENV}/lib/python2.7/** r,
@{ECA_VENV}/lib/python2.7/**/*.so m,
@{ANCHOR_VENV}/bin/python mixr,
@{ANCHOR_VENV}/bin/uwsgi mixr,
@{ANCHOR_VENV}/bin/ r,
@{ANCHOR_VENV}/lib/python2.7/ r,
@{ANCHOR_VENV}/lib/python2.7/** r,
@{ANCHOR_VENV}/lib/python2.7/**/*.so m,
}
}