wg-manager: new pkg

This commit is contained in:
nemunaire 2020-02-23 04:20:59 +01:00
parent e9ec1eb9b9
commit b3b2e5f11e
4 changed files with 210 additions and 0 deletions

23
pkg/wg-manager/Dockerfile Normal file
View 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
View File

@ -0,0 +1,2 @@
image: wg-manager
network: true

View 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")
}

View 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
}