Path: blob/main/components/proxy/plugins/sshtunnel/ssh-tunnel.go
2464 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 {65ln, err := caddy.Listen("tcp", "0.0.0.0:22")66if err != nil {67return err68}69s.listener = ln70go s.serve()71s.logger.Info("SSH Tunnel is running")72return nil73}7475func (s SSHTunnel) Stop() error {76err := s.listener.Close()77if err != nil {78return err79}80return nil81}8283func (s *SSHTunnel) serve() {84for {85conn, err := s.listener.Accept()86if nerr, ok := err.(net.Error); ok && nerr.Temporary() {87// ignore temporary network error88continue89}90if err != nil {91return92}93go s.handle(conn)94}95}9697func (s *SSHTunnel) handle(conn net.Conn) {98defer conn.Close()99addr := fmt.Sprintf("ws-proxy.%s.%s:22", os.Getenv("KUBE_NAMESPACE"), os.Getenv("KUBE_DOMAIN"))100tconn, err := net.Dial("tcp", addr)101if err != nil {102fmt.Printf("dial %s failed with:%v\n", addr, err)103return104}105defer tconn.Close()106ctx, cancel := context.WithCancel(context.Background())107go func() {108io.Copy(conn, tconn)109cancel()110}()111112go func() {113io.Copy(tconn, conn)114cancel()115}()116<-ctx.Done()117}118119var _ caddy.App = (*SSHTunnel)(nil)120121122