Enforce valid UTF8 keys on all backends.

The use of non-UTF8 keys with the DynamoDB back-end causes a failure
deep within the AWS request deserialization code, presenting a
non-obvious failure to the user.

This change adds validation to all backends that requires all keys
are valid UTF8 strings. It also adds a warning to the Backend
interface declaration that the keys may be constrained to valid
UTF8.

Other changes include:
 * Updating the `Backend` conformance test suite to not present binary
   keys to the various backend implementations.
 * Adding a `region` value to the DynamoDB configuration test input
 * Adding missing imports to `_test` files.
 * Updating build instructions in README
This commit is contained in:
Trent Clarke 2021-03-10 11:28:32 +11:00 committed by Russell Jones
parent f17625c1a8
commit 3149d0b953
6 changed files with 31 additions and 13 deletions

View file

@ -34,7 +34,9 @@ const (
Forever time.Duration = 0
)
// Backend implements abstraction over local or remote storage backend
// Backend implements abstraction over local or remote storage backend.
// Item keys are assumed to be valid UTF8, which may be enforced by the
// various Backend implementations.
type Backend interface {
// Create creates item if it does not exist
Create(ctx context.Context, i Item) (*Lease, error)

View file

@ -10,17 +10,18 @@ WARNING: Using DynamoDB involves reccuring charge from AWS.
The table created by the backend will provision 5/5 R/W capacity.
It should be covered by the free tier.
### Building
### Running tests
DynamoDB backend is not enabled by default. To enable it you have to
compile Teleport with `dynamo` build flag.
To build Teleport with DynamoDB enabled, run:
The DynamodDB tests are not run by default. To run them locally, try:
```
ADDFLAGS='-tags dynamodb' make teleport
go test -tags dynamodb -v ./lib/backend/dynamo
```
*NOTE:* you will need to provide a AWS credentials & a default region
(e.g. in your `~/.aws/credentials` & `~/.aws/config` files, or via
environment vars) for the tests to work.
### Quick Start
Add this storage configuration in `teleport` section of the config file (by default it's `/etc/teleport.yaml`):

View file

@ -20,6 +20,7 @@ package dynamo
import (
"context"
"os"
"testing"
"time"
@ -100,5 +101,5 @@ func (s *DynamoDBSuite) TestWatchersClose(c *check.C) {
}
func (s *DynamoDBSuite) TestLocking(c *check.C) {
s.suite.Locking(c)
s.suite.Locking(c, s.bk)
}

View file

@ -20,6 +20,7 @@ import (
"context"
"regexp"
"time"
"unicode/utf8"
"github.com/gravitational/trace"
@ -38,7 +39,7 @@ var blacklistPattern = regexp.MustCompile(`//`)
// isKeySafe checks if the passed in key conforms to whitelist
func isKeySafe(s []byte) bool {
return whitelistPattern.Match(s) && !blacklistPattern.Match(s)
return whitelistPattern.Match(s) && !blacklistPattern.Match(s) && utf8.Valid(s)
}
// Sanitizer wraps a Backend implementation to make sure all values requested

View file

@ -46,6 +46,10 @@ func (s *Suite) TestSanitizeBucket(c *check.C) {
inKey: RangeEnd([]byte("/")),
outError: false,
},
{
inKey: RangeEnd([]byte("Malformed \xf0\x90\x28\xbc UTF8")),
outError: true,
},
}
for i, tt := range tests {

View file

@ -20,6 +20,7 @@ package test
import (
"context"
"encoding/hex"
"math/rand"
"sync/atomic"
"time"
@ -100,12 +101,20 @@ func (s *BackendSuite) CRUD(c *check.C) {
c.Assert(err, check.IsNil)
c.Assert(string(out.Value), check.Equals, string(item.Value))
// put with binary key and value succeeds
key := make([]byte, 1024)
rand.Read(key)
// put with large key and binary value succeeds.
// NB: DynamoDB has a maximum overall key length of 1024 bytes, so
// we need to pick a random key size that will still fit in 1KiB
// when combined with the (currently) 33-byte prefix prepended
// by `prefix()`, so:
// (485 bytes * 2 (for hex encoding)) + 33 = 1003
// which gives us a little bit of room to spare
keyBytes := make([]byte, 485)
rand.Read(keyBytes)
key := hex.EncodeToString(keyBytes)
data := make([]byte, 1024)
rand.Read(data)
item = backend.Item{Key: prefix(string(key)), Value: data}
item = backend.Item{Key: prefix(key), Value: data}
_, err = s.B.Put(ctx, item)
c.Assert(err, check.IsNil)