Skip to content
This repository has been archived by the owner on Jul 12, 2023. It is now read-only.

Use new filesystem key manager #488

Merged
merged 2 commits into from
Sep 8, 2020
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 53 additions & 41 deletions docs/development.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,47 +33,36 @@ represent best practices.

Use the same Google credentials as you used in the previous steps.

1. Create a Google Cloud KMS key ring and two signing keys:
1. Change directory into this repository:

```sh
gcloud kms keyrings create "signing" \
--location "us"

gcloud kms keys create "token-signing" \
--location "us" \
--keyring "signing" \
--purpose "asymmetric-signing" \
--default-algorithm "ec-sign-p256-sha256"

gcloud kms keys create "certificate-signing" \
--location "us" \
--keyring "signing" \
--purpose "asymmetric-signing" \
--default-algorithm "ec-sign-p256-sha256" \
```text
cd /path/to/exposure-notifications-verification-server
```

To get the resource names to the keys (for use below):

```sh
gcloud kms keys describe "token-signing" \
--location "us" \
--keyring "signing"
1. Bootstrap the local key management system:

gcloud kms keys describe "certificate-signing" \
--location "us" \
--keyring "signing"
```text
go run ./tools/gen-keys
```

This will output some environment variables. **Save these environment
variables for the next step!**

The default development setup uses a local, on-disk key manager to persist
across server restarts. The production installation recommends a hosted key
management service like Google Cloud KMS. It is possible to use Google Cloud
KMS locally by following the instructions in the production setup guide.

1. Create a `.env` file with your configuration. This will aid future
development since you can `source` this file instead of trying to find all
these values again.

```sh
# Create a file named .env with these contents
export PROJECT_ID="YOUR_PROJECT_ID" # TODO: replace
# Google project configuration.
export PROJECT_ID="TODO"
export GOOGLE_CLOUD_PROJECT="${PROJECT_ID}"

# Get these values from the firebase console
# Get these values from the firebase console.
export FIREBASE_API_KEY="TODO"
export FIREBASE_PROJECT_ID="${PROJECT_ID}"
export FIREBASE_MESSAGE_SENDER_ID="TODO"
Expand All @@ -85,35 +74,58 @@ represent best practices.
export FIREBASE_PRIVACY_POLICY_URL="TODO"
export FIREBASE_TERMS_OF_SERVICE_URL="TODO"

# Populate these with the resource IDs from above. These values will be of
# the format:
#
# projects/ID/locations/us/keyRings/signing/cryptoKeys/token-signing/cryptoKeyVersions/1Z
export TOKEN_SIGNING_KEY="TODO"
export CERTIFICATE_SIGNING_KEY="TODO"

# Disable local observability
# Disable local observability.
export OBSERVABILITY_EXPORTER="NOOP"

# Configure a CSRF auth key. Create your own with `openssl rand -base64 32`.
# Configure CSRF for preventing request forgery. Create your own with:
#
# openssl rand -base64 32
#
export CSRF_AUTH_KEY="RcCNhTkS9tSDMSGcl4UCa1FUg9GmctkJpdI+eqZ+3v4="

# Configure cookie encryption, the first is 64 bytes, the second is 32.
# Create your own with `openssl rand -base64 NUM` where NUM is 32 or 64
# Create your own values with:
#
# openssl rand -base64 NUM
#
# where NUM is 32 or 64, respectively.
export COOKIE_KEYS="ARLaFwAqBGIkm5pLjAveJuahtCnX2NLoAUz2kCZKrScUaUkEaxHSvJLVYb5yAPCc441Cho5n5yp8jdEmy6hyig==,RLjcRZeqc07s6dh3OK4CM1POjHDZHC+usNU1w/XNTjM="

# Configure certificate key management. The CERTIFICATE_SIGNING_KEY should
# be the value output in the previous step.
export CERTIFICATE_KEY_MANAGER="FILESYSTEM"
export CERTIFICATE_KEY_FILESYSTEM_ROOT="$(pwd)/local"
export CERTIFICATE_SIGNING_KEY="TODO" # (e.g. "/system/certificate-signing/1122334455")

# Configure token key management. The TOKEN_SIGNING_KEY should be the value
# output in the previous step.
export TOKEN_KEY_MANAGER="FILESYSTEM"
export TOKEN_KEY_FILESYSTEM_ROOT="$(pwd)/local"
export TOKEN_SIGNING_KEY="TODO" # (e.g. "/system/token-signing/1122334455")

# Configure the database key manager. The CERTIFICATE_SIGNING_KEYRING and
# DB_ENCRYPTION_KEY should be the values output in the previous step.
export DB_KEY_MANAGER="FILESYSTEM"
export DB_KEY_FILESYSTEM_ROOT="$(pwd)/local"
export CERTIFICATE_SIGNING_KEYRING="TODO" # (e.g. "/realm")
export DB_ENCRYPTION_KEY="TODO" # (e.g. "/system/database-encryption")

# Use an in-memory key manager for encrypting values in the database. Create
# your own encryption key with `openssl rand -base64 64`.
export KEY_MANAGER="IN_MEMORY"
export DB_ENCRYPTION_KEY="O04ZjG4WuoceRd0k2pTqDN0r8omr6sbFL0U3T5b12Lo="

# Database HMAC keys - these should be at least 64 bytes, preferably 128
# Create your own with `openssl rand -base64 128`.
# Database HMAC keys - these should be at least 64 bytes, preferably 128.
# Create your own with:
#
# openssl rand -base64 128
#
export DB_APIKEY_DATABASE_KEY="RlV/RBEt0lDeK54r8U9Zi7EDFZid3fiKM2HFgjR9sZGMb+duuQomjGdNKYnzrNyKgeTBcc1V4qVs6fBrN6IFTLbgkp/u52MGhSooAQI4EuZ6JFuyxQBeu54Ia3mihF111BMcCWpHDg2MAh8k8f669plEQaqoQFg3GThP/Lx1OY0="
export DB_APIKEY_SIGNATURE_KEY="HFeglmupbtv/I2X04OQRl1V7mcvfAXuv8XtmIFYV6aYsPuwQVFtXDlfFrjouYT2Z6kYln7B90RcutHJNjpPDRkyBQ28HtWmid3dr0tpJ1KiiK5NGG7JS9mU8fCvEYklw5RV+1f8qN13nWzHpW8/RQw9rR/vQGy90yL5/aydBuVA="
export DB_VERIFICATION_CODE_DATABASE_KEY="YEN4+tnuf1DzQPryRzrPVilqT0Q2TO8IIg3C8prvXWGAaoABOWACl79hS40OneuaU8GsQHwhJ13wM2A5ooyOq+uqxCjrqVJZZXPU5xzl/6USEYAp4z2b0ZYrfkx2SRk1o9HfFi1RMqpaBf1TRIbsNOK9hNRG3nS2It49y6mR1ho="

# Enable dev mode
# Enable dev mode. Do not enable dev mode or database dev mode in production
# environments.
export DEV_MODE=1
export DB_DEBUG=1
```
Expand Down
77 changes: 77 additions & 0 deletions docs/production.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,81 @@

This page includes helpful tips for configuring things in production:

## Key management

The default production key management solution is [Google Cloud KMS][gcp-kms].
If you are using the Terraform configurations, the system will automatically
bootstrap and create the key rings and keys in Cloud KMS. If you are not using
the Terraform configurations, follow this guide to create the keys manually:

1. Create a Google Cloud KMS key ring

```sh
gcloud kms keyrings create "en-verification" \
--location "us"
```

Note that the "us" location is configurable. If you choose a different
location, substitute it in all future commands.

1. Create two signing keys - one for tokens and one for certificates:

```sh
gcloud kms keys create "token-signing" \
--location "us" \
--keyring "en-verification" \
--purpose "asymmetric-signing" \
--default-algorithm "ec-sign-p256-sha256" \
--protection-level "hsm"
```

```sh
gcloud kms keys create "certificate-signing" \
--location "us" \
--keyring "en-verification" \
--purpose "asymmetric-signing" \
--default-algorithm "ec-sign-p256-sha256" \
--protection-level "hsm"
```

Note the "us" location is configurable, but the key purpose and algorithm
must be the same as above.

1. Create an encryption key for encrypting values in the database:

```sh
gcloud kms keys create "database-encrypter" \
--location "us" \
--keyring "en-verification" \
--purpose "encryption" \
--rotation-period "30d" \
--protection-level "hsm"
```

1. Get the resource names to the keys:

```sh
gcloud kms keys describe "token-signing" \
--location "us" \
--keyring "en-verification"
```

```sh
gcloud kms keys describe "certificate-signing" \
--location "us" \
--keyring "en-verification"
```

```sh
gcloud kms keys describe "database-encrypter" \
--location "us" \
--keyring "en-verification"
```

1. Provide these values as the `TOKEN_SIGNING_KEY`, `CERTIFICATE_SIGNING_KEY`,
and `DB_ENCRYPTION_KEY` respectively in the environment where the services
will run. You also need to grant the service permission to use the keys.


## Observability (tracing and metrics)

Expand Down Expand Up @@ -220,3 +295,5 @@ lifetime is short, it is probably safe to remove the key beyond 30 days.

If you are using system keys, the system administrator will handle rotation. If
you are using realm keys, you can generate new keys in the UI.

[gcp-kms]: https://cloud.google.com/kms
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ module github.com/google/exposure-notifications-verification-server

go 1.14

replace github.com/google/exposure-notifications-server => ../exposure-notifications-server
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will need to update this after merging google/exposure-notifications-server#957

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pls update to v0.7.0


require (
cloud.google.com/go v0.65.0
cloud.google.com/go/firestore v1.3.0 // indirect
Expand Down
2 changes: 0 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -478,8 +478,6 @@ github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEW
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/exposure-notifications-server v0.6.2-0.20200901223640-ce4572602269 h1:BfZd4EeRIpiPdsz2DQZffmmogpglXR9tVgtxMZiEe18=
github.com/google/exposure-notifications-server v0.6.2-0.20200901223640-ce4572602269/go.mod h1:MzHiq/DqSLt+7GbS3vDJtAbFj2qjPFxLYa8ciRZuUeo=
github.com/google/flatbuffers v1.11.0/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
Expand Down
19 changes: 0 additions & 19 deletions pkg/database/database.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,25 +89,6 @@ func (c *Config) Load(ctx context.Context) (*Database, error) {
logger.Errorf("key manager does not support the SigningKeyManager interface, falling back to single verification signing key")
}

// If the key manager is in-memory, accept the key as a base64-encoded
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm glad to get rid of this - it was... hacky.

// in-memory key.
if c.Keys.KeyManagerType == keys.KeyManagerTypeInMemory {
typ, ok := keyManager.(keys.EncryptionKeyAdder)
if !ok {
return nil, fmt.Errorf("key manager does not support adding keys")
}

key, err := base64util.DecodeString(c.EncryptionKey)
if err != nil {
return nil, fmt.Errorf("encryption key is invalid: %w", err)
}

if err := typ.AddEncryptionKey("database-encryption-key", key); err != nil {
return nil, fmt.Errorf("failed to add encryption key: %w", err)
}
c.EncryptionKey = "database-encryption-key"
}

return &Database{
config: c,
keyManager: keyManager,
Expand Down
7 changes: 4 additions & 3 deletions pkg/database/database_util.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ package database
import (
"context"
"crypto/rand"
"encoding/base64"
"os"
"strconv"
"testing"
Expand Down Expand Up @@ -106,9 +105,8 @@ func NewTestDatabaseWithConfig(tb testing.TB) (*Database, *Config) {
},

Keys: keys.Config{
KeyManagerType: keys.KeyManagerTypeInMemory,
KeyManagerType: keys.KeyManagerTypeFilesystem,
},
EncryptionKey: base64.RawStdEncoding.EncodeToString(generateKeys(tb, 1, 32)[0]),
}

// Wait for the container to start - we'll retry connections in a loop below,
Expand All @@ -121,6 +119,9 @@ func NewTestDatabaseWithConfig(tb testing.TB) (*Database, *Config) {
tb.Fatal(err)
}

db.keyManager = keys.TestKeyManager(tb)
db.config.EncryptionKey = keys.TestEncryptionKey(tb, db.keyManager)

if err := db.Open(ctx); err != nil {
tb.Fatal(err)
}
Expand Down
Loading