Skip to content

Commit 60be0da

Browse files
committed
feat: implement multi-doc Wireguard config
Fixes #10963 Also hides/deprecated `.machine.network.interfaces`, as every piece of it is now available as proper multi-doc. Signed-off-by: Andrey Smirnov <andrey.smirnov@siderolabs.com>
1 parent cf014cb commit 60be0da

File tree

24 files changed

+1648
-3847
lines changed

24 files changed

+1648
-3847
lines changed

cmd/talosctl/cmd/mgmt/cluster/create/clusterops/configmaker/internal/makers/common.go

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -326,12 +326,20 @@ func (m *Maker[T]) finalizeMachineConfigs() (*bundle.Bundle, error) {
326326
for i := range m.ClusterRequest.Nodes {
327327
node := &m.ClusterRequest.Nodes[i]
328328

329-
patchedCfg, err := wireguardConfigBundle.PatchConfig(node.IPs[0], node.Config)
329+
patch, err := wireguardConfigBundle.PatchNode(node.IPs[0])
330330
if err != nil {
331331
return nil, err
332332
}
333333

334-
node.Config = patchedCfg
334+
out, err := configpatcher.Apply(configpatcher.WithConfig(node.Config), []configpatcher.Patch{patch})
335+
if err != nil {
336+
return nil, err
337+
}
338+
339+
node.Config, err = out.Config()
340+
if err != nil {
341+
return nil, err
342+
}
335343
}
336344
}
337345

cmd/talosctl/pkg/mgmt/helpers/wireguard.go

Lines changed: 47 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -7,84 +7,82 @@ package helpers
77
import (
88
"fmt"
99
"net/netip"
10-
"strings"
1110
"time"
1211

12+
"github.com/siderolabs/gen/xslices"
13+
"github.com/siderolabs/go-pointer"
1314
sideronet "github.com/siderolabs/net"
1415
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
1516

16-
"github.com/siderolabs/talos/pkg/machinery/config"
17+
"github.com/siderolabs/talos/pkg/machinery/config/configpatcher"
1718
"github.com/siderolabs/talos/pkg/machinery/config/container"
18-
"github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1"
19+
"github.com/siderolabs/talos/pkg/machinery/config/types/network"
1920
)
2021

2122
// NewWireguardConfigBundle creates a new Wireguard config bundle.
22-
func NewWireguardConfigBundle(ips []netip.Addr, wireguardCidr string, listenPort, mastersCount int) (*WireguardConfigBundle, error) {
23-
configs := map[string]*v1alpha1.Device{}
23+
func NewWireguardConfigBundle(ips []netip.Addr, wireguardCidr string, listenPort, controlplanesCount int) (*WireguardConfigBundle, error) {
24+
configs := map[netip.Addr]*network.WireguardConfigV1Alpha1{}
2425
keys := make([]wgtypes.Key, len(ips))
25-
peers := make([]*v1alpha1.DeviceWireguardPeer, len(ips))
26+
peers := make([]network.WireguardPeer, len(ips))
27+
28+
wgCidr, err := netip.ParsePrefix(wireguardCidr)
29+
if err != nil {
30+
return nil, fmt.Errorf("failed to parse wireguard cidr %s: %w", wireguardCidr, err)
31+
}
2632

2733
for i, ip := range ips {
2834
key, err := wgtypes.GeneratePrivateKey()
2935
if err != nil {
3036
return nil, err
3137
}
3238

39+
wgAddr, err := sideronet.NthIPInNetwork(wgCidr, i+2)
40+
if err != nil {
41+
return nil, err
42+
}
43+
3344
keys[i] = key
3445

35-
peers[i] = &v1alpha1.DeviceWireguardPeer{
36-
WireguardAllowedIPs: []string{
37-
wireguardCidr,
46+
peers[i] = network.WireguardPeer{
47+
WireguardAllowedIPs: []network.Prefix{
48+
{
49+
Prefix: netip.PrefixFrom(wgAddr, wgAddr.BitLen()),
50+
},
3851
},
3952
WireguardPublicKey: key.PublicKey().String(),
4053
WireguardPersistentKeepaliveInterval: time.Second * 5,
4154
}
4255

43-
if i < mastersCount {
44-
peers[i].WireguardEndpoint = fmt.Sprintf("%s:%d", ip.String(), listenPort)
56+
if i < controlplanesCount {
57+
peers[i].WireguardEndpoint = network.AddrPort{AddrPort: netip.AddrPortFrom(ip, uint16(listenPort))}
4558
}
4659
}
4760

48-
parts := strings.Split(wireguardCidr, "/")
49-
networkNumber := parts[1]
50-
51-
network, err := netip.ParsePrefix(wireguardCidr)
52-
if err != nil {
53-
return nil, err
54-
}
55-
5661
for i, nodeIP := range ips {
57-
wgIP, err := sideronet.NthIPInNetwork(network, i+2)
62+
wgAddr, err := sideronet.NthIPInNetwork(wgCidr, i+2)
5863
if err != nil {
5964
return nil, err
6065
}
6166

62-
config := &v1alpha1.DeviceWireguardConfig{}
67+
config := network.NewWireguardConfigV1Alpha1("wg0")
6368

64-
var currentPeers []*v1alpha1.DeviceWireguardPeer
65-
66-
// add all peers except self
67-
for _, peer := range peers {
68-
if peer.PublicKey() != keys[i].PublicKey().String() {
69-
currentPeers = append(currentPeers, peer)
70-
}
71-
}
72-
73-
config.WireguardPeers = currentPeers
69+
config.WireguardPeers = xslices.Filter(peers, func(p network.WireguardPeer) bool {
70+
return p.WireguardPublicKey != keys[i].PublicKey().String()
71+
})
7472
config.WireguardPrivateKey = keys[i].String()
75-
76-
device := &v1alpha1.Device{
77-
DeviceInterface: "wg0",
78-
DeviceAddresses: []string{fmt.Sprintf("%s/%s", wgIP.String(), networkNumber)},
79-
DeviceWireguardConfig: config,
80-
DeviceMTU: 1500,
73+
config.LinkAddresses = []network.AddressConfig{
74+
{
75+
AddressAddress: netip.PrefixFrom(wgAddr, wgCidr.Bits()),
76+
},
8177
}
78+
config.LinkUp = pointer.To(true)
79+
config.LinkMTU = 1500
8280

83-
if i < mastersCount {
81+
if i < controlplanesCount {
8482
config.WireguardListenPort = listenPort
8583
}
8684

87-
configs[nodeIP.String()] = device
85+
configs[nodeIP] = config
8886
}
8987

9088
return &WireguardConfigBundle{
@@ -94,25 +92,20 @@ func NewWireguardConfigBundle(ips []netip.Addr, wireguardCidr string, listenPort
9492

9593
// WireguardConfigBundle allows assembling wireguard network configuration with first controlplane being listen node.
9694
type WireguardConfigBundle struct {
97-
configs map[string]*v1alpha1.Device
95+
configs map[netip.Addr]*network.WireguardConfigV1Alpha1
9896
}
9997

100-
// PatchConfig generates config patch for a node and patches the configuration data.
101-
func (w *WireguardConfigBundle) PatchConfig(ip fmt.Stringer, cfg config.Provider) (config.Provider, error) {
102-
config := cfg.RawV1Alpha1().DeepCopy()
103-
104-
if config.MachineConfig.MachineNetwork == nil {
105-
config.MachineConfig.MachineNetwork = &v1alpha1.NetworkConfig{
106-
NetworkInterfaces: []*v1alpha1.Device{},
107-
}
108-
}
109-
110-
device, ok := w.configs[ip.String()]
98+
// PatchNode generates config patch for a node.
99+
func (w *WireguardConfigBundle) PatchNode(ip netip.Addr) (configpatcher.Patch, error) {
100+
cfg, ok := w.configs[ip]
111101
if !ok {
112-
return nil, fmt.Errorf("failed to get wireguard config for node %s", ip.String())
102+
return nil, fmt.Errorf("no wireguard config for ip %s", ip.String())
113103
}
114104

115-
config.MachineConfig.MachineNetwork.NetworkInterfaces = append(config.MachineConfig.MachineNetwork.NetworkInterfaces, device)
105+
ctr, err := container.New(cfg)
106+
if err != nil {
107+
return nil, fmt.Errorf("failed to create wireguard config container: %w", err)
108+
}
116109

117-
return container.New(config)
110+
return configpatcher.NewStrategicMergePatch(ctr), nil
118111
}

internal/app/machined/pkg/controllers/network/link_config.go

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -362,7 +362,7 @@ func (ctrl *LinkConfigController) processDevicesConfiguration(
362362
}
363363

364364
if device.WireguardConfig() != nil {
365-
if err := wireguardLink(linkMap[deviceInterface], device.WireguardConfig()); err != nil {
365+
if err := wireguardLinkLegacy(linkMap[deviceInterface], device.WireguardConfig()); err != nil {
366366
logger.Error("error parsing wireguard config", zap.Error(err))
367367
}
368368
}
@@ -479,6 +479,8 @@ func (ctrl *LinkConfigController) processLinkConfigs(logger *zap.Logger, linkMap
479479

480480
SetBridgeSlave(linkMap[slaveLinkName], linkName)
481481
}
482+
case talosconfig.NetworkWireguardConfig:
483+
wireguardLink(linkMap[linkName], specificLinkConfig)
482484
default:
483485
logger.Error("unknown link config type", zap.String("linkName", linkName), zap.String("type", fmt.Sprintf("%T", specificLinkConfig)))
484486
}
@@ -535,7 +537,7 @@ func vlanLink(link *network.LinkSpecSpec, vlanName, linkName string, vlan vlaner
535537
}
536538
}
537539

538-
func wireguardLink(link *network.LinkSpecSpec, config talosconfig.WireguardConfig) error {
540+
func wireguardLinkLegacy(link *network.LinkSpecSpec, config talosconfig.WireguardConfig) error {
539541
link.Logical = true
540542
link.Kind = network.LinkKindWireguard
541543
link.Type = nethelpers.LinkNone
@@ -568,6 +570,27 @@ func wireguardLink(link *network.LinkSpecSpec, config talosconfig.WireguardConfi
568570
return nil
569571
}
570572

573+
func wireguardLink(link *network.LinkSpecSpec, config talosconfig.NetworkWireguardConfig) {
574+
link.Logical = true
575+
link.Kind = network.LinkKindWireguard
576+
link.Type = nethelpers.LinkNone
577+
link.Wireguard = network.WireguardSpec{
578+
PrivateKey: config.PrivateKey(),
579+
ListenPort: config.ListenPort().ValueOr(0),
580+
FirewallMark: config.FirewallMark().ValueOr(0),
581+
}
582+
583+
for _, peer := range config.Peers() {
584+
link.Wireguard.Peers = append(link.Wireguard.Peers, network.WireguardPeer{
585+
PublicKey: peer.PublicKey(),
586+
PresharedKey: peer.PresharedKey().ValueOr(""),
587+
Endpoint: peer.Endpoint().ValueOr(""),
588+
PersistentKeepaliveInterval: peer.PersistentKeepalive().ValueOr(0),
589+
AllowedIPs: peer.AllowedIPs(),
590+
})
591+
}
592+
}
593+
571594
func dummyLink(link *network.LinkSpecSpec) {
572595
link.Logical = true
573596
link.Kind = "dummy"

internal/app/machined/pkg/controllers/network/link_config_test.go

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,14 @@ import (
1616
"github.com/siderolabs/go-procfs/procfs"
1717
"github.com/stretchr/testify/assert"
1818
"github.com/stretchr/testify/suite"
19+
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
1920

2021
"github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest"
2122
netctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/network"
2223
"github.com/siderolabs/talos/pkg/machinery/config/container"
2324
networkcfg "github.com/siderolabs/talos/pkg/machinery/config/types/network"
2425
"github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1"
26+
"github.com/siderolabs/talos/pkg/machinery/fipsmode"
2527
"github.com/siderolabs/talos/pkg/machinery/nethelpers"
2628
"github.com/siderolabs/talos/pkg/machinery/resources/config"
2729
"github.com/siderolabs/talos/pkg/machinery/resources/network"
@@ -568,6 +570,71 @@ func (suite *LinkConfigSuite) TestMachineConfigurationNewStyle() {
568570
)
569571
}
570572

573+
func (suite *LinkConfigSuite) TestMachineConfigurationNewStyleNotFIPS() {
574+
if fipsmode.Strict() {
575+
suite.T().Skip("skipping test in strict FIPS mode")
576+
}
577+
578+
suite.Require().NoError(suite.Runtime().RegisterController(&netctrl.LinkConfigController{}))
579+
580+
privKey, err := wgtypes.GeneratePrivateKey()
581+
suite.Require().NoError(err)
582+
583+
pskKey, err := wgtypes.GenerateKey()
584+
suite.Require().NoError(err)
585+
586+
peerKey, err := wgtypes.GenerateKey()
587+
suite.Require().NoError(err)
588+
589+
wc1 := networkcfg.NewWireguardConfigV1Alpha1("wg0")
590+
wc1.LinkUp = pointer.To(true)
591+
wc1.WireguardPrivateKey = privKey.String()
592+
wc1.WireguardListenPort = 12345
593+
wc1.WireguardPeers = []networkcfg.WireguardPeer{
594+
{
595+
WireguardPublicKey: peerKey.PublicKey().String(),
596+
WireguardPresharedKey: pskKey.String(),
597+
WireguardAllowedIPs: []networkcfg.Prefix{{Prefix: netip.MustParsePrefix("10.0.0.0/24")}},
598+
},
599+
}
600+
601+
ctr, err := container.New(wc1)
602+
suite.Require().NoError(err)
603+
604+
cfg := config.NewMachineConfig(ctr)
605+
suite.Create(cfg)
606+
607+
suite.assertLinks(
608+
[]string{
609+
"configuration/wg0",
610+
}, func(r *network.LinkSpec, asrt *assert.Assertions) {
611+
asrt.Equal(network.ConfigMachineConfiguration, r.TypedSpec().ConfigLayer)
612+
613+
asrt.True(r.TypedSpec().Up)
614+
asrt.True(r.TypedSpec().Logical)
615+
asrt.Equal(nethelpers.LinkNone, r.TypedSpec().Type)
616+
asrt.Equal(network.LinkKindWireguard, r.TypedSpec().Kind)
617+
asrt.Equal(
618+
network.WireguardSpec{
619+
PrivateKey: privKey.String(),
620+
ListenPort: 12345,
621+
FirewallMark: 0,
622+
Peers: []network.WireguardPeer{
623+
{
624+
PublicKey: peerKey.PublicKey().String(),
625+
PresharedKey: pskKey.String(),
626+
AllowedIPs: []netip.Prefix{
627+
netip.MustParsePrefix("10.0.0.0/24"),
628+
},
629+
},
630+
},
631+
},
632+
r.TypedSpec().Wireguard,
633+
)
634+
},
635+
)
636+
}
637+
571638
func (suite *LinkConfigSuite) TestDefaultUp() {
572639
suite.Require().NoError(
573640
suite.Runtime().RegisterController(

internal/integration/api/network-config.go

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"github.com/siderolabs/gen/xslices"
2323
"github.com/siderolabs/go-pointer"
2424
"github.com/stretchr/testify/assert"
25+
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
2526

2627
"github.com/siderolabs/talos/internal/integration/base"
2728
"github.com/siderolabs/talos/pkg/machinery/cel"
@@ -581,6 +582,57 @@ func (suite *NetworkConfigSuite) TestBridgeConfig() {
581582
rtestutils.AssertNoResource[*networkres.LinkStatus](nodeCtx, suite.T(), suite.Client.COSI, bridgeName)
582583
}
583584

585+
// TestWireguardConfig tests creation of Wireguard interfaces.
586+
func (suite *NetworkConfigSuite) TestWireguardConfig() {
587+
if suite.Cluster == nil {
588+
suite.T().Skip("skipping if cluster is not qemu/docker")
589+
}
590+
591+
node := suite.RandomDiscoveredNodeInternalIP(machine.TypeWorker)
592+
nodeCtx := client.WithNode(suite.ctx, node)
593+
594+
suite.T().Logf("testing on node %q", node)
595+
596+
wgName := "wg." + strconv.Itoa(rand.IntN(10000))
597+
598+
privateKey, err := wgtypes.GeneratePrivateKey()
599+
suite.Require().NoError(err)
600+
601+
peerKey, err := wgtypes.GeneratePrivateKey()
602+
suite.Require().NoError(err)
603+
604+
wg := network.NewWireguardConfigV1Alpha1(wgName)
605+
wg.WireguardPrivateKey = privateKey.String()
606+
wg.WireguardListenPort = 3042
607+
wg.WireguardPeers = []network.WireguardPeer{
608+
{
609+
WireguardPublicKey: peerKey.PublicKey().String(),
610+
WireguardAllowedIPs: []network.Prefix{
611+
{
612+
Prefix: netip.MustParsePrefix("192.168.2.0/24"),
613+
},
614+
},
615+
},
616+
}
617+
wg.LinkUp = pointer.To(true)
618+
619+
suite.PatchMachineConfig(nodeCtx, wg)
620+
621+
rtestutils.AssertResource(nodeCtx, suite.T(), suite.Client.COSI, wgName,
622+
func(link *networkres.LinkStatus, asrt *assert.Assertions) {
623+
asrt.Equal("wireguard", link.TypedSpec().Kind)
624+
asrt.Equal(wg.WireguardListenPort, link.TypedSpec().Wireguard.ListenPort)
625+
asrt.Len(link.TypedSpec().Wireguard.Peers, 1)
626+
asrt.Equal(peerKey.PublicKey().String(), link.TypedSpec().Wireguard.Peers[0].PublicKey)
627+
asrt.Equal(privateKey.PublicKey().String(), link.TypedSpec().Wireguard.PublicKey)
628+
},
629+
)
630+
631+
suite.RemoveMachineConfigDocumentsByName(nodeCtx, network.WireguardKind, wgName)
632+
633+
rtestutils.AssertNoResource[*networkres.LinkStatus](nodeCtx, suite.T(), suite.Client.COSI, wgName)
634+
}
635+
584636
func init() {
585637
allSuites = append(allSuites, new(NetworkConfigSuite))
586638
}

internal/pkg/configuration/configuration.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
44

55
// Package configuration implements configuration generation.
6+
//
7+
//nolint:staticcheck // this whole package is planned to be deprecated
68
package configuration
79

810
import (

0 commit comments

Comments
 (0)