snowflake-connector-python icon indicating copy to clipboard operation
snowflake-connector-python copied to clipboard

SNOW-676645: Add connector support for keypair authentication using hardware security modules or cloud key managers

Open arsatiki opened this issue 3 years ago • 0 comments

What is the current behavior?

Current Snowflake keypair authentication requires that the user has the private key in their possession as a file. However, Hardware Security Modules (e.g. Smartcards, Yubikeys, …) and Cloud Key Managers do not expose the private key to the user. Instead, they provide cryptographic operations as API functionality. I can, for example, ask the HSM to sign a certain string using the private key.

What is the desired behavior?

I would like to be able to use any cryptographic device or service capable of RSA-signatures for keypair authentication. More specifically, I want an interface against which I can implement my custom signing flow.

How would this improve snowflake-connector-python?

It would provide more options for secure logins. In our case, we would use Azure Key Vault to store keys and allow services with Managed Identity to read those keys. This lets our service accounts log in without exposing passwords or private keys at any phase.

References, Other Background

As a proof of concept, I have implemented a version of keypair authentication that abstracts signing and public key operations into a separate class. An instance of this class can be provided to the Snowflake connector to sign the token.

Here's an example. I will provide a full pull request soon.

class AzureKeyVaultManager(KeyManager):
    def __init__(self, kvclient, keyname):
        self._kvclient = kvclient
        self._keyname = keyname

    def public_key(self):
        pubkey = self._kvclient.get_key(self._keyname)
        e = int.from_bytes(pubkey.key.e, 'big')
        n = int.from_bytes(pubkey.key.n, 'big')
        rsakey = RSAPublicNumbers(e, n)
        return rsakey.public_key()

    def sign(self, message):
        cc = self._kvclient.get_cryptography_client(self._keyname)
        hash = sha256(message).digest()
        result = cc.sign(SignatureAlgorithm.rs256, hash)
        return result.signature


    ctx = snowflake.connector.connect(
        user='[email protected]',
        key_manager=AzureKeyVaultManager(kc, 'testkey'),
        authenticator='KMS',
        account='foo.west-europe.azure',
        warehouse='compute_wh',
        database='misc',
    )

Please note that this approach would not add new dependencies into the connector. The user of the connector library is responsible for providing the class, customized to their particular usecase.

arsatiki avatar Oct 12 '22 11:10 arsatiki