forked from gravitational/teleport
-
Notifications
You must be signed in to change notification settings - Fork 0
/
tunnel.go
255 lines (226 loc) · 6.92 KB
/
tunnel.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
package services
import (
"encoding/json"
"fmt"
"strings"
"github.com/gravitational/teleport/lib/defaults"
"github.com/gravitational/teleport/lib/utils"
"github.com/gravitational/trace"
)
// ReverseTunnel is SSH reverse tunnel established between a local Proxy
// and a remote Proxy. It helps to bypass firewall restrictions, so local
// clusters don't need to have the cluster involved
type ReverseTunnel interface {
// GetName returns tunnel object name
GetName() string
// GetClusterName returns name of the cluster
GetClusterName() string
// GetDialAddrs returns list of dial addresses for this cluster
GetDialAddrs() []string
// Check checks tunnel for errors
Check() error
}
// NewReverseTunnel returns new version of reverse tunnel
func NewReverseTunnel(clusterName string, dialAddrs []string) ReverseTunnel {
return &ReverseTunnelV2{
Kind: KindReverseTunnel,
Version: V2,
Metadata: Metadata{
Name: clusterName,
Namespace: defaults.Namespace,
},
Spec: ReverseTunnelSpecV2{
ClusterName: clusterName,
DialAddrs: dialAddrs,
},
}
}
// ReverseTunnelV2 is version 1 resource spec of the reverse tunnel
type ReverseTunnelV2 struct {
// Kind is a resource kind - always resource
Kind string `json:"kind"`
// Version is a resource version
Version string `json:"version"`
// Metadata is Role metadata
Metadata Metadata `json:"metadata"`
// Spec contains user specification
Spec ReverseTunnelSpecV2 `json:"spec"`
}
// V2 returns V2 version of the resource
func (r *ReverseTunnelV2) V2() *ReverseTunnelV2 {
return r
}
// V1 returns V1 version of the resource
func (r *ReverseTunnelV2) V1() *ReverseTunnelV1 {
return &ReverseTunnelV1{
DomainName: r.Spec.ClusterName,
DialAddrs: r.Spec.DialAddrs,
}
}
// GetName returns tunnel object name
func (r *ReverseTunnelV2) GetName() string {
return r.Metadata.Name
}
// GetClusterName returns name of the cluster
func (r *ReverseTunnelV2) GetClusterName() string {
return r.Spec.ClusterName
}
// GetDialAddrs returns list of dial addresses for this cluster
func (r *ReverseTunnelV2) GetDialAddrs() []string {
return r.Spec.DialAddrs
}
// Check returns nil if all parameters are good, error otherwise
func (r *ReverseTunnelV2) Check() error {
if r.Version == "" {
return trace.BadParameter("missing reverse tunnel version")
}
if strings.TrimSpace(r.Spec.ClusterName) == "" {
return trace.BadParameter("Reverse tunnel validation error: empty cluster name")
}
if len(r.Spec.DialAddrs) == 0 {
return trace.BadParameter("Invalid dial address for reverse tunnel '%v'", r.Spec.ClusterName)
}
for _, addr := range r.Spec.DialAddrs {
_, err := utils.ParseAddr(addr)
if err != nil {
return trace.Wrap(err)
}
}
return nil
}
// ReverseTunnelSpecV2 is a specification for V2 reverse tunnel
type ReverseTunnelSpecV2 struct {
// ClusterName is a domain name of remote cluster we are connecting to
ClusterName string `json:"cluster_name"`
// DialAddrs is a list of remote address to establish a connection to
// it's always SSH over TCP
DialAddrs []string `json:"dial_addrs,omitempty"`
}
// ReverseTunnelSpecV2Schema is JSON schema for reverse tunnel spec
const ReverseTunnelSpecV2Schema = `{
"type": "object",
"additionalProperties": false,
"required": ["cluster_name", "dial_addrs"],
"properties": {
"cluster_name": {"type": "string"},
"dial_addrs": {
"type": "array",
"items": {
"type": "string"
}
}
}
}`
// ReverseTunnelV1 is V1 version of reverse tunnel
type ReverseTunnelV1 struct {
// DomainName is a domain name of remote cluster we are connecting to
DomainName string `json:"domain_name"`
// DialAddrs is a list of remote address to establish a connection to
// it's always SSH over TCP
DialAddrs []string `json:"dial_addrs"`
}
// V1 returns V1 version of the resource
func (r *ReverseTunnelV1) V1() *ReverseTunnelV1 {
return r
}
// V2 returns V2 version of reverse tunnel
func (r *ReverseTunnelV1) V2() *ReverseTunnelV2 {
return &ReverseTunnelV2{
Kind: KindReverseTunnel,
Version: V2,
Metadata: Metadata{
Name: r.DomainName,
Namespace: defaults.Namespace,
},
Spec: ReverseTunnelSpecV2{
ClusterName: r.DomainName,
DialAddrs: r.DialAddrs,
},
}
}
// GetReverseTunnelSchema returns role schema with optionally injected
// schema for extensions
func GetReverseTunnelSchema() string {
return fmt.Sprintf(V2SchemaTemplate, MetadataSchema, ReverseTunnelSpecV2Schema)
}
// UnmarshalReverseTunnel unmarshals reverse tunnel from JSON or YAML,
// sets defaults and checks the schema
func UnmarshalReverseTunnel(data []byte) (ReverseTunnel, error) {
if len(data) == 0 {
return nil, trace.BadParameter("missing tunnel data")
}
var h ResourceHeader
err := json.Unmarshal(data, &h)
if err != nil {
return nil, trace.Wrap(err)
}
switch h.Version {
case "":
var r ReverseTunnelV1
err := json.Unmarshal(data, &r)
if err != nil {
return nil, trace.Wrap(err)
}
return r.V2(), nil
case V2:
var r ReverseTunnelV2
if err := utils.UnmarshalWithSchema(GetReverseTunnelSchema(), &r, data); err != nil {
return nil, trace.BadParameter(err.Error())
}
return &r, nil
}
return nil, trace.BadParameter("reverse tunnel version %v is not supported", h.Version)
}
var tunnelMarshaler ReverseTunnelMarshaler = &TeleportTunnelMarshaler{}
func SetReerseTunnelMarshaler(m ReverseTunnelMarshaler) {
marshalerMutex.Lock()
defer marshalerMutex.Unlock()
tunnelMarshaler = m
}
func GetReverseTunnelMarshaler() ReverseTunnelMarshaler {
marshalerMutex.Lock()
defer marshalerMutex.Unlock()
return tunnelMarshaler
}
// ReverseTunnelMarshaler implements marshal/unmarshal of reverse tunnel implementations
type ReverseTunnelMarshaler interface {
// UnmarshalReverseTunnel unmarshals reverse tunnel from binary representation
UnmarshalReverseTunnel(bytes []byte) (ReverseTunnel, error)
// MarshalReverseTunnel marshals reverse tunnel to binary representation
MarshalReverseTunnel(ReverseTunnel, ...MarshalOption) ([]byte, error)
}
type TeleportTunnelMarshaler struct{}
// UnmarshalReverseTunnel unmarshals reverse tunnel from JSON or YAML
func (*TeleportTunnelMarshaler) UnmarshalReverseTunnel(bytes []byte) (ReverseTunnel, error) {
return UnmarshalReverseTunnel(bytes)
}
// MarshalRole marshalls role into JSON
func (*TeleportTunnelMarshaler) MarshalReverseTunnel(rt ReverseTunnel, opts ...MarshalOption) ([]byte, error) {
cfg, err := collectOptions(opts)
if err != nil {
return nil, trace.Wrap(err)
}
type tunv1 interface {
V1() *ReverseTunnelV1
}
type tunv2 interface {
V2() *ReverseTunnelV2
}
version := cfg.GetVersion()
switch version {
case V1:
v, ok := rt.(tunv1)
if !ok {
return nil, trace.BadParameter("don't know how to marshal %v", V1)
}
return json.Marshal(v.V1())
case V2:
v, ok := rt.(tunv2)
if !ok {
return nil, trace.BadParameter("don't know how to marshal %v", V2)
}
return json.Marshal(v.V2())
default:
return nil, trace.BadParameter("version %v is not supported", version)
}
}