base ping audit impl (SSH handshake)
parent
df48ee3ce6
commit
8c0bbe8eb4
|
@ -13,6 +13,7 @@ This repository contains the base chain implementation that Colinear's compute m
|
||||||
| :-------- | :-------- |
|
| :-------- | :-------- |
|
||||||
| Leasing & settlement module | [x/colinearcore](./x/colinearcore/README.md) |
|
| Leasing & settlement module | [x/colinearcore](./x/colinearcore/README.md) |
|
||||||
| In-memory bid database | [x/colinearcore/memdb](./x/colinearcore/memdb/README.md) |
|
| In-memory bid database | [x/colinearcore/memdb](./x/colinearcore/memdb/README.md) |
|
||||||
|
| Hardware provider auditing | [x/colinearcore/audit](./x/colinearcore/audit/README.md) |
|
||||||
|
|
||||||
## Validators
|
## Validators
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
# Peer Auditing
|
||||||
|
|
||||||
|
The auditing mechanism contained in this folder ensures that hardware providers are running the hardware they say they are. The basic requirements will be as follows:
|
||||||
|
|
||||||
|
| Requirement | Punishment |
|
||||||
|
| :-- | :-- |
|
||||||
|
| Must stake a set amount of CLR. | N/A |
|
||||||
|
| Must complete a GPU puzzle for each declared GPU. | N/A |
|
||||||
|
| Must be online for validator pings | Staked funds slashed |
|
||||||
|
|
||||||
|
## GPU Puzzle
|
||||||
|
|
||||||
|
Work in progress.
|
||||||
|
|
||||||
|
## Validator Pings
|
||||||
|
|
||||||
|
Attempts to perform an SSH handshake using Go's builtin `crypto/ssh`. If the handshake times out, this means that practically speaking, the provider is offline.
|
|
@ -0,0 +1,109 @@
|
||||||
|
package audit
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/dgraph-io/badger/v2"
|
||||||
|
"golang.org/x/crypto/ssh"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Maps SSH ping result channel to address that is being pinged
|
||||||
|
var pingDB *badger.DB
|
||||||
|
|
||||||
|
func MountPingDB() {
|
||||||
|
var err error
|
||||||
|
pingDB, err = badger.Open(badger.DefaultOptions("").WithInMemory(true))
|
||||||
|
if err != nil {
|
||||||
|
// must force crash here, since ping db is absolutely required
|
||||||
|
log.Fatalf("Failed to mount in-memory db: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Begin ping to check if SSH is running on an open port on a host.
|
||||||
|
//
|
||||||
|
// NOTE: host should include a port number.
|
||||||
|
func BeginSshPing(host string) error {
|
||||||
|
|
||||||
|
k := []byte(host)
|
||||||
|
|
||||||
|
err := pingDB.Update(func(txn *badger.Txn) error {
|
||||||
|
_, err := txn.Get(k)
|
||||||
|
if err != nil {
|
||||||
|
if !errors.Is(err, badger.ErrKeyNotFound) {
|
||||||
|
// key-not-found condition required to write
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
goto writePing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fmt.Errorf("already pinging host %s", host)
|
||||||
|
|
||||||
|
writePing:
|
||||||
|
if err := txn.Set(k, []byte(strconv.FormatBool(false))); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
sshConfig := &ssh.ClientConfig{
|
||||||
|
User: "user",
|
||||||
|
Auth: []ssh.AuthMethod{ssh.Password("pass")},
|
||||||
|
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
|
||||||
|
Timeout: 100 * time.Second,
|
||||||
|
}
|
||||||
|
|
||||||
|
go dialSshAsync(host, sshConfig)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check on ongoing SSH ping
|
||||||
|
func CheckSshPing(host string) (bool, error) {
|
||||||
|
k := []byte(host)
|
||||||
|
|
||||||
|
var pingStatus bool
|
||||||
|
|
||||||
|
err := pingDB.View(func(txn *badger.Txn) error {
|
||||||
|
resVal, err := txn.Get(k)
|
||||||
|
if err != nil {
|
||||||
|
if !errors.Is(err, badger.ErrKeyNotFound) {
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
return fmt.Errorf("not pinging host %s", host)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = resVal.Value(func(val []byte) error {
|
||||||
|
pingStatus, err = strconv.ParseBool(string(val))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to decode ping status for host %s: %s", host, string(val))
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
return err
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return pingStatus, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func dialSshAsync(host string, conf *ssh.ClientConfig) {
|
||||||
|
ssh.Dial("tcp", host, conf)
|
||||||
|
pingDB.Update(func(txn *badger.Txn) error {
|
||||||
|
k := []byte(host)
|
||||||
|
return txn.Set(k, []byte(strconv.FormatBool(true)))
|
||||||
|
})
|
||||||
|
// any potential resulting error is swallowed since this is run asynchronously
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
package audit
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCheckSSH(t *testing.T) {
|
||||||
|
|
||||||
|
MountPingDB()
|
||||||
|
|
||||||
|
host1 := "102.1.41.3:22" // should evaluate to false
|
||||||
|
host2 := "127.0.0.1:22" // should evaluate to true
|
||||||
|
|
||||||
|
if err := BeginSshPing(host1); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := BeginSshPing(host2); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
time.Sleep(5 * time.Second)
|
||||||
|
|
||||||
|
res, err := CheckSshPing(host1)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
if res {
|
||||||
|
panic("check host 1 - res should be false")
|
||||||
|
}
|
||||||
|
|
||||||
|
res2, err := CheckSshPing(host2)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
if !res2 {
|
||||||
|
panic("check host 2 - res should be true")
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue