Step-by-step checklist for adding a new Policy CRD type to NIC. Use when implementing a new policy like AccessControl, RateLimit, JWTAuth, ExternalAuth, BasicAuth, IngressMTLS, EgressMTLS, OIDC, WAF, APIKey, Cache, or CORS, or extending the policy system with a new policy type.
72
88%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
Passed
No findings from the security scan
Follow these steps IN ORDER. Each step depends on the previous.
File: pkg/apis/configuration/v1/types.go
type MyPolicy struct { ... })*MyPolicy pointer field to PolicySpec*bool/*int = optional/nullable. Plain bool/int = required or zero-defaultfalse must be non-pointer value typesRun make update-codegen to update zz_generated.deepcopy.go.
Run make update-crds to regenerate config/crd/bases/, deploy/crds.yaml, and chart CRDs.
File: pkg/apis/configuration/validation/policy.go
validate<MyPolicy>(spec *v1.MyPolicy, fieldPath *field.Path) field.ErrorListvalidatePolicySpec() with field count increment and feature gate checkpolicy_test.go with valid and invalid casesFile: internal/configs/version2/http.go
type MyPolicyConfig struct { ... })*MyPolicyConfig or fields to Server, Location, or bothVirtualServerConfigFile: internal/configs/policy.go
policiesCfgadd<MyPolicy>Config() method following the pattern belowswitch in generatePolicies()policy_test.goFile: internal/configs/virtualserver.go
GenerateVirtualServerConfig(), extract from policiesCfg and assign to version2 fieldsaddPoliciesCfgToLocation() for location-level assignmentFile: internal/configs/ingress.go
generateNginxCfg(), extract from policiesCfg and assign to version1 fieldsgenerateNginxCfgForMergeableIngresses()internal/configs/version2/nginx.virtualserver.tmpl and internal/configs/version2/nginx-plus.virtualserver.tmplinternal/configs/version1/nginx.ingress.tmpl and internal/configs/version1/nginx-plus.ingress.tmpl{{- if }} / {{- with }} guards around directive blocksinternal/configs/version2/template_helper.go and/or internal/configs/version1/template_helper.go, matching the template version you are updatingserver{}server{}, location-level inside each location{}File: internal/configs/version2/templates_test.go
make test-update-snaps to regenerate snapshots__snapshots__/charts/nginx-ingress/values.yaml -- add value with ## doccharts/nginx-ingress/values.schema.json -- add schema entrycharts/nginx-ingress/templates/_helpers.tpl -- add CLI arg or ConfigMap keycharts/tests/testdata/ -- add test values filecharts/tests/helmunit_test.go -- add test caseFile: internal/k8s/
syncPolicy(), ensure the new type is handled for VS/VSR/IngressDirectory: tests/suite/
tests/data/<feature>/test_<feature>_policies_vs.py, _vsr.py, _ingress.py@pytest.mark.policies and @pytest.mark.policies_<feature> markersmake update-codegen after changing types.go -- the build will fail with missing DeepCopy methodscontainsDangerousChars() validationpoliciesCfg duplicate check must warn and return, not error (exception: addCORSConfig has no duplicate check -- it overwrites, since CORS is additive via headers)Every add*Config() method in internal/configs/policy.go follows this pattern:
func (p *policiesCfg) addMyPolicyConfig(spec *conf_v1.MyPolicy, key, namespace string,
secretRefs map[string]*secrets.SecretReference) *validationResults {
res := newValidationResults()
// 1. Duplicate check
if p.MyPolicy != nil {
res.addWarningf("MyPolicy policy already configured, ignoring")
return res
}
// 2. Secret resolution (if applicable)
secretKey := namespace + "/" + spec.Secret
secretRef := secretRefs[secretKey]
if secretRef.Error != nil {
res.isError = true
res.addWarningf("secret %s has error: %v", secretKey, secretRef.Error)
return res
}
if secretRef.Type != secrets.SecretTypeExpected {
res.isError = true
res.addWarningf("secret %s has wrong type", secretKey)
return res
}
// 3. Build template struct and assign
p.MyPolicy = &version2.MyPolicyConfig{
Field1: spec.Field1,
Field2: spec.Field2,
Secret: secretRef.Path,
}
return res
}{{- with $s.MyPolicy }}
my_directive {{ .Value }};
{{- if .OptionalField }}
my_optional_directive {{ .OptionalField }};
{{- end }}
{{- end }}0eb3072
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.