wg-manager: new pkg
This commit is contained in:
parent
e9ec1eb9b9
commit
b3b2e5f11e
23
pkg/wg-manager/Dockerfile
Normal file
23
pkg/wg-manager/Dockerfile
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
FROM golang:alpine as gobuild
|
||||||
|
|
||||||
|
RUN apk add --no-cache git
|
||||||
|
|
||||||
|
WORKDIR /go/src/wg-manager
|
||||||
|
|
||||||
|
ADD cmd ./
|
||||||
|
|
||||||
|
RUN go get -d -v
|
||||||
|
RUN go build -v
|
||||||
|
|
||||||
|
|
||||||
|
FROM alpine
|
||||||
|
MAINTAINER Pierre-Olivier Mercier <nemunaire@nemunai.re>
|
||||||
|
|
||||||
|
RUN apk add --no-cache --initdb \
|
||||||
|
wireguard-tools-wg
|
||||||
|
|
||||||
|
EXPOSE 8081
|
||||||
|
|
||||||
|
COPY --from=gobuild /go/src/wg-manager/wg-manager /bin/wg-manager
|
||||||
|
|
||||||
|
ENTRYPOINT ["/bin/wg-manager"]
|
2
pkg/wg-manager/build.yml
Normal file
2
pkg/wg-manager/build.yml
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
image: wg-manager
|
||||||
|
network: true
|
44
pkg/wg-manager/cmd/main.go
Normal file
44
pkg/wg-manager/cmd/main.go
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
var bind = flag.String("bind", ":8081", "Bind port/socket")
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
// Prepare graceful shutdown
|
||||||
|
interrupt := make(chan os.Signal, 1)
|
||||||
|
signal.Notify(interrupt, os.Interrupt, syscall.SIGTERM)
|
||||||
|
signal.Notify(interrupt, os.Interrupt, syscall.SIGINT)
|
||||||
|
|
||||||
|
srv := &http.Server{
|
||||||
|
Addr: *bind,
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Println("Registering handlers...")
|
||||||
|
mux := http.NewServeMux()
|
||||||
|
mux.HandleFunc("/register", register)
|
||||||
|
http.HandleFunc("/", mux.ServeHTTP)
|
||||||
|
|
||||||
|
// Serve content
|
||||||
|
go func() {
|
||||||
|
log.Fatal(srv.ListenAndServe())
|
||||||
|
}()
|
||||||
|
log.Println(fmt.Sprintf("Ready, listening on %s", *bind))
|
||||||
|
|
||||||
|
// Wait shutdown signal
|
||||||
|
<-interrupt
|
||||||
|
|
||||||
|
log.Print("The service is shutting down...")
|
||||||
|
srv.Shutdown(context.Background())
|
||||||
|
log.Println("done")
|
||||||
|
}
|
141
pkg/wg-manager/cmd/register.go
Normal file
141
pkg/wg-manager/cmd/register.go
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
IFaceName = "wg0"
|
||||||
|
TunnelPort = 12912
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
SrvPubKey string
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
// Generate private key
|
||||||
|
outPrvK, err := exec.Command("wg", "genkey").Output()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("Unable to generate prvkey:", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
outPrvK = bytes.TrimSpace(outPrvK)
|
||||||
|
|
||||||
|
prvFile, err := ioutil.TempFile("", "wg")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("Unable to create tmpPrvFile", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer os.Remove(prvFile.Name())
|
||||||
|
|
||||||
|
if _, err := prvFile.Write(outPrvK); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := prvFile.Close(); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Calculate public key
|
||||||
|
cmdPubK := exec.Command("wg", "pubkey")
|
||||||
|
cmdPubK.Stdin = bytes.NewReader(outPrvK)
|
||||||
|
outPubK, err := cmdPubK.Output()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("Unable to calculate pubkey:", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
SrvPubKey = string(bytes.TrimSpace(outPubK))
|
||||||
|
|
||||||
|
// Set parameters
|
||||||
|
outSet, err := exec.Command("wg", "set", IFaceName, "private-key", prvFile.Name(), "listen-port", strconv.Itoa(TunnelPort)).CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("Something happend during wg set:", err, string(outSet))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("wg configured: public key:%s", string(outPubK))
|
||||||
|
}
|
||||||
|
|
||||||
|
type PubTunnel struct {
|
||||||
|
PubKey []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func register(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if addr := r.Header.Get("X-Forwarded-For"); addr != "" {
|
||||||
|
r.RemoteAddr = addr
|
||||||
|
}
|
||||||
|
log.Printf("%s \"%s %s\" [%s]\n", r.RemoteAddr, r.Method, r.URL.Path, r.UserAgent())
|
||||||
|
|
||||||
|
// Read the body
|
||||||
|
if r.ContentLength < 0 || r.ContentLength > 6553600 {
|
||||||
|
http.Error(w, "{errmsg:\"Request too large or request size unknown\"}", http.StatusRequestEntityTooLarge)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var pt PubTunnel
|
||||||
|
if err := json.NewDecoder(r.Body).Decode(&pt); err != nil {
|
||||||
|
http.Error(w, fmt.Sprintf("{errmsg:%q}", err), http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if next_ip, err := findNextIP(); err != nil {
|
||||||
|
http.Error(w, fmt.Sprintf("{errmsg:%q}", err), http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
addWgPeer(pt.PubKey, next_ip)
|
||||||
|
|
||||||
|
w.Header().Set("Content-Type", "text/plain")
|
||||||
|
w.Write([]byte(fmt.Sprintf(`# Address=%s/18
|
||||||
|
|
||||||
|
[Peer]
|
||||||
|
PublicKey = %s
|
||||||
|
Endpoint = %s:%d
|
||||||
|
AllowedIPs = 0.0.0.0/0
|
||||||
|
PersistentKeepalive = 15
|
||||||
|
`, next_ip, SrvPubKey, "172.17.0.15", TunnelPort)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func findNextIP() (next_ip net.IP, err error) {
|
||||||
|
var out []byte
|
||||||
|
out, err = exec.Command("wg", "show", IFaceName, "allowed-ips").Output()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
all_ips := string(out)
|
||||||
|
|
||||||
|
for j := byte(128); j < 190; j++ {
|
||||||
|
for i := byte(1); i < 254; i++ {
|
||||||
|
if !strings.Contains(all_ips, fmt.Sprintf("172.23.%d.%d/32", j, i)) {
|
||||||
|
next_ip = net.IPv4(172, 23, j, i)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = errors.New("No IP found")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func addWgPeer(cltPubKey []byte, next_ip net.IP) (err error) {
|
||||||
|
_, err = exec.Command(
|
||||||
|
"wg", "set", IFaceName,
|
||||||
|
"peer", base64.StdEncoding.EncodeToString(cltPubKey),
|
||||||
|
"allowed-ips", fmt.Sprintf("%s/32", next_ip),
|
||||||
|
).Output()
|
||||||
|
return
|
||||||
|
}
|
Reference in New Issue
Block a user