cyberark/secrets-provider-for-k8s

View on GitHub
e2e/rotations_utils.go

Summary

Maintainability
A
2 hrs
Test Coverage
//go:build e2e
// +build e2e

package e2e

import (
    "bytes"
    "context"
    "fmt"
    "os"
    "os/exec"
    "path/filepath"

    corev1 "k8s.io/api/core/v1"
    metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    "k8s.io/client-go/kubernetes"
    "k8s.io/client-go/kubernetes/scheme"
    "k8s.io/client-go/tools/remotecommand"
    "sigs.k8s.io/e2e-framework/klient"
)

func DeleteTestSecret(client klient.Client) error {
    err := LoadPolicy(client, "conjur-delete-secret")
    if err != nil {
        return err
    }
    return nil
}

func RestoreTestSecret(client klient.Client) error {
    // Restore the actual secret object by reloading the policy
    err := LoadPolicy(client, "conjur-secrets")
    if err != nil {
        return err
    }
    // Restore the default value
    err = SetConjurSecret(client, "secrets/test_secret", "supersecret")
    if err != nil {
        return err
    }
    return nil
}

func CreateTestingDirectories(client klient.Client) error {
    // create 'generated' directory for generated policies (current /generated is .gitignored)
    cmd := exec.Command("mkdir", "../deploy/policy/generated")
    err := cmd.Run()
    if err != nil {
        return fmt.Errorf("failed to create 'generated' directory: %v", err)
    }

    // create 'policy' directory in conjur
    pod, err := FetchPodWithLabelSelector(client, ConjurNamespace(), ConjurCLILabelSelector)
    if err != nil {
        return fmt.Errorf("failed to fetch cli pod. %v", err)
    }

    var stdout, stderr bytes.Buffer
    command := []string{"mkdir", "policy"}
    if err := client.Resources(ConjurNamespace()).ExecInPod(context.TODO(), ConjurNamespace(), pod.Name, ConjurCLIContainer, command, &stdout, &stderr); err != nil {
        return fmt.Errorf("failed to create 'policy' directory in conjur. %v, %s", err, stderr.String())
    }

    return nil
}

func DeleteTestingDirectories(client klient.Client) error {
    // delete 'generated' directory for generated policies (current /generated is .gitignored)
    cmd := exec.Command("rm", "-rf", "../deploy/policy/generated")
    err := cmd.Run()
    if err != nil {
        return fmt.Errorf("failed to remove 'generated' directory: %v", err)
    }

    // delete 'policy' directory in conjur
    pod, err := FetchPodWithLabelSelector(client, ConjurNamespace(), ConjurCLILabelSelector)
    if err != nil {
        return fmt.Errorf("failed to fetch cli pod. %v", err)
    }

    var stdout, stderr bytes.Buffer
    command := []string{"rm", "-rf", "policy"}
    if err := client.Resources(ConjurNamespace()).ExecInPod(context.TODO(), ConjurNamespace(), pod.Name, ConjurCLIContainer, command, &stdout, &stderr); err != nil {
        return fmt.Errorf("failed to remove 'policy' directory in conjur. %v, %s", err, stderr.String())
    }

    return nil
}

func GeneratePolicyFromTemplate(client klient.Client, filename string) (string, string, error) {
    // retrieve and execute template
    templateName := fmt.Sprintf("%s.template.sh.yml", filename)
    templatePath := filepath.Join("..", "deploy", "policy", "templates", templateName)
    cmd := exec.Command(templatePath)

    // set output file
    fileName := fmt.Sprintf("%s.%s.yml", SecretsProviderNamespace(), filename)
    filePath := filepath.Join("..", "deploy", "policy", "generated", fileName)

    // generate policy
    file, err := os.Create(filePath)
    if err != nil {
        return "", "", fmt.Errorf("failed to create file: %v", err)
    }

    // redirect template contents to policy
    cmd.Stdout = file
    err = cmd.Start()
    if err != nil {
        return "", "", fmt.Errorf("failed to redirect output: %v", err)
    }

    // cmd.Wait() waits for any copying to stdin or copying from stdout or stderr to complete
    err = cmd.Wait()
    if err != nil {
        return "", "", fmt.Errorf("failed to wait for output redirection: %v", err)
    }

    return fileName, filePath, nil
}

func CopyFileIntoPod(client klient.Client, podName string, namespace string, containerName string, src string, dst string) error {
    // create client-go clientset
    clientset, err := kubernetes.NewForConfig(client.RESTConfig())
    if err != nil {
        return fmt.Errorf("unable to initialize K8s client: %v", err)
    }

    // open the file to copy
    localFile, err := os.Open(src)
    if err != nil {
        return fmt.Errorf("failed to open file: %v", err)
    }
    defer localFile.Close()

    pod, err := clientset.CoreV1().Pods(namespace).Get(context.TODO(), podName, metav1.GetOptions{})
    if err != nil {
        return fmt.Errorf("failed to retrieve pod: %v", err)
    }

    // validate container existsin pod
    var container *corev1.Container
    for _, c := range pod.Spec.Containers {
        if c.Name == containerName {
            container = &c
            break
        }
    }

    if container == nil {
        return fmt.Errorf("failed to find container in pod: %v", err)
    }

    // create a stream to the container
    req := clientset.CoreV1().RESTClient().Post().
        Resource("pods").
        Name(podName).
        Namespace(namespace).
        SubResource("exec").
        Param("container", containerName)

    req.VersionedParams(&corev1.PodExecOptions{
        Container: containerName,
        Command:   []string{"bash", "-c", "cat > " + dst},
        Stdin:     true,
        Stdout:    true,
        Stderr:    true,
    }, scheme.ParameterCodec)

    execute, err := remotecommand.NewSPDYExecutor(client.RESTConfig(), "POST", req.URL())
    if err != nil {
        return fmt.Errorf("failed to create executor: %v", err)
    }

    // Create a stream to the container
    err = execute.StreamWithContext(context.TODO(), remotecommand.StreamOptions{
        Stdin:  localFile,
        Stdout: os.Stdout,
        Stderr: os.Stderr,
        Tty:    false,
    })
    if err != nil {
        return fmt.Errorf("failed to stream: %v", err)
    }

    return nil
}

func LoadPolicy(client klient.Client, filename string) error {
    // create generated file from template
    fileName, src, err := GeneratePolicyFromTemplate(client, filename)
    if err != nil {
        return err
    }

    // copy policy into conjur pod
    pod, err := FetchPodWithLabelSelector(client, ConjurNamespace(), ConjurCLILabelSelector)
    if err != nil {
        return fmt.Errorf("failed to fetch cli pod. %v", err)
    }

    var dst string = "policy/" + fileName
    err = CopyFileIntoPod(client, pod.Name, ConjurNamespace(), ConjurCLIContainer, src, dst)
    if err != nil {
        return err
    }

    // update root with policy
    var stdout, stderr bytes.Buffer
    command := []string{"conjur", "policy", "update", "-b", "root", "-f", dst}
    if err := client.Resources(ConjurNamespace()).ExecInPod(context.TODO(), ConjurNamespace(), pod.Name, ConjurCLIContainer, command, &stdout, &stderr); err != nil {
        return fmt.Errorf("failed to update policy root in conjur: %v, %s", err, stderr.String())
    }

    return nil
}