Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/master' into error_cascade
Browse files Browse the repository at this point in the history
  • Loading branch information
xxxserxxx committed Nov 4, 2022
2 parents ae67bf4 + c7096d1 commit 91073c6
Show file tree
Hide file tree
Showing 23 changed files with 585 additions and 415 deletions.
13 changes: 6 additions & 7 deletions CONFIG.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,12 +68,6 @@ route the whole internet through the server peer, add `0.0.0.0/0` to the list
before adding peers. For more advanced options and theory, see
<https://www.wireguard.com/netns/>.

"ReportFile": "/var/lib/dsnetreport.json",

This is the location of the report file generated with `dsnet report`. It is
suggested that this command is run via a cron job; the report can be safely
consumed by a web service or DNS integration script, for instance.

The report contains no sensitive information. At one site I use it together
with [hugo](https://gohugo.io/)
[shortcodes](https://gohugo.io/templates/shortcode-templates/) to generate a
Expand Down Expand Up @@ -139,11 +133,16 @@ Any other CIDR networks that can be routed through this peer.
The public key derived from the private key generated by dsnet when the peer
was added.

"PresharedKey": "GcUtlze0BMuxo3iVEjpOahKdTf8xVfF8hDW3Ylw5az0="
"PresharedKey": "GcUtlze0BMuxo3iVEjpOahKdTf8xVfF8hDW3Ylw5az0=",

The pre-shared key for this peer. The peer has the same key defined as the
pre-shared key for the server peer. This is optional in wireguard but not for
dsnet due to the extra (post quantum!) security it provides.

"PersistentKeepalive": 25

The PersistentKeepalive value for the server in generated client configs, and
for each peer connected to the server.


}
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
.PHONY: all build compile quick clean

all: build
all: compile

clean:
@rm -r dist
Expand Down
19 changes: 9 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,11 @@
<h1 align="center">dsnet</h1>

<img src="/etc/logo/banner.svg" alt="Dsnet banner" width="100%">

<a href="https://repology.org/project/dsnet/versions">
<br />
<img src="https://repology.org/badge/vertical-allrepos/dsnet.svg" alt="Packaging status" align="right">
</a>

<p>
<i>Simple command to manage a centralised wireguard VPN. Think wg-quick but quicker: key generation + address allocation. It can generate ready-to-go client configs for
wg-quick, EdgeOS and NixOS.</i>
</p>

<p>
<a href="https://goreportcard.com/report/github.com/naggie/dsnet"><img src="https://goreportcard.com/badge/github.com/naggie/dsnet" /></a>
<a href="https://opensource.org/licenses/MIT"><img src="https://img.shields.io/badge/license-MIT-blue.svg" /></a>
Expand Down Expand Up @@ -48,7 +44,7 @@ It works on AMD64 based linux and also ARMv5.
init Create /etc/dsnetconfig.json containing default configuration + new keys without loading. Edit to taste.
regenerate Regenerate keys and config for peer
remove Remove a peer by hostname provided as argument + sync
report Generate a JSON status report to the location configured in /etc/dsnetconfig.json.
report Generate a JSON status report to stdout
sync Update wireguard configuration from /etc/dsnetconfig.json after validating
up Create the interface, run pre/post up, sync
version Print version
Expand Down Expand Up @@ -131,7 +127,6 @@ Main (automatically generated) configuration example:
"IP6": "fd00:d631:74ca:7b00:a28:11a1:b821:f013",
"DNS": "",
"Networks": [],
"ReportFile": "/var/lib/dsnetreport.json",
"PrivateKey": "uC+xz3v1mfjWBHepwiCgAmPebZcY+EdhaHAvqX2r7U8=",
"PostUp": "",
"PostDown" "",
Expand All @@ -156,8 +151,8 @@ See [CONFIG.md](CONFIG.md) for an explanation of each field.

# Report file overview

An example report file, generated by `dsnet report` to
`/var/lib/dsnetreport.json` by default:
An example report file, generated by `dsnet report`. Suggested location:
`/var/lib/dsnetreport.json`:

{
"ExternalIP": "198.51.100.2",
Expand Down Expand Up @@ -329,3 +324,7 @@ user. Combined with a periodic `dsnet sync` like above, it's possible to build
a secure web interface that does not require root. A web interface is currently
being created by a friend; it will not be part of dstask, rather a separate
project.

----

The dsnet logo was kindly designed by [@mirorauhala](https://github.com/mirorauhala).
11 changes: 9 additions & 2 deletions cmd/cli/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,11 +60,12 @@ type DsnetConfig struct {
// extra networks available, will be added to AllowedIPs
Networks []lib.JSONIPNet `validate:"required"`
// TODO Default subnets to route via VPN
ReportFile string `validate:"required"`
PrivateKey lib.JSONKey `validate:"required,len=44"`
PostUp string
PostDown string
Peers []PeerConfig `validate:"dive"`
// used for server and client
PersistentKeepalive int `validate:"gte=0,lte=255"`
}

// LoadConfigFile parses the json config file, validates and stuffs
Expand All @@ -81,7 +82,13 @@ func LoadConfigFile() (*DsnetConfig, error) {
return nil, err
}

conf := DsnetConfig{}
conf := DsnetConfig{
// set default for if key is not set. If it is set, this will not be
// used _even if value is zero!_
// Effectively, this is a migration
PersistentKeepalive: 25,
}

err = json.Unmarshal(raw, &conf)
if err != nil {
return nil, err
Expand Down
23 changes: 11 additions & 12 deletions cmd/cli/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import (
)

func Init() error {
reportFile := viper.GetString("report_file")
listenPort := viper.GetInt("listen_port")
configFile := viper.GetString("config_file")
interfaceName := viper.GetString("interface_name")
Expand All @@ -43,17 +42,17 @@ func Init() error {
}

conf := &DsnetConfig{
PrivateKey: privateKey,
ListenPort: listenPort,
Network: getPrivateNet(),
Network6: getULANet(),
Peers: []PeerConfig{},
Domain: "dsnet",
ReportFile: reportFile,
ExternalIP: externalIPV4,
ExternalIP6: externalIPV6,
InterfaceName: interfaceName,
Networks: []lib.JSONIPNet{},
PrivateKey: privateKey,
ListenPort: listenPort,
Network: getPrivateNet(),
Network6: getULANet(),
Peers: []PeerConfig{},
Domain: "dsnet",
ExternalIP: externalIPV4,
ExternalIP6: externalIPV6,
InterfaceName: interfaceName,
Networks: []lib.JSONIPNet{},
PersistentKeepalive: 25,
}

server := GetServer(conf)
Expand Down
94 changes: 30 additions & 64 deletions cmd/cli/report.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,9 @@ package cli
import (
"encoding/json"
"fmt"
"io/ioutil"
"net"
"os"
"time"

"github.com/go-playground/validator"
"github.com/naggie/dsnet/lib"
"github.com/spf13/viper"
"github.com/vishvananda/netlink"
Expand All @@ -17,9 +14,11 @@ import (
)

type DsnetReport struct {
ExternalIP net.IP
InterfaceName string
ListenPort int
ExternalIP net.IP
ExternalIP6 net.IP
ExternalHostname string
InterfaceName string
ListenPort int
// domain to append to hostnames. Relies on separate DNS server for
// resolution. Informational only.
Domain string
Expand Down Expand Up @@ -88,23 +87,19 @@ func GenerateReport() error {
return fmt.Errorf("%w - Could not retrieve device '%s'", err, conf.InterfaceName)
}

oldReport, err := LoadDsnetReport()
report, err := GetReport(dev, conf)
if err != nil {
return err
}
report, err := GetReport(dev, conf, oldReport)
if err != nil {
return err
}
return report.Save()
report.Print()
return nil
}

func GetReport(dev *wgtypes.Device, conf *DsnetConfig, oldReport *DsnetReport) (DsnetReport, error) {
func GetReport(dev *wgtypes.Device, conf *DsnetConfig) (DsnetReport, error) {
peerTimeout := viper.GetDuration("peer_timeout")
peerExpiry := viper.GetDuration("peer_expiry")
wgPeerIndex := make(map[wgtypes.Key]wgtypes.Peer)
peerReports := make([]PeerReport, 0)
oldPeerReportIndex := make(map[string]PeerReport)
peersOnline := 0

linkDev, err := netlink.LinkByName(conf.InterfaceName)
Expand All @@ -118,12 +113,6 @@ func GetReport(dev *wgtypes.Device, conf *DsnetConfig, oldReport *DsnetReport) (
wgPeerIndex[peer.PublicKey] = peer
}

if oldReport != nil {
for _, report := range oldReport.Peers {
oldPeerReportIndex[report.Hostname] = report
}
}

for _, peer := range conf.Peers {
wgPeer, known := wgPeerIndex[peer.PublicKey.Key]

Expand Down Expand Up @@ -168,54 +157,31 @@ func GetReport(dev *wgtypes.Device, conf *DsnetConfig, oldReport *DsnetReport) (
}

return DsnetReport{
ExternalIP: conf.ExternalIP,
InterfaceName: conf.InterfaceName,
ListenPort: conf.ListenPort,
Domain: conf.Domain,
IP: conf.IP,
IP6: conf.IP6,
Network: conf.Network,
Network6: conf.Network6,
DNS: conf.DNS,
Peers: peerReports,
PeersOnline: peersOnline,
PeersTotal: len(peerReports),
ReceiveBytes: stats.RxBytes,
TransmitBytes: stats.TxBytes,
ReceiveBytesSI: BytesToSI(stats.RxBytes),
TransmitBytesSI: BytesToSI(stats.TxBytes),
Timestamp: time.Now(),
ExternalIP: conf.ExternalIP,
ExternalIP6: conf.ExternalIP6,
ExternalHostname: conf.ExternalHostname,
InterfaceName: conf.InterfaceName,
ListenPort: conf.ListenPort,
Domain: conf.Domain,
IP: conf.IP,
IP6: conf.IP6,
Network: conf.Network,
Network6: conf.Network6,
DNS: conf.DNS,
Peers: peerReports,
PeersOnline: peersOnline,
PeersTotal: len(peerReports),
ReceiveBytes: stats.RxBytes,
TransmitBytes: stats.TxBytes,
ReceiveBytesSI: BytesToSI(stats.RxBytes),
TransmitBytesSI: BytesToSI(stats.TxBytes),
Timestamp: time.Now(),
}, nil
}

func (report *DsnetReport) Save() error {
reportFilePath := viper.GetString("report_file")

func (report *DsnetReport) Print() {
_json, _ := json.MarshalIndent(report, "", " ")
_json = append(_json, '\n')

err := ioutil.WriteFile(reportFilePath, _json, 0644)
return err
}

func LoadDsnetReport() (*DsnetReport, error) {
reportFilePath := viper.GetString("report_file_path")
raw, err := ioutil.ReadFile(reportFilePath)

if os.IsNotExist(err) {
return nil, err
} else if os.IsPermission(err) {
return nil, fmt.Errorf("%s cannot be accessed. Check read permissions.", reportFilePath)
} else {
return nil, err
}

report := DsnetReport{}
err = json.Unmarshal(raw, &report)
return nil, err

err = validator.New().Struct(report)
return nil, err

return &report, nil
fmt.Print(string(_json))
}
34 changes: 18 additions & 16 deletions cmd/cli/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,23 @@ import (
func GetServer(config *DsnetConfig) *lib.Server {
fallbackWGBin := viper.GetString("fallback_wg_bin")
return &lib.Server{
ExternalHostname: config.ExternalHostname,
ExternalIP: config.ExternalIP,
ExternalIP6: config.ExternalIP6,
ListenPort: config.ListenPort,
Domain: config.Domain,
InterfaceName: config.InterfaceName,
Network: config.Network,
Network6: config.Network6,
IP: config.IP,
IP6: config.IP6,
DNS: config.DNS,
PrivateKey: config.PrivateKey,
PostUp: config.PostUp,
PostDown: config.PostDown,
FallbackWGBin: fallbackWGBin,
Peers: jsonPeerToDsnetPeer(config.Peers),
ExternalHostname: config.ExternalHostname,
ExternalIP: config.ExternalIP,
ExternalIP6: config.ExternalIP6,
ListenPort: config.ListenPort,
Domain: config.Domain,
InterfaceName: config.InterfaceName,
Network: config.Network,
Network6: config.Network6,
IP: config.IP,
IP6: config.IP6,
DNS: config.DNS,
PrivateKey: config.PrivateKey,
PostUp: config.PostUp,
PostDown: config.PostDown,
FallbackWGBin: fallbackWGBin,
Peers: jsonPeerToDsnetPeer(config.Peers),
Networks: config.Networks,
PersistentKeepalive: config.PersistentKeepalive,
}
}
4 changes: 2 additions & 2 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ var (

reportCmd = &cobra.Command{
Use: "report",
Short: fmt.Sprintf("Generate a JSON status report to the location configured in %s.", viper.GetString("config_file")),
Short: "Generate a JSON status report to stdout",
RunE: func(cmd *cobra.Command, args []string) error {
return cli.GenerateReport()
},
Expand Down Expand Up @@ -149,6 +149,7 @@ func init() {
addCmd.Flags().StringVar(&description, "description", "", "description of the new peer")
addCmd.Flags().BoolVar(&confirm, "confirm", false, "confirm")
removeCmd.Flags().BoolVar(&confirm, "confirm", false, "confirm")
regenerateCmd.Flags().BoolVar(&confirm, "confirm", false, "confirm")

// Environment variable handling.
viper.AutomaticEnv()
Expand All @@ -163,7 +164,6 @@ func init() {
viper.SetDefault("config_file", "/etc/dsnetconfig.json")
viper.SetDefault("fallback_wg_bing", "wireguard-go")
viper.SetDefault("listen_port", 51820)
viper.SetDefault("report_file", "/var/lib/dsnetreport.json")
viper.SetDefault("interface_name", "dsnet")

// if last handshake (different from keepalive, see https://www.wireguard.com/protocol/)
Expand Down
1 change: 0 additions & 1 deletion contrib/report_rendering/php/dsnetreport.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
// Thanks to github.com/Write. See https://github.com/naggie/dsnet/issues/4#issuecomment-632928158 for background.

/* Look for dsnetreport.json in current directory */
/* Change "ReportFile": "/var/lib/dsnetreport.json" accordingly */
/* Also add a crontab to run "dsnet report" to refresh the dsnetreport file */
$json = file_get_contents(__DIR__.'/dsnetreport.json');

Expand Down
Loading

0 comments on commit 91073c6

Please sign in to comment.