Tap multiple pods statically (#51)

* WIP

* Update tap.go, provider.go, and 2 more files...

* WIP

* WIP

* Solved routine hanging forever: Added missing flag when calling mizuagent.

* Iterate channel with range.

* Panic if har channel is nil or if websocket connection is nil.

* StartPassiveTapper returns read only channel.

* Solved program exiting immediately: Wait for interrupt signal instead of exiting.

* Solve connecting issue - Retry a few times.

* Use lib const instead of magic.

* Nicer error prints.

* Don't coninue piping message if there is an error.

* Comment.

* Dependency injection.

* no message

* Fixed comment.

* Print tapped addresses when they are updated.

* Print errors in cleanup if there are any.

Co-authored-by: RamiBerm <rami.berman@up9.com>
Co-authored-by: Roee Gadot <roee.gadot@up9.com>
This commit is contained in:
nimrod-up9
2021-05-20 12:22:23 +03:00
committed by GitHub
parent 1ddc7f2f6b
commit da24608bec
14 changed files with 461 additions and 162 deletions

View File

@@ -3,12 +3,15 @@ package kubernetes
import (
_ "bytes"
"context"
"encoding/json"
"errors"
"fmt"
apps "k8s.io/api/apps/v1"
core "k8s.io/api/core/v1"
rbac "k8s.io/api/rbac/v1"
k8serrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/apimachinery/pkg/watch"
"k8s.io/client-go/kubernetes"
_ "k8s.io/client-go/plugin/pkg/client/auth/azure"
@@ -20,7 +23,7 @@ import (
_ "k8s.io/client-go/tools/portforward"
"k8s.io/client-go/util/homedir"
"path/filepath"
"strings"
"regexp"
)
type Provider struct {
@@ -77,48 +80,30 @@ func (provider *Provider) GetPods(ctx context.Context, namespace string) {
fmt.Printf("There are %d pods in Namespace %s\n", len(pods.Items), namespace)
}
func (provider *Provider) CreateMizuPod(ctx context.Context, namespace string, podName string, podImage string, tappedPodNamespace string, tappedPodName string, linkServiceAccount bool) (*core.Pod, error) {
tappedPod, err := provider.clientSet.CoreV1().Pods(tappedPodNamespace).Get(ctx, tappedPodName, metav1.GetOptions{})
if err != nil {
panic(err.Error())
}
podIps := make([]string, len(tappedPod.Status.PodIPs))
for ii, podIp := range tappedPod.Status.PodIPs {
podIps[ii] = podIp.IP
}
podIpsString := strings.Join(podIps, ",")
privileged := true
func (provider *Provider) CreateMizuAggregatorPod(ctx context.Context, namespace string, podName string, podImage string, linkServiceAccount bool) (*core.Pod, error) {
pod := &core.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: podName,
Namespace: namespace,
Labels: map[string]string{"app": podName},
},
Spec: core.PodSpec{
HostNetwork: true, // very important to make passive tapper see traffic
Containers: []core.Container{
{
Name: podName,
Image: podImage,
ImagePullPolicy: core.PullAlways,
SecurityContext: &core.SecurityContext{
Privileged: &privileged, // must be privileged to get node level traffic
},
Command: []string {"./mizuagent", "--aggregator"},
Env: []core.EnvVar{
{
Name: "HOST_MODE",
Value: "1",
},
{
Name: "TAPPED_ADDRESSES",
Value: podIpsString,
},
},
},
},
DNSPolicy: "ClusterFirstWithHostNet",
TerminationGracePeriodSeconds: new(int64),
NodeSelector: map[string]string{"kubernetes.io/hostname": tappedPod.Spec.NodeName},
},
}
//define the service account only when it exists to prevent pod crash
@@ -128,6 +113,21 @@ func (provider *Provider) CreateMizuPod(ctx context.Context, namespace string, p
return provider.clientSet.CoreV1().Pods(namespace).Create(ctx, pod, metav1.CreateOptions{})
}
func (provider *Provider) CreateService(ctx context.Context, namespace string, serviceName string, appLabelValue string) (*core.Service, error) {
service := core.Service{
ObjectMeta: metav1.ObjectMeta{
Name: serviceName,
Namespace: namespace,
},
Spec: core.ServiceSpec{
Ports: []core.ServicePort {{TargetPort: intstr.FromInt(8899), Port: 80}},
Type: core.ServiceTypeClusterIP,
Selector: map[string]string{"app": appLabelValue},
},
}
return provider.clientSet.CoreV1().Services(namespace).Create(ctx, &service, metav1.CreateOptions{})
}
func (provider *Provider) DoesMizuRBACExist(ctx context.Context, namespace string) (bool, error){
serviceAccount, err := provider.clientSet.CoreV1().ServiceAccounts(namespace).Get(ctx, serviceAccountName, metav1.GetOptions{})
@@ -200,8 +200,102 @@ func (provider *Provider) CreateMizuRBAC(ctx context.Context, namespace string ,
return nil
}
func (provider *Provider) RemovePod(ctx context.Context, namespace string, podName string) {
provider.clientSet.CoreV1().Pods(namespace).Delete(ctx, podName, metav1.DeleteOptions{})
func (provider *Provider) RemovePod(ctx context.Context, namespace string, podName string) error {
return provider.clientSet.CoreV1().Pods(namespace).Delete(ctx, podName, metav1.DeleteOptions{})
}
func (provider *Provider) RemoveService(ctx context.Context, namespace string, serviceName string) error {
return provider.clientSet.CoreV1().Services(namespace).Delete(ctx, serviceName, metav1.DeleteOptions{})
}
func (provider *Provider) RemoveDaemonSet(ctx context.Context, namespace string, daemonSetName string) error {
return provider.clientSet.AppsV1().DaemonSets(namespace).Delete(ctx, daemonSetName, metav1.DeleteOptions{})
}
func (provider *Provider) CreateMizuTapperDaemonSet(ctx context.Context, namespace string, daemonSetName string, podImage string, tapperPodName string, aggregatorPodIp string, nodeToTappedPodIPMap map[string][]string, linkServiceAccount bool) error {
nodeToTappedPodIPMapJsonStr, err := json.Marshal(nodeToTappedPodIPMap)
if err != nil {
return err
}
privileged := true
podTemplate := core.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Labels: map[string]string{"app": tapperPodName},
},
Spec: core.PodSpec{
HostNetwork: true, // very important to make passive tapper see traffic
Containers: []core.Container{
{
Name: tapperPodName,
Image: podImage,
ImagePullPolicy: core.PullAlways,
SecurityContext: &core.SecurityContext{
Privileged: &privileged, // must be privileged to get node level traffic
},
Command: []string {"./mizuagent", "-i", "any", "--tap", "--hardump", "--aggregator-address", fmt.Sprintf("ws://%s/wsTapper", aggregatorPodIp)},
Env: []core.EnvVar{
{
Name: "HOST_MODE",
Value: "1",
},
{
Name: "AGGREGATOR_ADDRESS",
Value: aggregatorPodIp,
},
{
Name: "TAPPED_ADDRESSES_PER_HOST",
Value: string(nodeToTappedPodIPMapJsonStr),
},
{
Name: "NODE_NAME",
ValueFrom: &core.EnvVarSource{
FieldRef: &core.ObjectFieldSelector {
APIVersion: "v1",
FieldPath: "spec.nodeName",
},
},
},
},
},
},
DNSPolicy: "ClusterFirstWithHostNet",
TerminationGracePeriodSeconds: new(int64),
// Affinity: TODO: define node selector for all relevant nodes for this mizu instance
},
}
if linkServiceAccount {
podTemplate.Spec.ServiceAccountName = serviceAccountName
}
labelSelector := metav1.LabelSelector{
MatchLabels: map[string]string{"app": tapperPodName},
}
daemonSet := apps.DaemonSet{
ObjectMeta: metav1.ObjectMeta{
Name: daemonSetName,
Namespace: namespace,
},
Spec: apps.DaemonSetSpec{
Selector: &labelSelector,
Template: podTemplate,
},
}
_, err = provider.clientSet.AppsV1().DaemonSets(namespace).Create(ctx, &daemonSet, metav1.CreateOptions{})
return err
}
func (provider *Provider) GetAllPodsMatchingRegex(ctx context.Context, regex *regexp.Regexp) ([]core.Pod, error) {
pods, err := provider.clientSet.CoreV1().Pods("").List(ctx, metav1.ListOptions{})
if err != nil {
return nil, err
}
matchingPods := make([]core.Pod, 0)
for _, pod := range pods.Items {
if regex.MatchString(pod.Name) {
matchingPods = append(matchingPods, pod)
}
}
return matchingPods, err
}
func getClientSet(config *restclient.Config) *kubernetes.Clientset {