diff --git a/NOTICE.md b/NOTICE.md index a658f263..4a2d8ed6 100644 --- a/NOTICE.md +++ b/NOTICE.md @@ -26,5 +26,6 @@ these files remains with the original authors. ### Robert Bosch GmbH - Becker Sebastian +- Hettwer Benjamin - Raskin Vadim - Trieflinger Sven diff --git a/charts/ephemeral/README.md b/charts/ephemeral/README.md index 1190dcd7..75d5deee 100644 --- a/charts/ephemeral/README.md +++ b/charts/ephemeral/README.md @@ -77,6 +77,8 @@ provided while installing the chart. For example, helm install --name my-release -f values.yaml ephemeral ``` + + ### Global Parameters | Parameter | Description | Default | @@ -94,6 +96,8 @@ helm install --name my-release -f values.yaml ephemeral | `discovery.service.annotations` | Annotations that should be attached to the Discovery service | `[]` | | `discovery.frontendUrl` | The external base URL of the VCP | \`\` | | `discovery.master.port` | The port of the master discovery service instance | \`\` | +| `discovery.tls.enabled` | Determines whether the service communicates over TLS or plaintext | `false` | +| `discovery.tls.secret` | The name of the k8s secret that holds the TLS keys and certificates | \`\` | | `discovery.isMaster` | Determines whether the service acts as master or slave | `true` | | `discovery.slave.connectTimeout` | Timeout to establish the connection to the upstream master Discovery Service | `60s` | | `discovery.stateTimeout` | Timeout in which the transition to the next state is expected | `60s` | @@ -101,12 +105,14 @@ helm install --name my-release -f values.yaml ephemeral ### Network Controller -| Parameter | Description | Default | -| ------------------------------------ | ---------------------------------------------------------------- | ------------------------------------------- | -| `networkController.image.registry` | Image registry used to pull the Network Controller Service image | `ghcr.io` | -| `networkController.image.repository` | Network Controller Image name | `carbynestack/ephemeral/network-controller` | -| `networkController.image.tag` | Network Controller Image tag | `latest` | -| `networkController.image.pullPolicy` | Network Controller Image pull policy | `IfNotPresent` | +| Parameter | Description | Default | +| ------------------------------------ | ------------------------------------------------------------------- | ------------------------------------------- | +| `networkController.image.registry` | Image registry used to pull the Network Controller Service image | `ghcr.io` | +| `networkController.image.repository` | Network Controller Image name | `carbynestack/ephemeral/network-controller` | +| `networkController.image.tag` | Network Controller Image tag | `latest` | +| `networkController.image.pullPolicy` | Network Controller Image pull policy | `IfNotPresent` | +| `networkController.tls.enabled` | Determines whether the service communicates over TLS or plaintext | `false` | +| `networkController.tls.secret` | The name of the k8s secret that holds the TLS keys and certificates | \`\` | ### Ephemeral Service @@ -133,6 +139,8 @@ helm install --name my-release -f values.yaml ephemeral | `ephemeral.discovery.port` | The port of the discovery service | `8080` | | `ephemeral.discovery.connectTimout` | Timeout to establish the connection to the discovery service | `60s` | | `ephemeral.frontendUrl` | The external base URL of the VCP | \`\` | +| `ephemeral.tls.enabled` | Determines whether the service communicates over TLS or plaintext | `false` | +| `ephemeral.tls.secret` | The name of the k8s secret that holds the TLS keys and certificates | \`\` | | `ephemeral.spdz.prime` | The prime used by SPDZ | \`\` | | `ephemeral.spdz.rInv` | The rInv used by SPDZ | \`\` | | `ephemeral.spdz.gfpMacKey` | The macKey for the prime protocol used by SPDZ | \`\` | @@ -144,3 +152,5 @@ helm install --name my-release -f values.yaml ephemeral | `ephemeral.networkEstablishTimeout` | Timeout to establish network connections | `1m` | | `ephemeral.player.stateTimeout` | Timeout in which the transition to the next state is expected | `60s` | | `ephemeral.player.computationTimeout` | Timeout in which the result of a game's mpc computation is expected | `60s` | + + diff --git a/charts/ephemeral/templates/discovery.yaml b/charts/ephemeral/templates/discovery.yaml index 3de65483..7eb99566 100644 --- a/charts/ephemeral/templates/discovery.yaml +++ b/charts/ephemeral/templates/discovery.yaml @@ -1,5 +1,5 @@ # -# Copyright (c) 2021-2023 - for information on the respective copyright owner +# Copyright (c) 2021-2024 - for information on the respective copyright owner # see the NOTICE file and/or the repository https://github.com/carbynestack/ephemeral. # # SPDX-License-Identifier: Apache-2.0 @@ -39,10 +39,16 @@ spec: volumeMounts: - name: config-volume mountPath: /etc/config + - name: tls-secret-volume + mountPath: /etc/tls + readOnly: true volumes: - name: config-volume configMap: name: discovery-config + - name: tls-secret-volume + secret: + secretName: {{ .Values.discovery.tls.secret }} serviceAccountName: discovery --- kind: Service @@ -60,7 +66,7 @@ spec: ports: - protocol: TCP port: 8080 - name: grpc-my + name: cs-tcp targetPort: 8080 --- apiVersion: v1 @@ -74,6 +80,7 @@ data: "frontendURL": "{{ .Values.discovery.frontendUrl }}", "masterHost": "{{ .Values.discovery.master.host }}", "masterPort": "{{ .Values.discovery.master.port }}", + "tlsEnabled": {{ .Values.discovery.tls.enabled }}, "slave": {{ if .Values.discovery.isMaster }}false{{ else }}true{{ end }}, "playerCount": {{ .Values.playerCount }}, "stateTimeout": "{{ .Values.discovery.stateTimeout }}", @@ -89,12 +96,25 @@ spec: selector: istio: ingressgateway # use istio default controller servers: +{{- if .Values.discovery.tls.enabled }} - port: number: 31400 - name: grpc-my + name: cs-grpc + protocol: HTTPS + tls: + mode: MUTUAL # enables mTLS on the Gateway + credentialName: {{ .Values.discovery.tls.secret }} # the name of the Secret that holds the TLS certs and CA certificate + hosts: + - "*" +{{- else }} + - port: + number: 31400 + name: cs-grpc protocol: GRPC hosts: - "*" +{{- end }} + --- apiVersion: networking.istio.io/v1beta1 kind: VirtualService diff --git a/charts/ephemeral/templates/ephemeral.yaml b/charts/ephemeral/templates/ephemeral.yaml index 6acd4f8a..5aecb491 100644 --- a/charts/ephemeral/templates/ephemeral.yaml +++ b/charts/ephemeral/templates/ephemeral.yaml @@ -1,5 +1,5 @@ # -# Copyright (c) 2021-2023 - for information on the respective copyright owner +# Copyright (c) 2021-2024 - for information on the respective copyright owner # see the NOTICE file and/or the repository https://github.com/carbynestack/ephemeral. # # SPDX-License-Identifier: Apache-2.0 @@ -38,6 +38,9 @@ spec: volumeMounts: - name: config-volume mountPath: /etc/config + - name: tls-secret-volume + mountPath: /etc/tls + readOnly: true {{- if or .Values.ephemeral.resources.requests.memory .Values.ephemeral.resources.requests.cpu .Values.ephemeral.resources.limits.memory .Values.ephemeral.resources.limits.cpu }} resources: {{- if or .Values.ephemeral.resources.requests.memory .Values.ephemeral.resources.requests.cpu }} @@ -63,6 +66,9 @@ spec: - name: config-volume configMap: name: {{ include "ephemeral.fullname" . }}-config1 + - name: tls-secret-volume + secret: + secretName: {{ .Values.ephemeral.tls.secret }} serviceAccountName: knative-serving --- apiVersion: v1 @@ -93,6 +99,7 @@ data: "tupleStock": {{ .Values.ephemeral.castor.tupleStock }} }, "frontendURL": "{{ .Values.ephemeral.frontendUrl }}", + "tlsEnabled": {{ .Values.ephemeral.tls.enabled }}, "discoveryConfig": { "host": "{{ .Values.ephemeral.discovery.host }}", "port": "{{ .Values.ephemeral.discovery.port }}", diff --git a/charts/ephemeral/templates/network-controller.yaml b/charts/ephemeral/templates/network-controller.yaml index e8a8ecaa..773cb181 100644 --- a/charts/ephemeral/templates/network-controller.yaml +++ b/charts/ephemeral/templates/network-controller.yaml @@ -1,5 +1,5 @@ # -# Copyright (c) 2021 - for information on the respective copyright owner +# Copyright (c) 2021-2024 - for information on the respective copyright owner # see the NOTICE file and/or the repository https://github.com/carbynestack/ephemeral. # # SPDX-License-Identifier: Apache-2.0 @@ -41,3 +41,23 @@ spec: fieldPath: metadata.name - name: OPERATOR_NAME value: "network-controller" + volumeMounts: + - name: config-volume + mountPath: /etc/config + volumes: + - name: config-volume + configMap: + name: {{ include "ephemeral.fullname" . }}-network-controller-config + serviceAccountName: network-controller + +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "ephemeral.fullname" . }}-network-controller-config +data: + config.json: |- + { + "tlsEnabled": {{ .Values.networkController.tls.enabled }}, + "tlsSecret": "{{ .Values.networkController.tls.secret }}" + } diff --git a/charts/ephemeral/values.yaml b/charts/ephemeral/values.yaml index e36aa185..b1881177 100644 --- a/charts/ephemeral/values.yaml +++ b/charts/ephemeral/values.yaml @@ -1,5 +1,5 @@ # -# Copyright (c) 2021-2023 - for information on the respective copyright owner +# Copyright (c) 2021-2024 - for information on the respective copyright owner # see the NOTICE file and/or the repository https://github.com/carbynestack/ephemeral. # # SPDX-License-Identifier: Apache-2.0 @@ -26,6 +26,9 @@ discovery: computationTimeout : "600s" slave: connectTimeout: "60s" + tls: + enabled: false + secret: ephemeral: service: @@ -56,6 +59,9 @@ ephemeral: path: "/" tupleStock: 1000 frontendUrl: + tls: + enabled: false + secret: discovery: host: discovery.default.svc.cluster.local port: 8080 @@ -81,3 +87,6 @@ networkController: tag: latest pullPolicy: "IfNotPresent" pullSecrets: [] + tls: + enabled: false + secret: diff --git a/cmd/discovery/main.go b/cmd/discovery/main.go index 2ed253a5..0c5c35d9 100644 --- a/cmd/discovery/main.go +++ b/cmd/discovery/main.go @@ -6,9 +6,12 @@ package main import ( "context" + "crypto/tls" "encoding/json" "errors" "fmt" + "time" + "github.com/carbynestack/ephemeral/pkg/discovery" c "github.com/carbynestack/ephemeral/pkg/discovery/transport/client" cl "github.com/carbynestack/ephemeral/pkg/discovery/transport/client" @@ -21,7 +24,6 @@ import ( "github.com/carbynestack/ephemeral/pkg/utils" mb "github.com/vardius/message-bus" "go.uber.org/zap" - "time" ) const ( @@ -32,6 +34,7 @@ const ( // DefaultPortRange is the range of ports used for MCP communication between the players. DefaultPortRange = "30000:30100" defaultConfigLocation = "/etc/config/config.json" + defaultTlsConfig = "/etc/tls" ) func main() { @@ -55,6 +58,16 @@ func main() { if err != nil { panic(err) } + + var tlsConfig *tls.Config + if config.TlsEnabled { + var err error + tlsConfig, err = utils.CreateTLSConfig(defaultTlsConfig) + if err != nil { + panic(err) + } + } + var upstreamConfig *DiscoveryClientTypedConfig if config.Slave { upstreamConfig = &DiscoveryClientTypedConfig{ @@ -63,7 +76,7 @@ func main() { ConnectTimeout: config.ConnectTimeout, } } - client, mode, err := NewClient(upstreamConfig, logger, errCh) + client, mode, err := NewClient(upstreamConfig, tlsConfig, logger, errCh) if err != nil { panic(err) } @@ -86,7 +99,7 @@ func main() { // NewClient returns a new client with parameters specific to the server mode. If upstreamClient is defined, the client // will be configured to forward incoming events to an upstream master server. With upstreamClient set to nil, the // service is considered to be the master service. -func NewClient(upstreamConfig *types.DiscoveryClientTypedConfig, logger *zap.SugaredLogger, errCh chan error) (*cl.Client, string, error) { +func NewClient(upstreamConfig *types.DiscoveryClientTypedConfig, tlsConfig *tls.Config, logger *zap.SugaredLogger, errCh chan error) (*cl.Client, string, error) { logger.Debug("Creating new discovery client") mode := ModeMaster client := &cl.Client{} @@ -105,6 +118,7 @@ func NewClient(upstreamConfig *types.DiscoveryClientTypedConfig, logger *zap.Sug ConnectTimeout: upstreamConfig.ConnectTimeout, Logger: logger, Context: context.Background(), + TlsConfig: tlsConfig, } client, err = c.NewClient(grpcClientConf) if err != nil { @@ -181,10 +195,12 @@ func ParseConfig(path string) (*DiscoveryTypedConfig, error) { if err != nil { return nil, errors.New(fmt.Sprintf("invalid connection timeout format: %v", err)) } + return &DiscoveryTypedConfig{ FrontendURL: conf.FrontendURL, MasterHost: conf.MasterHost, MasterPort: conf.MasterPort, + TlsEnabled: conf.TlsEnabled, Slave: conf.Slave, StateTimeout: stateTimeout, ComputationTimeout: computationTimeout, diff --git a/cmd/discovery/main_test.go b/cmd/discovery/main_test.go index 19b905b7..b20b1d6d 100644 --- a/cmd/discovery/main_test.go +++ b/cmd/discovery/main_test.go @@ -30,7 +30,7 @@ var _ = Describe("Main", func() { } logger := zap.NewNop().Sugar() errCh := make(chan error) - cl, mode, err := NewClient(conf, logger, errCh) + cl, mode, err := NewClient(conf, nil, logger, errCh) Expect(err).NotTo(HaveOccurred()) Expect(mode).To(Equal(ModeSlave)) Expect(cl).NotTo(BeNil()) diff --git a/cmd/ephemeral/main.go b/cmd/ephemeral/main.go index 603d8140..554ed01b 100644 --- a/cmd/ephemeral/main.go +++ b/cmd/ephemeral/main.go @@ -5,26 +5,30 @@ package main import ( + "crypto/tls" "encoding/json" "errors" + "github.com/carbynestack/ephemeral/pkg/amphora" "github.com/carbynestack/ephemeral/pkg/castor" . "github.com/carbynestack/ephemeral/pkg/ephemeral" l "github.com/carbynestack/ephemeral/pkg/logger" "github.com/carbynestack/ephemeral/pkg/utils" - . "github.com/carbynestack/ephemeral/pkg/types" "math/big" "net/http" "net/url" "time" + . "github.com/carbynestack/ephemeral/pkg/types" + "go.uber.org/zap" ) const ( - defaultConfig = "/etc/config/config.json" - defaultPort = "8080" + defaultConfig = "/etc/config/config.json" + defaultTlsConfig = "/etc/tls" + defaultPort = "8080" ) func main() { @@ -144,6 +148,15 @@ func InitTypedConfig(conf *SPDZEngineConfig) (*SPDZEngineTypedConfig, error) { return nil, err } + var tlsConfig *tls.Config + if conf.TlsEnabled { + var err error + tlsConfig, err = utils.CreateTLSConfig(defaultTlsConfig) + if err != nil { + return nil, err + } + } + return &SPDZEngineTypedConfig{ NetworkEstablishTimeout: networkEstablishTimeout, RetrySleep: retrySleep, @@ -168,5 +181,7 @@ func InitTypedConfig(conf *SPDZEngineConfig) (*SPDZEngineTypedConfig, error) { }, StateTimeout: stateTimeout, ComputationTimeout: computationTimeout, + TlsEnabled: conf.TlsEnabled, + TlsConfig: tlsConfig, }, nil } diff --git a/cmd/network-controller/main.go b/cmd/network-controller/main.go index 7828acc7..ea2ecbda 100644 --- a/cmd/network-controller/main.go +++ b/cmd/network-controller/main.go @@ -1,4 +1,4 @@ -// Copyright (c) 2021 - for information on the respective copyright owner +// Copyright (c) 2021-2024 - for information on the respective copyright owner // see the NOTICE file and/or the repository https://github.com/carbynestack/ephemeral. // // SPDX-License-Identifier: Apache-2.0 @@ -9,6 +9,7 @@ package main import ( "context" + "encoding/json" "flag" "fmt" "os" @@ -16,6 +17,8 @@ import ( "github.com/carbynestack/ephemeral/pkg/network-controller/apis" "github.com/carbynestack/ephemeral/pkg/network-controller/controller" + . "github.com/carbynestack/ephemeral/pkg/types" + "github.com/carbynestack/ephemeral/pkg/utils" "github.com/operator-framework/operator-sdk/pkg/k8sutil" "github.com/operator-framework/operator-sdk/pkg/leader" @@ -37,6 +40,8 @@ var ( ) var log = logf.Log.WithName("cmd") +const defaultConfigLocation = "/etc/config/config.json" + func printVersion() { log.Info(fmt.Sprintf("Go Version: %s", runtime.Version())) log.Info(fmt.Sprintf("Go OS/Arch: %s/%s", runtime.GOOS, runtime.GOARCH)) @@ -66,6 +71,16 @@ func main() { printVersion() + networkControllerConfig, err := ParseConfig(defaultConfigLocation) + if err != nil { + panic(err) + os.Exit(1) + } + log.Info(fmt.Sprintf("Starting with the config:\n%+v", networkControllerConfig)) + if err != nil { + panic(err) + } + namespace, err := k8sutil.GetWatchNamespace() if err != nil { log.Error(err, "Failed to get watch namespace") @@ -107,7 +122,7 @@ func main() { } // Setup all Controllers - if err := controller.AddToManager(mgr); err != nil { + if err := controller.AddToManager(mgr, networkControllerConfig); err != nil { log.Error(err, "") os.Exit(1) } @@ -126,3 +141,21 @@ func main() { os.Exit(1) } } + +// ParseConfig parses the configuration file of the discovery service. +func ParseConfig(path string) (*NetworkControllerTypedConfig, error) { + bytes, err := utils.ReadFile(path) + if err != nil { + panic(err) + } + var conf NetworkControllerConfig + err = json.Unmarshal(bytes, &conf) + if err != nil { + return nil, err + } + + return &NetworkControllerTypedConfig{ + TlsEnabled: conf.TlsEnabled, + TlsSecret: conf.TlsSecret, + }, nil +} diff --git a/pkg/discovery/transport/client/client.go b/pkg/discovery/transport/client/client.go index e7ebfe4f..ffb7d3cc 100644 --- a/pkg/discovery/transport/client/client.go +++ b/pkg/discovery/transport/client/client.go @@ -6,15 +6,18 @@ package client import ( "context" + "crypto/tls" "errors" - pb "github.com/carbynestack/ephemeral/pkg/discovery/transport/proto" "io" "time" + pb "github.com/carbynestack/ephemeral/pkg/discovery/transport/proto" + . "github.com/carbynestack/ephemeral/pkg/types" "go.uber.org/zap" "google.golang.org/grpc" + "google.golang.org/grpc/credentials" "google.golang.org/grpc/metadata" ) @@ -41,6 +44,8 @@ type TransportClientConfig struct { Logger *zap.SugaredLogger Context context.Context + + TlsConfig *tls.Config } // TransportConn is an interface for the underlying gRPC transport connection. @@ -101,7 +106,17 @@ func (c *Client) GetOut() chan *pb.Event { func (c *Client) Connect() (*grpc.ClientConn, error) { ctx, cancelConnect := context.WithTimeout(context.Background(), c.conf.ConnectTimeout) defer cancelConnect() - conn, err := grpc.DialContext(ctx, c.conf.Host+":"+c.conf.Port, grpc.WithBlock(), grpc.WithInsecure()) + + var opts []grpc.DialOption + if c.conf.TlsConfig != nil { + c.conf.Logger.Debug("Using TLS for gRPC connection") + creds := credentials.NewTLS(c.conf.TlsConfig) + opts = append(opts, grpc.WithTransportCredentials(creds)) + } else { + opts = append(opts, grpc.WithInsecure()) + } + + conn, err := grpc.DialContext(ctx, c.conf.Host+":"+c.conf.Port, append(opts, grpc.WithBlock())...) if err != nil { c.conf.Logger.Errorf("Error establishing a gRPC connection: %v", err) return nil, err diff --git a/pkg/ephemeral/io/feeder.go b/pkg/ephemeral/io/feeder.go index f30ee2b5..e78b0130 100644 --- a/pkg/ephemeral/io/feeder.go +++ b/pkg/ephemeral/io/feeder.go @@ -1,4 +1,4 @@ -// Copyright (c) 2021-2023 - for information on the respective copyright owner +// Copyright (c) 2021-2024 - for information on the respective copyright owner // see the NOTICE file and/or the repository https://github.com/carbynestack/ephemeral. // // SPDX-License-Identifier: Apache-2.0 @@ -7,10 +7,11 @@ package io import ( "encoding/json" "fmt" + "strings" + "github.com/carbynestack/ephemeral/pkg/amphora" "github.com/carbynestack/ephemeral/pkg/ephemeral/network" . "github.com/carbynestack/ephemeral/pkg/types" - "strings" "go.uber.org/zap" ) diff --git a/pkg/ephemeral/network/proxy.go b/pkg/ephemeral/network/proxy.go index 8d8fb5b6..b30405ed 100644 --- a/pkg/ephemeral/network/proxy.go +++ b/pkg/ephemeral/network/proxy.go @@ -1,4 +1,4 @@ -// Copyright (c) 2021-2023 - for information on the respective copyright owner +// Copyright (c) 2021-2024 - for information on the respective copyright owner // see the NOTICE file and/or the repository https://github.com/carbynestack/ephemeral. // // SPDX-License-Identifier: Apache-2.0 @@ -6,6 +6,7 @@ package network import ( "context" + "crypto/tls" "errors" "fmt" "net" @@ -35,6 +36,8 @@ func NewProxy(lg *zap.SugaredLogger, conf *SPDZEngineTypedConfig, checker Networ retrySleep: conf.RetrySleep, retryTimeout: conf.NetworkEstablishTimeout, tcpChecker: checker, + tlsEnabled: conf.TlsEnabled, + tlsConfig: conf.TlsConfig, } } @@ -50,6 +53,8 @@ type Proxy struct { // activeProxyIndicatorCh indicates that proxy was successfully started (see [tcpproxy.Proxy.Start]) if the channel // is closed. activeProxyIndicatorCh chan struct{} + tlsEnabled bool + tlsConfig *tls.Config } // Run start the tcpproxy, makes sure it has started by means of a ping. @@ -133,7 +138,31 @@ func (p *Proxy) addProxyEntry(config *ProxyConfig) *PingAwareTarget { // Start the TCP proxy to forward the requests from the base partner address to the target one. address := config.Host + ":" + config.Port p.logger.Infow(fmt.Sprintf("Adding TCP Proxy Entry for 'localhost:%s' -> '%s'", config.LocalPort, address), GameID, p.ctx.Act.GameID) - dialProxy := tcpproxy.DialProxy{Addr: address, DialTimeout: timeout} + + var dialProxy tcpproxy.DialProxy + + if p.tlsEnabled { + // Load TLS configuration + tlsConfig := p.tlsConfig + + dialProxy = tcpproxy.DialProxy{ + Addr: address, + DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) { + conn, err := tls.Dial(network, addr, tlsConfig) + if err != nil { + return nil, err + } + return conn, nil + }, + DialTimeout: timeout, + } + } else { + dialProxy = tcpproxy.DialProxy{ + Addr: address, + DialTimeout: timeout, + } + } + pat := &PingAwareTarget{ Next: &dialProxy, Logger: p.logger, diff --git a/pkg/ephemeral/network/tcpchecker.go b/pkg/ephemeral/network/tcpchecker.go index 0a30e29e..c74ecfb7 100644 --- a/pkg/ephemeral/network/tcpchecker.go +++ b/pkg/ephemeral/network/tcpchecker.go @@ -5,6 +5,7 @@ package network import ( + "crypto/tls" "fmt" "io" "net" @@ -32,6 +33,8 @@ type TCPCheckerConf struct { DialTimeout time.Duration RetryTimeout time.Duration Logger *zap.SugaredLogger + TlsEnabled bool + TlsConfig *tls.Config } // NewTCPChecker returns an instance of TCPChecker @@ -76,11 +79,29 @@ func (t *TCPChecker) tryToConnect(host, port string) bool { } } }() - conn, err = net.DialTimeout("tcp", host+":"+port, t.conf.DialTimeout) - if err != nil { - t.conf.Logger.Debugf("Error getting tcp connection %s", err.Error()) - return false + + if t.conf.TlsEnabled { + if t.conf.TlsConfig == nil { + t.conf.Logger.Errorf("TLS configuration is nil") + return false + } + dialer := &net.Dialer{Timeout: t.conf.DialTimeout} + t.conf.Logger.Debugf("Attempting to establish mTLS connection to %s:%s", host, port) + conn, err = tls.DialWithDialer(dialer, "tcp", host+":"+port, t.conf.TlsConfig) + if err != nil { + t.conf.Logger.Debugf("Error getting tcp connection using mTLS %s", err.Error()) + conn = nil // Ensure conn is nil to avoid defer panic + return false + } + } else { + t.conf.Logger.Debugf("Attempting to establish TCP connection to %s:%s", host, port) + conn, err = net.DialTimeout("tcp", host+":"+port, t.conf.DialTimeout) // Here is TCP connection established + if err != nil { + t.conf.Logger.Debugf("Error getting tcp connection %s", err.Error()) + return false + } } + err = conn.SetReadDeadline(time.Now().Add(t.conf.DialTimeout)) if err != nil { t.conf.Logger.Errorf("Error setting read deadline, %s\n", err.Error()) diff --git a/pkg/ephemeral/spdz.go b/pkg/ephemeral/spdz.go index 0949c34f..44a1e633 100644 --- a/pkg/ephemeral/spdz.go +++ b/pkg/ephemeral/spdz.go @@ -8,14 +8,6 @@ import ( "context" "errors" "fmt" - "github.com/carbynestack/ephemeral/pkg/castor" - d "github.com/carbynestack/ephemeral/pkg/discovery" - pb "github.com/carbynestack/ephemeral/pkg/discovery/transport/proto" - . "github.com/carbynestack/ephemeral/pkg/ephemeral/io" - "github.com/carbynestack/ephemeral/pkg/ephemeral/network" - . "github.com/carbynestack/ephemeral/pkg/types" - . "github.com/carbynestack/ephemeral/pkg/utils" - "github.com/google/uuid" "io/ioutil" "math/big" "os" @@ -25,6 +17,15 @@ import ( "sync" "time" + "github.com/carbynestack/ephemeral/pkg/castor" + d "github.com/carbynestack/ephemeral/pkg/discovery" + pb "github.com/carbynestack/ephemeral/pkg/discovery/transport/proto" + . "github.com/carbynestack/ephemeral/pkg/ephemeral/io" + "github.com/carbynestack/ephemeral/pkg/ephemeral/network" + . "github.com/carbynestack/ephemeral/pkg/types" + . "github.com/carbynestack/ephemeral/pkg/utils" + "github.com/google/uuid" + "go.uber.org/zap" ) @@ -136,6 +137,8 @@ func NewSPDZEngine(logger *zap.SugaredLogger, cmder Executor, config *SPDZEngine DialTimeout: tcpCheckerTimeout, RetryTimeout: config.NetworkEstablishTimeout, Logger: logger, + TlsEnabled: config.TlsEnabled, + TlsConfig: config.TlsConfig, } feeder := NewAmphoraFeeder(logger, config) checker := network.NewTCPChecker(c) diff --git a/pkg/network-controller/controller/controller.go b/pkg/network-controller/controller/controller.go index 19685fbb..a3c38524 100644 --- a/pkg/network-controller/controller/controller.go +++ b/pkg/network-controller/controller/controller.go @@ -1,4 +1,4 @@ -// Copyright (c) 2021 - for information on the respective copyright owner +// Copyright (c) 2021-2024 - for information on the respective copyright owner // see the NOTICE file and/or the repository https://github.com/carbynestack/ephemeral. // // SPDX-License-Identifier: Apache-2.0 @@ -8,16 +8,17 @@ package controller import ( + "github.com/carbynestack/ephemeral/pkg/types" "sigs.k8s.io/controller-runtime/pkg/manager" ) // AddToManagerFuncs is a list of functions to add all Controllers to the Manager -var AddToManagerFuncs []func(manager.Manager) error +var AddToManagerFuncs []func(manager.Manager, *types.NetworkControllerTypedConfig) error // AddToManager adds all Controllers to the Manager -func AddToManager(m manager.Manager) error { +func AddToManager(m manager.Manager, config *types.NetworkControllerTypedConfig) error { for _, f := range AddToManagerFuncs { - if err := f(m); err != nil { + if err := f(m, config); err != nil { return err } } diff --git a/pkg/network-controller/controller/network/network_controller.go b/pkg/network-controller/controller/network/network_controller.go index d09232fd..98e2dca0 100644 --- a/pkg/network-controller/controller/network/network_controller.go +++ b/pkg/network-controller/controller/network/network_controller.go @@ -1,4 +1,4 @@ -// Copyright (c) 2021 - for information on the respective copyright owner +// Copyright (c) 2021-2024 - for information on the respective copyright owner // see the NOTICE file and/or the repository https://github.com/carbynestack/ephemeral. // // SPDX-License-Identifier: Apache-2.0 @@ -10,14 +10,16 @@ package network import ( "context" "fmt" + mpcv1alpha1 "github.com/carbynestack/ephemeral/pkg/network-controller/apis/mpc/v1alpha1" + configtypes "github.com/carbynestack/ephemeral/pkg/types" clientset "github.com/knative/pkg/client/clientset/versioned" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" apierrs "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/types" + k8stypes "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/intstr" "knative.dev/pkg/apis/istio/v1alpha3" "sigs.k8s.io/controller-runtime/pkg/client" @@ -38,17 +40,17 @@ var podLabel = "mpc.podName" // Add creates a new Network Controller and adds it to the PortsState. The PortsState will set fields on the Controller // and Start it when the PortsState is Started. -func Add(mgr manager.Manager) error { - return add(mgr, newReconciler(mgr)) +func Add(mgr manager.Manager, config *configtypes.NetworkControllerTypedConfig) error { + return add(mgr, newReconciler(mgr, config)) } // newReconciler returns a new reconcile.Reconciler -func newReconciler(mgr manager.Manager) reconcile.Reconciler { +func newReconciler(mgr manager.Manager, config *configtypes.NetworkControllerTypedConfig) reconcile.Reconciler { c := mgr.GetConfig() cs := clientset.NewForConfigOrDie(c) - return &ReconcileNetwork{client: mgr.GetClient(), scheme: mgr.GetScheme(), sharedClientSet: cs} + return &ReconcileNetwork{client: mgr.GetClient(), scheme: mgr.GetScheme(), sharedClientSet: cs, config: config} } // add adds a new Controller to mgr with r as the reconcile.Reconciler @@ -100,6 +102,7 @@ type ReconcileNetwork struct { client client.Client scheme *runtime.Scheme sharedClientSet *clientset.Clientset + config *configtypes.NetworkControllerTypedConfig } // Reconcile reads that state of the cluster for a Network object and makes changes based on the state read @@ -131,7 +134,7 @@ func (r *ReconcileNetwork) Reconcile(request reconcile.Request) (reconcile.Resul podName := instance.Labels[podLabel] pod := &corev1.Pod{} - err = r.client.Get(context.TODO(), types.NamespacedName{Name: podName, Namespace: instance.Namespace}, pod) + err = r.client.Get(context.TODO(), k8stypes.NamespacedName{Name: podName, Namespace: instance.Namespace}, pod) if err != nil { reqLogger.Error(err, "not able to retrieve the Pod for the service") return reconcile.Result{}, err @@ -155,7 +158,7 @@ func (r *ReconcileNetwork) Reconcile(request reconcile.Request) (reconcile.Resul // Check if the Service already exists found := &corev1.Service{} - err = r.client.Get(context.TODO(), types.NamespacedName{Name: service.Name, Namespace: service.Namespace}, found) + err = r.client.Get(context.TODO(), k8stypes.NamespacedName{Name: service.Name, Namespace: service.Namespace}, found) if err != nil && errors.IsNotFound(err) { reqLogger.Info("Creating a new Service", "Service.Namespace", service.Namespace, "Service.Name", service.Name) err = r.client.Create(context.TODO(), service) @@ -167,9 +170,11 @@ func (r *ReconcileNetwork) Reconcile(request reconcile.Request) (reconcile.Resul return reconcile.Result{}, err } + log.Info(fmt.Sprintf("Reconcile with the config:\n%+v", r.config)) + // Check if the gateway already exist, create it otherwise. - gw := newGateway(instance, instance.Spec.Port) + gw := newGateway(instance, instance.Spec.Port, r.config) if err := controllerutil.SetControllerReference(instance, gw, r.scheme); err != nil { return reconcile.Result{}, err @@ -290,12 +295,18 @@ func newVirtualService(cr *mpcv1alpha1.Network, port int32, gateway string) *v1a }} } -func newGateway(cr *mpcv1alpha1.Network, port int32) *v1alpha3.Gateway { +func newGateway(cr *mpcv1alpha1.Network, port int32, config *configtypes.NetworkControllerTypedConfig) *v1alpha3.Gateway { gwlb := cr.Labels gwlb["mpc.gateway"] = "true" selectors := map[string]string{} selectors["istio"] = "ingressgateway" srv := newServer(cr.Name, int(port)) + if config.TlsEnabled { + srv.TLS = &v1alpha3.TLSOptions{ + Mode: v1alpha3.TLSModeMutual, // Use TLSModeMutual mode for mTLS + CredentialName: config.TlsSecret, // Name of the Kubernetes secret holding the certificate + } + } servers := []v1alpha3.Server{srv} return &v1alpha3.Gateway{ ObjectMeta: metav1.ObjectMeta{ diff --git a/pkg/types/types.go b/pkg/types/types.go index 03af0659..7c50b953 100644 --- a/pkg/types/types.go +++ b/pkg/types/types.go @@ -1,4 +1,4 @@ -// Copyright (c) 2021-2023 - for information on the respective copyright owner +// Copyright (c) 2021-2024 - for information on the respective copyright owner // see the NOTICE file and/or the repository https://github.com/carbynestack/ephemeral. // // SPDX-License-Identifier: Apache-2.0 @@ -6,11 +6,13 @@ package types import ( "context" + "crypto/tls" + "math/big" + "time" + "github.com/carbynestack/ephemeral/pkg/amphora" "github.com/carbynestack/ephemeral/pkg/castor" pb "github.com/carbynestack/ephemeral/pkg/discovery/transport/proto" - "math/big" - "time" mb "github.com/vardius/message-bus" "google.golang.org/grpc" @@ -34,6 +36,7 @@ type DiscoveryConfig struct { FrontendURL string `json:"frontendURL"` MasterHost string `json:"masterHost"` MasterPort string `json:"masterPort"` + TlsEnabled bool `json:"tlsEnabled"` Slave bool `json:"slave"` StateTimeout string `json:"stateTimeout"` ComputationTimeout string `json:"computationTimeout"` @@ -49,6 +52,7 @@ type DiscoveryTypedConfig struct { FrontendURL string MasterHost string MasterPort string + TlsEnabled bool Slave bool StateTimeout time.Duration ComputationTimeout time.Duration @@ -59,6 +63,18 @@ type DiscoveryTypedConfig struct { PlayerCount int } +// NetworkControllerConfig represents the config of the network-controller service. +type NetworkControllerConfig struct { + TlsEnabled bool `json:"tlsEnabled"` + TlsSecret string `json:"tlsSecret"` +} + +// NetworkControllerTypedConfig reflects NetworkControllerConfig, but it contains the real property types +type NetworkControllerTypedConfig struct { + TlsEnabled bool + TlsSecret string +} + // Activation is an object that is received as an input from the Ephemeral client. type Activation struct { AmphoraParams []string `json:"amphoraParams"` @@ -100,6 +116,7 @@ type SPDZEngineConfig struct { AmphoraConfig AmphoraConfig `json:"amphoraConfig"` CastorConfig CastorConfig `json:"castorConfig"` FrontendURL string `json:"frontendURL"` + TlsEnabled bool `json:"tlsEnabled"` PlayerID int32 `json:"playerID"` PlayerCount int32 `json:"playerCount"` MaxBulkSize int32 `json:"maxBulkSize"` @@ -164,4 +181,6 @@ type SPDZEngineTypedConfig struct { DiscoveryConfig DiscoveryClientTypedConfig StateTimeout time.Duration ComputationTimeout time.Duration + TlsEnabled bool + TlsConfig *tls.Config } diff --git a/pkg/utils/os.go b/pkg/utils/os.go index 547e367f..84a7988f 100644 --- a/pkg/utils/os.go +++ b/pkg/utils/os.go @@ -1,4 +1,4 @@ -// Copyright (c) 2021 - for information on the respective copyright owner +// Copyright (c) 2021-2024 - for information on the respective copyright owner // see the NOTICE file and/or the repository https://github.com/carbynestack/ephemeral. // // SPDX-License-Identifier: Apache-2.0 @@ -7,6 +7,8 @@ package utils import ( "bytes" "context" + "crypto/tls" + "crypto/x509" "errors" "io/ioutil" "os" @@ -90,3 +92,37 @@ func ReadFile(path string) ([]byte, error) { } return ioutil.ReadAll(file) } + +// CreateTLSConfig creates a tls.Config object for mTLS connections +func CreateTLSConfig(mountPath string) (*tls.Config, error) { + keyPath := filepath.Join(mountPath, "tls.key") + certPath := filepath.Join(mountPath, "tls.crt") + caCertPath := filepath.Join(mountPath, "cacert") + + // Load the client certificate and key + clientCert, err := tls.LoadX509KeyPair(certPath, keyPath) + if err != nil { + return nil, err + } + + // Read the CA certificate + caCertBytes, err := ioutil.ReadFile(caCertPath) + if err != nil { + return nil, err + } + + // Create a CertPool and add the CA certificate + caCertPool := x509.NewCertPool() + if !caCertPool.AppendCertsFromPEM(caCertBytes) { + return nil, err + } + + // Create and return the tls.Config object + tlsConfig := &tls.Config{ + Certificates: []tls.Certificate{clientCert}, + RootCAs: caCertPool, + InsecureSkipVerify: false, // Hostname verfication is enabled + } + + return tlsConfig, nil +}