[Bug]: `step ca provisioner add --ca-config` requires `--ca-url`/`--root` even for purely local/offline edits

Ouverte
#1,705 0 commentaires 1 réaction 0 personnes assignées Voir sur GitHub

Les mainteneurs répondent en général sous 1 jour

@vulragrag-star y travaille déjà.

Depuis le 16/9/2026.

  • #1706 par @vulragrag-star — ouverte

Évaluation

Difficulté
4/5
Temps estimé
3-5 jours
Accessibilité débutants
20/100
Type d'issue
Bug
Clarté
Clairement spécifiée
Activité
À l'abandon
Stack technique
go
Domaine
cli

Piste de recherche

Start with command/ca/provisioner/provisioner.go and utils/cautils/client.go, especially newCRUDClient and NewUnauthenticatedAdminClient; compare the local-config path with the behavior described for PR #809. Check the provisioner command tests and add coverage for editing an existing --ca-config without network flags. Done means offline edits work without requiring --ca-url or --root and a hanging CA does not block them; a linked pull request is already open.

Rédigé par le modèle d'indexation à partir du texte de l'issue.

Description

bug needs triage
Steps to Reproduce

Reproduced on 0.24.x–0.30.6; does not reproduce on 0.22.0 (pre-#809 behavior)

Create the ca.json (no step ca init, no defaults.json — the CA has never run) and a root cert, exactly as an offline bootstrap tool would produce them.

cat > /home/step/ca.json <<'EOF'
{
  "root": "/home/step/certs/root_ca.crt",
  "crt": "/home/step/certs/intermediate_ca.crt",
  "key": "awskms:key-id=test-key-id",
  "kms": { "type": "awskms", "uri": "awskms:region=us-east-1" },
  "address": ":9000",
  "dnsNames": ["ca.svc.cluster.local"],
  "db": { "type": "mysql", "dataSource": "user:pass@tcp(host:3306)/", "database": "smallstep" }
}
EOF

step certificate create --profile root-ca --no-password --insecure \
  "Test Root" /home/step/root_ca.crt /home/step/root_ca_key

echo mypassword > /home/step/pass.txt

step ca provisioner add step-issuer --type=JWK --create \
  --x509-default-dur=4368h --x509-min-dur=240h --x509-max-dur=8760h \
  --password-file=/home/step/pass.txt \
  --ca-config /home/step/config/ca.json
Your Environment
  • OS: Alpine Step CLI container running from docker (colima) on MacOS 26
  • step CLI version: 0.30.6
Expected Behavior

Since --ca-config points at an existing, valid ca.json file and there is no CA to talk to, step should edit that file locally without requiring any CA connectivity flags — as it did prior to v0.23.1, and as step ca provisioner add name --type=JWK --create --ca-config ca.json is still documented/shown in some examples without --ca-url.

Create a JWK provisioner and explicitly select the configuration file to update:

    step ca provisioner add cicd --type JWK --create --ca-config /path/to/ca.json

This example is defined in the EXAMPLES section of add.go itself (command/ca/provisioner/add.go#L227-L229), and is reproduced verbatim in:

Neither the source comment, the rendered --help text, nor the public docs page have been updated to add --ca-url/--root to this example since #809 was merged, even though running it as written now fails with error generating admin client: 'step ca provisioner add' requires the '--ca-url' flag.

Actual Behavior

Running this

step ca provisioner add step-issuer --type=JWK --create \
  --x509-default-dur=4368h --x509-min-dur=240h --x509-max-dur=8760h \
  --password-file=/home/step/pass.txt \
  --ca-config /home/step/config/ca.json

results in

error generating admin client: 'step ca provisioner add' requires the '--ca-url' flag

Adding --ca-url then immediately produces a second, similarly unconditional error:

error generating admin client: 'step ca provisioner add' requires the '--root' flag

Supplying both --ca-url (pointing at a Kubernetes Service hostname whose backing CA pod is not yet running/reachable) and --root (pointing at the just-created root cert on disk) does not fail immediately — instead, it can hang for the full HTTP client timeout before finally falling back to the local edit and failing:

step ca provisioner add step-issuer --type=JWK --create \
    --x509-default-dur=4368h --x509-min-dur=240h --x509-max-dur=8760h \
    --password-file=/home/step/pass.txt \
    --ca-config /home/step/config/ca.json \
    --ca-url "https://google.com:19999" \
    --root /home/step/certs/root_ca.crt

client GET https://google.com:19999/admin/admins failed: context deadline exceeded (Client.Timeout exceeded while awaiting headers)

(Waited the full request timeout, then failed outright — the local ca.json edit never happens in this case.)

By contrast, a --ca-url that fails fast (connection refused, e.g. https://127.0.0.1:1, or unresolvable DNS) correctly falls back to the local edit within milliseconds:

step ca provisioner add step-issuer --type=JWK --create \
    --x509-default-dur=4368h --x509-min-dur=240h --x509-max-dur=8760h \
    --password-file=/home/step/pass.txt \
    --ca-config /home/step/config/ca.json \
    --ca-url "https://127.0.0.1:1" \
    --root /home/step/certs/root_ca.crt

✔ CA Configuration: /home/step/config/ca.json
Success! Your `step-ca` config has been updated. ...

So the actual offline edit works fine — but only by accident, and only if the network error surfaces quickly. In a real Kubernetes environment, whether the CA's own not-yet-running Service address resolves to "fast connection refused" vs. "silent/hanging" is not something a bootstrap script can control or rely on, making this workflow flaky.

Additional Context

Root cause (traced in source)

In command/ca/provisioner/provisioner.go, newCRUDClient calls cautils.NewUnauthenticatedAdminClient(cliCtx) unconditionally before ever looking at cfgFile/--ca-config:

// utils/cautils/client.go
func NewUnauthenticatedAdminClient(ctx *cli.Context, opts ...ca.ClientOption) (*ca.AdminClient, error) {
	caURL, err := flags.ParseCaURLIfExists(ctx)
	if err != nil {
		return nil, err
	}
	if caURL == "" {
		return nil, errs.RequiredFlag(ctx, "ca-url")   // <-- hard-fails here if --ca-url absent
	}
	root := ctx.String("root")
	if root == "" {
		root = pki.GetRootCAPath()
		if _, err := os.Stat(root); err != nil {
			return nil, errs.RequiredFlag(ctx, "root")  // <-- hard-fails here if no root cert found
		}
	}
	...
	return ca.NewAdminClient(caURL, opts...)
}

Only once an AdminClient is successfully constructed does provisioner.go's newCRUDClient call .IsEnabled() and inspect the resulting error type to decide whether to fall back to local editing:

// command/ca/provisioner/provisioner.go
func newCRUDClient(cliCtx *cli.Context, cfgFile string) (crudClient, error) {
	unauthAdminClient, err := cautils.NewUnauthenticatedAdminClient(cliCtx)
	if err != nil {
		return nil, fmt.Errorf("error generating admin client: %w", err)
	}
	var netErr *net.OpError
	err = unauthAdminClient.IsEnabled()
	switch {
	case errors.As(err, &netErr) || errors.Is(err, ca.ErrAdminAPINotImplemented):
		// falls back to local ca.json edit
		...
	case errors.Is(err, ca.ErrAdminAPINotAuthorized):
		return cautils.NewAdminClient(cliCtx)
	default:
		return nil, err
	}
}

This logic was introduced in PR #809 ("Check for remote admin api before prompting for creds", commit 4c0891c03ecd06c71afd3cf08716e6d3c558f8d7), first released in v0.23.1. Before that commit, newCRUDClient checked --ca-config/cfgFile first and went straight to the local-edit path (newCaConfigClient) if the file existed — no --ca-url/--root were required at all in that case.

This is the same category of bug previously reported and fixed for step ca renew --offline in #627 ("Offline step ca renew insists on --ca-url and --root parameters") — but that fix was specific to the renew/certificate offline code path (cautils.NewClient + flags.Offline) and does not cover step ca provisioner add/update/remove, which use the separate newCRUDClient path shown above and have no --offline flag at all.

Related issues

  • #627 — Offline step ca renew insists on --ca-url and --root parameters (same category, different command, fixed by gating on --offline)
  • #473 — step ca provisioner add panics on fresh k8s install from helm chart (older, different failure mode, but same general theme of provisioner add being awkward to use against a CA that isn't fully live yet)
Contributing

Vote on this issue by adding a 👍 reaction.
To contribute a fix for this issue, leave a comment (and link to your pull request, if you've opened one already).

Langage dominant
Go
Étoiles
4.3k
Forks
320
Merge moyen
2 j 3 h
PR mergées (30 j)
11

Préparer son environnement

Par où commencer

  1. Lisez l'issue en entier, puis le guide de contribution du projet.
  2. Signalez en commentaire que vous la prenez — cela évite que deux personnes fassent le même travail.
  3. Forkez le dépôt et travaillez sur une branche.
  4. Ouvrez une pull request qui référence le numéro de l'issue.

Autres issues de smallstep/cli

Toutes les issues de smallstep/cli

Issues similaires

Plus d'issues Go

Recevez les nouvelles issues par e-mail

Un résumé court des issues GitHub adaptées aux débutants.