Skip to content

Commit

Permalink
draft v2
Browse files Browse the repository at this point in the history
  • Loading branch information
reddec committed Apr 7, 2020
1 parent 4a54b8e commit 0b1c9c2
Show file tree
Hide file tree
Showing 13 changed files with 819 additions and 5 deletions.
9 changes: 9 additions & 0 deletions cmd/tinc-web-boot/internal/support.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package internal

import (
"os/exec"
)

func DetectTincBinary(possibleBinary string) (string, error) {
return exec.LookPath(possibleBinary)
}
41 changes: 41 additions & 0 deletions cmd/tinc-web-boot/internal/support_windows.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package internal

import (
"os"
"os/exec"
"path/filepath"
)

func DetectTincBinary(possibleBinary string) (string, error) {
if v, err := os.Stat(possibleBinary); err == nil && !v.IsDir() {
return possibleBinary, nil
}
// look near executable
execPath, err := os.Executable()
if err != nil {
return "", err
}
binary := digAround(filepath.Dir(execPath))
if binary != "" {
return binary, nil
}
return exec.LookPath(possibleBinary)
}

func digAround(dir string) string {
var ans string
_ = filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if info.IsDir() {
return nil
}
if info.Name() == "tincd.exe" {
ans = filepath.Join(path, info.Name())
return os.ErrExist
}
return nil
})
return ans
}
33 changes: 33 additions & 0 deletions cmd/tinc-web-boot/main.go
Original file line number Diff line number Diff line change
@@ -1 +1,34 @@
package main

import (
"github.com/alecthomas/kong"
"log"
"tinc-web-boot/cmd/tinc-web-boot/internal"
)

type Main struct {
APIPort int `name:"api-port" env:"API_PORT" help:"API port" default:"18655"`
TincBin string `name:"tinc-bin" env:"TINC_BIN" help:"Custom tinc binary location" default:"tincd"`
Host string `name:"host" env:"HOST" help:"Binding host" default:"127.0.0.1"`
}

func main() {
var cli Main
ctx := kong.Parse(&cli)
var err error
if ctx.Command() == "" {
err = cli.Run()
} else {
err = ctx.Run(nil)
}
ctx.FatalIfErrorf(err)
}

func (m *Main) Run() error {
binary, err := internal.DetectTincBinary(m.TincBin)
if err != nil {
return err
}
log.Println("Detected Tinc binary:", binary)
return nil
}
6 changes: 5 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,8 @@ module tinc-web-boot

go 1.13

require github.com/phayes/permbits v0.0.0-20190612203442-39d7c581d2ee // indirect
require (
github.com/alecthomas/kong v0.2.4
github.com/gin-gonic/gin v1.6.2 // indirect
github.com/phayes/permbits v0.0.0-20190612203442-39d7c581d2ee
)
47 changes: 47 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,2 +1,49 @@
github.com/alecthomas/kong v0.2.4 h1:Y0ZBCHAvHhTHw7FFJ2FzCAAG4pkbTgA45nc7BpMhDNk=
github.com/alecthomas/kong v0.2.4/go.mod h1:kQOmtJgV+Lb4aj+I2LEn40cbtawdWJ9Y8QLq+lElKxE=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
github.com/gin-gonic/gin v1.6.2 h1:88crIK23zO6TqlQBt+f9FrPJNKm9ZEr7qjp9vl/d5TM=
github.com/gin-gonic/gin v1.6.2/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
github.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1Vv0sFl1UcHBOY=
github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=
github.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns=
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/phayes/permbits v0.0.0-20190612203442-39d7c581d2ee h1:P6U24L02WMfj9ymZTxl7CxS73JC99x3ukk+DBkgQGQs=
github.com/phayes/permbits v0.0.0-20190612203442-39d7c581d2ee/go.mod h1:3uODdxMgOaPYeWU7RzZLxVtJHZ/x1f/iHkBZuKJDzuY=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42 h1:vEOn+mP2zCOVzKckCZy6YsCtDblrpj/w7B9nxGNELpg=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
98 changes: 94 additions & 4 deletions network/network.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
package network

import (
"bytes"
"context"
"fmt"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"regexp"
"tinc-web-boot/utils"
)

type Storage struct {
Expand Down Expand Up @@ -83,15 +87,28 @@ func (network *Network) Nodes() ([]string, error) {
}

func (network *Network) Node(name string) (*Node, error) {
data, err := ioutil.ReadFile(network.node(name))
data, err := ioutil.ReadFile(network.NodeFile(name))
if err != nil {
return nil, err
}
var nd Node
return &nd, nd.UnmarshalText(data)
}

func (network *Network) configure(ctx context.Context, apiPort int, tincBin string) error {
func (network *Network) Put(node *Node) error {
data, err := node.MarshalText()
if err != nil {
return err
}
return ioutil.WriteFile(network.NodeFile(node.Name), data, 0755)
}

func (network *Network) IsDefined() bool {
v, err := os.Stat(network.configFile())
return err == nil && !v.IsDir()
}

func (network *Network) Configure(ctx context.Context, apiPort int, tincBin string) error {
config, err := network.Read()
if err != nil {
return err
Expand All @@ -116,9 +133,56 @@ func (network *Network) configure(ctx context.Context, apiPort int, tincBin stri
if err := network.saveScript("subnet-down", subnetDown(selfExec, apiPort)); err != nil {
return err
}

if err := network.generateKeysIfNeeded(ctx, tincBin); err != nil {
return fmt.Errorf("%s: generate keys: %w", network.Name(), err)
}
if err := network.indexPublicNodes(); err != nil {
return fmt.Errorf("%s: index public nodes: %w", network.Name(), err)
}
return network.postConfigure(ctx, config, tincBin)
}

func (network *Network) Logfile() string {
return filepath.Join(network.Root, "log.txt")
}

func (network *Network) Pidfile() string {
return filepath.Join(network.Root, "pid.run")
}

func (network *Network) Destroy() error {
return os.RemoveAll(network.Root)
}

func (network *Network) indexPublicNodes() error {
config, err := network.Read()
if err != nil {
return err
}

var publicNodes []string

list, err := network.Nodes()
if err != nil {
return err
}

for _, node := range list {
info, err := network.Node(node)
if err != nil {
return fmt.Errorf("parse node %s: %w", node, err)
}
if len(info.Address) > 0 {
publicNodes = append(publicNodes, node)
}
}

config.ConnectTo = publicNodes

return network.Update(config)
}

func (network *Network) configFile() string {
return filepath.Join(network.Root, "tinc.conf")
}
Expand All @@ -127,7 +191,7 @@ func (network *Network) hosts() string {
return filepath.Join(network.Root, "hosts")
}

func (network *Network) node(name string) string {
func (network *Network) NodeFile(name string) string {
name = regexp.MustCompile(`^[^a-zA-Z0-9_]+$`).ReplaceAllString(name, "")
return filepath.Join(network.hosts(), name)
}
Expand All @@ -136,11 +200,37 @@ func (network *Network) scriptFile(name string) string {
return filepath.Join(network.Root, name+scriptSuffix)
}

func (network *Network) privateKeyFile() string {
return filepath.Join(network.Root, "rsa_key.priv")
}

func (network *Network) saveScript(name string, content string) error {
file := network.scriptFile(name)
err := ioutil.WriteFile(name, []byte(content), 0755)
if err != nil {
return fmt.Errorf("%s: generate script %s: %w", network.Name(), name, err)
}
err = postProcessScript(file)
if err != nil {
return fmt.Errorf("%s: post-process script %s: %w", network.Name(), name, err)
}
return nil
}

func (network *Network) generateKeysIfNeeded(ctx context.Context, tincBin string) error {
_, err := os.Stat(network.privateKeyFile())
if err == nil {
return nil
}
if !os.IsNotExist(err) {
return err
}
return postProcessScript(file)

cmd := exec.CommandContext(ctx, tincBin, "-K", "4096", "-c", network.Root)
cmd.Stdin = bytes.NewReader(nil)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
utils.SetCmdAttrs(cmd)

return cmd.Run()
}
98 changes: 98 additions & 0 deletions tincd/api.go
Original file line number Diff line number Diff line change
@@ -1 +1,99 @@
package tincd

import (
"context"
"github.com/gin-gonic/gin"
"log"
"net"
"net/http"
"strconv"
"strings"
"time"
"tinc-web-boot/network"
)

const (
retryInterval = 1 * time.Second
communicationPort = 1655
)

func runAPI(ctx context.Context, requests chan<- peerReq, network *network.Network) {
config, err := network.Read()
if err != nil {
log.Println(network.Name(), ": read config", err)
return
}
selfNode, err := network.Node(config.Name)
if err != nil {
log.Println(network.Name(), ": read self node", err)
return
}
bindingHost := strings.TrimSpace(strings.Split(selfNode.Subnet, "/")[0])

lc := &net.ListenConfig{}
var listener net.Listener
for {

l, err := lc.Listen(ctx, "tcp", bindingHost+":"+strconv.Itoa(communicationPort))
if err == nil {
listener = l
break
}
log.Println(network.Name(), "listen:", err)
select {
case <-ctx.Done():
return
case <-time.After(retryInterval):
}
}
defer listener.Close()
router := setupRoutes(ctx, requests, network, config)
_ = router.RunListener(listener)
}

func setupRoutes(ctx context.Context, requests chan<- peerReq, network *network.Network, config *network.Config) *gin.Engine {
router := gin.Default()
router.POST("/rpc/watch", func(gctx *gin.Context) {
var params struct {
Subnet string `json:"subnet"`
Node string `json:"node"`
}
if err := gctx.BindJSON(&params); err != nil {
return
}
select {
case requests <- peerReq{
Node: params.Node,
Subnet: params.Subnet,
Add: true,
}:
case <-ctx.Done():

}
gctx.AbortWithStatus(http.StatusNoContent)
})

router.POST("/rpc/forget", func(gctx *gin.Context) {
var params struct {
Node string `json:"node"`
}
if err := gctx.BindJSON(&params); err != nil {
return
}
select {
case requests <- peerReq{
Node: params.Node,
Add: false,
}:
case <-ctx.Done():

}
gctx.AbortWithStatus(http.StatusNoContent)
})

router.GET("/", func(gctx *gin.Context) {
gctx.File(network.NodeFile(config.Name))
})

return router
}
Loading

0 comments on commit 0b1c9c2

Please sign in to comment.