-
Notifications
You must be signed in to change notification settings - Fork 68
chore(tls): cleanup sample yaml + auto cert for client auth #430
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
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 |
|---|---|---|
|
|
@@ -13,4 +13,4 @@ spec: | |
| providerCfg: | ||
| autoCfg: | ||
| commonName: "etcd-operator-system" | ||
| validityDuration: 365d | ||
| validityDuration: 8760h | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -294,7 +294,11 @@ func (ac *Provider) createNewSecret(ctx context.Context, secretKey client.Object | |
| return fmt.Errorf("validity duration converts to 0 years, must be at least 1 year") | ||
| } | ||
|
|
||
| tlsInfo, selfCertErr := transport.SelfCert(zap.NewNop(), tmpDir, hosts, validityYears) | ||
| // transport.SelfCert generates a self-signed cert whose ExtKeyUsage | ||
| // defaults to ServerAuth only. The operator reuses the server Secret | ||
| // as its own mTLS client identity talking with the etcd cluster. | ||
| // The cert must also carry ClientAuth. | ||
| tlsInfo, selfCertErr := transport.SelfCert(zap.NewNop(), tmpDir, hosts, validityYears, x509.ExtKeyUsageClientAuth) | ||
|
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 am curious why we did not see any issue previously without
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. What I see is that etcdserver pod has never enabled TLS args.
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.
Just double checked the source code on main branch, this is true. We mounted the secret into POD, but actually do not use it at all. Peers talk to each other use http always, and etcd-operator talks to etcd via http always as well. We missed such an elephant! |
||
| if selfCertErr != nil { | ||
| return fmt.Errorf("certificate creation via transport.SelfCert failed: %w", selfCertErr) | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,66 @@ | ||
| package auto | ||
|
|
||
| import ( | ||
| "context" | ||
| "crypto/x509" | ||
| "encoding/pem" | ||
| "testing" | ||
| "time" | ||
|
|
||
| "github.com/stretchr/testify/assert" | ||
| "github.com/stretchr/testify/require" | ||
| corev1 "k8s.io/api/core/v1" | ||
| "k8s.io/apimachinery/pkg/runtime" | ||
| "sigs.k8s.io/controller-runtime/pkg/client" | ||
| clientfake "sigs.k8s.io/controller-runtime/pkg/client/fake" | ||
|
|
||
| interfaces "go.etcd.io/etcd-operator/pkg/certificate/interfaces" | ||
| ) | ||
|
|
||
| // The operator uses the server certificate as a client to check etcd member | ||
| // status, so the auto-generated cert must carry the ClientAuth key usage. | ||
| func TestEnsureCertificateSecretCertHasClientAuth(t *testing.T) { | ||
| scheme := runtime.NewScheme() | ||
| require.NoError(t, corev1.AddToScheme(scheme)) | ||
|
|
||
| fakeClient := clientfake.NewClientBuilder().WithScheme(scheme).Build() | ||
| provider := New(fakeClient) | ||
|
|
||
| secretKey := client.ObjectKey{Name: "test-server-tls", Namespace: "default"} | ||
| cfg := &interfaces.Config{ | ||
| CommonName: "etcd.test", | ||
| ValidityDuration: interfaces.DefaultAutoValidity, | ||
| AltNames: interfaces.AltNames{ | ||
| DNSNames: []string{"test.default.svc.cluster.local"}, | ||
| }, | ||
| } | ||
|
|
||
| ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) | ||
| defer cancel() | ||
|
|
||
| require.NoError(t, provider.EnsureCertificateSecret(ctx, secretKey, cfg)) | ||
|
|
||
| // Fetch the generated Secret and verify the cert carries ClientAuth. | ||
| secret := &corev1.Secret{} | ||
| require.NoError(t, fakeClient.Get(ctx, secretKey, secret)) | ||
|
|
||
| certPEM, ok := secret.Data["tls.crt"] | ||
| require.True(t, ok, "secret must contain tls.crt") | ||
| require.NotEmpty(t, certPEM) | ||
|
|
||
| block, _ := pem.Decode(certPEM) | ||
| require.NotNil(t, block, "tls.crt is valid PEM") | ||
|
|
||
| cert, err := x509.ParseCertificate(block.Bytes) | ||
| require.NoError(t, err, "tls.crt parses as a certificate") | ||
|
|
||
| assert.Contains(t, cert.ExtKeyUsage, x509.ExtKeyUsageClientAuth, | ||
| "auto cert must carry ClientAuth so the operator can present it as a client (design D4-a)") | ||
| assert.Contains(t, cert.ExtKeyUsage, x509.ExtKeyUsageServerAuth, | ||
| "auto cert must keep ServerAuth for its primary server role") | ||
|
|
||
| // Sanity: the secret is structurally complete for an etcd TLS mount. | ||
| assert.NotEmpty(t, secret.Data["tls.key"]) | ||
| assert.NotEmpty(t, secret.Data["ca.crt"]) | ||
| assert.Equal(t, corev1.SecretTypeTLS, secret.Type) | ||
| } |
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.
We need to consider an alternative utility to replace the time.ParseDuration, we should support duration like "365d"
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.
OK, there gona be some research work. Do you mind that I handle it in a follow-up PR?
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.
sure