snowflakedb / snowflake-connector-python

Snowflake Connector for Python
https://pypi.python.org/pypi/snowflake-connector-python/
Apache License 2.0
568 stars 456 forks source link

SNOW-1502056: using connections.toml always returns a "Bad owner or permissions" on UserWarning #1978

Open patrickhowerter opened 1 week ago

patrickhowerter commented 1 week ago

Python version

Python 3.11.8

Operating system and processor architecture

Windows-10-10.0.19045-SP0

Installed packages

annotated-types==0.6.0
asn1crypto==1.5.1
atpublic==4.1.0
attrs==23.2.0
azure-core==1.30.1
azure-storage-blob==12.19.1
certifi==2024.6.2
cffi==1.16.0
charset-normalizer==3.3.2
cloudpickle==2.2.1
colorama==0.4.6
coverage==7.5.0
cryptography==42.0.8
dill==0.3.8
filelock==3.15.3
fsspec==2024.6.0
idna==3.7
importlib_metadata==7.1.0
importlib_resources==6.4.0
iniconfig==2.0.0
isodate==0.6.1
jaraco.classes==3.4.0
keyring==24.3.1
modin==0.28.1
more-itertools==10.2.0
numpy==1.26.4
packaging==24.1
pandas==2.2.1
platformdirs==4.2.2
pluggy==1.5.0
psutil==5.9.8
pyarrow==16.1.0
pycparser==2.22
pydantic==2.7.0
python-dateutil==2.9.0.post0
pytz==2024.1
pywin32-ctypes==0.2.2
PyYAML==6.0.1
requests==2.32.3
six==1.16.0
snowflake==0.8.1
snowflake-connector-python==3.11.0
snowflake-snowpark-python==1.18.0
snowflake._legacy==0.7.0
snowflake.core==0.8.1
sortedcontainers==2.4.0
tomlkit==0.12.5
typing_extensions==4.12.2
tzdata==2024.1
urllib3==2.2.2
zipp==3.18.1

What did you do?

I created a connection.toml file. Then changed the permissions so that the user is the only user with read-write permissions.  I still return this warning message:

import snowflake.snowpark as sp
session = sp.Session.builder.config('connection_name', 'mdo').create()

D:\mdo.snow\python_files\mdosdkenv\Lib\site-packages\snowflake\connector\config_manager.py:351: UserWarning: Bad owner or permissions on C:\Users\patri\AppData\Local\snowflake\connections.toml
  warn(f"Bad owner or permissions on {str(filep)}{chmod_message}")

What did you expect to see?

I expect to see no warning messages.

Can you set logging to DEBUG and collect the logs?

yes, here is the output:

D:\mdo.snow\python_files\mdosdkenv\Lib\site-packages\snowflake\connector\config_manager.py:351: UserWarning: Bad owner or permissions on C:\Users\patri\AppData\Local\snowflake\connections.toml
  warn(f"Bad owner or permissions on {str(filep)}{chmod_message}")
2024-06-24 09:38:28,865 - MainThread config_manager.py:352 - read_config() - DEBUG - reading configuration file from C:\Users\patri\AppData\Local\snowflake\connections.toml
2024-06-24 09:38:28,867 - MainThread connection.py:408 - __init__() - INFO - Snowflake Connector for Python Version: 3.11.0, Python Version: 3.11.8, Platform: Windows-10-10.0.19045-SP0
2024-06-24 09:38:28,868 - MainThread connection.py:714 - connect() - DEBUG - connect
2024-06-24 09:38:28,868 - MainThread connection.py:1099 - __config() - DEBUG - __config
2024-06-24 09:38:28,868 - MainThread connection.py:1258 - __config() - INFO - This connection is in OCSP Fail Open Mode. TLS Certificates would be checked for validity and revocation status. Any other Certificate Revocation related exceptions or OCSP Responder failures would be disregarded in favor of connectivity.
2024-06-24 09:38:28,868 - MainThread converter.py:159 - __init__() - DEBUG - use_numpy: False
2024-06-24 09:38:28,868 - MainThread converter_issue23517.py:27 - __init__() - DEBUG - initialized
2024-06-24 09:38:28,868 - MainThread connection.py:926 - __open_connection() - DEBUG - REST API object was created: kub99688.us-east-1.snowflakecomputing.com:443
2024-06-24 09:38:28,869 - MainThread _auth.py:174 - authenticate() - DEBUG - authenticate
2024-06-24 09:38:28,870 - MainThread _auth.py:208 - authenticate() - DEBUG - assertion content: ver:1-hint:5473948711610254-hiding
2024-06-24 09:38:28,870 - MainThread _auth.py:211 - authenticate() - DEBUG - account=hiding, user=hiding, database=MDOSDKDEVPH, schema=PUBLIC, warehouse=ETLWAREHOUSE, role=None, request_id=ab2121c1-4703-4e4b-8d83-a33547a41a19
2024-06-24 09:38:28,870 - MainThread _auth.py:244 - authenticate() - DEBUG - body['data']: {'CLIENT_APP_ID': 'PythonSnowpark', 'CLIENT_APP_VERSION': '1.18.0', 'SVN_REVISION': None, 'ACCOUNT_NAME': 'hiding', 'LOGIN_NAME': 'hiding', 'CLIENT_ENVIRONMENT': {'APPLICATION': 'PythonSnowpark', 'OS': 'Windows', 'OS_VERSION': 'Windows-10-10.0.19045-SP0', 'PYTHON_VERSION': '3.11.8', 'PYTHON_RUNTIME': 'CPython', 'PYTHON_COMPILER': 'MSC v.1937 64 bit (AMD64)', 'OCSP_MODE': 'FAIL_OPEN', 'TRACING': 10, 'LOGIN_TIMEOUT': None, 'NETWORK_TIMEOUT': None, 'SOCKET_TIMEOUT': None}, 'AUTHENTICATOR': 'ID_TOKEN', 'TOKEN': 'ver:1-hint:hiding', 'SESSION_PARAMETERS': {'CLIENT_PREFETCH_THREADS': 4, 'CLIENT_STORE_TEMPORARY_CREDENTIAL': True}}
2024-06-24 09:38:28,870 - MainThread retry.py:351 - from_int() - DEBUG - Converted retries value: 1 -> Retry(total=1, connect=None, read=None, redirect=None, status=None)
2024-06-24 09:38:28,871 - MainThread retry.py:351 - from_int() - DEBUG - Converted retries value: 1 -> Retry(total=1, connect=None, read=None, redirect=None, status=None)
2024-06-24 09:38:28,871 - MainThread network.py:1224 - _use_requests_session() - DEBUG - Session status for SessionPool 'hiding.us-east-1.snowflakecomputing.com', SessionPool 1/1 active sessions
2024-06-24 09:38:28,871 - MainThread network.py:875 - _request_exec_wrapper() - DEBUG - remaining request timeout: N/A ms, retry cnt: 1
2024-06-24 09:38:28,871 - MainThread network.py:857 - add_request_guid() - DEBUG - Request guid: a03df552-0d8e-4e65-a944-357737b6eb98
2024-06-24 09:38:28,871 - MainThread network.py:1065 - _request_exec() - DEBUG - socket timeout: 60
2024-06-24 09:38:28,873 - MainThread connectionpool.py:1019 - _new_conn() - DEBUG - Starting new HTTPS connection (1): hiding.us-east-1.snowflakecomputing.com:443
2024-06-24 09:38:29,403 - MainThread ssl_wrap_socket.py:79 - ssl_wrap_socket_with_ocsp() - DEBUG - OCSP Mode: FAIL_OPEN, OCSP response cache file name: None
2024-06-24 09:38:29,403 - MainThread ocsp_snowflake.py:534 - reset_ocsp_response_cache_uri() - DEBUG - ocsp_response_cache_uri: file://C:/Users/patri/AppData/Local/Snowflake/Caches/ocsp_response_cache.json
2024-06-24 09:38:29,403 - MainThread ocsp_snowflake.py:537 - reset_ocsp_response_cache_uri() - DEBUG - OCSP_VALIDATION_CACHE size: 298
2024-06-24 09:38:29,403 - MainThread ocsp_snowflake.py:333 - reset_ocsp_dynamic_cache_server_url() - DEBUG - OCSP response cache server is enabled: http://ocsp.snowflakecomputing.com/ocsp_response_cache.json
2024-06-24 09:38:29,403 - MainThread ocsp_snowflake.py:346 - reset_ocsp_dynamic_cache_server_url() - DEBUG - OCSP dynamic cache server RETRY URL: None
2024-06-24 09:38:29,403 - MainThread ocsp_snowflake.py:970 - validate() - DEBUG - validating certificate: hiding.us-east-1.snowflakecomputing.com
2024-06-24 09:38:29,403 - MainThread ocsp_asn1crypto.py:385 - extract_certificate_chain() - DEBUG - # of certificates: 4
2024-06-24 09:38:29,404 - MainThread ocsp_asn1crypto.py:390 - extract_certificate_chain() - DEBUG - subject: OrderedDict([('common_name', '*.va3.us-east-1.snowflakecomputing.com')]), issuer: OrderedDict([('country_name', 'US'), ('organization_name', 'Amazon'), ('common_name', 'Amazon RSA 2048 M02')])
2024-06-24 09:38:29,404 - MainThread ocsp_asn1crypto.py:390 - extract_certificate_chain() - DEBUG - subject: OrderedDict([('country_name', 'US'), ('organization_name', 'Amazon'), ('common_name', 'Amazon RSA 2048 M02')]), issuer: OrderedDict([('country_name', 'US'), ('organization_name', 'Amazon'), ('common_name', 'Amazon Root CA 1')])
2024-06-24 09:38:29,405 - MainThread ocsp_asn1crypto.py:390 - extract_certificate_chain() - DEBUG - subject: OrderedDict([('country_name', 'US'), ('organization_name', 'Amazon'), ('common_name', 'Amazon Root CA 1')]), issuer: OrderedDict([('country_name', 'US'), ('state_or_province_name', 'Arizona'), ('locality_name', 'Scottsdale'), ('organization_name', 'Starfield Technologies, Inc.'), ('common_name', 'Starfield Services Root Certificate Authority - G2')])
2024-06-24 09:38:29,405 - MainThread ocsp_asn1crypto.py:390 - extract_certificate_chain() - DEBUG - subject: OrderedDict([('country_name', 'US'), ('state_or_province_name', 'Arizona'), ('locality_name', 'Scottsdale'), ('organization_name', 'Starfield Technologies, Inc.'), ('common_name', 'Starfield Services Root Certificate Authority - G2')]), issuer: OrderedDict([('country_name', 'US'), ('organization_name', 'Starfield Technologies, Inc.'), ('organizational_unit_name', 'Starfield Class 2 Certification Authority')])
2024-06-24 09:38:29,407 - MainThread ocsp_asn1crypto.py:413 - create_pair_issuer_subject() - DEBUG - not found issuer_der: OrderedDict([('country_name', 'US'), ('organization_name', 'Starfield Technologies, Inc.'), ('organizational_unit_name', 'Starfield Class 2 Certification Authority')])
2024-06-24 09:38:29,407 - MainThread ocsp_snowflake.py:734 - find_cache() - DEBUG - hit cache for subject: OrderedDict([('common_name', '*.va3.us-east-1.snowflakecomputing.com')])
2024-06-24 09:38:29,409 - MainThread ocsp_asn1crypto.py:205 - is_valid_time() - DEBUG - Verifying the attached certificate is signed by the issuer. Valid Not After: 2025-12-10 00:00:00+00:00
2024-06-24 09:38:29,409 - MainThread ocsp_snowflake.py:734 - find_cache() - DEBUG - hit cache for subject: OrderedDict([('country_name', 'US'), ('organization_name', 'Amazon'), ('common_name', 'Amazon RSA 2048 M02')])
2024-06-24 09:38:29,410 - MainThread ocsp_asn1crypto.py:205 - is_valid_time() - DEBUG - Verifying the attached certificate is signed by the issuer. Valid Not After: 2025-12-10 00:00:00+00:00
2024-06-24 09:38:29,411 - MainThread ocsp_snowflake.py:734 - find_cache() - DEBUG - hit cache for subject: OrderedDict([('country_name', 'US'), ('organization_name', 'Amazon'), ('common_name', 'Amazon Root CA 1')])
2024-06-24 09:38:29,412 - MainThread ocsp_asn1crypto.py:205 - is_valid_time() - DEBUG - Verifying the attached certificate is signed by the issuer. Valid Not After: 2025-05-07 12:00:00+00:00
2024-06-24 09:38:29,412 - MainThread ocsp_snowflake.py:734 - find_cache() - DEBUG - hit cache for subject: OrderedDict([('country_name', 'US'), ('state_or_province_name', 'Arizona'), ('locality_name', 'Scottsdale'), ('organization_name', 'Starfield Technologies, Inc.'), ('common_name', 'Starfield Services Root Certificate Authority - G2')])
2024-06-24 09:38:29,414 - MainThread ocsp_snowflake.py:1027 - _validate() - DEBUG - ok
2024-06-24 09:38:29,547 - MainThread connectionpool.py:474 - _make_request() - DEBUG - https://hiding.us-east-1.snowflakecomputing.com:443 "POST /session/v1/login-request?request_id=ab2121c1-4703-4e4b-8d83-a33547a41a19&databaseName=hiding&schemaName=PUBLIC&warehouse=ETLWAREHOUSE&request_guid=hiding HTTP/1.1" 200 1707
2024-06-24 09:38:29,547 - MainThread network.py:1092 - _request_exec() - DEBUG - SUCCESS
2024-06-24 09:38:29,547 - MainThread network.py:1229 - _use_requests_session() - DEBUG - Session status for SessionPool 'hiding.us-east-1.snowflakecomputing.com', SessionPool 0/1 active sessions
2024-06-24 09:38:29,547 - MainThread network.py:745 - _post_request() - DEBUG - ret[code] = None, after post request
2024-06-24 09:38:29,547 - MainThread _auth.py:371 - authenticate() - DEBUG - completed authentication
2024-06-24 09:38:29,547 - MainThread _auth.py:418 - authenticate() - DEBUG - token = ******
2024-06-24 09:38:29,547 - MainThread _auth.py:426 - authenticate() - DEBUG - master_token = ******
2024-06-24 09:38:29,547 - MainThread _auth.py:434 - authenticate() - DEBUG - id_token = NULL
2024-06-24 09:38:29,547 - MainThread _auth.py:442 - authenticate() - DEBUG - mfa_token = NULL
2024-06-24 09:38:29,547 - MainThread connection.py:845 - cursor() - DEBUG - cursor
sfc-gh-dszmolka commented 1 week ago

hello - thanks for raising this. so we do check for file permissions mode and if the file is readable by Group or Others https://github.com/snowflakedb/snowflake-connector-python/blob/v3.11.0/src/snowflake/connector/config_manager.py#L336

but i'm not sure if the issue comes from the PythonConnector or not. reproduced the issue by creating the connections.toml, setting it as read-only mode :

PS C:\temp\python1978> Get-Acl .\connections.toml |Format-List

Path   : Microsoft.PowerShell.Core\FileSystem::C:\temp\python1978\connections.toml
Owner  : MYHOST\dszmolka
Group  : MYHOST\None
Access : MYHOST\dszmolka Allow  Read, Synchronize
Audit  :
Sddl   : O:S-1-5-21-1293141090-215775074-2841561624-1000G:S-1-5-21-1293141090-215775074-2841561624513D:PAI(A;;FR;;;S-1-5-21-1293141090-215775074-2841561624-1000)

(from what i understand about Windows; this looks like as of only me the owner have Read and Synchronize (whatever that is) permissions, nobody has anything else)

but when adding some debug logging to config_manager.py to actually write out the file permissions it is seeing on the file:

> python .\test.py
C:\temp\python1978

filep.stat().st_mode: 33206, oct: 0o100666. stat.S_IRGRP: 32, oct: 0o40. stat.S_IROTH: 4, oct: 0o4
C:\temp\python1978\venv\lib\site-packages\snowflake\connector\config_manager.py:352: UserWarning: Bad owner or permissions on C:\temp\python1978\connections.toml
  warn(f"Bad owner or permissions on {str(filep)}{chmod_message}")

which if i got correctly, the least-significant 4 bits (0666) show the file permissions similarly to Unix; which is quite disturbing because 0666 means the file is readable+writable for Owner, Group, and Others too. So the error message (complaining about bad permissions) makes sense at the first glance; as it should be only readable to Owner, if this is really the permission bits.

I'll keep digging how it would be the best to actually enforce the necessary 0600 permission on Windows (it's a simple instant chmod on Unix)

sfc-gh-dszmolka commented 1 week ago

well determining the file permissions on Windows with the same method as on Unix, don't seem to work. We'll need to see how we want to address this.

patrickhowerter commented 1 week ago

A couple of options I see here as this appears to be difficult to implement across operating systems.

  1. Allow an argument to Session.builder.config method, such as "check_config_file_permissions", with the default being False. In our use case, we do not have any private information (authenticator = "externalbrowser") in our config file anyways..
  2. Give the end users a function that writes the configuration file (this would automatically write the file with the appropriate permissions. This avoids end users going down the rabbit hole of trying to figure out how to permission the file correctly..