mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-23 19:56:01 +00:00
Merge pull request #105908 from stlaz/ps_annotations
[PodSecurity] Add annotations denoting the exemption reason and the enforcement policy used
This commit is contained in:
commit
ac2d872ed9
@ -286,7 +286,7 @@ func (a *Admission) ValidateNamespace(ctx context.Context, attrs api.Attributes)
|
|||||||
return sharedAllowedResponse()
|
return sharedAllowedResponse()
|
||||||
}
|
}
|
||||||
if a.exemptNamespace(attrs.GetNamespace()) {
|
if a.exemptNamespace(attrs.GetNamespace()) {
|
||||||
return sharedAllowedResponse()
|
return sharedAllowedByNamespaceExemptionResponse()
|
||||||
}
|
}
|
||||||
response := allowedResponse()
|
response := allowedResponse()
|
||||||
response.Warnings = a.EvaluatePodsInNamespace(ctx, namespace.Name, newPolicy.Enforce)
|
response.Warnings = a.EvaluatePodsInNamespace(ctx, namespace.Name, newPolicy.Enforce)
|
||||||
@ -319,8 +319,12 @@ func (a *Admission) ValidatePod(ctx context.Context, attrs api.Attributes) *admi
|
|||||||
return sharedAllowedResponse()
|
return sharedAllowedResponse()
|
||||||
}
|
}
|
||||||
// short-circuit on exempt namespaces and users
|
// short-circuit on exempt namespaces and users
|
||||||
if a.exemptNamespace(attrs.GetNamespace()) || a.exemptUser(attrs.GetUserName()) {
|
if a.exemptNamespace(attrs.GetNamespace()) {
|
||||||
return sharedAllowedResponse()
|
return sharedAllowedByNamespaceExemptionResponse()
|
||||||
|
}
|
||||||
|
|
||||||
|
if a.exemptUser(attrs.GetUserName()) {
|
||||||
|
return sharedAllowedByUserExemptionResponse()
|
||||||
}
|
}
|
||||||
|
|
||||||
// short-circuit on privileged enforce+audit+warn namespaces
|
// short-circuit on privileged enforce+audit+warn namespaces
|
||||||
@ -371,8 +375,12 @@ func (a *Admission) ValidatePodController(ctx context.Context, attrs api.Attribu
|
|||||||
return sharedAllowedResponse()
|
return sharedAllowedResponse()
|
||||||
}
|
}
|
||||||
// short-circuit on exempt namespaces and users
|
// short-circuit on exempt namespaces and users
|
||||||
if a.exemptNamespace(attrs.GetNamespace()) || a.exemptUser(attrs.GetUserName()) {
|
if a.exemptNamespace(attrs.GetNamespace()) {
|
||||||
return sharedAllowedResponse()
|
return sharedAllowedByNamespaceExemptionResponse()
|
||||||
|
}
|
||||||
|
|
||||||
|
if a.exemptUser(attrs.GetUserName()) {
|
||||||
|
return sharedAllowedByUserExemptionResponse()
|
||||||
}
|
}
|
||||||
|
|
||||||
// short-circuit on privileged audit+warn namespaces
|
// short-circuit on privileged audit+warn namespaces
|
||||||
@ -409,7 +417,7 @@ func (a *Admission) ValidatePodController(ctx context.Context, attrs api.Attribu
|
|||||||
func (a *Admission) EvaluatePod(ctx context.Context, nsPolicy api.Policy, nsPolicyErr error, podMetadata *metav1.ObjectMeta, podSpec *corev1.PodSpec, attrs api.Attributes, enforce bool) *admissionv1.AdmissionResponse {
|
func (a *Admission) EvaluatePod(ctx context.Context, nsPolicy api.Policy, nsPolicyErr error, podMetadata *metav1.ObjectMeta, podSpec *corev1.PodSpec, attrs api.Attributes, enforce bool) *admissionv1.AdmissionResponse {
|
||||||
// short-circuit on exempt runtimeclass
|
// short-circuit on exempt runtimeclass
|
||||||
if a.exemptRuntimeClass(podSpec.RuntimeClassName) {
|
if a.exemptRuntimeClass(podSpec.RuntimeClassName) {
|
||||||
return sharedAllowedResponse()
|
return sharedAllowedByRuntimeClassExemptionResponse()
|
||||||
}
|
}
|
||||||
|
|
||||||
auditAnnotations := map[string]string{}
|
auditAnnotations := map[string]string{}
|
||||||
@ -424,6 +432,8 @@ func (a *Admission) EvaluatePod(ctx context.Context, nsPolicy api.Policy, nsPoli
|
|||||||
|
|
||||||
response := allowedResponse()
|
response := allowedResponse()
|
||||||
if enforce {
|
if enforce {
|
||||||
|
auditAnnotations[api.EnforcedPolicyAnnotationKey] = nsPolicy.Enforce.String()
|
||||||
|
|
||||||
if result := policy.AggregateCheckResults(a.Evaluator.EvaluatePod(nsPolicy.Enforce, podMetadata, podSpec)); !result.Allowed {
|
if result := policy.AggregateCheckResults(a.Evaluator.EvaluatePod(nsPolicy.Enforce, podMetadata, podSpec)); !result.Allowed {
|
||||||
response = forbiddenResponse(fmt.Sprintf(
|
response = forbiddenResponse(fmt.Sprintf(
|
||||||
"pod violates PodSecurity %q: %s",
|
"pod violates PodSecurity %q: %s",
|
||||||
@ -438,7 +448,7 @@ func (a *Admission) EvaluatePod(ctx context.Context, nsPolicy api.Policy, nsPoli
|
|||||||
|
|
||||||
// TODO: reuse previous evaluation if audit level+version is the same as enforce level+version
|
// TODO: reuse previous evaluation if audit level+version is the same as enforce level+version
|
||||||
if result := policy.AggregateCheckResults(a.Evaluator.EvaluatePod(nsPolicy.Audit, podMetadata, podSpec)); !result.Allowed {
|
if result := policy.AggregateCheckResults(a.Evaluator.EvaluatePod(nsPolicy.Audit, podMetadata, podSpec)); !result.Allowed {
|
||||||
auditAnnotations["audit"] = fmt.Sprintf(
|
auditAnnotations[api.AuditViolationsAnnotationKey] = fmt.Sprintf(
|
||||||
"would violate PodSecurity %q: %s",
|
"would violate PodSecurity %q: %s",
|
||||||
nsPolicy.Audit.String(),
|
nsPolicy.Audit.String(),
|
||||||
result.ForbiddenDetail(),
|
result.ForbiddenDetail(),
|
||||||
@ -565,17 +575,41 @@ func (a *Admission) PolicyToEvaluate(labels map[string]string) (api.Policy, fiel
|
|||||||
return api.PolicyToEvaluate(labels, a.defaultPolicy)
|
return api.PolicyToEvaluate(labels, a.defaultPolicy)
|
||||||
}
|
}
|
||||||
|
|
||||||
var _sharedAllowedResponse = allowedResponse()
|
var (
|
||||||
|
_sharedAllowedResponse = allowedResponse()
|
||||||
|
_sharedAllowedByUserExemptionResponse = allowedByExemptResponse("user")
|
||||||
|
_sharedAllowedByNamespaceExemptionResponse = allowedByExemptResponse("namespace")
|
||||||
|
_sharedAllowedByRuntimeClassExemptionResponse = allowedByExemptResponse("runtimeClass")
|
||||||
|
)
|
||||||
|
|
||||||
func sharedAllowedResponse() *admissionv1.AdmissionResponse {
|
func sharedAllowedResponse() *admissionv1.AdmissionResponse {
|
||||||
return _sharedAllowedResponse
|
return _sharedAllowedResponse
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func sharedAllowedByUserExemptionResponse() *admissionv1.AdmissionResponse {
|
||||||
|
return _sharedAllowedByUserExemptionResponse
|
||||||
|
}
|
||||||
|
|
||||||
|
func sharedAllowedByNamespaceExemptionResponse() *admissionv1.AdmissionResponse {
|
||||||
|
return _sharedAllowedByNamespaceExemptionResponse
|
||||||
|
}
|
||||||
|
|
||||||
|
func sharedAllowedByRuntimeClassExemptionResponse() *admissionv1.AdmissionResponse {
|
||||||
|
return _sharedAllowedByRuntimeClassExemptionResponse
|
||||||
|
}
|
||||||
|
|
||||||
// allowedResponse is the response used when the admission decision is allow.
|
// allowedResponse is the response used when the admission decision is allow.
|
||||||
func allowedResponse() *admissionv1.AdmissionResponse {
|
func allowedResponse() *admissionv1.AdmissionResponse {
|
||||||
return &admissionv1.AdmissionResponse{Allowed: true}
|
return &admissionv1.AdmissionResponse{Allowed: true}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func allowedByExemptResponse(exemptionReason string) *admissionv1.AdmissionResponse {
|
||||||
|
return &admissionv1.AdmissionResponse{
|
||||||
|
Allowed: true,
|
||||||
|
AuditAnnotations: map[string]string{api.ExemptionReasonAnnotationKey: exemptionReason},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func failureResponse(msg string, reason metav1.StatusReason, code int32) *admissionv1.AdmissionResponse {
|
func failureResponse(msg string, reason metav1.StatusReason, code int32) *admissionv1.AdmissionResponse {
|
||||||
return &admissionv1.AdmissionResponse{
|
return &admissionv1.AdmissionResponse{
|
||||||
Allowed: false,
|
Allowed: false,
|
||||||
|
@ -609,25 +609,28 @@ func TestValidatePodController(t *testing.T) {
|
|||||||
gvr: schema.GroupVersionResource{Group: "apps", Version: "v1", Resource: "deployments"},
|
gvr: schema.GroupVersionResource{Group: "apps", Version: "v1", Resource: "deployments"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc: "namespace in exemptNamespaces will be exempted",
|
desc: "namespace in exemptNamespaces will be exempted",
|
||||||
newObject: &badDeploy,
|
newObject: &badDeploy,
|
||||||
gvk: schema.GroupVersionKind{Group: "apps", Version: "v1", Kind: "Deployment"},
|
gvk: schema.GroupVersionKind{Group: "apps", Version: "v1", Kind: "Deployment"},
|
||||||
gvr: schema.GroupVersionResource{Group: "apps", Version: "v1", Resource: "deployments"},
|
gvr: schema.GroupVersionResource{Group: "apps", Version: "v1", Resource: "deployments"},
|
||||||
exemptNamespaces: []string{testNamespace},
|
exemptNamespaces: []string{testNamespace},
|
||||||
|
expectAuditAnnotations: map[string]string{"exempt": "namespace"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc: "runtimeClass in exemptRuntimeClasses will be exempted",
|
desc: "runtimeClass in exemptRuntimeClasses will be exempted",
|
||||||
newObject: &badDeploy,
|
newObject: &badDeploy,
|
||||||
gvk: schema.GroupVersionKind{Group: "apps", Version: "v1", Kind: "Deployment"},
|
gvk: schema.GroupVersionKind{Group: "apps", Version: "v1", Kind: "Deployment"},
|
||||||
gvr: schema.GroupVersionResource{Group: "apps", Version: "v1", Resource: "deployments"},
|
gvr: schema.GroupVersionResource{Group: "apps", Version: "v1", Resource: "deployments"},
|
||||||
exemptRuntimeClasses: []string{"containerd"},
|
exemptRuntimeClasses: []string{"containerd"},
|
||||||
|
expectAuditAnnotations: map[string]string{"exempt": "runtimeClass"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc: "user in exemptUsers will be exempted",
|
desc: "user in exemptUsers will be exempted",
|
||||||
newObject: &badDeploy,
|
newObject: &badDeploy,
|
||||||
gvk: schema.GroupVersionKind{Group: "apps", Version: "v1", Kind: "Deployment"},
|
gvk: schema.GroupVersionKind{Group: "apps", Version: "v1", Kind: "Deployment"},
|
||||||
gvr: schema.GroupVersionResource{Group: "apps", Version: "v1", Resource: "deployments"},
|
gvr: schema.GroupVersionResource{Group: "apps", Version: "v1", Resource: "deployments"},
|
||||||
exemptUsers: []string{"testuser"},
|
exemptUsers: []string{"testuser"},
|
||||||
|
expectAuditAnnotations: map[string]string{"exempt": "user"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc: "podMetadata == nil && podSpec == nil will skip verification",
|
desc: "podMetadata == nil && podSpec == nil will skip verification",
|
||||||
@ -647,7 +650,7 @@ func TestValidatePodController(t *testing.T) {
|
|||||||
newObject: &badDeploy,
|
newObject: &badDeploy,
|
||||||
gvk: schema.GroupVersionKind{Group: "apps", Version: "v1", Kind: "Deployment"},
|
gvk: schema.GroupVersionKind{Group: "apps", Version: "v1", Kind: "Deployment"},
|
||||||
gvr: schema.GroupVersionResource{Group: "apps", Version: "v1", Resource: "deployments"},
|
gvr: schema.GroupVersionResource{Group: "apps", Version: "v1", Resource: "deployments"},
|
||||||
expectAuditAnnotations: map[string]string{"audit": "would violate PodSecurity \"baseline:latest\": forbidden sysctls (unknown)"},
|
expectAuditAnnotations: map[string]string{"audit-violations": "would violate PodSecurity \"baseline:latest\": forbidden sysctls (unknown)"},
|
||||||
expectWarnings: []string{"would violate PodSecurity \"baseline:latest\": forbidden sysctls (unknown)"},
|
expectWarnings: []string{"would violate PodSecurity \"baseline:latest\": forbidden sysctls (unknown)"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -656,7 +659,7 @@ func TestValidatePodController(t *testing.T) {
|
|||||||
oldObject: &goodDeploy,
|
oldObject: &goodDeploy,
|
||||||
gvk: schema.GroupVersionKind{Group: "apps", Version: "v1", Kind: "Deployment"},
|
gvk: schema.GroupVersionKind{Group: "apps", Version: "v1", Kind: "Deployment"},
|
||||||
gvr: schema.GroupVersionResource{Group: "apps", Version: "v1", Resource: "deployments"},
|
gvr: schema.GroupVersionResource{Group: "apps", Version: "v1", Resource: "deployments"},
|
||||||
expectAuditAnnotations: map[string]string{"audit": "would violate PodSecurity \"baseline:latest\": forbidden sysctls (unknown)"},
|
expectAuditAnnotations: map[string]string{"audit-violations": "would violate PodSecurity \"baseline:latest\": forbidden sysctls (unknown)"},
|
||||||
expectWarnings: []string{"would violate PodSecurity \"baseline:latest\": forbidden sysctls (unknown)"},
|
expectWarnings: []string{"would violate PodSecurity \"baseline:latest\": forbidden sysctls (unknown)"},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -43,4 +43,8 @@ const (
|
|||||||
AuditVersionLabel = labelPrefix + "audit-version"
|
AuditVersionLabel = labelPrefix + "audit-version"
|
||||||
WarnLevelLabel = labelPrefix + "warn"
|
WarnLevelLabel = labelPrefix + "warn"
|
||||||
WarnVersionLabel = labelPrefix + "warn-version"
|
WarnVersionLabel = labelPrefix + "warn-version"
|
||||||
|
|
||||||
|
ExemptionReasonAnnotationKey = "exempt"
|
||||||
|
AuditViolationsAnnotationKey = "audit-violations"
|
||||||
|
EnforcedPolicyAnnotationKey = "enforce-policy"
|
||||||
)
|
)
|
||||||
|
Loading…
Reference in New Issue
Block a user