Bug 2246923 - 3.6.0 regression: `stratis key set --capture-key` fails with termios.error: (25, 'Inappropriate ioctl for device')
Summary: 3.6.0 regression: `stratis key set --capture-key` fails with termios.error: (...
Keywords:
Status: ASSIGNED
Alias: None
Product: Fedora
Classification: Fedora
Component: stratis-cli
Version: 39
Hardware: Unspecified
OS: Linux
unspecified
high
Target Milestone: ---
Assignee: mulhern
QA Contact: Fedora Extras Quality Assurance
URL: https://cockpit-logs.us-east-1.linode...
Whiteboard: CockpitTest
Depends On:
Blocks:
TreeView+ depends on / blocked
 
Reported: 2023-10-30 07:01 UTC by Martin Pitt
Modified: 2023-10-31 15:48 UTC (History)
4 users (show)

Fixed In Version:
Doc Type: If docs needed, set a value
Doc Text:
Clone Of:
Environment:
Last Closed:
Type: ---
Embargoed:


Attachments (Terms of Use)

Description Martin Pitt 2023-10-30 07:01:08 UTC
Cockpit's tests recently started to run nightly checks on Fedora 39 updates-testing (until Friday it was still F38). This found a stratis-cli regression from the recent 3.5.3 → 3.6.0 update [1].

Our test needs to create a stratis key noninteractively. `stratis key set` has an option for that:

 --capture-key         Read key from stdin with no terminal echo or userspace buffer storage


In 3.5.3 this already does not work very well: When run in a normal terminal or ssh, it asks on the PTY despite the option:

# echo s3kr1t | stratis key set --capture-key pool1
Enter key data followed by the return key:

It even does that if none of stdin, out, err is on a tty:

# echo s3kr1t | stratis key set --capture-key pool1 2>&1 | cat
Enter key data followed by the return key: 

And *even* when running through nohup. The smallest hammer that works is

systemd-run --unit key --wait sh -exc 'echo s3kr1t | stratis key set --capture-key pool1; echo done'


With 3.5.3 this worked:

Running as unit: key.service
Finished with result: success
Main processes terminated with: code=exited/status=0
Service runtime: 541ms
CPU time consumed: 209ms

# stratis key 
Key Description     
pool1          

But with 3.6.0 even that doesn't work any more, and it runs into a traceback:

# systemd-run --unit key --wait sh -exc 'echo s3kr1t | stratis key set --capture-key pool1; echo done'
Running as unit: key.service
Finished with result: exit-code
Main processes terminated with: code=exited/status=1
Service runtime: 415ms
CPU time consumed: 186ms

# journalctl -u key

/usr/lib64/python3.12/getpass.py:91: GetPassWarning: Can not control echo on the terminal.
  passwd = fallback_getpass(prompt, stream)
Warning: Password input may be echoed.
Enter key data followed by the return key:
Warning: Password input may be echoed.
Verify key data entered: stratis encountered an unexpected error during execution. Please report the error and include in your report the stack trace shown below.
Traceback (most recent call last):
  File "/usr/lib64/python3.12/getpass.py", line 69, in unix_getpass
    old = termios.tcgetattr(fd)     # a copy to save
          ^^^^^^^^^^^^^^^^^^^^^
termios.error: (25, 'Inappropriate ioctl for device')
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
  File "/usr/lib/python3.12/site-packages/stratis_cli/_main.py", line 43, in the_func
    result.func(result)
  File "/usr/lib/python3.12/site-packages/stratis_cli/_parser/_parser.py", line 95, in wrapped_func
    func(*args)
  File "/usr/lib/python3.12/site-packages/stratis_cli/_actions/_top.py", line 171, in set_key
    ((changed, existing_modified), return_code, message) = _add_update_key(
                                                           ^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.12/site-packages/stratis_cli/_actions/_top.py", line 76, in _add_update_key
    verify = getpass(prompt="Verify key data entered: ")
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib64/python3.12/getpass.py", line 91, in unix_getpass
    passwd = fallback_getpass(prompt, stream)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib64/python3.12/getpass.py", line 126, in fallback_getpass
    return _raw_input(prompt, stream)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib64/python3.12/getpass.py", line 148, in _raw_input
    raise EOFError
EOFError
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
  File "/usr/bin/stratis", line 35, in <module>
    main()
  File "/usr/bin/stratis", line 32, in main
    return run()(sys.argv[1:])
           ^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.12/site-packages/stratis_cli/_main.py", line 64, in the_func
    handle_error(err)
  File "/usr/lib/python3.12/site-packages/stratis_cli/_error_reporting.py", line 368, in handle_error
    raise err
  File "/usr/lib/python3.12/site-packages/stratis_cli/_main.py", line 59, in the_func
    raise StratisCliActionError(command_line_args, result) from err
stratis_cli._errors.StratisCliActionError: Action selected by command-line arguments ['key', 'set', '--capture-key', 'pool1'] which were parsed to Namespace(propagate=False, unhyphenated_uuids=False, func=<function add_subcommand.<locals>.wrap_func.<locals>.wrapped_func at>
key.service: Main process exited, code=exited, status=1/FAILURE


Reproducible: Always



Expected Results:  
With --capture-key it should not even *try* to invoke the console. Read the password from stdin, and fail if it is empty, no fallbacks.

[1] https://bodhi.fedoraproject.org/updates/FEDORA-2023-c464ef1f0a

Comment 1 Marius Vollmer 2023-10-30 11:57:35 UTC
--keyfile-path /dev/stdin might work.

Comment 2 mulhern 2023-10-30 14:12:42 UTC
I believe that the only difference between the two versions is that for --capture-key option, the user must enter the password twice in 3.6.0.

Please let us know if that is the source of the problem. For your tests, I think switching to, e.g., --keyfile-path option, as suggested, would be the best approach.

Comment 3 Martin Pitt 2023-10-31 07:12:56 UTC
Indeed sending the password twice works:

  systemd-run --unit key --wait sh -exc 'echo -e "s3kr1t\ns3kr1t" | stratis key set --capture-key pool1; echo done'

So maybe the documentation of --capture-key is misleading then: Either it is not really meant for this noninteractive case, and then it should be documented differently, or it is broken and should be fixed to not touch the tty at all, and just read the password from stdin.

Indeed --keyfile-path works better:

   echo s3kr1t | stratis key set --keyfile-path /dev/stdin pool2

That doesn't need any jumping through hoops to hide the tty. I'll change our tests to use that. However, I keep this open to clarify/fix the --capture-key option.

Thank you!

Comment 4 Martin Pitt 2023-10-31 09:06:14 UTC
> Indeed --keyfile-path works better:

Ah no, it doesn't. While the *command* works, it seems to associate the given file path to the key, instead of reading the file and storing the contents as the key. So with /dev/stdin that is broken, see e.g. https://cockpit-logs.us-east-1.linodeobjects.com/pull-19549-20231031-071549-fd5d9d9a-fedora-38-storage/TestStorageStratisNBDE-testBasic-fedora-38-127.0.0.2-2301-FAIL.png .

So indeed there is a raison d'être for both --keyfile-path and --capture-key. I'll go back to the latter and just send the passphrase twice.

Comment 5 Martin Pitt 2023-10-31 15:48:09 UTC
Correction: This does work, it's just picky about *not* having a newline at the end. Adding `-n` fixes it:

  echo -n s3kr1t | stratis key set --keyfile-path /dev/stdin pool2


Note You need to log in before you can comment on or make changes to this bug.