You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

203 lines
4.7 KiB

package web
import (
"crypto/tls"
"log"
"net"
"net/http"
"github.com/labstack/echo/middleware"
"maze.io/gate/pkg/core"
"github.com/labstack/echo"
"maze.io/gate/pkg/core/logger"
)
// DefaultServer is our default server configuration.
var DefaultServer = Server{
Bind: ":4443",
}
// Server can accept incoming HTTP client connections.
type Server struct {
Bind string
TLS bool
Cert string
Key string
AccessLog logger.Config
// Static files directory.
Static string
// IdentityProvider
IdentityProvider core.IdentityProvider
log *logger.Logger
listener net.Listener
services []core.Service
err chan error
}
func (Server) Component() string {
return "httpd"
}
func (Server) Transports() []core.Transport {
return nil
}
func (httpd *Server) setup(log *logger.Logger) {
httpd.log = log.WithField(logger.Component, httpd.Component())
if httpd.Static == "" {
httpd.Static = "static"
}
}
func (httpd *Server) Setup(provider core.IdentityProvider) error {
httpd.IdentityProvider = provider
return nil
}
func (httpd *Server) Start(log *logger.Logger, errors chan<- error) error {
httpd.setup(log)
log = httpd.log.WithField(logger.Addr, httpd.Bind)
log.Info("start listener")
l, err := net.Listen("tcp", httpd.Bind)
if err != nil {
log.WithError(err).Error("start listener failed")
return err
}
if httpd.TLS {
log = log.WithFields(logger.Fields{
"cert": httpd.Cert,
"key": httpd.Key,
})
log.Trace("certificate loading")
certificate, err := tls.LoadX509KeyPair(httpd.Cert, httpd.Key)
if err != nil {
log.WithError(err).Error("loading failed")
_ = l.Close()
return err
}
log.WithField("chain", len(certificate.Certificate)).Trace("certificate loaded")
l = tls.NewListener(l, &tls.Config{
Certificates: []tls.Certificate{certificate},
})
}
go httpd.serve(l, errors)
return nil
}
func (httpd *Server) serve(l net.Listener, errors chan<- error) {
httpd.log.Debug("start server")
httpd.listener = l
// TODO(maze): pluggable authentication
// TODO(maze): dashboard that shows active connections (+ option to terminate?)
// TODO(maze): management page to view sessions
// TODO(maze): management page to view recordings
router := echo.New()
router.Use(logger.Middleware(httpd.AccessLog))
//router.Use(middleware.Recover())
router.Use(middleware.Static(httpd.Static))
api := router.Group("/api")
api.GET("/service", httpd.apiServiceList)
api.GET("/transport", httpd.apiTransportList)
api.GET("/transport/:id", httpd.apiTransportGet)
server := &http.Server{
Handler: router,
}
errors <- server.Serve(l)
}
func (httpd *Server) Close() error {
if httpd.listener != nil {
err := httpd.listener.Close()
httpd.listener = nil
return err
}
return nil
}
func (httpd *Server) AddService(s core.Service) {
httpd.services = append(httpd.services, s)
}
type apiResponse struct {
Total int `json:"total"`
Data []interface{} `json:"data"`
}
func (res *apiResponse) WriteTo(ctx echo.Context) error {
res.Total = len(res.Data)
log.Printf("write: %#+v", res)
return ctx.JSON(http.StatusOK, res)
}
func (httpd *Server) apiServiceList(ctx echo.Context) error {
var res apiResponse
for _, service := range httpd.services {
res.Data = append(res.Data, service.Component())
}
return res.WriteTo(ctx)
}
func (httpd *Server) apiTransportList(ctx echo.Context) error {
var res apiResponse
for _, service := range httpd.services {
for _, transport := range service.Transports() {
res.Data = append(res.Data, transportMap(transport))
}
}
return res.WriteTo(ctx)
}
func (httpd *Server) apiTransportGet(ctx echo.Context) error {
id := ctx.Param("id")
for _, service := range httpd.services {
for _, transport := range service.Transports() {
if transport.TransportID().String() == id {
return ctx.JSON(http.StatusOK, transportMap(transport))
}
}
}
return ctx.JSON(http.StatusNotFound, nil)
}
func transportMap(transport core.Transport) map[string]interface{} {
return map[string]interface{}{
"id": transport.TransportID().String(),
"dst": transport.RemoteAddr().String(),
"src": transport.LocalAddr().String(),
"time": transport.ConnectedAt(),
"user": transport.User().Login(),
"user_id": transport.User().ID().String(),
"tunnels": tunnelSlice(transport.Tunnels()),
}
}
func tunnelSlice(tunnels []core.Tunnel) (data []map[string]interface{}) {
data = make([]map[string]interface{}, len(tunnels))
for i, tunnel := range tunnels {
data[i] = tunnelMap(tunnel)
}
return
}
func tunnelMap(tunnel core.Tunnel) map[string]interface{} {
return map[string]interface{}{
"dst": tunnel.LocalAddr().String(),
"src": tunnel.RemoteAddr().String(),
"time": tunnel.ConnectedAt(),
"user": tunnel.User().Login(),
}
}