Skip to content

Commit

Permalink
apiserver/audit: split apart legacy funcs
Browse files Browse the repository at this point in the history
  • Loading branch information
sttts committed Jun 22, 2021
1 parent 214ee5f commit 3afab92
Show file tree
Hide file tree
Showing 4 changed files with 286 additions and 270 deletions.
74 changes: 0 additions & 74 deletions pkg/operator/apiserver/audit/audit_policies.go
Expand Up @@ -4,15 +4,11 @@ import (
"bytes"
"fmt"
"path"
"path/filepath"
"strings"

configv1 "github.com/openshift/api/config/v1"
assets "github.com/openshift/library-go/pkg/operator/apiserver/audit/bindata"
libgoapiserver "github.com/openshift/library-go/pkg/operator/configobserver/apiserver"
"github.com/openshift/library-go/pkg/operator/resource/resourceapply"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/serializer"
"k8s.io/apimachinery/pkg/runtime/serializer/json"
Expand Down Expand Up @@ -80,29 +76,6 @@ func DefaultPolicy() ([]byte, error) {
return buf.Bytes(), nil
}

// WithAuditPolicies is meant to wrap a standard Asset function usually provided by an operator.
// It delegates to GetAuditPolicies when the filename matches the predicate for retrieving a audit policy config map for target namespace and name.
func WithAuditPolicies(targetName string, targetNamespace string, assetDelegateFunc resourceapply.AssetFunc) resourceapply.AssetFunc {
return func(file string) ([]byte, error) {
if file != "audit-policies-cm.yaml" {
return assetDelegateFunc(file)
}

cm, err := GetAuditPolicies(targetName, targetNamespace)
if err != nil {
return nil, err
}
cm.Kind = "ConfigMap"
cm.APIVersion = "v1"

var buf bytes.Buffer
if err := coreYamlSerializer.Encode(cm, &buf); err != nil {
return nil, err
}
return buf.Bytes(), nil
}
}

// GetAuditPolicy computes the audit policy for the given audit config.
// Note: the returned Policy has Kind and APIVersion not set. This is responsibility of the caller
// when serializing it.
Expand All @@ -118,50 +91,3 @@ func GetAuditPolicy(audit configv1.Audit) (*auditv1.Policy, error) {

return p, nil
}

// GetAuditPolicies returns a config map that holds the audit policies for the target namespaces and name.
// Note: the returned ConfigMap has Kind and APIVersion not set. This is responsibility of the caller
// when serializing it.
func GetAuditPolicies(targetName, targetNamespace string) (*corev1.ConfigMap, error) {
cm := corev1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Namespace: targetNamespace,
Name: targetName,
},
Data: map[string]string{},
}

for _, profile := range []configv1.AuditProfileType{configv1.AuditProfileDefaultType, configv1.WriteRequestBodiesAuditProfileType, configv1.AllRequestBodiesAuditProfileType} {
policy, err := GetAuditPolicy(configv1.Audit{Profile: profile})
if err != nil {
return nil, err
}

policy.Kind = "Policy"
policy.APIVersion = auditv1.SchemeGroupVersion.String()

var buf bytes.Buffer
if err := auditYamlSerializer.Encode(policy, &buf); err != nil {
return nil, err
}

cm.Data[fmt.Sprintf("%s.yaml", strings.ToLower(string(profile)))] = buf.String()
}

return &cm, nil
}

// NewAuditPolicyPathGetter returns a path getter for audit policy file mounted into the given path of a Pod as a directory.
//
// openshift-apiserver and oauth-apiserver mounts the audit policy ConfigMap into
// the above path inside the Pod.
func NewAuditPolicyPathGetter(path string) (libgoapiserver.AuditPolicyPathGetterFunc, error) {
return func(profile string) (string, error) {
manifestName := fmt.Sprintf("pkg/operator/apiserver/audit/manifests/%s-rules.yaml", strings.ToLower(profile))
if _, err := assets.Asset(manifestName); err != nil {
return "", fmt.Errorf("invalid audit profile %q", profile)
}

return filepath.Join(path, fmt.Sprintf("%s.yaml", strings.ToLower(profile))), nil
}, nil
}
196 changes: 0 additions & 196 deletions pkg/operator/apiserver/audit/audit_policies_test.go
Expand Up @@ -5,204 +5,8 @@ import (
"io/ioutil"
"os"
"testing"

"github.com/openshift/library-go/pkg/operator/resource/resourceread"
"k8s.io/apimachinery/pkg/api/equality"
"k8s.io/apimachinery/pkg/util/diff"
auditv1 "k8s.io/apiserver/pkg/apis/audit/v1"
"sigs.k8s.io/yaml"
)

func TestWithAuditPolicies(t *testing.T) {
scenarios := []struct {
name string
delegate *fakeAsset
targetNamespace string
targetName string
targetFilename string
goldenFile string
}{
{
name: "happy path: the audit policies file for target namespace is created when the target file name matches",
targetNamespace: "ScenarioOne",
targetName: "audit",
targetFilename: "audit-policies-cm.yaml",
goldenFile: "./testdata/audit-policies-cm-scenario-1.yaml",
},
{
name: "the delegate is called when the target file name doesn't match",
targetNamespace: "ScenarioTwo",
targetName: "audit",
targetFilename: "trusted_ca_cm.yaml",
delegate: &fakeAsset{"", "trusted_ca_cm.yaml"},
},
}
for _, scenario := range scenarios {
t.Run(scenario.name, func(t *testing.T) {
// act
if scenario.delegate == nil {
scenario.delegate = &fakeAsset{}
}
target := WithAuditPolicies(scenario.targetName, scenario.targetNamespace, scenario.delegate.AssetFunc)
actualAuditPoliciesData, err := target(scenario.targetFilename)
if err != nil {
t.Fatal(err)
}

// validate
if len(scenario.goldenFile) > 0 {
actualAuditPolicies := resourceread.ReadConfigMapV1OrDie(actualAuditPoliciesData)
goldenAuditPoliciesData := readBytesFromFile(t, scenario.goldenFile)
goldenAuditPolicies := resourceread.ReadConfigMapV1OrDie(goldenAuditPoliciesData)

if got, expected := len(actualAuditPolicies.Data), len(goldenAuditPolicies.Data); got != expected {
t.Errorf("unexpected number of policies %d, expected %d", got, expected)
}

for name, bs := range actualAuditPolicies.Data {
var got auditv1.Policy
if err := yaml.Unmarshal([]byte(bs), &got); err != nil {
t.Errorf("failed to unmarshal policy %q: %v", name, err)
continue
}

bs, ok := goldenAuditPolicies.Data[name]
if !ok {
t.Errorf("unexpected policy %q", name)
continue
}
var expected auditv1.Policy
if err := yaml.Unmarshal([]byte(bs), &expected); err != nil {
t.Errorf("failed to unmarshal golden policy %q: %v", name, err)
continue
}

if !equality.Semantic.DeepEqual(got, expected) {
t.Errorf("policy %q differs: %s", name, diff.ObjectDiff(expected, got))
}
}
}
if err := scenario.delegate.Validate(); err != nil {
t.Fatal(err)
}
})
}
}

func TestGetAuditPolicies(t *testing.T) {
scenarios := []struct {
name string
targetNamespace string
targetName string
goldenFile string
}{
{
name: "happy path: the audit policies file for target namespace is created",
targetNamespace: "ScenarioOne",
targetName: "audit",
goldenFile: "./testdata/audit-policies-cm-scenario-1.yaml",
},
}
for _, scenario := range scenarios {
t.Run(scenario.name, func(t *testing.T) {
// act
actualAuditPolicies, err := GetAuditPolicies(scenario.targetName, scenario.targetNamespace)
if err != nil {
t.Fatal(err)
}

// validate
if len(scenario.goldenFile) > 0 {
goldenAuditPoliciesData := readBytesFromFile(t, scenario.goldenFile)
goldenAuditPolicies := resourceread.ReadConfigMapV1OrDie(goldenAuditPoliciesData)

if got, expected := len(actualAuditPolicies.Data), len(goldenAuditPolicies.Data); got != expected {
t.Errorf("unexpected number of policies %d, expected %d", got, expected)
}

for name, bs := range actualAuditPolicies.Data {
var got auditv1.Policy
if err := yaml.Unmarshal([]byte(bs), &got); err != nil {
t.Errorf("failed to unmarshal policy %q: %v", name, err)
continue
}

bs, ok := goldenAuditPolicies.Data[name]
if !ok {
t.Errorf("unexpected policy %q", name)
continue
}
var expected auditv1.Policy
if err := yaml.Unmarshal([]byte(bs), &expected); err != nil {
t.Errorf("failed to unmarshal golden policy %q: %v", name, err)
continue
}

if !equality.Semantic.DeepEqual(got, expected) {
t.Errorf("policy %q differs: %s", name, diff.ObjectDiff(expected, got))
}
}
}
})
}
}

func TestNewAuditPolicyPathGetter(t *testing.T) {
tests := []struct {
name string
profile string
expectedPath string
errExpected bool
}{
{
name: "Default audit policy",
profile: "Default",
expectedPath: "/var/run/configmaps/audit/default.yaml",
},
{
name: "WriteRequestBodies audit policy",
profile: "WriteRequestBodies",
expectedPath: "/var/run/configmaps/audit/writerequestbodies.yaml",
},
{
name: "AllRequestBodies audit policiys",
profile: "AllRequestBodies",
expectedPath: "/var/run/configmaps/audit/allrequestbodies.yaml",
},
{
name: "audit policy does not exist",
profile: "Foo",
errExpected: true,
},
}

pathGetter, err := NewAuditPolicyPathGetter("/var/run/configmaps/audit")
if err != nil {
t.Fatal(err)
}

for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
pathGot, err := pathGetter(test.profile)

if test.errExpected {
if err == nil {
t.Error("expected error but got none")
}

return
}

if err != nil {
t.Error(err)
}
if test.expectedPath != pathGot {
t.Errorf("path: got=%s, want=%s", pathGot, test.expectedPath)
}
})
}
}

func TestDefaultPolicy(t *testing.T) {
scenarios := []struct {
name string
Expand Down
85 changes: 85 additions & 0 deletions pkg/operator/apiserver/audit/legacy.go
@@ -0,0 +1,85 @@
package audit

import (
"bytes"
"fmt"
"strings"

configv1 "github.com/openshift/api/config/v1"
assets "github.com/openshift/library-go/pkg/operator/apiserver/audit/bindata"
libgoapiserver "github.com/openshift/library-go/pkg/operator/configobserver/apiserver"
"github.com/openshift/library-go/pkg/operator/resource/resourceapply"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
auditv1 "k8s.io/apiserver/pkg/apis/audit/v1"
)

// GetAuditPolicies returns a config map that holds the audit policies for the target namespaces and name.
// Note: the returned ConfigMap has Kind and APIVersion not set. This is responsibility of the caller
// when serializing it.
func GetAuditPolicies(targetName, targetNamespace string) (*corev1.ConfigMap, error) {
cm := corev1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Namespace: targetNamespace,
Name: targetName,
},
Data: map[string]string{},
}

for _, profile := range []configv1.AuditProfileType{configv1.AuditProfileDefaultType, configv1.WriteRequestBodiesAuditProfileType, configv1.AllRequestBodiesAuditProfileType} {
policy, err := GetAuditPolicy(configv1.Audit{Profile: profile})
if err != nil {
return nil, err
}

policy.Kind = "Policy"
policy.APIVersion = auditv1.SchemeGroupVersion.String()

var buf bytes.Buffer
if err := auditYamlSerializer.Encode(policy, &buf); err != nil {
return nil, err
}

cm.Data[fmt.Sprintf("%s.yaml", strings.ToLower(string(profile)))] = buf.String()
}

return &cm, nil
}

// WithAuditPolicies is meant to wrap a standard Asset function usually provided by an operator.
// It delegates to GetAuditPolicies when the filename matches the predicate for retrieving a audit policy config map for target namespace and name.
func WithAuditPolicies(targetName string, targetNamespace string, assetDelegateFunc resourceapply.AssetFunc) resourceapply.AssetFunc {
return func(file string) ([]byte, error) {
if file != "audit-policies-cm.yaml" {
return assetDelegateFunc(file)
}

cm, err := GetAuditPolicies(targetName, targetNamespace)
if err != nil {
return nil, err
}
cm.Kind = "ConfigMap"
cm.APIVersion = "v1"

var buf bytes.Buffer
if err := coreYamlSerializer.Encode(cm, &buf); err != nil {
return nil, err
}
return buf.Bytes(), nil
}
}

// NewAuditPolicyPathGetter returns a path getter for audit policy file mounted into the given path of a Pod as a directory.
//
// openshift-apiserver and oauth-apiserver mounts the audit policy ConfigMap into
// the above path inside the Pod.
func NewAuditPolicyPathGetter(path string) (libgoapiserver.AuditPolicyPathGetterFunc, error) {
return func(profile string) (string, error) {
manifestName := fmt.Sprintf("pkg/operator/apiserver/audit/manifests/%s-rules.yaml", strings.ToLower(profile))
if _, err := assets.Asset(manifestName); err != nil {
return "", fmt.Errorf("invalid audit profile %q", profile)
}

return fmt.Sprintf("%s/%s.yaml", path, strings.ToLower(profile)), nil
}, nil
}

0 comments on commit 3afab92

Please sign in to comment.