Path: blob/main/components/proxy/plugins/sshtunnel/ssh-tunnel.go
3608 views
// Copyright (c) 2022 Gitpod GmbH. All rights reserved.1// Licensed under the GNU Affero General Public License (AGPL).2// See License.AGPL.txt in the project root for license information.34package sshtunnel56import (7"context"8"encoding/json"9"fmt"10"io"11"net"12"os"1314"github.com/caddyserver/caddy/v2"15"github.com/caddyserver/caddy/v2/caddyconfig"16"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"17"github.com/caddyserver/caddy/v2/caddyconfig/httpcaddyfile"18"go.uber.org/zap"19)2021func init() {22caddy.RegisterModule(SSHTunnel{})23caddyconfig.RegisterAdapter("inject-ssh-tunnel", InjectSSHTunnelAdaper{})24}2526func (a InjectSSHTunnelAdaper) Adapt(body []byte, options map[string]interface{}) (result []byte, warnings []caddyconfig.Warning, err error) {27c := caddyfile.Adapter{ServerType: httpcaddyfile.ServerType{}}28result, warnings, err = c.Adapt(body, options)29if err != nil {30return31}32var r map[string]interface{}33err = json.Unmarshal(result, &r)34if err != nil {35return36}37if apps, ok := r["apps"].(map[string]interface{}); ok {38apps["ssh-tunnel"] = make(map[string]interface{})39result, err = json.Marshal(r)40}41return42}4344type InjectSSHTunnelAdaper struct {45}4647type SSHTunnel struct {48listener net.Listener49logger *zap.Logger50}5152func (SSHTunnel) CaddyModule() caddy.ModuleInfo {53return caddy.ModuleInfo{54ID: "ssh-tunnel",55New: func() caddy.Module { return new(SSHTunnel) },56}57}5859func (s *SSHTunnel) Provision(ctx caddy.Context) error {60s.logger = ctx.Logger(s)61return nil62}6364func (s *SSHTunnel) Start() error {65netAddr, err := caddy.ParseNetworkAddress("tcp/0.0.0.0:22")66if err != nil {67return err68}69lnAny, err := netAddr.Listen(context.Background(), 0, net.ListenConfig{})70if err != nil {71return err72}73s.listener = lnAny.(net.Listener)74go s.serve()75s.logger.Info("SSH Tunnel is running")76return nil77}7879func (s SSHTunnel) Stop() error {80err := s.listener.Close()81if err != nil {82return err83}84return nil85}8687func (s *SSHTunnel) serve() {88for {89conn, err := s.listener.Accept()90if nerr, ok := err.(net.Error); ok && nerr.Temporary() {91// ignore temporary network error92continue93}94if err != nil {95return96}97go s.handle(conn)98}99}100101func (s *SSHTunnel) handle(conn net.Conn) {102defer conn.Close()103addr := fmt.Sprintf("ws-proxy.%s.%s:22", os.Getenv("KUBE_NAMESPACE"), os.Getenv("KUBE_DOMAIN"))104tconn, err := net.Dial("tcp", addr)105if err != nil {106fmt.Printf("dial %s failed with:%v\n", addr, err)107return108}109defer tconn.Close()110ctx, cancel := context.WithCancel(context.Background())111go func() {112io.Copy(conn, tconn)113cancel()114}()115116go func() {117io.Copy(tconn, conn)118cancel()119}()120<-ctx.Done()121}122123var _ caddy.App = (*SSHTunnel)(nil)124125126