mirror of https://github.com/k3s-io/k3s
Remove deprecated legacy audit logging code.
parent
d017bebf6b
commit
3f730d4c25
|
@ -392,10 +392,6 @@ METADATA_CLOBBERS_CONFIG="${METADATA_CLOBBERS_CONFIG:-false}"
|
|||
|
||||
ENABLE_BIG_CLUSTER_SUBNETS="${ENABLE_BIG_CLUSTER_SUBNETS:-false}"
|
||||
|
||||
if [[ "${ENABLE_APISERVER_BASIC_AUDIT:-}" == "true" ]]; then
|
||||
echo "Warning: Basic audit logging is deprecated and will be removed. Please use advanced auditing instead."
|
||||
fi
|
||||
|
||||
if [[ -n "${LOGROTATE_FILES_MAX_COUNT:-}" ]]; then
|
||||
PROVIDER_VARS="${PROVIDER_VARS:-} LOGROTATE_FILES_MAX_COUNT"
|
||||
fi
|
||||
|
|
|
@ -406,10 +406,6 @@ ENABLE_LEGACY_ABAC="${ENABLE_LEGACY_ABAC:-false}" # true, false
|
|||
ENABLE_APISERVER_ADVANCED_AUDIT="${ENABLE_APISERVER_ADVANCED_AUDIT:-true}" # true, false
|
||||
ADVANCED_AUDIT_LOG_MODE="${ADVANCED_AUDIT_LOG_MODE:-batch}" # batch, blocking
|
||||
|
||||
if [[ "${ENABLE_APISERVER_BASIC_AUDIT:-}" == "true" ]]; then
|
||||
echo "Warning: Basic audit logging is deprecated and will be removed. Please use advanced auditing instead."
|
||||
fi
|
||||
|
||||
ENABLE_BIG_CLUSTER_SUBNETS="${ENABLE_BIG_CLUSTER_SUBNETS:-false}"
|
||||
|
||||
if [[ -n "${LOGROTATE_FILES_MAX_COUNT:-}" ]]; then
|
||||
|
|
|
@ -1526,26 +1526,7 @@ function start-kube-apiserver {
|
|||
local audit_policy_config_volume=""
|
||||
local audit_webhook_config_mount=""
|
||||
local audit_webhook_config_volume=""
|
||||
if [[ "${ENABLE_APISERVER_BASIC_AUDIT:-}" == "true" ]]; then
|
||||
# We currently only support enabling with a fixed path and with built-in log
|
||||
# rotation "disabled" (large value) so it behaves like kube-apiserver.log.
|
||||
# External log rotation should be set up the same as for kube-apiserver.log.
|
||||
params+=" --audit-log-path=/var/log/kube-apiserver-audit.log"
|
||||
params+=" --audit-log-maxage=0"
|
||||
params+=" --audit-log-maxbackup=0"
|
||||
# Lumberjack doesn't offer any way to disable size-based rotation. It also
|
||||
# has an in-memory counter that doesn't notice if you truncate the file.
|
||||
# 2000000000 (in MiB) is a large number that fits in 31 bits. If the log
|
||||
# grows at 10MiB/s (~30K QPS), it will rotate after ~6 years if apiserver
|
||||
# never restarts. Please manually restart apiserver before this time.
|
||||
params+=" --audit-log-maxsize=2000000000"
|
||||
# Disable AdvancedAuditing enabled by default
|
||||
if [[ -z "${FEATURE_GATES:-}" ]]; then
|
||||
FEATURE_GATES="AdvancedAuditing=false"
|
||||
else
|
||||
FEATURE_GATES="${FEATURE_GATES},AdvancedAuditing=false"
|
||||
fi
|
||||
elif [[ "${ENABLE_APISERVER_ADVANCED_AUDIT:-}" == "true" ]]; then
|
||||
if [[ "${ENABLE_APISERVER_ADVANCED_AUDIT:-}" == "true" ]]; then
|
||||
local -r audit_policy_file="/etc/audit_policy.config"
|
||||
params+=" --audit-policy-file=${audit_policy_file}"
|
||||
# Create the audit policy file, and mount it into the apiserver pod.
|
||||
|
|
|
@ -873,7 +873,6 @@ KUBE_ADDON_REGISTRY: $(yaml-quote ${KUBE_ADDON_REGISTRY:-})
|
|||
MULTIZONE: $(yaml-quote ${MULTIZONE:-})
|
||||
NON_MASQUERADE_CIDR: $(yaml-quote ${NON_MASQUERADE_CIDR:-})
|
||||
ENABLE_DEFAULT_STORAGE_CLASS: $(yaml-quote ${ENABLE_DEFAULT_STORAGE_CLASS:-})
|
||||
ENABLE_APISERVER_BASIC_AUDIT: $(yaml-quote ${ENABLE_APISERVER_BASIC_AUDIT:-})
|
||||
ENABLE_APISERVER_ADVANCED_AUDIT: $(yaml-quote ${ENABLE_APISERVER_ADVANCED_AUDIT:-})
|
||||
ENABLE_CACHE_MUTATION_DETECTOR: $(yaml-quote ${ENABLE_CACHE_MUTATION_DETECTOR:-false})
|
||||
ENABLE_PATCH_CONVERSION_DETECTOR: $(yaml-quote ${ENABLE_PATCH_CONVERSION_DETECTOR:-false})
|
||||
|
|
|
@ -84,9 +84,6 @@ ENABLE_POD_PRIORITY_PREEMPTION=${ENABLE_POD_PRIORITY_PREEMPTION:-""}
|
|||
# enable kubernetes dashboard
|
||||
ENABLE_CLUSTER_DASHBOARD=${KUBE_ENABLE_CLUSTER_DASHBOARD:-false}
|
||||
|
||||
# enable audit log
|
||||
ENABLE_APISERVER_BASIC_AUDIT=${ENABLE_APISERVER_BASIC_AUDIT:-false}
|
||||
|
||||
# RBAC Mode options
|
||||
AUTHORIZATION_MODE=${AUTHORIZATION_MODE:-"Node,RBAC"}
|
||||
KUBECONFIG_TOKEN=${KUBECONFIG_TOKEN:-""}
|
||||
|
@ -477,24 +474,6 @@ function start_apiserver {
|
|||
# The order defined here dose not matter.
|
||||
ENABLE_ADMISSION_PLUGINS=LimitRanger,ServiceAccount${security_admission},DefaultStorageClass,DefaultTolerationSeconds,MutatingAdmissionWebhook,ValidatingAdmissionWebhook,ResourceQuota,StorageObjectInUseProtection
|
||||
|
||||
audit_arg=""
|
||||
APISERVER_BASIC_AUDIT_LOG=""
|
||||
if [[ "${ENABLE_APISERVER_BASIC_AUDIT:-}" = true ]]; then
|
||||
# We currently only support enabling with a fixed path and with built-in log
|
||||
# rotation "disabled" (large value) so it behaves like kube-apiserver.log.
|
||||
# External log rotation should be set up the same as for kube-apiserver.log.
|
||||
APISERVER_BASIC_AUDIT_LOG=/tmp/kube-apiserver-audit.log
|
||||
audit_arg=" --audit-log-path=${APISERVER_BASIC_AUDIT_LOG}"
|
||||
audit_arg+=" --audit-log-maxage=0"
|
||||
audit_arg+=" --audit-log-maxbackup=0"
|
||||
# Lumberjack doesn't offer any way to disable size-based rotation. It also
|
||||
# has an in-memory counter that doesn't notice if you truncate the file.
|
||||
# 2000000000 (in MiB) is a large number that fits in 31 bits. If the log
|
||||
# grows at 10MiB/s (~30K QPS), it will rotate after ~6 years if apiserver
|
||||
# never restarts. Please manually restart apiserver before this time.
|
||||
audit_arg+=" --audit-log-maxsize=2000000000"
|
||||
fi
|
||||
|
||||
swagger_arg=""
|
||||
if [[ "${ENABLE_SWAGGER_UI}" = true ]]; then
|
||||
swagger_arg="--enable-swagger-ui=true "
|
||||
|
@ -572,7 +551,7 @@ function start_apiserver {
|
|||
fi
|
||||
|
||||
APISERVER_LOG=${LOG_DIR}/kube-apiserver.log
|
||||
${CONTROLPLANE_SUDO} "${GO_OUT}/hyperkube" apiserver ${swagger_arg} ${audit_arg} ${authorizer_arg} ${priv_arg} ${runtime_config} \
|
||||
${CONTROLPLANE_SUDO} "${GO_OUT}/hyperkube" apiserver ${swagger_arg} ${authorizer_arg} ${priv_arg} ${runtime_config} \
|
||||
${cloud_config_arg} \
|
||||
${advertise_address} \
|
||||
${node_port_range} \
|
||||
|
@ -969,10 +948,6 @@ Logs:
|
|||
EOF
|
||||
fi
|
||||
|
||||
if [[ "${ENABLE_APISERVER_BASIC_AUDIT:-}" = true ]]; then
|
||||
echo " ${APISERVER_BASIC_AUDIT_LOG}"
|
||||
fi
|
||||
|
||||
if [[ "${START_MODE}" == "all" ]]; then
|
||||
echo " ${KUBELET_LOG}"
|
||||
elif [[ "${START_MODE}" == "nokubelet" ]]; then
|
||||
|
|
|
@ -406,7 +406,7 @@ var defaultKubernetesFeatureGates = map[utilfeature.Feature]utilfeature.FeatureS
|
|||
// inherited features from generic apiserver, relisted here to get a conflict if it is changed
|
||||
// unintentionally on either side:
|
||||
genericfeatures.StreamingProxyRedirects: {Default: true, PreRelease: utilfeature.Beta},
|
||||
genericfeatures.AdvancedAuditing: {Default: true, PreRelease: utilfeature.Beta},
|
||||
genericfeatures.AdvancedAuditing: {Default: true, PreRelease: utilfeature.GA},
|
||||
genericfeatures.APIResponseCompression: {Default: false, PreRelease: utilfeature.Alpha},
|
||||
genericfeatures.Initializers: {Default: false, PreRelease: utilfeature.Alpha},
|
||||
genericfeatures.APIListChunking: {Default: true, PreRelease: utilfeature.Beta},
|
||||
|
|
|
@ -12,10 +12,8 @@ go_library(
|
|||
deps = [
|
||||
"//staging/src/k8s.io/apiserver/pkg/authentication/user:go_default_library",
|
||||
"//staging/src/k8s.io/apiserver/pkg/endpoints/filters:go_default_library",
|
||||
"//staging/src/k8s.io/apiserver/pkg/features:go_default_library",
|
||||
"//staging/src/k8s.io/apiserver/pkg/server:go_default_library",
|
||||
"//staging/src/k8s.io/apiserver/pkg/server/filters:go_default_library",
|
||||
"//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
|
|
|
@ -21,10 +21,8 @@ import (
|
|||
|
||||
"k8s.io/apiserver/pkg/authentication/user"
|
||||
genericapifilters "k8s.io/apiserver/pkg/endpoints/filters"
|
||||
"k8s.io/apiserver/pkg/features"
|
||||
"k8s.io/apiserver/pkg/server"
|
||||
genericfilters "k8s.io/apiserver/pkg/server/filters"
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
)
|
||||
|
||||
// DeprecatedInsecureServingInfo is required to serve http. HTTP does NOT include authentication or authorization.
|
||||
|
@ -33,11 +31,7 @@ import (
|
|||
|
||||
func BuildInsecureHandlerChain(apiHandler http.Handler, c *server.Config) http.Handler {
|
||||
handler := apiHandler
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.AdvancedAuditing) {
|
||||
handler = genericapifilters.WithAudit(handler, c.AuditBackend, c.AuditPolicyChecker, c.LongRunningFunc)
|
||||
} else {
|
||||
handler = genericapifilters.WithLegacyAudit(handler, c.LegacyAuditWriter)
|
||||
}
|
||||
handler = genericapifilters.WithAudit(handler, c.AuditBackend, c.AuditPolicyChecker, c.LongRunningFunc)
|
||||
handler = genericapifilters.WithAuthentication(handler, insecureSuperuser{}, nil)
|
||||
handler = genericfilters.WithCORS(handler, c.CorsAllowedOriginList, nil, nil, nil, "true")
|
||||
handler = genericfilters.WithTimeoutForNonLongRunningRequests(handler, c.LongRunningFunc, c.RequestTimeout)
|
||||
|
|
|
@ -14,7 +14,6 @@ go_test(
|
|||
"authn_audit_test.go",
|
||||
"authorization_test.go",
|
||||
"impersonation_test.go",
|
||||
"legacy_audit_test.go",
|
||||
"requestinfo_test.go",
|
||||
],
|
||||
embed = [":go_default_library"],
|
||||
|
@ -47,7 +46,6 @@ go_library(
|
|||
"authorization.go",
|
||||
"doc.go",
|
||||
"impersonation.go",
|
||||
"legacy_audit.go",
|
||||
"requestinfo.go",
|
||||
],
|
||||
importmap = "k8s.io/kubernetes/vendor/k8s.io/apiserver/pkg/endpoints/filters",
|
||||
|
@ -59,7 +57,6 @@ go_library(
|
|||
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/util/net:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/util/runtime:go_default_library",
|
||||
"//staging/src/k8s.io/apiserver/pkg/apis/audit:go_default_library",
|
||||
"//staging/src/k8s.io/apiserver/pkg/audit:go_default_library",
|
||||
|
@ -72,7 +69,6 @@ go_library(
|
|||
"//staging/src/k8s.io/apiserver/pkg/endpoints/request:go_default_library",
|
||||
"//staging/src/k8s.io/apiserver/pkg/server/httplog:go_default_library",
|
||||
"//vendor/github.com/golang/glog:go_default_library",
|
||||
"//vendor/github.com/pborman/uuid:go_default_library",
|
||||
"//vendor/github.com/prometheus/client_golang/prometheus:go_default_library",
|
||||
],
|
||||
)
|
||||
|
|
|
@ -1,161 +0,0 @@
|
|||
/*
|
||||
Copyright 2016 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 filters
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"github.com/pborman/uuid"
|
||||
|
||||
authenticationapi "k8s.io/api/authentication/v1"
|
||||
utilnet "k8s.io/apimachinery/pkg/util/net"
|
||||
"k8s.io/apiserver/pkg/endpoints/handlers/responsewriters"
|
||||
)
|
||||
|
||||
var _ http.ResponseWriter = &legacyAuditResponseWriter{}
|
||||
|
||||
type legacyAuditResponseWriter struct {
|
||||
http.ResponseWriter
|
||||
out io.Writer
|
||||
id string
|
||||
}
|
||||
|
||||
func (a *legacyAuditResponseWriter) printResponse(code int) {
|
||||
line := fmt.Sprintf("%s AUDIT: id=%q response=\"%d\"\n", time.Now().Format(time.RFC3339Nano), a.id, code)
|
||||
if _, err := fmt.Fprint(a.out, line); err != nil {
|
||||
glog.Errorf("Unable to write audit log: %s, the error is: %v", line, err)
|
||||
}
|
||||
}
|
||||
|
||||
func (a *legacyAuditResponseWriter) WriteHeader(code int) {
|
||||
a.printResponse(code)
|
||||
a.ResponseWriter.WriteHeader(code)
|
||||
}
|
||||
|
||||
// fancyLegacyResponseWriterDelegator implements http.CloseNotifier, http.Flusher and
|
||||
// http.Hijacker which are needed to make certain http operation (e.g. watch, rsh, etc)
|
||||
// working.
|
||||
type fancyLegacyResponseWriterDelegator struct {
|
||||
*legacyAuditResponseWriter
|
||||
}
|
||||
|
||||
func (f *fancyLegacyResponseWriterDelegator) CloseNotify() <-chan bool {
|
||||
return f.ResponseWriter.(http.CloseNotifier).CloseNotify()
|
||||
}
|
||||
|
||||
func (f *fancyLegacyResponseWriterDelegator) Flush() {
|
||||
f.ResponseWriter.(http.Flusher).Flush()
|
||||
}
|
||||
|
||||
func (f *fancyLegacyResponseWriterDelegator) Hijack() (net.Conn, *bufio.ReadWriter, error) {
|
||||
// fake a response status before protocol switch happens
|
||||
f.printResponse(http.StatusSwitchingProtocols)
|
||||
return f.ResponseWriter.(http.Hijacker).Hijack()
|
||||
}
|
||||
|
||||
var _ http.CloseNotifier = &fancyLegacyResponseWriterDelegator{}
|
||||
var _ http.Flusher = &fancyLegacyResponseWriterDelegator{}
|
||||
var _ http.Hijacker = &fancyLegacyResponseWriterDelegator{}
|
||||
|
||||
// WithLegacyAudit decorates a http.Handler with audit logging information for all the
|
||||
// requests coming to the server. If out is nil, no decoration takes place.
|
||||
// Each audit log contains two entries:
|
||||
// 1. the request line containing:
|
||||
// - unique id allowing to match the response line (see 2)
|
||||
// - source ip of the request
|
||||
// - HTTP method being invoked
|
||||
// - original user invoking the operation
|
||||
// - original user's groups info
|
||||
// - impersonated user for the operation
|
||||
// - impersonated groups info
|
||||
// - namespace of the request or <none>
|
||||
// - uri is the full URI as requested
|
||||
// 2. the response line containing:
|
||||
// - the unique id from 1
|
||||
// - response code
|
||||
func WithLegacyAudit(handler http.Handler, out io.Writer) http.Handler {
|
||||
if out == nil {
|
||||
return handler
|
||||
}
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
ctx := req.Context()
|
||||
attribs, err := GetAuthorizerAttributes(ctx)
|
||||
if err != nil {
|
||||
responsewriters.InternalError(w, req, err)
|
||||
return
|
||||
}
|
||||
|
||||
username := "<none>"
|
||||
groups := "<none>"
|
||||
if attribs.GetUser() != nil {
|
||||
username = attribs.GetUser().GetName()
|
||||
if userGroups := attribs.GetUser().GetGroups(); len(userGroups) > 0 {
|
||||
groups = auditStringSlice(userGroups)
|
||||
}
|
||||
}
|
||||
asuser := req.Header.Get(authenticationapi.ImpersonateUserHeader)
|
||||
if len(asuser) == 0 {
|
||||
asuser = "<self>"
|
||||
}
|
||||
asgroups := "<lookup>"
|
||||
requestedGroups := req.Header[authenticationapi.ImpersonateGroupHeader]
|
||||
if len(requestedGroups) > 0 {
|
||||
asgroups = auditStringSlice(requestedGroups)
|
||||
}
|
||||
namespace := attribs.GetNamespace()
|
||||
if len(namespace) == 0 {
|
||||
namespace = "<none>"
|
||||
}
|
||||
id := uuid.NewRandom().String()
|
||||
|
||||
line := fmt.Sprintf("%s AUDIT: id=%q ip=%q method=%q user=%q groups=%q as=%q asgroups=%q namespace=%q uri=%q\n",
|
||||
time.Now().Format(time.RFC3339Nano), id, utilnet.GetClientIP(req), req.Method, username, groups, asuser, asgroups, namespace, req.URL)
|
||||
if _, err := fmt.Fprint(out, line); err != nil {
|
||||
glog.Errorf("Unable to write audit log: %s, the error is: %v", line, err)
|
||||
}
|
||||
respWriter := legacyDecorateResponseWriter(w, out, id)
|
||||
handler.ServeHTTP(respWriter, req)
|
||||
})
|
||||
}
|
||||
|
||||
func auditStringSlice(inList []string) string {
|
||||
quotedElements := make([]string, len(inList))
|
||||
for i, in := range inList {
|
||||
quotedElements[i] = fmt.Sprintf("%q", in)
|
||||
}
|
||||
return strings.Join(quotedElements, ",")
|
||||
}
|
||||
|
||||
func legacyDecorateResponseWriter(responseWriter http.ResponseWriter, out io.Writer, id string) http.ResponseWriter {
|
||||
delegate := &legacyAuditResponseWriter{ResponseWriter: responseWriter, out: out, id: id}
|
||||
// check if the ResponseWriter we're wrapping is the fancy one we need
|
||||
// or if the basic is sufficient
|
||||
_, cn := responseWriter.(http.CloseNotifier)
|
||||
_, fl := responseWriter.(http.Flusher)
|
||||
_, hj := responseWriter.(http.Hijacker)
|
||||
if cn && fl && hj {
|
||||
return &fancyLegacyResponseWriterDelegator{delegate}
|
||||
}
|
||||
return delegate
|
||||
}
|
|
@ -1,104 +0,0 @@
|
|||
/*
|
||||
Copyright 2016 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 filters
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"k8s.io/apiserver/pkg/authentication/user"
|
||||
)
|
||||
|
||||
func TestLegacyConstructResponseWriter(t *testing.T) {
|
||||
actual := legacyDecorateResponseWriter(&simpleResponseWriter{}, ioutil.Discard, "")
|
||||
switch v := actual.(type) {
|
||||
case *legacyAuditResponseWriter:
|
||||
default:
|
||||
t.Errorf("Expected auditResponseWriter, got %v", reflect.TypeOf(v))
|
||||
}
|
||||
|
||||
actual = legacyDecorateResponseWriter(&fancyResponseWriter{}, ioutil.Discard, "")
|
||||
switch v := actual.(type) {
|
||||
case *fancyLegacyResponseWriterDelegator:
|
||||
default:
|
||||
t.Errorf("Expected fancyResponseWriterDelegator, got %v", reflect.TypeOf(v))
|
||||
}
|
||||
}
|
||||
|
||||
func TestLegacyAudit(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
|
||||
handler := WithLegacyAudit(&fakeHTTPHandler{}, &buf)
|
||||
|
||||
req, _ := http.NewRequest("GET", "/api/v1/namespaces/default/pods", nil)
|
||||
req.RemoteAddr = "127.0.0.1"
|
||||
req = withTestContext(req, &user.DefaultInfo{Name: "admin"}, nil)
|
||||
handler.ServeHTTP(httptest.NewRecorder(), req)
|
||||
line := strings.Split(strings.TrimSpace(buf.String()), "\n")
|
||||
if len(line) != 2 {
|
||||
t.Fatalf("Unexpected amount of lines in audit log: %d", len(line))
|
||||
}
|
||||
match, err := regexp.MatchString(`[\d\:\-\.\+TZ]+ AUDIT: id="[\w-]+" ip="127.0.0.1" method="GET" user="admin" groups="<none>" as="<self>" asgroups="<lookup>" namespace="default" uri="/api/v1/namespaces/default/pods"`, line[0])
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error matching first line: %v", err)
|
||||
}
|
||||
if !match {
|
||||
t.Errorf("Unexpected first line of audit: %s", line[0])
|
||||
}
|
||||
match, err = regexp.MatchString(`[\d\:\-\.\+TZ]+ AUDIT: id="[\w-]+" response="200"`, line[1])
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error matching second line: %v", err)
|
||||
}
|
||||
if !match {
|
||||
t.Errorf("Unexpected second line of audit: %s", line[1])
|
||||
}
|
||||
}
|
||||
|
||||
func TestLegacyAuditNoPanicOnNilUser(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
|
||||
handler := WithLegacyAudit(&fakeHTTPHandler{}, &buf)
|
||||
|
||||
req, _ := http.NewRequest("GET", "/api/v1/namespaces/default/pods", nil)
|
||||
req.RemoteAddr = "127.0.0.1"
|
||||
req = withTestContext(req, nil, nil)
|
||||
handler.ServeHTTP(httptest.NewRecorder(), req)
|
||||
line := strings.Split(strings.TrimSpace(buf.String()), "\n")
|
||||
if len(line) != 2 {
|
||||
t.Fatalf("Unexpected amount of lines in audit log: %d", len(line))
|
||||
}
|
||||
match, err := regexp.MatchString(`[\d\:\-\.\+TZ]+ AUDIT: id="[\w-]+" ip="127.0.0.1" method="GET" user="<none>" groups="<none>" as="<self>" asgroups="<lookup>" namespace="default" uri="/api/v1/namespaces/default/pods"`, line[0])
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error matching first line: %v", err)
|
||||
}
|
||||
if !match {
|
||||
t.Errorf("Unexpected first line of audit: %s", line[0])
|
||||
}
|
||||
match, err = regexp.MatchString(`[\d\:\-\.\+TZ]+ AUDIT: id="[\w-]+" response="200"`, line[1])
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error matching second line: %v", err)
|
||||
}
|
||||
if !match {
|
||||
t.Errorf("Unexpected second line of audit: %s", line[1])
|
||||
}
|
||||
}
|
|
@ -37,6 +37,7 @@ const (
|
|||
// owner: @tallclair
|
||||
// alpha: v1.7
|
||||
// beta: v1.8
|
||||
// GA: v1.12
|
||||
//
|
||||
// AdvancedAuditing enables a much more general API auditing pipeline, which includes support for
|
||||
// pluggable output backends and an audit policy specifying how different requests should be
|
||||
|
@ -82,7 +83,7 @@ func init() {
|
|||
// available throughout Kubernetes binaries.
|
||||
var defaultKubernetesFeatureGates = map[utilfeature.Feature]utilfeature.FeatureSpec{
|
||||
StreamingProxyRedirects: {Default: true, PreRelease: utilfeature.Beta},
|
||||
AdvancedAuditing: {Default: true, PreRelease: utilfeature.Beta},
|
||||
AdvancedAuditing: {Default: true, PreRelease: utilfeature.GA},
|
||||
APIResponseCompression: {Default: false, PreRelease: utilfeature.Alpha},
|
||||
Initializers: {Default: false, PreRelease: utilfeature.Alpha},
|
||||
APIListChunking: {Default: true, PreRelease: utilfeature.Beta},
|
||||
|
|
|
@ -20,7 +20,6 @@ import (
|
|||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
goruntime "runtime"
|
||||
|
@ -114,8 +113,6 @@ type Config struct {
|
|||
|
||||
// Version will enable the /version endpoint if non-nil
|
||||
Version *version.Info
|
||||
// LegacyAuditWriter is the destination for audit logs. If nil, they will not be written.
|
||||
LegacyAuditWriter io.Writer
|
||||
// AuditBackend is where audit events are sent to.
|
||||
AuditBackend audit.Backend
|
||||
// AuditPolicyChecker makes the decision of whether and how to audit log a request.
|
||||
|
@ -534,15 +531,9 @@ func DefaultBuildHandlerChain(apiHandler http.Handler, c *Config) http.Handler {
|
|||
handler := genericapifilters.WithAuthorization(apiHandler, c.Authorization.Authorizer, c.Serializer)
|
||||
handler = genericfilters.WithMaxInFlightLimit(handler, c.MaxRequestsInFlight, c.MaxMutatingRequestsInFlight, c.LongRunningFunc)
|
||||
handler = genericapifilters.WithImpersonation(handler, c.Authorization.Authorizer, c.Serializer)
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.AdvancedAuditing) {
|
||||
handler = genericapifilters.WithAudit(handler, c.AuditBackend, c.AuditPolicyChecker, c.LongRunningFunc)
|
||||
} else {
|
||||
handler = genericapifilters.WithLegacyAudit(handler, c.LegacyAuditWriter)
|
||||
}
|
||||
handler = genericapifilters.WithAudit(handler, c.AuditBackend, c.AuditPolicyChecker, c.LongRunningFunc)
|
||||
failedHandler := genericapifilters.Unauthorized(c.Serializer, c.Authentication.SupportsBasicAuth)
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.AdvancedAuditing) {
|
||||
failedHandler = genericapifilters.WithFailedAuthenticationAudit(failedHandler, c.AuditBackend, c.AuditPolicyChecker)
|
||||
}
|
||||
failedHandler = genericapifilters.WithFailedAuthenticationAudit(failedHandler, c.AuditBackend, c.AuditPolicyChecker)
|
||||
handler = genericapifilters.WithAuthentication(handler, c.Authentication.Authenticator, failedHandler)
|
||||
handler = genericfilters.WithCORS(handler, c.CorsAllowedOriginList, nil, nil, nil, "true")
|
||||
handler = genericfilters.WithTimeoutForNonLongRunningRequests(handler, c.LongRunningFunc, c.RequestTimeout)
|
||||
|
|
|
@ -33,9 +33,7 @@ import (
|
|||
auditv1beta1 "k8s.io/apiserver/pkg/apis/audit/v1beta1"
|
||||
"k8s.io/apiserver/pkg/audit"
|
||||
"k8s.io/apiserver/pkg/audit/policy"
|
||||
"k8s.io/apiserver/pkg/features"
|
||||
"k8s.io/apiserver/pkg/server"
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
pluginbuffered "k8s.io/apiserver/plugin/pkg/audit/buffered"
|
||||
pluginlog "k8s.io/apiserver/plugin/pkg/audit/log"
|
||||
plugintruncate "k8s.io/apiserver/plugin/pkg/audit/truncate"
|
||||
|
@ -59,17 +57,12 @@ func appendBackend(existing, newBackend audit.Backend) audit.Backend {
|
|||
return audit.Union(existing, newBackend)
|
||||
}
|
||||
|
||||
func advancedAuditingEnabled() bool {
|
||||
return utilfeature.DefaultFeatureGate.Enabled(features.AdvancedAuditing)
|
||||
}
|
||||
|
||||
type AuditOptions struct {
|
||||
// Policy configuration file for filtering audit events that are captured.
|
||||
// If unspecified, a default is provided.
|
||||
PolicyFile string
|
||||
|
||||
// Plugin options
|
||||
|
||||
LogOptions AuditLogOptions
|
||||
WebhookOptions AuditWebhookOptions
|
||||
}
|
||||
|
@ -110,8 +103,6 @@ type AuditTruncateOptions struct {
|
|||
}
|
||||
|
||||
// AuditLogOptions determines the output of the structured audit log by default.
|
||||
// If the AdvancedAuditing feature is set to false, AuditLogOptions holds the legacy
|
||||
// audit log writer.
|
||||
type AuditLogOptions struct {
|
||||
Path string
|
||||
MaxAge int
|
||||
|
@ -179,17 +170,7 @@ func (o *AuditOptions) Validate() []error {
|
|||
return nil
|
||||
}
|
||||
|
||||
allErrors := []error{}
|
||||
|
||||
if !advancedAuditingEnabled() {
|
||||
if len(o.PolicyFile) > 0 {
|
||||
allErrors = append(allErrors, fmt.Errorf("feature '%s' must be enabled to set option --audit-policy-file", features.AdvancedAuditing))
|
||||
}
|
||||
if len(o.WebhookOptions.ConfigFile) > 0 {
|
||||
allErrors = append(allErrors, fmt.Errorf("feature '%s' must be enabled to set option --audit-webhook-config-file", features.AdvancedAuditing))
|
||||
}
|
||||
}
|
||||
|
||||
var allErrors []error
|
||||
allErrors = append(allErrors, o.LogOptions.Validate()...)
|
||||
allErrors = append(allErrors, o.WebhookOptions.Validate()...)
|
||||
|
||||
|
@ -263,8 +244,7 @@ func (o *AuditOptions) AddFlags(fs *pflag.FlagSet) {
|
|||
}
|
||||
|
||||
fs.StringVar(&o.PolicyFile, "audit-policy-file", o.PolicyFile,
|
||||
"Path to the file that defines the audit policy configuration. Requires the 'AdvancedAuditing' feature gate."+
|
||||
" With AdvancedAuditing, a profile is required to enable auditing.")
|
||||
"Path to the file that defines the audit policy configuration.")
|
||||
|
||||
o.LogOptions.AddFlags(fs)
|
||||
o.LogOptions.BatchOptions.AddFlags(pluginlog.PluginName, fs)
|
||||
|
@ -279,19 +259,14 @@ func (o *AuditOptions) ApplyTo(c *server.Config) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// Apply legacy audit options if advanced audit is not enabled.
|
||||
if !advancedAuditingEnabled() {
|
||||
return o.LogOptions.legacyApplyTo(c)
|
||||
}
|
||||
|
||||
// Apply advanced options if advanced audit is enabled.
|
||||
// Apply advanced options.
|
||||
// 1. Apply generic options.
|
||||
if err := o.applyTo(c); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 2. Apply plugin options.
|
||||
if err := o.LogOptions.advancedApplyTo(c); err != nil {
|
||||
if err := o.LogOptions.applyTo(c); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := o.WebhookOptions.applyTo(c); err != nil {
|
||||
|
@ -390,8 +365,8 @@ func (o *AuditLogOptions) AddFlags(fs *pflag.FlagSet) {
|
|||
"The maximum size in megabytes of the audit log file before it gets rotated.")
|
||||
fs.StringVar(&o.Format, "audit-log-format", o.Format,
|
||||
"Format of saved audits. \"legacy\" indicates 1-line text format for each event."+
|
||||
" \"json\" indicates structured json format. Requires the 'AdvancedAuditing' feature"+
|
||||
" gate. Known formats are "+strings.Join(pluginlog.AllowedFormats, ",")+".")
|
||||
" \"json\" indicates structured json format. Known formats are "+
|
||||
strings.Join(pluginlog.AllowedFormats, ",")+".")
|
||||
fs.StringVar(&o.GroupVersionString, "audit-log-version", o.GroupVersionString,
|
||||
"API group and version used for serializing audit events written to log.")
|
||||
}
|
||||
|
@ -403,30 +378,29 @@ func (o *AuditLogOptions) Validate() []error {
|
|||
}
|
||||
|
||||
var allErrors []error
|
||||
if advancedAuditingEnabled() {
|
||||
if err := validateBackendBatchOptions(pluginlog.PluginName, o.BatchOptions); err != nil {
|
||||
allErrors = append(allErrors, err)
|
||||
}
|
||||
if err := o.TruncateOptions.Validate(pluginlog.PluginName); err != nil {
|
||||
allErrors = append(allErrors, err)
|
||||
}
|
||||
|
||||
if err := validateGroupVersionString(o.GroupVersionString); err != nil {
|
||||
allErrors = append(allErrors, err)
|
||||
}
|
||||
if err := validateBackendBatchOptions(pluginlog.PluginName, o.BatchOptions); err != nil {
|
||||
allErrors = append(allErrors, err)
|
||||
}
|
||||
if err := o.TruncateOptions.Validate(pluginlog.PluginName); err != nil {
|
||||
allErrors = append(allErrors, err)
|
||||
}
|
||||
|
||||
// Check log format
|
||||
validFormat := false
|
||||
for _, f := range pluginlog.AllowedFormats {
|
||||
if f == o.Format {
|
||||
validFormat = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !validFormat {
|
||||
allErrors = append(allErrors, fmt.Errorf("invalid audit log format %s, allowed formats are %q", o.Format, strings.Join(pluginlog.AllowedFormats, ",")))
|
||||
if err := validateGroupVersionString(o.GroupVersionString); err != nil {
|
||||
allErrors = append(allErrors, err)
|
||||
}
|
||||
|
||||
// Check log format
|
||||
validFormat := false
|
||||
for _, f := range pluginlog.AllowedFormats {
|
||||
if f == o.Format {
|
||||
validFormat = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !validFormat {
|
||||
allErrors = append(allErrors, fmt.Errorf("invalid audit log format %s, allowed formats are %q", o.Format, strings.Join(pluginlog.AllowedFormats, ",")))
|
||||
}
|
||||
|
||||
// Check validities of MaxAge, MaxBackups and MaxSize of log options, if file log backend is enabled.
|
||||
if o.MaxAge < 0 {
|
||||
|
@ -464,7 +438,7 @@ func (o *AuditLogOptions) getWriter() io.Writer {
|
|||
return w
|
||||
}
|
||||
|
||||
func (o *AuditLogOptions) advancedApplyTo(c *server.Config) error {
|
||||
func (o *AuditLogOptions) applyTo(c *server.Config) error {
|
||||
if w := o.getWriter(); w != nil {
|
||||
groupVersion, _ := schema.ParseGroupVersion(o.GroupVersionString)
|
||||
log := pluginlog.NewBackend(w, o.Format, groupVersion)
|
||||
|
@ -475,15 +449,9 @@ func (o *AuditLogOptions) advancedApplyTo(c *server.Config) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (o *AuditLogOptions) legacyApplyTo(c *server.Config) error {
|
||||
c.LegacyAuditWriter = o.getWriter()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *AuditWebhookOptions) AddFlags(fs *pflag.FlagSet) {
|
||||
fs.StringVar(&o.ConfigFile, "audit-webhook-config-file", o.ConfigFile,
|
||||
"Path to a kubeconfig formatted file that defines the audit webhook configuration."+
|
||||
" Requires the 'AdvancedAuditing' feature gate.")
|
||||
"Path to a kubeconfig formatted file that defines the audit webhook configuration.")
|
||||
fs.DurationVar(&o.InitialBackoff, "audit-webhook-initial-backoff",
|
||||
o.InitialBackoff, "The amount of time to wait before retrying the first failed request.")
|
||||
fs.DurationVar(&o.InitialBackoff, "audit-webhook-batch-initial-backoff",
|
||||
|
@ -500,17 +468,15 @@ func (o *AuditWebhookOptions) Validate() []error {
|
|||
}
|
||||
|
||||
var allErrors []error
|
||||
if advancedAuditingEnabled() {
|
||||
if err := validateBackendBatchOptions(pluginwebhook.PluginName, o.BatchOptions); err != nil {
|
||||
allErrors = append(allErrors, err)
|
||||
}
|
||||
if err := o.TruncateOptions.Validate(pluginwebhook.PluginName); err != nil {
|
||||
allErrors = append(allErrors, err)
|
||||
}
|
||||
if err := validateBackendBatchOptions(pluginwebhook.PluginName, o.BatchOptions); err != nil {
|
||||
allErrors = append(allErrors, err)
|
||||
}
|
||||
if err := o.TruncateOptions.Validate(pluginwebhook.PluginName); err != nil {
|
||||
allErrors = append(allErrors, err)
|
||||
}
|
||||
|
||||
if err := validateGroupVersionString(o.GroupVersionString); err != nil {
|
||||
allErrors = append(allErrors, err)
|
||||
}
|
||||
if err := validateGroupVersionString(o.GroupVersionString); err != nil {
|
||||
allErrors = append(allErrors, err)
|
||||
}
|
||||
return allErrors
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue