mirror of
https://github.com/kyverno/kyverno.git
synced 2025-03-15 12:17:56 +00:00
Add a registry flag to allow direct access to container registries in the CLI (#3396)
* Add a registry flag to allow direct access to container registries in the CLI Signed-off-by: Sambhav Kothari <sambhavs.email@gmail.com>
This commit is contained in:
parent
9e623bbf6e
commit
6498425937
11 changed files with 130 additions and 27 deletions
6
Makefile
6
Makefile
|
@ -253,12 +253,16 @@ test-cli-local: cli
|
||||||
|
|
||||||
.PHONY: test-cli-local-mutate
|
.PHONY: test-cli-local-mutate
|
||||||
test-cli-local-mutate: cli
|
test-cli-local-mutate: cli
|
||||||
cmd/cli/kubectl-kyverno/kyverno test ./test/cli/test
|
cmd/cli/kubectl-kyverno/kyverno test ./test/cli/test-mutate
|
||||||
|
|
||||||
.PHONY: test-cli-test-case-selector-flag
|
.PHONY: test-cli-test-case-selector-flag
|
||||||
test-cli-test-case-selector-flag: cli
|
test-cli-test-case-selector-flag: cli
|
||||||
cmd/cli/kubectl-kyverno/kyverno test ./test/cli/test --test-case-selector "policy=disallow-latest-tag, rule=require-image-tag, resource=test-require-image-tag-pass"
|
cmd/cli/kubectl-kyverno/kyverno test ./test/cli/test --test-case-selector "policy=disallow-latest-tag, rule=require-image-tag, resource=test-require-image-tag-pass"
|
||||||
|
|
||||||
|
.PHONY: test-cli-registry
|
||||||
|
test-cli-registry: cli
|
||||||
|
cmd/cli/kubectl-kyverno/kyverno test ./test/cli/registry
|
||||||
|
|
||||||
# go get downloads and installs the binary
|
# go get downloads and installs the binary
|
||||||
# we temporarily add the GO_ACC to the path
|
# we temporarily add the GO_ACC to the path
|
||||||
test-unit: $(GO_ACC)
|
test-unit: $(GO_ACC)
|
||||||
|
|
|
@ -24,12 +24,18 @@ func LoadContext(logger logr.Logger, contextEntries []kyverno.ContextEntry, ctx
|
||||||
|
|
||||||
policyName := ctx.Policy.Name
|
policyName := ctx.Policy.Name
|
||||||
if store.GetMock() {
|
if store.GetMock() {
|
||||||
rule := store.GetPolicyRuleFromContext(policyName, ruleName)
|
if store.GetRegistryAccess() {
|
||||||
if rule == nil || len(rule.Values) == 0 {
|
for _, entry := range contextEntries {
|
||||||
return fmt.Errorf("No values found for policy %s rule %s", policyName, ruleName)
|
if entry.ImageRegistry != nil {
|
||||||
|
if err := loadImageData(logger, entry, ctx); err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rule := store.GetPolicyRuleFromContext(policyName, ruleName)
|
||||||
|
if rule != nil && len(rule.Values) > 0 {
|
||||||
variables := rule.Values
|
variables := rule.Values
|
||||||
|
|
||||||
for key, value := range variables {
|
for key, value := range variables {
|
||||||
if trimmedTypedValue := strings.Trim(value, "\n"); strings.Contains(trimmedTypedValue, "\n") {
|
if trimmedTypedValue := strings.Trim(value, "\n"); strings.Contains(trimmedTypedValue, "\n") {
|
||||||
tmp := map[string]interface{}{key: value}
|
tmp := map[string]interface{}{key: value}
|
||||||
|
@ -44,6 +50,7 @@ func LoadContext(logger logr.Logger, contextEntries []kyverno.ContextEntry, ctx
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
for _, entry := range contextEntries {
|
for _, entry := range contextEntries {
|
||||||
if entry.ConfigMap != nil {
|
if entry.ConfigMap != nil {
|
||||||
|
|
|
@ -105,7 +105,7 @@ More info: https://kyverno.io/docs/kyverno-cli/
|
||||||
func Command() *cobra.Command {
|
func Command() *cobra.Command {
|
||||||
var cmd *cobra.Command
|
var cmd *cobra.Command
|
||||||
var resourcePaths []string
|
var resourcePaths []string
|
||||||
var cluster, policyReport, stdin bool
|
var cluster, policyReport, stdin, registryAccess bool
|
||||||
var mutateLogPath, variablesString, valuesFile, namespace string
|
var mutateLogPath, variablesString, valuesFile, namespace string
|
||||||
|
|
||||||
cmd = &cobra.Command{
|
cmd = &cobra.Command{
|
||||||
|
@ -122,7 +122,7 @@ func Command() *cobra.Command {
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
rc, resources, skipInvalidPolicies, pvInfos, err := applyCommandHelper(resourcePaths, cluster, policyReport, mutateLogPath, variablesString, valuesFile, namespace, policyPaths, stdin)
|
rc, resources, skipInvalidPolicies, pvInfos, err := applyCommandHelper(resourcePaths, cluster, policyReport, mutateLogPath, variablesString, valuesFile, namespace, policyPaths, stdin, registryAccess)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -141,12 +141,14 @@ func Command() *cobra.Command {
|
||||||
cmd.Flags().BoolVarP(&policyReport, "policy-report", "", false, "Generates policy report when passed (default policyviolation r")
|
cmd.Flags().BoolVarP(&policyReport, "policy-report", "", false, "Generates policy report when passed (default policyviolation r")
|
||||||
cmd.Flags().StringVarP(&namespace, "namespace", "n", "", "Optional Policy parameter passed with cluster flag")
|
cmd.Flags().StringVarP(&namespace, "namespace", "n", "", "Optional Policy parameter passed with cluster flag")
|
||||||
cmd.Flags().BoolVarP(&stdin, "stdin", "i", false, "Optional mutate policy parameter to pipe directly through to kubectl")
|
cmd.Flags().BoolVarP(&stdin, "stdin", "i", false, "Optional mutate policy parameter to pipe directly through to kubectl")
|
||||||
|
cmd.Flags().BoolVarP(®istryAccess, "registry", "", false, "If set to true, access the image registry using local docker credentials to populate external data")
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
func applyCommandHelper(resourcePaths []string, cluster bool, policyReport bool, mutateLogPath string,
|
func applyCommandHelper(resourcePaths []string, cluster bool, policyReport bool, mutateLogPath string,
|
||||||
variablesString string, valuesFile string, namespace string, policyPaths []string, stdin bool) (rc *common.ResultCounts, resources []*unstructured.Unstructured, skipInvalidPolicies SkippedInvalidPolicies, pvInfos []policyreport.Info, err error) {
|
variablesString string, valuesFile string, namespace string, policyPaths []string, stdin, registryAccess bool) (rc *common.ResultCounts, resources []*unstructured.Unstructured, skipInvalidPolicies SkippedInvalidPolicies, pvInfos []policyreport.Info, err error) {
|
||||||
store.SetMock(true)
|
store.SetMock(true)
|
||||||
|
store.SetRegistryAccess(registryAccess)
|
||||||
kubernetesConfig := genericclioptions.NewConfigFlags(true)
|
kubernetesConfig := genericclioptions.NewConfigFlags(true)
|
||||||
fs := memfs.New()
|
fs := memfs.New()
|
||||||
|
|
||||||
|
|
|
@ -72,7 +72,7 @@ func Test_Apply(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tc := range testcases {
|
for _, tc := range testcases {
|
||||||
_, _, _, info, _ := applyCommandHelper(tc.ResourcePaths, false, true, "", "", "", "", tc.PolicyPaths, false)
|
_, _, _, info, _ := applyCommandHelper(tc.ResourcePaths, false, true, "", "", "", "", tc.PolicyPaths, false, false)
|
||||||
resps := buildPolicyReports(info)
|
resps := buildPolicyReports(info)
|
||||||
for i, resp := range resps {
|
for i, resp := range resps {
|
||||||
compareSummary(tc.expectedPolicyReports[i].Summary, resp.UnstructuredContent()["summary"].(map[string]interface{}))
|
compareSummary(tc.expectedPolicyReports[i].Summary, resp.UnstructuredContent()["summary"].(map[string]interface{}))
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package store
|
package store
|
||||||
|
|
||||||
var Mock bool
|
import "github.com/kyverno/kyverno/pkg/registryclient"
|
||||||
|
|
||||||
|
var Mock, RegistryAccess bool
|
||||||
var ContextVar Context
|
var ContextVar Context
|
||||||
|
|
||||||
func SetMock(mock bool) {
|
func SetMock(mock bool) {
|
||||||
|
@ -11,6 +13,17 @@ func GetMock() bool {
|
||||||
return Mock
|
return Mock
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func SetRegistryAccess(access bool) {
|
||||||
|
if access {
|
||||||
|
registryclient.InitializeLocal()
|
||||||
|
}
|
||||||
|
RegistryAccess = access
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetRegistryAccess() bool {
|
||||||
|
return RegistryAccess
|
||||||
|
}
|
||||||
|
|
||||||
func SetContext(context Context) {
|
func SetContext(context Context) {
|
||||||
ContextVar = context
|
ContextVar = context
|
||||||
}
|
}
|
||||||
|
|
|
@ -154,6 +154,7 @@ func Command() *cobra.Command {
|
||||||
var testCase string
|
var testCase string
|
||||||
var testFile []byte
|
var testFile []byte
|
||||||
var fileName, gitBranch string
|
var fileName, gitBranch string
|
||||||
|
var registryAccess bool
|
||||||
cmd = &cobra.Command{
|
cmd = &cobra.Command{
|
||||||
Use: "test <path_to_folder_Containing_test.yamls> [flags]\n kyverno test <path_to_gitRepository_with_dir> --git-branch <branchName>\n kyverno test --manifest-mutate > kyverno-test.yaml\n kyverno test --manifest-validate > kyverno-test.yaml",
|
Use: "test <path_to_folder_Containing_test.yamls> [flags]\n kyverno test <path_to_gitRepository_with_dir> --git-branch <branchName>\n kyverno test --manifest-mutate > kyverno-test.yaml\n kyverno test --manifest-validate > kyverno-test.yaml",
|
||||||
// Args: cobra.ExactArgs(1),
|
// Args: cobra.ExactArgs(1),
|
||||||
|
@ -211,6 +212,7 @@ results:
|
||||||
fmt.Println(string(testFile))
|
fmt.Println(string(testFile))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
store.SetRegistryAccess(registryAccess)
|
||||||
_, err = testCommandExecute(dirPath, fileName, gitBranch, testCase)
|
_, err = testCommandExecute(dirPath, fileName, gitBranch, testCase)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Log.V(3).Info("a directory is required")
|
log.Log.V(3).Info("a directory is required")
|
||||||
|
@ -225,6 +227,7 @@ results:
|
||||||
cmd.Flags().StringVarP(&testCase, "test-case-selector", "t", "", `run some specific test cases by passing a string argument in double quotes to this flag like - "policy=<policy_name>, rule=<rule_name>, resource=<resource_name". The argument could be any combination of policy, rule and resource.`)
|
cmd.Flags().StringVarP(&testCase, "test-case-selector", "t", "", `run some specific test cases by passing a string argument in double quotes to this flag like - "policy=<policy_name>, rule=<rule_name>, resource=<resource_name". The argument could be any combination of policy, rule and resource.`)
|
||||||
cmd.Flags().BoolP("manifest-mutate", "", false, "prints out a template test manifest for a mutate policy")
|
cmd.Flags().BoolP("manifest-mutate", "", false, "prints out a template test manifest for a mutate policy")
|
||||||
cmd.Flags().BoolP("manifest-validate", "", false, "prints out a template test manifest for a validate policy")
|
cmd.Flags().BoolP("manifest-validate", "", false, "prints out a template test manifest for a validate policy")
|
||||||
|
cmd.Flags().BoolVarP(®istryAccess, "registry", "", false, "If set to true, access the image registry using local docker credentials to populate external data")
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -30,6 +30,11 @@ var (
|
||||||
DefaultKeychain authn.Keychain = defaultKeychain
|
DefaultKeychain authn.Keychain = defaultKeychain
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// InitializeLocal loads the docker credentials and initializes the default auth method for container registry API calls
|
||||||
|
func InitializeLocal() {
|
||||||
|
DefaultKeychain = authn.DefaultKeychain
|
||||||
|
}
|
||||||
|
|
||||||
// Initialize loads the image pull secrets and initializes the default auth method for container registry API calls
|
// Initialize loads the image pull secrets and initializes the default auth method for container registry API calls
|
||||||
func Initialize(client kubernetes.Interface, namespace, serviceAccount string, imagePullSecrets []string) error {
|
func Initialize(client kubernetes.Interface, namespace, serviceAccount string, imagePullSecrets []string) error {
|
||||||
kubeClient = client
|
kubeClient = client
|
||||||
|
|
33
test/cli/registry/image-example.yaml
Normal file
33
test/cli/registry/image-example.yaml
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
apiVersion: kyverno.io/v1
|
||||||
|
kind: ClusterPolicy
|
||||||
|
metadata:
|
||||||
|
name: images
|
||||||
|
spec:
|
||||||
|
validationFailureAction: enforce
|
||||||
|
rules:
|
||||||
|
- name: only-allow-trusted-images
|
||||||
|
match:
|
||||||
|
resources:
|
||||||
|
kinds:
|
||||||
|
- Pod
|
||||||
|
preconditions:
|
||||||
|
- key: "{{request.operation}}"
|
||||||
|
operator: NotEquals
|
||||||
|
value: DELETE
|
||||||
|
validate:
|
||||||
|
message: "images with root user are not allowed"
|
||||||
|
foreach:
|
||||||
|
- list: "request.object.spec.containers"
|
||||||
|
context:
|
||||||
|
- name: imageData
|
||||||
|
imageRegistry:
|
||||||
|
reference: "{{ element.image }}"
|
||||||
|
deny:
|
||||||
|
conditions:
|
||||||
|
all:
|
||||||
|
- key: "{{ imageData.configData.config.User || ''}}"
|
||||||
|
operator: Equals
|
||||||
|
value: ""
|
||||||
|
- key: "{{ imageData.registry }}"
|
||||||
|
operator: NotEquals
|
||||||
|
value: "ghcr.io"
|
17
test/cli/registry/kyverno-test.yaml
Normal file
17
test/cli/registry/kyverno-test.yaml
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
name: test-variables
|
||||||
|
policies:
|
||||||
|
- image-example.yaml
|
||||||
|
resources:
|
||||||
|
- resources.yaml
|
||||||
|
results:
|
||||||
|
- policy: images
|
||||||
|
rule: only-allow-trusted-images
|
||||||
|
resource: test-pod-with-non-root-user-image
|
||||||
|
kind: Pod
|
||||||
|
status: pass
|
||||||
|
- policy: images
|
||||||
|
rule: only-allow-trusted-images
|
||||||
|
resource: test-pod-with-trusted-registry
|
||||||
|
kind: Pod
|
||||||
|
status: pass
|
||||||
|
|
19
test/cli/registry/resources.yaml
Normal file
19
test/cli/registry/resources.yaml
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Pod
|
||||||
|
metadata:
|
||||||
|
name: test-pod-with-non-root-user-image
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: solr
|
||||||
|
image: solr
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Pod
|
||||||
|
metadata:
|
||||||
|
name: test-pod-with-trusted-registry
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: kyverno
|
||||||
|
image: ghcr.io/kyverno/kyverno
|
||||||
|
|
|
@ -43,40 +43,40 @@ results:
|
||||||
patchedResource: patchedResource6.yaml
|
patchedResource: patchedResource6.yaml
|
||||||
kind: Pod
|
kind: Pod
|
||||||
result: pass
|
result: pass
|
||||||
- policy: testing/add-ndots
|
- policy: add-ndots
|
||||||
rule: add-ndots
|
rule: add-ndots
|
||||||
resource: resource-equal-to-patch-res-for-cp
|
resource: resource-equal-to-patch-res-for-cp
|
||||||
namespace: practice
|
namespace: practice
|
||||||
patchedResource: patchedResource7.yaml
|
patchedResource: patchedResource7.yaml
|
||||||
kind: Pod
|
kind: Pod
|
||||||
result: skip
|
result: skip
|
||||||
- policy: testing/add-ndots
|
- policy: add-ndots
|
||||||
rule: add-ndots
|
rule: add-ndots
|
||||||
resource: same-name-but-diff-namespace
|
resource: same-name-but-diff-namespace
|
||||||
patchedResource: patchedResource8.yaml
|
patchedResource: patchedResource8.yaml
|
||||||
namespace: testing
|
namespace: testing
|
||||||
kind: Pod
|
kind: Pod
|
||||||
result: pass
|
result: pass
|
||||||
- policy: testing/add-ndots
|
- policy: add-ndots
|
||||||
rule: add-ndots
|
rule: add-ndots
|
||||||
resource: same-name-but-diff-namespace
|
resource: same-name-but-diff-namespace
|
||||||
patchedResource: patchedResource9.yaml
|
patchedResource: patchedResource9.yaml
|
||||||
kind: Pod
|
kind: Pod
|
||||||
namespace: production
|
namespace: production
|
||||||
result: skip
|
result: skip
|
||||||
- policy: testing/add-ndots
|
- policy: add-ndots
|
||||||
rule: add-ndots
|
rule: add-ndots
|
||||||
resource: mydeploy
|
resource: mydeploy
|
||||||
patchedResource: patchedResource10.yaml
|
patchedResource: patchedResource10.yaml
|
||||||
kind: Deployment
|
kind: Deployment
|
||||||
result: skip
|
result: skip
|
||||||
- policy: testing/add-ndots
|
- policy: add-ndots
|
||||||
rule: add-ndots
|
rule: add-ndots
|
||||||
resource: same-name-but-diff-kind
|
resource: same-name-but-diff-kind
|
||||||
patchedResource: patchedResource5.yaml
|
patchedResource: patchedResource5.yaml
|
||||||
kind: Service
|
kind: Service
|
||||||
result: skip
|
result: skip
|
||||||
- policy: testing/add-ndots
|
- policy: add-ndots
|
||||||
rule: add-ndots
|
rule: add-ndots
|
||||||
resource: same-name-but-diff-kind
|
resource: same-name-but-diff-kind
|
||||||
patchedResource: patchedResource11.yaml
|
patchedResource: patchedResource11.yaml
|
||||||
|
|
Loading…
Add table
Reference in a new issue