diff --git a/app/namespace.go b/app/namespace.go index 8fd924c8..c2f7ae4a 100644 --- a/app/namespace.go +++ b/app/namespace.go @@ -1060,46 +1060,69 @@ func NewNamespaceCommand(getNamespaceClientFn GetNamespaceClientFn) (CommandOut, CaCertificateFlag, CaCertificateFileFlag, caCertificateFingerprintFlag, + &cli.BoolFlag{ + Name: "all", + Usage: "If set, all existing certificates will be removed", + }, }, Action: func(ctx *cli.Context) error { + removeAll := ctx.Bool("all") + if removeAll && (ctx.String(caCertificateFingerprintFlagName) != "" || + ctx.String(CaCertificateFlagName) != "" || + ctx.Path(CaCertificateFileFlagName) != "") { + return fmt.Errorf("cannot use --all with other certificate flags") + } + n, existingCerts, err := c.parseExistingCerts(ctx) if err != nil { return err } - var certs caCerts - if ctx.String(caCertificateFingerprintFlagName) != "" { - certs, err = removeCertWithFingerprint( - existingCerts, - ctx.String(caCertificateFingerprintFlagName), - ) - if err != nil { - return err - } - } else { - readCerts, err := readAndParseCACerts(ctx) - if err != nil { - return err + + if removeAll && (n.Spec.AuthMethod == namespace.AUTH_METHOD_MTLS || + n.Spec.AuthMethod == namespace.AUTH_METHOD_API_KEY_OR_MTLS) { + return fmt.Errorf("cannot remove all certificates when mTLS is enabled") + } + + var certBundle string + if !removeAll { + var certs caCerts + if ctx.String(caCertificateFingerprintFlagName) != "" { + certs, err = removeCertWithFingerprint( + existingCerts, + ctx.String(caCertificateFingerprintFlagName), + ) + if err != nil { + return err + } + } else { + readCerts, err := readAndParseCACerts(ctx) + if err != nil { + return err + } + certs, err = removeCerts(existingCerts, readCerts) + if err != nil { + return err + } } - certs, err = removeCerts(existingCerts, readCerts) + certBundle, err = certs.bundle() if err != nil { return err } } - bundle, err := certs.bundle() - if err != nil { - return err - } - if n.Spec.AcceptedClientCa == bundle { + + if n.Spec.AcceptedClientCa == certBundle { if ctx.Bool(IdempotentFlagName) { return nil } return errors.New("nothing to change") } - n.Spec.AcceptedClientCa = bundle + + n.Spec.AcceptedClientCa = certBundle y, err := ConfirmPrompt(ctx, "removing ca certificates can cause connectivity disruption if there are any clients using certificates that cannot be verified. confirm remove?") if err != nil || !y { return err } + return c.updateNamespace(ctx, n) }, }, diff --git a/app/namespace_test.go b/app/namespace_test.go index 505ce922..e1ab1339 100644 --- a/app/namespace_test.go +++ b/app/namespace_test.go @@ -634,6 +634,18 @@ func (s *NamespaceTestSuite) TestUpdateRemoveCA() { expectGet: func(g *namespaceservice.GetNamespaceResponse) {}, args: []string{"namespace", "accepted-client-ca", "remove", "--namespace", ns}, expectErr: true, + }, { + name: "err cert flag with all", + args: []string{"namespace", "accepted-client-ca", "remove", "--namespace", ns, "--ca-certificate", cert1, "--all"}, + expectErr: true, + }, { + name: "err cert file flag with all", + args: []string{"namespace", "accepted-client-ca", "remove", "--namespace", ns, "--ca-certificate-file", path, "--all"}, + expectErr: true, + }, { + name: "err cert fingerprint flag with all", + args: []string{"namespace", "accepted-client-ca", "remove", "--namespace", ns, "--ca-certificate-fingerprint", cert2fingerprint, "--all"}, + expectErr: true, }, { name: "remove 1st cert", args: []string{"n", "ca", "remove", "-n", ns, "--ca-certificate", cert1}, @@ -648,6 +660,36 @@ func (s *NamespaceTestSuite) TestUpdateRemoveCA() { expectUpdate: func(r *namespaceservice.UpdateNamespaceRequest) { r.Spec.AcceptedClientCa = cert1 }, + }, { + name: "err remove all certs with mtls", + args: []string{"n", "ca", "remove", "-n", ns, "--all"}, + expectGet: func(g *namespaceservice.GetNamespaceResponse) {}, + expectErr: true, + }, { + name: "err remove all certs with api_key_or_mtls", + args: []string{"n", "ca", "remove", "-n", ns, "--all"}, + expectGet: func(g *namespaceservice.GetNamespaceResponse) { + g.Namespace.Spec.AuthMethod = namespace.AUTH_METHOD_API_KEY_OR_MTLS + }, + expectErr: true, + }, { + name: "remove all certs: api_key", + args: []string{"n", "ca", "remove", "-n", ns, "--all"}, + expectGet: func(g *namespaceservice.GetNamespaceResponse) { + g.Namespace.Spec.AuthMethod = namespace.AUTH_METHOD_API_KEY + }, + expectUpdate: func(r *namespaceservice.UpdateNamespaceRequest) { + r.Spec.AcceptedClientCa = "" + }, + }, { + name: "remove all certs: restricted", + args: []string{"n", "ca", "remove", "-n", ns, "--all"}, + expectGet: func(g *namespaceservice.GetNamespaceResponse) { + g.Namespace.Spec.AuthMethod = namespace.AUTH_METHOD_RESTRICTED + }, + expectUpdate: func(r *namespaceservice.UpdateNamespaceRequest) { + r.Spec.AcceptedClientCa = "" + }, }, { name: "remove unknown cert", args: []string{"n", "ca", "r", "-n", ns, "--ca-certificate", cert3}, @@ -710,6 +752,7 @@ func (s *NamespaceTestSuite) TestUpdateRemoveCA() { "attr1": namespace.SEARCH_ATTRIBUTE_TYPE_BOOL, }, RetentionDays: 7, + AuthMethod: namespace.AUTH_METHOD_MTLS, }, State: namespace.STATE_ACTIVE, ResourceVersion: "ver1",