Unverified Commit fdeaa8c6 authored by Kubernetes Submit Queue's avatar Kubernetes Submit Queue Committed by GitHub

Merge pull request #59067 from chuckha/audit

Automatic merge from submit-queue. If you want to cherry-pick this change to another branch, please follow the instructions <a href="https://github.com/kubernetes/community/blob/master/contributors/devel/cherry-picks.md">here</a>. Feature Gate - Kubeadm Audit Logging Fixes kubernetes/kubeadm#623 Signed-off-by: 's avatarChuck Ha <ha.chuck@gmail.com> **What this PR does / why we need it**: This PR enables [Auditing](https://kubernetes.io/docs/tasks/debug-application-cluster/audit/) behind a featureGate. A user can supply their own audit policy with configuration option as well as a place for the audit logs to live. If no policy is supplied a default policy will be provided. The default policy will log all Metadata level policy logs. It is the example provided in the documentation. **Which issue(s) this PR fixes** *(optional, in `fixes #<issue number>(, fixes #<issue_number>, ...)` format, will close the issue(s) when PR gets merged)*: Fixes kubernetes/kubeadm#623 **Special notes for your reviewer**: **Release note**: ```release-note kubeadm: Enable auditing behind a feature gate. ```
parents 66ccfcb4 ed769174
......@@ -110,6 +110,11 @@ func Funcs(codecs runtimeserializer.CodecFactory) []interface{} {
ConfigSyncPeriod: metav1.Duration{Duration: 1},
},
}
obj.AuditPolicyConfiguration = kubeadm.AuditPolicyConfiguration{
Path: "foo",
LogDir: "/foo",
LogMaxAge: utilpointer.Int32Ptr(0),
}
},
func(obj *kubeadm.NodeConfiguration, c fuzz.Continue) {
c.FuzzNoCustom(obj)
......
......@@ -108,6 +108,9 @@ type MasterConfiguration struct {
// used for all control plane components.
UnifiedControlPlaneImage string
// AuditPolicyConfiguration defines the options for the api server audit system.
AuditPolicyConfiguration AuditPolicyConfiguration
// FeatureGates enabled by the user.
FeatureGates map[string]bool
}
......@@ -263,3 +266,14 @@ type HostPathMount struct {
type KubeProxy struct {
Config *kubeproxyconfigv1alpha1.KubeProxyConfiguration
}
// 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
//TODO(chuckha) add other options for audit policy.
}
......@@ -68,6 +68,12 @@ const (
KubeproxyKubeConfigFileName = "/var/lib/kube-proxy/kubeconfig.conf"
)
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)
)
func addDefaultingFuncs(scheme *runtime.Scheme) error {
return RegisterDefaults(scheme)
}
......@@ -117,6 +123,7 @@ func SetDefaults_MasterConfiguration(obj *MasterConfiguration) {
SetDefaults_KubeletConfiguration(obj)
}
SetDefaults_ProxyConfiguration(obj)
SetDefaults_AuditPolicyConfiguration(obj)
}
// SetDefaults_ProxyConfiguration assigns default values for the Proxy
......@@ -207,3 +214,13 @@ func SetDefaults_KubeletConfiguration(obj *MasterConfiguration) {
scheme.Default(obj.KubeletConfiguration.BaseConfig)
}
}
// 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
}
}
......@@ -100,6 +100,9 @@ type MasterConfiguration struct {
// be used for all control plane components.
UnifiedControlPlaneImage string `json:"unifiedControlPlaneImage"`
// AuditPolicyConfiguration defines the options for the api server audit system
AuditPolicyConfiguration AuditPolicyConfiguration `json:"auditPolicy"`
// FeatureGates enabled by the user.
FeatureGates map[string]bool `json:"featureGates,omitempty"`
}
......@@ -243,3 +246,14 @@ type HostPathMount struct {
type KubeProxy struct {
Config *kubeproxyconfigv1alpha1.KubeProxyConfiguration `json:"config,omitempty"`
}
// 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"`
//TODO(chuckha) add other options for audit policy.
}
......@@ -42,6 +42,8 @@ func RegisterConversions(scheme *runtime.Scheme) error {
return scheme.AddGeneratedConversionFuncs(
Convert_v1alpha1_API_To_kubeadm_API,
Convert_kubeadm_API_To_v1alpha1_API,
Convert_v1alpha1_AuditPolicyConfiguration_To_kubeadm_AuditPolicyConfiguration,
Convert_kubeadm_AuditPolicyConfiguration_To_v1alpha1_AuditPolicyConfiguration,
Convert_v1alpha1_Etcd_To_kubeadm_Etcd,
Convert_kubeadm_Etcd_To_v1alpha1_Etcd,
Convert_v1alpha1_HostPathMount_To_kubeadm_HostPathMount,
......@@ -85,6 +87,30 @@ func Convert_kubeadm_API_To_v1alpha1_API(in *kubeadm.API, out *API, s conversion
return autoConvert_kubeadm_API_To_v1alpha1_API(in, out, s)
}
func autoConvert_v1alpha1_AuditPolicyConfiguration_To_kubeadm_AuditPolicyConfiguration(in *AuditPolicyConfiguration, out *kubeadm.AuditPolicyConfiguration, s conversion.Scope) error {
out.Path = in.Path
out.LogDir = in.LogDir
out.LogMaxAge = (*int32)(unsafe.Pointer(in.LogMaxAge))
return nil
}
// Convert_v1alpha1_AuditPolicyConfiguration_To_kubeadm_AuditPolicyConfiguration is an autogenerated conversion function.
func Convert_v1alpha1_AuditPolicyConfiguration_To_kubeadm_AuditPolicyConfiguration(in *AuditPolicyConfiguration, out *kubeadm.AuditPolicyConfiguration, s conversion.Scope) error {
return autoConvert_v1alpha1_AuditPolicyConfiguration_To_kubeadm_AuditPolicyConfiguration(in, out, s)
}
func autoConvert_kubeadm_AuditPolicyConfiguration_To_v1alpha1_AuditPolicyConfiguration(in *kubeadm.AuditPolicyConfiguration, out *AuditPolicyConfiguration, s conversion.Scope) error {
out.Path = in.Path
out.LogDir = in.LogDir
out.LogMaxAge = (*int32)(unsafe.Pointer(in.LogMaxAge))
return nil
}
// Convert_kubeadm_AuditPolicyConfiguration_To_v1alpha1_AuditPolicyConfiguration is an autogenerated conversion function.
func Convert_kubeadm_AuditPolicyConfiguration_To_v1alpha1_AuditPolicyConfiguration(in *kubeadm.AuditPolicyConfiguration, out *AuditPolicyConfiguration, s conversion.Scope) error {
return autoConvert_kubeadm_AuditPolicyConfiguration_To_v1alpha1_AuditPolicyConfiguration(in, out, s)
}
func autoConvert_v1alpha1_Etcd_To_kubeadm_Etcd(in *Etcd, out *kubeadm.Etcd, s conversion.Scope) error {
out.Endpoints = *(*[]string)(unsafe.Pointer(&in.Endpoints))
out.CAFile = in.CAFile
......@@ -217,6 +243,9 @@ func autoConvert_v1alpha1_MasterConfiguration_To_kubeadm_MasterConfiguration(in
out.ImageRepository = in.ImageRepository
out.ImagePullPolicy = core_v1.PullPolicy(in.ImagePullPolicy)
out.UnifiedControlPlaneImage = in.UnifiedControlPlaneImage
if err := Convert_v1alpha1_AuditPolicyConfiguration_To_kubeadm_AuditPolicyConfiguration(&in.AuditPolicyConfiguration, &out.AuditPolicyConfiguration, s); err != nil {
return err
}
out.FeatureGates = *(*map[string]bool)(unsafe.Pointer(&in.FeatureGates))
return nil
}
......@@ -261,6 +290,9 @@ func autoConvert_kubeadm_MasterConfiguration_To_v1alpha1_MasterConfiguration(in
out.ImageRepository = in.ImageRepository
// INFO: in.CIImageRepository opted out of conversion generation
out.UnifiedControlPlaneImage = in.UnifiedControlPlaneImage
if err := Convert_kubeadm_AuditPolicyConfiguration_To_v1alpha1_AuditPolicyConfiguration(&in.AuditPolicyConfiguration, &out.AuditPolicyConfiguration, s); err != nil {
return err
}
out.FeatureGates = *(*map[string]bool)(unsafe.Pointer(&in.FeatureGates))
return nil
}
......
......@@ -44,6 +44,31 @@ func (in *API) DeepCopy() *API {
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *AuditPolicyConfiguration) DeepCopyInto(out *AuditPolicyConfiguration) {
*out = *in
if in.LogMaxAge != nil {
in, out := &in.LogMaxAge, &out.LogMaxAge
if *in == nil {
*out = nil
} else {
*out = new(int32)
**out = **in
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AuditPolicyConfiguration.
func (in *AuditPolicyConfiguration) DeepCopy() *AuditPolicyConfiguration {
if in == nil {
return nil
}
out := new(AuditPolicyConfiguration)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Etcd) DeepCopyInto(out *Etcd) {
*out = *in
if in.Endpoints != nil {
......@@ -210,6 +235,7 @@ func (in *MasterConfiguration) DeepCopyInto(out *MasterConfiguration) {
*out = make([]string, len(*in))
copy(*out, *in)
}
in.AuditPolicyConfiguration.DeepCopyInto(&out.AuditPolicyConfiguration)
if in.FeatureGates != nil {
in, out := &in.FeatureGates, &out.FeatureGates
*out = make(map[string]bool, len(*in))
......
......@@ -44,6 +44,31 @@ func (in *API) DeepCopy() *API {
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *AuditPolicyConfiguration) DeepCopyInto(out *AuditPolicyConfiguration) {
*out = *in
if in.LogMaxAge != nil {
in, out := &in.LogMaxAge, &out.LogMaxAge
if *in == nil {
*out = nil
} else {
*out = new(int32)
**out = **in
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AuditPolicyConfiguration.
func (in *AuditPolicyConfiguration) DeepCopy() *AuditPolicyConfiguration {
if in == nil {
return nil
}
out := new(AuditPolicyConfiguration)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Etcd) DeepCopyInto(out *Etcd) {
*out = *in
if in.Endpoints != nil {
......@@ -210,6 +235,7 @@ func (in *MasterConfiguration) DeepCopyInto(out *MasterConfiguration) {
*out = make([]string, len(*in))
copy(*out, *in)
}
in.AuditPolicyConfiguration.DeepCopyInto(&out.AuditPolicyConfiguration)
if in.FeatureGates != nil {
in, out := &in.FeatureGates, &out.FeatureGates
*out = make(map[string]bool, len(*in))
......
......@@ -47,6 +47,7 @@ go_library(
"//cmd/kubeadm/app/preflight:go_default_library",
"//cmd/kubeadm/app/util:go_default_library",
"//cmd/kubeadm/app/util/apiclient:go_default_library",
"//cmd/kubeadm/app/util/audit:go_default_library",
"//cmd/kubeadm/app/util/config:go_default_library",
"//cmd/kubeadm/app/util/dryrun:go_default_library",
"//cmd/kubeadm/app/util/kubeconfig:go_default_library",
......
......@@ -57,6 +57,7 @@ import (
"k8s.io/kubernetes/cmd/kubeadm/app/preflight"
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
"k8s.io/kubernetes/cmd/kubeadm/app/util/apiclient"
auditutil "k8s.io/kubernetes/cmd/kubeadm/app/util/audit"
configutil "k8s.io/kubernetes/cmd/kubeadm/app/util/config"
dryrunutil "k8s.io/kubernetes/cmd/kubeadm/app/util/dryrun"
kubeconfigutil "k8s.io/kubernetes/cmd/kubeadm/app/util/kubeconfig"
......@@ -322,6 +323,21 @@ func (i *Init) Run(out io.Writer) error {
fmt.Println("[externalca] The file 'ca.key' was not found, yet all other certificates are present. Using external CA mode - certificates or kubeconfig will not be generated.")
}
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)
}
}
}
// Temporarily set cfg.CertificatesDir to the "real value" when writing controlplane manifests
// This is needed for writing the right kind of manifests
i.cfg.CertificatesDir = realCertsDir
......
......@@ -41,6 +41,9 @@ func TestPrintConfiguration(t *testing.T) {
api:
advertiseAddress: ""
bindPort: 0
auditPolicy:
logDir: ""
path: ""
certificatesDir: ""
cloudProvider: ""
etcd:
......@@ -75,6 +78,9 @@ func TestPrintConfiguration(t *testing.T) {
api:
advertiseAddress: ""
bindPort: 0
auditPolicy:
logDir: ""
path: ""
certificatesDir: ""
cloudProvider: ""
etcd:
......@@ -114,6 +120,9 @@ func TestPrintConfiguration(t *testing.T) {
api:
advertiseAddress: ""
bindPort: 0
auditPolicy:
logDir: ""
path: ""
certificatesDir: ""
cloudProvider: ""
etcd:
......
......@@ -203,6 +203,19 @@ const (
// CRICtlPackage defines the go package that installs crictl
CRICtlPackage = "github.com/kubernetes-incubator/cri-tools/cmd/crictl"
// 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"
// 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"
)
var (
......@@ -311,3 +324,8 @@ func GetDNSIP(svcSubnet string) (net.IP, error) {
return dnsIP, nil
}
// GetStaticPodAuditPolicyFile returns the path to the audit policy file within a static pod
func GetStaticPodAuditPolicyFile() string {
return filepath.Join(KubernetesDir, AuditPolicyDir, AuditPolicyFile)
}
......@@ -41,6 +41,9 @@ const (
// DynamicKubeletConfig is alpha in v1.9
DynamicKubeletConfig = "DynamicKubeletConfig"
// Auditing is beta in 1.8
Auditing = "Auditing"
)
var v190 = version.MustParseSemantic("v1.9.0-alpha.1")
......@@ -53,6 +56,7 @@ var InitFeatureGates = FeatureList{
HighAvailability: {FeatureSpec: utilfeature.FeatureSpec{Default: false, PreRelease: utilfeature.Alpha}, MinimumVersion: v190, HiddenInHelpText: true},
CoreDNS: {FeatureSpec: utilfeature.FeatureSpec{Default: false, PreRelease: utilfeature.Alpha}, MinimumVersion: v190},
DynamicKubeletConfig: {FeatureSpec: utilfeature.FeatureSpec{Default: false, PreRelease: utilfeature.Alpha}, MinimumVersion: v190},
Auditing: {FeatureSpec: utilfeature.FeatureSpec{Default: false, PreRelease: utilfeature.Alpha}},
}
// Feature represents a feature being gated
......
......@@ -21,6 +21,7 @@ go_test(
"//cmd/kubeadm/app/phases/certs:go_default_library",
"//cmd/kubeadm/test:go_default_library",
"//pkg/master/reconcilers:go_default_library",
"//pkg/util/pointer:go_default_library",
"//pkg/util/version:go_default_library",
"//vendor/k8s.io/api/core/v1:go_default_library",
],
......
......@@ -224,6 +224,16 @@ func getAPIServerCommand(cfg *kubeadmapi.MasterConfiguration, k8sVersion *versio
command = append(command, "--feature-gates=DynamicKubeletConfig=true")
}
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))
}
}
return command
}
......
......@@ -32,6 +32,7 @@ import (
"k8s.io/kubernetes/pkg/util/version"
testutil "k8s.io/kubernetes/cmd/kubeadm/test"
utilpointer "k8s.io/kubernetes/pkg/util/pointer"
)
const (
......@@ -338,6 +339,11 @@ func TestGetAPIServerCommand(t *testing.T) {
Etcd: kubeadmapi.Etcd{CertFile: "fiz", KeyFile: "faz"},
CertificatesDir: testCertsDir,
KubernetesVersion: "v1.9.3",
AuditPolicyConfiguration: kubeadmapi.AuditPolicyConfiguration{
Path: "/foo/bar",
LogDir: "/foo/baz",
LogMaxAge: utilpointer.Int32Ptr(10),
}, // ignored without the feature gate
},
expected: []string{
"kube-apiserver",
......@@ -446,9 +452,12 @@ func TestGetAPIServerCommand(t *testing.T) {
cfg: &kubeadmapi.MasterConfiguration{
API: kubeadmapi.API{BindPort: 123, AdvertiseAddress: "2001:db8::1"},
Networking: kubeadmapi.Networking{ServiceSubnet: "bar"},
FeatureGates: map[string]bool{features.HighAvailability: true},
FeatureGates: map[string]bool{features.HighAvailability: true, features.Auditing: true},
CertificatesDir: testCertsDir,
KubernetesVersion: "v1.9.0-beta.0",
AuditPolicyConfiguration: kubeadmapi.AuditPolicyConfiguration{
LogMaxAge: utilpointer.Int32Ptr(0),
},
},
expected: []string{
"kube-apiserver",
......@@ -476,6 +485,9 @@ func TestGetAPIServerCommand(t *testing.T) {
"--advertise-address=2001:db8::1",
"--etcd-servers=http://127.0.0.1:2379",
fmt.Sprintf("--endpoint-reconciler-type=%s", reconcilers.LeaseEndpointReconcilerType),
"--audit-policy-file=/etc/kubernetes/audit/audit.yaml",
"--audit-log-path=/var/log/kubernetes/audit/audit.log",
"--audit-log-maxage=0",
},
},
{
......
......@@ -26,6 +26,7 @@ import (
"k8s.io/apimachinery/pkg/util/sets"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
"k8s.io/kubernetes/cmd/kubeadm/app/features"
staticpodutil "k8s.io/kubernetes/cmd/kubeadm/app/util/staticpod"
)
......@@ -47,6 +48,7 @@ var caCertsPkiVolumePath = "/etc/pki"
func getHostPathVolumesForTheControlPlane(cfg *kubeadmapi.MasterConfiguration) controlPlaneHostPathMounts {
hostPathDirectoryOrCreate := v1.HostPathDirectoryOrCreate
hostPathFileOrCreate := v1.HostPathFileOrCreate
hostPathFile := v1.HostPathFile
mounts := newControlPlaneHostPathMounts()
// HostPath volumes for the API Server
......@@ -55,7 +57,12 @@ func getHostPathVolumesForTheControlPlane(cfg *kubeadmapi.MasterConfiguration) c
mounts.NewHostPathMount(kubeadmconstants.KubeAPIServer, kubeadmconstants.KubeCertificatesVolumeName, cfg.CertificatesDir, cfg.CertificatesDir, true, &hostPathDirectoryOrCreate)
// Read-only mount for the ca certs (/etc/ssl/certs) directory
mounts.NewHostPathMount(kubeadmconstants.KubeAPIServer, caCertsVolumeName, caCertsVolumePath, caCertsVolumePath, true, &hostPathDirectoryOrCreate)
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)
// Write mount for the audit logs.
mounts.NewHostPathMount(kubeadmconstants.KubeAPIServer, kubeadmconstants.KubeAuditPolicyLogVolumeName, cfg.AuditPolicyConfiguration.LogDir, kubeadmconstants.StaticPodAuditPolicyLogDir, false, &hostPathDirectoryOrCreate)
}
// If external etcd is specified, mount the directories needed for accessing the CA/serving certs and the private key
if len(cfg.Etcd.Endpoints) != 0 {
etcdVols, etcdVolMounts := getEtcdCertVolumes(cfg.Etcd, cfg.CertificatesDir)
......
......@@ -26,6 +26,7 @@ import (
"k8s.io/api/core/v1"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
"k8s.io/kubernetes/cmd/kubeadm/app/features"
)
func TestGetEtcdCertVolumes(t *testing.T) {
......@@ -258,6 +259,7 @@ func TestGetEtcdCertVolumes(t *testing.T) {
func TestGetHostPathVolumesForTheControlPlane(t *testing.T) {
hostPathDirectoryOrCreate := v1.HostPathDirectoryOrCreate
hostPathFileOrCreate := v1.HostPathFileOrCreate
hostPathFile := v1.HostPathFile
volMap := make(map[string]map[string]v1.Volume)
volMap[kubeadmconstants.KubeAPIServer] = map[string]v1.Volume{}
volMap[kubeadmconstants.KubeAPIServer]["k8s-certs"] = v1.Volume{
......@@ -278,6 +280,24 @@ func TestGetHostPathVolumesForTheControlPlane(t *testing.T) {
},
},
}
volMap[kubeadmconstants.KubeAPIServer]["audit"] = v1.Volume{
Name: "audit",
VolumeSource: v1.VolumeSource{
HostPath: &v1.HostPathVolumeSource{
Path: "/foo/bar/baz.yaml",
Type: &hostPathFile,
},
},
}
volMap[kubeadmconstants.KubeAPIServer]["audit-log"] = v1.Volume{
Name: "audit-log",
VolumeSource: v1.VolumeSource{
HostPath: &v1.HostPathVolumeSource{
Path: "/bar/foo",
Type: &hostPathDirectoryOrCreate,
},
},
}
volMap[kubeadmconstants.KubeControllerManager] = map[string]v1.Volume{}
volMap[kubeadmconstants.KubeControllerManager]["k8s-certs"] = v1.Volume{
Name: "k8s-certs",
......@@ -328,6 +348,16 @@ func TestGetHostPathVolumesForTheControlPlane(t *testing.T) {
MountPath: "/etc/ssl/certs",
ReadOnly: true,
}
volMountMap[kubeadmconstants.KubeAPIServer]["audit"] = v1.VolumeMount{
Name: "audit",
MountPath: "/etc/kubernetes/audit/audit.yaml",
ReadOnly: true,
}
volMountMap[kubeadmconstants.KubeAPIServer]["audit-log"] = v1.VolumeMount{
Name: "audit-log",
MountPath: "/var/log/kubernetes/audit",
ReadOnly: false,
}
volMountMap[kubeadmconstants.KubeControllerManager] = map[string]v1.VolumeMount{}
volMountMap[kubeadmconstants.KubeControllerManager]["k8s-certs"] = v1.VolumeMount{
Name: "k8s-certs",
......@@ -481,6 +511,11 @@ func TestGetHostPathVolumesForTheControlPlane(t *testing.T) {
cfg: &kubeadmapi.MasterConfiguration{
CertificatesDir: testCertsDir,
Etcd: kubeadmapi.Etcd{},
FeatureGates: map[string]bool{features.Auditing: true},
AuditPolicyConfiguration: kubeadmapi.AuditPolicyConfiguration{
Path: "/foo/bar/baz.yaml",
LogDir: "/bar/foo",
},
},
vol: volMap,
volMount: volMountMap,
......
......@@ -61,6 +61,7 @@ filegroup(
srcs = [
":package-srcs",
"//cmd/kubeadm/app/util/apiclient:all-srcs",
"//cmd/kubeadm/app/util/audit:all-srcs",
"//cmd/kubeadm/app/util/config:all-srcs",
"//cmd/kubeadm/app/util/dryrun:all-srcs",
"//cmd/kubeadm/app/util/kubeconfig:all-srcs",
......
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
go_library(
name = "go_default_library",
srcs = ["utils.go"],
importpath = "k8s.io/kubernetes/cmd/kubeadm/app/util/audit",
visibility = ["//visibility:public"],
deps = [
"//cmd/kubeadm/app/util:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apiserver/pkg/apis/audit/v1beta1:go_default_library",
"//vendor/k8s.io/client-go/kubernetes/scheme:go_default_library",
],
)
go_test(
name = "go_default_test",
srcs = ["utils_test.go"],
embed = [":go_default_library"],
importpath = "k8s.io/kubernetes/cmd/kubeadm/app/util/audit",
deps = [
"//pkg/kubectl/scheme:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//vendor/k8s.io/apiserver/pkg/apis/audit/v1beta1:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
/*
Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package audit
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
auditv1beta1 "k8s.io/apiserver/pkg/apis/audit/v1beta1"
"k8s.io/client-go/kubernetes/scheme"
"k8s.io/kubernetes/cmd/kubeadm/app/util"
)
// 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)
}
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
}
/*
Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package audit
import (
"io/ioutil"
"os"
"path/filepath"
"testing"
"k8s.io/apimachinery/pkg/runtime"
auditv1beta1 "k8s.io/apiserver/pkg/apis/audit/v1beta1"
"k8s.io/kubernetes/pkg/kubectl/scheme"
)
func cleanup(t *testing.T, path string) {
err := os.RemoveAll(path)
if err != nil {
t.Fatalf("Failed to clean up %v: %v", path, err)
}
}
func TestCreateDefaultAuditLogPolicy(t *testing.T) {
// make a tempdir
tempDir, err := ioutil.TempDir("/tmp", "audit-test")
if err != nil {
t.Fatalf("could not create a tempdir: %v", err)
}
defer cleanup(t, tempDir)
auditPolicyFile := filepath.Join(tempDir, "test.yaml")
if err = CreateDefaultAuditLogPolicy(auditPolicyFile); err != nil {
t.Fatalf("failed to create audit log policy: %v", err)
}
// turn the audit log back into a policy
policyBytes, err := ioutil.ReadFile(auditPolicyFile)
if err != nil {
t.Fatalf("failed to read %v: %v", auditPolicyFile, err)
}
policy := auditv1beta1.Policy{}
err = runtime.DecodeInto(scheme.Codecs.UniversalDecoder(), policyBytes, &policy)
if err != nil {
t.Fatalf("failed to decode written policy: %v", err)
}
if policy.Kind != "Policy" {
t.Fatalf("did not decode policy properly")
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment