Skip to content

fix: Choose proper PEM vs DER format of private key instead of always using PEM format - #508

Merged
edgarrmondragon merged 6 commits into
MeltanoLabs:mainfrom
joshua-janicas:fix/issue-427
Jul 15, 2026
Merged

fix: Choose proper PEM vs DER format of private key instead of always using PEM format#508
edgarrmondragon merged 6 commits into
MeltanoLabs:mainfrom
joshua-janicas:fix/issue-427

Conversation

@joshua-janicas

@joshua-janicas joshua-janicas commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

Closes #427.

Issue

Right now when I try to run a meltano run command with my private key in a PEM format, i get the warning:

PEMFormat_Meltano

However, when i remove the first and last line (BEGIN/END) for the formatting and put it all one one line, I get an error which ends in a stacktrace as such

line='            self.connector.prepare_schema(\n'), TracebackFrame(filename='C:\\Git\\elt-pipeline\\.meltano\\loaders\\target-snowflake\\venv\\Lib\\site-packages\\singer_sdk\\sql\\connector.py', function='prepare_schema', lineno=1378, line='        if not self.schema_exists(schema_name):\n'), TracebackFrame(filename='C:\\Git\\elt-pipeline\\.meltano\\loaders\\target-snowflake\\venv\\Lib\\site-packages\\target_snowflake\\connector.py', function='schema_exists', lineno=393, line='        schema_names = self.inspector.get_schema_names()\n'), TracebackFrame(filename='C:\\Git\\elt-pipeline\\.meltano\\loaders\\target-snowflake\\venv\\Lib\\site-packages\\target_snowflake\\connector.py', function='inspector', lineno=121, line='            self._inspector = sqlalchemy.inspect(self._engine)\n'), TracebackFrame(filename='C:\\Git\\elt-pipeline\\.meltano\\loaders\\target-snowflake\\venv\\Lib\\site-packages\\singer_sdk\\sql\\connector.py', function='_engine', lineno=879, line='            self._cached_engine = self.create_engine()\n'), TracebackFrame(filename='C:\\Git\\elt-pipeline\\.meltano\\loaders\\target-snowflake\\venv\\Lib\\site-packages\\target_snowflake\\connector.py', function='create_engine', lineno=294, line='            connect_args=self.get_connect_args(),\n'), TracebackFrame(filename='C:\\Git\\elt-pipeline\\.meltano\\loaders\\target-snowflake\\venv\\Lib\\site-packages\\target_snowflake\\connector.py', function='get_connect_args', lineno=267, line='            connect_args["private_key"] = self.get_private_key()\n'), TracebackFrame(filename='C:\\Git\\elt-pipeline\\.meltano\\loaders\\target-snowflake\\venv\\Lib\\site-packages\\target_snowflake\\connector.py', function='get_private_key', lineno=202, line='        p_key = serialization.load_pem_private_key(\n')], cause=None, context=None), 'name': 'target-snowflake', 'pid': 31272, 'thread_name': 'MainThread', 'app_name': 'singer-sdk', 'stream_name': None, 'plugin_logger': 'target-snowflake', 'event': 'Unable to load PEM file. See https://cryptography.io/en/latest/faq/#why-can-t-i-import-my-pem-file for more details. MalformedFraming', 'run_id': '019effb9-34dd-7b22-9916-1e25eea2f5cf', 'level': 'error', 'timestamp': '2026-06-25T17:00:20.104056Z'}
  Arguments: ()
  2026-06-25T17:00:20.397811Z [error    ] meltano      Loader failed      
  2026-06-25T17:00:20.398595Z [error    ] meltano      Block run completed
  2026-06-25T17:00:20.398897Z [info     ] meltano      Run completed  

I then tried to encode it as a base64 var, taking my private key, processing it via

import base64

encoded = base64.b64encode(open("private_key.pem", "rb").read()).decode()
print(encoded)

then importing that output into my env var.

Funnily enough, that then worked for meltano's normal tap/target, but broke the dbt-snowflake connection

2026-06-25T17:38:05.194254Z [info     ] meltano      17:38:05  Found 288 models, 5545 data tests, 104 seeds, 2 operations, 327 sources, 939 macros
  2026-06-25T17:38:05.277444Z [info     ] meltano      17:38:05           
  2026-06-25T17:38:05.277843Z [info     ] meltano      17:38:05  Concurrency: 8 threads (target='dev')
  2026-06-25T17:38:05.278073Z [info     ] meltano      17:38:05           
  2026-06-25T17:38:05.355483Z [info     ] meltano      17:38:05           
  2026-06-25T17:38:05.355853Z [info     ] meltano      17:38:05  Finished running  in 0 hours 0 minutes and 0.08 seconds (0.08s).
  2026-06-25T17:38:05.356165Z [info     ] meltano      17:38:05  Encountered an error:
  2026-06-25T17:38:05.356340Z [info     ] meltano      Runtime Error      
  2026-06-25T17:38:05.356505Z [info     ] meltano        Database error while listing schemas in database "TECH01"
  2026-06-25T17:38:05.356785Z [info     ] meltano        Database Error   
  2026-06-25T17:38:05.356956Z [info     ] meltano          Could not deserialize key data. The data may be in an incorrect format, it may be encrypted with an unsupported algorithm, or it may be an unsupported key type (e.g. EC curves with explicit parameters). Details: ASN.1 parsing error: unexpected tag (got Tag { value: 13, constructed: true, class: Universal })
  2026-06-25T17:38:06.736057Z [info     ] meltano      error invoking dbt None        error_message=dbt invocation failed returncode=2

The only solution at this juncture was to have it as two separate env vars since the dbt-snowflake didn't like the b64 encoding. Which I thought was kind of silly.

What I changed

I then looked into the connector.py and noticed that in all 3 sections of the python function get_private_key that no matter what it was always serializing it as if it was a PEM format. So instead now it treats it as a DER format in the final else condition at first, before falling back to PEM. The other two conditions that are checked beforehand still rely on PEM.

For now the deprecation warnings are in place in case the maintainer decides to go forward with it, but at least now we can put the private key on a single line as an env var and continue to read it in either full PEM format, removing the headers and leaving it as one big block, or b64 encoding the whole PEM file (though in that case it would still break with dbt).

Solution Review

I did the changes locally on my repo (where i went directly into the connector.py)
image

Then i ran one of my tap/target commands,
meltano run tap-mssql-static target-snowflake
followed by a dbt command.
meltano invoke dbt-snowflake:run --select stg_core_db__program

Normal PEM Format

PEMFormat_Meltano PEMFormat_DBT

Removed the PEM, Left as One Line

NonPEMFormat_Meltano NonPEMFormat_DBT

Converted the PEM into a full b64

Showing again that it works for meltano, but breaks for dbt (which it is its own separate issue).
image

image

Forced the PEM b64 into a bad value

I removed 4 characters from the PEM data I converted into b64, to see if it would error out properly, and it did.
image

@joshua-janicas
joshua-janicas requested a review from a team as a code owner June 25, 2026 19:08
Signed-off-by: Edgar Ramírez Mondragón <edgarrm358@gmail.com>
Signed-off-by: Edgar Ramírez Mondragón <edgarrm358@gmail.com>
@edgarrmondragon edgarrmondragon added the bug Something isn't working label Jul 15, 2026
@edgarrmondragon edgarrmondragon self-assigned this Jul 15, 2026
@edgarrmondragon

Copy link
Copy Markdown
Member

Thanks @joshua-janicas !

@edgarrmondragon
edgarrmondragon merged commit c704907 into MeltanoLabs:main Jul 15, 2026
2 of 6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Non Base64-encoded private keys Deprecation Warning

2 participants