mirror of
https://github.com/edgelesssys/constellation.git
synced 2024-12-27 00:19:36 -05:00
167 lines
4.3 KiB
Go
167 lines
4.3 KiB
Go
package vpn
|
|
|
|
import (
|
|
"fmt"
|
|
"net"
|
|
"time"
|
|
|
|
wgquick "github.com/nmiculinic/wg-quick-go"
|
|
"github.com/vishvananda/netlink"
|
|
"golang.zx2c4.com/wireguard/wgctrl"
|
|
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
|
)
|
|
|
|
const (
|
|
interfaceName = "wg0"
|
|
wireguardPort = 51820
|
|
)
|
|
|
|
type vpn interface {
|
|
ConfigureDevice(name string, cfg wgtypes.Config) error
|
|
}
|
|
|
|
type networkLink interface {
|
|
LinkAdd(link netlink.Link) error
|
|
LinkByName(name string) (netlink.Link, error)
|
|
ParseAddr(s string) (*netlink.Addr, error)
|
|
AddrAdd(link netlink.Link, addr *netlink.Addr) error
|
|
LinkSetUp(link netlink.Link) error
|
|
}
|
|
|
|
type netLink struct{}
|
|
|
|
func newNetLink() *netLink {
|
|
return &netLink{}
|
|
}
|
|
|
|
func (n *netLink) LinkAdd(link netlink.Link) error {
|
|
return netlink.LinkAdd(link)
|
|
}
|
|
|
|
func (n *netLink) LinkByName(name string) (netlink.Link, error) {
|
|
return netlink.LinkByName(name)
|
|
}
|
|
|
|
func (n *netLink) ParseAddr(s string) (*netlink.Addr, error) {
|
|
return netlink.ParseAddr(s)
|
|
}
|
|
|
|
func (n *netLink) AddrAdd(link netlink.Link, addr *netlink.Addr) error {
|
|
return netlink.AddrAdd(link, addr)
|
|
}
|
|
|
|
func (n *netLink) LinkSetUp(link netlink.Link) error {
|
|
return netlink.LinkSetUp(link)
|
|
}
|
|
|
|
type Configurer struct {
|
|
netLink networkLink
|
|
vpn vpn
|
|
}
|
|
|
|
// NewConfigurerWithDefaults creates a new vpn client.
|
|
func NewConfigurerWithDefaults() (*Configurer, error) {
|
|
vpn, err := wgctrl.New()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &Configurer{netLink: newNetLink(), vpn: vpn}, nil
|
|
}
|
|
|
|
// NewConfigurer creates a new vpn client with the provided
|
|
// network link and vpn interface.
|
|
func NewConfigurer(netLink networkLink, vpn vpn) (*Configurer, error) {
|
|
return &Configurer{netLink: netLink, vpn: vpn}, nil
|
|
}
|
|
|
|
// Configure configures a WireGuard interface
|
|
// WireGuard will listen on its default port.
|
|
// The peer must have the IP 10.118.0.1 in the vpn.
|
|
func (c *Configurer) Configure(clientVpnIp, coordinatorPubKey, coordinatorPubIP, clientPrivKey string) error {
|
|
wgLink := &netlink.Wireguard{LinkAttrs: netlink.LinkAttrs{Name: interfaceName}}
|
|
if err := c.netLink.LinkAdd(wgLink); err != nil {
|
|
return err
|
|
}
|
|
|
|
link, err := c.netLink.LinkByName(interfaceName)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
addr, err := c.netLink.ParseAddr(clientVpnIp + "/16")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err := c.netLink.AddrAdd(link, addr); err != nil {
|
|
return err
|
|
}
|
|
if err := c.netLink.LinkSetUp(link); err != nil {
|
|
return err
|
|
}
|
|
|
|
config, err := NewConfig(coordinatorPubKey, coordinatorPubIP, clientPrivKey)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return c.vpn.ConfigureDevice(interfaceName, config)
|
|
}
|
|
|
|
// NewConfig creates a new WireGuard configuration.
|
|
func NewConfig(coordinatorPubKey, coordinatorPubIP, clientPrivKey string) (wgtypes.Config, error) {
|
|
_, allowedIPs, err := net.ParseCIDR("10.118.0.1/32")
|
|
if err != nil {
|
|
return wgtypes.Config{}, fmt.Errorf("parsing CIDR: %w", err)
|
|
}
|
|
|
|
coordinatorPubKeyParsed, err := wgtypes.ParseKey(coordinatorPubKey)
|
|
if err != nil {
|
|
return wgtypes.Config{}, fmt.Errorf("parsing coordinator public key: %w", err)
|
|
}
|
|
|
|
var endpoint *net.UDPAddr
|
|
if ip := net.ParseIP(coordinatorPubIP); ip != nil {
|
|
endpoint = &net.UDPAddr{IP: ip, Port: wireguardPort}
|
|
} else {
|
|
endpoint = nil
|
|
}
|
|
clientPrivKeyParsed, err := wgtypes.ParseKey(clientPrivKey)
|
|
if err != nil {
|
|
return wgtypes.Config{}, fmt.Errorf("parsing client private key: %w", err)
|
|
}
|
|
listenPort := wireguardPort
|
|
|
|
keepAlive := 10 * time.Second
|
|
return wgtypes.Config{
|
|
PrivateKey: &clientPrivKeyParsed,
|
|
ListenPort: &listenPort,
|
|
ReplacePeers: false,
|
|
Peers: []wgtypes.PeerConfig{
|
|
{
|
|
PublicKey: coordinatorPubKeyParsed,
|
|
UpdateOnly: false,
|
|
Endpoint: endpoint,
|
|
AllowedIPs: []net.IPNet{*allowedIPs},
|
|
PersistentKeepaliveInterval: &keepAlive,
|
|
},
|
|
},
|
|
}, nil
|
|
}
|
|
|
|
// NewWGQuickConfig create a new WireGuard wg-quick configuration file and mashals it to bytes.
|
|
func NewWGQuickConfig(config wgtypes.Config, clientVPNIP string, mtu int) ([]byte, error) {
|
|
clientIP := net.ParseIP(clientVPNIP)
|
|
if clientIP == nil {
|
|
return nil, fmt.Errorf("invalid client vpn ip '%s'", clientVPNIP)
|
|
}
|
|
quickfile := wgquick.Config{
|
|
Config: config,
|
|
Address: []net.IPNet{{IP: clientIP, Mask: []byte{255, 255, 0, 0}}},
|
|
MTU: mtu,
|
|
}
|
|
data, err := quickfile.MarshalText()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("marshal wg-quick config: %w", err)
|
|
}
|
|
return data, nil
|
|
}
|