Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
gitpod-io
GitHub Repository: gitpod-io/gitpod
Path: blob/main/components/proxy/plugins/sshtunnel/ssh-tunnel.go
2464 views
1
// Copyright (c) 2022 Gitpod GmbH. All rights reserved.
2
// Licensed under the GNU Affero General Public License (AGPL).
3
// See License.AGPL.txt in the project root for license information.
4
5
package sshtunnel
6
7
import (
8
"context"
9
"encoding/json"
10
"fmt"
11
"io"
12
"net"
13
"os"
14
15
"github.com/caddyserver/caddy/v2"
16
"github.com/caddyserver/caddy/v2/caddyconfig"
17
"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
18
"github.com/caddyserver/caddy/v2/caddyconfig/httpcaddyfile"
19
"go.uber.org/zap"
20
)
21
22
func init() {
23
caddy.RegisterModule(SSHTunnel{})
24
caddyconfig.RegisterAdapter("inject-ssh-tunnel", InjectSSHTunnelAdaper{})
25
}
26
27
func (a InjectSSHTunnelAdaper) Adapt(body []byte, options map[string]interface{}) (result []byte, warnings []caddyconfig.Warning, err error) {
28
c := caddyfile.Adapter{ServerType: httpcaddyfile.ServerType{}}
29
result, warnings, err = c.Adapt(body, options)
30
if err != nil {
31
return
32
}
33
var r map[string]interface{}
34
err = json.Unmarshal(result, &r)
35
if err != nil {
36
return
37
}
38
if apps, ok := r["apps"].(map[string]interface{}); ok {
39
apps["ssh-tunnel"] = make(map[string]interface{})
40
result, err = json.Marshal(r)
41
}
42
return
43
}
44
45
type InjectSSHTunnelAdaper struct {
46
}
47
48
type SSHTunnel struct {
49
listener net.Listener
50
logger *zap.Logger
51
}
52
53
func (SSHTunnel) CaddyModule() caddy.ModuleInfo {
54
return caddy.ModuleInfo{
55
ID: "ssh-tunnel",
56
New: func() caddy.Module { return new(SSHTunnel) },
57
}
58
}
59
60
func (s *SSHTunnel) Provision(ctx caddy.Context) error {
61
s.logger = ctx.Logger(s)
62
return nil
63
}
64
65
func (s *SSHTunnel) Start() error {
66
ln, err := caddy.Listen("tcp", "0.0.0.0:22")
67
if err != nil {
68
return err
69
}
70
s.listener = ln
71
go s.serve()
72
s.logger.Info("SSH Tunnel is running")
73
return nil
74
}
75
76
func (s SSHTunnel) Stop() error {
77
err := s.listener.Close()
78
if err != nil {
79
return err
80
}
81
return nil
82
}
83
84
func (s *SSHTunnel) serve() {
85
for {
86
conn, err := s.listener.Accept()
87
if nerr, ok := err.(net.Error); ok && nerr.Temporary() {
88
// ignore temporary network error
89
continue
90
}
91
if err != nil {
92
return
93
}
94
go s.handle(conn)
95
}
96
}
97
98
func (s *SSHTunnel) handle(conn net.Conn) {
99
defer conn.Close()
100
addr := fmt.Sprintf("ws-proxy.%s.%s:22", os.Getenv("KUBE_NAMESPACE"), os.Getenv("KUBE_DOMAIN"))
101
tconn, err := net.Dial("tcp", addr)
102
if err != nil {
103
fmt.Printf("dial %s failed with:%v\n", addr, err)
104
return
105
}
106
defer tconn.Close()
107
ctx, cancel := context.WithCancel(context.Background())
108
go func() {
109
io.Copy(conn, tconn)
110
cancel()
111
}()
112
113
go func() {
114
io.Copy(tconn, conn)
115
cancel()
116
}()
117
<-ctx.Done()
118
}
119
120
var _ caddy.App = (*SSHTunnel)(nil)
121
122