Documentation ¶
Overview ¶
Package clortho provides key management for clients.
The two most important types in this package are the Resolver and the KeyRing. A KeyRing is essentially a local cache of keys, accessible via the key's kid (key ID) attribute. A Resolver resolves keys by key ID from an external source, optionally using a KeyRing as a cache.
A Refresher can be used to asynchronously update keys in one or more KeyRing instances or arbitrary client code that handles refresh events.
Index ¶
- Constants
- Variables
- type CancelListenerFunc
- type Config
- type ContentMeta
- type Expander
- type Fetcher
- type FetcherOption
- type FileLoader
- type HTTPClient
- type HTTPEncoder
- type HTTPLoader
- type HTTPLoaderError
- type InvalidFormatError
- type JWKKeyParser
- type JWKSetParser
- type Key
- type KeyAccessor
- type KeyRing
- type Keys
- type Loader
- type LoaderOption
- type NotAFileError
- type Parser
- type ParserOption
- type RefreshConfig
- type RefreshEvent
- type RefreshListener
- type RefreshSource
- type Refresher
- type RefresherOption
- type ResolveConfig
- type ResolveEvent
- type ResolveListener
- type Resolver
- type ResolverOption
- type ResolverRefresherOption
- type Thumbprinter
- type UnsupportedFormatError
- type UnsupportedSchemeError
Constants ¶
const ( // DefaultRefreshInterval is used as the base interval between key refreshes when an // interval couldn't be determined any other way. DefaultRefreshInterval = time.Hour * 24 // DefaultRefreshMinInterval is the hard minimum for the base interval between key refreshes // regardless of how the base interval was determined. DefaultRefreshMinInterval = time.Minute * 10 // DefaultRefreshJitter is the default randomization factor for key refreshes. DefaultRefreshJitter = 0.1 )
const ( // MediaTypeJSON is the media type for JSON data. By default, content with this media type // may contain either a single JWK or a JWK set. MediaTypeJSON = "application/json" // SuffixJSON is the file suffix for JSON data. By default, files with this suffix may // contain either a single JWK or a JWK Set. SuffixJSON = ".json" // MediaTypeJWK is the media type for a single JWK. MediaTypeJWK = "application/jwk+json" // SuffixJWK is the file suffix for a single JWK. SuffixJWK = ".jwk" // MediaTypeJWKSet is the media type for a JWK set. MediaTypeJWKSet = "application/jwk-set+json" // SuffixJWKSet is the file suffix for a JWK set. SuffixJWKSet = ".jwk-set" // MediaTypePEM is the media type for a PEM-encoded key. MediaTypePEM = "application/x-pem-file" // SuffixPEM is the file suffix for a PEM-encoded key. SuffixPEM = ".pem" )
const (
// KeyIDParameter is the name of the URI template parameter for expanding key URIs.
KeyIDParameterName = "keyID"
)
Variables ¶
var ( // ErrRefresherStarted is returned by Refresher.Start if the Refresher is running. ErrRefresherStarted = errors.New("That refresher has already been started") // ErrRefresherStopped is returned by Refresher.Stop if the Refresher is not running. ErrRefresherStopped = errors.New("That refresher is not running") )
var ( // ErrNoTemplate indicates that no URI template is available for that Resolver's method. ErrNoTemplate = errors.New("No URI template expander has been configured for that method.") // ErrKeyNotFound indicates that a key could not be resolved, e.g. a key ID did not exist. ErrKeyNotFound = errors.New("No such key exists") )
Functions ¶
This section is empty.
Types ¶
type CancelListenerFunc ¶
type CancelListenerFunc func()
CancelListenerFunc removes the listener it's associated with and cancels any future events sent to that listener.
A CancelListenerFunc is idempotent: after the first invocation, calling this closure will have no effect.
type Config ¶
type Config struct { // Resolve is the subset of configuration that establishes how individual // keys will be resolved (or, fetched) on demand. Resolve ResolveConfig `json:"resolve" yaml:"resolve"` // Refresh is the subset of configuration that configures how keys are // refreshed asynchronously. Refresh RefreshConfig `json:"refresh" yaml:"refresh"` }
Config configures clortho from (possibly) externally unmarshaled locations.
type ContentMeta ¶
type ContentMeta struct { // Format describes the type of key content. This will typically be either // a file suffix (e.g. .pem, .jwk) or a media type (e.g. application/json, application/json+jwk). // A custom Loader is free to produce its own format values, which must be // understood by a corresponding Parser. Format string // TTL is the length of time this content is considered current. A Refresher will // use this value to determine when to load content again. TTL time.Duration // LastModified is the modification timestamp of the content. For files, this will be // the FileInfo.ModTime() value. For HTTP responses, this will be the Last-Modified header. // // In the case of HTTP, this field is also used to supply a Last-Modified header in the // request. LastModified time.Time }
ContentMeta holds metadata about a piece of content.
type Expander ¶
type Expander interface { // Expand takes a value map and returns the URI resulting from that expansion. Expand(interface{}) (string, error) }
Expander is the strategy for expanding a URI template.
func NewExpander ¶
NewExpander constructs an Expander from a URI template.
type Fetcher ¶
type Fetcher interface { // Fetch grabs keys from a URI. The prev ContentMeta may either be an empty struct, e.g. ContentMeta{}, // or the ContentMeta from a previous call to Fetch. // // This method ensures that each key has a key ID. For keys that do not have a key ID from their source, // a key ID is generated using a thumbprint hash. Fetch(ctx context.Context, location string, prev ContentMeta) (keys []Key, next ContentMeta, err error) }
Fetcher handles fetching keys from URI locations. This is the typical application-layer interface. Generally, clients should use this interface over Loader and Parser.
func NewFetcher ¶
func NewFetcher(options ...FetcherOption) (Fetcher, error)
NewFetcher produces a Fetcher from a set of configuration options.
type FetcherOption ¶
type FetcherOption interface {
// contains filtered or unexported methods
}
FetcherOption is a configuration option passed to NewFetcher.
func WithKeyIDHash ¶
func WithKeyIDHash(h crypto.Hash) FetcherOption
WithKeyIDHash sets the cryptographic hash used to generate key IDs for keys which do not have them. By default, crypto.SHA256 is used.
func WithLoader ¶
func WithLoader(l Loader) FetcherOption
WithLoader defines the Loader strategy for a Fetcher. By default, a Fetcher uses a Loader created with no options.
func WithParser ¶
func WithParser(p Parser) FetcherOption
WithParser defines the Parser strategy for a Fetcher. By default, a Fetcher uses a Parser created with no options.
type FileLoader ¶
type FileLoader struct { // Root is the relative root against which all location paths are resolved. // This field is required. // // By default, the Loader create via NewLoader uses os.DirFS("/") for this // field. Root fs.FS }
FileLoader is a Loader implementation that reads content from a file system. All location paths are relative to a supplied root.
func (FileLoader) LoadContent ¶
func (fl FileLoader) LoadContent(_ context.Context, location string, meta ContentMeta) ([]byte, ContentMeta, error)
type HTTPClient ¶
HTTPClient is the minimal interface required by a component which can handle HTTP transactions with a server. *http.Client implements this interface.
type HTTPEncoder ¶
HTTPEncoder is a strategy closure type for modifying an HTTP request prior to issuing it through a client.
type HTTPLoader ¶
type HTTPLoader struct { // Client is the HTTP client used to transact with HTTP servers. // If unset, http.DefaultClient is used. Client HTTPClient // Encoders holds an optional slice of HTTPEncoder instances that are used // to modify requests prior to sending them to the Client. Encoders []HTTPEncoder // Timeout is an optional timeout for each HTTP operation. If unset, // no timeout is used. Timeout time.Duration }
HTTPLoader is a Loader strategy for obtaining content from HTTP servers.
func (HTTPLoader) LoadContent ¶
func (hl HTTPLoader) LoadContent(ctx context.Context, location string, meta ContentMeta) ([]byte, ContentMeta, error)
type HTTPLoaderError ¶
HTTPLoaderError indicates that an error occurred when transacting with a HTTP-based source of key material.
func (*HTTPLoaderError) Error ¶
func (hle *HTTPLoaderError) Error() string
type InvalidFormatError ¶ added in v0.0.4
type InvalidFormatError struct {
Format string
}
InvalidFormatError indicates that a format cannot be associated with a Parser because the format string is invalid. For example, format strings that contain semi-colons (;) are invalid because matching a Parser by MIME parameters is not supported.
func (InvalidFormatError) Error ¶ added in v0.0.4
func (ife InvalidFormatError) Error() string
Error satisfies the error interface.
type JWKKeyParser ¶
type JWKKeyParser struct {
Options []jwk.ParseOption
}
JWKKeyParser parses content as a single JWK.
type JWKSetParser ¶
type JWKSetParser struct {
Options []jwk.ParseOption
}
JWKSetParser parses content as a JWK set.
type Key ¶
type Key interface { Thumbprinter // KeyID is the identifier for this Key. This method corresponds to the kid field of a JWK. // Note that a KeyID is entirely optional. This method can return the empty string. KeyID() string // KeyType is the type of this Key, e.g. EC, RSA, etc. This method corresponds to // the kty field of a JWK. // // A KeyType is required. This method always returns a non-empty string. KeyType() string // KeyUsage describes how this key is allowed to be used. This method corresponds to // the use field of a JWK. // // A KeyUsage is optional. This method can return the empty string. KeyUsage() string // Raw is the raw key, e.g. *rsa.PublicKey, *rsa.PrivateKey, etc. This is the actual underlying // cryptographic key that should be used. Raw() interface{} // Public is the public portion of the raw key. If this key is already a public key, this method // returns the same key as Raw. Public() crypto.PublicKey }
Key is the minimal interface for cryptographic keys. Once created, a Key is immutable.
func EnsureKeyID ¶
EnsureKeyID conditionally assigns a key ID to a given key. The updated Key is returned, along with any error from the hash.
If k already has a key ID, it is returned as is with no error.
If k does not have a key ID, a thumbprint is generated using the supplied hash. The returned key will be a copy of k with the newly generated key ID. If an error occurred, then k is returned as is.
type KeyAccessor ¶
type KeyAccessor interface { // Get returns the Key associated with the given key identifier (kid). // If there is no such key, the second return is false. Get(keyID string) (Key, bool) // Len returns the number of keys currently in this collection. Len() int }
KeyAccessor is a read-only interface to a set of keys.
type KeyRing ¶
type KeyRing interface { KeyAccessor RefreshListener // Add allows ad hoc keys to be added to this ring. Any key that has // no key ID will be skipped. // // This method returns the actual count of keys added. This will include // keys already in the ring, since those will be overwritten with the new Key object. Add(...Key) int // Remove allows add hoc keys to be removed from this ring. Any key ID that isn't // in this ring is ignored. The actual count of deleted keys is returned. Remove(keyIDs ...string) int }
KeyRing is a client-side cache of keys. Implementations are always safe for concurrent access.
A KeyRing can consume events from a Refresher, which will update the ring's set of keys.
func NewKeyRing ¶
NewKeyRing constructs a KeyRing with an optional set of initial keys. Any key that has no key ID is skipped.
type Keys ¶
type Keys []Key
Keys is a sortable slice of Key instances. Sorting is done by keyID, ascending. Keys with no keyID are sorted after those that have a keyID.
func (Keys) AppendKeyIDs ¶ added in v0.0.3
AppendKeyIDs appends the key Id of each key to the supplied slice, then returns the result.
type Loader ¶
type Loader interface { // LoadContent retrieves the key content from location. Location must be a URL parseable // with url.Parse. // // This method returns a ContentMeta describing useful characteristics of the content, mostly around // caching. This returned metadata can be passed to subsequent calls to make key retrieval more // efficient. LoadContent(ctx context.Context, location string, meta ContentMeta) ([]byte, ContentMeta, error) }
Loader handles the retrieval of content from an external location.
func NewLoader ¶
func NewLoader(options ...LoaderOption) (Loader, error)
NewLoader builds a Loader from a set of options.
By default, the returned Loader handles http, https, and file locations. The default loader, when there is no scheme, is a file loader.
type LoaderOption ¶
type LoaderOption interface {
// contains filtered or unexported methods
}
LoaderOption represents a configurable option for building a Loader.
func WithSchemes ¶
func WithSchemes(l Loader, schemes ...string) LoaderOption
WithSchemes registers a loader as handling one or more URI schemes. Use this to add custom schemes or to override one of the schemes a loader handles by default.
By default, a Loader created with NewLoader handles the file, http, and https schemes, as well as file paths without a scheme.
type NotAFileError ¶
type NotAFileError struct {
Location string
}
NotAFileError indicates that a file URI didn't refer to a system file, but instead referred to a directory, pipe, etc.
func (*NotAFileError) Error ¶
func (nafe *NotAFileError) Error() string
type Parser ¶
type Parser interface { // Parse parses data, expected to be in the given format, into zero or more Keys. // If only one key is present in the data, this method returns a 1-element slice. // // Format is an opaque string which used as a key to determine which parsing algorithm // to apply to the data. Most commonly, format is either a file suffix (including the // leading '.') or a media type such as application/json. If format contains any MIME // parameters, e.g. text/xml;charset=utf-8, they are ignored. // // Custom parsers should usually avoid trying to validate format. This is because // a Parser might be registered with a nonstandard format. The format is available to // custom parser code primarily for debugging. Parse(format string, data []byte) ([]Key, error) }
Parser turns raw data into one or more Key instances.
func NewParser ¶
func NewParser(options ...ParserOption) (Parser, error)
NewParser returns a Parser tailored with the given options.
The returned Parser handles the following formats by default:
application/json application/jwk+json application/jwk-set+json application/x-pem-file .json .jwk .jwk-set .pem
A caller can use WithFormats to change the parser associated with a format or to register a Parser for a new, custom format.
type ParserOption ¶
type ParserOption interface {
// contains filtered or unexported methods
}
ParserOption allows tailoring of the Parser returned by NewParser.
func WithFormats ¶
func WithFormats(p Parser, formats ...string) ParserOption
WithFormats associates a Parsers with one or more formats. Each format is an opaque string simply used as a way to look up a parsing algorithm. Typically, a format is a file suffix (including the leading '.') or a media type such as application/json.
type RefreshConfig ¶
type RefreshConfig struct { // Sources are the set of refresh sources to be polled for key material. // // If this slice is empty, a Refresher is still created, but it will // do nothing. // // If there are multiple sources with the same URI, an error is raised. Sources []RefreshSource `json:"sources" yaml:"sources"` }
RefreshConfig configures all aspects of key refresh.
type RefreshEvent ¶
type RefreshEvent struct { // URI is the source of the keys. URI string // Err is the error that occurred while trying to interact with the URI. // This field can be nil to indicate no error. When this field is non-nil, // the Keys field will be populated with the last known valid set of keys // from the given URI. Err error // Keys represents the complete set of keys from the URI. When Err is not nil, // this field will be set to the last known valid set of keys. // // This field will be sorted by KeyID. Keys Keys // New are the keys that a brand new with this event. These keys will be // included in the Keys field. // // This field will be sorted by KeyID. New Keys // Deleted are the keys that are now missing from the refreshed keys. // These keys will not be in the Keys field. These keys will have been present // in the previous event(s). // // This field will be sorted by KeyID. Deleted Keys }
RefreshEvent represents a set of keys from a given URI that has been asynchronously fetched.
type RefreshListener ¶
type RefreshListener interface { // OnRefreshEvent receives a refresh event. This method must not panic. OnRefreshEvent(RefreshEvent) }
RefreshListener is a sink for RefreshEvents.
type RefreshSource ¶
type RefreshSource struct { // URI is the location where keys are served. By default, clortho supports // file://, http://, and https:// URIs, as well as standard file system paths // such as /etc/foo/bar.jwk. // // This field is required and has no default. URI string `json:"uri" yaml:"uri"` // Interval is the base time between refreshing keys from this source. This value // is used when the source URI doesn't specify any sort of time-to-live or expiry. // For example, if an http source doesn't specify a Cache-Control header, this value is used. // // If this field is not positive, DefaultRefreshInterval is used. Interval time.Duration `json:"interval" yaml:"interval"` // MinInterval specifies the absolute minimum time between key refreshes from this source. // Regardless of HTTP headers, the Interval field, etc, key refreshes will not occur more // often than this field indicates. // // If this value is not positive, DefaultRefreshMinInterval is used. MinInterval time.Duration `json:"minInterval" yaml:"minInterval"` // Jitter is the randomization factor applied to the interval between refreshes. No matter how // the interval is determined (e.g. Cache-Control, Interval field, etc), a random value between // [1-Jitter,1+Jitter]*interval is used as the actual time before the next attempted refresh. // // Valid values are between 0.0 and 1.0, exclusive. If this value is outside that range, // including being unset, DefaultRefreshJitter is used instead. Jitter float64 `json:"jitter" yaml:"jitter"` }
RefreshSource describes a single location where keys are retrieved on a schedule.
type Refresher ¶
type Refresher interface { // Start bootstraps tasks that fetch keys and dispatch events to any listeners. // Keys will arrive asynchronously to any registered listeners. // // If this Refresher has already been started, this method returns ErrRefresherStarted. Start(context.Context) error // Stop shuts down all refresh tasks. // // If this Refresher is not running, this method returns ErrRefresherStopped. Stop(context.Context) error // AddListener registers a channel that receives refresh events. No caching of events // is done. The supplied listener will receive events the next time any of the key // sources are queried. // // The returned closure can be used to cancel refreshes sent to the listener. Clients // are not required to use this closure, particularly if the listener is active for the // life of the application. AddListener(l RefreshListener) CancelListenerFunc }
Refresher handles asynchronously refreshing sets of keys from one or more sources.
func NewRefresher ¶
func NewRefresher(options ...RefresherOption) (Refresher, error)
NewRefresher constructs a Refresher using the supplied options. Without any options, a default Loader and Parser are created and used.
type RefresherOption ¶
type RefresherOption interface {
// contains filtered or unexported methods
}
RefresherOption is a configurable option passed to NewRefresher.
func WithSources ¶
func WithSources(sources ...RefreshSource) RefresherOption
WithSources associates external sources of keys with a Refresher. This option is cumulative: all sources from each call to WithSources will be added to the configured Refresher.
type ResolveConfig ¶
type ResolveConfig struct { // Template is a URI template used to fetch keys. This template may // use a single parameter named keyID, e.g. http://keys.com/{keyID}. Template string `json:"template" yaml:"template"` // Timeout refers to the maximum time to wait for a refresh operation. // There is no default for this field. If unset, no timeout is applied. Timeout time.Duration `json:"timeout" yaml:"timeout"` }
ResolveConfig configures how to fetch individual keys on demand.
type ResolveEvent ¶
type ResolveEvent struct { // URI is the actual, expanded URI used to obtain the key material. URI string // KeyID is the key ID that was resolved. KeyID string // Key is the key material that was returned from the URI. Key Key // Err holds any error that occurred while trying to fetch key material. // If this field is set, Key will be nil. Err error }
ResolveEvent holds information about a key ID that has been resolved.
type ResolveListener ¶
type ResolveListener interface { // OnResolveEvent receives notifications for attempts to resolve keys. This // method must not panic. OnResolveEvent(ResolveEvent) }
ResolveListener is a sink for ResolveEvents.
type Resolver ¶
type Resolver interface { // Resolve attempts to locate a key with a given keyID (kid). Resolve(ctx context.Context, keyID string) (Key, error) // AddListener attaches a sink for ResolveEvents. Only events that // occur after this method call will be dispatched to the given listener. AddListener(ResolveListener) CancelListenerFunc }
Resolver allows synchronous resolution of keys.
func NewResolver ¶
func NewResolver(options ...ResolverOption) (Resolver, error)
NewResolver constructs a Resolver from a set of options. By default, a Resolver uses the DefaultLoader() and DefaultParser().
If no URI template is supplied, this function returns ErrNoTemplate.
type ResolverOption ¶
type ResolverOption interface {
// contains filtered or unexported methods
}
ResolverOption represents a configurable option passed to NewResolver.
func WithKeyIDExpander ¶
func WithKeyIDExpander(e Expander) ResolverOption
WithKeyIDExpander establishes the Expander strategy used for resolving individual keys. Callers may use this option to associate a custom Expander with a Resolver.
func WithKeyIDTemplate ¶
func WithKeyIDTemplate(t string) ResolverOption
WithKeyIDTemplate establishes the URI template used for resolving individual keys.
func WithKeyRing ¶
func WithKeyRing(kr KeyRing) ResolverOption
WithKeyRing sets a KeyRing to act as a cache for the Resolver. By default, a Resolver is not associated with any KeyRing.
type ResolverRefresherOption ¶
type ResolverRefresherOption interface { ResolverOption RefresherOption }
ResolverRefresherOption is a configurable option that applies to both a Refresher and a Resolver.
func WithConfig ¶ added in v0.0.3
func WithConfig(cfg Config) ResolverRefresherOption
WithConfig uses a Config struct to configure a Refresher and/or Resolver.
func WithFetcher ¶
func WithFetcher(f Fetcher) ResolverRefresherOption
WithFetcher configures the Fetcher instance used by either a Resolver or a Refresher. By default, DefaultFetcher() is used.
type Thumbprinter ¶
type Thumbprinter interface { // Thumbprint produces the RFC 7638 thumbprint hash, using the supplied algorithm. The // typical value to pass to this method is crypto.SHA256. // // The returned byte slice contains the raw bytes of the hash. To convert it to a string // conforming to RFC 7638, use base64.RawURLEncoding.EncodeToString. Thumbprint(crypto.Hash) ([]byte, error) }
Thumbprinter is implemented by anything that can produce a secure thumbprint of itself.
type UnsupportedFormatError ¶
type UnsupportedFormatError struct {
Format string
}
UnsupportedFormatError indicates that a Parser cannot parse a given format.
func (UnsupportedFormatError) Error ¶
func (ufe UnsupportedFormatError) Error() string
Error implements the error interface.
type UnsupportedSchemeError ¶
type UnsupportedSchemeError struct {
Location string
}
UnsupportedSchemeError indicates that a URI's scheme was not registered and couldn't be handled by a Loader.
func (*UnsupportedSchemeError) Error ¶
func (use *UnsupportedSchemeError) Error() string
Source Files ¶
Directories ¶
Path | Synopsis |
---|---|
Package clorthofx provides integration with go.uber.org/fx.
|
Package clorthofx provides integration with go.uber.org/fx. |
Package clorthometrics integrates clortho events with Prometheus metrics.
|
Package clorthometrics integrates clortho events with Prometheus metrics. |
Package clorthozap provides basic integration with go.uber.org/zap.
|
Package clorthozap provides basic integration with go.uber.org/zap. |