forked from kubernetes/dashboard
-
Notifications
You must be signed in to change notification settings - Fork 4
/
keyholder.go
173 lines (148 loc) · 5.16 KB
/
keyholder.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
// Copyright 2017 The Kubernetes Dashboard Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package jwe
import (
"crypto/rand"
"crypto/rsa"
"log"
"sync"
authApi "github.com/kubernetes/dashboard/src/app/backend/auth/api"
syncApi "github.com/kubernetes/dashboard/src/app/backend/sync/api"
jose "gopkg.in/square/go-jose.v2"
k8sErrors "k8s.io/apimachinery/pkg/api/errors"
metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/watch"
"k8s.io/client-go/pkg/api/v1"
)
// Entries held by resource used to synchronize encryption key data.
const (
holderMapKeyEntry = "priv"
holderMapCertEntry = "pub"
)
// KeyHolder is responsible for generating, storing and synchronizing encryption key used for token
// generation/decryption.
type KeyHolder interface {
// Returns encrypter instance that can be used to encrypt data.
Encrypter() jose.Encrypter
// Returns encryption key that can be used to decrypt data.
Key() *rsa.PrivateKey
// Forces refresh of encryption key synchronized with kubernetes resource (secret).
Refresh()
}
// Implements KeyHolder interface
type rsaKeyHolder struct {
// 256-byte random RSA key pair. Synced with a key saved in a secret.
key *rsa.PrivateKey
synchronizer syncApi.Synchronizer
mux sync.Mutex
}
// Encrypter implements key holder interface. See KeyHolder for more information.
// Used encryption algorithms:
// - Content encryption: AES-GCM (256)
// - Key management: RSA-OAEP-SHA256
func (self *rsaKeyHolder) Encrypter() jose.Encrypter {
publicKey := &self.Key().PublicKey
encrypter, err := jose.NewEncrypter(jose.A256GCM, jose.Recipient{Algorithm: jose.RSA_OAEP_256, Key: publicKey}, nil)
if err != nil {
panic(err)
}
return encrypter
}
// Key implements key holder interface. See KeyHolder for more information.
func (self *rsaKeyHolder) Key() *rsa.PrivateKey {
self.mux.Lock()
defer self.mux.Unlock()
return self.key
}
// Refresh implements key holder interface. See KeyHolder for more information.
func (self *rsaKeyHolder) Refresh() {
self.synchronizer.Refresh()
self.update(self.synchronizer.Get())
}
// Handler function executed by synchronizer used to store encryption key. It is called whenever watched object
// is created or updated.
func (self *rsaKeyHolder) update(obj runtime.Object) {
self.mux.Lock()
defer self.mux.Unlock()
secret := obj.(*v1.Secret)
priv, err := ParseRSAKey(string(secret.Data[holderMapKeyEntry]), string(secret.Data[holderMapCertEntry]))
if err != nil {
// Secret was probably tampered with. Delete it and let it be recreated from local copy.
err := self.synchronizer.Delete()
if err != nil {
panic(err)
}
return
}
self.key = priv
}
// Handler function executed by synchronizer used to store encryption key. It is called whenever watched object
// is gets deleted. It is then recreated based on local key.
func (self *rsaKeyHolder) recreate(obj runtime.Object) {
secret := obj.(*v1.Secret)
log.Printf("Synchronized secret %s has been deleted. Recreating.", secret.Name)
self.synchronizer.Create(self.getEncryptionKeyHolder())
}
func (self *rsaKeyHolder) init() {
// Register event handlers
self.synchronizer.RegisterActionHandler(self.update, watch.Added, watch.Modified)
self.synchronizer.RegisterActionHandler(self.recreate, watch.Deleted)
// Try to init key from synchronized object
if obj := self.synchronizer.Get(); obj != nil {
log.Print("Initializing JWE encryption key from synchronized object")
self.update(obj)
return
}
// If secret with key was not found generate new key
self.initEncryptionKey()
// Try to save generated key in a secret
log.Printf("Storing encryption key in a secret")
err := self.synchronizer.Create(self.getEncryptionKeyHolder())
if err != nil && !k8sErrors.IsAlreadyExists(err) {
panic(err)
}
}
func (self *rsaKeyHolder) getEncryptionKeyHolder() runtime.Object {
priv, pub := ExportRSAKeyOrDie(self.Key())
return &v1.Secret{
ObjectMeta: metaV1.ObjectMeta{
Namespace: authApi.EncryptionKeyHolderNamespace,
Name: authApi.EncryptionKeyHolderName,
},
Data: map[string][]byte{
holderMapKeyEntry: []byte(priv),
holderMapCertEntry: []byte(pub),
},
}
}
// Generates encryption key used to encrypt token payload.
func (self *rsaKeyHolder) initEncryptionKey() {
log.Print("Generating JWE encryption key")
self.mux.Lock()
defer self.mux.Unlock()
privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
panic(err)
}
self.key = privateKey
}
// NewRSAKeyHolder creates new KeyHolder instance.
func NewRSAKeyHolder(synchronizer syncApi.Synchronizer) KeyHolder {
holder := &rsaKeyHolder{
synchronizer: synchronizer,
}
holder.init()
return holder
}