From f631ac057b4769363abbe9ff8d2811f88055f2fa Mon Sep 17 00:00:00 2001 From: Martin/Geno Date: Tue, 24 Jul 2018 15:07:11 +0200 Subject: [PATCH] add ssh timeout and ip prefix filter --- config_example.conf | 2 ++ main.go | 4 ++-- runtime/config.go | 6 ++++-- runtime/node.go | 6 +++++- runtime/node_test.go | 6 +++--- runtime/yanic.go | 10 ++++++---- ssh/execute_test.go | 3 ++- ssh/manager.go | 43 +++++++++++++++++++++++++++++++++++++++++-- ssh/manager_test.go | 3 ++- ssh/run_test.go | 3 ++- 10 files changed, 69 insertions(+), 17 deletions(-) diff --git a/config_example.conf b/config_example.conf index 270c39f..af725b3 100644 --- a/config_example.conf +++ b/config_example.conf @@ -7,6 +7,8 @@ secret = "passw0rd" ssh_key = "~/.ssh/id_rsa" ssh_interface = "wlp4s0" +ssh_ipaddress_suffix = "fe80:" +ssh_timeout = "1m" # enable receiving yanic_enable = true diff --git a/main.go b/main.go index 7e561e3..07a4f30 100644 --- a/main.go +++ b/main.go @@ -44,13 +44,13 @@ func main() { log.Info("starting...") - sshmanager := ssh.NewManager(config.SSHPrivateKey) + sshmanager := ssh.NewManager(config.SSHPrivateKey, config.SSHTimeout.Duration) nodes = runtime.NewNodes(config.StatePath, config.SSHInterface, sshmanager) nodesSaveWorker := worker.NewWorker(time.Duration(3)*time.Second, nodes.Saver) nodesUpdateWorker := worker.NewWorker(time.Duration(3)*time.Minute, nodes.Updater) nodesYanic := runtimeYanic.NewNodes(&runtimeYanic.NodesConfig{}) - db := runtime.NewYanicDB(nodes) + db := runtime.NewYanicDB(nodes, config.SSHIPAddressSuffix) go nodesSaveWorker.Start() go nodesUpdateWorker.Start() diff --git a/runtime/config.go b/runtime/config.go index beee151..76d1222 100644 --- a/runtime/config.go +++ b/runtime/config.go @@ -20,8 +20,10 @@ type Config struct { Secret string `toml:"secret"` // SSH private key - SSHPrivateKey string `toml:"ssh_key"` - SSHInterface string `toml:"ssh_interface"` + SSHPrivateKey string `toml:"ssh_key"` + SSHInterface string `toml:"ssh_interface"` + SSHIPAddressSuffix string `toml:"ssh_ipaddress_suffix"` + SSHTimeout duration.Duration `toml:"ssh_timeout"` // yanic socket YanicEnable bool `toml:"yanic_enable"` diff --git a/runtime/node.go b/runtime/node.go index 3f3890e..2615f67 100644 --- a/runtime/node.go +++ b/runtime/node.go @@ -3,6 +3,7 @@ package runtime import ( "bytes" "net" + "strings" yanicData "github.com/FreifunkBremen/yanic/data" "github.com/FreifunkBremen/yanic/lib/jsontime" @@ -23,13 +24,16 @@ type Node struct { } `json:"statistics" mapstructure:"-"` } -func NewNode(nodeOrigin *yanicRuntime.Node) *Node { +func NewNode(nodeOrigin *yanicRuntime.Node, ipPrefix string) *Node { if nodeinfo := nodeOrigin.Nodeinfo; nodeinfo != nil { node := &Node{ Hostname: nodeinfo.Hostname, NodeID: nodeinfo.NodeID, } for _, ip := range nodeinfo.Network.Addresses { + if !strings.HasPrefix(ip, ipPrefix) { + continue + } ipAddr := net.ParseIP(ip) if node.Address == nil || ipAddr.IsGlobalUnicast() { node.Address = ipAddr diff --git a/runtime/node_test.go b/runtime/node_test.go index 9cbb4ec..917fbb6 100644 --- a/runtime/node_test.go +++ b/runtime/node_test.go @@ -15,7 +15,7 @@ func TestNode(t *testing.T) { node1 := &yanicRuntime.Node{ Address: &net.UDPAddr{IP: net.ParseIP("ff02::1")}, } - n1 := NewNode(node1) + n1 := NewNode(node1, "") assert.Nil(n1) node1.Nodeinfo = &yanicData.NodeInfo{ @@ -23,11 +23,11 @@ func TestNode(t *testing.T) { Wireless: &yanicData.Wireless{}, Location: &yanicData.Location{Altitude: 13}, } - n1 = NewNode(node1) + n1 = NewNode(node1, "") assert.NotNil(n1) assert.Equal(float64(13), n1.Location.Altitude) - n2 := NewNode(node1) + n2 := NewNode(node1, "") assert.True(n2.IsEqual(n1)) node1.Nodeinfo.Owner.Contact = "blub2" diff --git a/runtime/yanic.go b/runtime/yanic.go index 3d76ad6..2db7fd6 100644 --- a/runtime/yanic.go +++ b/runtime/yanic.go @@ -14,17 +14,19 @@ import ( type YanicDB struct { databaseYanic.Connection - nodes *Nodes + nodes *Nodes + prefix string } -func NewYanicDB(nodes *Nodes) *YanicDB { +func NewYanicDB(nodes *Nodes, prefix string) *YanicDB { return &YanicDB{ - nodes: nodes, + nodes: nodes, + prefix: prefix, } } func (conn *YanicDB) InsertNode(n *runtimeYanic.Node) { - node := NewNode(n) + node := NewNode(n, conn.prefix) if node == nil { return } diff --git a/ssh/execute_test.go b/ssh/execute_test.go index 3fcd8e5..46e2d26 100644 --- a/ssh/execute_test.go +++ b/ssh/execute_test.go @@ -3,6 +3,7 @@ package ssh import ( "net" "testing" + "time" "github.com/stretchr/testify/assert" ) @@ -12,7 +13,7 @@ func TestExecute(t *testing.T) { addr := net.TCPAddr{IP: net.ParseIP("fd2f:5119:f2c::127"), Port: 22} - mgmt := NewManager("~/.ssh/id_rsa") + mgmt := NewManager("~/.ssh/id_rsa", time.Second) assert.NotNil(mgmt, "no new manager created") client, err := mgmt.ConnectTo(addr) diff --git a/ssh/manager.go b/ssh/manager.go index 710a07f..91afd05 100644 --- a/ssh/manager.go +++ b/ssh/manager.go @@ -16,10 +16,11 @@ type Manager struct { config *ssh.ClientConfig clientsBlacklist map[string]time.Time clientsMUX sync.Mutex + timeout time.Duration } // create a new SSH Connection Manager by ssh file -func NewManager(file string) *Manager { +func NewManager(file string, timeout time.Duration) *Manager { var auths []ssh.AuthMethod if auth := SSHAgent(); auth != nil { auths = append(auths, auth) @@ -36,9 +37,34 @@ func NewManager(file string) *Manager { return &Manager{ config: sshConfig, clientsBlacklist: make(map[string]time.Time), + timeout: timeout, } } +// Conn wraps a net.Conn, and sets a deadline for every read +// and write operation. +type Conn struct { + net.Conn + ReadTimeout time.Duration + WriteTimeout time.Duration +} + +func (c *Conn) Read(b []byte) (int, error) { + err := c.Conn.SetReadDeadline(time.Now().Add(c.ReadTimeout)) + if err != nil { + return 0, err + } + return c.Conn.Read(b) +} + +func (c *Conn) Write(b []byte) (int, error) { + err := c.Conn.SetWriteDeadline(time.Now().Add(c.WriteTimeout)) + if err != nil { + return 0, err + } + return c.Conn.Write(b) +} + func (m *Manager) ConnectTo(addr net.TCPAddr) (*ssh.Client, error) { m.clientsMUX.Lock() defer m.clientsMUX.Unlock() @@ -50,7 +76,20 @@ func (m *Manager) ConnectTo(addr net.TCPAddr) (*ssh.Client, error) { } } - client, err := ssh.Dial("tcp", addr.String(), m.config) + addrString := addr.String() + + conn, err := net.DialTimeout("tcp", addrString, m.timeout) + if err != nil { + return nil, err + } + + timeoutConn := &Conn{conn, m.timeout, m.timeout} + c, chans, reqs, err := ssh.NewClientConn(timeoutConn, addrString, m.config) + if err != nil { + return nil, err + } + + client := ssh.NewClient(c, chans, reqs) if err != nil { if strings.Contains(err.Error(), "no supported methods remain") { m.clientsBlacklist[addr.IP.String()] = time.Now() diff --git a/ssh/manager_test.go b/ssh/manager_test.go index 7d17e18..2bbfd8a 100644 --- a/ssh/manager_test.go +++ b/ssh/manager_test.go @@ -3,6 +3,7 @@ package ssh import ( "net" "testing" + "time" "github.com/stretchr/testify/assert" ) @@ -10,7 +11,7 @@ import ( func TestManager(t *testing.T) { assert := assert.New(t) - mgmt := NewManager("~/.ssh/id_rsa") + mgmt := NewManager("~/.ssh/id_rsa", time.Second) assert.NotNil(mgmt, "no new manager created") client, _ := mgmt.ConnectTo(net.TCPAddr{IP: net.ParseIP("fd2f:5119:f2c::127"), Port: 22}) diff --git a/ssh/run_test.go b/ssh/run_test.go index 05d3434..9c4a599 100644 --- a/ssh/run_test.go +++ b/ssh/run_test.go @@ -4,6 +4,7 @@ import ( "net" "strconv" "testing" + "time" "github.com/stretchr/testify/assert" ) @@ -11,7 +12,7 @@ import ( func TestRun(t *testing.T) { assert := assert.New(t) addr := net.TCPAddr{IP: net.ParseIP("fd2f:5119:f2c::127"), Port: 22} - mgmt := NewManager("~/.ssh/id_rsa") + mgmt := NewManager("~/.ssh/id_rsa", time.Second) assert.NotNil(mgmt, "no new manager created") client, err := mgmt.ConnectTo(addr)