Skip to content

Commit

Permalink
feat: implement SideroLink
Browse files Browse the repository at this point in the history
Fixes #615

This implements the SideroLink integration on Sidero side. This feature
requires Talos 0.14 from master as of right now.

SideroLink status is stored in `ServerBinding` resource for each server.

Signed-off-by: Andrey Smirnov <andrey.smirnov@talos-systems.com>
  • Loading branch information
smira committed Nov 25, 2021
1 parent adc73b6 commit ab29103
Show file tree
Hide file tree
Showing 26 changed files with 1,018 additions and 92 deletions.
5 changes: 5 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -150,9 +150,14 @@ linters:
- paralleltest
- thelper
- wrapcheck
- ireturn
- tagliatelle
- varnamelen
- gomoddirectives
# abandoned linters for which golangci shows the warning that the repo is archived by the owner
- interfacer
- maligned
- scopelint
disable-all: false
fast: false

Expand Down
11 changes: 10 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ ENV GOMODCACHE /.cache/mod
RUN --mount=type=cache,target=/.cache go install sigs.k8s.io/controller-tools/cmd/controller-gen@v0.6.2
RUN --mount=type=cache,target=/.cache go install k8s.io/code-generator/cmd/conversion-gen@v0.21.3
RUN --mount=type=cache,target=/.cache go install mvdan.cc/gofumpt/gofumports@v0.1.1
RUN curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | bash -s -- -b /toolchain/bin v1.38.0
RUN curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | bash -s -- -b /toolchain/bin v1.43.0
WORKDIR /src
COPY ./go.mod ./
COPY ./go.sum ./
Expand Down Expand Up @@ -136,6 +136,14 @@ ARG GO_LDFLAGS
RUN --mount=type=cache,target=/.cache GOOS=linux GOARCH=${TARGETARCH} go build ${GO_BUILDFLAGS} -ldflags "${GO_LDFLAGS} -X main.TalosRelease=${TALOS_RELEASE}" -o /manager ./app/sidero-controller-manager
RUN chmod +x /manager

FROM base AS build-siderolink-manager
ARG TALOS_RELEASE
ARG TARGETARCH
ARG GO_BUILDFLAGS
ARG GO_LDFLAGS
RUN --mount=type=cache,target=/.cache GOOS=linux GOARCH=${TARGETARCH} go build ${GO_BUILDFLAGS} -ldflags "${GO_LDFLAGS} -X main.TalosRelease=${TALOS_RELEASE}" -o /siderolink-manager ./app/sidero-controller-manager/cmd/siderolink-manager
RUN chmod +x /siderolink-manager

FROM base AS agent-build-amd64
ARG GO_BUILDFLAGS
ARG GO_LDFLAGS
Expand Down Expand Up @@ -185,6 +193,7 @@ COPY --from=initramfs-archive-arm64 /initramfs.xz /var/lib/sidero/env/agent-arm6
COPY --from=pkg-kernel-amd64 /boot/vmlinuz /var/lib/sidero/env/agent-amd64/vmlinuz
COPY --from=pkg-kernel-arm64 /boot/vmlinuz /var/lib/sidero/env/agent-arm64/vmlinuz
COPY --from=build-sidero-controller-manager /manager /manager
COPY --from=build-siderolink-manager /siderolink-manager /siderolink-manager

FROM sidero-controller-manager-image AS sidero-controller-manager
LABEL org.opencontainers.image.source https://github.com/talos-systems/sidero
Expand Down
12 changes: 12 additions & 0 deletions app/caps-controller-manager/api/v1alpha3/serverbinding_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,18 @@ const ServerBindingMetalMachineRefField = "spec.metalMachineRef.name"
type ServerBindingSpec struct {
ServerClassRef *corev1.ObjectReference `json:"serverClassRef,omitempty"`
MetalMachineRef corev1.ObjectReference `json:"metalMachineRef"`

// SideroLink describes state of the SideroLink tunnel.
// +optional
SideroLink SideroLinkSpec `json:"siderolink,omitempty"`
}

// SideroLinkSpec defines the state of SideroLink connection.
type SideroLinkSpec struct {
// NodeAddress is the tunnel address of the node.
NodeAddress string `json:"address"`
// NodePublicKey is the Wireguard public key of the node.
NodePublicKey string `json:"publicKey"`
}

// ServerBindingState defines the observed state of ServerBinding.
Expand Down
16 changes: 16 additions & 0 deletions app/caps-controller-manager/api/v1alpha3/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,20 @@ spec:
description: 'UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids'
type: string
type: object
siderolink:
description: SideroLink describes state of the SideroLink tunnel.
properties:
address:
description: NodeAddress is the tunnel address of the node.
type: string
publicKey:
description: NodePublicKey is the Wireguard public key of the
node.
type: string
required:
- address
- publicKey
type: object
required:
- metalMachineRef
type: object
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ func (r *ServerBindingReconciler) Reconcile(ctx context.Context, req ctrl.Reques

err = r.Get(ctx, req.NamespacedName, serverBinding)
if apierrors.IsNotFound(err) {
return r.reconcileTransition(logger, req)
return r.reconcileTransition(ctx, logger, req)
}

if err != nil {
Expand Down Expand Up @@ -138,9 +138,7 @@ func (r *ServerBindingReconciler) SetupWithManager(ctx context.Context, mgr ctrl
Complete(r)
}

func (r *ServerBindingReconciler) reconcileTransition(logger logr.Logger, req ctrl.Request) (_ ctrl.Result, err error) {
ctx := context.Background()

func (r *ServerBindingReconciler) reconcileTransition(ctx context.Context, logger logr.Logger, req ctrl.Request) (_ ctrl.Result, err error) {
logger.Info("reconciling missing serverbinding")

var metalMachineList infrav1.MetalMachineList
Expand Down
33 changes: 33 additions & 0 deletions app/sidero-controller-manager/cmd/siderolink-manager/kubernetes.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

package main

import (
"k8s.io/apimachinery/pkg/runtime"
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/rest"
ctrl "sigs.k8s.io/controller-runtime"
runtimeclient "sigs.k8s.io/controller-runtime/pkg/client"

sidero "github.com/talos-systems/sidero/app/caps-controller-manager/api/v1alpha3"
)

func getMetalClient() (runtimeclient.Client, *rest.Config, error) {
kubeconfig := ctrl.GetConfigOrDie()

scheme := runtime.NewScheme()

if err := clientgoscheme.AddToScheme(scheme); err != nil {
return nil, nil, err
}

if err := sidero.AddToScheme(scheme); err != nil {
return nil, nil, err
}

client, err := runtimeclient.New(kubeconfig, runtimeclient.Options{Scheme: scheme})

return client, kubeconfig, err
}
168 changes: 168 additions & 0 deletions app/sidero-controller-manager/cmd/siderolink-manager/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

package main

import (
"context"
"errors"
"flag"
"fmt"
"log"
"net"
"os"
"os/signal"
"syscall"

grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware"
grpc_zap "github.com/grpc-ecosystem/go-grpc-middleware/logging/zap"
grpc_recovery "github.com/grpc-ecosystem/go-grpc-middleware/recovery"
grpc_ctxtags "github.com/grpc-ecosystem/go-grpc-middleware/tags"
"go.uber.org/zap"
"golang.org/x/sync/errgroup"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"inet.af/netaddr"

pb "github.com/talos-systems/siderolink/api/siderolink"
"github.com/talos-systems/siderolink/pkg/wireguard"

"github.com/talos-systems/sidero/app/sidero-controller-manager/internal/siderolink"
"github.com/talos-systems/sidero/app/sidero-controller-manager/pkg/constants"
)

var (
wireguardEndpoint string
wireguardPort int
)

func main() {
flag.StringVar(&wireguardEndpoint, "wireguard-endpoint", "", "The endpoint (IP address) SideroLink can be reached at from the servers.")
flag.IntVar(&wireguardPort, "wireguard-port", 51821, "The TCP port SideroLink can be reached at from the servers.")

flag.Parse()

if wireguardEndpoint == "-" {
wireguardEndpoint = ""
}

if wireguardEndpoint == "" {
if endpoint, ok := os.LookupEnv("API_ENDPOINT"); ok {
wireguardEndpoint = endpoint
} else {
log.Fatal("no Wireguard endpoint found")
}
}

if err := run(); err != nil {
fmt.Fprintf(os.Stderr, "error: %s", err)
os.Exit(1)
}
}

func recoveryHandler(logger *zap.Logger) grpc_recovery.RecoveryHandlerFunc {
return func(p interface{}) error {
if logger != nil {
logger.Error("grpc panic", zap.Any("panic", p), zap.Stack("stack"))
}

return status.Errorf(codes.Internal, "%v", p)
}
}

func run() error {
logger, err := zap.NewProduction()
if err != nil {
return fmt.Errorf("failed to initialize logger: %w", err)
}

zap.ReplaceGlobals(logger)
zap.RedirectStdLog(logger)

metalclient, kubeconfig, err := getMetalClient()
if err != nil {
return fmt.Errorf("error building runtime client: %w", err)
}

ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
defer cancel()

eg, ctx := errgroup.WithContext(ctx)

lis, err := net.Listen("tcp", constants.SideroLinkInternalAPIEndpoint)
if err != nil {
return fmt.Errorf("error listening for gRPC API: %w", err)
}

cfg := siderolink.Config{
WireguardEndpoint: fmt.Sprintf("%s:%d", wireguardEndpoint, wireguardPort),
}

if err = cfg.LoadOrCreate(ctx, metalclient); err != nil {
return err
}

wireguardEndpoint, err := netaddr.ParseIPPort(cfg.WireguardEndpoint)
if err != nil {
return fmt.Errorf("invalid Wireguard endpoint: %w", err)
}

wgDevice, err := wireguard.NewDevice(cfg.ServerAddress, cfg.PrivateKey, wireguardEndpoint.Port())
if err != nil {
return fmt.Errorf("error initializing wgDevice: %w", err)
}

defer wgDevice.Close() //nolint:errcheck

grpc_zap.ReplaceGrpcLoggerV2(logger)

recoveryOpt := grpc_recovery.WithRecoveryHandler(recoveryHandler(logger))

serverOptions := []grpc.ServerOption{
grpc_middleware.WithUnaryServerChain(
grpc_ctxtags.UnaryServerInterceptor(),
grpc_zap.UnaryServerInterceptor(logger),
grpc_recovery.UnaryServerInterceptor(recoveryOpt),
),
grpc_middleware.WithStreamServerChain(
grpc_ctxtags.StreamServerInterceptor(),
grpc_zap.StreamServerInterceptor(logger),
grpc_recovery.StreamServerInterceptor(recoveryOpt),
),
}

srv := siderolink.NewServer(&cfg, metalclient)

peers := siderolink.NewPeerState(kubeconfig, logger)

s := grpc.NewServer(serverOptions...)
pb.RegisterProvisionServiceServer(s, srv)

eg.Go(func() error {
return wgDevice.Run(ctx, logger, peers)
})

eg.Go(func() error {
return peers.Run(ctx)
})

eg.Go(func() error {
return s.Serve(lis)
})

eg.Go(func() error {
<-ctx.Done()

s.Stop()

return nil
})

if err := eg.Wait(); err != nil && !errors.Is(err, grpc.ErrServerStopped) && errors.Is(err, context.Canceled) {
return err
}

return nil
}
50 changes: 50 additions & 0 deletions app/sidero-controller-manager/config/manager/manager.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,19 @@ spec:
---
apiVersion: v1
kind: Service
metadata:
name: siderolink
namespace: system
spec:
ports:
- port: ${SIDERO_CONTROLLER_MANAGER_SIDEROLINK_PORT:=51821}
targetPort: siderolink
protocol: UDP
selector:
control-plane: sidero-controller-manager
---
apiVersion: v1
kind: Service
metadata:
name: http
namespace: system
Expand Down Expand Up @@ -90,4 +103,41 @@ spec:
path: /healthz
port: http
initialDelaySeconds: 15
- command:
- /siderolink-manager
args:
- --wireguard-endpoint=${SIDERO_CONTROLLER_MANAGER_SIDEROLINK_ENDPOINT:=-}
- --wireguard-port=${SIDERO_CONTROLLER_MANAGER_SIDEROLINK_PORT:=51821}
image: controller:latest
imagePullPolicy: Always
name: siderolink
env:
- name: API_ENDPOINT
valueFrom:
fieldRef:
fieldPath: status.hostIP
ports:
- name: siderolink
containerPort: ${SIDERO_CONTROLLER_MANAGER_SIDEROLINK_PORT:=51821}
protocol: UDP
resources:
limits:
cpu: 500m
memory: 512Mi
requests:
cpu: 50m
memory: 128Mi
securityContext:
capabilities:
add:
- NET_ADMIN
privileged: false
volumeMounts:
- mountPath: /dev/net/tun
name: dev-tun
volumes:
- hostPath:
path: /dev/net/tun
type: CharDevice
name: dev-tun
terminationGracePeriodSeconds: 10

0 comments on commit ab29103

Please sign in to comment.