Ctrl K
ComunidadGuías…

server/ssh/http/server.go

Rama master · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
package http

import (
	"errors"
	"net/http"
	"sync"
	"time"

	"github.com/gorilla/websocket"
	"github.com/labstack/echo/v5"
	"github.com/shellhub-io/shellhub/pkg/clock"
	"github.com/shellhub-io/shellhub/pkg/revdial"
	routesmiddleware "github.com/shellhub-io/shellhub/server/api/routes/middleware"
	"github.com/shellhub-io/shellhub/server/api/services"
	"github.com/shellhub-io/shellhub/server/ssh/pkg/dialer"
	"github.com/shellhub-io/shellhub/server/ssh/session"
	log "github.com/sirupsen/logrus"
)

// Message is the JSON body returned to an agent whose tunnel request was refused.
type Message struct {
	Message string `json:"message"`
}

// NewMessageFromError renders err as the body of a refusal.
func NewMessageFromError(err error) Message {
	return Message{
		Message: err.Error(),
	}
}

// Config controls optional features for the SSH HTTP sidecar server.
type Config struct {
	// RequireAcceptedTunnel refuses the reverse tunnel for a device that is not accepted (pending or
	// rejected). Off by default; opt-in per instance.
	RequireAcceptedTunnel bool
	// Clock is what the tunnels the server accepts read the time from. Nil means the wall clock.
	Clock clock.Clock
	// TunnelPingInterval is how often a v1 tunnel is pinged, each answered ping a beat for its
	// device. A non-positive value leaves the adapter's default of 30 seconds.
	TunnelPingInterval time.Duration
}

func (c *Config) clock() clock.Clock {
	if c == nil || c.Clock == nil {
		return clock.New()
	}

	return c.Clock
}

// ErrDeviceTunnelDial is returned when the device holds a tunnel but the connection over it
// could not be opened, which is distinct from the device being offline.
var ErrDeviceTunnelDial = errors.New("failed to connect to device")

var upgrader = websocket.Upgrader{
	ReadBufferSize:  1024,
	WriteBufferSize: 1024,
	WriteBufferPool: new(sync.Pool),
	Subprotocols:    []string{"binary"},
	CheckOrigin: func(_ *http.Request) bool {
		return true
	},
}

// Register adds the SSH routes to the API's router. The binder, validator and
// error handler are the API's; they are a superset of what these handlers need.
//
// authn is the API's authenticator, used to declare which of these routes are
// reachable without a credential and which accept a device token. It must not be
// nil.
func Register(router *echo.Echo, authn *routesmiddleware.Authenticator, d *dialer.Dialer, service services.Service, sessions *session.Registry, cfg *Config) {
	handlers := &Handlers{
		Dialer:   d,
		Tunnels:  d.Manager,
		Service:  service,
		Sessions: sessions,
		Config:   cfg,
	}

	router.GET(HandleConnectionV1Path, handlers.HandleConnectionV1)
	authn.AllowDevice(http.MethodGet, HandleConnectionV1Path)

	router.GET(HandleConnectionV2Path, handlers.HandleConnectionV2)
	authn.AllowDevice(http.MethodGet, HandleConnectionV2Path)

	router.GET(HandleRevdialPath, echo.WrapHandler(revdial.ConnHandler(upgrader)))
	authn.AllowAnonymous(http.MethodGet, HandleRevdialPath)

	router.POST(HandleSSHClosePath, handlers.HandleSSHClose)

	if err := applyTunnelExtensions(router, authn, d); err != nil {
		log.WithError(err).Error("failed to register the tunnel extensions")
	}
}