[Bug]: `step ca provisioner add --ca-config` requires `--ca-url`/`--root` even for purely local/offline edits
Les mainteneurs répondent en général sous 1 jour
Évaluation
- Difficulté
- 4/5
- Temps estimé
- 3-5 jours
- Accessibilité débutants
- 20/100
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
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:
- the live
--helpoutput of the CLI (confirmed on0.30.6:step ca provisioner add --help) - the public reference docs at https://smallstep.com/docs/step-cli/reference/ca/provisioner/add/#examples (currently live, second example under "Examples")
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 renewinsists on --ca-url and --root parameters (same category, different command, fixed by gating on--offline) - #473 —
step ca provisioner addpanics on fresh k8s install from helm chart (older, different failure mode, but same general theme ofprovisioner addbeing 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
- Aucun Dockerfile ni fichier Docker Compose
- Propose un modèle de pull request
- Lire le guide de contribution
Par où commencer
- Lisez l'issue en entier, puis le guide de contribution du projet.
- Signalez en commentaire que vous la prenez — cela évite que deux personnes fassent le même travail.
- Forkez le dépôt et travaillez sur une branche.
- Ouvrez une pull request qui référence le numéro de l'issue.
Autres issues de smallstep/cli
-
[Bug]: `Match exec "step ssh check-host %h"` causes SSH connections through ProxyJump to hangOuvertebug needs triage
Difficulté 2/5 1-3 heures Accessibilité débutants 74/100
Les mainteneurs répondent en général sous 1 jour
-
enhancement needs triage
Difficulté 4/5 3-5 jours Accessibilité débutants 32/100
Les mainteneurs répondent en général sous 1 jour
-
enhancement needs triage
Difficulté 3/5 1-2 jours Accessibilité débutants 57/100
smallstep/cli#1713 · 1 réaction ·
Les mainteneurs répondent en général sous 1 jour
-
build s390x and riscv64 binariesOuverteenhancement needs triage
Difficulté 4/5 3-5 jours Accessibilité débutants 35/100
smallstep/cli#1710 · 1 commentaire · 1 réaction ·
Les mainteneurs répondent en général sous 1 jour
-
bug needs triage
Difficulté 3/5 1-2 jours Accessibilité débutants 63/100
Les mainteneurs répondent en général sous 1 jour
Toutes les issues de smallstep/cli
Issues similaires
-
Difficulté 2/5 1-3 heures Accessibilité débutants 79/100
Les mainteneurs répondent en général sous 1 jour
-
Difficulté 2/5 1-3 heures Accessibilité débutants 85/100
openmcp-project/service-provider-crossplane#330 ·
Les mainteneurs répondent en général sous 1 jour
-
Difficulté 2/5 1-3 heures Accessibilité débutants 88/100
openmcp-project/service-provider-kro#220 ·
Les mainteneurs répondent en général sous 1 jour
-
Difficulté 2/5 1-3 heures Accessibilité débutants 78/100
-
[FEATURE]: Set fieldPath and reason on the CEL validation rulesPeut-être pris @bjosv l’a pris aujourd’hui. Ouverteenhancement needs-triage
Difficulté 2/5 1-3 heures Accessibilité débutants 72/100
valkey-io/valkey-operator#618 ·
Les mainteneurs répondent en général sous 3 jours