teleport/lib/services/session.go
2017-04-07 16:54:15 -07:00

428 lines
12 KiB
Go

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
}
// 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
}
// 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))
}
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)
utils.UTC(&ws.Metadata.Expires)
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)
}
}