-
Notifications
You must be signed in to change notification settings - Fork 12
/
Copy pathproxy_test.go
116 lines (99 loc) · 3.62 KB
/
proxy_test.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
package spice_test
import (
"fmt"
"github.com/sirupsen/logrus"
"github.com/jsimonetti/go-spice"
"github.com/jsimonetti/go-spice/red"
)
func ExampleProxy() {
// create a new logger to be used for the proxy and the authenticator
log := logrus.New()
log.SetLevel(logrus.DebugLevel)
// create a new instance of the sample authenticator
authSpice := &AuthSpice{
log: log.WithField("component", "authenticator"),
}
// create the proxy using the logger and authenticator
logger := spice.Adapt(log.WithField("component", "proxy"))
proxy, err := spice.New(spice.WithLogger(logger),
spice.WithAuthenticator(authSpice))
if err != nil {
log.Fatalf("error: %s", err)
}
// start listening for tenant connections
log.Fatal(proxy.ListenAndServe("tcp", "127.0.0.1:5900"))
}
// AuthSpice is an example implementation of a spice Authenticator
type AuthSpice struct {
log *logrus.Entry
computeMap map[string]string
}
// Next will check the supplied token and return authorisation information
func (a *AuthSpice) Next(c spice.AuthContext) (bool, string, error) {
// convert the AuthContext into an AuthSpiceContext, since we do that
var ctx spice.AuthSpiceContext
var ok bool
if ctx, ok = c.(spice.AuthSpiceContext); !ok {
return false, "", fmt.Errorf("invalid auth method")
}
// retrieve the token sent by the tenant
token, err := ctx.Token()
if err != nil {
return false, "", err
}
// is the previously saved token is set and matches the token
// sent by the tenant we return the previously saved compute address
if ctx.LoadToken() != "" && ctx.LoadToken() == token {
a.log.Debug("LoadToken found and matches password")
return true, ctx.LoadAddress(), nil
}
// find the compute node for this token
if destination, ok := a.resolveComputeAddress(token); ok {
a.log.Debugf("Ticket validated, compute node at %s", destination)
// save the token and compute address into the context
// so it can be saved into the session table by the proxy
ctx.SaveToken(token)
ctx.SaveAddress(destination)
return true, ctx.LoadAddress(), nil
}
a.log.Warn("authentication failed")
return false, "", nil
}
// Method returns the Spice auth method
func (a *AuthSpice) Method() red.AuthMethod {
return red.AuthMethodSpice
}
// resolveComputeAddress is a custom function that checks the token and returns
// a compute node address
func (a *AuthSpice) resolveComputeAddress(token string) (string, bool) {
// this is just an example, lookup your token somewhere and resolve it
// to a compute node. When creating your own authentication you should
// probably use one-time tokens for the tenant authentication.
// Using a method based on the below sequence of events:
//
// a) Tenant authenticates using token '123e4567:secretpw'
// b) The Authenticator looks up the token '123e4567' in a shared store
// (kv store or database)
// c) The value of token 123e4567 is an encrypted compute
// node computeAddress.
// Attempt to decrypt the computeAddress using 'secretpw'.
// If this results in a valid compute node computeAddress,
// the user is granted access, and de compute destination
// is set to the decrypted node computeAddress. In the same transaction,
// a new token+secret should be generated, and the old one destroyed
if compute, ok := a.computeMap[token]; ok {
a.log.Warn("bogus token check and compute node")
return compute, true
}
return "", false
}
// Init initialises this authenticator
func (a *AuthSpice) Init() error {
// fill in some compute nodes
a.computeMap = map[string]string{
"test": "127.0.0.1:5901",
"test2": "127.0.0.1:5902",
}
a.log.Debug("AuthSpice initialised")
return nil
}