2022-12-08 13:05:19 -03:00

121 lines
2.5 KiB
Go

// Copyright (c) 2022 Gitpod GmbH. All rights reserved.
// Licensed under the GNU Affero General Public License (AGPL).
// See License.AGPL.txt in the project root for license information.
package sshtunnel
import (
"context"
"encoding/json"
"fmt"
"io"
"net"
"os"
"github.com/caddyserver/caddy/v2"
"github.com/caddyserver/caddy/v2/caddyconfig"
"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
"github.com/caddyserver/caddy/v2/caddyconfig/httpcaddyfile"
"go.uber.org/zap"
)
func init() {
caddy.RegisterModule(SSHTunnel{})
caddyconfig.RegisterAdapter("inject-ssh-tunnel", InjectSSHTunnelAdaper{})
}
func (a InjectSSHTunnelAdaper) Adapt(body []byte, options map[string]interface{}) (result []byte, warnings []caddyconfig.Warning, err error) {
c := caddyfile.Adapter{ServerType: httpcaddyfile.ServerType{}}
result, warnings, err = c.Adapt(body, options)
if err != nil {
return
}
var r map[string]interface{}
err = json.Unmarshal(result, &r)
if err != nil {
return
}
if apps, ok := r["apps"].(map[string]interface{}); ok {
apps["ssh-tunnel"] = make(map[string]interface{})
result, err = json.Marshal(r)
}
return
}
type InjectSSHTunnelAdaper struct {
}
type SSHTunnel struct {
listener net.Listener
logger *zap.Logger
}
func (SSHTunnel) CaddyModule() caddy.ModuleInfo {
return caddy.ModuleInfo{
ID: "ssh-tunnel",
New: func() caddy.Module { return new(SSHTunnel) },
}
}
func (s *SSHTunnel) Provision(ctx caddy.Context) error {
s.logger = ctx.Logger(s)
return nil
}
func (s *SSHTunnel) Start() error {
ln, err := caddy.Listen("tcp", "0.0.0.0:22")
if err != nil {
return err
}
s.listener = ln
go s.serve()
s.logger.Info("SSH Tunnel is running")
return nil
}
func (s SSHTunnel) Stop() error {
err := s.listener.Close()
if err != nil {
return err
}
return nil
}
func (s *SSHTunnel) serve() {
for {
conn, err := s.listener.Accept()
if nerr, ok := err.(net.Error); ok && nerr.Temporary() {
// ignore temporary network error
continue
}
if err != nil {
return
}
go s.handle(conn)
}
}
func (s *SSHTunnel) handle(conn net.Conn) {
defer conn.Close()
addr := fmt.Sprintf("ws-proxy.%s.%s:22", os.Getenv("KUBE_NAMESPACE"), os.Getenv("KUBE_DOMAIN"))
tconn, err := net.Dial("tcp", addr)
if err != nil {
fmt.Printf("dial %s failed with:%v\n", addr, err)
return
}
defer tconn.Close()
ctx, cancel := context.WithCancel(context.Background())
go func() {
io.Copy(conn, tconn)
cancel()
}()
go func() {
io.Copy(tconn, conn)
cancel()
}()
<-ctx.Done()
}
var _ caddy.App = (*SSHTunnel)(nil)