Skip to content

Commit

Permalink
Merge branch 'master' of github.com:naggie/dsnet into error_cascade
Browse files Browse the repository at this point in the history
  • Loading branch information
naggie committed Aug 3, 2024
2 parents 91073c6 + baf0beb commit 5f9f781
Show file tree
Hide file tree
Showing 7 changed files with 166 additions and 13 deletions.
22 changes: 21 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -209,13 +209,15 @@ default. It can also generate VyOS/Vyatta configuration for EdgeOS/Unifi devices
such as the Edgerouter 4 using the
[wireguard-vyatta](https://github.com/WireGuard/wireguard-vyatta-ubnt) package,
as well as configuration for [NixOS](https://nixos.org), ready to be added to
`configuration.nix` environment definition.
`configuration.nix` environment definition. [MikroTik RouterOS](https://mikrotik.com/software)
support is also available.

To change the config file format, set the following environment variables:

* `DSNET_OUTPUT=vyatta`
* `DSNET_OUTPUT=wg-quick`
* `DSNET_OUTPUT=nixos`
* `DSNET_OUTPUT=routeros`

Example vyatta output:

Expand Down Expand Up @@ -263,6 +265,24 @@ Example NixOS output:
};
};

Example MikroTik RouterOS output:

/interface wireguard
add name=wg0 private-key="CDWdi0IcMZgla1hCYI41JejjuFaPCle+vPBxvX5OvVE=";
/interface list member
add interface=wg0 list=LAN
/ip address
add address=10.55.148.2/22 interface=wg0
/ipv6 address
add address=fd00:1965:946d:5000:5a88:878d:dc0:c777/64 advertise=no eui-64=no no-dad=no interface=wg0
/interface wireguard peers
add interface=wg0 \
public-key="iE7dleTu34JOCC4A8xdIZcnbNE+aoji8i1JpP+gdt0M=" \
preshared-key="Ch0BdZ6Um29D34awlWBSNa+cz1wGOUuHshjYIyqKxGU=" \
endpoint-address=198.51.100.73 \
endpoint-port=51820 \
persistent-keepalive=25s \
allowed-address=10.55.148.0/22,fd00:1965:946d:5000::/64,192.168.10.0/24,fe80::1/64

# FAQ

Expand Down
12 changes: 9 additions & 3 deletions cmd/cli/add.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,20 @@ import (
)

// Add prompts for the required information and creates a new peer
func Add(hostname, owner, description string, confirm bool) error {
// TODO accept existing pubkey
func Add(hostname string, privKey, pubKey bool, owner, description string, confirm bool) {
config, err := LoadConfigFile()
if err != nil {
return fmt.Errorf("%w - failed to load configuration file", err)

Check failure on line 15 in cmd/cli/add.go

View workflow job for this annotation

GitHub Actions / build (1.17, GOARCH=amd64)

too many arguments to return

Check failure on line 15 in cmd/cli/add.go

View workflow job for this annotation

GitHub Actions / build (1.18, GOARCH=amd64)

too many return values

Check failure on line 15 in cmd/cli/add.go

View workflow job for this annotation

GitHub Actions / build (1.17, GOARCH=arm GOARM=6)

too many arguments to return

Check failure on line 15 in cmd/cli/add.go

View workflow job for this annotation

GitHub Actions / build (1.18, GOARCH=arm GOARM=7)

too many return values

Check failure on line 15 in cmd/cli/add.go

View workflow job for this annotation

GitHub Actions / build (1.17, GOARCH=arm GOARM=7)

too many arguments to return

Check failure on line 15 in cmd/cli/add.go

View workflow job for this annotation

GitHub Actions / build (1.18, GOARCH=arm GOARM=6)

too many return values

Check failure on line 15 in cmd/cli/add.go

View workflow job for this annotation

GitHub Actions / build (1.17, GOARCH=arm GOARM=5)

too many arguments to return

Check failure on line 15 in cmd/cli/add.go

View workflow job for this annotation

GitHub Actions / build (1.17, GOARCH=arm64)

too many arguments to return

Check failure on line 15 in cmd/cli/add.go

View workflow job for this annotation

GitHub Actions / build (1.18, GOARCH=arm64)

too many return values

Check failure on line 15 in cmd/cli/add.go

View workflow job for this annotation

GitHub Actions / build (1.18, GOARCH=arm GOARM=5)

too many return values
}
server := GetServer(config)

var private, public string
if privKey {
private = MustPromptString("private key", true)

Check failure on line 21 in cmd/cli/add.go

View workflow job for this annotation

GitHub Actions / build (1.17, GOARCH=amd64)

undefined: MustPromptString

Check failure on line 21 in cmd/cli/add.go

View workflow job for this annotation

GitHub Actions / build (1.18, GOARCH=amd64)

undefined: MustPromptString

Check failure on line 21 in cmd/cli/add.go

View workflow job for this annotation

GitHub Actions / build (1.17, GOARCH=arm GOARM=6)

undefined: MustPromptString

Check failure on line 21 in cmd/cli/add.go

View workflow job for this annotation

GitHub Actions / build (1.18, GOARCH=arm GOARM=7)

undefined: MustPromptString

Check failure on line 21 in cmd/cli/add.go

View workflow job for this annotation

GitHub Actions / build (1.17, GOARCH=arm GOARM=7)

undefined: MustPromptString

Check failure on line 21 in cmd/cli/add.go

View workflow job for this annotation

GitHub Actions / build (1.18, GOARCH=arm GOARM=6)

undefined: MustPromptString

Check failure on line 21 in cmd/cli/add.go

View workflow job for this annotation

GitHub Actions / build (1.17, GOARCH=arm GOARM=5)

undefined: MustPromptString

Check failure on line 21 in cmd/cli/add.go

View workflow job for this annotation

GitHub Actions / build (1.17, GOARCH=arm64)

undefined: MustPromptString

Check failure on line 21 in cmd/cli/add.go

View workflow job for this annotation

GitHub Actions / build (1.18, GOARCH=arm64)

undefined: MustPromptString

Check failure on line 21 in cmd/cli/add.go

View workflow job for this annotation

GitHub Actions / build (1.18, GOARCH=arm GOARM=5)

undefined: MustPromptString
}
if pubKey {
public = MustPromptString("public key", true)

Check failure on line 24 in cmd/cli/add.go

View workflow job for this annotation

GitHub Actions / build (1.17, GOARCH=amd64)

undefined: MustPromptString

Check failure on line 24 in cmd/cli/add.go

View workflow job for this annotation

GitHub Actions / build (1.18, GOARCH=amd64)

undefined: MustPromptString

Check failure on line 24 in cmd/cli/add.go

View workflow job for this annotation

GitHub Actions / build (1.17, GOARCH=arm GOARM=6)

undefined: MustPromptString

Check failure on line 24 in cmd/cli/add.go

View workflow job for this annotation

GitHub Actions / build (1.18, GOARCH=arm GOARM=7)

undefined: MustPromptString

Check failure on line 24 in cmd/cli/add.go

View workflow job for this annotation

GitHub Actions / build (1.17, GOARCH=arm GOARM=7)

undefined: MustPromptString

Check failure on line 24 in cmd/cli/add.go

View workflow job for this annotation

GitHub Actions / build (1.18, GOARCH=arm GOARM=6)

undefined: MustPromptString

Check failure on line 24 in cmd/cli/add.go

View workflow job for this annotation

GitHub Actions / build (1.17, GOARCH=arm GOARM=5)

undefined: MustPromptString

Check failure on line 24 in cmd/cli/add.go

View workflow job for this annotation

GitHub Actions / build (1.17, GOARCH=arm64)

undefined: MustPromptString

Check failure on line 24 in cmd/cli/add.go

View workflow job for this annotation

GitHub Actions / build (1.18, GOARCH=arm64)

undefined: MustPromptString

Check failure on line 24 in cmd/cli/add.go

View workflow job for this annotation

GitHub Actions / build (1.18, GOARCH=arm GOARM=5)

undefined: MustPromptString
}
if owner == "" {
owner, err = PromptString("owner", true)
if err != nil {
Expand All @@ -38,7 +44,7 @@ func Add(hostname, owner, description string, confirm bool) error {
// newline (not on stdout) to separate config
fmt.Fprintln(os.Stderr)

peer, err := lib.NewPeer(server, owner, hostname, description)
peer, err := lib.NewPeer(server, private, public, owner, hostname, description)
if err != nil {
return fmt.Errorf("%w - failed to get new peer", err)

Check failure on line 49 in cmd/cli/add.go

View workflow job for this annotation

GitHub Actions / build (1.17, GOARCH=amd64)

too many arguments to return

Check failure on line 49 in cmd/cli/add.go

View workflow job for this annotation

GitHub Actions / build (1.18, GOARCH=amd64)

too many return values

Check failure on line 49 in cmd/cli/add.go

View workflow job for this annotation

GitHub Actions / build (1.17, GOARCH=arm GOARM=6)

too many arguments to return

Check failure on line 49 in cmd/cli/add.go

View workflow job for this annotation

GitHub Actions / build (1.18, GOARCH=arm GOARM=7)

too many return values

Check failure on line 49 in cmd/cli/add.go

View workflow job for this annotation

GitHub Actions / build (1.17, GOARCH=arm GOARM=7)

too many arguments to return

Check failure on line 49 in cmd/cli/add.go

View workflow job for this annotation

GitHub Actions / build (1.18, GOARCH=arm GOARM=6)

too many return values

Check failure on line 49 in cmd/cli/add.go

View workflow job for this annotation

GitHub Actions / build (1.17, GOARCH=arm GOARM=5)

too many arguments to return

Check failure on line 49 in cmd/cli/add.go

View workflow job for this annotation

GitHub Actions / build (1.17, GOARCH=arm64)

too many arguments to return

Check failure on line 49 in cmd/cli/add.go

View workflow job for this annotation

GitHub Actions / build (1.18, GOARCH=arm64)

too many return values

Check failure on line 49 in cmd/cli/add.go

View workflow job for this annotation

GitHub Actions / build (1.18, GOARCH=arm GOARM=5)

too many return values
}
Expand Down
15 changes: 11 additions & 4 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,17 +73,22 @@ var (
}

addCmd = &cobra.Command{
Use: "add [hostname]",
Short: "Add a new peer + sync",
Use: "add <hostname>",
Short: "Add a new peer + sync, optionally using a provided WireGuard private key",
PreRunE: func(cmd *cobra.Command, args []string) error {
// Make sure we have the hostname
if len(args) != 1 {
return errors.New("Missing hostname argument")
}
return nil
},
RunE: func(cmd *cobra.Command, args []string) error {
return cli.Add(args[0], owner, description, confirm)
RunE: func(cmd *cobra.Command, args []string) {
privKey, err := cmd.PersistentFlags().GetBool("private-key")
pubKey, err := cmd.PersistentFlags().GetBool("public-key")
if err != nil {
cli.ExitFail("%w - error processing key flag", err)
}
cli.Add(args[0], privKey, pubKey, owner, description, confirm)
},
}

Expand Down Expand Up @@ -148,6 +153,8 @@ func init() {
addCmd.Flags().StringVar(&owner, "owner", "", "owner of the new peer")
addCmd.Flags().StringVar(&description, "description", "", "description of the new peer")
addCmd.Flags().BoolVar(&confirm, "confirm", false, "confirm")
addCmd.PersistentFlags().BoolP("private-key", "r", false, "Accept user-supplied private key. If supplied, dsnet will generate a public key.")
addCmd.PersistentFlags().BoolP("public-key", "u", false, "Accept user-supplied public key. If supplied, the user must add the private key to the generated config (or provide it with --private-key).")
removeCmd.Flags().BoolVar(&confirm, "confirm", false, "confirm")
regenerateCmd.Flags().BoolVar(&confirm, "confirm", false, "confirm")

Expand Down
38 changes: 38 additions & 0 deletions contrib/report_rendering/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,41 @@ Courtesy of [@frillip](https://github.com/frillip/)

![dsnet report table](https://raw.githubusercontent.com/naggie/dsnet/master/etc/dsnet-report-js.png)

# CLI (bash)

On the command line, you can use [jtbl](https://github.com/kellyjonbrazil/jtbl) (and [jq](https://stedolan.github.io/jq/)) for a nice table rendering with this snippet:

```bash
sudo dsnet report | jq '.Peers' | jtbl
```

The output looks like:
```
╒═════════╤═══════╤══════════╤══════════╤══════════╤══════════╤═════════╤════════╤══════════╤══════════╤══════════╤══════════╤══════════╤══════════╤══════════╕
│ Owner │ IP6 │ Hostna │ Descri │ Online │ Dorman │ Added │ IP │ Extern │ Networ │ LastHa │ Receiv │ Transm │ Receiv │ Transm │
│ │ │ me │ ption │ │ t │ │ │ alIP │ ks │ ndshak │ eBytes │ itByte │ eBytes │ itByte │
│ │ │ │ │ │ │ │ │ │ │ eTime │ │ s │ SI │ sSI │
╞═════════╪═══════╪══════════╪══════════╪══════════╪══════════╪═════════╪════════╪══════════╪══════════╪══════════╪══════════╪══════════╪══════════╪══════════╡
│ xyz │ │ eaetl │ eaetl. │ True │ False │ 2222-0 │ 99.99. │ dddd:d │ [] │ 1111-1 │ 175995 │ 447007 │ 175.9 │ 32.7 M │
│ │ │ │ fooo │ │ │ 2-22T1 │ 99.9 │ dd:ddd │ │ 1-11T1 │ 424 │ 28 │ MB │ B │
│ │ │ │ │ │ │ 2:22:5 │ │ d:dddd │ │ 1:11:1 │ │ │ │ │
│ │ │ │ │ │ │ 2.2274 │ │ :dddd: │ │ 1.1111 │ │ │ │ │
│ │ │ │ │ │ │ 22222- │ │ dddd:d │ │ 11111- │ │ │ │ │
│ │ │ │ │ │ │ 22:20 │ │ ddd:dd │ │ 11:11 │ │ │ │ │
│ │ │ │ │ │ │ │ │ dd │ │ │ │ │ │ │
├─────────┼───────┼──────────┼──────────┼──────────┼──────────┼─────────┼────────┼──────────┼──────────┼──────────┼──────────┼──────────┼──────────┼──────────┤
│ xyz │ │ ammedu │ ammedu │ True │ False │ 2222-0 │ 88.88. │ eeee:e │ [] │ 1111-1 │ 751670 │ 759741 │ 6.7 GB │ 727.7 │
│ │ │ │ .mymy. │ │ │ 2-22T1 │ 88.8 │ eee:ee │ │ 1-11T1 │ 2852 │ 076 │ │ MB │
│ │ │ │ com │ │ │ 2:22:4 │ │ ee:eee │ │ 1:11:1 │ │ │ │ │
│ │ │ │ │ │ │ 2.2292 │ │ e::e │ │ 1.1111 │ │ │ │ │
│ │ │ │ │ │ │ 22226- │ │ │ │ 11111- │ │ │ │ │
│ │ │ │ │ │ │ 22:20 │ │ │ │ 11:11 │ │ │ │ │
├─────────┼───────┼──────────┼──────────┼──────────┼──────────┼─────────┼────────┼──────────┼──────────┼──────────┼──────────┼──────────┼──────────┼──────────┤
...
```

To tighten up the table, use JQ to remove columns you're not interested in:

```
sudo dsnet report | jq '.Peers | map(del(.Added,.Networks,.IP6,.Owner))' | jtbl
```
4 changes: 4 additions & 0 deletions lib/generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ func getPeerConfTplString(peerType PeerType) (string, error) {
return vyattaPeerConf, nil
case NixOS:
return nixosPeerConf, nil
case RouterOS:
return routerosPeerConf, nil
default:
return "", fmt.Errorf("unrecognized peer type")
}
Expand Down Expand Up @@ -84,6 +86,8 @@ func AsciiPeerConfig(peer Peer, peerType string, server Server) (*bytes.Buffer,
return GetWGPeerTemplate(peer, Vyatta, server)
case "nixos":
return GetWGPeerTemplate(peer, NixOS, server)
case "routeros":
return GetWGPeerTemplate(peer, RouterOS, server)
default:
return nil, errors.New("unrecognised OUTPUT type")
}
Expand Down
52 changes: 47 additions & 5 deletions lib/peer.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ import (
"errors"
"fmt"
"net"
"strings"
"time"

"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
)

// PeerType is what configuration to use when generating
Expand All @@ -21,6 +24,9 @@ const (
// NixOS is a declartive linux distro
// https://nixos.wiki/wiki/Wireguard
NixOS
// RouterOS is proprietary Linux based OS by MikroTik
// https://help.mikrotik.com/docs/display/ROS/WireGuard
RouterOS
)

type Peer struct {
Expand All @@ -37,19 +43,55 @@ type Peer struct {
PersistentKeepalive int
}

func NewPeer(server *Server, owner string, hostname string, description string) (Peer, error) {
// NewPeer generates a peer from the supplied arguments and generates keys if needed.
// - server is required and provides network information
// - private is a base64-encoded private key; if the empty string, a new key will be generated
// - public is a base64-encoded public key. If empty, it will be generated from the private key.
// If **not** empty, the private key will be included IFF a private key was provided.
// - owner is the owner name (required)
// - hostname is the name of the peer (required)
// - description is the annotation for the peer
func NewPeer(server *Server, private, public, owner, hostname, description string) (Peer, error) {
if owner == "" {
return Peer{}, errors.New("missing owner")
}
if hostname == "" {
return Peer{}, errors.New("missing hostname")
}

privateKey, err := GenerateJSONPrivateKey()
if err != nil {
return Peer{}, fmt.Errorf("failed to generate private key: %s", err)
var privateKey JSONKey
if private != "" {
userKey := &JSONKey{}
userKey.UnmarshalJSON([]byte(private))
privateKey = *userKey
} else {
var err error
privateKey, err = GenerateJSONPrivateKey()
if err != nil {
return Peer{}, fmt.Errorf("failed to generate private key: %s", err)
}
}

var publicKey JSONKey
if public != "" {
b64Key := strings.Trim(string(public), "\"")
key, err := wgtypes.ParseKey(b64Key)
if err != nil {
return Peer{}, err
}
publicKey = JSONKey{Key: key}
if private == "" {
privateKey = JSONKey{Key: wgtypes.Key([wgtypes.KeyLen]byte{})}
} else {
pubK := privateKey.PublicKey()
ascK := pubK.Key.String()
if ascK != public {
return Peer{}, fmt.Errorf("user-supplied private and public keys are not related")
}
}
} else {
publicKey = privateKey.PublicKey()
}
publicKey := privateKey.PublicKey()

presharedKey, err := GenerateJSONKey()
if err != nil {
Expand Down
36 changes: 36 additions & 0 deletions lib/templates.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,3 +90,39 @@ const nixosPeerConf = `networking.wireguard.interfaces = {{ "{" }}
{{ "};" }}
{{ "};" }}
`

const routerosPeerConf = `/interface wireguard
add name=wg0 private-key="{{ .Peer.PrivateKey.Key }}";
/interface list member
add interface=wg0 list=LAN
/ip address
{{ if gt (.Server.Network.IPNet.IP | len) 0 -}}
add address={{ .Peer.IP }}/{{ .CidrSize }} interface=wg0
{{ end -}}
/ipv6 address
{{ if gt (.Server.Network6.IPNet.IP | len) 0 -}}
add address={{ .Peer.IP6 }}/{{ .CidrSize6 }} advertise=no interface=wg0
{{ end -}}
/interface wireguard peers
{{/* MikroTik RouterOS does not like trailing commas in arrays */ -}}
{{ $first := true -}}
add interface=wg0 \
public-key="{{ .Server.PrivateKey.PublicKey.Key }}" \
preshared-key="{{ .Peer.PresharedKey.Key }}" \
endpoint-address={{ .Endpoint }} \
endpoint-port={{ .Server.ListenPort }} \
persistent-keepalive={{ .Server.PersistentKeepalive }}s \
allowed-address=
{{- if gt (.Server.Network.IPNet.IP | len) 0 }}
{{- if $first}}{{$first = false}}{{else}},{{end}}
{{- .Server.Network.IPNet.IP }}/{{ .CidrSize }}
{{- end }}
{{- if gt (.Server.Network6.IPNet.IP | len) 0 }}
{{- if $first}}{{$first = false}}{{else}},{{end}}
{{- .Server.Network6.IPNet.IP }}/{{ .CidrSize6 }}
{{- end }}
{{- range .Server.Networks }}
{{- if $first}}{{$first = false}}{{else}},{{end}}
{{- . }}
{{- end }}
`

0 comments on commit 5f9f781

Please sign in to comment.