Hello all--
I'm working with Jesse Wolfe to design a back-end REST API for the
features of the "puppet cert" command-line tool. This is a first cut
at a proposed interface. I'd appreciate comments about whether this
is a good way to go.
Thanks,
Paul
=====
Motivation
==========
Most of Puppet's major components are either exposed through the REST
API, or implemented internally using the indirector. This allows the
components to be controlled from a separate application (possibly on a
remote machine) such as Puppet Dashboard. In addition it allows
advanced customers to connect the components of Puppet together in new
and unexpected ways to meet their needs. A major exception to this is
the "CertificateAuthority" component, which allows the Puppet master
to manage, sign, and revoke the certificates that agents use to
authenticate themselves. It would be useful to have a REST API for
the certificate authority so that a new client machine could be
provisioned without having to execute any command-line operations on
the master.
Scope
=====
This proposal adds functionality to the "puppet master" application,
when used in the default configuration where it doubles as a
certificate authority. It allows a suitably authenticated client to
use the REST API to perform all the actions available through "puppet
cert", except for "--generate". The client need not have access to an
OpenSSL library to perform these actions. The interface does not
compromise the security of the certificate authority's private key.
Access to this feature will be disabled by default, and can be enabled
using Puppet's standard "authconfig" mechanism.
API
===
certificate_status and certificate_statuses
-------------------------------------------
To retrieve information about the status of the host called
<hostname>, issue a GET request to
https://<master>/<environment>/certificate_status/<hostname>. If the
hostname is recognized, this returns a JSON hash object of the form:
{'state': <state>, 'hostname': <hostname>,
'fingerprint': <fingerprint>, 'error_message': <error_message>}
<state> is one of:
- 'requested' if the host has sent the server a certificate request
but no certificate has been issued.
- 'signed' if a valid certificate exists for the host.
- 'revoked' if a certificate exists for the host but has been revoked.
- 'invalid' the host fails validation for some reason other than the
certificate having been revoked.
<hostname> is the fully qualified domain name of the host.
<fingerprint> is the fingerprint of the host's certificate or
certificate request, as a string
(e.g. "67:70:3E:E8:D9:75:29:F7:67:87:0E:A7:70:EE:02:80"). The default
digest algorithm for the fingerprint is MD5. To select another
digest, add the parameter "?digest=<digest>" to the query string. All
digests supported by Ruby's OpenSSL::Digest class are permitted.
<error_message> is a string saying why the host failed verification
(if the host is in the 'revoked' or 'invalid' state). Otherwise it is
the empty string.
This takes the place of the "puppet cert" options "--verify",
"--fingerprint", and "--digest".
To retrieve information about the status of multiple hosts, issue a
GET request to
https://<master>/<environment>/certificate_statuses/<hostname_pattern>.
This returns JSON list object containing JSON hashes of the form
described above, for all known hosts that match <hostname_pattern>.
<hostname_pattern> may be "*" to match all hosts.
To restrict the search to just hosts that are awaiting certificates,
add the parameter "?restrict=waiting" to the query string. To
restrict the search to just hosts that have signed certificates, add
the parameter "?restrict=signed".
This takes the place of the "puppet cert" option "--list".
To cause the master to sign or revoke a certificate for a given host,
issue a PUT request to
https://<master>/<environment>/certificate_status/<hostname>, and send
the following JSON data:
{'state': <desired_state>}
<desired_state> is one of:
- 'signed': causes the master to sign a certificate for the given
host. The host must be in the 'requested' state for this to
succeed.
- 'revoked': causes the master to revoke a certificate for the given
host. The host must be in the 'signed' state for this to succeed.
(Other hash elements, if present, are ignored).
This takes the place of the "puppet cert" options "--sign" and
"--revoke".
To cause the master to discard certificate information for a given
host, issue a DELETE request to
https://<master>/<environment>/certificate_status/<hostname>.
This takes the place of the "puppet cert" option "--clean".
certificate and certificate_request
-----------------------------------
The existing REST APIs for
https://<master>/<environment>/certificate/<hostname> and
https://<master>/<environment>/certificate_request/<hostname> (which
are already used internally for exchanging certificates and requests
between master and agent) are extended to support a human-readable
text format. This format is only supported for reading. This takes
the place of the "puppet cert" option "--print", and uses the same
format.
Not supported
-------------
The remaining action supported by "puppet cert" is "--generate". This
action generates the host's public/private key pair on the master,
with the understanding that the user will manually copy the generated
key pair to the client. Since a manual copy is necessary in order for
"--generate" to be useful, it doesn't make sense to provide a REST API
that allows a user to invoke it from a remote machine.
IMPLEMENTATION
==============
To implement the new API we'll need to add a new model class
Puppet::SSL::CertificateState, and a new indirection
"certificate_status". The model class will be a thin wrapper for the
four properties described above (status, hostname, fingerprint, and
error_message). We'll add a "CA" indirector terminus which is able to
create these objects in response to find() and search() methods, is
able to sign and revoke certificates in response to the save() method,
and is able to delete certificates and requests in response to the
destroy() method.
In addition, we'll need to add new formatters for
Puppet::SSL::Certificate and Puppet::SSL::CertificateRequest to
support the human-readable text format.
The existing code for "puppet cert" will be re-worked to use the new
model class internally, so that the same code paths are exercised
whether or not the customer is using the REST API.
SECURITY
========
The new REST API for "certificate_status" will not be mentioned by the
default configuration in Puppet::Network::RestAuthConfig, so it will
be disabled by default. Users who want to enable it can do so, and
can set access controls for it, using the standard "auth.conf"
mechanism.
The only parts of the REST API that are a potential security risk for
users are the "save" and "destroy" methods for "certificate_status".
These should be enabled with care, since they can be used to allow new
machines to be managed by the Puppet master, or to stop old machines
from being managed by the Puppet master.
The other parts of the REST API (the "find" and "search" methods of
"certificate_status", and the new format for "certificate" and
"certificate_request") don't expose any additional security holes,
since they merely automate what a client with access to OpenSSL could
already do with the certificates published by the puppet master.
None of the proposed APIs exposes any private keys.
--
You received this message because you are subscribed to the Google Groups
"Puppet Developers" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to
[email protected].
For more options, visit this group at
http://groups.google.com/group/puppet-dev?hl=en.