Skip to content

Instantly share code, notes, and snippets.

@hh
Last active April 19, 2018 21:19
Show Gist options
  • Save hh/9fadc21b2163b252051b5e319c59a80e to your computer and use it in GitHub Desktop.
Save hh/9fadc21b2163b252051b5e319c59a80e to your computer and use it in GitHub Desktop.
kubeadm audit-webhook research

audit-webhook needs

–audit-webhook-config-file

Most similar to AuditPolicyConfiguration / –audit-policy-file

It can’t really have a default value, because you need to specify a webhook url.

–audit-webhook-initial-backoff

Most similar to LogMaxAge / –audit-log-maxage INT

The default is 10s, but I’m unsure we should specificy it if we aren’t provided it as a config option.

add other options for audit webhook

--audit-webhook-config-file string                        Path to a kubeconfig formatted file that defines the audit webhook configuration. Requires the 'AdvancedAuditing' feature gate.
--audit-webhook-mode string                               Strategy for sending audit events. Blocking indicates sending events should block server responses. Batch causes the backend to buffer and write events asynchronously. Known modes are batch,blocking. (default "batch")
--audit-webhook-initial-backoff duration                  The amount of time to wait before retrying the first failed request. (default 10s)
--audit-webhook-batch-buffer-size int                     The size of the buffer to store events before batching and writing. Only used in batch mode. (default 10000)
--audit-webhook-batch-max-size int                        The maximum size of a batch. Only used in batch mode. (default 400)
--audit-webhook-batch-max-wait duration                   The amount of time to wait before force writing the batch that hadn't reached the max size. Only used in batch mode. (default 30s)
--audit-webhook-batch-throttle-burst int                  Maximum number of requests sent at the same moment if ThrottleQPS was not utilized before. Only used in batch mode. (default 15)
--audit-webhook-batch-throttle-enable                     Whether batching throttling is enabled. Only used in batch mode. (default true)
--audit-webhook-batch-throttle-qps float32                Maximum average number of batches per second. Only used in batch mode. (default 10)

Types, Defaults, and Constants

// AuditPolicyConfiguration holds the options for configuring the api server audit policy.
type AuditPolicyConfiguration struct {
	// Path is the local path to an audit policy.
	Path string
	// LogDir is the local path to the directory where logs should be stored.
	LogDir string
	// LogMaxAge is the number of days logs will be stored for. 0 indicates forever.
	LogMaxAge *int32
	// WebhookConfigPath is the local path to webhook policy.
	WebhookConfigPath string
	// WebhookInitialBackoff is the time to wait (in seconds) before retrying the first failed request.
	WebhookInitialBackoff *int32 //defaults to 10s if not provided
	//TODO(chuckha) add other options for audit policy.
}
// AuditPolicyConfiguration holds the options for configuring the api server audit policy.
type AuditPolicyConfiguration struct {
	// Path is the local path to an audit policy.
	Path string `json:"path"`
	// LogDir is the local path to the directory where logs should be stored.
	LogDir string `json:"logDir"`
	// LogMaxAge is the number of days logs will be stored for. 0 indicates forever.
	LogMaxAge *int32 `json:"logMaxAge,omitempty"`
	// WebhookConfigPath is the local path to webhook policy.
	WebhookConfigPath string `json:"webhookConfigPath"`
	// WebhookInitialBackoff is the time to wait (in seconds) before retrying the first failed request.
	WebhookInitialBackoff *int32 `json:"webhookInitialBackoff,omitempty"`
	//TODO(chuckha) add other options for audit policy.
}
var (
	// DefaultAuditPolicyLogMaxAge is defined as a var so its address can be taken
	// It is the number of days to store audit logs
	DefaultAuditPolicyLogMaxAge = int32(2)
  // POSSIBLY NOT NEEDED, IF WE DO NOT WANT A DEFAULT
	// DefaultAuditWebhookInitialBackoff also needs it's address taken
	// It is the number of seconds to wait before retrying the first failed request.
	DefaultAuditWebhookInitialBackoff = int32(2)
)
// SetDefaults_AuditPolicyConfiguration sets default values for the AuditPolicyConfiguration
func SetDefaults_AuditPolicyConfiguration(obj *MasterConfiguration) {
	if obj.AuditPolicyConfiguration.LogDir == "" {
		obj.AuditPolicyConfiguration.LogDir = constants.StaticPodAuditPolicyLogDir
	}
	if obj.AuditPolicyConfiguration.LogMaxAge == nil {
		obj.AuditPolicyConfiguration.LogMaxAge = &DefaultAuditPolicyLogMaxAge
	}
  // POSSIBLY NOT NEEDED, IF WE DO NOT WANT A DEFAULT
	if obj.AuditPolicyConfiguration.WebhookInitialBackoff == nil {
		obj.AuditPolicyConfiguration.WebhookInitialBackoff = &DefaultAuditWebhookInitialBackoff
	}
}

app/constants/constants.go::kubeaudit

	// KubeAuditPolicyVolumeName is the name of the volume that will contain the audit policy
	KubeAuditPolicyVolumeName = "audit"
	// AuditPolicyDir is the directory that will contain the audit policy
	AuditPolicyDir = "audit"
	// AuditPolicyFile is the name of the audit policy file itself
	AuditPolicyFile = "audit.yaml"
	// AuditWebhookConfigFile is the name of the audit webhook config file itself
	AuditWebhookConfigFile = "webhook.yaml"
	// AuditPolicyLogFile is the name of the file audit logs get written to
	AuditPolicyLogFile = "audit.log"
	// KubeAuditPolicyLogVolumeName is the name of the volume that will contain the audit logs
	KubeAuditPolicyLogVolumeName = "audit-log"
	// StaticPodAuditPolicyLogDir is the name of the directory in the static pod that will have the audit logs
	StaticPodAuditPolicyLogDir = "/var/log/kubernetes/audit"

app/constants/constants.go:: getstaticpodauditpolicyfile

// GetStaticPodAuditPolicyFile returns the path to the audit policy file within a static pod
func GetStaticPodAuditPolicyFile() string {
	return filepath.Join(KubernetesDir, AuditPolicyDir, AuditPolicyFile)
}
// GetStaticPodAuditWebhookConfigFile returns the path to the audit webhook config file within a static pod
func GetStaticPodAuditWebhookConfigFile() string {
	return filepath.Join(KubernetesDir, AuditPolicyDir, AuditWebhookConfigFile)
}

Implementation

	if features.Enabled(i.cfg.FeatureGates, features.Auditing) {
		// Setup the AuditPolicy (either it was passed in and exists or it wasn't passed in and generate a default policy)
		if i.cfg.AuditPolicyConfiguration.Path != "" {
			// TODO(chuckha) ensure passed in audit policy is valid so users don't have to find the error in the api server log.
			if _, err := os.Stat(i.cfg.AuditPolicyConfiguration.Path); err != nil {
				return fmt.Errorf("error getting file info for audit policy file %q [%v]", i.cfg.AuditPolicyConfiguration.Path, err)
			}
		} else {
			i.cfg.AuditPolicyConfiguration.Path = filepath.Join(kubeConfigDir, kubeadmconstants.AuditPolicyDir, kubeadmconstants.AuditPolicyFile)
			if err := auditutil.CreateDefaultAuditLogPolicy(i.cfg.AuditPolicyConfiguration.Path); err != nil {
				return fmt.Errorf("error creating default audit policy %q [%v]", i.cfg.AuditPolicyConfiguration.Path, err)
			}
		}
		// Verify the AuditWebhookConfigFile (verify it exists if it was passed in)
		if i.cfg.AuditPolicyConfiguration.WebhookConfigPath != "" {
			// TODO(chuckha) ensure passed in audit webhook config is valid so users don't have to find the error in the api server log.
			if _, err := os.Stat(i.cfg.AuditPolicyConfiguration.WebhookConfigPath); err != nil {
				return fmt.Errorf("error getting file info for audit webhook config file %q [%v]", i.cfg.AuditPolicyConfiguration.WebhookConfigPath, err)
			}
		} else {
			i.cfg.AuditPolicyConfiguration.WebhookConfigPath = filepath.Join(kubeConfigDir, kubeadmconstants.AuditPolicyDir, kubeadmconstants.AuditWebhookConfigFile)
			if err := auditutil.CreateDefaultAuditWebhookConfig(i.cfg.AuditPolicyConfiguration.WebhookConfigPath); err != nil {
				return fmt.Errorf("error creating default audit policy %q [%v]", i.cfg.AuditPolicyConfiguration.WebhookConfigPath, err)
			}
		}

		}
 if features.Enabled(cfg.FeatureGates, features.Auditing) {
		command = append(command, "--audit-policy-file="+kubeadmconstants.GetStaticPodAuditPolicyFile())
		command = append(command, "--audit-log-path="+filepath.Join(kubeadmconstants.StaticPodAuditPolicyLogDir, kubeadmconstants.AuditPolicyLogFile))
		if cfg.AuditPolicyConfiguration.LogMaxAge == nil {
			command = append(command, fmt.Sprintf("--audit-log-maxage=%d", kubeadmapiext.DefaultAuditPolicyLogMaxAge))
		} else {
			command = append(command, fmt.Sprintf("--audit-log-maxage=%d", *cfg.AuditPolicyConfiguration.LogMaxAge))
		}
		if cfg.AuditPolicyConfiguration.WebhookConfigPath != nil {
		  command = append(command, "--audit-webhook-config-file="+kubeadmconstants.GetStaticPodAuditWebhookConfigFile())
   }
		if cfg.AuditPolicyConfiguration.WebhookInitialBackoff != nil {
			command = append(command, fmt.Sprintf("--audit-webhook-initial-backoff=%d", *cfg.AuditPolicyConfiguration.WebhookInitialBackoff))
   }
	}
 if features.Enabled(cfg.FeatureGates, features.Auditing) {
		// Read-only mount for the audit policy file.
		mounts.NewHostPathMount(kubeadmconstants.KubeAPIServer, kubeadmconstants.KubeAuditPolicyVolumeName, cfg.AuditPolicyConfiguration.Path, kubeadmconstants.GetStaticPodAuditPolicyFile(), true, &hostPathFile)
		// Read-only mount for the audit webhook config file.
		mounts.NewHostPathMount(kubeadmconstants.KubeAPIServer, kubeadmconstants.KubeAuditPolicyVolumeName, cfg.AuditPolicyConfiguration.Path, kubeadmconstants.GetStaticPodAuditWebhookConfigFile(), true, &hostPathFile)
		// Write mount for the audit logs.
		mounts.NewHostPathMount(kubeadmconstants.KubeAPIServer, kubeadmconstants.KubeAuditPolicyLogVolumeName, cfg.AuditPolicyConfiguration.LogDir, kubeadmconstants.StaticPodAuditPolicyLogDir, false, &hostPathDirectoryOrCreate)
	}
// CreateDefaultAuditLogPolicy writes the default audit log policy to disk.
func CreateDefaultAuditLogPolicy(policyFile string) error {
	policy := auditv1beta1.Policy{
		TypeMeta: metav1.TypeMeta{
			APIVersion: "audit.k8s.io/v1beta1",
			Kind:       "Policy",
		},
		Rules: []auditv1beta1.PolicyRule{
			{
				Level: auditv1beta1.LevelMetadata,
			},
		},
	}
	return writePolicyToDisk(policyFile, &policy)
}

// CreateDefaultAuditWebhookConfig writes the default audit webhook config to disk.
func CreateDefaultAuditWebhookConfig(webhookConfigFile string) error {
  // We do not have a default, but we do have a volume mapping for this file
	return os.OpenFile(name, os.O_RDONLY|os.O_CREATE, 0644)
}

func writePolicyToDisk(policyFile string, policy *auditv1beta1.Policy) error {
	// creates target folder if not already exists
	if err := os.MkdirAll(filepath.Dir(policyFile), 0700); err != nil {
		return fmt.Errorf("failed to create directory %q: %v", filepath.Dir(policyFile), err)
	}

	// Registers auditv1beta1 with the runtime Scheme
	auditv1beta1.AddToScheme(scheme.Scheme)

	// writes the policy to disk
	serialized, err := util.MarshalToYaml(policy, auditv1beta1.SchemeGroupVersion)
	if err != nil {
		return fmt.Errorf("failed to marshal audit policy to YAML: %v", err)
	}

	if err := ioutil.WriteFile(policyFile, serialized, 0600); err != nil {
		return fmt.Errorf("failed to write audit policy to %v: %v", policyFile, err)
	}

	return nil
}
func writeWebhookConfigToDisk(webhookConfigFile string, policy *auditv1beta1.Policy) error {
	// creates target folder if not already exists
	if err := os.MkdirAll(filepath.Dir(webhookConfigFile), 0700); err != nil {
		return fmt.Errorf("failed to create directory %q: %v", filepath.Dir(webhookConfigFile), err)
	}

	// Registers auditv1beta1 with the runtime Scheme
	auditv1beta1.AddToScheme(scheme.Scheme)

	// writes the policy to disk
	serialized, err := util.MarshalToYaml(policy, auditv1beta1.SchemeGroupVersion)
	if err != nil {
		return fmt.Errorf("failed to marshal audit webhook config to YAML: %v", err)
	}

	if err := ioutil.WriteFile(webhookConfigFile, serialized, 0600); err != nil {
		return fmt.Errorf("failed to write audit webhook config to %v: %v", webhookConfigFile, err)
	}

	return nil
}

tests

Fuzzer is looking for sample data types to generate fuzzer functions.

What will the mount be called?

I see audit audit-log I’m unsure if the audit KubAuditPolicyVolumeName of audit, is a single volume with a subdir I can place the webhook.yml into.

I think having a different name, within the same volume mount should work.

Added the other file we add on disk

A default AuditWebhookConfigFile is basically an empty file… Let’s figure out if the mount stuff works if we disable it’s creation.... less tests and code.

It looks like this is to test upgrades.... not sure what we should test for here… possible changing the webhook endpoint?

Tests command line arguments… should explore this a bit once we get it working.

@hh
Copy link
Author

hh commented Apr 19, 2018

Links works if the file is dropped into /cmd/kubeadm of a https://github.com/kubernetes/kubernetes checkout.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment