From 106dca421cd90fc3c2aae309d6ba0560fe7ed918 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Ganne?= Date: Tue, 8 Dec 2020 12:14:20 +0100 Subject: [PATCH 1/4] always use full Service Instance Name Service instance name is defined in RFC6763 section 4.1 as Service Instance Name = . . Use it instead of . for consistency. --- README.md | 10 +++++----- wgsd.go | 10 +++++----- wgsd_test.go | 16 ++++++++-------- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index e7c70cd..95e7c0b 100644 --- a/README.md +++ b/README.md @@ -74,12 +74,12 @@ _wireguard._udp.example.com. 0 IN PTR yutrled535igkl7bdlerl6m4vjxsxm3uqqpl4nmsn2 _wireguard._udp.example.com. 0 IN PTR wmrid55v4enhxqx2jstyoyvkicj5pihkb2tr7r42smiu3t5l4i5q====._wireguard._udp.example.com. $ $ dig @127.0.0.1 -p 5353 yutrled535igkl7bdlerl6m4vjxsxm3uqqpl4nmsn27mt56ad4ha====._wireguard._udp.example.com. SRV +noall +answer +additional -yutrled535igkl7bdlerl6m4vjxsxm3uqqpl4nmsn27mt56ad4ha====._wireguard._udp.example.com. 0 IN SRV 0 0 7777 yutrled535igkl7bdlerl6m4vjxsxm3uqqpl4nmsn27mt56ad4ha====.example.com. -yutrled535igkl7bdlerl6m4vjxsxm3uqqpl4nmsn27mt56ad4ha====.example.com. 0 IN A 203.0.113.1 +yutrled535igkl7bdlerl6m4vjxsxm3uqqpl4nmsn27mt56ad4ha====._wireguard._udp.example.com. 0 IN SRV 0 0 7777 yutrled535igkl7bdlerl6m4vjxsxm3uqqpl4nmsn27mt56ad4ha====._wireguard._udp.example.com. +yutrled535igkl7bdlerl6m4vjxsxm3uqqpl4nmsn27mt56ad4ha====._wireguard._udp.example.com. 0 IN A 203.0.113.1 $ $ dig @127.0.0.1 -p 5353 wmrid55v4enhxqx2jstyoyvkicj5pihkb2tr7r42smiu3t5l4i5q====._wireguard._udp.example.com. SRV +noall +answer +additional -wmrid55v4enhxqx2jstyoyvkicj5pihkb2tr7r42smiu3t5l4i5q====._wireguard._udp.example.com. 0 IN SRV 0 0 8888 wmrid55v4enhxqx2jstyoyvkicj5pihkb2tr7r42smiu3t5l4i5q====.example.com. -wmrid55v4enhxqx2jstyoyvkicj5pihkb2tr7r42smiu3t5l4i5q====.example.com. 0 IN A 198.51.100.1 +wmrid55v4enhxqx2jstyoyvkicj5pihkb2tr7r42smiu3t5l4i5q====._wireguard._udp.example.com. 0 IN SRV 0 0 8888 wmrid55v4enhxqx2jstyoyvkicj5pihkb2tr7r42smiu3t5l4i5q====._wireguard._udp.example.com. +wmrid55v4enhxqx2jstyoyvkicj5pihkb2tr7r42smiu3t5l4i5q====._wireguard._udp.example.com. 0 IN A 198.51.100.1 ``` Converting public keys to Base64 with coreutils: @@ -93,4 +93,4 @@ syKB97XhGnvC+kynh2KqQJPXoOoOpx/HmpMRTc+r4js= ## TODOs - [x] unit tests - [ ] SOA record support -- [x] CI & release binaries \ No newline at end of file +- [x] CI & release binaries diff --git a/wgsd.go b/wgsd.go index a63529e..7baf360 100644 --- a/wgsd.go +++ b/wgsd.go @@ -37,7 +37,8 @@ type wgctrlClient interface { const ( keyLen = 56 // the number of characters in a base32-encoded Wireguard public key spPrefix = "_wireguard._udp." - serviceInstanceLen = keyLen + len(".") + len(spPrefix) + spSubPrefix = "." + spPrefix + serviceInstanceLen = keyLen + len(spSubPrefix) ) func (p *WGSD) ServeDNS(ctx context.Context, w dns.ResponseWriter, @@ -114,15 +115,14 @@ func (p *WGSD) ServeDNS(ctx context.Context, w dns.ResponseWriter, Priority: 0, Weight: 0, Port: uint16(endpoint.Port), - Target: fmt.Sprintf("%s.%s", - strings.ToLower(pubKey), p.zone), + Target: pubKey + spSubPrefix + p.zone, }) w.WriteMsg(m) // nolint: errcheck return dns.RcodeSuccess, nil } } return nxDomain(p.zone, w, r) - case len(name) == keyLen+1 && (qtype == dns.TypeA || + case len(name) == len(spSubPrefix)+keyLen && (qtype == dns.TypeA || qtype == dns.TypeAAAA): pubKey := name[:keyLen] for _, peer := range device.Peers { @@ -148,7 +148,7 @@ func getHostRR(pubKey, zone string, endpoint *net.UDPAddr) dns.RR { if endpoint == nil || endpoint.IP == nil { return nil } - name := fmt.Sprintf("%s.%s", strings.ToLower(pubKey), zone) + name := strings.ToLower(pubKey) + spSubPrefix + zone switch { case endpoint.IP.To4() != nil: return &dns.A{ diff --git a/wgsd_test.go b/wgsd_test.go index 9ef5e56..35569c5 100644 --- a/wgsd_test.go +++ b/wgsd_test.go @@ -70,10 +70,10 @@ func TestWGSD(t *testing.T) { Qtype: dns.TypeSRV, Rcode: dns.RcodeSuccess, Answer: []dns.RR{ - test.SRV(fmt.Sprintf("%s._wireguard._udp.example.com. 0 IN SRV 0 0 1 %s.example.com.", peer1b32, peer1b32)), + test.SRV(fmt.Sprintf("%s._wireguard._udp.example.com. 0 IN SRV 0 0 1 %s._wireguard._udp.example.com.", peer1b32, peer1b32)), }, Extra: []dns.RR{ - test.A(fmt.Sprintf("%s.example.com. 0 IN A %s", peer1b32, peer1.Endpoint.IP.String())), + test.A(fmt.Sprintf("%s._wireguard._udp.example.com. 0 IN A %s", peer1b32, peer1.Endpoint.IP.String())), }, }, { @@ -81,26 +81,26 @@ func TestWGSD(t *testing.T) { Qtype: dns.TypeSRV, Rcode: dns.RcodeSuccess, Answer: []dns.RR{ - test.SRV(fmt.Sprintf("%s._wireguard._udp.example.com. 0 IN SRV 0 0 2 %s.example.com.", peer2b32, peer2b32)), + test.SRV(fmt.Sprintf("%s._wireguard._udp.example.com. 0 IN SRV 0 0 2 %s._wireguard._udp.example.com.", peer2b32, peer2b32)), }, Extra: []dns.RR{ - test.AAAA(fmt.Sprintf("%s.example.com. 0 IN AAAA %s", peer2b32, peer2.Endpoint.IP.String())), + test.AAAA(fmt.Sprintf("%s._wireguard._udp.example.com. 0 IN AAAA %s", peer2b32, peer2.Endpoint.IP.String())), }, }, { - Qname: fmt.Sprintf("%s.example.com.", peer1b32), + Qname: fmt.Sprintf("%s._wireguard._udp.example.com.", peer1b32), Qtype: dns.TypeA, Rcode: dns.RcodeSuccess, Answer: []dns.RR{ - test.A(fmt.Sprintf("%s.example.com. 0 IN A %s", peer1b32, peer1.Endpoint.IP.String())), + test.A(fmt.Sprintf("%s._wireguard._udp.example.com. 0 IN A %s", peer1b32, peer1.Endpoint.IP.String())), }, }, { - Qname: fmt.Sprintf("%s.example.com.", peer2b32), + Qname: fmt.Sprintf("%s._wireguard._udp.example.com.", peer2b32), Qtype: dns.TypeAAAA, Rcode: dns.RcodeSuccess, Answer: []dns.RR{ - test.AAAA(fmt.Sprintf("%s.example.com. 0 IN AAAA %s", peer2b32, peer2.Endpoint.IP.String())), + test.AAAA(fmt.Sprintf("%s._wireguard._udp.example.com. 0 IN AAAA %s", peer2b32, peer2.Endpoint.IP.String())), }, }, { From 6dd954687ed4165e93469ed5a428ee366fc8024c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Ganne?= Date: Mon, 7 Dec 2020 10:47:08 +0100 Subject: [PATCH 2/4] add support for peer auto-discovery in wgsd This patch allows clients to auto-discover other mesh peers: - add a TXT record to default SRV answer containing the allowed ip and public key: clients no longer have to have other peers pre-configured, all necessary configuration is contained in the SRV record. Only a connection to the registry is necessary to be allowed to connect to any other peer - allow clients to request SRV records by allowed ip in addition to public key: a client wanted to communicate with a specific ip can now discover the associated peer w/o knowing its public key before-hand Limitations: - the exported allowed ip config is only the 1st subnet configured - for 2 peers to communicate, both must setup the wireguard association --- wgsd.go | 86 +++++++++++++++++++++++++++++++++++----------------- wgsd_test.go | 10 ++++-- 2 files changed, 66 insertions(+), 30 deletions(-) diff --git a/wgsd.go b/wgsd.go index 7baf360..3cfecf4 100644 --- a/wgsd.go +++ b/wgsd.go @@ -35,12 +35,13 @@ type wgctrlClient interface { } const ( - keyLen = 56 // the number of characters in a base32-encoded Wireguard public key - spPrefix = "_wireguard._udp." - spSubPrefix = "." + spPrefix - serviceInstanceLen = keyLen + len(spSubPrefix) + keyLen = 56 // the number of characters in a base32-encoded Wireguard public key + spPrefix = "_wireguard._udp." + spSubPrefix = "." + spPrefix ) +var emptySubnet = net.IPNet{IP: net.IPv4zero, Mask: net.IPv4Mask(0, 0, 0, 0)} + func (p *WGSD) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) { // request.Request is a convenience struct we wrap around the msg and @@ -94,32 +95,61 @@ func (p *WGSD) ServeDNS(ctx context.Context, w dns.ResponseWriter, } w.WriteMsg(m) // nolint: errcheck return dns.RcodeSuccess, nil - case len(name) == serviceInstanceLen && qtype == dns.TypeSRV: - pubKey := name[:keyLen] + case qtype == dns.TypeSRV && strings.HasSuffix(name, spSubPrefix): + name = name[:len(name)-len(spSubPrefix)] + logger.Debugf("received query for: %s type: %s", name, + dns.TypeToString[qtype]) for _, peer := range device.Peers { - if strings.EqualFold( - base32.StdEncoding.EncodeToString(peer.PublicKey[:]), pubKey) { - endpoint := peer.Endpoint - hostRR := getHostRR(pubKey, p.zone, endpoint) - if hostRR == nil { - return nxDomain(p.zone, w, r) - } - m.Extra = append(m.Extra, hostRR) - m.Answer = append(m.Answer, &dns.SRV{ - Hdr: dns.RR_Header{ - Name: state.Name(), - Rrtype: dns.TypeSRV, - Class: dns.ClassINET, - Ttl: 0, - }, - Priority: 0, - Weight: 0, - Port: uint16(endpoint.Port), - Target: pubKey + spSubPrefix + p.zone, - }) - w.WriteMsg(m) // nolint: errcheck - return dns.RcodeSuccess, nil + pubKey := base32.StdEncoding.EncodeToString(peer.PublicKey[:]) + allowedIPs := &emptySubnet + if len(peer.AllowedIPs) >= 1 { + allowedIPs = &peer.AllowedIPs[0] } + if len(name) == keyLen { + // check by keyname + if !strings.EqualFold(pubKey, name[:keyLen]) { + continue + } + } else { + // check by ip + ip := net.ParseIP(name) + if ip == nil || !allowedIPs.Contains(ip) { + continue + } + } + endpoint := peer.Endpoint + hostRR := getHostRR(pubKey, p.zone, endpoint) + if hostRR == nil { + return nxDomain(p.zone, w, r) + } + m.Extra = append(m.Extra, hostRR) + pubKey = strings.ToLower(pubKey) + m.Extra = append(m.Extra, &dns.TXT{ + Hdr: dns.RR_Header{ + Name: state.Name(), + Rrtype: dns.TypeTXT, + Class: dns.ClassINET, + Ttl: 0, + }, + Txt: []string{ + "allowedip=" + allowedIPs.String(), + "pubkey=" + strings.TrimRight(pubKey, "="), + }, + }) + m.Answer = append(m.Answer, &dns.SRV{ + Hdr: dns.RR_Header{ + Name: state.Name(), + Rrtype: dns.TypeSRV, + Class: dns.ClassINET, + Ttl: 0, + }, + Priority: 0, + Weight: 0, + Port: uint16(endpoint.Port), + Target: pubKey + spSubPrefix + p.zone, + }) + w.WriteMsg(m) // nolint: errcheck + return dns.RcodeSuccess, nil } return nxDomain(p.zone, w, r) case len(name) == len(spSubPrefix)+keyLen && (qtype == dns.TypeA || diff --git a/wgsd_test.go b/wgsd_test.go index 35569c5..7b92053 100644 --- a/wgsd_test.go +++ b/wgsd_test.go @@ -28,22 +28,26 @@ func (m *mockClient) Device(d string) (*wgtypes.Device, error) { func TestWGSD(t *testing.T) { key1 := [32]byte{} key1[0] = 1 + _, allowedip1, _ := net.ParseCIDR("2.2.2.2/32") peer1 := wgtypes.Peer{ Endpoint: &net.UDPAddr{ IP: net.ParseIP("1.1.1.1"), Port: 1, }, - PublicKey: key1, + AllowedIPs: []net.IPNet{*allowedip1}, + PublicKey: key1, } peer1b32 := strings.ToLower(base32.StdEncoding.EncodeToString(peer1.PublicKey[:])) key2 := [32]byte{} key2[0] = 2 + _, allowedip2, _ := net.ParseCIDR("::3/128") peer2 := wgtypes.Peer{ Endpoint: &net.UDPAddr{ IP: net.ParseIP("::2"), Port: 2, }, - PublicKey: key2, + AllowedIPs: []net.IPNet{*allowedip2}, + PublicKey: key2, } peer2b32 := strings.ToLower(base32.StdEncoding.EncodeToString(peer2.PublicKey[:])) p := &WGSD{ @@ -74,6 +78,7 @@ func TestWGSD(t *testing.T) { }, Extra: []dns.RR{ test.A(fmt.Sprintf("%s._wireguard._udp.example.com. 0 IN A %s", peer1b32, peer1.Endpoint.IP.String())), + test.TXT(fmt.Sprintf("%s._wireguard._udp.example.com. 0 IN TXT allowedip=%s pubkey=%s", peer1b32, peer1.AllowedIPs[0].String(), strings.TrimRight(peer1b32, "="))), }, }, { @@ -85,6 +90,7 @@ func TestWGSD(t *testing.T) { }, Extra: []dns.RR{ test.AAAA(fmt.Sprintf("%s._wireguard._udp.example.com. 0 IN AAAA %s", peer2b32, peer2.Endpoint.IP.String())), + test.TXT(fmt.Sprintf("%s._wireguard._udp.example.com. 0 IN TXT allowedip=%s pubkey=%s", peer2b32, peer2.AllowedIPs[0].String(), strings.TrimRight(peer2b32, "="))), }, }, { From 5c11196a75cd04ecf4f2ab53c52d4d9808a0ff66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Ganne?= Date: Mon, 7 Dec 2020 10:56:31 +0100 Subject: [PATCH 3/4] add support for peer auto-discovery in wgsd-client This patch allows wgsd-client to auto-discover other mesh peers: - use peer allowed ip and pubkey from TXT record in SRV answer - iterate on peers extracted from PTR answer instead of local wireguard configuration: the client no longer need to pre-configured with all the peers - allow to connect to a specific peer using its pubkey or allowed ip --- cmd/wgsd-client/main.go | 205 +++++++++++++++++++++++++++------------- 1 file changed, 140 insertions(+), 65 deletions(-) diff --git a/cmd/wgsd-client/main.go b/cmd/wgsd-client/main.go index 27e5f4a..7897671 100644 --- a/cmd/wgsd-client/main.go +++ b/cmd/wgsd-client/main.go @@ -3,13 +3,12 @@ package main import ( "context" "encoding/base32" - "encoding/base64" "flag" - "fmt" "log" "net" "os" "os/signal" + "strings" "syscall" "time" @@ -24,6 +23,12 @@ var ( dnsServerFlag = flag.String("dns", "", "ip:port of DNS server") dnsZoneFlag = flag.String("zone", "", "dns zone name") + serviceFlag = flag.String("service", "", "service ip or public key") +) + +const ( + keyLen = 56 // the number of characters in a base32-encoded Wireguard public key + spPrefix = "_wireguard._udp." ) func main() { @@ -64,76 +69,45 @@ func main() { dnsClient := &dns.Client{ Timeout: time.Second * 5, } - for _, peer := range wgDevice.Peers { + + suffix := spPrefix + dns.Fqdn(*dnsZoneFlag) + + if len(*serviceFlag) >= 1 { + fqdn := *serviceFlag + suffix = "." + suffix + if !strings.HasSuffix(fqdn, suffix) { + fqdn += suffix + } + ConnectPeer(ctx, wgClient, wgDevice, dnsClient, fqdn, *dnsServerFlag) + return + } + + srvCtx, srvCancel := context.WithCancel(ctx) + m := &dns.Msg{} + m.SetQuestion(suffix, dns.TypePTR) + r, _, err := dnsClient.ExchangeContext(srvCtx, m, *dnsServerFlag) + srvCancel() + if err != nil { + log.Printf("failed to lookup PTR: %v", err) + return + } + if len(r.Answer) < 1 { + log.Printf("no PTR records found") + return + } + + for _, answer := range r.Answer { select { case <-ctx.Done(): return default: } - srvCtx, srvCancel := context.WithCancel(ctx) - pubKeyBase32 := base32.StdEncoding.EncodeToString(peer.PublicKey[:]) - pubKeyBase64 := base64.StdEncoding.EncodeToString(peer.PublicKey[:]) - m := &dns.Msg{} - question := fmt.Sprintf("%s._wireguard._udp.%s", - pubKeyBase32, dns.Fqdn(*dnsZoneFlag)) - m.SetQuestion(question, dns.TypeSRV) - r, _, err := dnsClient.ExchangeContext(srvCtx, m, *dnsServerFlag) - srvCancel() - if err != nil { - log.Printf( - "[%s] failed to lookup SRV: %v", pubKeyBase64, err) + ptr, ok := answer.(*dns.PTR) + if !ok { + log.Printf("non-PTR answer in response to PTR query: %s", answer.String()) continue } - if len(r.Answer) < 1 { - log.Printf("[%s] no SRV records found", pubKeyBase64) - continue - } - srv, ok := r.Answer[0].(*dns.SRV) - if !ok { - log.Printf( - "[%s] non-SRV answer in response to SRV query: %s", - pubKeyBase64, r.Answer[0].String()) - } - if len(r.Extra) < 1 { - log.Printf("[%s] SRV response missing extra A/AAAA", - pubKeyBase64) - } - var endpointIP net.IP - hostA, ok := r.Extra[0].(*dns.A) - if !ok { - hostAAAA, ok := r.Extra[0].(*dns.AAAA) - if !ok { - log.Printf( - "[%s] non-A/AAAA extra in SRV response: %s", - pubKeyBase64, r.Extra[0].String()) - continue - } - endpointIP = hostAAAA.AAAA - } else { - endpointIP = hostA.A - } - peerConfig := wgtypes.PeerConfig{ - PublicKey: peer.PublicKey, - UpdateOnly: true, - Endpoint: &net.UDPAddr{ - IP: endpointIP, - Port: int(srv.Port), - }, - } - deviceConfig := wgtypes.Config{ - PrivateKey: &wgDevice.PrivateKey, - ReplacePeers: false, - Peers: []wgtypes.PeerConfig{peerConfig}, - } - if wgDevice.FirewallMark > 0 { - deviceConfig.FirewallMark = &wgDevice.FirewallMark - } - err = wgClient.ConfigureDevice(*deviceFlag, deviceConfig) - if err != nil { - log.Printf( - "[%s] failed to configure peer on %s, error: %v", - pubKeyBase64, *deviceFlag, err) - } + ConnectPeer(ctx, wgClient, wgDevice, dnsClient, ptr.Ptr, *dnsServerFlag) } }() sigCh := make(chan os.Signal, 1) @@ -146,3 +120,104 @@ func main() { case <-done: } } + +func ConnectPeer(ctx context.Context, wgClient *wgctrl.Client, wgDevice *wgtypes.Device, dnsClient *dns.Client, serviceFqdn string, dnsServer string) { + srvCtx, srvCancel := context.WithCancel(ctx) + m := &dns.Msg{} + m.SetQuestion(serviceFqdn, dns.TypeSRV) + r, _, err := dnsClient.ExchangeContext(srvCtx, m, dnsServer) + srvCancel() + if err != nil { + log.Printf( + "[%s] failed to lookup SRV: %v", serviceFqdn, err) + return + } + if len(r.Answer) < 1 { + log.Printf("[%s] no SRV records found", serviceFqdn) + return + } + srv, ok := r.Answer[0].(*dns.SRV) + if !ok { + log.Printf( + "[%s] non-SRV answer in response to SRV query: %s", + serviceFqdn, r.Answer[0].String()) + return + } + if len(r.Extra) < 2 { + log.Printf("[%s] SRV response missing extra A/AAAA and TXT", + serviceFqdn) + return + } + var endpointIP net.IP + hostA, ok := r.Extra[0].(*dns.A) + if !ok { + hostAAAA, ok := r.Extra[0].(*dns.AAAA) + if !ok { + log.Printf( + "[%s] non-A/AAAA extra in SRV response: %s", + serviceFqdn, r.Extra[0].String()) + return + } + endpointIP = hostAAAA.AAAA + } else { + endpointIP = hostA.A + } + txt, ok := r.Extra[1].(*dns.TXT) + if !ok { + log.Printf("[%s] non-TXT extra in SRV response: %s", + serviceFqdn, r.Extra[1].String()) + return + } + allowedIPsString := strings.TrimPrefix(strings.ToLower(txt.Txt[0]), "allowedip=") + _, allowedIPs, err := net.ParseCIDR(allowedIPsString) + if err != nil { + log.Printf("[%s] failed to parse allowedip in TXT extra: %s", serviceFqdn, r.Extra[1].String()) + return + } + pubKeyString := strings.TrimPrefix(strings.ToUpper(txt.Txt[1]), "PUBKEY=") + if len(pubKeyString) < keyLen { + pubKeyString += strings.Repeat("=", keyLen-len(pubKeyString)) + } + pubKeyBytes, err := base32.StdEncoding.DecodeString(strings.ToUpper(pubKeyString)) + if err != nil { + log.Printf("[%s] failed to decode base32 key %s: %v", serviceFqdn, pubKeyString, err) + return + } + pubKeyWg, err := wgtypes.NewKey(pubKeyBytes) + if err != nil { + log.Printf("[%s] failed to create wg key: %v", serviceFqdn, err) + return + } + if pubKeyWg == wgDevice.PublicKey { + log.Printf("[%s] skipping ourself", serviceFqdn) + return + } + + peerConfig := wgtypes.PeerConfig{ + PublicKey: pubKeyWg, + UpdateOnly: false, + Endpoint: &net.UDPAddr{ + IP: endpointIP, + Port: int(srv.Port), + }, + ReplaceAllowedIPs: true, + AllowedIPs: []net.IPNet{*allowedIPs}, + } + deviceConfig := wgtypes.Config{ + PrivateKey: &wgDevice.PrivateKey, + ReplacePeers: false, + Peers: []wgtypes.PeerConfig{peerConfig}, + } + if wgDevice.FirewallMark > 0 { + deviceConfig.FirewallMark = &wgDevice.FirewallMark + } + err = wgClient.ConfigureDevice(*deviceFlag, deviceConfig) + if err != nil { + log.Printf( + "[%s] failed to configure peer on %s, error: %v", + serviceFqdn, *deviceFlag, err) + return + } + + log.Printf("[%s] configure peer on %s", serviceFqdn, *deviceFlag) +} From 1b07f68f20301e86fec7874431f06a7a613fa0e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Ganne?= Date: Mon, 7 Dec 2020 12:04:23 +0100 Subject: [PATCH 4/4] add vagrant playground --- vagrant/Corefile | 6 +++++ vagrant/README | 11 +++++++++ vagrant/Vagrantfile | 54 +++++++++++++++++++++++++++++++++++++++++++++ vagrant/add.sh | 32 +++++++++++++++++++++++++++ vagrant/setup.sh | 19 ++++++++++++++++ 5 files changed, 122 insertions(+) create mode 100644 vagrant/Corefile create mode 100644 vagrant/README create mode 100644 vagrant/Vagrantfile create mode 100755 vagrant/add.sh create mode 100755 vagrant/setup.sh diff --git a/vagrant/Corefile b/vagrant/Corefile new file mode 100644 index 0000000..03d208b --- /dev/null +++ b/vagrant/Corefile @@ -0,0 +1,6 @@ +.:5353 { + debug + bind 127.0.0.1 + bind 192.168.100.10 + wgsd example.com. wg0 +} diff --git a/vagrant/README b/vagrant/README new file mode 100644 index 0000000..609648e --- /dev/null +++ b/vagrant/README @@ -0,0 +1,11 @@ +Quick start instructions + +Clone & build wgsd: +~# go get github.com/jwhited/wgsd + +Start and provision VMs with Vagrant: +~# cd ~/go/src/github.com/jwhited/wgsd/vagrant +~# vagrant up + +Setup Wireguard Mesh: +~# ./setup.sh diff --git a/vagrant/Vagrantfile b/vagrant/Vagrantfile new file mode 100644 index 0000000..fbca03b --- /dev/null +++ b/vagrant/Vagrantfile @@ -0,0 +1,54 @@ +# -*- mode: ruby -*- +# vi: set ft=ruby : + +Vagrant.configure("2") do |config| + + config.trigger.before :up do |trigger| + trigger.run = {inline: "cp -uvf ../../../../../bin/coredns ../../../../../bin/wgsd-client ."} + end + + config.vm.box = "ubuntu/focal64" + config.vm.box_check_update = false + + config.vm.synced_folder ".", "/vagrant", type: "rsync" + + config.vm.provision "shell", inline: <<-SHELL + apt-get -y update + apt-get -y install wireguard + SHELL + + config.vm.define "registry" do |registry| + registry.vm.hostname = "registry" + registry.vm.network "private_network", ip: "192.168.33.10" + registry.vm.provision "shell", inline: <<-SHELL + wg genkey | tee /etc/wireguard/privatekey | wg pubkey | tee /etc/wireguard/publickey + cat > /etc/wireguard/wg0.conf << EOF +[Interface] +PrivateKey = $(cat /etc/wireguard/privatekey) +Address = 192.168.100.10/24 +SaveConfig = True +ListenPort = 51820 +EOF + chmod 600 /etc/wireguard/{privatekey,wg0.conf} + chmod 644 /etc/wireguard/publickey + chmod 711 /etc/wireguard + systemctl enable wg-quick@wg0 + systemctl start wg-quick@wg0 + cat > /etc/rc.local << EOF +#!/bin/sh +/vagrant/coredns -conf /vagrant/Corefile | logger & +EOF + chmod 755 /etc/rc.local + sleep 1 + /etc/rc.local + SHELL + end + + (1..4).each do |i| + config.vm.define "client-#{i}" do |client| + client.vm.hostname = "client-#{i}" + client.vm.network "private_network", ip: "192.168.33.10#{i}" + end + end + +end diff --git a/vagrant/add.sh b/vagrant/add.sh new file mode 100755 index 0000000..d887c8b --- /dev/null +++ b/vagrant/add.sh @@ -0,0 +1,32 @@ +#!/bin/sh +set -eux +VM=$1 +ADDR=$2 + +SERVER_KEY=$(vagrant ssh registry -- cat /etc/wireguard/publickey) + +vagrant ssh $VM -- sudo bash -s << EOF +wg genkey | tee /etc/wireguard/privatekey | wg pubkey | tee /etc/wireguard/publickey +# linux config +cat > /etc/wireguard/wg0.conf << CLIENTEOF +[Interface] +PrivateKey = \$(cat /etc/wireguard/privatekey) +Address = $ADDR/24 +ListenPort = 51820 +[Peer] +PublicKey = $SERVER_KEY +Endpoint = 192.168.33.10:51820 +AllowedIPs = 192.168.100.10/32 +CLIENTEOF +chmod 600 /etc/wireguard/{privatekey,wg0.conf} +chmod 644 /etc/wireguard/publickey +chmod 711 /etc/wireguard +EOF + +CLIENT_KEY=$(vagrant ssh $VM -- cat /etc/wireguard/publickey) + +vagrant ssh registry -- sudo wg set wg0 peer $CLIENT_KEY allowed-ips $ADDR/32 + +vagrant ssh $VM -- sudo systemctl enable wg-quick@wg0 +vagrant ssh $VM -- sudo systemctl restart wg-quick@wg0 +vagrant ssh $VM -- ping -c2 192.168.100.10 diff --git a/vagrant/setup.sh b/vagrant/setup.sh new file mode 100755 index 0000000..3a1df8f --- /dev/null +++ b/vagrant/setup.sh @@ -0,0 +1,19 @@ +#!/bin/bash +set -eux + +PEER_NR=4 + +for ((i=1;i<=$PEER_NR;i++));do + ./add.sh client-$i 192.168.100.10$i +done + +for ((i=1;i<=$PEER_NR;i++));do + vagrant ssh client-$i -- sudo /vagrant/wgsd-client -device wg0 -dns 192.168.100.10:5353 -zone example.com. +done + +for ((i=1;i<=$PEER_NR;i++));do + vagrant ssh client-$i -- ping -c2 192.168.100.10 + for ((j=1;j<=$PEER_NR;j++));do + vagrant ssh client-$i -- ping -c2 192.168.100.10$j + done +done