server/ssh/http/server.go
Rama master · réplica de lectura del repositorio original.
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")
}
}