From 300948894f5565c2a54f2cdcae60be345593b53d Mon Sep 17 00:00:00 2001 From: Carl Tashian Date: Thu, 29 Jan 2026 17:29:38 +0000 Subject: [PATCH 1/3] Block new Linked CA creation in step ca init Remove support for creating new Linked CA deployments: - Remove "linked" from --deployment-type flag options - Return clear error when --deployment-type=linked is used - Remove "Linked" from interactive deployment type selection - Update help text to point users to Step CA Pro Existing Linked CAs can still run (via certificates repo) but new ones cannot be created in open-source step-ca. This is phase 1 of removing Linked CA from open-source step-ca. A future release will remove the functionality entirely. Co-Authored-By: Claude Opus 4.5 --- CHANGELOG.md | 6 ++++++ command/ca/init.go | 42 ++++++++++++++++++------------------------ 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 53b19dadc..cdc2c69ba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,12 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## [x.y.z] - TBD +### Deprecated + +- Creating new Linked CA deployments via `step ca init --deployment-type=linked` + is no longer supported in open-source step-ca. Users requiring Linked CA + features should use Step CA Pro. See https://smallstep.com/product/step-ca-pro/ + ### Changed - Suppress output messages for `step certificate needs-renewal` and `step ssh diff --git a/command/ca/init.go b/command/ca/init.go index 52871389a..08025a5dd 100644 --- a/command/ca/init.go +++ b/command/ca/init.go @@ -81,19 +81,15 @@ func initCommand() cli.Command { Choose standalone if you'd like to run step-ca yourself and do not want cloud services or commercial support. - **linked** - : An instance of step-ca with locally managed keys that connects to your - Certificate Manager account for provisioner management, alerting, - reporting, revocation, and other managed services. - Choose linked if you'd like cloud services and support, but need to - control your authority's signing keys. - **hosted** : A highly available, fully-managed instance of step-ca run by smallstep just for you. Choose hosted if you'd like cloud services and support. -: More information and pricing at: https://u.step.sm/cm`, +: More information and pricing at: https://u.step.sm/cm + +: Note: Linked CA deployment type is available in Step CA Pro. + See https://smallstep.com/product/step-ca-pro/`, }, cli.StringFlag{ Name: "name", @@ -445,10 +441,7 @@ func initAction(ctx *cli.Context) (err error) { ui.Println() return nil } - // When initializing a linked CA, providing the --acme flag doesn't currently - // result in the default ACME provisioner being added. We may want to support this - // for ease of use, but this seems to require a bit of refactoring when generating - // the full CA configuration with DB initialization. + // The --acme flag is only supported with standalone deployments. if deploymentType != pki.StandaloneDeployment && addDefaultACMEProvisioner { return fmt.Errorf("adding a default ACME provisioner by providing the --acme flag is not supported with deployment type %q.\nPlease use `step ca provisioner add acme --type ACME` after initializing your CA", deploymentType.String()) } @@ -606,9 +599,7 @@ func initAction(ctx *cli.Context) (err error) { pki.WithSuperAdminSubject(firstSuperAdminSubject), ) } - if deploymentType == pki.LinkedDeployment { - pkiOpts = append(pkiOpts, pki.WithAdmin()) - } else if ctx.Bool("ssh") { + if ctx.Bool("ssh") { pkiOpts = append(pkiOpts, pki.WithSSH()) } if noDB { @@ -638,7 +629,7 @@ func initAction(ctx *cli.Context) (err error) { // but this is not common on RA mode. ui.Println("Choose a password for your first provisioner.", ui.WithValue(password)) } else { - // Linked CAs will use OIDC as a first provisioner. + // Hosted deployments use a different provisioner setup. if pkiOnly || deploymentType != pki.StandaloneDeployment { ui.Println("Choose a password for your CA keys.", ui.WithValue(password)) } else { @@ -767,9 +758,16 @@ func promptDeploymentType(ctx *cli.Context, isRA bool) (pki.DeploymentType, erro return pki.StandaloneDeployment, nil } + // Linked deployment type is no longer available in open-source step-ca. + // It is available in Step CA Pro. See https://smallstep.com/product/step-ca-pro/ + if deploymentType == "linked" { + return 0, errors.New("Creating new Linked CAs is no longer supported in open-source step-ca.\n" + + "Linked CA functionality is available in Step CA Pro.\n" + + "See: https://smallstep.com/product/step-ca-pro/") + } + deploymentTypes = []deployment{ {"Standalone", "step-ca instance you run yourself", pki.StandaloneDeployment}, - {"Linked", "standalone, plus cloud configuration, reporting & alerting", pki.LinkedDeployment}, {"Hosted", "fully-managed step-ca cloud instance run for you by smallstep", pki.HostedDeployment}, } @@ -777,25 +775,21 @@ func promptDeploymentType(ctx *cli.Context, isRA bool) (pki.DeploymentType, erro switch deploymentType { case "": // Deployment type Hosted is not supported for RAs - deploymentTypes = deploymentTypes[:2] + deploymentTypes = deploymentTypes[:1] case "standalone": return pki.StandaloneDeployment, nil - case "linked": - return pki.LinkedDeployment, nil default: - return 0, errs.InvalidFlagValue(ctx, "deployment-type", deploymentType, "standalone or linked") + return 0, errs.InvalidFlagValue(ctx, "deployment-type", deploymentType, "standalone") } } else { switch deploymentType { case "": case "standalone": return pki.StandaloneDeployment, nil - case "linked": - return pki.LinkedDeployment, nil case "hosted": return pki.HostedDeployment, nil default: - return 0, errs.InvalidFlagValue(ctx, "deployment-type", deploymentType, "standalone, linked or hosted") + return 0, errs.InvalidFlagValue(ctx, "deployment-type", deploymentType, "standalone or hosted") } } From d62f394b34624fc31cf3f7f37b6c89e455eda509 Mon Sep 17 00:00:00 2001 From: Carl Tashian Date: Thu, 29 Jan 2026 19:56:13 +0000 Subject: [PATCH 2/3] Update migration instructions with step-ca import command Add complete migration workflow using the new step-ca import command to the Linked CA deprecation section. Co-Authored-By: Claude Opus 4.5 --- CHANGELOG.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index cdc2c69ba..b40575399 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,6 +34,32 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. is no longer supported in open-source step-ca. Users requiring Linked CA features should use Step CA Pro. See https://smallstep.com/product/step-ca-pro/ +#### Migrating from Linked CA to Standalone + +To migrate an existing linked CA to standalone mode: + +1. Export your current configuration including cloud-stored provisioners: + ``` + step-ca export $(step path)/config/ca.json --token $STEP_CA_TOKEN > export.json + ``` + +2. Stop the CA + +3. Update your `ca.json`: + - Remove the `authority.linkedca` section + - Ensure `authority.enableAdmin: true` + - Ensure `db` is configured + +4. Import the provisioners and admins: + ``` + step-ca import $(step path)/config/ca.json export.json + ``` + +5. Start the CA without the `--token` flag: + ``` + step-ca $(step path)/config/ca.json + ``` + ### Changed - Suppress output messages for `step certificate needs-renewal` and `step ssh From bbe7724a18546539f77f8c083b82bbd177b6dc2b Mon Sep 17 00:00:00 2001 From: Carl Tashian Date: Mon, 2 Feb 2026 09:44:41 -0800 Subject: [PATCH 3/3] Add tests for promptDeploymentType to improve code coverage Cover the linked deployment type deprecation error and other deployment type validation paths. Co-Authored-By: Claude Opus 4.5 --- command/ca/init_test.go | 83 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) diff --git a/command/ca/init_test.go b/command/ca/init_test.go index 06aa75793..fc2ff72d2 100644 --- a/command/ca/init_test.go +++ b/command/ca/init_test.go @@ -1,9 +1,13 @@ package ca import ( + "flag" "reflect" "testing" + "github.com/smallstep/certificates/pki" + "github.com/stretchr/testify/assert" + "github.com/urfave/cli" _ "go.step.sm/crypto/kms/azurekms" ) @@ -119,3 +123,82 @@ func Test_processDNSValue(t *testing.T) { }) } } + +func Test_promptDeploymentType(t *testing.T) { + app := &cli.App{} + + tests := []struct { + name string + deploymentType string + isRA bool + want pki.DeploymentType + wantErr string + }{ + { + name: "ok/standalone", + deploymentType: "standalone", + isRA: false, + want: pki.StandaloneDeployment, + }, + { + name: "ok/standalone-ra", + deploymentType: "standalone", + isRA: true, + want: pki.StandaloneDeployment, + }, + { + name: "ok/hosted", + deploymentType: "hosted", + isRA: false, + want: pki.HostedDeployment, + }, + { + name: "fail/linked-deprecated", + deploymentType: "linked", + isRA: false, + wantErr: "Creating new Linked CAs is no longer supported in open-source step-ca", + }, + { + name: "fail/linked-deprecated-ra", + deploymentType: "linked", + isRA: true, + wantErr: "Creating new Linked CAs is no longer supported in open-source step-ca", + }, + { + name: "fail/invalid-type", + deploymentType: "invalid", + isRA: false, + wantErr: "invalid value", + }, + { + name: "fail/invalid-type-ra", + deploymentType: "invalid", + isRA: true, + wantErr: "invalid value", + }, + { + name: "fail/hosted-ra", + deploymentType: "hosted", + isRA: true, + wantErr: "invalid value", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + set := flag.NewFlagSet("test", 0) + _ = set.String("deployment-type", "", "") + ctx := cli.NewContext(app, set, nil) + _ = ctx.Set("deployment-type", tt.deploymentType) + + got, err := promptDeploymentType(ctx, tt.isRA) + if tt.wantErr != "" { + assert.Error(t, err) + assert.Contains(t, err.Error(), tt.wantErr) + } else { + assert.NoError(t, err) + assert.Equal(t, tt.want, got) + } + }) + } +}