Merge pull request #291 from docker/server-docs

Server and signer docs
This commit is contained in:
Ying Li 2015-12-15 13:26:08 -08:00
commit 20c557a10b
7 changed files with 1114 additions and 4 deletions

View File

@ -244,8 +244,8 @@ func usage() {
// endpoints. The addr should not be exposed externally. For most of these to
// work, tls cannot be enabled on the endpoint, so it is generally separate.
func debugServer(addr string) {
logrus.Info("Debug server listening on", addr)
logrus.Infof("Debug server listening on %s", addr)
if err := http.ListenAndServe(addr, nil); err != nil {
logrus.Fatal("error listening on debug interface: ", err)
logrus.Fatalf("error listening on debug interface: %v", err)
}
}

View File

@ -240,8 +240,8 @@ func usage() {
// endpoints. The addr should not be exposed externally. For most of these to
// work, tls cannot be enabled on the endpoint, so it is generally separate.
func debugServer(addr string) {
log.Println("Debug server listening on", addr)
logrus.Infof("Debug server listening on %s", addr)
if err := http.ListenAndServe(addr, nil); err != nil {
log.Fatalf("error listening on debug interface: %v", err)
logrus.Fatalf("error listening on debug interface: %v", err)
}
}

View File

@ -0,0 +1,350 @@
<!--[metadata]>
+++
title = "Notary Serer Configuration File"
description = "Specifies the configuration file for Notary Server"
keywords = ["docker, notary, notary-server, configuration"]
[menu.main]
parent="mn_notary"
+++
<![end-metadata]-->
# Notary Server Configuration File
An example (full) server configuration file.
```json
{
"server": {
"http_addr": ":4443",
"tls_key_file": "./fixtures/notary-server.key",
"tls_cert_file": "./fixtures/notary-server.crt",
},
"trust_service": {
"type": "remote",
"hostname": "notarysigner",
"port": "7899",
"key_algorithm": "ecdsa",
"tls_ca_file": "./fixtures/root-ca.crt",
"tls_client_cert": "./fixtures/notary-server.crt",
"tls_client_key": "./fixtures/notary-server.key"
},
"storage": {
"backend": "mysql",
"db_url": "user:pass@tcp(notarymysql:3306)/databasename?parseTime=true"
},
"auth": {
"type": "token",
"options": {
"realm": "https://auth.docker.io/token",
"service": "notary-server",
"issuer": "auth.docker.io",
"rootcertbundle": "/path/to/auth.docker.io/cert"
}
},
"logging": {
"level": "debug"
},
"reporting": {
"bugsnag": {
"api_key": "c9d60ae4c7e70c4b6c4ebd3e8056d2b8",
"release_stage": "production"
}
}
}
```
## `server` section (required)
Example:
```json
"server": {
"http_addr": ":4443",
"tls_key_file": "./fixtures/notary-server.key",
"tls_cert_file": "./fixtures/notary-server.crt"
}
```
<table>
<tr>
<th>Parameter</th>
<th>Required</th>
<th>Description</th>
</tr>
<tr>
<td valign="top"><code>http_addr</code></td>
<td valign="top">yes</td>
<td valign="top">The TCP address (IP and port) to listen on. Examples:
<ul>
<li><code>":4443"</code> means listen on port 4443 on all IPs (and
hence all interfaces, such as those listed when you run
<code>ifconfig</code>)</li>
<li><code>"127.0.0.1:4443"</code> means listen on port 4443 on
localhost only. That means that the server will not be
acessible except locally (via SSH tunnel, or just on a local
terminal)</li>
</ul>
</td>
</tr>
<tr>
<td valign="top"><code>tls_key_file</code></td>
<td valign="top">no</td>
<td valign="top">The path to the private key to use for
HTTPS. Must be provided together with <code>tls_cert_file</code>,
or not at all. If neither are provided, the server will use HTTP
instead of HTTPS. The path is relative to the directory of the
configuration file.</td>
</tr>
<tr>
<td valign="top"><code>tls_cert_file</code></td>
<td valign="top">no</td>
<td valign="top">The path to the certificate to use for HTTPS.
Must be provided together with <code>tls_key_file</code>, or not
at all. If neither are provided, the server will use HTTP instead
of HTTPS. The path is relative to the directory of the
configuration file.</td>
</tr>
</table>
## `trust service` section (required)
This section configures either a remote trust service, such as
[Notary Signer](notary-signer.md) or a local in-memory ED25519 trust service.
Remote trust service example:
```json
"trust_service": {
"type": "remote",
"hostname": "notarysigner",
"port": "7899",
"key_algorithm": "ecdsa",
"tls_ca_file": "./fixtures/root-ca.crt",
"tls_client_key": "./fixtures/notary-server.key",
"tls_client_cert": "./fixtures/notary-server.crt"
}
```
Local trust service example:
```json
"trust_service": {
"type": "local"
}
```
<table>
<tr>
<th>Parameter</th>
<th>Required</th>
<th>Description</th>
</tr>
<tr>
<td valign="top"><code>type</code></td>
<td valign="top">yes</td>
<td valign="top">Must be <code>"remote"</code> or <code>"local"</code></td>
</tr>
<tr>
<td valign="top"><code>hostname</code></td>
<td valign="top">yes if remote</td>
<td valign="top">The hostname of the remote trust service</td>
</tr>
<tr>
<td valign="top"><code>port</code></td>
<td valign="top">yes if remote</td>
<td valign="top">The GRPC port of the remote trust service</td>
</tr>
<tr>
<td valign="top"><code>key_algorithm</code></td>
<td valign="top">yes if remote</td>
<td valign="top">Algorithm to use to generate keys stored on the
signing service. Valid values are <code>"ecdsa"</code>,
<code>"rsa"</code>, and <code>"ed25519"</code>.</td>
</tr>
<tr>
<td valign="top"><code>tls_ca_file</code></td>
<td valign="top">no</td>
<td valign="top">The path to the root CA that signed the TLS
certificate of the remote service. This parameter if said root
CA is not in the system's default trust roots. The path is
relative to the directory of the configuration file.</td>
</tr>
<tr>
<td valign="top"><code>tls_client_key</code></td>
<td valign="top">no</td>
<td valign="top">The path to the private key to use for TLS mutual
authentication. This must be provided together with
<code>tls_client_cert</code> or not at all. The path is relative
to the directory of the configuration file.</td>
</tr>
<tr>
<td valign="top"><code>tls_client_cert</code></td>
<td valign="top">no</td>
<td valign="top">The path to the certificate to use for TLS mutual
authentication. This must be provided together with
<code>tls_client_key</code> or not at all. The path is relative
to the directory of the configuration file.</td>
</tr>
</table>
## `storage` section (required)
The storage section specifies which storage backend the server should use to
store TUF metadata. Currently, we only support MySQL or an in-memory store.
DB storage example:
```json
"storage": {
"backend": "mysql",
"db_url": "user:pass@tcp(notarymysql:3306)/databasename?parseTime=true"
}
```
<table>
<tr>
<th>Parameter</th>
<th>Required</th>
<th>Description</th>
</tr>
<tr>
<td valign="top"><code>backend</code></td>
<td valign="top">yes</td>
<td valign="top">Must be <code>"mysql"</code> or <code>"memory"</code>.
If <code>"memory"</code> is selected, the <code>db_url</code>
is ignored.</td>
</tr>
<tr>
<td valign="top"><code>db_url</code></td>
<td valign="top">yes if not <code>memory</code></td>
<td valign="top">The <a href="https://github.com/go-sql-driver/mysql">
the Data Source Name used to access the DB.</a>
(note: please include "parseTime=true" as part of the the DSN)</td>
</tr>
</table>
## `auth` section (optional)
This sections specifies the authentication options for the server.
Currently, we only support token authentication.
Example:
```json
"auth": {
"type": "token",
"options": {
"realm": "https://auth.docker.io",
"service": "notary-server",
"issuer": "auth.docker.io",
"rootcertbundle": "/path/to/auth.docker.io/cert"
}
}
```
Note that this entire section is optional. However, if you would like
authentication for your server, then you need the required parameters below to
configure it.
**Token authentication:**
This is an implementation of the same authentication used by
[docker registry](https://github.com/docker/distribution). (JTW token-based
authentication post login.)
<table>
<tr>
<th>Parameter</th>
<th>Required</th>
<th>Description</th>
</tr>
<tr>
<td valign="top"><code>type</code></td>
<td valign="top">yes</td>
<td valign="top">Must be `"token"`; all other values will result in no
authentication (and the rest of the parameters will be ignored)</td>
</tr>
<tr>
<td valign="top"><code>options</code></td>
<td valign="top">yes</td>
<td valign="top">The options for token auth. Please see
<a href="https://github.com/docker/distribution/blob/master/docs/configuration.md#token">
the registry token configuration documentation</a>
for the parameter details.</td>
</tr>
</table>
## `logging` section (optional)
The logging section sets the log level of the server. If it is not provided
or invalid, the server defaults to an ERROR logging level.
Example:
```json
"logging": {
"level": "debug"
}
```
Note that this entire section is optional. However, if you would like to
specify a different log level, then you need the required parameters
below to configure it.
<table>
<tr>
<th>Parameter</th>
<th>Required</th>
<th>Description</th>
</tr>
<tr>
<td valign="top"><code>level</code></td>
<td valign="top">yes</td>
<td valign="top">One of <code>"debug"</code>, <code>"info"</code>,
<code>"warning"</code>, <code>"error"</code>, <code>"fatal"</code>,
or <code>"panic"</code></td>
</tr>
</table>
## `reporting` section (optional)
The reporting section contains any configuration for useful for running the
service, such as reporting errors. Currently, we only support reporting errors
to [Bugsnag](https://bugsnag.com).
See [bugsnag-go](https://github.com/bugsnag/bugsnag-go/) for more information
about these configuration parameters.
```json
"reporting": {
"bugsnag": {
"api_key": "c9d60ae4c7e70c4b6c4ebd3e8056d2b8",
"release_stage": "production"
}
}
```
Note that this entire section is optional. However, if you would like to
report errors to Bugsnag, then you need to include a `bugsnag` subsection,
along with the required parameters below, to configure it.
**Bugsnag reporting:**
<table>
<tr>
<th>Parameter</th>
<th>Required</th>
<th>Description</th>
</tr>
<tr>
<td valign="top"><code>api_key</code></td>
<td valign="top">yes</td>
<td>The BugSnag API key to use to report errors.</td>
</tr>
<tr>
<td valign="top"><code>release_stage</code></td>
<td valign="top">yes</td>
<td>The current release stage, such as "production". You can
use this value to filter errors in the Bugsnag dashboard.</td>
</tr>
</table>

189
docs/notary-server.md Normal file
View File

@ -0,0 +1,189 @@
<!--[metadata]>
+++
title = "Notary Server"
description = "Description of the Notary Server"
keywords = ["docker, notary, notary-server"]
[menu.main]
parent="mn_notary"
+++
<![end-metadata]-->
# Notary Server
The Notary Server stores and updates the signed
[TUF metadata files](
https://github.com/theupdateframework/tuf/blob/develop/docs/tuf-spec.txt#L348)
for a repository. The root, snapshot, and targets metadata files are generated
and signed by clients, and the timestamp metadata file is generated and signed
by the server.
The server creates and stores timestamp keys for each repository (preferably
using a remote key storage/signing service such as
[Notary Signer](notary-signer.md)).
When clients upload metadata files, the server checks them for conflicts and
verifies the signatures and key references in the files. If everything
checks out, the server then signs the timestamp metadata file for the
repository, which certifies that the files the client uploaded are the most
recent for that repository.
### Authentication
Notary Server supports authentication from clients using [JWT](http://jwt.io/)
tokens. This requires an authorization server that manages access controls,
and a cert bundle from this authorization server containing the public key it
uses to sign tokens.
If token authentication is enabled on Notary Server, then any client that
does not have a token will be redirected to the authoriziation server.
The client will log in, obtain a token, and then present the token to
Notary Server on future requests.
Notary Server should be configured to trust signatures from that authorization
server.
Please see the docs for [Docker Registry v2 authentication](
https://github.com/docker/distribution/blob/master/docs/spec/auth/token.md)
for more information.
### Server storage
Notary Server uses MySQL as a backend for storing the timestamp
public keys and the TUF metadata for each repository. It relies on a signing
service to store the private keys.
### Signing service
We recommend deploying Notary Server with a separate, remote signing
service: [Notary Signer](notary-signer.md). This signing service generates
and stores the timestamp private keys and performs signing for the server.
By using remote a signing service, the private keys would never need to be
stored on the server itself.
Notary Signer supports mutual authentication - when you generate client
certificates for your deployment of Notary Server, please make
sure that the certificates **are not CAs**. Otherwise if the server is
compromised, it can sign any number of other client certs.
As an example, please see [this script](opensslCertGen.sh) to see how to
generate client SSL certs with basic constraints using OpenSSL.
### How to configure and run Notary Server
A JSON configuration file is used to configure Notary Server. Please see the
[Notary Server configuration document](notary-server-config.md)
for more details about the format of the configuration file.
You can also override the parameters of the configuration by
setting environment variables of the form `NOTARY_SERVER_var`.
`var` is the ALL-CAPS, `"_"`-delimited path of keys from the top level of the
configuration JSON.
For instance, if you wanted to override the storage URL of the Notary Server
configuration:
```json
"storage": {
"backend": "mysql",
"db_url": "dockercondemo:dockercondemo@tcp(notary-mysql)/dockercondemo"
}
```
the full path of keys is `storage -> db_url`. So the environment variable you'd
need to set would be `NOTARY_SERVER_STORAGE_DB_URL`.
For example, if running the binary:
```
$ export NOTARY_SERVER_STORAGE_DB_URL=myuser:mypass@tcp(my-db)/dbname?parseTime=true
$ NOTARY_SERVER_LOGGING_LEVEL=info notary-server -config /path/to/config.json
```
Note that you cannot override a key whose value is another map.
For instance, setting
`NOTARY_SERVER_STORAGE='{"storage": {"backend": "memory"}}'` will not
set in-memory storage. It just fails to parse. You can only override keys
whose values are strings or numbers.
#### Running Notary Server
Configuration options:
- `-config=<config file>` - The JSON configuration file.
- `-debug` - Passing this flag enables the debugging server on `localhost:8080`.
The debugging server provides [pprof](https://golang.org/pkg/net/http/pprof/)
and [expvar](https://golang.org/pkg/expvar/) endpoints.
Get the official Docker image, which comes with [some sane defaults](
https://github.com/docker/notary/blob/master/fixtures/server-config-local.json),
which include a remote trust service but local in-memory backing store.
You can override the default configuration with environment variables.
For example, if you wanted to run it with just a local signing service instead
(not recommended for production):
```
$ docker pull docker.io/docker/notary-server
$ docker run -p "4443:4443" \
-e NOTARY_SERVER_TRUST_SERVICE_TYPE=local
notary-server
```
Alternately, you can run the image with your own configuration file entirely.
You just need to mount your configuration directory, and then pass the path to
that configuration file as an argument to the `docker run` command:
```
$ docker run -p "4443:4443" \
-v /path/to/config/dir/on/host:/etc/docker/notary-server/ \
notary-server -config=/etc/docker/notary-server/config.json
```
You can also pass the `-debug` flag to the container in addition to the
configuration file, but the debug server port is not exposed by the container.
In order to view the debug endpoints, you will have to `docker exec` into
your container.
### What happens if the server is compromised
The server does not hold any keys for repositories, except the for timestamp
keys if you are using a local signing service, so the attacker cannot modify
the root, targets, or snapshots metadata.
If you are using a signer service, an attacker cannot get access to the
timestamp key either. They can use the server's credentials to get the signer
service to sign arbitrary data, such as an empty timestamp,
an invalid timestamp, or an old timestamp.
However, TOFU (trust on first use) would prevent the attacker from tricking
existing clients for existing repositories to download arbitrary data.
They would need the original root/target/snapshots keys to do that. The
attacker could only, by signing bad timestamps, prevent the such a user from
seeing any updated metadata.
The attacker can also make all new keys, and simply replace the repository
metadata with metadata signed with these new keys. New clients who have not
seen the repository before will trust this bad data, but older clients will
know that something is wrong.
### Ops features
Notary Server provides the following features for operational friendliness:
1. A health endpoint at `/_notary_server/health` which returns 200 and a
body of `{}` if the server is healthy, and a 500 with a map of
failed services if the server cannot access its storage backend.
If it cannot contact the signing service, an error will be logged but the
service will still be considered healthy, because it can still serve
existing metadata. It cannot accept updates, so the service is degraded.
1. A [Bugsnag](https://bugsnag.com) hook for error logs, if a Bugsnag
configuration is provided.
1. A [prometheus](http://prometheus.io/) endpoint at `/_notary_server/metrics`
which provides HTTP stats.

View File

@ -0,0 +1,290 @@
<!--[metadata]>
+++
title = "Notary Signer Configuration File"
description = "Specifies the configuration file for Notary Signer"
keywords = ["docker, notary, notary-signer, configuration"]
[menu.main]
parent="mn_notary"
+++
<![end-metadata]-->
# Notary Signer Configuration File
An example (full) server configuration file.
```json
{
"server": {
"http_addr": ":4444",
"grpc_addr": ":7899",
"tls_cert_file": "./fixtures/notary-signer.crt",
"tls_key_file": "./fixtures/notary-signer.key",
"client_ca_file": "./fixtures/notary-server.crt"
},
"logging": {
"level": 2
},
"storage": {
"backend": "mysql",
"db_url": "user:pass@tcp(notarymysql:3306)/databasename?parseTime=true",
"default_alias": "passwordalias1"
},
"reporting": {
"bugsnag": {
"api_key": "c9d60ae4c7e70c4b6c4ebd3e8056d2b8",
"release_stage": "production"
}
}
}
```
## `server` section (required)
"server" in this case refers to Notary Signer's HTTP/GRPC server, not
"Notary Server".
Example:
```json
"server": {
"http_addr": ":4444",
"grpc_addr": ":7899",
"tls_cert_file": "./fixtures/notary-signer.crt",
"tls_key_file": "./fixtures/notary-signer.key",
"client_ca_file": "./fixtures/notary-server.crt"
}
```
<table>
<tr>
<th>Parameter</th>
<th>Required</th>
<th>Description</th>
</tr>
<tr>
<td valign="top"><code>http_addr</code></td>
<td valign="top">yes</td>
<td valign="top">The TCP address (IP and port) to listen for HTTP
traffic on. Examples:
<ul>
<li><code>":4444"</code> means listen on port 4444 on all IPs (and
hence all interfaces, such as those listed when you run
<code>ifconfig</code>)</li>
<li><code>"127.0.0.1:4444"</code> means listen on port 4444 on
localhost only. That means that the server will not be
acessible except locally (via SSH tunnel, or just on a local
terminal)</li>
</ul>
</td>
</tr>
<tr>
<td valign="top"><code>grpc_addr</code></td>
<td valign="top">yes</td>
<td valign="top">The TCP address (IP and port) to listen for GRPC
traffic. Examples:
<ul>
<li><code>":7899"</code> means listen on port 7899 on all IPs (and
hence all interfaces, such as those listed when you run
<code>ifconfig</code>)</li>
<li><code>"127.0.0.1:7899"</code> means listen on port 7899 on
localhost only. That means that the server will not be
acessible except locally (via SSH tunnel, or just on a local
terminal)</li>
</ul>
</td>
</tr>
<tr>
<td valign="top"><code>tls_key_file</code></td>
<td valign="top">yes</td>
<td valign="top">The path to the private key to use for
HTTPS. The path is relative to the directory of the
configuration file.</td>
</tr>
<tr>
<td valign="top"><code>tls_cert_file</code></td>
<td valign="top">yes</td>
<td valign="top">The path to the certificate to use for
HTTPS. The path is relative to the directory of the
configuration file.</td>
</tr>
<tr>
<td valign="top"><code>client_ca_file</code></td>
<td valign="top">no</td>
<td valign="top">The root certificate to trust for
mutual authentication. If provided, any clients connecting to
Notary Signer will have to have a client certificate signed by
this root. If not provided, mutual authentication will not be
required. The path is relative to the directory of the
configuration file.</td>
</tr>
</table>
## `storage` section (required)
This is used to store encrypted priate keys. We only support MySQL or an
in-memory store, currently.
Example:
```json
"storage": {
"backend": "mysql",
"db_url": "user:pass@tcp(notarymysql:3306)/databasename?parseTime=true",
"default_alias": "passwordalias1"
}
```
<table>
<tr>
<th>Parameter</th>
<th>Required</th>
<th>Description</th>
</tr>
<tr>
<td valign="top"><code>backend</code></td>
<td valign="top">yes</td>
<td valign="top">Must be <code>"mysql"</code> or <code>"memory"</code>.
If <code>"memory"</code> is selected, the <code>db_url</code>
is ignored.</td>
</tr>
<tr>
<td valign="top"><code>db_url</code></td>
<td valign="top">yes if not <code>memory</code></td>
<td valign="top">The <a href="https://github.com/go-sql-driver/mysql">
the Data Source Name used to access the DB.</a>
(note: please include "parseTime=true" as part of the the DSN)</td>
</tr>
<tr>
<td valign="top"><code>default_alias</code></td>
<td valign="top">yes if not <code>memory</code></td>
<td valign="top">This parameter specifies the alias of the current
password used to encrypt the private keys in the DB. All new
private keys will be encrypted using this password, which
must also be provided as the environment variable
<code>NOTARY_SIGNER_&lt;DEFAULT_ALIAS_VALUE&gt;</code>.</td>
</tr>
</table>
#### Environment variables (required if using MySQL)
Notary Signer
[stores the private keys in encrypted form](notary-signer.md#signer-storage).
The alias of the passphrase used to encrypt the keys is also stored. In order
to encrypt the keys for storage and decrypt the keys for signing, the
passphrase must be passed in as an environment variable.
For example, the configuration above specifies the default password alias to be
`passwordalias1`.
If this configuration is used, then you must:
`export NOTARY_SIGNER_PASSWORDALIAS1=mypassword`
so that that Notary Signer knows to encrypt all keys with the passphrase
"mypassword", and to decrypt any private key stored with password alias
"passwordalias1" with the passphrase "mypassword".
Older passwords may also be provided as environment variables. For instance,
let's say that you wanted to change the password that is used to create new
keys (rotating the passphrase and re-encrypting all the private keys is not
supported yet).
You could change the config to look like:
```json
"storage": {
"backend": "mysql",
"db_url": "user:pass@tcp(notarymysql:3306)/databasename?parseTime=true",
"default_alias": "passwordalias2"
}
```
Then you can set:
```
export NOTARY_SIGNER_PASSWORDALIAS1=mypassword
export NOTARY_SIGNER_PASSWORDALIAS2=mynewfancypassword
```
That way, all new keys will be encrypted and decrypted using the passphrase
"mynewfancypassword", but old keys that were encrypted using the passphrase
"mypassword" can still be decrypted.
The environment variables for the older passwords are optional, but Notary
Signer will not be able to decrypt older keys if they are not provided, and
attempts to sign data using those keys will fail.
## `logging` section (optional)
The logging section sets the log level of the server. If it is not provided
or invalid, the signer defaults to an ERROR logging level.
Example:
```json
"logging": {
"level": "debug"
}
```
Note that this entire section is optional. However, if you would like to
specify a different log level, then you need the required parameters
below to configure it.
<table>
<tr>
<th>Parameter</th>
<th>Required</th>
<th>Description</th>
</tr>
<tr>
<td valign="top"><code>level</code></td>
<td valign="top">yes</td>
<td valign="top">One of <code>"debug"</code>, <code>"info"</code>,
<code>"warning"</code>, <code>"error"</code>, <code>"fatal"</code>,
or <code>"panic"</code></td>
</tr>
</table>
## `reporting` section (optional)
The reporting section contains any configuration for useful for running the
service, such as reporting errors. Currently, we only support reporting errors
to [Bugsnag](https://bugsnag.com).
See [bugsnag-go](https://github.com/bugsnag/bugsnag-go/) for more information
about these configuration parameters.
```json
"reporting": {
"bugsnag": {
"api_key": "c9d60ae4c7e70c4b6c4ebd3e8056d2b8",
"release_stage": "production"
}
}
```
Note that this entire section is optional. However, if you would like to
report errors to Bugsnag, then you need to include a `bugsnag` subsection,
along with the required parameters below, to configure it.
**Bugsnag reporting:**
<table>
<tr>
<th>Parameter</th>
<th>Required</th>
<th>Description</th>
</tr>
<tr>
<td valign="top"><code>api_key</code></td>
<td valign="top">yes</td>
<td>The BugSnag API key to use to report errors.</td>
</tr>
<tr>
<td valign="top"><code>release_stage</code></td>
<td valign="top">yes</td>
<td>The current release stage, such as "production". You can
use this value to filter errors in the Bugsnag dashboard.</td>
</tr>
</table>

148
docs/notary-signer.md Normal file
View File

@ -0,0 +1,148 @@
<!--[metadata]>
+++
title = "Notary Signer"
description = "Description of the Notary Signer"
keywords = ["docker, notary, notary-singer"]
[menu.main]
parent="mn_notary"
+++
<![end-metadata]-->
# Notary Signer
The Notary Signer is a remote store for private keys. It creates and delete
keys, signs data, and returns public key information on demand via its HTTP or
RPC api.
It is intended to be used as a remote RPC service for a
[Notary Server](notary-server.md)'s timestamp private keys.
### Authentication
Notary Signer supports mutual TLS authentication from
[Notary Server](notary-server.md).
Note that when you generate client certificates to be used with Notary Signer,
please make sure that the certificates **are not CAs**. Otherwise any client
that is compromised can sign any number of other client certs.
As an example, please see [this script](opensslCertGen.sh) to see how to
generate client SSL certs with basic constraints using OpenSSL.
### Signer storage
Notary Signer uses MySQL as a backend for storing the encrypted private keys
that is responsible for. The private keys[wrapped](
https://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-31#section-4.4)
and [encrypted](
https://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-31#section-4.8)
using [Javascript Object Signing and Encryption](
https://github.com/dvsekhvalnov/jose2go).
The passphrase used to encrypt the keys is passed as an environment variable,
the name of which [is specified by the confguration file](
notary-signer-config.md#storage-section-required).
### How to configure and run Notary Signer
A JSON configuration file is used to configure Notary Signer. Please see the
[Notary Signer configuration document](notary-signer-config.md)
for more details about the format of the configuration file.
You can also override the parameters of the configuration by
setting environment variables of the form `NOTARY_SIGNER_var`.
`var` is the ALL-CAPS, `"_"`-delimited path of keys from the top level of the
configuration JSON.
For instance, if you wanted to override the storage URL of the Notary Signer
configuration:
```json
"storage": {
"backend": "mysql",
"db_url": "dockercondemo:dockercondemo@tcp(notary-mysql)/dockercondemo"
}
```
the full path of keys is `storage -> db_url`. So the environment variable you'd
need to set would be `NOTARY_SIGNER_STORAGE_DB_URL`.
Note that you cannot override a key whose value is another map.
For instance, setting `NOTARY_SIGNER_STORAGE=""` will not disable the
MySQL storage. You can only override keys whose values are strings or numbers.
For example, if running the binary:
```
$ export NOTARY_SIGNER_STORAGE_DB_URL=myuser:mypass@tcp(my-db)/dbname?parseTime=true
$ NOTARY_SIGNER_LOGGING_LEVEL=info notary-signer -config /path/to/config.json
```
Note that you cannot override a key whose value is another map.
For instance, setting
`NOTARY_SIGNER_STORAGE='{"storage": {"backend": "memory"}}'` will not
set in-memory storage. It just fails to parse. You can only override keys
whose values are strings or numbers.
#### Running Notary Signer
Configuration options:
- `-config=<config file>` - The JSON configuration file.
- `-debug` - Passing this flag enables the debugging server on `localhost:8080`.
The debugging server provides [pprof](https://golang.org/pkg/net/http/pprof/)
and [expvar](https://golang.org/pkg/expvar/) endpoints.
Get the official Docker image, which comes with [some sane defaults](
https://github.com/docker/notary/blob/master/fixtures/signer-config-local.json),
which uses a local in-memory backing store (not recommended for production).
You can override the default configuration with environment variables.
For example, if you wanted to run it with your own DB
(recommended for production):
```
$ docker pull docker.io/docker/notary-signer
$ docker run -p "4444:4444" \
-e NOTARY_SIGNER_STORAGE_DB_BACKEND="mysql" \
-e NOTARY_SIGNER_STORAGE_DB_URL="myuser:mypass@tcp(my-db)/dbName"
notary-signer
```
Alternately, you can run the image with your own configuration file entirely.
You just need to mount your configuration directory, and then pass the path to
that configuration file as an argument to the `docker run` command:
```
$ docker run -p "4444:4444" \
-v /path/to/config/dir/on/host:/etc/docker/notary-signer \
notary-signer -config=/etc/docker/notary-server/config.json
```
You can also pass the `-debug` flag to the container in addition to the
configuration file, but the debug server port is not exposed by the container.
In order to view the debug endpoints, you will have to `docker exec` into
your container.
### What happens if the signer is compromised
All the timestamp private keys stored on the signer will be compromised, and
an attacker can sign anything they wish with the timestamp key.
However, the attacker cannot do anything useful with the timestamp keys unless
they also [compromise the Notary Server](
notary-server.md#what-happens-if-the-server-is-compromised)
The attacker can prevent Notary Signer from signing timestap metadata from
Notary Server and return invalid public key IDs when the Notary Server
requests it. This means an attacker can execute a denial of service attack
that prevents the Notary Server from being able to update any metadata.
### Ops features
Notary Signer provides the following features for operational friendliness:
1. A [Bugsnag](https://bugsnag.com) hook for error logs, if a Bugsnag
configuration is provided.

133
docs/opensslCertGen.sh Executable file
View File

@ -0,0 +1,133 @@
#!/usr/bin/env bash
# Script to be used for generating testing certs only - for a production system,
# a public CA or an internal CA should be used
CLIENT_USAGE=<<EOL
Generate a self-signed client cert and key to be used in mutual TLS.
${0} client [-o <output file prefix>]
Example:
${0} client -o clienttls
EOL
SERVER_USAGE=<<EOL
Generate a self-signed cert key and certificate.
${0} server -n <common name> [-o <output file prefix>]
[-r <root key if don't want it self-signed>]
[-a <subjectAltName>] [-a <subjectAltName>] ...
Example:
${0} server -o servertls -n notary-server -a DNS:notaryserver \
-a DNS:notary_server -a IP:127.0.0.1"
EOL
if [[ -z "${1}" ]]; then
printf "${CLIENT_USAGE}\n\n${SERVER_USAGE}\n\n"
exit 1
fi
OPENSSLCNF=
for path in /etc/openssl/openssl.cnf /etc/ssl/openssl.cnf /usr/local/etc/openssl/openssl.cnf; do
if [[ -e ${path} ]]; then
OPENSSLCNF=${path}
fi
done
if [[ -z ${OPENSSLCNF} ]]; then
printf "Could not find openssl.cnf"
exit 1
fi
if [[ "${1}" == "client" ]]; then
# Generate client keys - ensure that these keys are NOT CA's, otherwise
# any client that is compromised can sign any number of other client
# certs.
OUT="clienttls"
while getopts "o:" opt "${@:2}"; do
case "${opt}" in
o)
OUT="${OPTARG}"
;;
*)
printf "${CLIENT_USAGE}"
exit 1
;;
esac
done
openssl genrsa -out "${OUT}.key" 4096
openssl req -new -key "${OUT}.key" -out "${OUT}.csr" \
-subj '/C=US/ST=CA/L=San Francisco/O=Docker/CN=Notary Testing Client Auth'
cat > "${OUT}.cnf" <<EOL
[ssl_client]
basicConstraints = critical,CA:FALSE
nsCertType = critical, client
keyUsage = critical, digitalSignature, nonRepudiation
extendedKeyUsage = critical, clientAuth
authorityKeyIdentifier=keyid,issuer
EOL
openssl x509 -req -days 3650 -in "${OUT}.csr" -signkey "${OUT}.key" \
-out "${OUT}.crt" -extfile "${OUT}.cnf" -extensions ssl_client
rm "${OUT}.cnf" "${OUT}.csr"
fi
if [[ "${1}" == "server" ]]; then
# Create a server certificate
OUT="servertls"
COMMONNAME=
SAN=
while getopts ":o:n:a:" opt "${@:2}"; do
case "${opt}" in
o)
OUT="${OPTARG}"
;;
n)
COMMONNAME="${OPTARG}"
;;
a)
SAN="${SAN} ${OPTARG}"
;;
*)
printf "${SERVER_USAGE}\n\n"
exit 1
;;
esac
done
if [[ -z "${COMMONNAME}" ]]; then
printf "Please provide a common name/domain for the cert."
printf "\n\n${SERVER_USAGE}\n\n"
exit 1
fi
PPRINT_DOMAINS="${COMMONNAME}$(printf ", %s" "${SAN[@]}")"
printf "Generating server certificate for domains: ${PPRINT_DOMAINS}\n\n"
# see https://www.openssl.org/docs/manmaster/apps/x509v3_config.html for
# more information on extensions
cat "${OPENSSLCNF}" > "${OUT}.cnf"
cat >> "${OUT}.cnf" <<EOL
[ v3_req ]
basicConstraints = critical,CA:FALSE
nsCertType = critical, server
keyUsage = critical, digitalSignature, nonRepudiation
extendedKeyUsage = critical, serverAuth
authorityKeyIdentifier=keyid,issuer
EOL
if [[ -n "${SAN}" ]]; then
printf "subjectAltName=$(echo ${SAN[@]} | tr ' ' ,)" >> "${OUT}.cnf"
fi
openssl genrsa -out "${OUT}.key" 4096
openssl req -new -nodes -key "${OUT}.key" -out "${OUT}.csr" \
-subj "/C=US/ST=CA/L=San Francisco/O=Docker/CN=${COMMONNAME}" \
-config "${OUT}.cnf" -extensions "v3_req"
openssl x509 -req -days 3650 -in "${OUT}.csr" -signkey "${OUT}.key" \
-out "${OUT}.crt" -extensions v3_req -extfile "${OUT}.cnf"
fi