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
3608 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
netAddr, err := caddy.ParseNetworkAddress("tcp/0.0.0.0:22")
67
if err != nil {
68
return err
69
}
70
lnAny, err := netAddr.Listen(context.Background(), 0, net.ListenConfig{})
71
if err != nil {
72
return err
73
}
74
s.listener = lnAny.(net.Listener)
75
go s.serve()
76
s.logger.Info("SSH Tunnel is running")
77
return nil
78
}
79
80
func (s SSHTunnel) Stop() error {
81
err := s.listener.Close()
82
if err != nil {
83
return err
84
}
85
return nil
86
}
87
88
func (s *SSHTunnel) serve() {
89
for {
90
conn, err := s.listener.Accept()
91
if nerr, ok := err.(net.Error); ok && nerr.Temporary() {
92
// ignore temporary network error
93
continue
94
}
95
if err != nil {
96
return
97
}
98
go s.handle(conn)
99
}
100
}
101
102
func (s *SSHTunnel) handle(conn net.Conn) {
103
defer conn.Close()
104
addr := fmt.Sprintf("ws-proxy.%s.%s:22", os.Getenv("KUBE_NAMESPACE"), os.Getenv("KUBE_DOMAIN"))
105
tconn, err := net.Dial("tcp", addr)
106
if err != nil {
107
fmt.Printf("dial %s failed with:%v\n", addr, err)
108
return
109
}
110
defer tconn.Close()
111
ctx, cancel := context.WithCancel(context.Background())
112
go func() {
113
io.Copy(conn, tconn)
114
cancel()
115
}()
116
117
go func() {
118
io.Copy(tconn, conn)
119
cancel()
120
}()
121
<-ctx.Done()
122
}
123
124
var _ caddy.App = (*SSHTunnel)(nil)
125
126