Step CLI should not try to open a TTY if one is not available
jdoss opened this issue · comments
I have run into some cases where step cli tries to open a TTY to prompt for input on an error while being called by systemd. This results in an unhelpful error message such as in the journal:
May 02 08:55:28 sw-0608 step[702563]: error allocating terminal: open /dev/tty: no such device or address
Which doesn't give the user any clear information as to why the command failed when being called via systemd. For example this is the unit file that caused the above error:
[Unit]
Description=Issue a smallstep certificate for %i
After=network-online.target step-ca-bootstrap.service
Wants=network-online.target step-ca-bootstrap.service
Before=%i.service
[Service]
Type=oneshot
PassEnvironment=HOSTNAME
EnvironmentFile=/etc/step/env
ExecStartPre=step ca root /etc/%i/tls/%i-ca.crt --force
ExecStart=step ca certificate ${HOSTNAME} \
--ca-url ${STEP_CA_URL} \
--provisioner ${STEP_CA_PROVISIONER} \
/etc/%i/tls/%i.crt \
/etc/%i/tls/%i.key \
--san 127.0.0.1 \
--not-after 24h \
--force \
--provisioner-password-file=${STEP_PROVISIONER_PASSWORD_FILE}
Restart=on-failure
RestartSec=10s
[Install]
WantedBy=multi-user.target
The above systemd error was generated by systemd's EnvironmentFile
directive removing the PassEnvironment=HOSTNAME
and it caused ${HOSTNAME}
to be blank. This caused step to try to open a TTY and prompt for input which then fails because one does not exist when running step
via systemd.
A reproducer for this specific case:
go run cmd/step/main.go ca certificate "" s.crt s.key
✔ What DNS names or IP addresses would you like to use? (e.g. internal.smallstep.com): <some input>
✔ Provisioner: Admin JWK (JWK) [kid: -bpK0AkvgJpt4Ju9_MaWDrzQL_fnOKOJiI521JkiuHU]
Please enter the password to decrypt the provisioner key:
token subject '<some input>' and argument '' do not match
exit status 1
Normally an invocation without the SAN set would result in the CLI returning with an error already. Apparently The ""
is a valid input, but results in hitting the following code in utils/cautils/certificate_flow.go
:
if subject == "" {
subject, err = ui.Prompt("What DNS names or IP addresses would you like to use? (e.g. internal.smallstep.com)", ui.WithValidateNotEmpty())
if err != nil {
return "", err
}
}
It is that prompt for <some input>
that results in the open /dev/tty: no such device or address
error.
To solve this, the CLI 1) needs to be aware of the input and validate that the input is not empty (and otherwise OK) and 2) needs to be aware of the environment its operating in. If these are true, the CLI can return a clearer error message about missing parameters or prompt for them when that's supported by its environment. This is not specific to this error; there are more cases where a similar error can happen.
I just got bit by this, it took me a while to understand what was actually going wrong, as I couldn't understand why the system was trying to open the tty. In my case it was being triggered by the certificate being in place already but not passing --force
so step was trying to prompt me for the overwrite.
A related issue: #502
Following, hit this issue in github actions since the runner was being re-used and the cert already existed, a step ca bootstrap
was requesting on stdin overwrite (y/n)?
in a non-tty context. Same a @the-maldridge
I get same error too use by ansible
- name: Generate an OpenSSL certificate signed with your Step-CA
ansible.builtin.shell:
cmd: sudo step-cli certificate sign --profile intermediate-ca ipa.csr root_ca.crt /etc/step-ca/secrets/root_ca_key | sudo tee -a ipa.crt
chdir: /etc/step-ca/certs