Skip to content
This repository has been archived by the owner on Jun 14, 2018. It is now read-only.

add webhook validation #1158

Merged
merged 19 commits into from
Sep 8, 2017
Merged

Conversation

ayj
Copy link
Contributor

@ayj ayj commented Sep 1, 2017

What this PR does / why we need it:

Add the istio.io/pilot/platform/kube/admit package which implements the ExternalAdmissionWebhook for server-side validation of Pilot configuration. This includes a script to enable the workaround described here.

This feature requires 1.7.x with external-admission-webhooks. General instructions to enable are here. Specific testing instructions for GKE and Minikube are documented here.

Which issue this PR fixes (optional, in fixes #<issue number>(, fixes #<issue_number>, ...) format, will close that issue when PR gets merged): fixes #

Fixes #1066.

Special notes for your reviewer:

UPDATED: this PR includes integration with the istio-pilot discovery service. The admission registration is disabled by default since it requires an additional workaround for GKE (see platform/kube/admit/webhook-workaround.sh).

Users can experiment with this feature on GKE using the e2e pre-submit test with the following sequence of commands.

  1. run bin/e2e.sh -use-admission-webhook
  2. cancel the test after the deployment and test pods are created
  3. run platform/kube/admit/webhook-workaround.sh and specify the server-name, secret-name, namespace, and port number of the pilot discovery service, e.g.
platform/kube/admit/webhook-workaround.sh \ 
    --service-name istio-pilot \
    --secret-name pilot-webhook \
    --namespace istio-test-w1g0f \
    --port 443

This script converts the istio-pilot discovery service to type=LoadBalancer, waits for ingress IP, creates the workaround external service/endpoint, generates required CA and server certs for webhook, and creates the corresponding k8s secret which is consumed by the admission webhook server.

  1. wait until the LoadBalancer IP forwarding rule is propagated in GCP. You can try curl'ing <ExternalIP>:8000/v1/registration until a valid response is returned.

  2. Create a (in)valid pilot configuration via kubectl (or istioctl). Invalid configuration should be rejected by apiserver with appropriate error message.

Release note:

Add the `istio.io/pilot/platform/kube/admit` package which implements the ExternalAdmissionWebhook for server-side validation of Pilot configuration. This includes a script to enable the workaround described [here](https://github.com/kubernetes/kubernetes/issues/49987#issuecomment-319739227).

This feature requires 1.7.x with [external-admission-webhooks](https://kubernetes.io/docs/admin/extensible-admission-controllers/#external-admission-webhooks). General instructions to enable are [here](https://kubernetes.io/docs/admin/extensible-admission-controllers/#enable-external-admission-webhooks). Specific testing instructions for GKE and Minikube are documented [here](https://github.com/istio/pilot/blob/master/doc/testing.md#create-development-cluster).

@ayj ayj force-pushed the crd-validation-webhook-step-1 branch from bb3b1e7 to c755afb Compare September 1, 2017 01:31
@ayj
Copy link
Contributor Author

ayj commented Sep 1, 2017

/test pilot-e2e-smoketest

@ayj
Copy link
Contributor Author

ayj commented Sep 1, 2017

/retest

1 similar comment
@ayj
Copy link
Contributor Author

ayj commented Sep 1, 2017

/retest

Copy link
Contributor

@kyessenov kyessenov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me (modulo minor comments and requests for clarification). Can you document cluster requirements for this PR? Is this already available on GKE?

DefaultAdmissionServiceName = "istio-pilot-config"
)

// Admit implements the external admission webhook for validation
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

validation of


// DefaultAdmissionServiceName is the default service of the
// validation webhook.
DefaultAdmissionServiceName = "istio-pilot-config"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should this be pilot itself? seems unnecessary to allocate a service just for validation.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, though we'll need a new port since this is HTTPS. We'll also need to temporary use a different service name as a workaround for the issue described here, but that should eventually go away (k8s 1.8+).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see, then it makes sense but deserves a note in the comment.

validateNamespaces []string
}

// GetAPIServerExtensionCACert gets the CA cert that will signed the
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will be signed by

// controller.
func GetAPIServerExtensionCACert(cl kubernetes.Interface) ([]byte, error) {
const name = "extension-apiserver-authentication"
c, err := cl.CoreV1().ConfigMaps(metav1.NamespaceSystem).Get(name, metav1.GetOptions{})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not a secret to store CA cert?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is kube-system the right place to store the config? Is the name of the config standard?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is where Kubernetes stores the aggregate apiserver's client CA certificate. We don't control this.

$ kubectl -n kube-system get cm extension-apiserver-authentication 
NAME                                 DATA      AGE
extension-apiserver-authentication   6         17d

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please mention this. It might appear as if we're choosing these names.

}

// New creates a new instance of the admission webhook controller.
func New(descriptor model.ConfigDescriptor, hookConfigName, hookName, serviceName, serviceNamespace string, validateNamespaces []string, caBundle []byte) *Admit { // nolint: lll
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: constructors are usually called Create or Make around here. Too bad there's no standard name for the pattern. Also, Admit does not sound good as a thing, maybe call it AdmissionController.

}
}

// Unregister registers the external admission webhook
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unregisters

Rules: []admissionregistrationv1alpha1.RuleWithOperations{{
Operations: []admissionregistrationv1alpha1.OperationType{
admissionregistrationv1alpha1.Create,
admissionregistrationv1alpha1.Update,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PATCH?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

func (a *Admit) Register(client admissionClient.ExternalAdmissionHookConfigurationInterface) error {
var resources []string
for _, schema := range a.descriptor {
resources = append(resources, schema.Plural)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this expecting the kind name? then it should "RouteRules", not "route-rules"

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

then it's probably to remove "-" if the library code doesn't deal with it well.

},
},
}
client.Delete(webhook.Name, nil) // nolint: errcheck
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

might be worth logging even if ignoring the error, for debugging purposes

// This file was generated using openssl by the gencerts.sh script
// and holds raw certificates for the webhook tests.

package admit
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

admit_test is probably better to make it clear these are test artifacts

@codecov
Copy link

codecov bot commented Sep 5, 2017

Codecov Report

Merging #1158 into master will decrease coverage by 0.54%.
The diff coverage is 54.03%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #1158      +/-   ##
==========================================
- Coverage   70.47%   69.93%   -0.55%     
==========================================
  Files          55       54       -1     
  Lines        6399     6732     +333     
==========================================
+ Hits         4510     4708     +198     
- Misses       1668     1798     +130     
- Partials      221      226       +5
Impacted Files Coverage Δ
adapter/config/crd/conversion.go 89.09% <100%> (ø) ⬆️
platform/kube/admit/admit.go 50.44% <50.44%> (ø)
adapter/config/crd/controller.go 68.21% <80%> (ø) ⬆️
adapter/config/crd/client.go 69.05% <92.3%> (ø) ⬆️
platform/kube/controller.go 57.19% <0%> (-1.37%) ⬇️
proxy/envoy/config.go 83.24% <0%> (-0.28%) ⬇️
test/mock/config.go 0% <0%> (ø) ⬆️
adapter/serviceregistry/aggregate/controller.go
platform/eureka/serviceaccounts.go
... and 3 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 4f34dbb...2035394. Read the comment docs.

@rshriram
Copy link
Member

rshriram commented Sep 6, 2017

/retest

@rshriram
Copy link
Member

rshriram commented Sep 6, 2017

cc @GregHanson .. you might want to update your k8s image to 1.7.4

@@ -1,7 +1,7 @@
#!/bin/bash
set -ex

buildifier -showlog -mode=check $(find . -type f \( -name 'BUILD' -or -name 'WORKSPACE' -or -wholename '.*bazel' -or -wholename '.*bzl' \) -print )
buildifier -showlog -mode=check $(find . -type f \( -name 'BUILD' -or -name 'WORKSPACE' -or -wholename '.*bazel$' -or -wholename '.*bzl$' \) -print )
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good fix!

@ayj
Copy link
Contributor Author

ayj commented Sep 7, 2017

I went ahead and integrated the webhook with istio-pilot discovery service. It's off by default and requires an additional helper script to work on GKE (see platform/kube/admit/webhook-workaround.sh). I set things up so the majority of the code can be used as-is with some minimal changes to cert derivation/distribution w/o webhook-workaround.sh once k8s 1.8 on GKE is available.

@ayj
Copy link
Contributor Author

ayj commented Sep 7, 2017

/retest

Copy link
Contributor

@kyessenov kyessenov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good. Thanks, Jason!

// // validate webhook's service certificate.
// CABundle []byte

// DomainSuffix is the DNS domain suffix for Istio CRD resources, e.g. local.cluster.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: cluster.local

@@ -29,6 +29,11 @@ rules:
- apiGroups: [""]
resources: ["namespaces", "nodes", "secrets"]
verbs: ["get", "list", "watch"]
{{if .UseAdmissionWebhook}}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: may drop this, since it's a superset of RBAC permissions.

@ayj
Copy link
Contributor Author

ayj commented Sep 7, 2017

/retest

@rshriram
Copy link
Member

rshriram commented Sep 7, 2017

@ayj please merge with master.. and upload test logs from your local test cluster run.. Need e2e tests from istio/istio to pass.

@kyessenov
Copy link
Contributor

rebase please.

@rshriram rshriram merged commit e621733 into istio:master Sep 8, 2017
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Implement CRD validation with external admission webhooks
5 participants