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:
parent
6b56a2a994
commit
b61edc5020
19
CONTRIBUTING.md
Normal file
19
CONTRIBUTING.md
Normal 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
226
README.md
@ -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.
|
||||
|
@ -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"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user