mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-22 03:11:40 +00:00
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: Chuck 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. ```
This commit is contained in:
commit
fdeaa8c67a
@ -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
|
||||
}
|
||||
|
@ -43,6 +43,31 @@ func (in *API) DeepCopy() *API {
|
||||
return out
|
||||
}
|
||||
|
||||
// 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
|
||||
@ -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))
|
||||
|
@ -43,6 +43,31 @@ func (in *API) DeepCopy() *API {
|
||||
return out
|
||||
}
|
||||
|
||||
// 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
|
||||
@ -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",
|
||||
|
40
cmd/kubeadm/app/util/audit/BUILD
Normal file
40
cmd/kubeadm/app/util/audit/BUILD
Normal file
@ -0,0 +1,40 @@
|
||||
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"],
|
||||
)
|
67
cmd/kubeadm/app/util/audit/utils.go
Normal file
67
cmd/kubeadm/app/util/audit/utils.go
Normal file
@ -0,0 +1,67 @@
|
||||
/*
|
||||
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
|
||||
}
|
61
cmd/kubeadm/app/util/audit/utils_test.go
Normal file
61
cmd/kubeadm/app/util/audit/utils_test.go
Normal file
@ -0,0 +1,61 @@
|
||||
/*
|
||||
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")
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user