diff --git a/.gitignore b/.gitignore
index f1c181e..ecfd606 100644
--- a/.gitignore
+++ b/.gitignore
@@ -10,3 +10,70 @@
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
+
+# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm
+# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
+
+# User-specific stuff
+.idea/**/workspace.xml
+.idea/**/tasks.xml
+.idea/**/usage.statistics.xml
+.idea/**/dictionaries
+.idea/**/shelf
+
+# Generated files
+.idea/**/contentModel.xml
+
+# Sensitive or high-churn files
+.idea/**/dataSources/
+.idea/**/dataSources.ids
+.idea/**/dataSources.local.xml
+.idea/**/sqlDataSources.xml
+.idea/**/dynamic.xml
+.idea/**/uiDesigner.xml
+.idea/**/dbnavigator.xml
+
+# Gradle
+.idea/**/gradle.xml
+.idea/**/libraries
+
+# Gradle and Maven with auto-import
+# When using Gradle or Maven with auto-import, you should exclude module files,
+# since they will be recreated, and may cause churn. Uncomment if using
+# auto-import.
+# .idea/modules.xml
+# .idea/*.iml
+# .idea/modules
+
+# CMake
+cmake-build-*/
+
+# Mongo Explorer plugin
+.idea/**/mongoSettings.xml
+
+# File-based project format
+*.iws
+
+# IntelliJ
+out/
+
+# mpeltonen/sbt-idea plugin
+.idea_modules/
+
+# JIRA plugin
+atlassian-ide-plugin.xml
+
+# Cursive Clojure plugin
+.idea/replstate.xml
+
+# Crashlytics plugin (for Android Studio and IntelliJ)
+com_crashlytics_export_strings.xml
+crashlytics.properties
+crashlytics-build.properties
+fabric.properties
+
+# Editor-based Rest Client
+.idea/httpRequests
+
+# Android studio 3.1+ serialized cache file
+.idea/caches/build_file_checksums.ser
diff --git a/.idea/encodings.xml b/.idea/encodings.xml
new file mode 100644
index 0000000..bde1df6
--- /dev/null
+++ b/.idea/encodings.xml
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000..10af178
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000..f3760a5
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..5ace414
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/.idea/wg-quick-go.iml b/.idea/wg-quick-go.iml
new file mode 100644
index 0000000..9ef4a9b
--- /dev/null
+++ b/.idea/wg-quick-go.iml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
new file mode 100644
index 0000000..8ee1396
--- /dev/null
+++ b/.pre-commit-config.yaml
@@ -0,0 +1,22 @@
+exclude: ".vscode/*"
+repos:
+- repo: https://github.com/pre-commit/pre-commit-hooks
+ rev: v2.1.0
+ hooks:
+ - id: check-added-large-files
+ - id: check-ast
+ - id: check-case-conflict
+ - id: check-json
+ - id: check-merge-conflict
+ - id: check-symlinks
+ - id: end-of-file-fixer
+ - id: pretty-format-json
+ args:
+ - --autofix
+ - id: requirements-txt-fixer
+ - id: trailing-whitespace
+ - id: detect-private-key
+- repo: git://github.com/dnephin/pre-commit-golang
+ rev: v0.3.2
+ hooks:
+ - id: go-fmt
diff --git a/go.mod b/go.mod
new file mode 100644
index 0000000..88056c5
--- /dev/null
+++ b/go.mod
@@ -0,0 +1,29 @@
+module github.com/nmiculinic/wg-quick-go
+
+go 1.12
+
+require (
+ github.com/KrakenSystems/wg-operator v0.0.0-20190326122344-c32b2bf8f73a
+ github.com/emicklei/go-restful v2.9.1+incompatible // indirect
+ github.com/go-openapi/spec v0.19.0 // indirect
+ github.com/gogo/protobuf v1.2.1 // indirect
+ github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf // indirect
+ github.com/json-iterator/go v1.1.6 // indirect
+ github.com/mdlayher/wireguardctrl v0.0.0-20190325182839-fcf15c501b90
+ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
+ github.com/modern-go/reflect2 v1.0.1 // indirect
+ github.com/onsi/ginkgo v1.8.0 // indirect
+ github.com/onsi/gomega v1.5.0 // indirect
+ github.com/sirupsen/logrus v1.4.0
+ github.com/spf13/pflag v1.0.3 // indirect
+ github.com/stretchr/testify v1.3.0
+ github.com/vishvananda/netlink v1.0.0
+ github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc // indirect
+ gopkg.in/inf.v0 v0.9.1 // indirect
+ k8s.io/api v0.0.0-20190313115550-3c12c96769cc // indirect
+ k8s.io/apimachinery v0.0.0-20190323104403-03ac7a9ade42
+ k8s.io/klog v0.2.0 // indirect
+ k8s.io/kube-openapi v0.0.0-20190320154901-5e45bb682580 // indirect
+ sigs.k8s.io/controller-runtime v0.1.10 // indirect
+ sigs.k8s.io/yaml v1.1.0 // indirect
+)
diff --git a/go.sum b/go.sum
new file mode 100644
index 0000000..877d367
--- /dev/null
+++ b/go.sum
@@ -0,0 +1,112 @@
+github.com/KrakenSystems/wg-operator v0.0.0-20190326122344-c32b2bf8f73a h1:FUwaIyTLYJ2rJbH7/VrcDT673oW495PQ/5yhcnOY0Hs=
+github.com/KrakenSystems/wg-operator v0.0.0-20190326122344-c32b2bf8f73a/go.mod h1:KbgWSIZ/HfS5PPTlVYMtcfPDaf6psweYj50kjvyRPiw=
+github.com/PuerkitoBio/purell v1.1.0 h1:rmGxhojJlM0tuKtfdvliR84CFHljx9ag64t2xmVkjK4=
+github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
+github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
+github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
+github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/emicklei/go-restful v2.9.1+incompatible h1:TOKUBeOLGz/xFJ6a9bO8++X6xPN9HZR3LGv2RqnE6xM=
+github.com/emicklei/go-restful v2.9.1+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
+github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
+github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
+github.com/go-openapi/jsonpointer v0.17.0 h1:nH6xp8XdXHx8dqveo0ZuJBluCO2qGrPbDNZ0dwoRHP0=
+github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
+github.com/go-openapi/jsonreference v0.17.0 h1:yJW3HCkTHg7NOA+gZ83IPHzUSnUzGXhGmsdiCcMexbA=
+github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=
+github.com/go-openapi/spec v0.19.0 h1:A4SZ6IWh3lnjH0rG0Z5lkxazMGBECtrZcbyYQi+64k4=
+github.com/go-openapi/spec v0.19.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=
+github.com/go-openapi/swag v0.17.0 h1:iqrgMg7Q7SvtbWLlltPrkMs0UBJI6oTSs79JFRUi880=
+github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=
+github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE=
+github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
+github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
+github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ=
+github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
+github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf h1:+RRA9JqSOZFfKrOeqr2z77+8R2RKyh8PG66dcu1V0ck=
+github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI=
+github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
+github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
+github.com/json-iterator/go v1.1.6 h1:MrUvLMLTMxbqFJ9kzlvat/rYZqZnW3u4wkLzWTaFwKs=
+github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
+github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
+github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
+github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329 h1:2gxZ0XQIU/5z3Z3bUBu+FXuk2pFbkN6tcwi/pjyaDic=
+github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
+github.com/mdlayher/genetlink v0.0.0-20190313224034-60417448a851 h1:QYJTEbSDJvDBQenHYMxoiBQPgZ4QUcm75vACe3dkW7o=
+github.com/mdlayher/genetlink v0.0.0-20190313224034-60417448a851/go.mod h1:EsbsAEUEs15qC1cosAwxgCWV0Qhd8TmkxnA9Kw1Vhl4=
+github.com/mdlayher/netlink v0.0.0-20190313131330-258ea9dff42c h1:qYXI+3AN4zBWsTF5drEu1akWPu2juaXPs58tZ4/GaCg=
+github.com/mdlayher/netlink v0.0.0-20190313131330-258ea9dff42c/go.mod h1:eQB3mZE4aiYnlUsyGGCOpPETfdQq4Jhsgf1fk3cwQaA=
+github.com/mdlayher/wireguardctrl v0.0.0-20190325182839-fcf15c501b90 h1:OgbuxwTPCoAmEheXwQANix39Z1QK+6mLjfP6qrtqsB0=
+github.com/mdlayher/wireguardctrl v0.0.0-20190325182839-fcf15c501b90/go.mod h1:BdyuvE2RP0RjONNIBHLdaVM7iarlHbioRUbsVXeOAJM=
+github.com/mikioh/ipaddr v0.0.0-20190227002745-725a6cbe920e h1:Pa/tBadWnc5p6P32+GcWt20TvPB9dqlDbBn81oWptqE=
+github.com/mikioh/ipaddr v0.0.0-20190227002745-725a6cbe920e/go.mod h1:Ickgr2WtCLZ2MDGd4Gr0geeCH5HybhRJbonOgQpvSxc=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
+github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
+github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/ginkgo v1.8.0 h1:VkHVNpR4iVnU8XQR6DBm8BqYjN7CRzw+xKUbVVbbW9w=
+github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/gomega v1.5.0 h1:izbySO9zDPmjJ8rDjLvkA2zJHIo+HkYXHnf7eN7SSyo=
+github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/sirupsen/logrus v1.4.0 h1:yKenngtzGh+cUSSh6GWbxW2abRqhYUSR/t/6+2QqNvE=
+github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
+github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
+github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.1.1/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 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/vishvananda/netlink v1.0.0 h1:bqNY2lgheFIu1meHUFSH3d7vG93AFyqg3oGbJCOJgSM=
+github.com/vishvananda/netlink v1.0.0/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk=
+github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc h1:R83G5ikgLMxrBvLh22JhdfI8K6YXEPHx5P03Uu3DRs4=
+github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc/go.mod h1:ZjcWmFBXmLKZu9Nxj3WKYEafiSqer2rnvPr0en9UNpI=
+golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a h1:YX8ljsm6wXlHZO+aRz9Exqr0evNhKRNe5K/gi+zKh4U=
+golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190313220215-9f648a60d977 h1:actzWV6iWn3GLqN8dZjzsB+CLt+gaV2+wsxroxiQI8I=
+golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA=
+golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190312061237-fead79001313 h1:pczuHS43Cp2ktBEEmLwScxgjWsBSzdaQiKzUyf3DTTc=
+golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
+gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
+gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
+gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
+gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
+gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
+gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE=
+gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+k8s.io/api v0.0.0-20190313115550-3c12c96769cc h1:m/JS6kQd00rICnXLWlnJzMFQB4AplcURUopS8dKiWmI=
+k8s.io/api v0.0.0-20190313115550-3c12c96769cc/go.mod h1:iuAfoD4hCxJ8Onx9kaTIt30j7jUFS00AXQi6QMi99vA=
+k8s.io/apimachinery v0.0.0-20190323104403-03ac7a9ade42 h1:g8bMHs6e/f1W6z/yNaUsOCvFCaaDLNlEbtVcnIIrkhA=
+k8s.io/apimachinery v0.0.0-20190323104403-03ac7a9ade42/go.mod h1:ccL7Eh7zubPUSh9A3USN90/OzHNSVN6zxzde07TDCL0=
+k8s.io/klog v0.2.0 h1:0ElL0OHzF3N+OhoJTL0uca20SxtYt4X4+bzHeqrB83c=
+k8s.io/klog v0.2.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
+k8s.io/kube-openapi v0.0.0-20190320154901-5e45bb682580 h1:fq0ZXW/BAIFZH+dazlups6JTVdwzRo5d9riFA103yuQ=
+k8s.io/kube-openapi v0.0.0-20190320154901-5e45bb682580/go.mod h1:BXM9ceUBTj2QnfH2MK1odQs778ajze1RxcmP6S8RVVc=
+sigs.k8s.io/controller-runtime v0.1.10 h1:amLOmcekVdnsD1uIpmgRqfTbQWJ2qxvQkcdeFhcotn4=
+sigs.k8s.io/controller-runtime v0.1.10/go.mod h1:HFAYoOh6XMV+jKF1UjFwrknPbowfyHEHHRdJMf2jMX8=
+sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs=
+sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
diff --git a/wg.go b/wg.go
new file mode 100644
index 0000000..66782f6
--- /dev/null
+++ b/wg.go
@@ -0,0 +1,260 @@
+package wgctl
+
+import (
+ "bytes"
+ "encoding/base64"
+ "github.com/mdlayher/wireguardctrl"
+ "github.com/mdlayher/wireguardctrl/wgtypes"
+ log "github.com/sirupsen/logrus"
+ "github.com/vishvananda/netlink"
+ "net"
+ "os/exec"
+ "syscall"
+ "text/template"
+)
+
+type Config struct {
+ wgtypes.Config
+
+ // Address — a comma-separated list of IP (v4 or v6) addresses (optionally with CIDR masks) to be assigned to the interface. May be specified multiple times.
+ Address []*net.IPNet
+
+ // — a comma-separated list of IP (v4 or v6) addresses to be set as the interface’s DNS servers. May be specified multiple times. Upon bringing the interface up, this runs ‘resolvconf -a tun.INTERFACE -m 0 -x‘ and upon bringing it down, this runs ‘resolvconf -d tun.INTERFACE‘. If these particular invocations of resolvconf(8) are undesirable, the PostUp and PostDown keys below may be used instead.
+ DNS []net.IP
+ // — if not specified, the MTU is automatically determined from the endpoint addresses or the system default route, which is usually a sane choice. However, to manually specify an MTU to override this automatic discovery, this value may be specified explicitly.
+ MTU int
+
+ // Table — Controls the routing table to which routes are added. There are two special values: ‘off’ disables the creation of routes altogether, and ‘auto’ (the default) adds routes to the default table and enables special handling of default routes.
+ Table int
+
+ // PreUp, PostUp, PreDown, PostDown — script snippets which will be executed by bash(1) before/after setting up/tearing down the interface, most commonly used to configure custom DNS options or firewall rules. The special string ‘%i’ is expanded to INTERFACE. Each one may be specified multiple times, in which case the commands are executed in order.
+
+ PreUp string
+ PostUp string
+ PreDown string
+ PostDown string
+
+ // SaveConfig — if set to ‘true’, the configuration is saved from the current state of the interface upon shutdown.
+ SaveConfig bool
+}
+
+func (cfg *Config) String() string {
+ b, err := cfg.MarshalText()
+ if err != nil {
+ panic(err)
+ }
+ return string(b)
+}
+
+func (cfg *Config) MarshalText() (text []byte, err error) {
+ buff := &bytes.Buffer{}
+ if err := cfgTemplate.Execute(buff, cfg); err != nil {
+ return nil, err
+ }
+ return buff.Bytes(), nil
+}
+
+const wgtypeTemplateSpec = `[Interface]
+{{- range := .Address }}
+Address = {{ . }}
+{{ end }}
+{{- range := .DNS }}
+DNS = {{ . }}
+{{ end }}
+PrivateKey = {{ .PrivateKey | wgKey }}
+{{- if .ListenPort }}{{ "\n" }}ListenPort = {{ .ListenPort }}{{ end }}
+{{- if .MTU }}{{ "\n" }}MTU = {{ .MTU }}{{ end }}
+{{- if .Table }}{{ "\n" }}Table = {{ .Table }}{{ end }}
+{{- if .PreUp }}{{ "\n" }}PreUp = {{ .PreUp }}{{ end }}
+{{- if .PostUp }}{{ "\n" }}Table = {{ .Table }}{{ end }}
+{{- if .PreDown }}{{ "\n" }}PreDown = {{ .PreDown }}{{ end }}
+{{- if .PostDown }}{{ "\n" }}PostDown = {{ .PostDown }}{{ end }}
+{{- if .SaveConfig }}{{ "\n" }}SaveConfig = {{ .SaveConfig }}{{ end }}
+
+{{- range .Peers }}
+[Peer]
+PublicKey = {{ .PublicKey | wgKey }}
+AllowedIps = {{ range $i, $el := .AllowedIPs }}{{if $i}}, {{ end }}{{ $el }}{{ end }}
+{{- if .Endpoint }}{{ "\n" }}Endpoint = {{ .Endpoint }}{{ end }}
+{{- end }}
+`
+
+func serializeKey(key *wgtypes.Key) string {
+ return base64.StdEncoding.EncodeToString(key[:])
+}
+
+func ParseKey(key string) (wgtypes.Key, error) {
+ var pkey wgtypes.Key
+ pkeySlice, err := base64.StdEncoding.DecodeString(key)
+ if err != nil {
+ return pkey, err
+ }
+ copy(pkey[:], pkeySlice[:])
+ return pkey, nil
+}
+
+var cfgTemplate = template.Must(
+ template.
+ New("wg-cfg").
+ Funcs(template.FuncMap(map[string]interface{}{"wgKey": serializeKey})).
+ Parse(wgtypeTemplateSpec))
+
+func (cfg *Config) Up(iface string) error {
+
+ link, err := netlink.LinkByName(iface)
+ if err != nil {
+ if _, ok := err.(netlink.LinkNotFoundError); !ok {
+ log.Error(err, "cannot read link, probably doesn't exist")
+ return err
+ }
+ log.Info("link not found, creating")
+ wgLink := &netlink.GenericLink{
+ LinkAttrs: netlink.LinkAttrs{
+ Name: iface,
+ },
+ LinkType: "wireguard",
+ }
+ if err := netlink.LinkAdd(wgLink); err != nil {
+ log.Error(err, "cannot create link", "iface", iface)
+ return err
+ }
+ if err := exec.Command("ip", "link", "add", "dev", iface, "type", "wireguard").Run(); err != nil {
+ }
+
+ link, err = netlink.LinkByName(iface)
+ if err != nil {
+ log.Error(err, "cannot read link")
+ return err
+ }
+ }
+ log.Info("link", "type", link.Type(), "attrs", link.Attrs())
+ if err := netlink.LinkSetUp(link); err != nil {
+ log.Error(err, "cannot set link up", "type", link.Type(), "attrs", link.Attrs())
+ return err
+ }
+ log.Info("set device up", "iface", iface)
+
+ cl, err := wireguardctrl.New()
+ if err != nil {
+ log.Error(err, "cannot setup wireguard device")
+ return err
+ }
+
+ if err := cl.ConfigureDevice(iface, cfg.Config); err != nil {
+ log.Error(err, "cannot configure device", "iface", iface)
+ return err
+ }
+
+ if err := syncAddress(link, cfg); err != nil {
+ log.Error(err, "cannot sync addresses")
+ return err
+ }
+
+ if err := syncRoutes(link, cfg); err != nil {
+ log.Error(err, "cannot sync routes")
+ return err
+ }
+
+ log.Info("Successfully setup device", "iface", iface)
+ return nil
+
+}
+
+func syncAddress(link netlink.Link, cfg *Config) error {
+ addrs, err := netlink.AddrList(link, syscall.AF_INET)
+ if err != nil {
+ log.Error(err, "cannot read link address")
+ return err
+ }
+
+ presentAddresses := make(map[string]int, 0)
+ for _, addr := range addrs {
+ presentAddresses[addr.IPNet.String()] = 1
+ }
+
+ for _, addr := range cfg.Address {
+ _, present := presentAddresses[addr.String()]
+ presentAddresses[addr.String()] = 2
+ if present {
+ log.Info("address present", "addr", addr, "iface", link.Attrs().Name)
+ continue
+ }
+
+ if err := netlink.AddrAdd(link, &netlink.Addr{
+ IPNet: addr,
+ }); err != nil {
+ log.Error(err, "cannot add addr", "iface", link.Attrs().Name)
+ return err
+ }
+ log.Info("address added", "addr", addr, "iface", link.Attrs().Name)
+ }
+
+ for addr, p := range presentAddresses {
+ if p < 2 {
+ nlAddr, err := netlink.ParseAddr(addr)
+ if err != nil {
+ log.Error(err, "cannot parse del addr", "iface", link.Attrs().Name, "addr", addr)
+ return err
+ }
+ if err := netlink.AddrAdd(link, nlAddr); err != nil {
+ log.Error(err, "cannot delete addr", "iface", link.Attrs().Name, "addr", addr)
+ return err
+ }
+ log.Info("address deleted", "addr", addr, "iface", link.Attrs().Name)
+ }
+ }
+ return nil
+}
+
+func syncRoutes(link netlink.Link, cfg *Config) error {
+ routes, err := netlink.RouteList(link, syscall.AF_INET)
+ if err != nil {
+ log.Error(err, "cannot read existing routes")
+ return err
+ }
+
+ presentRoutes := make(map[string]int, 0)
+ for _, r := range routes {
+ presentRoutes[r.Dst.String()] = 1
+ }
+
+ for _, peer := range cfg.Peers {
+ for _, rt := range peer.AllowedIPs {
+ _, present := presentRoutes[rt.String()]
+ presentRoutes[rt.String()] = 2
+ if present {
+ log.Info("route present", "iface", link.Attrs().Name, "route", rt.String())
+ continue
+ }
+ if err := netlink.RouteAdd(&netlink.Route{
+ LinkIndex: link.Attrs().Index,
+ Dst: &rt,
+ }); err != nil {
+ log.Error(err, "cannot setup route", "iface", link.Attrs().Name, "route", rt.String())
+ return err
+ }
+ log.Info("route added", "iface", link.Attrs().Name, "route", rt.String())
+ }
+ }
+
+ // Clean extra routes
+ for rtStr, p := range presentRoutes {
+ _, rt, err := net.ParseCIDR(rtStr)
+ if err != nil {
+ log.Info("cannot parse route", "iface", link.Attrs().Name, "route", rtStr)
+ return err
+ }
+ if p < 2 {
+ log.Info("extra manual route found", "iface", link.Attrs().Name, "route", rt.String())
+ if err := netlink.RouteDel(&netlink.Route{
+ LinkIndex: link.Attrs().Index,
+ Dst: rt,
+ }); err != nil {
+ log.Error(err, "cannot setup route", "iface", link.Attrs().Name, "route", rt.String())
+ return err
+ }
+ log.Info("route deleted", "iface", link.Attrs().Name, "route", rt)
+ }
+ }
+ return nil
+}