Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
henrybarreto committed Aug 5, 2024
1 parent edbdd8f commit 73ad9bc
Show file tree
Hide file tree
Showing 10 changed files with 428 additions and 10 deletions.
3 changes: 3 additions & 0 deletions agent/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,10 @@ require (
github.com/pkg/errors v0.9.1 // indirect
github.com/pkg/sftp v1.13.5 // indirect
github.com/sethvargo/go-envconfig v0.9.0 // indirect
github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/vishvananda/netlink v1.2.1-beta.2 // indirect
github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.51.0 // indirect
go.opentelemetry.io/otel v1.26.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.26.0 // indirect
Expand Down
9 changes: 9 additions & 0 deletions agent/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,8 @@ github.com/shellhub-io/ssh v0.0.0-20230224143412-edd48dfd6eea h1:7tEI9nukSYZViCj
github.com/shellhub-io/ssh v0.0.0-20230224143412-edd48dfd6eea/go.mod h1:8XB4KraRrX39qHhT6yxPsHedjA08I/uBVwj4xC+/+z4=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8 h1:TG/diQgUe0pntT/2D9tmUCz4VNwm9MfrtPr0SU2qSX8=
github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8/go.mod h1:P5HUIBuIWKbyjl083/loAegFkfbFNx5i2qEP4CNbm7E=
github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
Expand All @@ -118,6 +120,11 @@ github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyC
github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo=
github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
github.com/vishvananda/netlink v1.2.1-beta.2 h1:Llsql0lnQEbHj0I1OuKyp8otXp0r3q0mPkuhwHfStVs=
github.com/vishvananda/netlink v1.2.1-beta.2/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho=
github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f h1:p4VB7kIXpOQvVn1ZaTIVp+3vuYAXFe3OJEvjbUYJLaA=
github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.51.0 h1:Xs2Ncz0gNihqu9iosIZ5SkBbWo5T8JhhLJFMQL1qmLI=
Expand Down Expand Up @@ -159,6 +166,8 @@ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
Expand Down
42 changes: 32 additions & 10 deletions agent/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -163,23 +163,45 @@ func main() {
}()
}

if err := ag.ListenSSH(ctx); err != nil {
log.WithError(err).WithFields(log.Fields{
"version": AgentVersion,
"mode": mode,
"tenant_id": cfg.TenantID,
"server_address": cfg.ServerAddress,
"preferred_hostname": cfg.PreferredHostname,
}).Fatal("Failed to listen for SSH connections")
}
go func() {
if err := ag.ListenSSH(ctx); err != nil {
log.WithError(err).WithFields(log.Fields{
"version": AgentVersion,
"mode": mode,
"tenant_id": cfg.TenantID,
"server_address": cfg.ServerAddress,
"preferred_hostname": cfg.PreferredHostname,
}).Fatal("Failed to listen for SSH connections")
}
}()

go func() {
if !cfg.VPN {
log.Info("VPN is disable")

return
}

for {
log.Info("Waiting for VPN connection")

if err := ag.ListenVPN(ctx); err != nil {
log.WithError(err).Error("Failed to listen for VPN connection. Retrying in 10 seconds.")
}

time.Sleep(10 * time.Second)
}
}()

<-ctx.Done()

log.WithFields(log.Fields{
"version": AgentVersion,
"mode": mode,
"tenant_id": cfg.TenantID,
"server_address": cfg.ServerAddress,
"preferred_hostname": cfg.PreferredHostname,
}).Info("Stopped listening for connections")
}).Info("Agent Stopped")
},
}

Expand Down
1 change: 1 addition & 0 deletions docker-compose.agent.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ services:
- SHELLHUB_PRIVATE_KEY=/go/src/github.com/shellhub-io/shellhub/agent/shellhub.key
- SHELLHUB_TENANT_ID=00000000-0000-4000-0000-000000000000
- SHELLHUB_VERSION=${SHELLHUB_VERSION}
- SHELLHUB_VPN=${SHELLHUB_VPN}
- SHELLHUB_LOG_LEVEL=${SHELLHUB_LOG_LEVEL}
- SHELLHUB_LOG_FORMAT=${SHELLHUB_LOG_FORMAT}
volumes:
Expand Down
3 changes: 3 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,10 @@ require (
github.com/pkg/sftp v1.13.5
github.com/sethvargo/go-envconfig v0.9.0
github.com/sirupsen/logrus v1.9.3
github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8
github.com/stretchr/testify v1.9.0
github.com/testcontainers/testcontainers-go/modules/redis v0.32.0
github.com/vishvananda/netlink v1.2.1-beta.2
golang.org/x/crypto v0.22.0
golang.org/x/sys v0.19.0
)
Expand Down Expand Up @@ -96,6 +98,7 @@ require (
github.com/ulikunitz/xz v0.5.11 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasttemplate v1.2.2 // indirect
github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f // indirect
github.com/vmihailenco/go-tinylfu v0.2.2 // indirect
github.com/vmihailenco/msgpack/v5 v5.3.5 // indirect
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
Expand Down
11 changes: 11 additions & 0 deletions pkg/agent/agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ import (
"github.com/shellhub-io/shellhub/pkg/agent/pkg/keygen"
"github.com/shellhub-io/shellhub/pkg/agent/pkg/sysinfo"
"github.com/shellhub-io/shellhub/pkg/agent/ssh"
"github.com/shellhub-io/shellhub/pkg/agent/vpn"
"github.com/shellhub-io/shellhub/pkg/api/client"
"github.com/shellhub-io/shellhub/pkg/envs"
"github.com/shellhub-io/shellhub/pkg/models"
Expand Down Expand Up @@ -114,6 +115,9 @@ type Config struct {
// MaxRetryConnectionTimeout specifies the maximum time, in seconds, that an agent will wait
// before attempting to reconnect to the ShellHub server. Default is 60 seconds.
MaxRetryConnectionTimeout int `env:"MAX_RETRY_CONNECTION_TIMEOUT,default=60" validate:"min=10,max=120"`

// Defines if the device will try to connect to the namespace's VPN.
VPN bool `env:"VPN,default=false"`
}

func LoadConfigFromEnv() (*Config, map[string]interface{}, error) {
Expand Down Expand Up @@ -163,6 +167,7 @@ type Agent struct {
serverInfo *models.Info
cli client.Client
ssh *ssh.SSH
vpn *vpn.VPN
mode Mode
}

Expand Down Expand Up @@ -357,6 +362,12 @@ func (a *Agent) ListenSSH(ctx context.Context) error {
return a.ssh.Listen(ctx)
}

func (a *Agent) ListenVPN(ctx context.Context) error {
a.vpn = vpn.NewVPN(a.cli, a.authData.Token)

return a.vpn.Listen(ctx)
}

// CheckUpdate gets the ShellHub's server version.
func (a *Agent) CheckUpdate() (*semver.Version, error) {
info, err := a.cli.GetInfo(AgentVersion)
Expand Down
32 changes: 32 additions & 0 deletions pkg/agent/pkg/tunnel/tunnel.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,38 @@ func NewTunnel() *Tunnel {
return t
}

const ContextKeyHTTPConn string = "http-conn"

// NewCustomTunnel creates a new [Tunnel] with the route to the connect, in a POST, and close, in a DELETE, actions.
func NewCustomTunnel(connPath string, closePath string) *Tunnel {
router := echo.New()

t := &Tunnel{
router: router,
srv: &http.Server{
Handler: router,
ConnContext: func(ctx context.Context, c net.Conn) context.Context {
return context.WithValue(ctx, ContextKeyHTTPConn, c) //nolint:revive
},
},
ConnHandler: func(e echo.Context) error {
panic("connHandler can not be nil")
},
CloseHandler: func(e echo.Context) error {
panic("closeHandler can not be nil")
},
}

router.POST(connPath, func(e echo.Context) error {
return t.ConnHandler(e)
})
router.DELETE(closePath, func(e echo.Context) error {
return t.CloseHandler(e)
})

return t
}

// Listen to reverse listener.
func (t *Tunnel) Listen(l *revdial.Listener) error {
return t.srv.Serve(l)
Expand Down
74 changes: 74 additions & 0 deletions pkg/agent/vpn/handlers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package vpn

import (
"errors"
"fmt"
"net"

"github.com/labstack/echo/v4"
log "github.com/sirupsen/logrus"
"github.com/vishvananda/netlink"
)

// PacketMaxSize defines the max size of each IP packet received.
// TODO: This isn't the real max size; set the right one.
const PacketMaxSize = 2048

// InterfaceName defines the default name for the network interface created by ShellHub.
const InterfaceName string = "shb"

var ErrGenerateInterface = errors.New("failed to generate an interface")

func GenerateInterfaceName() (string, error) {
const attempts = 10

for i := 0; i < attempts; i++ {
name := fmt.Sprintf("%s%d", InterfaceName, i)

if _, err := netlink.LinkByName(name); err == nil {
continue
}

return name, nil
}

return "", ErrGenerateInterface
}

func handler(handler func(net.Conn, *Settings) error) func(c echo.Context) error {
return func(c echo.Context) error {
conn, _, err := c.Response().Hijack()
if err != nil {
log.Error(err)

return err
}

defer conn.Close()

settings, err := ParseSettings(c.Request().Body)
if err != nil {
log.WithError(err).Error("faild to parse the settings")

return err
}

// NOTE: the [handler] is called to handler the core logic of the VPN client, while this handler is used to extract
// the connection and the settings data.
if err := handler(conn, settings); err != nil {
log.WithError(err).Error("failed to handler the vpn connection between server and agent")

return err
}

return nil
}
}

func closeHandler(callback func() error) func(c echo.Context) error {
return func(c echo.Context) error {
log.Trace("close handler called")

return callback()
}
}
35 changes: 35 additions & 0 deletions pkg/agent/vpn/settings.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package vpn

import (
"encoding/json"
"fmt"
"io"
"net"
)

type Settings struct {
Address [4]byte `json:"address"`
Mask byte `json:"mask"`
}

// ParseSettings read and parses the [Settings] structure from an [io.Reader].
func ParseSettings(data io.Reader) (*Settings, error) {
body, err := io.ReadAll(data)
if err != nil {
return nil, err
}

settings := Settings{}
if err = json.Unmarshal(body, &settings); err != nil {
return nil, err
}

return &settings, nil
}

// String converts a [Settings] to a string representation on the format $IP/$Mask.
func (s *Settings) String() string {
ip := net.IPv4(s.Address[0], s.Address[1], s.Address[2], s.Address[3])

return fmt.Sprintf("%s/%d", ip.String(), s.Mask)
}
Loading

0 comments on commit 73ad9bc

Please sign in to comment.