blob: bad6ed58e33f47dff9ee6d10c84fd69b6d134c5b [file] [log] [blame]
Matthias Andreas Benkard832a54e2019-01-29 09:27:38 +01001/*
2Copyright 2017 The Kubernetes Authors.
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8 http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15*/
16
17// Package value contains methods for assisting with transformation of values in storage.
18package value
19
20import (
21 "bytes"
22 "fmt"
23 "sync"
24 "time"
25)
26
27func init() {
28 RegisterMetrics()
29}
30
31// Context is additional information that a storage transformation may need to verify the data at rest.
32type Context interface {
33 // AuthenticatedData should return an array of bytes that describes the current value. If the value changes,
34 // the transformer may report the value as unreadable or tampered. This may be nil if no such description exists
35 // or is needed. For additional verification, set this to data that strongly identifies the value, such as
36 // the key and creation version of the stored data.
37 AuthenticatedData() []byte
38}
39
40// Transformer allows a value to be transformed before being read from or written to the underlying store. The methods
41// must be able to undo the transformation caused by the other.
42type Transformer interface {
43 // TransformFromStorage may transform the provided data from its underlying storage representation or return an error.
44 // Stale is true if the object on disk is stale and a write to etcd should be issued, even if the contents of the object
45 // have not changed.
46 TransformFromStorage(data []byte, context Context) (out []byte, stale bool, err error)
47 // TransformToStorage may transform the provided data into the appropriate form in storage or return an error.
48 TransformToStorage(data []byte, context Context) (out []byte, err error)
49}
50
51type identityTransformer struct{}
52
53// IdentityTransformer performs no transformation of the provided data.
54var IdentityTransformer Transformer = identityTransformer{}
55
56func (identityTransformer) TransformFromStorage(b []byte, ctx Context) ([]byte, bool, error) {
57 return b, false, nil
58}
59func (identityTransformer) TransformToStorage(b []byte, ctx Context) ([]byte, error) {
60 return b, nil
61}
62
63// DefaultContext is a simple implementation of Context for a slice of bytes.
64type DefaultContext []byte
65
66// AuthenticatedData returns itself.
67func (c DefaultContext) AuthenticatedData() []byte { return []byte(c) }
68
69// MutableTransformer allows a transformer to be changed safely at runtime.
70type MutableTransformer struct {
71 lock sync.RWMutex
72 transformer Transformer
73}
74
75// NewMutableTransformer creates a transformer that can be updated at any time by calling Set()
76func NewMutableTransformer(transformer Transformer) *MutableTransformer {
77 return &MutableTransformer{transformer: transformer}
78}
79
80// Set updates the nested transformer.
81func (t *MutableTransformer) Set(transformer Transformer) {
82 t.lock.Lock()
83 t.transformer = transformer
84 t.lock.Unlock()
85}
86
87func (t *MutableTransformer) TransformFromStorage(data []byte, context Context) (out []byte, stale bool, err error) {
88 defer func(start time.Time) {
89 RecordTransformation("from_storage", start, err)
90 }(time.Now())
91 t.lock.RLock()
92 transformer := t.transformer
93 t.lock.RUnlock()
94 return transformer.TransformFromStorage(data, context)
95}
96func (t *MutableTransformer) TransformToStorage(data []byte, context Context) (out []byte, err error) {
97 defer func(start time.Time) {
98 RecordTransformation("to_storage", start, err)
99 }(time.Now())
100 t.lock.RLock()
101 transformer := t.transformer
102 t.lock.RUnlock()
103 return transformer.TransformToStorage(data, context)
104}
105
106// PrefixTransformer holds a transformer interface and the prefix that the transformation is located under.
107type PrefixTransformer struct {
108 Prefix []byte
109 Transformer Transformer
110}
111
112type prefixTransformers struct {
113 transformers []PrefixTransformer
114 err error
115}
116
117var _ Transformer = &prefixTransformers{}
118
119// NewPrefixTransformers supports the Transformer interface by checking the incoming data against the provided
120// prefixes in order. The first matching prefix will be used to transform the value (the prefix is stripped
121// before the Transformer interface is invoked). The first provided transformer will be used when writing to
122// the store.
123func NewPrefixTransformers(err error, transformers ...PrefixTransformer) Transformer {
124 if err == nil {
125 err = fmt.Errorf("the provided value does not match any of the supported transformers")
126 }
127 return &prefixTransformers{
128 transformers: transformers,
129 err: err,
130 }
131}
132
133// TransformFromStorage finds the first transformer with a prefix matching the provided data and returns
134// the result of transforming the value. It will always mark any transformation as stale that is not using
135// the first transformer.
136func (t *prefixTransformers) TransformFromStorage(data []byte, context Context) ([]byte, bool, error) {
137 for i, transformer := range t.transformers {
138 if bytes.HasPrefix(data, transformer.Prefix) {
139 result, stale, err := transformer.Transformer.TransformFromStorage(data[len(transformer.Prefix):], context)
140 // To migrate away from encryption, user can specify an identity transformer higher up
141 // (in the config file) than the encryption transformer. In that scenario, the identity transformer needs to
142 // identify (during reads from disk) whether the data being read is encrypted or not. If the data is encrypted,
143 // it shall throw an error, but that error should not prevent subsequent transformers from being tried.
144 if len(transformer.Prefix) == 0 && err != nil {
145 continue
146 }
147 return result, stale || i != 0, err
148 }
149 }
150 return nil, false, t.err
151}
152
153// TransformToStorage uses the first transformer and adds its prefix to the data.
154func (t *prefixTransformers) TransformToStorage(data []byte, context Context) ([]byte, error) {
155 transformer := t.transformers[0]
156 prefixedData := make([]byte, len(transformer.Prefix), len(data)+len(transformer.Prefix))
157 copy(prefixedData, transformer.Prefix)
158 result, err := transformer.Transformer.TransformToStorage(data, context)
159 if err != nil {
160 return nil, err
161 }
162 prefixedData = append(prefixedData, result...)
163 return prefixedData, nil
164}