2022-04-04 08:29:28 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"net"
|
|
|
|
"os"
|
|
|
|
"path/filepath"
|
|
|
|
|
2024-01-20 15:41:54 +00:00
|
|
|
"github.com/rs/zerolog/log"
|
2022-04-04 08:29:28 +00:00
|
|
|
"golang.zx2c4.com/wireguard/wgctrl"
|
|
|
|
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
|
|
|
|
|
|
|
"novit.tech/direktil/pkg/config"
|
|
|
|
)
|
|
|
|
|
|
|
|
func setupVPN(vpn config.VPNDef, localGenDir string) {
|
2024-01-20 15:41:54 +00:00
|
|
|
log := log.With().Str("vpn", vpn.Name).Logger()
|
|
|
|
|
|
|
|
log.Info().Msg("VPN: setting up")
|
2022-04-04 08:29:28 +00:00
|
|
|
|
|
|
|
vpnDir := filepath.Join(localGenDir, vpn.Name)
|
|
|
|
os.MkdirAll(vpnDir, 0750)
|
|
|
|
|
2024-01-20 15:41:54 +00:00
|
|
|
logMsg := log.Info()
|
|
|
|
if vpn.ListenPort != nil {
|
|
|
|
logMsg.Int("ListenPort", *vpn.ListenPort)
|
|
|
|
}
|
|
|
|
|
2022-04-04 08:29:28 +00:00
|
|
|
// public/private key
|
|
|
|
keyFile := filepath.Join(vpnDir, "key")
|
2023-11-27 13:08:44 +00:00
|
|
|
keyBytes, err := os.ReadFile(keyFile)
|
2022-04-04 08:29:28 +00:00
|
|
|
if os.IsNotExist(err) {
|
|
|
|
key, err := wgtypes.GeneratePrivateKey()
|
|
|
|
if err != nil {
|
|
|
|
fatalf("failed to generate VPN key: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
keyBytes = []byte(key.String())
|
|
|
|
|
2023-11-27 13:08:44 +00:00
|
|
|
os.WriteFile(keyFile, keyBytes, 0600)
|
2022-04-04 08:29:28 +00:00
|
|
|
} else if err != nil {
|
|
|
|
fatalf("failed to read VPN key: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
key, err := wgtypes.ParseKey(string(keyBytes))
|
|
|
|
if err != nil {
|
|
|
|
fatalf("bad VPN key: %v", err)
|
|
|
|
}
|
|
|
|
|
2024-01-20 15:41:54 +00:00
|
|
|
logMsg.Stringer("PublicKey", key.PublicKey())
|
2022-04-04 08:29:28 +00:00
|
|
|
|
|
|
|
// pre-shared key
|
|
|
|
pskeyFile := filepath.Join(vpnDir, "pskey")
|
2023-11-27 13:08:44 +00:00
|
|
|
pskeyBytes, err := os.ReadFile(pskeyFile)
|
2022-04-04 08:29:28 +00:00
|
|
|
if os.IsNotExist(err) {
|
|
|
|
key, err := wgtypes.GenerateKey()
|
|
|
|
if err != nil {
|
|
|
|
fatalf("failed to generate VPN pre-shared key: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
pskeyBytes = []byte(key.String())
|
|
|
|
|
2023-11-27 13:08:44 +00:00
|
|
|
os.WriteFile(pskeyFile, pskeyBytes, 0600)
|
2022-04-04 08:29:28 +00:00
|
|
|
} else if err != nil {
|
|
|
|
fatalf("failed to read VPN pre-shared key: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
pskey, err := wgtypes.ParseKey(string(pskeyBytes))
|
|
|
|
if err != nil {
|
|
|
|
fatalf("bad VPN pre-shared key: %v", err)
|
|
|
|
}
|
|
|
|
|
2024-01-20 15:41:54 +00:00
|
|
|
{
|
|
|
|
keyStr := key.String()
|
|
|
|
logMsg.Str("PresharedKey", keyStr[0:4]+"..."+keyStr[len(keyStr)-4:])
|
|
|
|
}
|
2022-04-04 08:29:28 +00:00
|
|
|
|
|
|
|
// setup interface
|
|
|
|
cfg := wgtypes.Config{
|
|
|
|
PrivateKey: &key,
|
|
|
|
ListenPort: vpn.ListenPort,
|
|
|
|
Peers: make([]wgtypes.PeerConfig, 0, len(vpn.Peers)),
|
|
|
|
}
|
|
|
|
|
|
|
|
for idx, vpnPeer := range vpn.Peers {
|
|
|
|
vpnPeer := vpnPeer
|
|
|
|
|
|
|
|
wgPeer := wgtypes.PeerConfig{
|
|
|
|
Endpoint: vpnPeer.Endpoint,
|
|
|
|
AllowedIPs: make([]net.IPNet, 0, len(vpnPeer.AllowedIPs)),
|
|
|
|
|
|
|
|
PersistentKeepaliveInterval: &vpnPeer.KeepAlive,
|
|
|
|
}
|
|
|
|
|
|
|
|
if vpnPeer.WithPreSharedKey {
|
|
|
|
wgPeer.PresharedKey = &pskey
|
|
|
|
}
|
|
|
|
|
|
|
|
pubkey, err := wgtypes.ParseKey(vpnPeer.PublicKey)
|
|
|
|
if err != nil {
|
|
|
|
fatalf("bad VPN peer[%d] public key: %v", idx, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
wgPeer.PublicKey = pubkey
|
|
|
|
|
|
|
|
for _, ipnetStr := range vpnPeer.AllowedIPs {
|
|
|
|
_, ipnet, err := net.ParseCIDR(ipnetStr)
|
|
|
|
if err != nil {
|
|
|
|
fatalf("bad IP/net: %q: %v", ipnetStr, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
wgPeer.AllowedIPs = append(wgPeer.AllowedIPs, *ipnet)
|
|
|
|
}
|
|
|
|
|
|
|
|
cfg.Peers = append(cfg.Peers, wgPeer)
|
|
|
|
}
|
|
|
|
|
|
|
|
wg, err := wgctrl.New()
|
|
|
|
if err != nil {
|
|
|
|
fatalf("failed to setup WireGuard client: %v", err)
|
|
|
|
}
|
|
|
|
defer wg.Close()
|
|
|
|
|
2024-01-20 15:41:54 +00:00
|
|
|
log.Info().Strs("ips", vpn.IPs).Msg("VPN: creating interface")
|
2022-04-04 08:29:28 +00:00
|
|
|
run("ip", "link", "add", vpn.Name, "type", "wireguard")
|
|
|
|
|
|
|
|
for _, ip := range vpn.IPs {
|
|
|
|
run("ip", "addr", "add", ip, "dev", vpn.Name)
|
|
|
|
}
|
|
|
|
|
2024-01-20 15:41:54 +00:00
|
|
|
logMsg.Msg("VPN: configuring interface")
|
2022-04-04 08:29:28 +00:00
|
|
|
err = wg.ConfigureDevice(vpn.Name, cfg)
|
|
|
|
if err != nil {
|
|
|
|
fatalf("failed to setup VPN %s: %v", vpn.Name, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
run("ip", "link", "set", vpn.Name, "up")
|
|
|
|
}
|