Unverified Commit ed769174 authored by Chuck Ha's avatar Chuck Ha

Enable Audit Logs Behind a Feature Gate

Audit logs are configurable via the MasterConfiguration file. All options are ignored unless the FeatureGate is enabled. Fixes kubernetes/kubeadm#623 Signed-off-by: 's avatarChuck Ha <ha.chuck@gmail.com>
parent 1e1b32bf
...@@ -111,6 +111,11 @@ func Funcs(codecs runtimeserializer.CodecFactory) []interface{} { ...@@ -111,6 +111,11 @@ func Funcs(codecs runtimeserializer.CodecFactory) []interface{} {
ConfigSyncPeriod: metav1.Duration{Duration: 1}, ConfigSyncPeriod: metav1.Duration{Duration: 1},
}, },
} }
obj.AuditPolicyConfiguration = kubeadm.AuditPolicyConfiguration{
Path: "foo",
LogDir: "/foo",
LogMaxAge: utilpointer.Int32Ptr(0),
}
}, },
func(obj *kubeadm.NodeConfiguration, c fuzz.Continue) { func(obj *kubeadm.NodeConfiguration, c fuzz.Continue) {
c.FuzzNoCustom(obj) c.FuzzNoCustom(obj)
......
...@@ -104,6 +104,9 @@ type MasterConfiguration struct { ...@@ -104,6 +104,9 @@ type MasterConfiguration struct {
// used for all control plane components. // used for all control plane components.
UnifiedControlPlaneImage string UnifiedControlPlaneImage string
// AuditPolicyConfiguration defines the options for the api server audit system.
AuditPolicyConfiguration AuditPolicyConfiguration
// FeatureGates enabled by the user. // FeatureGates enabled by the user.
FeatureGates map[string]bool FeatureGates map[string]bool
} }
...@@ -259,3 +262,14 @@ type HostPathMount struct { ...@@ -259,3 +262,14 @@ type HostPathMount struct {
type KubeProxy struct { type KubeProxy struct {
Config *kubeproxyconfigv1alpha1.KubeProxyConfiguration 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.
}
...@@ -69,6 +69,12 @@ const ( ...@@ -69,6 +69,12 @@ const (
KubeproxyKubeConfigFileName = "/var/lib/kube-proxy/kubeconfig.conf" 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 { func addDefaultingFuncs(scheme *runtime.Scheme) error {
return RegisterDefaults(scheme) return RegisterDefaults(scheme)
} }
...@@ -118,6 +124,7 @@ func SetDefaults_MasterConfiguration(obj *MasterConfiguration) { ...@@ -118,6 +124,7 @@ func SetDefaults_MasterConfiguration(obj *MasterConfiguration) {
SetDefaults_KubeletConfiguration(obj) SetDefaults_KubeletConfiguration(obj)
} }
SetDefaults_ProxyConfiguration(obj) SetDefaults_ProxyConfiguration(obj)
SetDefaults_AuditPolicyConfiguration(obj)
} }
// SetDefaults_ProxyConfiguration assigns default values for the Proxy // SetDefaults_ProxyConfiguration assigns default values for the Proxy
...@@ -211,3 +218,13 @@ func SetDefaults_KubeletConfiguration(obj *MasterConfiguration) { ...@@ -211,3 +218,13 @@ func SetDefaults_KubeletConfiguration(obj *MasterConfiguration) {
scheme.Default(obj.KubeletConfiguration.BaseConfig) 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
}
}
...@@ -97,6 +97,9 @@ type MasterConfiguration struct { ...@@ -97,6 +97,9 @@ type MasterConfiguration struct {
// be used for all control plane components. // be used for all control plane components.
UnifiedControlPlaneImage string `json:"unifiedControlPlaneImage"` UnifiedControlPlaneImage string `json:"unifiedControlPlaneImage"`
// AuditPolicyConfiguration defines the options for the api server audit system
AuditPolicyConfiguration AuditPolicyConfiguration `json:"auditPolicy"`
// FeatureGates enabled by the user. // FeatureGates enabled by the user.
FeatureGates map[string]bool `json:"featureGates,omitempty"` FeatureGates map[string]bool `json:"featureGates,omitempty"`
} }
...@@ -240,3 +243,14 @@ type HostPathMount struct { ...@@ -240,3 +243,14 @@ type HostPathMount struct {
type KubeProxy struct { type KubeProxy struct {
Config *kubeproxyconfigv1alpha1.KubeProxyConfiguration `json:"config,omitempty"` 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.
}
...@@ -41,6 +41,8 @@ func RegisterConversions(scheme *runtime.Scheme) error { ...@@ -41,6 +41,8 @@ func RegisterConversions(scheme *runtime.Scheme) error {
return scheme.AddGeneratedConversionFuncs( return scheme.AddGeneratedConversionFuncs(
Convert_v1alpha1_API_To_kubeadm_API, Convert_v1alpha1_API_To_kubeadm_API,
Convert_kubeadm_API_To_v1alpha1_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_v1alpha1_Etcd_To_kubeadm_Etcd,
Convert_kubeadm_Etcd_To_v1alpha1_Etcd, Convert_kubeadm_Etcd_To_v1alpha1_Etcd,
Convert_v1alpha1_HostPathMount_To_kubeadm_HostPathMount, Convert_v1alpha1_HostPathMount_To_kubeadm_HostPathMount,
...@@ -84,6 +86,30 @@ func Convert_kubeadm_API_To_v1alpha1_API(in *kubeadm.API, out *API, s conversion ...@@ -84,6 +86,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) 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 { func autoConvert_v1alpha1_Etcd_To_kubeadm_Etcd(in *Etcd, out *kubeadm.Etcd, s conversion.Scope) error {
out.Endpoints = *(*[]string)(unsafe.Pointer(&in.Endpoints)) out.Endpoints = *(*[]string)(unsafe.Pointer(&in.Endpoints))
out.CAFile = in.CAFile out.CAFile = in.CAFile
...@@ -215,6 +241,9 @@ func autoConvert_v1alpha1_MasterConfiguration_To_kubeadm_MasterConfiguration(in ...@@ -215,6 +241,9 @@ func autoConvert_v1alpha1_MasterConfiguration_To_kubeadm_MasterConfiguration(in
out.CertificatesDir = in.CertificatesDir out.CertificatesDir = in.CertificatesDir
out.ImageRepository = in.ImageRepository out.ImageRepository = in.ImageRepository
out.UnifiedControlPlaneImage = in.UnifiedControlPlaneImage 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)) out.FeatureGates = *(*map[string]bool)(unsafe.Pointer(&in.FeatureGates))
return nil return nil
} }
...@@ -258,6 +287,9 @@ func autoConvert_kubeadm_MasterConfiguration_To_v1alpha1_MasterConfiguration(in ...@@ -258,6 +287,9 @@ func autoConvert_kubeadm_MasterConfiguration_To_v1alpha1_MasterConfiguration(in
out.ImageRepository = in.ImageRepository out.ImageRepository = in.ImageRepository
// INFO: in.CIImageRepository opted out of conversion generation // INFO: in.CIImageRepository opted out of conversion generation
out.UnifiedControlPlaneImage = in.UnifiedControlPlaneImage 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)) out.FeatureGates = *(*map[string]bool)(unsafe.Pointer(&in.FeatureGates))
return nil return nil
} }
......
...@@ -44,6 +44,31 @@ func (in *API) DeepCopy() *API { ...@@ -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. // 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) { func (in *Etcd) DeepCopyInto(out *Etcd) {
*out = *in *out = *in
if in.Endpoints != nil { if in.Endpoints != nil {
...@@ -210,6 +235,7 @@ func (in *MasterConfiguration) DeepCopyInto(out *MasterConfiguration) { ...@@ -210,6 +235,7 @@ func (in *MasterConfiguration) DeepCopyInto(out *MasterConfiguration) {
*out = make([]string, len(*in)) *out = make([]string, len(*in))
copy(*out, *in) copy(*out, *in)
} }
in.AuditPolicyConfiguration.DeepCopyInto(&out.AuditPolicyConfiguration)
if in.FeatureGates != nil { if in.FeatureGates != nil {
in, out := &in.FeatureGates, &out.FeatureGates in, out := &in.FeatureGates, &out.FeatureGates
*out = make(map[string]bool, len(*in)) *out = make(map[string]bool, len(*in))
......
...@@ -44,6 +44,31 @@ func (in *API) DeepCopy() *API { ...@@ -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. // 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) { func (in *Etcd) DeepCopyInto(out *Etcd) {
*out = *in *out = *in
if in.Endpoints != nil { if in.Endpoints != nil {
...@@ -210,6 +235,7 @@ func (in *MasterConfiguration) DeepCopyInto(out *MasterConfiguration) { ...@@ -210,6 +235,7 @@ func (in *MasterConfiguration) DeepCopyInto(out *MasterConfiguration) {
*out = make([]string, len(*in)) *out = make([]string, len(*in))
copy(*out, *in) copy(*out, *in)
} }
in.AuditPolicyConfiguration.DeepCopyInto(&out.AuditPolicyConfiguration)
if in.FeatureGates != nil { if in.FeatureGates != nil {
in, out := &in.FeatureGates, &out.FeatureGates in, out := &in.FeatureGates, &out.FeatureGates
*out = make(map[string]bool, len(*in)) *out = make(map[string]bool, len(*in))
......
...@@ -47,6 +47,7 @@ go_library( ...@@ -47,6 +47,7 @@ go_library(
"//cmd/kubeadm/app/preflight:go_default_library", "//cmd/kubeadm/app/preflight:go_default_library",
"//cmd/kubeadm/app/util:go_default_library", "//cmd/kubeadm/app/util:go_default_library",
"//cmd/kubeadm/app/util/apiclient: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/config:go_default_library",
"//cmd/kubeadm/app/util/dryrun:go_default_library", "//cmd/kubeadm/app/util/dryrun:go_default_library",
"//cmd/kubeadm/app/util/kubeconfig:go_default_library", "//cmd/kubeadm/app/util/kubeconfig:go_default_library",
......
...@@ -56,6 +56,7 @@ import ( ...@@ -56,6 +56,7 @@ import (
"k8s.io/kubernetes/cmd/kubeadm/app/preflight" "k8s.io/kubernetes/cmd/kubeadm/app/preflight"
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util" kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
"k8s.io/kubernetes/cmd/kubeadm/app/util/apiclient" "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" configutil "k8s.io/kubernetes/cmd/kubeadm/app/util/config"
dryrunutil "k8s.io/kubernetes/cmd/kubeadm/app/util/dryrun" dryrunutil "k8s.io/kubernetes/cmd/kubeadm/app/util/dryrun"
kubeconfigutil "k8s.io/kubernetes/cmd/kubeadm/app/util/kubeconfig" kubeconfigutil "k8s.io/kubernetes/cmd/kubeadm/app/util/kubeconfig"
...@@ -319,6 +320,21 @@ func (i *Init) Run(out io.Writer) error { ...@@ -319,6 +320,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.") 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 // Temporarily set cfg.CertificatesDir to the "real value" when writing controlplane manifests
// This is needed for writing the right kind of manifests // This is needed for writing the right kind of manifests
i.cfg.CertificatesDir = realCertsDir i.cfg.CertificatesDir = realCertsDir
......
...@@ -41,6 +41,9 @@ func TestPrintConfiguration(t *testing.T) { ...@@ -41,6 +41,9 @@ func TestPrintConfiguration(t *testing.T) {
api: api:
advertiseAddress: "" advertiseAddress: ""
bindPort: 0 bindPort: 0
auditPolicy:
logDir: ""
path: ""
certificatesDir: "" certificatesDir: ""
cloudProvider: "" cloudProvider: ""
etcd: etcd:
...@@ -75,6 +78,9 @@ func TestPrintConfiguration(t *testing.T) { ...@@ -75,6 +78,9 @@ func TestPrintConfiguration(t *testing.T) {
api: api:
advertiseAddress: "" advertiseAddress: ""
bindPort: 0 bindPort: 0
auditPolicy:
logDir: ""
path: ""
certificatesDir: "" certificatesDir: ""
cloudProvider: "" cloudProvider: ""
etcd: etcd:
...@@ -114,6 +120,9 @@ func TestPrintConfiguration(t *testing.T) { ...@@ -114,6 +120,9 @@ func TestPrintConfiguration(t *testing.T) {
api: api:
advertiseAddress: "" advertiseAddress: ""
bindPort: 0 bindPort: 0
auditPolicy:
logDir: ""
path: ""
certificatesDir: "" certificatesDir: ""
cloudProvider: "" cloudProvider: ""
etcd: etcd:
......
...@@ -203,6 +203,19 @@ const ( ...@@ -203,6 +203,19 @@ const (
// CRICtlPackage defines the go package that installs crictl // CRICtlPackage defines the go package that installs crictl
CRICtlPackage = "github.com/kubernetes-incubator/cri-tools/cmd/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 ( var (
...@@ -311,3 +324,8 @@ func GetDNSIP(svcSubnet string) (net.IP, error) { ...@@ -311,3 +324,8 @@ func GetDNSIP(svcSubnet string) (net.IP, error) {
return dnsIP, nil 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 ( ...@@ -41,6 +41,9 @@ const (
// DynamicKubeletConfig is alpha in v1.9 // DynamicKubeletConfig is alpha in v1.9
DynamicKubeletConfig = "DynamicKubeletConfig" DynamicKubeletConfig = "DynamicKubeletConfig"
// Auditing is beta in 1.8
Auditing = "Auditing"
) )
var v190 = version.MustParseSemantic("v1.9.0-alpha.1") var v190 = version.MustParseSemantic("v1.9.0-alpha.1")
...@@ -53,6 +56,7 @@ var InitFeatureGates = FeatureList{ ...@@ -53,6 +56,7 @@ var InitFeatureGates = FeatureList{
HighAvailability: {FeatureSpec: utilfeature.FeatureSpec{Default: false, PreRelease: utilfeature.Alpha}, MinimumVersion: v190, HiddenInHelpText: true}, HighAvailability: {FeatureSpec: utilfeature.FeatureSpec{Default: false, PreRelease: utilfeature.Alpha}, MinimumVersion: v190, HiddenInHelpText: true},
CoreDNS: {FeatureSpec: utilfeature.FeatureSpec{Default: false, PreRelease: utilfeature.Alpha}, MinimumVersion: v190}, CoreDNS: {FeatureSpec: utilfeature.FeatureSpec{Default: false, PreRelease: utilfeature.Alpha}, MinimumVersion: v190},
DynamicKubeletConfig: {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 // Feature represents a feature being gated
......
...@@ -21,6 +21,7 @@ go_test( ...@@ -21,6 +21,7 @@ go_test(
"//cmd/kubeadm/app/phases/certs:go_default_library", "//cmd/kubeadm/app/phases/certs:go_default_library",
"//cmd/kubeadm/test:go_default_library", "//cmd/kubeadm/test:go_default_library",
"//pkg/master/reconcilers:go_default_library", "//pkg/master/reconcilers:go_default_library",
"//pkg/util/pointer:go_default_library",
"//pkg/util/version:go_default_library", "//pkg/util/version:go_default_library",
"//vendor/k8s.io/api/core/v1:go_default_library", "//vendor/k8s.io/api/core/v1:go_default_library",
], ],
......
...@@ -221,6 +221,16 @@ func getAPIServerCommand(cfg *kubeadmapi.MasterConfiguration, k8sVersion *versio ...@@ -221,6 +221,16 @@ func getAPIServerCommand(cfg *kubeadmapi.MasterConfiguration, k8sVersion *versio
command = append(command, "--feature-gates=DynamicKubeletConfig=true") 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 return command
} }
......
...@@ -32,6 +32,7 @@ import ( ...@@ -32,6 +32,7 @@ import (
"k8s.io/kubernetes/pkg/util/version" "k8s.io/kubernetes/pkg/util/version"
testutil "k8s.io/kubernetes/cmd/kubeadm/test" testutil "k8s.io/kubernetes/cmd/kubeadm/test"
utilpointer "k8s.io/kubernetes/pkg/util/pointer"
) )
const ( const (
...@@ -338,6 +339,11 @@ func TestGetAPIServerCommand(t *testing.T) { ...@@ -338,6 +339,11 @@ func TestGetAPIServerCommand(t *testing.T) {
Etcd: kubeadmapi.Etcd{CertFile: "fiz", KeyFile: "faz"}, Etcd: kubeadmapi.Etcd{CertFile: "fiz", KeyFile: "faz"},
CertificatesDir: testCertsDir, CertificatesDir: testCertsDir,
KubernetesVersion: "v1.9.3", KubernetesVersion: "v1.9.3",
AuditPolicyConfiguration: kubeadmapi.AuditPolicyConfiguration{
Path: "/foo/bar",
LogDir: "/foo/baz",
LogMaxAge: utilpointer.Int32Ptr(10),
}, // ignored without the feature gate
}, },
expected: []string{ expected: []string{
"kube-apiserver", "kube-apiserver",
...@@ -446,9 +452,12 @@ func TestGetAPIServerCommand(t *testing.T) { ...@@ -446,9 +452,12 @@ func TestGetAPIServerCommand(t *testing.T) {
cfg: &kubeadmapi.MasterConfiguration{ cfg: &kubeadmapi.MasterConfiguration{
API: kubeadmapi.API{BindPort: 123, AdvertiseAddress: "2001:db8::1"}, API: kubeadmapi.API{BindPort: 123, AdvertiseAddress: "2001:db8::1"},
Networking: kubeadmapi.Networking{ServiceSubnet: "bar"}, Networking: kubeadmapi.Networking{ServiceSubnet: "bar"},
FeatureGates: map[string]bool{features.HighAvailability: true}, FeatureGates: map[string]bool{features.HighAvailability: true, features.Auditing: true},
CertificatesDir: testCertsDir, CertificatesDir: testCertsDir,
KubernetesVersion: "v1.9.0-beta.0", KubernetesVersion: "v1.9.0-beta.0",
AuditPolicyConfiguration: kubeadmapi.AuditPolicyConfiguration{
LogMaxAge: utilpointer.Int32Ptr(0),
},
}, },
expected: []string{ expected: []string{
"kube-apiserver", "kube-apiserver",
...@@ -476,6 +485,9 @@ func TestGetAPIServerCommand(t *testing.T) { ...@@ -476,6 +485,9 @@ func TestGetAPIServerCommand(t *testing.T) {
"--advertise-address=2001:db8::1", "--advertise-address=2001:db8::1",
"--etcd-servers=http://127.0.0.1:2379", "--etcd-servers=http://127.0.0.1:2379",
fmt.Sprintf("--endpoint-reconciler-type=%s", reconcilers.LeaseEndpointReconcilerType), 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 ( ...@@ -26,6 +26,7 @@ import (
"k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/sets"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants" kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
"k8s.io/kubernetes/cmd/kubeadm/app/features"
staticpodutil "k8s.io/kubernetes/cmd/kubeadm/app/util/staticpod" staticpodutil "k8s.io/kubernetes/cmd/kubeadm/app/util/staticpod"
) )
...@@ -47,6 +48,7 @@ var caCertsPkiVolumePath = "/etc/pki" ...@@ -47,6 +48,7 @@ var caCertsPkiVolumePath = "/etc/pki"
func getHostPathVolumesForTheControlPlane(cfg *kubeadmapi.MasterConfiguration) controlPlaneHostPathMounts { func getHostPathVolumesForTheControlPlane(cfg *kubeadmapi.MasterConfiguration) controlPlaneHostPathMounts {
hostPathDirectoryOrCreate := v1.HostPathDirectoryOrCreate hostPathDirectoryOrCreate := v1.HostPathDirectoryOrCreate
hostPathFileOrCreate := v1.HostPathFileOrCreate hostPathFileOrCreate := v1.HostPathFileOrCreate
hostPathFile := v1.HostPathFile
mounts := newControlPlaneHostPathMounts() mounts := newControlPlaneHostPathMounts()
// HostPath volumes for the API Server // HostPath volumes for the API Server
...@@ -55,7 +57,12 @@ func getHostPathVolumesForTheControlPlane(cfg *kubeadmapi.MasterConfiguration) c ...@@ -55,7 +57,12 @@ func getHostPathVolumesForTheControlPlane(cfg *kubeadmapi.MasterConfiguration) c
mounts.NewHostPathMount(kubeadmconstants.KubeAPIServer, kubeadmconstants.KubeCertificatesVolumeName, cfg.CertificatesDir, cfg.CertificatesDir, true, &hostPathDirectoryOrCreate) mounts.NewHostPathMount(kubeadmconstants.KubeAPIServer, kubeadmconstants.KubeCertificatesVolumeName, cfg.CertificatesDir, cfg.CertificatesDir, true, &hostPathDirectoryOrCreate)
// Read-only mount for the ca certs (/etc/ssl/certs) directory // Read-only mount for the ca certs (/etc/ssl/certs) directory
mounts.NewHostPathMount(kubeadmconstants.KubeAPIServer, caCertsVolumeName, caCertsVolumePath, caCertsVolumePath, true, &hostPathDirectoryOrCreate) 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 external etcd is specified, mount the directories needed for accessing the CA/serving certs and the private key
if len(cfg.Etcd.Endpoints) != 0 { if len(cfg.Etcd.Endpoints) != 0 {
etcdVols, etcdVolMounts := getEtcdCertVolumes(cfg.Etcd, cfg.CertificatesDir) etcdVols, etcdVolMounts := getEtcdCertVolumes(cfg.Etcd, cfg.CertificatesDir)
......
...@@ -26,6 +26,7 @@ import ( ...@@ -26,6 +26,7 @@ import (
"k8s.io/api/core/v1" "k8s.io/api/core/v1"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants" kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
"k8s.io/kubernetes/cmd/kubeadm/app/features"
) )
func TestGetEtcdCertVolumes(t *testing.T) { func TestGetEtcdCertVolumes(t *testing.T) {
...@@ -258,6 +259,7 @@ func TestGetEtcdCertVolumes(t *testing.T) { ...@@ -258,6 +259,7 @@ func TestGetEtcdCertVolumes(t *testing.T) {
func TestGetHostPathVolumesForTheControlPlane(t *testing.T) { func TestGetHostPathVolumesForTheControlPlane(t *testing.T) {
hostPathDirectoryOrCreate := v1.HostPathDirectoryOrCreate hostPathDirectoryOrCreate := v1.HostPathDirectoryOrCreate
hostPathFileOrCreate := v1.HostPathFileOrCreate hostPathFileOrCreate := v1.HostPathFileOrCreate
hostPathFile := v1.HostPathFile
volMap := make(map[string]map[string]v1.Volume) volMap := make(map[string]map[string]v1.Volume)
volMap[kubeadmconstants.KubeAPIServer] = map[string]v1.Volume{} volMap[kubeadmconstants.KubeAPIServer] = map[string]v1.Volume{}
volMap[kubeadmconstants.KubeAPIServer]["k8s-certs"] = v1.Volume{ volMap[kubeadmconstants.KubeAPIServer]["k8s-certs"] = v1.Volume{
...@@ -278,6 +280,24 @@ func TestGetHostPathVolumesForTheControlPlane(t *testing.T) { ...@@ -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] = map[string]v1.Volume{}
volMap[kubeadmconstants.KubeControllerManager]["k8s-certs"] = v1.Volume{ volMap[kubeadmconstants.KubeControllerManager]["k8s-certs"] = v1.Volume{
Name: "k8s-certs", Name: "k8s-certs",
...@@ -328,6 +348,16 @@ func TestGetHostPathVolumesForTheControlPlane(t *testing.T) { ...@@ -328,6 +348,16 @@ func TestGetHostPathVolumesForTheControlPlane(t *testing.T) {
MountPath: "/etc/ssl/certs", MountPath: "/etc/ssl/certs",
ReadOnly: true, 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] = map[string]v1.VolumeMount{}
volMountMap[kubeadmconstants.KubeControllerManager]["k8s-certs"] = v1.VolumeMount{ volMountMap[kubeadmconstants.KubeControllerManager]["k8s-certs"] = v1.VolumeMount{
Name: "k8s-certs", Name: "k8s-certs",
...@@ -481,6 +511,11 @@ func TestGetHostPathVolumesForTheControlPlane(t *testing.T) { ...@@ -481,6 +511,11 @@ func TestGetHostPathVolumesForTheControlPlane(t *testing.T) {
cfg: &kubeadmapi.MasterConfiguration{ cfg: &kubeadmapi.MasterConfiguration{
CertificatesDir: testCertsDir, CertificatesDir: testCertsDir,
Etcd: kubeadmapi.Etcd{}, Etcd: kubeadmapi.Etcd{},
FeatureGates: map[string]bool{features.Auditing: true},
AuditPolicyConfiguration: kubeadmapi.AuditPolicyConfiguration{
Path: "/foo/bar/baz.yaml",
LogDir: "/bar/foo",
},
}, },
vol: volMap, vol: volMap,
volMount: volMountMap, volMount: volMountMap,
......
...@@ -61,6 +61,7 @@ filegroup( ...@@ -61,6 +61,7 @@ filegroup(
srcs = [ srcs = [
":package-srcs", ":package-srcs",
"//cmd/kubeadm/app/util/apiclient:all-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/config:all-srcs",
"//cmd/kubeadm/app/util/dryrun:all-srcs", "//cmd/kubeadm/app/util/dryrun:all-srcs",
"//cmd/kubeadm/app/util/kubeconfig: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