kes

package module
v0.8.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 5, 2020 License: AGPL-3.0 Imports: 14 Imported by: 20

README

KES

KES is a stateless and distributed key-management system for high-performance applications. We built KES as the bridge between modern applications - running as containers on Kubernetes - and centralized KMS solutions. Therefore, KES has been designed to be simple, scalable and secure by default. It has just a few knobs to tweak instead of a complex configuration and does not require a deep understanding of secure key-management or cryptography.

Architecture

KES

Install

Binary Releases
OS ARCH Binary
Linux amd64 linux-amd64
Linux arm linux-arm
Apple amd64 darwin-amd64
Windows amd64 windows-amd64

You can also verify the binary with minisign by downloading the corresponding .minisign signature file. Then run:

minisign -Vm <OS-ARCH>.zip -P RWRcOzQ19UrKLp4rkfssIwwWiWagluGJ8fpUBh/BeH+bZV3keFcdIJTF
Docker

Pull the latest release via:

docker pull minio/kes
Build from source
GO111MODULE=on go get github.com/minio/kes/cmd/kes

You will need a working Go environment. Therefore, please follow How to install Go. Minimum version required is go1.13

Getting Started

For your first steps checkout our Getting Started guide.

License

Use of KES is governed by the AGPLv3 license that can be found in the LICENSE file.

Documentation

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type AuditEvent added in v0.7.0

type AuditEvent struct {
	// Time is the point in time when the
	// audit event has been created.
	Time time.Time `json:"time"`

	// Request contains audit log information
	// about the request received from a client.
	Request AuditEventRequest `json:"request"`

	// Response contains audit log information
	// about the response sent to the client.
	Response AuditEventResponse `json:"response"`
}

AuditEvent is the event type the KES server produces when it has handled a request right before responding to the client.

When a clients subscribes to the KES server audit log it receives a stream of JSON-encoded audit events separated by a newline.

type AuditEventRequest added in v0.7.0

type AuditEventRequest struct {
	Path     string `json:"path"`
	Identity string `json:"identity"`
}

AuditEventRequest contains the audit information about a request sent by a client to a KES server.

In particular, it contains the identity of the client and other audit-related information.

type AuditEventResponse added in v0.7.0

type AuditEventResponse struct {
	StatusCode int           `json:"code"`
	Time       time.Duration `json:"time"`
}

AuditEventResponse contains the audit information about a response sent to a client by a KES server.

In particular, it contains the response status code and other audit-related information.

type AuditStream added in v0.7.0

type AuditStream struct {
	// contains filtered or unexported fields
}

AuditStream provides a convenient interface for iterating over a stream of AuditEvents. Successive calls to the Next method will step through the audit events of an io.Reader.

By default, the AuditStream breaks the underlying stream into lines and expects a JSON-encoded AuditEvent per line - unless the line is empty. Empty lines will be ignored.

Iterating stops at the end of the stream, the first I/O error, an AuditEvent event too large to fit in the buffer, or when the stream gets closed.

Closing an AuditStream closes the underlying io.Reader, if it implements io.Closer, and any subsequent call to Next will return false.

func NewAuditStream added in v0.7.0

func NewAuditStream(r io.Reader) *AuditStream

NewAuditStream returns a new AuditStream that splits r into lines and tries to parse each line as JSON-encoded AuditEvent.

Example
package main

import (
	"fmt"
	"strings"

	"github.com/minio/kes"
)

const AuditStream = `{"time":"2020-03-24T12:37:33Z","request":{"path":"/v1/log/audit/trace","identity":"dd46485bedc9ad2909d2e8f9017216eec4413bc5c64b236d992f7ec19c843c5f"},"response":{"code":200, "time":12106}}
{"time":"2020-03-24T12:38:02Z","request":{"path":"/v1/policy/list/*","identity":"dd46485bedc9ad2909d2e8f9017216eec4413bc5c64b236d992f7ec19c843c5f"},"response":{"code":200, "time":15572}}
{"time":"2020-03-24T12:39:02Z","request":{"path":"/v1/identity/list/*","identity":"dd46485bedc9ad2909d2e8f9017216eec4413bc5c64b236d992f7ec19c843c5f"},"response":{"code":200, "time":15953}}`

func main() {
	reader := strings.NewReader(AuditStream)

	stream := kes.NewAuditStream(reader)
	for stream.Next() {
		event := stream.Event()

		fmt.Println(event.Time)
	}
	if err := stream.Err(); err != nil {
		panic(err) // TODO: error handling
	}
}
Output:

2020-03-24 12:37:33 +0000 UTC
2020-03-24 12:38:02 +0000 UTC
2020-03-24 12:39:02 +0000 UTC

func (*AuditStream) Bytes added in v0.7.0

func (s *AuditStream) Bytes() []byte

Bytes returns the most recent raw AuditEvent content generated by a call to Next. It may not contain valid JSON.

The underlying array may point to data that will be overwritten by a subsequent call to Next. It does no allocation.

func (*AuditStream) Close added in v0.7.0

func (s *AuditStream) Close() (err error)

Close closes the underlying stream - i.e. the io.Reader if if implements io.Closer. After Close has been called once the Next method will return false.

func (*AuditStream) Err added in v0.7.0

func (s *AuditStream) Err() error

Err returns the first non-EOF error that was encountered while iterating over the stream and unmarshaling AuditEvents.

Err does not return any error returned from Close.

func (*AuditStream) Event added in v0.7.0

func (s *AuditStream) Event() AuditEvent

Event returns the most recent AuditEvent generated by a call to Next.

func (*AuditStream) Next added in v0.7.0

func (s *AuditStream) Next() bool

Next advances the stream to the next AuditEvent, which will then be available through the Event and Bytes method. It returns false when the stream iteration stops - i.e. by reaching the end of the stream, closing the stream or in case of an error. After Next returns false, the Err method will return any error that occurred while iterating and parsing the stream.

type Client

type Client struct {
	// Endpoint is the KES server HTTPS endpoint.
	// For example: https://127.0.0.1:7373
	Endpoint string

	// HTTPClient is the HTTP client.
	//
	// The HTTP client uses its http.RoundTripper
	// to send requests resp. receive responses.
	//
	// It must not be modified concurrently.
	HTTPClient http.Client
}

Client is a KES client. Usually, a new client is instantiated via the NewClient or NewClientWithConfig functions.

In general, a client just requires: • a KES server endpoint • a X.509 certificate for authentication.

However, custom transport protcols, timeouts, connection pooling, etc. can be specified via a custom http.RoundTripper. For example:

client := &Client{
    Endpoint:   "https:127.0.0.1:7373",
    HTTPClient: http.Client{
        Transport: &http.Transport{
           // specify custom behavior...

           TLSClientConfig: &tls.Config{
               Certificates: []tls.Certificates{clientCert},
           },
        },
    },
 }

A custom transport protocol can be used via a custom implemention of the http.RoundTripper interface.

func NewClient

func NewClient(endpoint string, cert tls.Certificate) *Client

NewClient returns a new KES client with the given KES server endpoint that uses the given TLS certficate mTLS authentication.

The TLS certificate must be valid for client authentication.

NewClient uses an http.Transport with reasonable defaults.

func NewClientWithConfig added in v0.8.0

func NewClientWithConfig(endpoint string, config *tls.Config) *Client

NewClientWithConfig returns a new KES client with the given KES server endpoint that uses the given TLS config for mTLS authentication.

Therefore, the config.Certificates must contain a TLS certificate that is valid for client authentication.

NewClientWithConfig uses an http.Transport with reasonable defaults.

func (*Client) AssignIdentity

func (c *Client) AssignIdentity(policy string, id Identity) error

func (*Client) CreateKey

func (c *Client) CreateKey(key string) error

CreateKey tries to create a new cryptographic key with the specified name.

The key will be generated by the server. The client application does not have the cryptographic key at any point in time.

func (*Client) Decrypt added in v0.8.0

func (c *Client) Decrypt(key string, ciphertext, context []byte) ([]byte, error)

Decrypt tries to decrypt the given ciphertext with the specified key and returns plaintext on success.

The context value must match the context used when the ciphertext was produced. If no context was used the context value should be set to nil.

func (*Client) DeleteKey

func (c *Client) DeleteKey(key string) error

DeleteKey deletes the given key. Once a key has been deleted all data, that has been encrypted with it, cannot be decrypted anymore.

func (*Client) DeletePolicy

func (c *Client) DeletePolicy(name string) error

func (*Client) ForgetIdentity

func (c *Client) ForgetIdentity(id Identity) error

func (*Client) GenerateKey added in v0.8.0

func (c *Client) GenerateKey(key string, context []byte) (DEK, error)

GenerateKey generates a new data encryption key (DEK). The context is cryptographically bound to the DEK.

A DEK has a plaintext and a ciphertext representation. The plaintext should be used to perform a cryptographic operation - for example: encrypt some data.

The ciphertext is the result of encrypting the plaintext with the given key. It should be stored at a durable location but does not need to stay secret. The ciphertext can only be decrypted with the given key at the server.

Whenever an application needs the DEK's plaintext representation it should send the ciphertext to the server via the Decrypt method.

The context is cryptographically bound to the ciphertext and the same context value must be provided whenever the ciphertext should be decrypted. An application either must remember the context or must be able to re-generate it.

If an application does not wish to specify a context value it can set it to nil.

func (*Client) ImportKey added in v0.5.0

func (c *Client) ImportKey(name string, key []byte) error

ImportKey tries to import the given key as cryptographic key with the specified name.

In contrast to CreateKey, the client specifies, and therefore, knows the value of the cryptographic key.

func (*Client) ListIdentities

func (c *Client) ListIdentities(pattern string) (map[Identity]string, error)

func (*Client) ListPolicies

func (c *Client) ListPolicies(pattern string) ([]string, error)

func (*Client) ReadPolicy

func (c *Client) ReadPolicy(name string) (*Policy, error)

func (*Client) TraceAuditLog added in v0.5.0

func (c *Client) TraceAuditLog() (*AuditStream, error)

TraceAuditLog subscribes to the KES server audit log and returns a stream of audit events on success.

It returns ErrNotAllowed if the client does not have sufficient permissions to subscribe to the audit log.

func (*Client) TraceErrorLog added in v0.7.0

func (c *Client) TraceErrorLog() (*ErrorStream, error)

TraceErrorLog subscribes to the KES server error log and returns a stream of error events on success.

It returns ErrNotAllowed if the client does not have sufficient permissions to subscribe to the error log.

func (*Client) Version added in v0.6.0

func (c *Client) Version() (string, error)

Version tries to fetch the version information from the KES server.

func (*Client) WritePolicy

func (c *Client) WritePolicy(name string, policy *Policy) error

type DEK added in v0.8.0

type DEK struct {
	Plaintext  []byte
	Ciphertext []byte
}

DEK is a data encryption key. It has a plaintext and a ciphertext representation.

Applications should use the plaintext for cryptographic operations and store the ciphertext at a durable location.

If the DEK is used to e.g. encrypt some data then it's safe to store the DEK's ciphertext representation next to the encrypted data. The ciphertext representation does not need to stay secret.

DEK implements binary as well as text marshaling. However, only the ciphertext representation gets encoded. The plaintext should never be stored anywhere. Therefore, after unmarshaling there will be no plaintext representation. To obtain it the ciphertext must be decrypted.

func (DEK) MarshalBinary added in v0.8.0

func (d DEK) MarshalBinary() ([]byte, error)

MarshalBinary returns DEK's ciphertext representation. It never returns an error.

func (DEK) MarshalText added in v0.8.0

func (d DEK) MarshalText() ([]byte, error)

MarshalText encodes the DEK's ciphertext into a base64-encoded text and returns the result.

It never returns an error.

func (*DEK) UnmarshalBinary added in v0.8.0

func (d *DEK) UnmarshalBinary(data []byte) error

UnmarshalBinary sets DEK's ciphertext to the given data. It never returns an error and DEK's plaintext will be nil.

func (*DEK) UnmarshalText added in v0.8.0

func (d *DEK) UnmarshalText(text []byte) error

UnmarshalText tries to decode a base64-encoded text and sets DEK's ciphertext to the decoded data.

It returns an error if text is not base64-encoded.

UnmarshalText sets DEK's plaintext to nil.

type Error added in v0.6.0

type Error struct {
	// contains filtered or unexported fields
}

Error is the type of client-server API errors. A Client returns an Error if a server responds with a well-formed error message.

An Error contains the HTTP status code sent by the server. Errors with the same status code and error message are equal. In particular:

ErrKeyExists == NewError(400, "key does already exist") // true

The client may distinguish errors as following:

switch err := client.CreateKey("example-key"); err {
    case nil: // Success!
    case ErrKeyExists:
       // The key "example-key" already exists.
    case ErrNotAllowed:
       // We don't have the permission to create this key.
    default:
       // Something else went wrong.
}
var (
	ErrKeyNotFound Error = NewError(http.StatusNotFound, "key does not exist")
	ErrKeyExists   Error = NewError(http.StatusBadRequest, "key does already exist")
	ErrKeySealed   Error = NewError(http.StatusForbidden, "key is sealed")
	ErrNotAllowed  Error = NewError(http.StatusForbidden, "prohibited by policy")
)

func NewError

func NewError(code int, msg string) Error

NewError returns a new Error with the given HTTP status code and error message.

Two errors with the same status code and error message are equal.

func (Error) Error added in v0.6.0

func (e Error) Error() string

func (Error) Status added in v0.6.0

func (e Error) Status() int

Status returns the HTTP status code of the error.

type ErrorEvent added in v0.7.0

type ErrorEvent struct {
	Message string `json:"message"` // The logged error message
}

ErrorEvent is the event type the KES server produces when it encounters and logs an error.

When a clients subscribes to the KES server error log it receives a stream of JSON-encoded error events separated by a newline.

type ErrorStream added in v0.7.0

type ErrorStream struct {
	// contains filtered or unexported fields
}

ErrorStream provides a convenient interface for iterating over a stream of ErrorEvents. Successive calls to the Next method will step through the error events of an io.Reader.

By default, the ErrorStream breaks the underlying stream into lines and expects a JSON-encoded ErrorEvent per line - unless the line is empty. Empty lines will be ignored.

Iterating stops at the end of the stream, the first I/O error, a ErrorEvent event too large to fit in the buffer, or when the stream gets closed.

Closing an ErrorStream closes the underlying io.Reader, if it implements io.Closer, and any subsequent call to Next will return false.

func NewErrorStream added in v0.7.0

func NewErrorStream(r io.Reader) *ErrorStream

NewErrorStream returns an new ErrorStream that splits r into lines and tries to parse each line as JSON-encoded ErrorEvent.

Example
package main

import (
	"fmt"
	"strings"

	"github.com/minio/kes"
)

const ErrorStream = `{"message":"2020/03/24 14:46:10 aws: secret was not encrypted with '4f9147d9-a676-47cd-ad3f-3485abf9123d'"}
{"message":"2020/03/24 14:46:17 aws: the CMK 'ff8e2c25-b259-4f74-a001-c7b62d17e0a4' does not exist"}
{"message":"2020/03/24 14:46:25 aws: the CMK '8fc17745-9647-4797-b170-afd8b52ed7c0' cannot be used for decryption"}`

func main() {
	reader := strings.NewReader(ErrorStream)

	stream := kes.NewErrorStream(reader)
	for stream.Next() {
		event := stream.Event()

		fmt.Println(event.Message)
	}
	if err := stream.Err(); err != nil {
		panic(err) // TODO: error handling
	}
}
Output:

2020/03/24 14:46:10 aws: secret was not encrypted with '4f9147d9-a676-47cd-ad3f-3485abf9123d'
2020/03/24 14:46:17 aws: the CMK 'ff8e2c25-b259-4f74-a001-c7b62d17e0a4' does not exist
2020/03/24 14:46:25 aws: the CMK '8fc17745-9647-4797-b170-afd8b52ed7c0' cannot be used for decryption

func (*ErrorStream) Bytes added in v0.7.0

func (s *ErrorStream) Bytes() []byte

Bytes returns the most recent raw ErrorEvent content generated by a call to Next. It may not contain valid JSON.

The underlying array may point to data that will be overwritten by a subsequent call to Next. It does no allocation.

func (*ErrorStream) Close added in v0.7.0

func (s *ErrorStream) Close() (err error)

Close closes the underlying stream - i.e. the io.Reader if if implements io.Closer. After Close has been called once the Next method will return false.

func (*ErrorStream) Err added in v0.7.0

func (s *ErrorStream) Err() error

Err returns the first non-EOF error that was encountered while iterating over the stream and unmarshaling ErrorEvents.

Err does not return any error returned from Close.

func (*ErrorStream) Event added in v0.7.0

func (s *ErrorStream) Event() ErrorEvent

Event returns the most recent ErrorEvent generated by a call to Next.

func (*ErrorStream) Next added in v0.7.0

func (s *ErrorStream) Next() bool

Next advances the stream to the next ErrorEvent, which will then be available through the Event and Bytes method. It returns false when the stream iteration stops - i.e. by reaching the end of the stream, closing the stream or in case of an error. After Next returns false, the Err method will return any error that occurred while iterating and parsing the stream.

type Identity

type Identity string

An Identity should uniquely identify a client and is computed from the X.509 certificate presented by the client during the TLS handshake using an IdentityFunc.

const IdentityUnknown Identity = ""

IdentityUnknown is the identity returned by an IdentityFunc if it cannot map a particular X.509 certificate to an actual identity.

func (Identity) IsUnknown

func (id Identity) IsUnknown() bool

IsUnknown returns true if and only if the identity is IdentityUnknown.

func (Identity) String

func (id Identity) String() string

String returns the string representation of the identity.

type Policy

type Policy struct {
	// contains filtered or unexported fields
}

func NewPolicy

func NewPolicy(patterns ...string) (*Policy, error)

func (Policy) MarshalJSON

func (p Policy) MarshalJSON() ([]byte, error)

func (*Policy) String

func (p *Policy) String() string

func (*Policy) UnmarshalJSON

func (p *Policy) UnmarshalJSON(b []byte) error

func (*Policy) Verify

func (p *Policy) Verify(r *http.Request) error

Directories

Path Synopsis
cmd
kes
internal
aws
fs
Package fs implements a key-value store that stores keys as file names and values as file content.
Package fs implements a key-value store that stores keys as file names and values as file content.
log
mem
Package mem implements an in-memory key-value store.
Package mem implements an in-memory key-value store.
vault
Package vault implements a secret key store that stores secret keys as key-value entries on the Hashicorp Vault K/V secret backend.
Package vault implements a secret key store that stores secret keys as key-value entries on the Hashicorp Vault K/V secret backend.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL