-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Support config to deploy internal certificates automatically #13005
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
70d58d7
3af4f43
fb30cfc
4d19c6a
8140587
d313bd5
56d0063
6e5ba2a
0389bee
dba0c95
200a1f3
b829926
bd56955
7a929b0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -40,6 +40,7 @@ import ( | |
| metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
| "k8s.io/apimachinery/pkg/util/wait" | ||
|
|
||
| "knative.dev/control-protocol/pkg/certificates" | ||
| network "knative.dev/networking/pkg" | ||
| netcfg "knative.dev/networking/pkg/config" | ||
| netprobe "knative.dev/networking/pkg/http/probe" | ||
|
|
@@ -156,14 +157,14 @@ func main() { | |
| logger.Fatalw("Failed to construct network config", zap.Error(err)) | ||
| } | ||
|
|
||
| // Enable TLS against queue-proxy when the CA and SA are specified. | ||
| tlsEnabled := networkConfig.QueueProxyCA != "" && networkConfig.QueueProxySAN != "" | ||
| // Enable TLS against queue-proxy when internal-encryption is enabled. | ||
| tlsEnabled := networkConfig.InternalEncryption | ||
|
|
||
| // Enable TLS client when queue-proxy-ca is specified. | ||
| // At this moment activator with TLS does not disable HTTP. | ||
| // See also https://github.com/knative/serving/issues/12808. | ||
| if tlsEnabled { | ||
| caSecret, err := kubeClient.CoreV1().Secrets(system.Namespace()).Get(ctx, networkConfig.QueueProxyCA, metav1.GetOptions{}) | ||
| caSecret, err := kubeClient.CoreV1().Secrets(system.Namespace()).Get(ctx, netcfg.ServingInternalCertName, metav1.GetOptions{}) | ||
| if err != nil { | ||
| logger.Fatalw("Failed to get secret", zap.Error(err)) | ||
| } | ||
|
|
@@ -173,14 +174,14 @@ func main() { | |
| pool = x509.NewCertPool() | ||
| } | ||
|
|
||
| if ok := pool.AppendCertsFromPEM(caSecret.Data["ca.crt"]); !ok { | ||
| if ok := pool.AppendCertsFromPEM(caSecret.Data[certificates.SecretCaCertKey]); !ok { | ||
| logger.Fatalw("Failed to append ca cert to the RootCAs") | ||
| } | ||
|
|
||
| tlsConf := &tls.Config{ | ||
| RootCAs: pool, | ||
| InsecureSkipVerify: false, | ||
| ServerName: networkConfig.QueueProxySAN, | ||
| ServerName: certificates.FakeDnsName, | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd like to incorporate the destination namespace in this
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As I commented below (or above), it would be possible with "activator always in the path" workaround but it will be super difficult to drop the "activator always in the path" if we support it.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think there are a couple options here:
For now, I'd suggest we do 1, and then add 2 in a future iteration.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Alright, I keep "the activator always in the path" for 1 in this PR. |
||
| MinVersion: tls.VersionTLS12, | ||
| } | ||
| transport = pkgnet.NewProxyAutoTLSTransport(env.MaxIdleProxyConns, env.MaxIdleProxyConnsPerHost, tlsConf) | ||
|
|
@@ -275,15 +276,15 @@ func main() { | |
| }(name, server) | ||
| } | ||
|
|
||
| // Enable TLS server when activator-server-cert is specified. | ||
| // Enable TLS server when internal-encryption is specified. | ||
| // At this moment activator with TLS does not disable HTTP. | ||
| // See also https://github.com/knative/serving/issues/12808. | ||
| if networkConfig.ActivatorCertSecret != "" { | ||
| secret, err := kubeClient.CoreV1().Secrets(system.Namespace()).Get(ctx, networkConfig.ActivatorCertSecret, metav1.GetOptions{}) | ||
| if networkConfig.InternalEncryption { | ||
| secret, err := kubeClient.CoreV1().Secrets(system.Namespace()).Get(ctx, netcfg.ServingInternalCertName, metav1.GetOptions{}) | ||
| if err != nil { | ||
| logger.Fatalw("failed to get secret", zap.Error(err)) | ||
| } | ||
| cert, err := tls.X509KeyPair(secret.Data["tls.crt"], secret.Data["tls.key"]) | ||
| cert, err := tls.X509KeyPair(secret.Data[certificates.SecretCertKey], secret.Data[certificates.SecretPKKey]) | ||
| if err != nil { | ||
| logger.Fatalw("failed to load certs", zap.Error(err)) | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -21,6 +21,7 @@ import ( | |
| "fmt" | ||
|
|
||
| appsv1 "k8s.io/api/apps/v1" | ||
| corev1 "k8s.io/api/core/v1" | ||
| "k8s.io/apimachinery/pkg/api/equality" | ||
| metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
|
|
||
|
|
@@ -30,6 +31,7 @@ import ( | |
| "knative.dev/pkg/logging" | ||
| autoscalingv1alpha1 "knative.dev/serving/pkg/apis/autoscaling/v1alpha1" | ||
| v1 "knative.dev/serving/pkg/apis/serving/v1" | ||
| "knative.dev/serving/pkg/networking" | ||
| "knative.dev/serving/pkg/reconciler/revision/config" | ||
| "knative.dev/serving/pkg/reconciler/revision/resources" | ||
| ) | ||
|
|
@@ -46,6 +48,20 @@ func (c *Reconciler) createDeployment(ctx context.Context, rev *v1.Revision) (*a | |
| return c.kubeclient.AppsV1().Deployments(deployment.Namespace).Create(ctx, deployment, metav1.CreateOptions{}) | ||
| } | ||
|
|
||
| func (c *Reconciler) createSecret(ctx context.Context, ns *corev1.Namespace) (*corev1.Secret, error) { | ||
| secret := &corev1.Secret{ | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess data are filled in by the control-plane reconciler. Was this part of the original design to use the control-plane? It would be nice to have a high a level diagram of secrets created and encrypted paths (maybe there is one).
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There is a doc, but I'm not sure it specifies this level of detail. I'm slightly confused about where the secret comes from here, and what the lifecycle is if internal encryption is disabled. (It may be that it's fine to drop this resource, but I don't quite understand the lifecycle.)
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The lifecycle of the secret in user's ns (queue-proxy's server cert) is:
There are many ways to manage the secret in user's ns and so I chose the simplest way for now. |
||
| ObjectMeta: metav1.ObjectMeta{ | ||
| Name: ns.Name + "-" + networking.ServingCertName, | ||
| Namespace: ns.Name, | ||
| OwnerReferences: []metav1.OwnerReference{*metav1.NewControllerRef(ns, corev1.SchemeGroupVersion.WithKind("Namespace"))}, | ||
| Labels: map[string]string{ | ||
| networking.ServingCertName + "-ctrl": "data-plane", | ||
| }, | ||
| }, | ||
| } | ||
| return c.kubeclient.CoreV1().Secrets(secret.Namespace).Create(ctx, secret, metav1.CreateOptions{}) | ||
| } | ||
|
|
||
| func (c *Reconciler) checkAndUpdateDeployment(ctx context.Context, rev *v1.Revision, have *appsv1.Deployment) (*appsv1.Deployment, error) { | ||
| logger := logging.FromContext(ctx) | ||
| cfgs := config.FromContext(ctx) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -28,11 +28,13 @@ import ( | |
| apierrs "k8s.io/apimachinery/pkg/api/errors" | ||
| metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
|
|
||
| "knative.dev/control-protocol/pkg/certificates" | ||
| "knative.dev/pkg/kmeta" | ||
| "knative.dev/pkg/kmp" | ||
| "knative.dev/pkg/logging" | ||
| "knative.dev/pkg/logging/logkey" | ||
| v1 "knative.dev/serving/pkg/apis/serving/v1" | ||
| "knative.dev/serving/pkg/networking" | ||
| "knative.dev/serving/pkg/reconciler/revision/resources" | ||
| resourcenames "knative.dev/serving/pkg/reconciler/revision/resources/names" | ||
| ) | ||
|
|
@@ -197,3 +199,34 @@ func hasDeploymentTimedOut(deployment *appsv1.Deployment) bool { | |
| } | ||
| return false | ||
| } | ||
|
|
||
| func (c *Reconciler) reconcileSecret(ctx context.Context, rev *v1.Revision) error { | ||
| ns := rev.Namespace | ||
| secretName := ns + "-" + networking.ServingCertName | ||
| logger := logging.FromContext(ctx) | ||
| logger.Info("Reconciling Secret: ", secretName) | ||
|
|
||
| secret, err := c.kubeclient.CoreV1().Secrets(ns).Get(ctx, secretName, metav1.GetOptions{}) | ||
| if apierrs.IsNotFound(err) { | ||
| namespace, err := c.kubeclient.CoreV1().Namespaces().Get(ctx, ns, metav1.GetOptions{}) | ||
| if err != nil { | ||
| return fmt.Errorf("failed to get Namespace %q: %w", secretName, err) | ||
| } | ||
| if secret, err = c.createSecret(ctx, namespace); err != nil { | ||
| return fmt.Errorf("failed to create Secret %q: %w", secretName, err) | ||
| } | ||
| logger.Info("Created Secret: ", secretName) | ||
| } else if err != nil { | ||
| return fmt.Errorf("failed to get secret %q: %w", secretName, err) | ||
| } | ||
|
|
||
| // Verify if secret has been added the data. | ||
| if _, ok := secret.Data[certificates.SecretCertKey]; !ok { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we need both the
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think you want a check for the |
||
| return fmt.Errorf("public cert in the secret is not ready yet") | ||
| } | ||
| if _, ok := secret.Data[certificates.SecretPKKey]; !ok { | ||
| return fmt.Errorf("private key in the secret is not ready yet") | ||
| } | ||
|
|
||
| return nil | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder whether we should provide an option on these errors to fall back to HTTP (for example, if the configmap has been switched, but the cert hasn't propagated yet).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It makes sense, but I believe that current implementation is troublesome to verify the internal traffic is surely encrypted or not.
So some users do not want to expect the fallback feature and I think we should add it later.
(I am thinking that we should add an option based on Istio's mTLS mode like
STRICT(TLS only) andPERMISSIVE(allow to fallback).There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed -- I think it makes sense to be able to operate in three phases: encryption off, encryption opportunistically enabled, encryption required.
I think it's okay to defer this work to "beta"