Zero Copy Prerequisites: Snowflake Connection

📘

Beta: Zero Copy is currently in Open Beta and is being rolled out incrementally to all customers.

Optimove Zero Copy reads data directly from your Snowflake account using a dedicated, read-only service user that authenticates with an RSA key pair. This guide covers the prerequisites your team completes in Snowflake before the connection can be established: generating the key pair, creating a least-privilege role and service user, configuring authentication and network policies, and verifying the setup.

This is a one-time setup performed by a Snowflake administrator on your account. No customer data leaves Snowflake during these steps.

📘

You will need a Snowflake user with the ACCOUNTADMIN role, OpenSSL installed locally, and an existing target database, warehouse, and schema before you begin.

Prerequisites

  • Access to Snowflake with the ACCOUNTADMIN role
  • OpenSSL installed on your local machine
  • The target database, warehouse, and schema already exist

Step 1: Generate the RSA Key Pair

Run the following on your local machine to generate the key pair. The private key is used by the Optimove service to authenticate; the public key is registered on the Snowflake user in Step 3.

# Generate private key (PEM format)
openssl genrsa -out rsa_key_raw.pem 2048
openssl pkcs8 -topk8 -in rsa_key_raw.pem -out rsa_key.p8 -nocrypt
 
# Generate public key
openssl rsa -in rsa_key.p8 -pubout -out rsa_key.pub
 
# Print the public key value (without header/footer) — copy this output
grep -v "PUBLIC KEY" rsa_key.pub | tr -d '\n'
💡

How the key pair works

Your local machine is used only to generate the keys. Once generated:

  • The public key goes to Snowflake (pasted into the CREATE USER statement).
  • The private key goes to the service (for example, a GKE cluster, stored as a secret).
    After both keys are in place, you can safely delete the files from your local machine — the connection between the service and Snowflake does not depend on it. Keep a backup of the private key in a secrets manager (for example, GCP Secret Manager or Vault) in case you need to re-deploy. If you lose it, you must generate a new key pair and update the public key in Snowflake.
📘

Private-key format for the Optimove platform

When you later configure this connection in the Optimove platform, you enter the private key as a base64-encoded string. Generate it from rsa_key.p8:

  cat rsa_key.p8 | base64 | tr -d '\n'

Step 2: Create the Read-Only Role

Create a dedicated role and grant it read-only access to the target database. The FUTURE grants ensure new schemas, tables, and views are covered automatically.

USE ROLE ACCOUNTADMIN;
 
-- Create the role
CREATE ROLE OPTIMOVE_ZERO_COPY_READONLY;
 
-- Grant warehouse usage
GRANT USAGE ON WAREHOUSE <YOUR_WAREHOUSE> TO ROLE OPTIMOVE_ZERO_COPY_READONLY;
 
-- Grant read access on the database
GRANT USAGE ON DATABASE <YOUR_DATABASE> TO ROLE OPTIMOVE_ZERO_COPY_READONLY;
GRANT USAGE ON ALL SCHEMAS IN DATABASE <YOUR_DATABASE> TO ROLE OPTIMOVE_ZERO_COPY_READONLY;
GRANT SELECT ON ALL TABLES IN DATABASE <YOUR_DATABASE> TO ROLE OPTIMOVE_ZERO_COPY_READONLY;
GRANT SELECT ON ALL VIEWS IN DATABASE <YOUR_DATABASE> TO ROLE OPTIMOVE_ZERO_COPY_READONLY;
 
-- Grant for future schemas, tables, and views (auto-applies to new objects)
GRANT USAGE ON FUTURE SCHEMAS IN DATABASE <YOUR_DATABASE> TO ROLE OPTIMOVE_ZERO_COPY_READONLY;
GRANT SELECT ON FUTURE TABLES IN DATABASE <YOUR_DATABASE> TO ROLE OPTIMOVE_ZERO_COPY_READONLY;
GRANT SELECT ON FUTURE VIEWS IN DATABASE <YOUR_DATABASE> TO ROLE OPTIMOVE_ZERO_COPY_READONLY;

Step 3: Create the Service User with the RSA Public Key

Create a SERVICE-type user, attach the public key from Step 1, and assign the read-only role.

USE ROLE ACCOUNTADMIN;
 
CREATE USER <USERNAME>
  TYPE = SERVICE
  RSA_PUBLIC_KEY = '<PASTE_PUBLIC_KEY_HERE>'
  DEFAULT_ROLE = OPTIMOVE_ZERO_COPY_READONLY;
 
GRANT ROLE OPTIMOVE_ZERO_COPY_READONLY TO USER <USERNAME>;
📘

Replace <PASTE_PUBLIC_KEY_HERE> with the output from the grep command in Step 1 — the base64 string only, with no header or footer lines.

Step 4: Create an Authentication Policy for Key-Pair Auth

If your account has an authentication policy that blocks key-pair auth (error 390202), create a user-level policy so this user can authenticate with a key pair.

USE ROLE ACCOUNTADMIN;
 
CREATE AUTHENTICATION POLICY <USERNAME>_keypair_policy
  AUTHENTICATION_METHODS = ('KEYPAIR')
  CLIENT_TYPES = ('DRIVERS')
  MFA_ENROLLMENT = 'OPTIONAL';
 
ALTER USER <USERNAME>
  SET AUTHENTICATION POLICY <USERNAME>_keypair_policy;
💡

Why is this needed?

Some accounts have a default authentication policy (for example, NO_MFA_AUTHENTICATION_POLICY) that only allows password auth. A user-level policy overrides it for this user only, with no impact on other users.

⚠️

CLIENT_TYPES = ('DRIVERS') covers all programmatic connections (Python, JDBC, ODBC, and so on). Do not include SNOWFLAKE_UI when using only KEYPAIR — they are incompatible.

Step 5: Verify the Setup

Confirm the public key, role grants, and authentication policy are in place.

-- Verify public key is set
DESCRIBE USER <USERNAME>;
-- Look for RSA_PUBLIC_KEY_FP — it should not be empty
 
-- Verify role grants
SHOW GRANTS TO USER <USERNAME>;
 
-- Verify auth policy
SHOW AUTHENTICATION POLICIES;

Step 6: Test the Connection

Test the service user's connection using either of the following methods.

Your account identifier uses the format MYORG-MYACCOUNT. To find it, run:

SELECT CURRENT_ORGANIZATION_NAME() || '-' || CURRENT_ACCOUNT_NAME();
snowsql \
  -a <ACCOUNT_IDENTIFIER> \
  -u <USERNAME> \
  --private-key-path rsa_key.p8
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.backends import default_backend
import snowflake.connector
 
with open("rsa_key.p8", "rb") as key_file:
    p_key = serialization.load_pem_private_key(
        key_file.read(),
        password=None,
        backend=default_backend()
    )
 
pkb = p_key.private_bytes(
    encoding=serialization.Encoding.DER,
    format=serialization.PrivateFormat.PKCS8,
    encryption_algorithm=serialization.NoEncryption()
)
 
conn = snowflake.connector.connect(
    account="<ACCOUNT_IDENTIFIER>",
    user="<USERNAME>",
    private_key=pkb,
)
 
cur = conn.cursor()
cur.execute("SELECT CURRENT_USER(), CURRENT_ROLE()")
print(cur.fetchone())
📘

The Python example requires the Snowflake connector and the cryptography library:

  pip install snowflake-connector-python cryptography

The connector expects the private key as DER-encoded bytes, which is why the key is loaded from PEM and re-serialized to DER above.

Step 7: Configure the Network Policy (IP Allow List)

Apply the network restriction after you have confirmed the connection works in Step 6.

First, check whether a network policy already exists:

SHOW NETWORK POLICIES;

If no policies exist, Snowflake allows all IPs by default — you can skip this step.

If a policy exists, create a dedicated user-level policy for the service user and attach it. This scopes the IP restriction to this user only and avoids the risk of locking out other Snowflake users.

Optimove's service runs the query from a wide range of IPs within your region, so you must include all Optimove IPs for your region — US or EU. Find the full list in the IP Allow List.

USE ROLE ACCOUNTADMIN;
 
CREATE NETWORK POLICY OPTIMOVE_ZERO_COPY_POLICY
  ALLOWED_IP_LIST = ('<OPTIMOVE_IP_1>', '<OPTIMOVE_IP_2>');
 
ALTER USER <USERNAME> SET NETWORK_POLICY = OPTIMOVE_ZERO_COPY_POLICY;
💡

Recommended: include all listed IPs for your region

Because the service may connect from any IP in your regional range, adding all the IPs listed for your region on the IP Allow List page is the most reliable way to avoid intermittent connection failures.

Troubleshooting

ErrorCauseFix
utf-8 codec can't decode bytePrivate key is in binary DER format, not PEMRegenerate with -outform PEM, or convert: openssl pkcs8 -inform DER -in key.p8 -outform PEM -out key_pem.p8 -nocrypt
Incorrect paddingBase64 string is malformed (copy/paste issue)Re-copy the key carefully, or try base64-encoding it (`cat rsa_key.p8base64`)
390202 AUTHN_POLICY_AUTHN_ATTEMPT_REJECTEDAuthentication policy blocks key-pair authCreate a user-level auth policy (see Step 4)
IP is not allowed to access SnowflakeNetwork policy is blocking the service IPEnsure all Optimove IPs for your region are in the user-level policy (see Step 7). Not all regional IPs may be included

Quick Reference: Diagnostic Queries

-- Check network policies
SHOW NETWORK POLICIES;
DESCRIBE NETWORK POLICY <policy_name>;
 
-- Check auth policies
SHOW AUTHENTICATION POLICIES;
 
-- Check failed logins
SELECT event_timestamp, user_name, client_ip, error_code, error_message
FROM snowflake.account_usage.login_history
WHERE is_success = 'NO'
  AND event_timestamp >= DATEADD('hour', -24, CURRENT_TIMESTAMP())
ORDER BY event_timestamp DESC;
 
-- Check account URL
SELECT CURRENT_ORGANIZATION_NAME() || '-' || CURRENT_ACCOUNT_NAME();


Did this page help you?