Skip to content

Commit

Permalink
Support nconnect network feature
Browse files Browse the repository at this point in the history
Signed-off-by: bill fort <fxbao@hotmail.com>
  • Loading branch information
billfort committed Aug 11, 2023
1 parent d8d2c68 commit ea3932e
Show file tree
Hide file tree
Showing 62 changed files with 2,609 additions and 392 deletions.
129 changes: 124 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@ tunneling, thus benefits from all the advantages of
[nkn-tunnel](https://github.com/nknorg/nkn-tunnel):

- Network agnostic: Neither sender nor receiver needs to have public IP address
or port forwarding. NKN tunnel only establish outbound (websocket)
connections, so Internet access is all they need on both side.
or port forwarding. NKN tunnel only establishes outbound (websocket)
connections, so Internet access is all they need on both sides.

- Top level security: All data are end to end authenticated and encrypted. No
one else in the world except sender and receiver can see or modify the content
one else in the world except the sender and receiver can see or modify the content
of the data. The same public key is used for both routing and encryption,
eliminating the possibility of man in the middle attack.

Expand Down Expand Up @@ -288,10 +288,129 @@ $ nConnect -c -a server-address1 -a server-address2 -a server-address3
```


## Use `config.json` to Simplify Command Arguments

You can use `config.json` to simpliy command arguments. Move config.client.json or config.server.json as `config.json` and edit it before starting your nConnect client or server. After saving `config.json`, you can start nConnect simply.
You can use `config.json` to simplify command arguments. Copy config.client.json or config.server.json as `config.json` and edit it before starting your nConnect client or server. After saving `config.json`, you can start nConnect simply.

## Set up a Virtual Private Network by nConnect
Yes, nConnect supports setting up a virtual private network. It means many computers can join a nConnect virtual network, and access each other just like all nodes are in a local network no matter where they are.

In nConnect private virtual network, there are two types of nodes: one manager node, and network member nodes. The manager node is an administrative node that configures network parameters and authorizes members.

To set up a nConnect network, you need firstly to start a network manager node.

### Start a network manager
To start the network manager, we copy `config.network.json` to `config.manager.json`:

```
$ cp config.network.josn config.manager.json
```

Then edit config.json, enable `NetworkManager`, and give a value to "identifier", just like below:

```
{
"identifier": "manager",
"AdminHTTPAddr": "127.0.0.1:8001",
}
```

Then start nConnect as a network manager with parameter `-m -f config.manager.json` :

```
$ ./nConnect -m -f config.manager.json
```

After nConnect network manager starts, you can see a console printed message:

```
nConnect network manager is listening at: manager.0ec192083....
Network manager web serve at: http://127.0.0.1:8001/network
```

Copy this listening address: `manager.0ec192083....`, it is the manager's address. Other member nodes need this address to join this network.
After the manager starts, you can visit the web service `http://127.0.0.1:8001/network` (default), to config, to manage the network.

### Start some network members and join the network
On another computer, you can start a network member, and let it join the nConnect which you start above.
First, you copy `config.network.json` to `config.member.json`

```
$ cp config.network.json config.member.json
```

Then edit `config.member.json` to edit `identifier`, `managerAddress` and `nodeName`.

```
{
"identifier": "alice",
"managerAddress": "manager.0ec192083....",
"nodeName": "alice",
"seed": "...",
"AdminHTTPAddr": "127.0.0.1:8000",
}
```

Set `managerAddress` as your network manager's listening address, and identify your node name `nodeName`. Each network member should have different `nodeName`.
The field `seed` is the seed of the wallet which you use to pay for `tuna` fee. Please keep it secured. If your wallet has zero balance, then nConnect Server could not start at `tuna` mode.

Then you can start this node to join the network:

```
$ ./nConnect -n -s -c -f config.member.json --tuna --vpn --udp
```

`-n` means this is a network member `node`
For a network member, you may start both `-c` client, and `-s` server, which means you can access other nodes, and other nodes can access you too.
Or you can only set `-c`, which means you can access other nodes, but you don't want other nodes to access you.
Or you can only set `-s`, which means you can only be accessed, and you don't want to access other nodes.

### Manage the network

When a network member starts, it first will send `JoinNetwork` message to the network manager.
After the network administrator should open the manager's web administrate page `http://127.0.0.1:8001/network` (default), to config the network name, IP range, netmask, and gateway.

There are two lists on the manager's web page:
* Waiting for Authorization
This lists all the nodes which are waiting for authorization to join this network. The administrator can accept it or reject it.
Only authorized nodes can become network members and will get a network-specific IP address.
When authorizing a node, it will pop up a dialog to set this node's permission to other nodes which decide if all members or only some of them can access this node.

* Network Members
In the network members list, the administrator can reset nodes' access permission and remove a node from the network (authorization).

If you don't see your node information in `Waiting for Authorization`, please click `Refresh` button to fetch updated data from the manager.

### Test your network
From the manager web page, you can see all member's IP. To test your network, you can run a TCP server on a member node:

```
nconnect$ go run tests/tools/tcpmain.go -server
```

Then you run a TCP client on another member node, for example, the TCP server node's IP is: 10.20.30.2

```
nconnect$ go run tests/tools/tcpmain.go -serverAddr 10.20.30.2
```
You will see messages transmit on both the server and the client.

## Contributing

Expand Down
75 changes: 49 additions & 26 deletions admin/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package admin
import (
"encoding/json"
"errors"
"log"
"time"

"github.com/nknorg/nconnect/config"
Expand All @@ -14,7 +15,7 @@ const (
)

var (
errReplyTimeout = errors.New("wait for reply timeout")
ErrReplyTimeout = errors.New("wait for reply timeout")
)

var (
Expand All @@ -23,18 +24,21 @@ var (

type Client struct {
*nkn.MultiClient
replyTimeout time.Duration
ReplyTimeout time.Duration
}

func NewClient(account *nkn.Account, clientConfig *nkn.ClientConfig) (*Client, error) {
m, err := nkn.NewMultiClient(account, config.RandomIdentifier(), 4, false, clientConfig)
func NewClient(account *nkn.Account, clientConfig *nkn.ClientConfig, identifier string) (*Client, error) {
if identifier == "" {
identifier = config.RandomIdentifier()
}
m, err := nkn.NewMultiClient(account, identifier, 4, false, clientConfig)
if err != nil {
return nil, err
}

c := &Client{
MultiClient: m,
replyTimeout: replyTimeout,
ReplyTimeout: replyTimeout,
}

<-m.OnConnect.C
Expand All @@ -43,36 +47,18 @@ func NewClient(account *nkn.Account, clientConfig *nkn.ClientConfig) (*Client, e
}

func (c *Client) RPCCall(addr, method string, params interface{}, result interface{}) error {
req, err := json.Marshal(map[string]interface{}{
req := map[string]interface{}{
"id": "nConnect",
"method": method,
"params": params,
})
if err != nil {
return err
}

var onReply *nkn.OnMessage
var reply *nkn.Message
Loop:
for i := 0; i < 3; i++ { // retry 3 times if timeout
onReply, err = c.Send(nkn.NewStringArray(addr), req, nil)
if err != nil {
return err
}

select {
case reply = <-onReply.C:
break Loop
case <-time.After(c.replyTimeout):
err = errReplyTimeout
}
}
reply, err := c.SendMsg(addr, req, true)
if err != nil {
return err
}

resp := &rpcResp{
resp := &RpcResp{
Result: result,
}
err = json.Unmarshal(reply.Data, resp)
Expand All @@ -95,3 +81,40 @@ func (c *Client) GetInfo(addr string) (*GetInfoJSON, error) {
}
return res, nil
}

func (c *Client) SendMsg(address string, msg interface{}, waitResponse bool) (reply *nkn.Message, err error) {
if c.ReplyTimeout == 0 {
c.ReplyTimeout = replyTimeout
}

reqBytes, err := json.Marshal(msg)
if err != nil {
return nil, err
}

var onReply *nkn.OnMessage
for i := 0; i < 3; i++ {
onReply, err = c.Send(nkn.NewStringArray(address), reqBytes, nil)
if err != nil {
return nil, err
}

if !waitResponse {
return nil, nil
}

select {
case reply = <-onReply.C:
return reply, nil

case <-time.After(c.ReplyTimeout):
err = ErrReplyTimeout
}
}

if err == ErrReplyTimeout {
log.Printf("Wait for repsone timeout, please make sure the server is running and reachable")
}

return nil, err
}
8 changes: 4 additions & 4 deletions admin/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,15 +46,15 @@ var (
}
)

type rpcReq struct {
type RpcReq struct {
ID string `json:"id"`
JSONRPC string `json:"jsonrpc"`
Method string `json:"method"`
Params map[string]interface{} `json:"params"`
Token string `json:"token"`
}

type rpcResp struct {
type RpcResp struct {
Result interface{} `json:"result,omitempty"`
Error string `json:"error,omitempty"`
}
Expand Down Expand Up @@ -107,8 +107,8 @@ type getLogJSON struct {
MaxSize int `json:"maxSize"`
}

func handleRequest(req *rpcReq, persistConf, mergedConf *config.Config, tun *tunnel.Tunnel, rpcPerm permission) *rpcResp {
resp := &rpcResp{}
func handleRequest(req *RpcReq, persistConf, mergedConf *config.Config, tun *tunnel.Tunnel, rpcPerm permission) *RpcResp {
resp := &RpcResp{}

if rpcPermissions[req.Method]&rpcPerm == 0 {
resp.Error = errPermissionDenied.Error()
Expand Down
2 changes: 1 addition & 1 deletion admin/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ func StartNKNServer(account *nkn.Account, identifier string, clientConfig *nkn.C
for {
msg := <-m.OnMessage.C

req := &rpcReq{}
req := &RpcReq{}
err := json.Unmarshal(msg.Data, req)
if err != nil {
log.Println("Unmarshal client request error:", err)
Expand Down
4 changes: 2 additions & 2 deletions admin/web.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,13 @@ func StartWebServer(listenAddr string, tun *tunnel.Tunnel, persistConf, mergedCo
r.Use(gzip.Gzip(gzip.DefaultCompression))

r.POST("/rpc/admin", func(c *gin.Context) {
req := &rpcReq{}
req := &RpcReq{}
if err := c.ShouldBindJSON(req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
if mergedConf.DisableAdminHTTPAPI {
c.JSON(http.StatusOK, &rpcResp{Error: errAdminHTTPAPIDisabled.Error()})
c.JSON(http.StatusOK, &RpcResp{Error: errAdminHTTPAPIDisabled.Error()})
return
}
resp := handleRequest(req, persistConf, mergedConf, tun, rpcPermissionWeb)
Expand Down
Loading

0 comments on commit ea3932e

Please sign in to comment.