Skip to content

Commit

Permalink
RusticaAgent readme (#17)
Browse files Browse the repository at this point in the history
  • Loading branch information
obelisk authored Feb 6, 2022
1 parent cc66a70 commit d1d3106
Show file tree
Hide file tree
Showing 5 changed files with 148 additions and 51 deletions.
85 changes: 42 additions & 43 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,26 +1,45 @@
# Rustica

Rustica is a Yubikey backed SSHCA written in Rust. It is designed to be used with the accompanying `rustica-agent` tool for certificate handling but speaks gRPC so other integrations are possible. Rustica may use a Yubikey to store its private keys but also supports unencrypted OpenSSH format private key files.
Rustica is a Yubikey backed SSHCA written in Rust. It is designed to be used with the accompanying `rustica-agent` tool for certificate handling but speaks gRPC so other integrations are possible.

## Features
- Yubikey backed private keys
- Multiple Ways To Secure Private Keys
- File
- Yubikey 4/5 (non HSM)
- AmazonKMS
- Multiple Ways To Store Permissions
- Built in SQLite Database
- External Authorization Server
- Multiple Supported Logging Systems
- Stdout
- InfluxDB
- Splunk
- Just In Time Certificate Generation
- Local or remote decision engine support
- InfluxDB logging support
- Ability to use different CAs for user and host certs
- gRPC over TLS with required mTLS
- Use Different Keys For User and Hosts
- gRPC With mTLS
- Docker Scratch Container Support
- Extensive Feature Support

### JITC
The default for Rustica is to use Just In Time Certificate Generation meaning certificates are generated with a TTL of 10s (though this is adjustable on a key by key basis when using local authentication).
### Protected Key Material
Malicious access to the Rustica private key would result in serious compromise and thus Rustica provides two ways to mitigate this risk with Yubikey and AmazonKMS support. These signing modules use keys that cannot be exported resulting in more control over how the private key is being used. If using AmazonKMS, Amazon logs can be compared with Rustica logs to provide assurance no misuse has occured.

### Host Restriction
It is possible to grant a principal to a user that is only valid for certain hostnames. This is achieved by setting the restricted host permission in the database. When in use, the certificate generated will have the `force-command` CriticalOption enabled. This will force the user to run a bash script, loaded inside the cert, that contains all hostnames she is allowed to log in to. If the hostname name of the remote host does not match any in the list, the connection is closed.
### Just-In-Time Certificate Generation
Rustica and RusticaAgent work together to use short lived certificates that are generated on the fly only when needed. In effect this means your deployment will never need to deal with revocation because after ten seconds (the default) all issued certificates will have expired.

### Multiple Supported Logging Systems
All certificate issues can be logged to InfluxDB or Splunk if desired. See the logging submodule and the examples in `examples/` for more information.

### gRPC With mTLS
Rustica requires all connections be made using mutually authenticated TLS. This provides an extra level of authentication to the service and allows the tying of x509 certificates to SSH logins.

### Docker Scratch Container
When using either AmazonKMS or file based keys, Rustica can be compiled to a statically linked binary capable of running in a docker container with no external dependencies. The `docker/` folder contains `Dockerfile`s to compile Rustica this way for both amd64 (standard x86_64 architectures) and aarch64 (capable of running on Amazon Graviton servers).

### InfluxDB Logging
All certificate issues are logged to InfluxDB under the table `rustica_logs`. The log contains the fingerprint and some other metadata about the key used.
### Extensive Feature Support
Compile in only what you need to reduce binary size and dependency bloat. If you're planning on using AmazonKMS for storing your keys, Rustica can be compiled without Yubikey dependencies and vice versa. The same is also true for authorization, if using a remote authorization service, Rustica can be compiled without Diesel and SQLite.

### gRPC over TLS
There is a script in the resources folder to generate a self signed CA, along with the server cert. Rustica also requires mTLS for connections so an example client cert is also generated.
### EXPERIMENTAL: Host Restriction
It is possible to grant a principal to a user that is only valid for certain hostnames. This is achieved by setting the restricted host permission in the database. When in use, the certificate generated will have the `force-command` CriticalOption enabled. This will force the user to run a bash script, loaded inside the cert, that contains all hostnames she is allowed to log in to. If the hostname name of the remote host does not match any in the list, the connection is closed.

## Key Support
The following key types have Yubikey support:
Expand All @@ -35,40 +54,20 @@ The following key types have file support:
The following key types have no support:
- ECDSA 521

## Quickstart with Local Authorization
Create an example set of all required keys and certs:
`cd resources && ./create_certs.sh && cd ..`

Read the documentation in migrations/*/up.sql. It explains how the authorization system works in much more detail and how to authorize keys via database inserts. You may modify that file directly to build your example authorization db.
> Key IDs are the SHA256 hashes of the public portion of an SSH key. To show the fingerprint of an existing SSH key use:
> `ssh-keygen -lf <path to key>`
## Running An Example Deployment
This repository comes with a set of configuration files and database to be used as an example. New certificates can be easily generated using the scripts in `resources/`.

To build the db: `diesel migration run` from the rustica directory
This will create a authorization database and is specified to Rustica via environment variable `DATABASE_URL`.
> If you have issues running diesel you may need to install it with:
> `cargo install diesel_cli --no-default-features --features sqlite`
### Start Rustica
`rustica --config examples/rustica_local_file.toml`

Create a configuration based on `resources/rustica_example_local.toml` that contains the keys you generated with `create_certs.sh`.
### Pull a certificate with RusticaAgent
`rustica-agent --config examples/rustica_agent_local.toml -i`

Run Rustica (from the root the repository):
```
cargo run --bin rustica -- --config /path/to/your/config
```
The details of the certificate will be printed to the screen.

Finally run rustica-agent:
```
cargo run --bin rustica-agent -- \
--mtlscert resources/testhost.pem \
--mtlskey resources/testhost.key \
--server "https://localhost:50051" \
--capem resources/ca.pem \
-f resources/example_user_key \
-i
```
## Running Tests
Rustica ships with a small suite of integration tests aimed at ensuring some of the lesser known features do not get broken with updates. They require docker to be installed and can be run with the script in `tests/integration.sh`

If all has gone according to plan, you will see your certificate details print out on the screen (as well as the traditional encoding below)


## Security Warning
No review has been done. I built it because I thought people could find it useful. Be wary about using this in production without doing a thorough code review. If you find mistakes, please open a pull request or if it's a security bug, email me.

Expand Down
44 changes: 40 additions & 4 deletions rustica-agent/README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,43 @@
# Rustica Agent
# RusticaAgent

Rustica Agent the agent portion of the Rustica SSHCA. It is designed to be an SSH-Agent that uses keys loaded on a Yubikey though also supports a file, generally for requesting host certs which are not stored on Yubikeys. See command help for more complete documentation on use.
## Introduction
This is the agent portion of the project and manages keys as well as talking to Rustica and remote SSH hosts. It supports using a Yubikey for hardware backed SSH keys as well as adding external key files after the agent has started.

Rustica-Agent does not support the normal array of SSH-Agent calls, instead only supporting `Identities` and `Sign`. The reason for this is the call for identities initiates a request to a Rustica SSHCA server. Rustica is then expected to return a cert that contains all permissions your key is allowed, including all principals, and hosts. This SSH certificate is generated containing the public portion of the key present on your Yubikey (which may contain up to 24 keys, the key used is chosen at agent start either by command line flag or configuration file).
## Limitations
RusticaAgent does not support the normal array of SSH-Agent calls, the currently supported calls are:

It is possible to generate keys that require touch (and Rustica Agent allows you to create such keys) though SSH in unaware this is happening so you must notice your key is blinking and act accordingly. Pin is not supported.
- `Identities` - Called when connecting to a host or running `ssh-add -L`
- `Sign` - Called when connecting to a host and a public key has been accepted.
- `AddIdentity` - Called when running `ssh-add <path>`

## Usage
When using RusticaAgent it is preferable to provide a configuration file that contains all the parameters needed for normal operation. Any configuration file setting may be override by also providing it on the command line. RusticaAgent also only presents a single Yubikey backed key to the remote server but will present any other keys added with the `AddIdentity` call (keys added with `ssh-add`).

An example configuration file can be found at `../examples/rustica_agent_local.toml`. This file is compatible with a Rustica instance running `../examples/rustica_local_file.toml`.

## Examples
### Daemon Mode
`rustica-agent --config ../examples/rustica_agent_local.toml --slot R1 --socket /tmp/rustica_agent_tmp_socket`

> Note: RusticaAgent does not currently fork and background itself because it causes issues accessing USB devices on macOS.
This command:
- Loads all the settings from the config file (server, mTLS, TLS, etc)
- Overrides the key to use with the key in Yubikey slot R1
- Overrides the socket path to be used
- Starts listening for requests on that unix socket

### Immiediate Mode
`rustica-agent --config ../examples/rustica_agent_local.toml -i`

Fetches a certificate from the backend, pretty prints it to stdout, and quits. This is useful for double checking what permissions the server has granted you for a key.

### Immiediate Save Mode
`rustica-agent --config ../examples/rustica_agent_local.toml -i -o fetched-cert.pub`

This is exactly the same as above but outputs a valid SSH certificate for the provided key. This is useful for file based keys, particularly for refreshing server certificates.

### Provision Mode
`rustica-agent --config ../examples/rustica_agent_local.toml --slot R1 provision -r --subject ExampleKey`

This creates a new key pair in the given slot. The -r flag means that touch will be needed for every usage and the subject flag adds the given string to the CN of the generated x509 self signed certificate. Once this is complete, it will generated the attestation chain using the `F9` slot and attempt to register the key with the Rustica server. The backend will use the attestation chain to verify the key was generated on a hardware device and then permissions may be assigned to it.
4 changes: 2 additions & 2 deletions rustica-agent/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ pub fn configure() -> Result<RusticaAgentAction, ConfigurationError> {
)
.subcommand(
App::new("provision")
.about("Provision this slot with a new private key. The pin number must be passed as parameter here")
.about("Provision this slot with a new private key")
.arg(
Arg::new("management-key")
.help("Specify the management key")
Expand All @@ -208,7 +208,7 @@ pub fn configure() -> Result<RusticaAgentAction, ConfigurationError> {
)
.arg(
Arg::new("require-touch")
.help("Newly provisioned key requires touch for signing operations (touch cached for 15 seconds)")
.help("Require the key to always be tapped. If this is not selected, a tap will be required if not tapped in the last 15 seconds.")
.long("require-touch")
.short('r')
)
Expand Down
2 changes: 1 addition & 1 deletion rustica-agent/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,7 @@ pub fn provision_new_key(mut yubikey: YubikeySigner, pin: &str, subj: &str, mgm_
println!("You're creating a key that will require touch to use.");
TouchPolicy::Always
} else {
TouchPolicy::Never
TouchPolicy::Cached
};

if yubikey.yk.unlock(pin.as_bytes(), mgm_key).is_err() {
Expand Down
64 changes: 63 additions & 1 deletion rustica/README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,65 @@
# Rustica

The server portion of the Rustica project. It is designed to be used with rustica-agent for certificate handling but speaks generic gRPC so other integrations are possible. Rustica requires at least 1 + N standard (not HSM) Yubikey 4/5s: one for the server, and one for every client (though rustica-agent is capable of using a key file instead). You may also wish to generate keys directly on yubikeys (see the provision command in rustica-agent) so they never exist off the key.
The server portion of the Rustica project.

## Building
Depending on your needs, Rustica can be built with several different features to enable different use cases. To build them all (generally for testing), run:
`cargo build --features=all`. Below is summary of all the optional features, what they do, and how to configure them.

## amazon-kms
This compiles in support to use AmazonKMS as the backend for signing. This requires defining two key identifiers as well as AWS credentials that can access them. These keys must be asymettric, with the Sign/Verify capabilities. Encrypt/Decrypt will not work.

### Example Configuration
```toml
[signing."amazonkms"]
aws_access_key_id = "XXXXXXXXXXXXXXXXXXXX"
aws_secret_access_key = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
aws_region = "us-west-2"
user_key_id = "mrk-00000000000000000000000000000000"
user_key_signing_algorithm = "ECDSA_SHA_384"
host_key_id = "mrk-00000000000000000000000000000000"
host_key_signing_algorithm = "ECDSA_SHA_384"
```

### yubikey-support
This compiles in support to use a connected Yubikey 4/5 as the backend for signing. This requires defining two slot identifiers in the form of R followed by a number from 1 to 20 inclusive. For example R1, R9, R12, R20. These keys must be ECDSA 256/384. RSA keys are not supported at this time.

### Example Configuration
```toml
[signing."yubikey"]
user_slot = "R2"
host_slot = "R3"
```

## influx
Compiles in support to log to an InfluxDB backend. See the example configurations for more details on how to set this up.

### Example Configuration
```toml
[logging."influx"]
address = "http://some-local-influx-instance:8080"
database = "rustica"
dataset = "rustica_logs"
user = "influx_user"
password = "influx_password"
```

## splunk
Compiles in support to log to an Splunk backend. See the example configurations for more details on how to set this up.

### Example Configuration
```toml
[logging."splunk"]
token = "c46d7213-19ea-4a66-b83b-e4b06188d197"
url = "https://http-inputs-examplecompany.splunkcloud.com/services/collector"
timeout = 5
```

## local-db
Compiles in support for Rustica to handle authorization without talking to an external service. This requires a local SQLite database with all configured permissions and grants. See `rustica/migrations/2021-01-14-051956_hosts/up.sql` for a detailed explanation of how to configure this database.

### Example Configuration
```toml
[authorization."database"]
path = "examples/example.db"
```

0 comments on commit d1d3106

Please sign in to comment.