mirror of
https://github.com/kubenetworks/kubevpn.git
synced 2025-12-24 11:51:13 +08:00
feat: encrypt with tls 1.3 (#522)
This commit is contained in:
@@ -31,6 +31,8 @@ const (
|
||||
TLSCertKey = "tls_crt"
|
||||
// TLSPrivateKeyKey is the key for the private key field in a TLS secret.
|
||||
TLSPrivateKeyKey = "tls_key"
|
||||
// TLSServerName for tls config server name
|
||||
TLSServerName = "tls_server_name"
|
||||
|
||||
// container name
|
||||
ContainerSidecarEnvoyProxy = "envoy-proxy"
|
||||
@@ -170,10 +172,47 @@ var (
|
||||
)
|
||||
|
||||
var (
|
||||
// network layer ip needs 20 bytes
|
||||
// transport layer UDP header needs 8 bytes
|
||||
// UDP over TCP header needs 22 bytes
|
||||
DefaultMTU = 1500 - 20 - 8 - 21
|
||||
// DefaultMTU
|
||||
/**
|
||||
+--------------------------------------------------------------------+
|
||||
| Original IP Packet from TUN |
|
||||
+-------------------+------------------------------------------------+
|
||||
| IP Header (20B) | Payload (MTU size) |
|
||||
+-------------------+------------------------------------------------+
|
||||
|
||||
|
||||
After adding custom 2-byte header:
|
||||
+----+-------------------+-------------------------------------------+
|
||||
| LH | IP Header (20B) | Payload |
|
||||
+----+-------------------+-------------------------------------------+
|
||||
| 2B | 20B | 1453 - 20 = 1433B |
|
||||
+----+-------------------+-------------------------------------------+
|
||||
|
||||
TLS 1.3 Record Structure Breakdown:
|
||||
+---------------------+--------------------------+-------------------+
|
||||
| TLS Header (5B) | Encrypted Data (N) | Auth Tag (16B) |
|
||||
+---------------------+--------------------------+-------------------+
|
||||
| Content Type (1) | ↑ | AEAD Authentication
|
||||
| Version (2) | Encrypted Payload | (e.g. AES-GCM) |
|
||||
| Length (2) | (Original Data + LH2) | |
|
||||
+---------------------+--------------------------+-------------------+
|
||||
|←------- 5B --------→|←---- Length Field ------→|←----- 16B -------→|
|
||||
|
||||
|
||||
Final Ethernet Frame:
|
||||
+--------+----------------+----------------+-----------------------+--------+
|
||||
| EthHdr | IP Header | TCP Header | TLS Components |
|
||||
| (14B) | (20B) | (20B) +---------+-------------+--------+
|
||||
| | | | Hdr(5B) | Data+LH2 | Tag(16)|
|
||||
+--------+----------------+----------------+---------+-------------+--------+
|
||||
|←------------------- Total 1500B Ethernet Frame --------------------------→|
|
||||
|
||||
ipv4: 20
|
||||
ipv6: 40
|
||||
|
||||
mtu: 1417
|
||||
*/
|
||||
DefaultMTU = 1500 - max(20, 40) - 20 - 5 - 2 - 16
|
||||
)
|
||||
|
||||
var (
|
||||
|
||||
@@ -70,7 +70,7 @@ func (c *Forwarder) getConn(ctx context.Context) (net.Conn, error) {
|
||||
if c.IsEmpty() {
|
||||
return nil, ErrorEmptyForwarder
|
||||
}
|
||||
return c.Node().Client.Dial(ctx, c.resolve(c.Node().Addr))
|
||||
return c.Node().Client.Dial(ctx, c.Node().Addr)
|
||||
}
|
||||
|
||||
type Handler interface {
|
||||
|
||||
@@ -2,6 +2,8 @@ package core
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"net"
|
||||
"sync"
|
||||
|
||||
@@ -65,9 +67,19 @@ func GvisorTCPListener(addr string) (net.Listener, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ln, err := net.ListenTCP("tcp", laddr)
|
||||
listener, err := net.ListenTCP("tcp", laddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &tcpKeepAliveListener{TCPListener: ln}, nil
|
||||
serverConfig, err := util.GetTlsServerConfig(nil)
|
||||
if err != nil {
|
||||
if errors.Is(err, util.ErrNoTLSConfig) {
|
||||
plog.G(context.Background()).Warn("tls config not found in config, use raw tcp mode")
|
||||
return &tcpKeepAliveListener{TCPListener: listener}, nil
|
||||
}
|
||||
plog.G(context.Background()).Errorf("failed to get tls server config: %v", err)
|
||||
_ = listener.Close()
|
||||
return nil, err
|
||||
}
|
||||
return tls.NewListener(&tcpKeepAliveListener{TCPListener: listener}, serverConfig), nil
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ func (r *Route) ParseForwarder() (*Forwarder, error) {
|
||||
}
|
||||
forwarder.Client = &Client{
|
||||
Connector: NewUDPOverTCPConnector(),
|
||||
Transporter: TCPTransporter(),
|
||||
Transporter: TCPTransporter(nil),
|
||||
}
|
||||
return NewForwarder(r.Retries, forwarder), nil
|
||||
}
|
||||
|
||||
@@ -2,20 +2,44 @@ package core
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"net"
|
||||
|
||||
"github.com/wencaiwulue/kubevpn/v2/pkg/config"
|
||||
plog "github.com/wencaiwulue/kubevpn/v2/pkg/log"
|
||||
"github.com/wencaiwulue/kubevpn/v2/pkg/util"
|
||||
)
|
||||
|
||||
type tcpTransporter struct{}
|
||||
type tcpTransporter struct {
|
||||
tlsConfig *tls.Config
|
||||
}
|
||||
|
||||
func TCPTransporter() Transporter {
|
||||
return &tcpTransporter{}
|
||||
func TCPTransporter(tlsInfo map[string][]byte) Transporter {
|
||||
tlsConfig, err := util.GetTlsClientConfig(tlsInfo)
|
||||
if err != nil {
|
||||
if errors.Is(err, util.ErrNoTLSConfig) {
|
||||
plog.G(context.Background()).Warn("tls config not found in config, use raw tcp mode")
|
||||
return &tcpTransporter{}
|
||||
}
|
||||
plog.G(context.Background()).Errorf("failed to get tls client config: %v", err)
|
||||
return &tcpTransporter{}
|
||||
}
|
||||
return &tcpTransporter{tlsConfig: tlsConfig}
|
||||
}
|
||||
|
||||
func (tr *tcpTransporter) Dial(ctx context.Context, addr string) (net.Conn, error) {
|
||||
dialer := &net.Dialer{Timeout: config.DialTimeout}
|
||||
return dialer.DialContext(ctx, "tcp", addr)
|
||||
conn, err := dialer.DialContext(ctx, "tcp", addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if tr.tlsConfig == nil {
|
||||
plog.G(ctx).Debugf("tls config not found in config, use raw tcp mode")
|
||||
return conn, nil
|
||||
}
|
||||
plog.G(ctx).Debugf("use tls mode")
|
||||
return tls.Client(conn, tr.tlsConfig), nil
|
||||
}
|
||||
|
||||
func TCPListener(addr string) (net.Listener, error) {
|
||||
@@ -23,11 +47,20 @@ func TCPListener(addr string) (net.Listener, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ln, err := net.ListenTCP("tcp", laddr)
|
||||
listener, err := net.ListenTCP("tcp", laddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &tcpKeepAliveListener{TCPListener: ln}, nil
|
||||
serverConfig, err := util.GetTlsServerConfig(nil)
|
||||
if err != nil {
|
||||
if errors.Is(err, util.ErrNoTLSConfig) {
|
||||
plog.G(context.Background()).Warn("tls config not found in config, use raw tcp mode")
|
||||
return &tcpKeepAliveListener{TCPListener: listener}, nil
|
||||
}
|
||||
plog.G(context.Background()).Errorf("failed to get tls server config: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
return tls.NewListener(&tcpKeepAliveListener{TCPListener: listener}, serverConfig), nil
|
||||
}
|
||||
|
||||
type tcpKeepAliveListener struct {
|
||||
|
||||
@@ -21,6 +21,7 @@ import (
|
||||
"github.com/pkg/errors"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"google.golang.org/grpc/metadata"
|
||||
admissionv1 "k8s.io/api/admissionregistration/v1"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
apinetworkingv1 "k8s.io/api/networking/v1"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
@@ -155,6 +156,11 @@ func (c *ConnectOptions) CreateRemoteInboundPod(ctx context.Context, namespace s
|
||||
c.proxyWorkloads = make(ProxyList, 0)
|
||||
}
|
||||
|
||||
tlsSecret, err := c.clientset.CoreV1().Secrets(c.Namespace).Get(ctx, config.ConfigMapPodTrafficManager, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, workload := range workloads {
|
||||
plog.G(ctx).Infof("Injecting inbound sidecar for %s in namespace %s", workload, namespace)
|
||||
configInfo := util.PodRouteConfig{
|
||||
@@ -175,11 +181,11 @@ func (c *ConnectOptions) CreateRemoteInboundPod(ctx context.Context, namespace s
|
||||
// https://kubernetes.io/docs/concepts/workloads/pods/ephemeral-containers/
|
||||
// means mesh mode
|
||||
if c.Engine == config.EngineGvisor {
|
||||
err = inject.InjectEnvoySidecar(ctx, c.factory, c.clientset, c.Namespace, object, headers, portMap)
|
||||
err = inject.InjectEnvoySidecar(ctx, c.factory, c.clientset, c.Namespace, object, headers, portMap, tlsSecret)
|
||||
} else if len(headers) != 0 || len(portMap) != 0 {
|
||||
err = inject.InjectVPNAndEnvoySidecar(ctx, c.factory, c.clientset.CoreV1().ConfigMaps(c.Namespace), c.Namespace, object, configInfo, headers, portMap)
|
||||
err = inject.InjectVPNAndEnvoySidecar(ctx, c.factory, c.clientset.CoreV1().ConfigMaps(c.Namespace), c.Namespace, object, configInfo, headers, portMap, tlsSecret)
|
||||
} else {
|
||||
err = inject.InjectVPNSidecar(ctx, c.factory, c.Namespace, object, configInfo)
|
||||
err = inject.InjectVPNSidecar(ctx, c.factory, c.Namespace, object, configInfo, tlsSecret)
|
||||
}
|
||||
if err != nil {
|
||||
plog.G(ctx).Errorf("Injecting inbound sidecar for %s in namespace %s failed: %s", workload, namespace, err.Error())
|
||||
@@ -370,6 +376,11 @@ func (c *ConnectOptions) portForward(ctx context.Context, portPair []string) err
|
||||
func (c *ConnectOptions) startLocalTunServer(ctx context.Context, forwardAddress string, lite bool) (err error) {
|
||||
plog.G(ctx).Debugf("IPv4: %s, IPv6: %s", c.localTunIPv4.IP.String(), c.localTunIPv6.IP.String())
|
||||
|
||||
tlsSecret, err := c.clientset.CoreV1().Secrets(c.Namespace).Get(ctx, config.ConfigMapPodTrafficManager, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var cidrList []*net.IPNet
|
||||
if !lite {
|
||||
cidrList = append(cidrList, config.CIDR, config.CIDR6)
|
||||
@@ -423,7 +434,7 @@ func (c *ConnectOptions) startLocalTunServer(ctx context.Context, forwardAddress
|
||||
}
|
||||
forward.Client = &core.Client{
|
||||
Connector: core.NewUDPOverTCPConnector(),
|
||||
Transporter: core.TCPTransporter(),
|
||||
Transporter: core.TCPTransporter(tlsSecret.Data),
|
||||
}
|
||||
forwarder := core.NewForwarder(5, forward)
|
||||
|
||||
@@ -957,8 +968,14 @@ func (c *ConnectOptions) upgradeDeploy(ctx context.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
plog.G(ctx).Infof("Set image %s --> %s...", serverImg, clientImg)
|
||||
// 1) update secret
|
||||
err = upgradeSecretSpec(ctx, c.factory, c.Namespace)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 2) update deploy
|
||||
plog.G(ctx).Infof("Set image %s --> %s...", serverImg, clientImg)
|
||||
err = upgradeDeploySpec(ctx, c.factory, c.Namespace, deploy.Name, c.Engine == config.EngineGvisor)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -1051,6 +1068,49 @@ func upgradeDeploySpec(ctx context.Context, f cmdutil.Factory, ns, name string,
|
||||
return nil
|
||||
}
|
||||
|
||||
func upgradeSecretSpec(ctx context.Context, f cmdutil.Factory, ns string) error {
|
||||
crt, key, host, err := util.GenTLSCert(ctx, ns)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
secret := genSecret(ns, crt, key, host)
|
||||
|
||||
clientset, err := f.KubernetesClientSet()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
currentSecret, err := clientset.CoreV1().Secrets(ns).Get(ctx, secret.Name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// already have three keys
|
||||
if currentSecret.Data[config.TLSServerName] != nil &&
|
||||
currentSecret.Data[config.TLSPrivateKeyKey] != nil &&
|
||||
currentSecret.Data[config.TLSCertKey] != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
_, err = clientset.CoreV1().Secrets(ns).Update(ctx, secret, metav1.UpdateOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
mutatingWebhookConfig := genMutatingWebhookConfiguration(ns, crt)
|
||||
var current *admissionv1.MutatingWebhookConfiguration
|
||||
current, err = clientset.AdmissionregistrationV1().MutatingWebhookConfigurations().Get(ctx, mutatingWebhookConfig.Name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
mutatingWebhookConfig.ResourceVersion = current.ResourceVersion
|
||||
_, err = clientset.AdmissionregistrationV1().MutatingWebhookConfigurations().Update(ctx, mutatingWebhookConfig, metav1.UpdateOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func restartDeploy(ctx context.Context, f cmdutil.Factory, clientset *kubernetes.Clientset, ns, name string) error {
|
||||
label := fields.OneTermEqualSelector("app", config.ConfigMapPodTrafficManager).String()
|
||||
list, err := util.GetRunningPodList(ctx, clientset, ns, label)
|
||||
|
||||
@@ -18,7 +18,6 @@ import (
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
corev1 "k8s.io/client-go/kubernetes/typed/core/v1"
|
||||
"k8s.io/client-go/util/cert"
|
||||
"k8s.io/kubectl/pkg/polymorphichelpers"
|
||||
"k8s.io/kubectl/pkg/util/podutils"
|
||||
"k8s.io/utils/pointer"
|
||||
@@ -114,17 +113,14 @@ func createOutboundPod(ctx context.Context, clientset *kubernetes.Clientset, nam
|
||||
return err
|
||||
}
|
||||
|
||||
domain := util.GetTlsDomain(namespace)
|
||||
var crt, key []byte
|
||||
crt, key, err = cert.GenerateSelfSignedCertKey(domain, nil, nil)
|
||||
crt, key, host, err := util.GenTLSCert(ctx, namespace)
|
||||
if err != nil {
|
||||
plog.G(ctx).Errorf("Generate self signed cert and key error: %s", err.Error())
|
||||
return err
|
||||
}
|
||||
// reason why not use v1.SecretTypeTls is because it needs key called tls.crt and tls.key, but tls.key can not as env variable
|
||||
// ➜ ~ export tls.key=a
|
||||
//export: not valid in this context: tls.key
|
||||
secret := genSecret(namespace, crt, key)
|
||||
secret := genSecret(namespace, crt, key, host)
|
||||
_, err = clientset.CoreV1().Secrets(namespace).Create(ctx, secret, metav1.CreateOptions{})
|
||||
if err != nil {
|
||||
plog.G(ctx).Errorf("Creating secret error: %s", err.Error())
|
||||
@@ -282,7 +278,7 @@ func genService(namespace string, udp8422 string, tcp10800 string, tcp9002 strin
|
||||
}
|
||||
}
|
||||
|
||||
func genSecret(namespace string, crt []byte, key []byte) *v1.Secret {
|
||||
func genSecret(namespace string, crt []byte, key []byte, host []byte) *v1.Secret {
|
||||
secret := &v1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: config.ConfigMapPodTrafficManager,
|
||||
@@ -291,6 +287,7 @@ func genSecret(namespace string, crt []byte, key []byte) *v1.Secret {
|
||||
Data: map[string][]byte{
|
||||
config.TLSCertKey: crt,
|
||||
config.TLSPrivateKeyKey: key,
|
||||
config.TLSServerName: host,
|
||||
},
|
||||
Type: v1.SecretTypeOpaque,
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ func RemoveContainers(spec *v1.PodTemplateSpec) {
|
||||
}
|
||||
|
||||
// AddMeshContainer todo envoy support ipv6
|
||||
func AddMeshContainer(spec *v1.PodTemplateSpec, ns, nodeId string, c util.PodRouteConfig, ipv6 bool, connectNamespace string) {
|
||||
func AddMeshContainer(spec *v1.PodTemplateSpec, ns, nodeId string, c util.PodRouteConfig, ipv6 bool, connectNamespace string, secret *v1.Secret) {
|
||||
// remove envoy proxy containers if already exist
|
||||
RemoveContainers(spec)
|
||||
|
||||
@@ -98,6 +98,18 @@ kubevpn server -l "tun:/localhost:8422?net=${TunIPv4}&net6=${TunIPv6}&route=${CI
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: config.TLSServerName,
|
||||
Value: string(secret.Data[config.TLSServerName]),
|
||||
},
|
||||
{
|
||||
Name: config.TLSCertKey,
|
||||
Value: string(secret.Data[config.TLSCertKey]),
|
||||
},
|
||||
{
|
||||
Name: config.TLSPrivateKeyKey,
|
||||
Value: string(secret.Data[config.TLSPrivateKeyKey]),
|
||||
},
|
||||
},
|
||||
Resources: v1.ResourceRequirements{
|
||||
Requests: map[v1.ResourceName]resource.Quantity{
|
||||
@@ -159,7 +171,7 @@ kubevpn server -l "tun:/localhost:8422?net=${TunIPv4}&net6=${TunIPv6}&route=${CI
|
||||
})
|
||||
}
|
||||
|
||||
func AddEnvoyContainer(spec *v1.PodTemplateSpec, ns, nodeId string, ipv6 bool, connectNamespace string) {
|
||||
func AddEnvoyContainer(spec *v1.PodTemplateSpec, ns, nodeId string, ipv6 bool, connectNamespace string, secret *v1.Secret) {
|
||||
// remove envoy proxy containers if already exist
|
||||
RemoveContainers(spec)
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ func RemoveContainer(spec *corev1.PodSpec) {
|
||||
}
|
||||
}
|
||||
|
||||
func AddContainer(spec *corev1.PodSpec, c util.PodRouteConfig, connectNamespace string) {
|
||||
func AddContainer(spec *corev1.PodSpec, c util.PodRouteConfig, connectNamespace string, secret *corev1.Secret) {
|
||||
// remove vpn container if already exist
|
||||
RemoveContainer(spec)
|
||||
spec.Containers = append(spec.Containers, corev1.Container{
|
||||
@@ -71,6 +71,18 @@ func AddContainer(spec *corev1.PodSpec, c util.PodRouteConfig, connectNamespace
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: config.TLSServerName,
|
||||
Value: string(secret.Data[config.TLSServerName]),
|
||||
},
|
||||
{
|
||||
Name: config.TLSCertKey,
|
||||
Value: string(secret.Data[config.TLSCertKey]),
|
||||
},
|
||||
{
|
||||
Name: config.TLSPrivateKeyKey,
|
||||
Value: string(secret.Data[config.TLSPrivateKeyKey]),
|
||||
},
|
||||
},
|
||||
Command: []string{"/bin/sh", "-c"},
|
||||
// https://www.netfilter.org/documentation/HOWTO/NAT-HOWTO-6.html#ss6.2
|
||||
|
||||
@@ -28,7 +28,7 @@ import (
|
||||
|
||||
// InjectEnvoySidecar patch a sidecar, using iptables to do port-forward let this pod decide should go to 233.254.254.100 or request to 127.0.0.1
|
||||
// https://istio.io/latest/docs/ops/deployment/requirements/#ports-used-by-istio
|
||||
func InjectEnvoySidecar(ctx context.Context, f cmdutil.Factory, clientset *kubernetes.Clientset, connectNamespace string, object *runtimeresource.Info, headers map[string]string, portMap []string) (err error) {
|
||||
func InjectEnvoySidecar(ctx context.Context, f cmdutil.Factory, clientset *kubernetes.Clientset, connectNamespace string, object *runtimeresource.Info, headers map[string]string, portMap []string, secret *v1.Secret) (err error) {
|
||||
u := object.Object.(*unstructured.Unstructured)
|
||||
var templateSpec *v1.PodTemplateSpec
|
||||
var path []string
|
||||
@@ -66,7 +66,7 @@ func InjectEnvoySidecar(ctx context.Context, f cmdutil.Factory, clientset *kuber
|
||||
|
||||
enableIPv6, _ := util.DetectPodSupportIPv6(ctx, f, connectNamespace)
|
||||
// (1) add mesh container
|
||||
AddEnvoyContainer(templateSpec, object.Namespace, nodeID, enableIPv6, connectNamespace)
|
||||
AddEnvoyContainer(templateSpec, object.Namespace, nodeID, enableIPv6, connectNamespace, secret)
|
||||
helper := pkgresource.NewHelper(object.Client, object.Mapping)
|
||||
ps := []P{
|
||||
{
|
||||
|
||||
@@ -31,7 +31,7 @@ import (
|
||||
// https://istio.io/latest/docs/ops/deployment/requirements/#ports-used-by-istio
|
||||
|
||||
// InjectVPNAndEnvoySidecar patch a sidecar, using iptables to do port-forward let this pod decide should go to 233.254.254.100 or request to 127.0.0.1
|
||||
func InjectVPNAndEnvoySidecar(ctx context.Context, f cmdutil.Factory, mapInterface v12.ConfigMapInterface, connectNamespace string, object *runtimeresource.Info, c util.PodRouteConfig, headers map[string]string, portMaps []string) (err error) {
|
||||
func InjectVPNAndEnvoySidecar(ctx context.Context, f cmdutil.Factory, mapInterface v12.ConfigMapInterface, connectNamespace string, object *runtimeresource.Info, c util.PodRouteConfig, headers map[string]string, portMaps []string, secret *v1.Secret) (err error) {
|
||||
u := object.Object.(*unstructured.Unstructured)
|
||||
var templateSpec *v1.PodTemplateSpec
|
||||
var path []string
|
||||
@@ -90,7 +90,7 @@ func InjectVPNAndEnvoySidecar(ctx context.Context, f cmdutil.Factory, mapInterfa
|
||||
|
||||
enableIPv6, _ := util.DetectPodSupportIPv6(ctx, f, connectNamespace)
|
||||
// (1) add mesh container
|
||||
AddMeshContainer(templateSpec, object.Namespace, nodeID, c, enableIPv6, connectNamespace)
|
||||
AddMeshContainer(templateSpec, object.Namespace, nodeID, c, enableIPv6, connectNamespace, secret)
|
||||
helper := pkgresource.NewHelper(object.Client, object.Mapping)
|
||||
ps := []P{
|
||||
{
|
||||
|
||||
@@ -24,7 +24,7 @@ import (
|
||||
util2 "github.com/wencaiwulue/kubevpn/v2/pkg/util"
|
||||
)
|
||||
|
||||
func InjectVPNSidecar(ctx context.Context, f util.Factory, connectNamespace string, object *resource.Info, c util2.PodRouteConfig) error {
|
||||
func InjectVPNSidecar(ctx context.Context, f util.Factory, connectNamespace string, object *resource.Info, c util2.PodRouteConfig, secret *v1.Secret) error {
|
||||
u := object.Object.(*unstructured.Unstructured)
|
||||
|
||||
podTempSpec, path, err := util2.GetPodTemplateSpecPath(u)
|
||||
@@ -51,7 +51,7 @@ func InjectVPNSidecar(ctx context.Context, f util.Factory, connectNamespace stri
|
||||
return err
|
||||
}
|
||||
|
||||
AddContainer(&podTempSpec.Spec, c, connectNamespace)
|
||||
AddContainer(&podTempSpec.Spec, c, connectNamespace, secret)
|
||||
|
||||
workload := fmt.Sprintf("%s/%s", object.Mapping.Resource.Resource, object.Name)
|
||||
helper := resource.NewHelper(object.Client, object.Mapping)
|
||||
|
||||
112
pkg/util/tls.go
Normal file
112
pkg/util/tls.go
Normal file
@@ -0,0 +1,112 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"k8s.io/client-go/util/cert"
|
||||
|
||||
"github.com/wencaiwulue/kubevpn/v2/pkg/config"
|
||||
"github.com/wencaiwulue/kubevpn/v2/pkg/log"
|
||||
)
|
||||
|
||||
func GetTlsClientConfig(tlsSecret map[string][]byte) (*tls.Config, error) {
|
||||
crtBytes, keyBytes, serverName, err := getTls(tlsSecret)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pair, err := tls.X509KeyPair(crtBytes, keyBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
certPool := x509.NewCertPool()
|
||||
certPool.AppendCertsFromPEM(crtBytes)
|
||||
client := &tls.Config{
|
||||
RootCAs: certPool,
|
||||
Certificates: []tls.Certificate{pair},
|
||||
ServerName: string(serverName),
|
||||
MinVersion: tls.VersionTLS13,
|
||||
MaxVersion: tls.VersionTLS13,
|
||||
}
|
||||
return client, nil
|
||||
}
|
||||
|
||||
func GetTlsServerConfig(tlsInfo map[string][]byte) (*tls.Config, error) {
|
||||
crtBytes, keyBytes, serverName, err := getTls(tlsInfo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pair, err := tls.X509KeyPair(crtBytes, keyBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
client := &tls.Config{
|
||||
Certificates: []tls.Certificate{pair},
|
||||
ServerName: string(serverName),
|
||||
MinVersion: tls.VersionTLS13,
|
||||
MaxVersion: tls.VersionTLS13,
|
||||
}
|
||||
return client, nil
|
||||
}
|
||||
|
||||
var ErrNoTLSConfig = errors.New("no TLS configuration found")
|
||||
|
||||
func getTls(tlsSecret map[string][]byte) (crtBytes []byte, keyBytes []byte, serverName []byte, err error) {
|
||||
if tlsSecret != nil {
|
||||
crtBytes = tlsSecret[config.TLSCertKey]
|
||||
keyBytes = tlsSecret[config.TLSPrivateKeyKey]
|
||||
serverName = tlsSecret[config.TLSServerName]
|
||||
return
|
||||
}
|
||||
|
||||
if os.Getenv(config.TLSCertKey) == "" ||
|
||||
os.Getenv(config.TLSPrivateKeyKey) == "" ||
|
||||
os.Getenv(config.TLSServerName) == "" {
|
||||
return nil, nil, nil, ErrNoTLSConfig
|
||||
}
|
||||
|
||||
crtBytes = []byte(os.Getenv(config.TLSCertKey))
|
||||
keyBytes = []byte(os.Getenv(config.TLSPrivateKeyKey))
|
||||
serverName = []byte(os.Getenv(config.TLSServerName))
|
||||
return
|
||||
}
|
||||
|
||||
func GetTLSHost(ns string) string {
|
||||
return fmt.Sprintf("%s.%s", config.ConfigMapPodTrafficManager, ns)
|
||||
}
|
||||
|
||||
func GenTLSCert(ctx context.Context, ns string) ([]byte, []byte, []byte, error) {
|
||||
host := GetTLSHost(ns)
|
||||
alternateIPs := []net.IP{net.IPv4(127, 0, 0, 1)}
|
||||
alternateDNS := []string{"localhost"}
|
||||
// for Mutatingwebhookconfigurations will use domain: kubevpn-traffic-manager.xxx.svc
|
||||
alternateDNS = append(alternateDNS, fmt.Sprintf("%s.svc", host))
|
||||
crt, key, err := cert.GenerateSelfSignedCertKeyWithFixtures(host, alternateIPs, alternateDNS, ".")
|
||||
if err != nil {
|
||||
log.G(ctx).Errorf("Generate self signed cert and key error: %s", err.Error())
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
// ref --start vendor/k8s.io/client-go/util/cert/cert.go:113
|
||||
baseName := fmt.Sprintf("%s_%s_%s", host, strings.Join(ipsToStrings(alternateIPs), "-"), strings.Join(alternateDNS, "-"))
|
||||
_ = os.Remove(baseName + ".crt")
|
||||
_ = os.Remove(baseName + ".key")
|
||||
// ref --end
|
||||
return crt, key, []byte(host), nil
|
||||
}
|
||||
|
||||
func ipsToStrings(ips []net.IP) []string {
|
||||
ss := make([]string, 0, len(ips))
|
||||
for _, ip := range ips {
|
||||
ss = append(ss, ip.String())
|
||||
}
|
||||
return ss
|
||||
}
|
||||
68
pkg/util/tls_test.go
Normal file
68
pkg/util/tls_test.go
Normal file
@@ -0,0 +1,68 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"io"
|
||||
"net"
|
||||
"testing"
|
||||
|
||||
"github.com/wencaiwulue/kubevpn/v2/pkg/config"
|
||||
)
|
||||
|
||||
func TestTLS(t *testing.T) {
|
||||
ns := "kubevpn"
|
||||
crtBytes, keyBytes, alternateDNS, err := GenTLSCert(context.Background(), GetTLSHost(ns))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Setenv(config.TLSCertKey, string(crtBytes))
|
||||
t.Setenv(config.TLSPrivateKeyKey, string(keyBytes))
|
||||
t.Setenv(config.TLSServerName, string(alternateDNS))
|
||||
|
||||
listen, err := net.Listen("tcp", ":9090")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
tlsServerConfig, err := GetTlsServerConfig(nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
listener := tls.NewListener(listen, tlsServerConfig)
|
||||
go func() {
|
||||
for {
|
||||
conn, err := listener.Accept()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
go func(conn net.Conn) {
|
||||
bytes := make([]byte, 1024)
|
||||
all, err2 := conn.Read(bytes)
|
||||
if err2 != nil {
|
||||
t.Error(err2)
|
||||
return
|
||||
}
|
||||
defer conn.Close()
|
||||
t.Log(string(bytes[:all]))
|
||||
io.WriteString(conn, "hello client")
|
||||
}(conn)
|
||||
}
|
||||
}()
|
||||
dial, err := net.Dial("tcp", ":9090")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
clientConfig, err := GetTlsClientConfig(nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
client := tls.Client(dial, clientConfig)
|
||||
client.Write([]byte("hi server"))
|
||||
all, err := io.ReadAll(client)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Log(string(all))
|
||||
}
|
||||
@@ -33,7 +33,6 @@ import (
|
||||
cmdutil "k8s.io/kubectl/pkg/cmd/util"
|
||||
"k8s.io/kubectl/pkg/polymorphichelpers"
|
||||
|
||||
"github.com/wencaiwulue/kubevpn/v2/pkg/config"
|
||||
"github.com/wencaiwulue/kubevpn/v2/pkg/driver"
|
||||
plog "github.com/wencaiwulue/kubevpn/v2/pkg/log"
|
||||
)
|
||||
@@ -235,10 +234,6 @@ func CanI(clientset *kubernetes.Clientset, sa, ns string, resource *rbacv1.Polic
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func GetTlsDomain(ns string) string {
|
||||
return config.ConfigMapPodTrafficManager + "." + ns + "." + "svc"
|
||||
}
|
||||
|
||||
func RemoveLargerOverlappingCIDRs(cidrNets []*net.IPNet) []*net.IPNet {
|
||||
sort.Slice(cidrNets, func(i, j int) bool {
|
||||
onesI, _ := cidrNets[i].Mask.Size()
|
||||
|
||||
@@ -21,6 +21,7 @@ import (
|
||||
"github.com/wencaiwulue/kubevpn/v2/pkg/dhcp"
|
||||
"github.com/wencaiwulue/kubevpn/v2/pkg/dhcp/rpc"
|
||||
plog "github.com/wencaiwulue/kubevpn/v2/pkg/log"
|
||||
putil "github.com/wencaiwulue/kubevpn/v2/pkg/util"
|
||||
)
|
||||
|
||||
func Main(f util.Factory) error {
|
||||
@@ -38,10 +39,9 @@ func Main(f util.Factory) error {
|
||||
_, _ = w.Write([]byte("ok"))
|
||||
})
|
||||
|
||||
var pairs []tls.Certificate
|
||||
pairs, err = getSSLKeyPairs()
|
||||
tlsConfig, err := putil.GetTlsServerConfig(nil)
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("failed to load tls certificate: %v", err)
|
||||
}
|
||||
|
||||
grpcServer := grpc.NewServer()
|
||||
@@ -58,7 +58,7 @@ func Main(f util.Factory) error {
|
||||
// With downgrading-capable gRPC server, which can also handle HTTP.
|
||||
downgradingServer := &http.Server{
|
||||
Addr: fmt.Sprintf(":%d", 80),
|
||||
TLSConfig: &tls.Config{Certificates: pairs},
|
||||
TLSConfig: &tls.Config{Certificates: tlsConfig.Certificates},
|
||||
}
|
||||
defer downgradingServer.Close()
|
||||
var h2Server http2.Server
|
||||
@@ -73,20 +73,3 @@ func Main(f util.Factory) error {
|
||||
rpc.RegisterDHCPServer(grpcServer, dhcp.NewServer(clientset))
|
||||
return downgradingServer.ListenAndServeTLS("", "")
|
||||
}
|
||||
|
||||
func getSSLKeyPairs() ([]tls.Certificate, error) {
|
||||
cert, ok := os.LookupEnv(config.TLSCertKey)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("can not get %s from env", config.TLSCertKey)
|
||||
}
|
||||
var key string
|
||||
key, ok = os.LookupEnv(config.TLSPrivateKeyKey)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("can not get %s from env", config.TLSPrivateKeyKey)
|
||||
}
|
||||
pair, err := tls.X509KeyPair([]byte(cert), []byte(key))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to load certificate and key ,err: %v", err)
|
||||
}
|
||||
return []tls.Certificate{pair}, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user