I have been attempting to output a signed public key generated from ssh-keygen (CLI) using python and the 'subprocess' library. I've also tried the 'os' library with the same results. I'm really looking to understand why it isn't doing what I want: displaying the output of 'ssh-keygen -Lf {keyfile}' to the screen like I expect it to.
When I use this command on the same keyfile using the CLI (darwin / macOS Ventura 13.1) I get the expected results. When I use the command in Python3.10, I get a return-code of '1' and no significant error to help me understand the problem. Please see below:
Expected results:
shell-prompt$> ssh-keygen -Lf ~/.ssh/signed_key.pub
/Users/USER/.ssh/signed_kali-os.pub:
Type: [email protected] user certificate
Public key: RSA-CERT SHA256:REDACTED
Signing CA: RSA SHA256:REDACTED (using rsa-sha2-256)
Key ID: "[email protected]"
Serial: REDACTED
Valid: from 2023-01-24T20:56:07 to 2023-01-24T21:01:37
Principals:
principal-user
Critical Options: (none)
Extensions:
permit-pty
Output from basic Python3.10 script is either '1' when printing return code, or empty (None) when printing stdout and/or stderr. I have tried all variations of printing that I can think of.
These are the two code solutions that I have attempted with no luck. When I replace the command (ssh-keygen) with something rudimentary like an 'ls -l' or 'cat', I get output as expected. I am confident that the variable signedPath works as expected because it works in other parts of the code not shown, and when I replace the variable with a hardcoded path it still fails.
1)
if sys.platform == "linux" or sys.platform == "linux2" or sys.platform == "darwin":
keyOut = subprocess.run(['ssh-keygen','-Lf',signedPath],capture_output=True)
print(keyOut.stdout.decode())
2)
os.system('ssh-keygen -Lf {key}'.format(key=signedPath))
What am I looking for? Ultimately, I would like to use this code to output the signed-public-key to the screen, because I like the output format that I get with ssh-keygen and I have had problems with other SSH key libraries in Python. If there is a better solution, I'd love to get some help with that, but I really am set on trying to understand why this specific code isn't working, so I'd like an answer on that more than a separate solution. Any help here is greatly appreciated.
--- SOLVED ---
Well...this is embarrassing...but I know it happens to all of us. I added a sleep for 5 seconds, and it works as expected now. Frustrating, as after my code exited the file is completed writing so I never knew it was 0-bytes during runtime, until I put more print statements in. The code I'm writing communicates with a vault server to sign my public key, and I wasn't giving it time to complete, so python was trying to read a 0-byte file...I figured this out while making another minimal code sample.
--- SOLUTION ---
If you encounter a similar problem, it may be that the file you're trying to read is 0-bytes during run-time.
CodePudding user response:
This isn't quite an answer -- it's not clear from your question why you're not seeing the expected output -- but I wanted to demonstrate what a minimal, complete, verifiable example ("MCVE") might look like, as well as provide a working example.
Consider this code, which (a) generates a key to use as a signing certificate, (b) generates a key to be signed, and then (c) signs the key from (b) with the key from (a):
#!/usr/bin/python
import os
import subprocess
expected_files = [
"ca_key",
"ca_key.pub",
"user_key",
"user_key.pub",
"user_key-cert.pub",
]
for name in expected_files:
try:
os.remove(name)
except FileNotFoundError:
pass
# I'm using `check_output` -- and ignoring the return value -- in order to
# supress the output from these commands. This is effectively a shortcut
# for `subprocess.run` with `capture_output=True` and `check=True`.
print("generating keys")
subprocess.check_output(["ssh-keygen", "-t", "rsa", "-f", "ca_key", "-N", ""])
subprocess.check_output(["ssh-keygen", "-t", "rsa", "-f", "user_key", "-N", ""])
subprocess.check_output(
["ssh-keygen", "-s", "ca_key", "-I", "user_key", "user_key.pub"]
)
# Verify that we created everything we expected to create.
for name in expected_files:
assert os.path.isfile(name)
# Here's the code from your question.
signedPath = "user_key-cert.pub"
keyOut = subprocess.run(["ssh-keygen", "-Lf", signedPath], capture_output=True)
print("keyOut:", keyOut.stdout.decode())
The above code is a self-contained example: it creates all the necessary files to run the code you're asking about in your question; someone can imply copy the example, paste it into a file, and run it.
On my system (sys.platform
== linux
), running this example produces:
generating keys
Signed user key user_key-cert.pub: id "user_key" serial 0 valid forever
keyOut: user_key-cert.pub:
Type: [email protected] user certificate
Public key: RSA-CERT SHA256:Bca gRvhx3twR0vFhIU3gr TKIfFq2I1 lraJ6Z2QAI
Signing CA: RSA SHA256:muz 6U5h9ZGyslm50m1F0VNG8VrseuQRzYwdUm2iGoo (using rsa-sha2-512)
Key ID: "user_key"
Serial: 0
Valid: forever
Principals: (none)
Critical Options: (none)
Extensions:
permit-X11-forwarding
permit-agent-forwarding
permit-port-forwarding
permit-pty
permit-user-rc
If you run the code on a Linux system and you see different behavior, I'm sure that with some additional details about your environment we can figure out what's going on.