snowflakedb/snowflake-cli

SNOW-1752110: generate-jwt should not fail on empty passphrase

sfc-gh-vtimofeenko opened this issue · 6 comments

SnowCLI version

Snowflake CLI version: 3.2.0.dev0

Python version

Python 3.11.6

Platform

macOS-14.7-arm64-arm-64bit

What happened

For a key without passphrase, the passphrase is passed as empty string instead of None.

Potential fixes

Use dynamic defaults for prompts

Move passphrase to arguments for generate_jwt function and use this click approach allows automating the "don't show prompt if envvar is set".

Use value_proc in typer's prompt

Basically this will use a tiny value post-processor which turns empty string into None. Seems to work (all tests pass, jwt is generated); I'd be happy to submit a PR.

diff --git a/src/snowflake/cli/_plugins/connection/commands.py b/src/snowflake/cli/_plugins/connection/commands.py
index 543f70f2..0b34eec2 100644
--- a/src/snowflake/cli/_plugins/connection/commands.py
+++ b/src/snowflake/cli/_plugins/connection/commands.py
@@ -13,6 +13,7 @@
 # limitations under the License.
 
 from __future__ import annotations
+from typing import Optional
 
 import logging
 import os.path
@@ -386,12 +387,18 @@ def generate_jwt(
 ) -> CommandResult:
     """Generate a JWT token, which will be printed out and displayed.."""
     passphrase = os.getenv("PRIVATE_KEY_PASSPHRASE", None)
+
+    def maybe_str(x: str) -> Optional[str]:
+        """Parser turns empty string into None."""
+        return None if x == "" else str(x)
+
     if not passphrase:
         passphrase = typer.prompt(
             "Enter private key file password (Press enter if none)",
             hide_input=True,
             type=str,
             default="",
+            value_proc=maybe_str,
         )
     try:
         token = connector.auth.get_token_from_private_key(

Console output

❯ snow connection generate-jwt --private-key <path-to-p8>                         
Enter private key file password (Press enter if none) []: # Press enter here

An unexpected exception occurred. Use --debug option to see the traceback. Exception message:

Password was given but private key is not encrypted.

How to reproduce

  1. Set up a Snowflake user with key-pair auth, don't set the passphrase
  2. Try to generate jwt for the user

Interesting, I can't replicate the behavior. After this change I'm getting TypeError: Password was not given but private key is encrypted on my key. While it works fine without your change. FYI the implementation is based on snowsql --generate-jwt

Looks like the latest version produces the same error without my patch:

❯ pipx install git+https://github.com/snowflakedb/snowflake-cli.git           
⚠️  Note: snow was already on your PATH at /Users/vtimofeenko/.nix-profile/bin/snow
  installed package snowflake-cli 3.2.0.dev0, installed using Python 3.11.9
  These apps are now globally available
    - snow
done! ✨ 🌟 ✨
❯ ~/.local/bin/snow --version
Snowflake CLI version: 3.2.0.dev0
❯ ~/.local/bin/snow --config-file=./test.toml connection generate-jwt
...config_manager.py:351: UserWarning: Bad owner or permissions on test.toml.
 * To change owner, run `chown $USER "test.toml"`.
 * To restrict permissions, run `chmod 0600 "test.toml"`.

  warn(f"Bad owner or permissions on {str(filep)}{chmod_message}")
Enter private key file password (Press enter if none) []: 

An unexpected exception occurred. Use --debug option to see the traceback. Exception message:

Password was given but private key is not encrypted.
❯ cat test.toml                                                
[connections.default]
account = "<REDACTED>"
user = "snowcli_key_test"
private_key_path = "<REDACTED>"

If using RC4:

❯ pipx install git+https://github.com/snowflakedb/snowflake-cli.git@v3.1.0-rc4
⚠️  Note: snow was already on your PATH at /Users/vtimofeenko/.nix-profile/bin/snow
  installed package snowflake-cli 3.1.0rc4, installed using Python 3.11.9
  These apps are now globally available
    - snow
done! ✨ 🌟 ✨
❯ ~/.local/bin/snow --version                                        
Snowflake CLI version: 3.1.0rc4
❯ ~/.local/bin/snow --config-file=./test.toml connection generate-jwt
# <same complaint about permissions>
Enter private key file password (Press enter if none) []: 

An unexpected exception occurred. Use --debug option to see the traceback. Exception message:

Password was given but private key is not encrypted.

Just tried this out in a (reasonably) clean environment, same thing.

The chevron ("❯") denotes the command I am executing in zsh. The line above it is the CWD.

~/.ssh 
❯ openssl genrsa 2048 | openssl pkcs8 -topk8 -inform PEM -out 1747.p8 -nocrypt
Generating RSA private key, 2048 bit long modulus
.......................+++++
.............+++++
e is 65537 (0x10001)

~/.ssh 
❯ openssl rsa -in ./1747.p8 -pubout -out ./1747.pub
writing RSA key
❯ snow sql --query "CREATE USER REPR_1747 RSA_PUBLIC_KEY='$(cat ./1747.pub | grep -v '-' | tr -d '\n')'" # I usually use TYPE=SYSTEM, but let's not use it
CREATE USER REPR_1747 RSA_PUBLIC_KEY='MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAx+MUeeI2PIXX1H+CAKBtvfy3y0y0zNhLGcqEkAheMDFEjFf5N2Tvtzufji/x2uPDawbRibsTi/WM1P8yEG+yxyibqXG4FYnGCOXtowR1G4n4VDEgjA6tz+xT/rqeQzOEt2X4562c1YMF7WbgAg/husDpS5t079/pBPp/EDS0uiOoJbjEzxK3yiHH6biZsFd0
KJJwE8etZKdzdjdvIIgwibA239mlpwi8lwBgheuepsPelWiSzjbFaiSBlpKzOhKI4fV4qCPGba5btzwQzONia/8siZ9Huaip1CTUuCSpAB0bcqKSP3HSpeb5ZBeRhMM0LDQa6fqpwhTs8eZm/QyydQIDAQAB'
+--------------------------------------+
| status                               |
|--------------------------------------|
| User REPR_1747 successfully created. |
+--------------------------------------+
~/.ssh 
❯ cd $(mktemp -d)

# Just to make sure the env is pure
20zv3yk14bsdmy2f3qvvssgh0000gn/T/tmp.sZ7vPVD5xx 
❯ cat <<EOF>config.toml
∙ [connections.default]
∙ EOF

# Install snowflake-cli using pipx and make sure pipx version is being used for reproducibility purposes
20zv3yk14bsdmy2f3qvvssgh0000gn/T/tmp.sZ7vPVD5xx 
❯ pipx install snowflake-cli                                              
⚠️  Note: snow was already on your PATH at /Users/vtimofeenko/.nix-profile/bin/snow
  installed package snowflake-cli 3.1.0, installed using Python 3.11.9
  These apps are now globally available
    - snow
done! ✨ 🌟 ✨
20zv3yk14bsdmy2f3qvvssgh0000gn/T/tmp.sZ7vPVD5xx took 9s 
❯ which snow                                                 
/Users/vtimofeenko/.nix-profile/bin/snow

20zv3yk14bsdmy2f3qvvssgh0000gn/T/tmp.sZ7vPVD5xx 
❯ PATH=~/.local/bin:$PATH

20zv3yk14bsdmy2f3qvvssgh0000gn/T/tmp.sZ7vPVD5xx 
❯ which snow
/Users/vtimofeenko/.local/bin/snow

20zv3yk14bsdmy2f3qvvssgh0000gn/T/tmp.sZ7vPVD5xx 
❯ snow --version                                                                                                                                          
Snowflake CLI version: 3.1.0

20zv3yk14bsdmy2f3qvvssgh0000gn/T/tmp.sZ7vPVD5xx 
❯ chmod 600 ./config.toml 
20zv3yk14bsdmy2f3qvvssgh0000gn/T/tmp.sZ7vPVD5xx 
❯ snow --config-file ./config.toml connection list
+-------------------------------------------+
| connection_name | parameters | is_default |
|-----------------+------------+------------|
| default         | {}         | True       |
+-------------------------------------------+

# Checking that env is empty:
20zv3yk14bsdmy2f3qvvssgh0000gn/T/tmp.sZ7vPVD5xx took 28s 
❯ env | rg SNOWFLAKE
# It is empty, no SNOWFLAKE vars

# OK, we have snowcli freshly installed, there is nothing in the config file
# Let's try generate-jwt with just command-line supplied args
❯ snow --config-file ./config.toml connection generate-jwt --private-key-path ~/.ssh/1747.p8 --account "<REDACTED>" --user "REPR_1747"
Enter private key file password (Press enter if none) []:   # << just hit "Enter"

An unexpected exception occurred. Use --debug option to see the traceback. Exception message:

Password was given but private key is not encrypted.

# Just in case my environment is messing with things, let's try bash:
❯ bash
# ... 
bash-3.2$ snow --config-file ./config.toml connection generate-jwt --private-key-path ~/.ssh/1747.p8 --account "<REDACTED>" --user "REPR_1747"
Enter private key file password (Press enter if none) []: 

An unexpected exception occurred. Use --debug option to see the traceback. Exception message:

Password was given but private key is not encrypted.

# Let's try the live version back in zsh
20zv3yk14bsdmy2f3qvvssgh0000gn/T/tmp.sZ7vPVD5xx 
❯ pipx install git+https://github.com/snowflakedb/snowflake-cli.git                                                                                     
⚠️  Note: snow was already on your PATH at /Users/vtimofeenko/.nix-profile/bin/snow
  installed package snowflake-cli 3.2.0.dev0, installed using Python 3.11.9
  These apps are now globally available
    - snow
done! ✨ 🌟 ✨

20zv3yk14bsdmy2f3qvvssgh0000gn/T/tmp.sZ7vPVD5xx took 16s 
❯ which snow
/Users/vtimofeenko/.local/bin/snow

20zv3yk14bsdmy2f3qvvssgh0000gn/T/tmp.sZ7vPVD5xx 
❯ snow --version                                                                                                                                        
Snowflake CLI version: 3.2.0.dev0

20zv3yk14bsdmy2f3qvvssgh0000gn/T/tmp.sZ7vPVD5xx took 3s 
❯ snow --config-file ./config.toml connection generate-jwt --private-key-path ~/.ssh/1747.p8 --account "<REDACTED>" --user "REPR_1747"
Enter private key file password (Press enter if none) []: 

An unexpected exception occurred. Use --debug option to see the traceback. Exception message:

Password was given but private key is not encrypted.

# Same thing :(

Thanks! The problem is caused by -nocrypt. It seems that using -nocrypt causes to the key to have really no passphrase as opposed to clicking enter on passphrase promt.

@sfc-gh-vtimofeenko would you mind testing out #1841 locally? I was able to replicate both cases (no passphrase and empty passphrase) and the code should work now for both cases.

Thank you very much!

Works fine:

❯ pipx install git+https://github.com/snowflakedb/snowflake-cli.git
❯ PATH=~/.local/bin:$PATH
❯ snow --config-file ./config.toml connection generate-jwt --private-key-path ~/.ssh/1747.p8 --account "<REDACTED>" --user "REPR_1747"
<proper JWT>