Skip to content

Commit ea62d9d

Browse files
committed
Enable full advanced audit in origin
1 parent d27faa4 commit ea62d9d

File tree

12 files changed

+147
-4
lines changed

12 files changed

+147
-4
lines changed

contrib/completions/bash/openshift

+4
Original file line numberDiff line numberDiff line change
@@ -32067,6 +32067,8 @@ _openshift_start_kubernetes_apiserver()
3206732067
local_nonpersistent_flags+=("--anonymous-auth")
3206832068
flags+=("--apiserver-count=")
3206932069
local_nonpersistent_flags+=("--apiserver-count=")
32070+
flags+=("--audit-log-format=")
32071+
local_nonpersistent_flags+=("--audit-log-format=")
3207032072
flags+=("--audit-log-maxage=")
3207132073
local_nonpersistent_flags+=("--audit-log-maxage=")
3207232074
flags+=("--audit-log-maxbackup=")
@@ -33079,6 +33081,8 @@ _openshift_start_template-service-broker()
3307933081
flags_with_completion=()
3308033082
flags_completion=()
3308133083

33084+
flags+=("--audit-log-format=")
33085+
local_nonpersistent_flags+=("--audit-log-format=")
3308233086
flags+=("--audit-log-maxage=")
3308333087
local_nonpersistent_flags+=("--audit-log-maxage=")
3308433088
flags+=("--audit-log-maxbackup=")

contrib/completions/zsh/openshift

+4
Original file line numberDiff line numberDiff line change
@@ -32216,6 +32216,8 @@ _openshift_start_kubernetes_apiserver()
3221632216
local_nonpersistent_flags+=("--anonymous-auth")
3221732217
flags+=("--apiserver-count=")
3221832218
local_nonpersistent_flags+=("--apiserver-count=")
32219+
flags+=("--audit-log-format=")
32220+
local_nonpersistent_flags+=("--audit-log-format=")
3221932221
flags+=("--audit-log-maxage=")
3222032222
local_nonpersistent_flags+=("--audit-log-maxage=")
3222132223
flags+=("--audit-log-maxbackup=")
@@ -33228,6 +33230,8 @@ _openshift_start_template-service-broker()
3322833230
flags_with_completion=()
3322933231
flags_completion=()
3323033232

33233+
flags+=("--audit-log-format=")
33234+
local_nonpersistent_flags+=("--audit-log-format=")
3323133235
flags+=("--audit-log-maxage=")
3323233236
local_nonpersistent_flags+=("--audit-log-maxage=")
3323333237
flags+=("--audit-log-maxbackup=")

pkg/cmd/server/api/helpers.go

+1
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,7 @@ func GetMasterFileReferences(config *MasterConfig) []*string {
271271
}
272272

273273
refs = append(refs, &config.AuditConfig.AuditFilePath)
274+
refs = append(refs, &config.AuditConfig.PolicyFile)
274275

275276
return refs
276277
}

pkg/cmd/server/api/install/install.go

+6
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import (
77

88
"k8s.io/apimachinery/pkg/api/meta"
99
"k8s.io/apimachinery/pkg/runtime/schema"
10+
"k8s.io/apiserver/pkg/apis/audit"
11+
auditv1alpha1 "k8s.io/apiserver/pkg/apis/audit/v1alpha1"
1012

1113
configapi "github.com/openshift/origin/pkg/cmd/server/api"
1214
configapiv1 "github.com/openshift/origin/pkg/cmd/server/api/v1"
@@ -55,6 +57,10 @@ func addVersionsToScheme(externalVersions ...schema.GroupVersion) {
5557
continue
5658
}
5759
}
60+
// we additionally need to enable audit versions, since we embed the audit
61+
// policy file inside master-config.yaml
62+
audit.AddToScheme(configapi.Scheme)
63+
auditv1alpha1.AddToScheme(configapi.Scheme)
5864
}
5965

6066
func interfacesFor(version schema.GroupVersion) (*meta.VersionInterfaces, error) {

pkg/cmd/server/api/types.go

+15-1
Original file line numberDiff line numberDiff line change
@@ -464,7 +464,6 @@ type AggregatorConfig struct {
464464
// AuditConfig holds configuration for the audit capabilities
465465
type AuditConfig struct {
466466
// If this flag is set, audit log will be printed in the logs.
467-
// The logs contains, method, user and a requested URL.
468467
Enabled bool
469468
// All requests coming to the apiserver will be logged to this file.
470469
AuditFilePath string
@@ -474,6 +473,21 @@ type AuditConfig struct {
474473
MaximumRetainedFiles int
475474
// Maximum size in megabytes of the log file before it gets rotated. Defaults to 100MB.
476475
MaximumFileSizeMegabytes int
476+
477+
// PolicyFile is a path to the file that defines the audit policy configuration.
478+
PolicyFile string
479+
// PolicyConfiguration is an embedded policy configuration object to be used
480+
// as the audit policy configuration. If present, it will be used instead of
481+
// the path to the policy file.
482+
PolicyConfiguration runtime.Object
483+
484+
// Format of saved audits (legacy or json).
485+
LogFormat string
486+
487+
// Path to a .kubeconfig formatted file that defines the audit webhook configuration.
488+
WebHookKubeConfig string
489+
// Strategy for sending audit events (block or batch).
490+
WebHookMode string
477491
}
478492

479493
// JenkinsPipelineConfig holds configuration for the Jenkins pipeline strategy

pkg/cmd/server/api/v1/conversions.go

+4
Original file line numberDiff line numberDiff line change
@@ -405,6 +405,7 @@ func (c *MasterConfig) DecodeNestedObjects(d runtime.Decoder) error {
405405
apihelpers.DecodeNestedRawExtensionOrUnknown(d, &c.OAuthConfig.IdentityProviders[i].Provider)
406406
}
407407
}
408+
apihelpers.DecodeNestedRawExtensionOrUnknown(d, &c.AuditConfig.PolicyConfiguration)
408409
return nil
409410
}
410411

@@ -434,5 +435,8 @@ func (c *MasterConfig) EncodeNestedObjects(e runtime.Encoder) error {
434435
}
435436
}
436437
}
438+
if err := apihelpers.EncodeNestedRawExtension(e, &c.AuditConfig.PolicyConfiguration); err != nil {
439+
return err
440+
}
437441
return nil
438442
}

pkg/cmd/server/api/v1/swagger_doc.go

+5
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,11 @@ var map_AuditConfig = map[string]string{
9090
"maximumFileRetentionDays": "Maximum number of days to retain old log files based on the timestamp encoded in their filename.",
9191
"maximumRetainedFiles": "Maximum number of old log files to retain.",
9292
"maximumFileSizeMegabytes": "Maximum size in megabytes of the log file before it gets rotated. Defaults to 100MB.",
93+
"policyFile": "PolicyFile is a path to the file that defines the audit policy configuration.",
94+
"policyConfiguration": "PolicyConfiguration is an embedded policy configuration object to be used as the audit policy configuration. If present, it will be used instead of the path to the policy file.",
95+
"logFormat": "Format of saved audits (legacy or json).",
96+
"webHookKubeConfig": "Path to a .kubeconfig formatted file that defines the audit webhook configuration.",
97+
"webHookMode": "Strategy for sending audit events (block or batch).",
9398
}
9499

95100
func (AuditConfig) SwaggerDoc() map[string]string {

pkg/cmd/server/api/v1/types.go

+15
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,21 @@ type AuditConfig struct {
331331
MaximumRetainedFiles int `json:"maximumRetainedFiles"`
332332
// Maximum size in megabytes of the log file before it gets rotated. Defaults to 100MB.
333333
MaximumFileSizeMegabytes int `json:"maximumFileSizeMegabytes"`
334+
335+
// PolicyFile is a path to the file that defines the audit policy configuration.
336+
PolicyFile string `json:"policyFile"`
337+
// PolicyConfiguration is an embedded policy configuration object to be used
338+
// as the audit policy configuration. If present, it will be used instead of
339+
// the path to the policy file.
340+
PolicyConfiguration runtime.RawExtension `json:"policyConfiguration"`
341+
342+
// Format of saved audits (legacy or json).
343+
LogFormat string `json:"logFormat"`
344+
345+
// Path to a .kubeconfig formatted file that defines the audit webhook configuration.
346+
WebHookKubeConfig string `json:"webHookKubeConfig"`
347+
// Strategy for sending audit events (block or batch).
348+
WebHookMode string `json:"webHookMode"`
334349
}
335350

336351
// JenkinsPipelineConfig holds configuration for the Jenkins pipeline strategy

pkg/cmd/server/api/v1/types_test.go

+5
Original file line numberDiff line numberDiff line change
@@ -117,9 +117,14 @@ assetConfig:
117117
auditConfig:
118118
auditFilePath: ""
119119
enabled: false
120+
logFormat: ""
120121
maximumFileRetentionDays: 0
121122
maximumFileSizeMegabytes: 0
122123
maximumRetainedFiles: 0
124+
policyConfiguration: null
125+
policyFile: ""
126+
webHookKubeConfig: ""
127+
webHookMode: ""
123128
authConfig:
124129
requestHeader: null
125130
controllerConfig:

pkg/cmd/server/api/validation/master.go

+55
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,12 @@ import (
1414
"k8s.io/apimachinery/pkg/util/sets"
1515
kuval "k8s.io/apimachinery/pkg/util/validation"
1616
"k8s.io/apimachinery/pkg/util/validation/field"
17+
auditinternal "k8s.io/apiserver/pkg/apis/audit"
18+
auditvalidation "k8s.io/apiserver/pkg/apis/audit/validation"
19+
auditpolicy "k8s.io/apiserver/pkg/audit/policy"
20+
auditlog "k8s.io/apiserver/plugin/pkg/audit/log"
21+
auditwebhook "k8s.io/apiserver/plugin/pkg/audit/webhook"
22+
"k8s.io/client-go/tools/clientcmd"
1723
apiserveroptions "k8s.io/kubernetes/cmd/kube-apiserver/app/options"
1824
kcmoptions "k8s.io/kubernetes/cmd/kube-controller-manager/app/options"
1925
kvalidation "k8s.io/kubernetes/pkg/api/validation"
@@ -238,6 +244,9 @@ func ValidateAggregatorConfig(config api.AggregatorConfig, fldPath *field.Path)
238244

239245
func ValidateAuditConfig(config api.AuditConfig, fldPath *field.Path) ValidationResults {
240246
validationResults := ValidationResults{}
247+
if !config.Enabled {
248+
return validationResults
249+
}
241250

242251
if len(config.AuditFilePath) == 0 {
243252
// for backwards compatibility reasons we can't error this out
@@ -253,6 +262,52 @@ func ValidateAuditConfig(config api.AuditConfig, fldPath *field.Path) Validation
253262
validationResults.AddErrors(field.Invalid(fldPath.Child("maximumFileSizeMegabytes"), config.MaximumFileSizeMegabytes, "must be greater than or equal to 0"))
254263
}
255264

265+
// setting policy file will turn the advanced auditing on
266+
if config.PolicyConfiguration != nil && len(config.PolicyFile) > 0 {
267+
validationResults.AddWarnings(field.Forbidden(fldPath.Child("policyFile"), "both policyFile and policyConfiguration are specified, the latter will take precedence"))
268+
}
269+
if config.PolicyConfiguration != nil || len(config.PolicyFile) > 0 {
270+
if config.PolicyConfiguration == nil {
271+
policy, err := auditpolicy.LoadPolicyFromFile(config.PolicyFile)
272+
if err != nil {
273+
validationResults.AddErrors(field.Invalid(fldPath.Child("policyFile"), config.PolicyFile, err.Error()))
274+
}
275+
if policy == nil || len(policy.Rules) == 0 {
276+
validationResults.AddErrors(field.Invalid(fldPath.Child("policyFile"), config.PolicyFile, "a policy file with 0 policies is not valid"))
277+
}
278+
} else {
279+
policyConfiguration, ok := config.PolicyConfiguration.(*auditinternal.Policy)
280+
if !ok {
281+
validationResults.AddErrors(field.Invalid(fldPath.Child("policyConfiguration"), config.PolicyConfiguration, "must be of type audit/v1alpha1.Policy"))
282+
} else {
283+
if err := auditvalidation.ValidatePolicy(policyConfiguration); err != nil {
284+
validationResults.AddErrors(field.Invalid(fldPath.Child("policyConfiguration"), config.PolicyConfiguration, err.ToAggregate().Error()))
285+
}
286+
if len(policyConfiguration.Rules) == 0 {
287+
validationResults.AddErrors(field.Invalid(fldPath.Child("policyConfiguration"), config.PolicyFile, "a policy configuration with 0 policies is not valid"))
288+
}
289+
}
290+
}
291+
if config.LogFormat != auditlog.FormatLegacy && config.LogFormat != auditlog.FormatJson {
292+
validationResults.AddErrors(field.NotSupported(fldPath.Child("logFormat"), config.LogFormat, auditlog.AllowedFormats))
293+
}
294+
if len(config.AuditFilePath) == 0 {
295+
validationResults.AddErrors(field.Required(fldPath.Child("auditFilePath"), "advanced audit requires a separate log file"))
296+
}
297+
298+
if len(config.WebHookKubeConfig) > 0 {
299+
if config.WebHookMode != auditwebhook.ModeBatch && config.WebHookMode != auditwebhook.ModeBlocking {
300+
validationResults.AddErrors(field.NotSupported(fldPath.Child("webhookMode"), config.WebHookMode, auditwebhook.AllowedModes))
301+
}
302+
loadingRules := clientcmd.NewDefaultClientConfigLoadingRules()
303+
loadingRules.ExplicitPath = config.WebHookKubeConfig
304+
loader := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, &clientcmd.ConfigOverrides{})
305+
if _, err := loader.ClientConfig(); err != nil {
306+
validationResults.AddErrors(field.Invalid(fldPath.Child("webhookConfigFile"), config.WebHookKubeConfig, err.Error()))
307+
}
308+
}
309+
}
310+
256311
return validationResults
257312
}
258313

pkg/cmd/server/kubernetes/master/master_config.go

+30-3
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import (
2929
"k8s.io/apimachinery/pkg/util/sets"
3030
"k8s.io/apiserver/pkg/admission"
3131
auditinternal "k8s.io/apiserver/pkg/apis/audit"
32+
"k8s.io/apiserver/pkg/audit"
3233
auditpolicy "k8s.io/apiserver/pkg/audit/policy"
3334
"k8s.io/apiserver/pkg/authentication/authenticator"
3435
"k8s.io/apiserver/pkg/authorization/authorizer"
@@ -45,6 +46,7 @@ import (
4546
storagefactory "k8s.io/apiserver/pkg/storage/storagebackend/factory"
4647
utilflag "k8s.io/apiserver/pkg/util/flag"
4748
auditlog "k8s.io/apiserver/plugin/pkg/audit/log"
49+
auditwebhook "k8s.io/apiserver/plugin/pkg/audit/webhook"
4850
kapiserveroptions "k8s.io/kubernetes/cmd/kube-apiserver/app/options"
4951
cmapp "k8s.io/kubernetes/cmd/kube-controller-manager/app/options"
5052
kapi "k8s.io/kubernetes/pkg/api"
@@ -177,12 +179,11 @@ func BuildKubeAPIserverOptions(masterConfig configapi.MasterConfig) (*kapiserver
177179
args["feature-gates"] = []string{existing[0] + ",AdvancedAuditing=true"}
178180
} else {
179181
args["feature-gates"] = []string{"AdvancedAuditing=true"}
180-
181182
}
182183
}
183184
// TODO: this should be done in config validation (along with the above) so we can provide
184185
// proper errors
185-
if err := cmdflags.Resolve(masterConfig.KubernetesMasterConfig.APIServerArguments, server.AddFlags); len(err) > 0 {
186+
if err := cmdflags.Resolve(args, server.AddFlags); len(err) > 0 {
186187
return nil, kerrors.NewAggregate(err)
187188
}
188189

@@ -526,12 +527,38 @@ func buildKubeApiserverConfig(
526527
// backwards compatible writer to regular log
527528
writer = cmdutil.NewGLogWriterV(0)
528529
}
529-
genericConfig.AuditBackend = auditlog.NewBackend(writer)
530+
genericConfig.AuditBackend = auditlog.NewBackend(writer, auditlog.FormatLegacy)
530531
genericConfig.AuditPolicyChecker = auditpolicy.NewChecker(&auditinternal.Policy{
531532
// This is for backwards compatibility maintaining the old visibility, ie. just
532533
// raw overview of the requests comming in.
533534
Rules: []auditinternal.PolicyRule{{Level: auditinternal.LevelMetadata}},
534535
})
536+
537+
// when a policy file is defined we enable the advanced auditing
538+
if masterConfig.AuditConfig.PolicyConfiguration != nil || len(masterConfig.AuditConfig.PolicyFile) > 0 {
539+
// policy configuration
540+
if masterConfig.AuditConfig.PolicyConfiguration == nil {
541+
p, _ := auditpolicy.LoadPolicyFromFile(masterConfig.AuditConfig.PolicyFile)
542+
genericConfig.AuditPolicyChecker = auditpolicy.NewChecker(p)
543+
} else if len(masterConfig.AuditConfig.PolicyFile) > 0 {
544+
p := masterConfig.AuditConfig.PolicyConfiguration.(*auditinternal.Policy)
545+
genericConfig.AuditPolicyChecker = auditpolicy.NewChecker(p)
546+
}
547+
548+
// log configuration, only when file path was provided
549+
if len(masterConfig.AuditConfig.AuditFilePath) > 0 {
550+
genericConfig.AuditBackend = auditlog.NewBackend(writer, masterConfig.AuditConfig.LogFormat)
551+
}
552+
553+
// webhook configuration, only when config file was provided
554+
if len(masterConfig.AuditConfig.WebHookKubeConfig) > 0 {
555+
webhook, err := auditwebhook.NewBackend(masterConfig.AuditConfig.WebHookKubeConfig, masterConfig.AuditConfig.WebHookMode)
556+
if err != nil {
557+
glog.Fatalf("Audit webhook initialization failed: %v", err)
558+
}
559+
genericConfig.AuditBackend = audit.Union(genericConfig.AuditBackend, webhook)
560+
}
561+
}
535562
}
536563

537564
kubeApiserverConfig := &master.Config{

pkg/cmd/server/kubernetes/master/master_config_test.go

+3
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,9 @@ func TestAPIServerDefaults(t *testing.T) {
119119
HTTPTimeout: time.Duration(5) * time.Second,
120120
},
121121
Audit: &apiserveroptions.AuditOptions{
122+
LogOptions: apiserveroptions.AuditLogOptions{
123+
Format: "legacy",
124+
},
122125
WebhookOptions: apiserveroptions.AuditWebhookOptions{
123126
Mode: "batch",
124127
},

0 commit comments

Comments
 (0)