forked from gravitational/teleport
-
Notifications
You must be signed in to change notification settings - Fork 0
/
session.go
443 lines (387 loc) · 12.4 KB
/
session.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
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
package services
import (
"encoding/json"
"fmt"
"time"
"github.com/gravitational/teleport/lib/defaults"
"github.com/gravitational/teleport/lib/utils"
"github.com/gravitational/trace"
)
// WebSession stores key and value used to authenticate with SSH
// notes on behalf of user
type WebSession interface {
GetMetadata() Metadata
// GetShortName returns visible short name used in logging
GetShortName() string
// GetName returns session name
GetName() string
// GetUser returns the user this session is associated with
GetUser() string
// SetName sets session name
SetName(string)
// SetUser sets user associated with this session
SetUser(string)
// GetPub is returns public certificate signed by auth server
GetPub() []byte
// GetPriv returns private OpenSSH key used to auth with SSH nodes
GetPriv() []byte
// BearerToken is a special bearer token used for additional
// bearer authentication
GetBearerToken() string
// SetBearerTokenExpiryTime sets bearer token expiry time
SetBearerTokenExpiryTime(time.Time)
// SetExpiryTime sets session expiry time
SetExpiryTime(time.Time)
// GetBearerTokenExpiryTime - absolute time when token expires
GetBearerTokenExpiryTime() time.Time
// GetExpiryTime - absolute time when web session expires
GetExpiryTime() time.Time
// V1 returns V1 version of the resource
V1() *WebSessionV1
// V2 returns V2 version of the resource
V2() *WebSessionV2
// WithoutSecrets returns copy of the web session but without private keys
WithoutSecrets() WebSession
// CheckAndSetDefaults checks and set default values for any missing fields.
CheckAndSetDefaults() error
}
// NewWebSession returns new instance of the web session based on the V2 spec
func NewWebSession(name string, spec WebSessionSpecV2) WebSession {
return &WebSessionV2{
Kind: KindWebSession,
Version: V2,
Metadata: Metadata{
Name: name,
Namespace: defaults.Namespace,
},
Spec: spec,
}
}
// WebSessionV2 is version 2 spec for session
type WebSessionV2 struct {
// Kind is a resource kind
Kind string `json:"kind"`
// Version is version
Version string `json:"version"`
// Metadata is connector metadata
Metadata Metadata `json:"metadata"`
// Spec contains cert authority specification
Spec WebSessionSpecV2 `json:"spec"`
}
// WebSessionSpecV2 is a spec for V2 session
type WebSessionSpecV2 struct {
// User is a user this web session belongs to
User string `json:"user"`
// Pub is a public certificate signed by auth server
Pub []byte `json:"pub"`
// Priv is a private OpenSSH key used to auth with SSH nodes
Priv []byte `json:"priv,omitempty"`
// BearerToken is a special bearer token used for additional
// bearer authentication
BearerToken string `json:"bearer_token"`
// BearerTokenExpires - absolute time when token expires
BearerTokenExpires time.Time `json:"bearer_token_expires"`
// Expires - absolute time when session expires
Expires time.Time `json:"expires"`
}
// GetMetadata returns metadata
func (ws *WebSessionV2) GetMetadata() Metadata {
return ws.Metadata
}
// WithoutSecrets returns copy of the object but without secrets
func (ws *WebSessionV2) WithoutSecrets() WebSession {
v2 := ws.V2()
v2.Spec.Priv = nil
return v2
}
// CheckAndSetDefaults checks and set default values for any missing fields.
func (ws *WebSessionV2) CheckAndSetDefaults() error {
err := ws.Metadata.CheckAndSetDefaults()
if err != nil {
return trace.Wrap(err)
}
return nil
}
// SetName sets session name
func (ws *WebSessionV2) SetName(name string) {
ws.Metadata.Name = name
}
// SetUser sets user associated with this session
func (ws *WebSessionV2) SetUser(u string) {
ws.Spec.User = u
}
// GetUser returns the user this session is associated with
func (ws *WebSessionV2) GetUser() string {
return ws.Spec.User
}
// GetShortName returns visible short name used in logging
func (ws *WebSessionV2) GetShortName() string {
if len(ws.Metadata.Name) < 4 {
return "<undefined>"
}
return ws.Metadata.Name[:4]
}
// GetName returns session name
func (ws *WebSessionV2) GetName() string {
return ws.Metadata.Name
}
// GetPub is returns public certificate signed by auth server
func (ws *WebSessionV2) GetPub() []byte {
return ws.Spec.Pub
}
// GetPriv returns private OpenSSH key used to auth with SSH nodes
func (ws *WebSessionV2) GetPriv() []byte {
return ws.Spec.Priv
}
// BearerToken is a special bearer token used for additional
// bearer authentication
func (ws *WebSessionV2) GetBearerToken() string {
return ws.Spec.BearerToken
}
// SetBearerTokenExpiryTime sets bearer token expiry time
func (ws *WebSessionV2) SetBearerTokenExpiryTime(tm time.Time) {
ws.Spec.BearerTokenExpires = tm
}
// SetExpiryTime sets session expiry time
func (ws *WebSessionV2) SetExpiryTime(tm time.Time) {
ws.Spec.Expires = tm
}
// GetBearerTokenExpiryTime - absolute time when token expires
func (ws *WebSessionV2) GetBearerTokenExpiryTime() time.Time {
return ws.Spec.BearerTokenExpires
}
// GetExpiryTime - absolute time when web session expires
func (ws *WebSessionV2) GetExpiryTime() time.Time {
return ws.Spec.Expires
}
// V2 returns V2 version of the resource
func (ws *WebSessionV2) V2() *WebSessionV2 {
return ws
}
// V1 returns V1 version of the object
func (ws *WebSessionV2) V1() *WebSessionV1 {
return &WebSessionV1{
ID: ws.Metadata.Name,
Priv: ws.Spec.Priv,
Pub: ws.Spec.Pub,
BearerToken: ws.Spec.BearerToken,
Expires: ws.Spec.Expires,
}
}
// WebSessionSpecV2Schema is JSON schema for cert authority V2
const WebSessionSpecV2Schema = `{
"type": "object",
"additionalProperties": false,
"required": ["pub", "bearer_token", "bearer_token_expires", "expires", "user"],
"properties": {
"user": {"type": "string"},
"pub": {"type": "string"},
"priv": {"type": "string"},
"bearer_token": {"type": "string"},
"bearer_token_expires": {"type": "string"},
"expires": {"type": "string"}%v
}
}`
// WebSession stores key and value used to authenticate with SSH
// nodes on behalf of user
type WebSessionV1 struct {
// ID is session ID
ID string `json:"id"`
// User is a user this web session is associated with
User string `json:"user"`
// Pub is a public certificate signed by auth server
Pub []byte `json:"pub"`
// Priv is a private OpenSSH key used to auth with SSH nodes
Priv []byte `json:"priv,omitempty"`
// BearerToken is a special bearer token used for additional
// bearer authentication
BearerToken string `json:"bearer_token"`
// Expires - absolute time when token expires
Expires time.Time `json:"expires"`
}
// V1 returns V1 version of the resource
func (s *WebSessionV1) V1() *WebSessionV1 {
return s
}
// V2 returns V2 version of the resource
func (s *WebSessionV1) V2() *WebSessionV2 {
return &WebSessionV2{
Kind: KindWebSession,
Version: V2,
Metadata: Metadata{
Name: s.ID,
Namespace: defaults.Namespace,
},
Spec: WebSessionSpecV2{
Pub: s.Pub,
Priv: s.Priv,
BearerToken: s.BearerToken,
Expires: s.Expires,
BearerTokenExpires: s.Expires,
},
}
}
// WithoutSecrets returns copy of the web session but without private keys
func (ws *WebSessionV1) WithoutSecrets() WebSession {
v2 := ws.V2()
v2.Spec.Priv = nil
return nil
}
// SetName sets session name
func (ws *WebSessionV1) SetName(name string) {
ws.ID = name
}
// SetUser sets user associated with this session
func (ws *WebSessionV1) SetUser(u string) {
ws.User = u
}
// GetUser returns the user this session is associated with
func (ws *WebSessionV1) GetUser() string {
return ws.User
}
// GetShortName returns visible short name used in logging
func (ws *WebSessionV1) GetShortName() string {
if len(ws.ID) < 4 {
return "<undefined>"
}
return ws.ID[:4]
}
// GetName returns session name
func (ws *WebSessionV1) GetName() string {
return ws.ID
}
// GetPub is returns public certificate signed by auth server
func (ws *WebSessionV1) GetPub() []byte {
return ws.Pub
}
// GetPriv returns private OpenSSH key used to auth with SSH nodes
func (ws *WebSessionV1) GetPriv() []byte {
return ws.Priv
}
// BearerToken is a special bearer token used for additional
// bearer authentication
func (ws *WebSessionV1) GetBearerToken() string {
return ws.BearerToken
}
// Expires - absolute time when token expires
func (ws *WebSessionV1) GetExpiryTime() time.Time {
return ws.Expires
}
// SetExpiryTime sets session expiry time
func (ws *WebSessionV1) SetExpiryTime(tm time.Time) {
ws.Expires = tm
}
// GetBearerRoken - absolute time when token expires
func (ws *WebSessionV1) GetBearerTokenExpiryTime() time.Time {
return ws.Expires
}
// SetBearerTokenExpiryTime sets session expiry time
func (ws *WebSessionV1) SetBearerTokenExpiryTime(tm time.Time) {
ws.Expires = tm
}
var webSessionMarshaler WebSessionMarshaler = &TeleportWebSessionMarshaler{}
// SetWebSessionMarshaler sets global user marshaler
func SetWebSessionMarshaler(u WebSessionMarshaler) {
marshalerMutex.Lock()
defer marshalerMutex.Unlock()
webSessionMarshaler = u
}
// GetWebSessionMarshaler returns currently set user marshaler
func GetWebSessionMarshaler() WebSessionMarshaler {
marshalerMutex.RLock()
defer marshalerMutex.RUnlock()
return webSessionMarshaler
}
// WebSessionMarshaler implements marshal/unmarshal of User implementations
// mostly adds support for extended versions
type WebSessionMarshaler interface {
// UnmarshalWebSession unmarhsals cert authority from binary representation
UnmarshalWebSession(bytes []byte) (WebSession, error)
// MarshalWebSession to binary representation
MarshalWebSession(c WebSession, opts ...MarshalOption) ([]byte, error)
// GenerateWebSession generates new web session and is used to
// inject additional data in extenstions
GenerateWebSession(WebSession) (WebSession, error)
// ExtendWebSession extends web session and is used to
// inject additional data in extenstions when session is getting renewed
ExtendWebSession(WebSession) (WebSession, error)
}
// GetWebSessionSchema returns JSON Schema for web session
func GetWebSessionSchema() string {
return GetWebSessionSchemaWithExtensions("")
}
// GetWebSessionSchemaWithExtensions returns JSON Schema for web session with user-supplied extensions
func GetWebSessionSchemaWithExtensions(extension string) string {
return fmt.Sprintf(V2SchemaTemplate, MetadataSchema, fmt.Sprintf(WebSessionSpecV2Schema, extension), DefaultDefinitions)
}
type TeleportWebSessionMarshaler struct{}
// GenerateWebSession generates new web session and is used to
// inject additional data in extenstions
func (*TeleportWebSessionMarshaler) GenerateWebSession(ws WebSession) (WebSession, error) {
return ws, nil
}
// ExtendWebSession renews web session and is used to
// inject additional data in extenstions when session is getting renewed
func (*TeleportWebSessionMarshaler) ExtendWebSession(ws WebSession) (WebSession, error) {
return ws, nil
}
// UnmarshalWebSession unmarshals web session from on-disk byte format
func (*TeleportWebSessionMarshaler) UnmarshalWebSession(bytes []byte) (WebSession, error) {
var h ResourceHeader
err := json.Unmarshal(bytes, &h)
if err != nil {
return nil, trace.Wrap(err)
}
switch h.Version {
case "":
var ws WebSessionV1
err := json.Unmarshal(bytes, &ws)
if err != nil {
return nil, trace.Wrap(err)
}
utils.UTC(&ws.Expires)
return ws.V2(), nil
case V2:
var ws WebSessionV2
if err := utils.UnmarshalWithSchema(GetWebSessionSchema(), &ws, bytes); err != nil {
return nil, trace.BadParameter(err.Error())
}
utils.UTC(&ws.Spec.BearerTokenExpires)
utils.UTC(&ws.Spec.Expires)
if err := ws.CheckAndSetDefaults(); err != nil {
return nil, trace.Wrap(err)
}
return &ws, nil
}
return nil, trace.BadParameter("web session resource version %v is not supported", h.Version)
}
// MarshalWebSession marshals web session into on-disk representation
func (*TeleportWebSessionMarshaler) MarshalWebSession(ws WebSession, opts ...MarshalOption) ([]byte, error) {
cfg, err := collectOptions(opts)
if err != nil {
return nil, trace.Wrap(err)
}
type ws1 interface {
V1() *WebSessionV1
}
type ws2 interface {
V2() *WebSessionV2
}
version := cfg.GetVersion()
switch version {
case V1:
v, ok := ws.(ws1)
if !ok {
return nil, trace.BadParameter("don't know how to marshal session %v", V1)
}
return json.Marshal(v.V1())
case V2:
v, ok := ws.(ws2)
if !ok {
return nil, trace.BadParameter("don't know how to marshal session %v", V2)
}
return json.Marshal(v.V2())
default:
return nil, trace.BadParameter("version %v is not supported", version)
}
}