Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: SSH into containers through Node #1216

Merged
merged 2 commits into from Aug 30, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions insonmnia/node/config.go
Expand Up @@ -9,6 +9,7 @@ import (
"github.com/sonm-io/core/insonmnia/logging"
"github.com/sonm-io/core/insonmnia/matcher"
"github.com/sonm-io/core/insonmnia/npp"
"github.com/sonm-io/core/insonmnia/ssh"
"github.com/sonm-io/core/optimus"
"github.com/sonm-io/core/util/debug"
)
Expand All @@ -31,6 +32,7 @@ type Config struct {
Matcher *matcher.YAMLConfig `yaml:"matcher"`
Predictor *optimus.PredictorConfig `yaml:"predictor"`
Debug *debug.Config `yaml:"debug"`
SSH *ssh.ProxyServerConfig `yaml:"ssh"`
}

// NewConfig loads localNode config from given .yaml file
Expand Down
4 changes: 4 additions & 0 deletions insonmnia/node/mod.go
Expand Up @@ -62,6 +62,10 @@ func New(ctx context.Context, cfg *Config, options ...Option) (*Node, error) {
)
}

if cfg.SSH != nil {
serverOptions = append(serverOptions, WithSSH(*cfg.SSH, transportCredentials, remoteOptions.eth.Market(), log.Sugar()))
}

server, err := newServer(cfg.Node, services, serverOptions...)
if err != nil {
return nil, fmt.Errorf("failed to build Node instance: %s", err)
Expand Down
19 changes: 18 additions & 1 deletion insonmnia/node/options.go
Expand Up @@ -5,7 +5,9 @@ import (
"crypto/sha256"

"github.com/ethereum/go-ethereum/crypto"
"github.com/sonm-io/core/blockchain"
"github.com/sonm-io/core/insonmnia/auth"
"github.com/sonm-io/core/insonmnia/ssh"
"github.com/sonm-io/core/util/rest"
"github.com/sonm-io/core/util/xgrpc"
"go.uber.org/zap"
Expand Down Expand Up @@ -37,12 +39,14 @@ type serverOptions struct {
allowREST bool
optionsREST []rest.Option
exposeGRPCMetrics bool
sshProxy SSHServer
log *zap.Logger
}

func newServerOptions() *serverOptions {
return &serverOptions{
log: zap.NewNop(),
sshProxy: &ssh.NilSSHProxyServer{},
log: zap.NewNop(),
}
}

Expand Down Expand Up @@ -89,6 +93,19 @@ func WithGRPCServerMetrics() ServerOption {
}
}

func WithSSH(cfg ssh.ProxyServerConfig, credentials credentials.TransportCredentials, market blockchain.MarketAPI, log *zap.SugaredLogger) ServerOption {
return func(o *serverOptions) error {
server, err := ssh.NewSSHProxyServer(cfg, credentials, market, log)
if err != nil {
return err
}

o.sshProxy = server

return nil
}
}

func WithServerLog(log *zap.Logger) ServerOption {
return func(o *serverOptions) error {
o.log = log
Expand Down
12 changes: 10 additions & 2 deletions insonmnia/node/server.go
Expand Up @@ -54,6 +54,10 @@ type Services interface {
Run(ctx context.Context) error
}

type SSHServer interface {
Serve(ctx context.Context) error
}

// Server is a server for LocalNode instance.
//
// Its responsibility is to manage network part, i.e. creating TCP servers and
Expand All @@ -68,6 +72,7 @@ type Server struct {
// Servers for processing requests.
serverGRPC *grpc.Server
serverREST *rest.Server
serverSSH SSHServer

log *zap.SugaredLogger
}
Expand Down Expand Up @@ -111,7 +116,8 @@ func newServer(cfg nodeConfig, services Services, options ...ServerOption) (*Ser
GRPC: toLocalAddrs(listenersGRPC),
REST: toLocalAddrs(listenersREST),
},
services: services,
services: services,
serverSSH: opts.sshProxy,
}

if opts.allowGRPC {
Expand Down Expand Up @@ -178,7 +184,9 @@ func (m *Server) Serve(ctx context.Context) error {
wg.Go(func() error {
return m.services.Run(ctx)
})
// TODO: Also add debug server and ssh.
wg.Go(func() error {
return m.serverSSH.Serve(ctx)
})

<-ctx.Done()

Expand Down
6 changes: 6 additions & 0 deletions insonmnia/npp/dial.go
Expand Up @@ -4,6 +4,7 @@ package npp

import (
"context"
"fmt"
"net"
"time"

Expand Down Expand Up @@ -105,6 +106,11 @@ func (m *Dialer) DialContext(ctx context.Context, addr auth.Addr) (net.Conn, err
}
}

if m.relayDialer == nil {
log.Warn("failed to connect using NPP - all methods failed")
return nil, fmt.Errorf("failed to connect using NPP - all methods failed")
}

log.Debug("connecting using Relay")
channel := make(chan connTuple)
go func() {
Expand Down
12 changes: 11 additions & 1 deletion insonmnia/npp/options.go
Expand Up @@ -7,6 +7,7 @@ import (

"github.com/sonm-io/core/insonmnia/npp/relay"
"github.com/sonm-io/core/insonmnia/npp/rendezvous"
"github.com/sonm-io/core/proto"
"go.uber.org/zap"
"google.golang.org/grpc/credentials"
)
Expand All @@ -23,6 +24,7 @@ type options struct {
nppMaxBackoffInterval time.Duration
relayListener *relay.Listener
relayDialer *relay.Dialer
protocol string
}

func newOptions() *options {
Expand All @@ -31,6 +33,7 @@ func newOptions() *options {
nppBacklog: 128,
nppMinBackoffInterval: 500 * time.Millisecond,
nppMaxBackoffInterval: 8000 * time.Millisecond,
protocol: sonm.DefaultNPPProtocol,
}
}

Expand All @@ -49,7 +52,7 @@ func WithRendezvous(cfg rendezvous.Config, credentials credentials.TransportCred
for _, addr := range cfg.Endpoints {
client, err := newRendezvousClient(ctx, addr, credentials)
if err == nil {
return newNATPuncher(ctx, cfg, client, o.log)
return newNATPuncher(ctx, cfg, client, o.protocol, o.log)
}
}

Expand Down Expand Up @@ -112,3 +115,10 @@ func WithRelayDialer(dialer *relay.Dialer) Option {
return nil
}
}

func WithProtocol(protocol string) Option {
return func(o *options) error {
o.protocol = protocol
return nil
}
}
7 changes: 5 additions & 2 deletions insonmnia/npp/puncher.go
Expand Up @@ -52,14 +52,15 @@ type natPuncher struct {

client *rendezvousClient
pending *lane.Queue
protocol string
listener net.Listener
listenerChannel chan connTuple

maxAttempts int
timeout time.Duration
}

func newNATPuncher(ctx context.Context, cfg rendezvous.Config, client *rendezvousClient, log *zap.Logger) (NATPuncher, error) {
func newNATPuncher(ctx context.Context, cfg rendezvous.Config, client *rendezvousClient, proto string, log *zap.Logger) (NATPuncher, error) {
// It's important here to reuse the Rendezvous client local address for
// successful NAT penetration in the case of cone NAT.
listener, err := reuseport.Listen(protocol, client.LocalAddr().String())
Expand All @@ -74,6 +75,7 @@ func newNATPuncher(ctx context.Context, cfg rendezvous.Config, client *rendezvou
log: log,
client: client,
pending: lane.NewQueue(),
protocol: proto,
listenerChannel: channel,
listener: listener,

Expand Down Expand Up @@ -165,7 +167,7 @@ func (m *natPuncher) resolve(ctx context.Context, addr common.Address) (*sonm.Re
}

request := &sonm.ConnectRequest{
Protocol: protocol,
Protocol: m.protocol,
PrivateAddrs: []*sonm.Addr{},
ID: addr.Bytes(),
}
Expand All @@ -185,6 +187,7 @@ func (m *natPuncher) publish(ctx context.Context) (*sonm.RendezvousReply, error)
}

request := &sonm.PublishRequest{
Protocol: m.protocol,
PrivateAddrs: []*sonm.Addr{},
}

Expand Down
11 changes: 11 additions & 0 deletions insonmnia/ssh/config.go
@@ -0,0 +1,11 @@
package ssh

import (
"github.com/sonm-io/core/insonmnia/npp"
)

// ProxyServerConfig specifies SSH proxy server configuration.
type ProxyServerConfig struct {
Addr string `yaml:"endpoint" required:"true"`
NPP npp.Config `yaml:"npp" required:"true"`
}