Skip to content

Machine‐to‐Machine Authentication Using Snowflake

nicole-osterbrock edited this page Jul 12, 2026 · 8 revisions

Project Overview

This project demonstrates how to implement secure Machine-to-Machine (M2M) authentication in Snowflake using a dedicated service account, RSA key-pair authentication, and Role-Based Access Control (RBAC).

The lab follows enterprise security best practices by eliminating shared administrator credentials, enforcing the Principle of Least Privilege, validating authorization boundaries, and demonstrating key rotation.


Technologies

  • Snowflake
  • Python
  • Snowflake Connector for Python
  • OpenSSL
  • SQL
  • RSA Key Pair Authentication
  • RBAC
  • Identity & Access Management (IAM)

Goal

Build a complete Machine-to-Machine (M2M) authentication lab using Snowflake.


Step 1 – Create a Snowflake Trial Account

Objective

Provision a Snowflake environment to build and test Machine-to-Machine (M2M) authentication.

Actions Performed

  • Registered for a Snowflake Trial account.
  • Logged into the Snowsight interface.
  • Verified access as the ACCOUNTADMIN role.
  • Confirmed the default compute warehouse (COMPUTE_WH) was automatically provisioned.

What Happened

Snowflake provisioned a secure cloud environment containing:

  • Identity and Access Management (IAM)
  • Compute resources
  • Storage
  • SQL execution engine
  • Administrative interface

Security Concept

Authentication begins at the Snowflake account level. All users, service accounts, roles, and authentication methods are managed from this account.


Step 2 – Verify the Compute Warehouse

Objective

Verify compute resources are available to execute SQL queries.

Actions Performed

Verified that the default warehouse COMPUTE_WH exists and is available.

What Happened

A warehouse is Snowflake's compute layer. Unlike traditional databases, compute is separated from storage, allowing independent scaling and automatic suspension when not in use.

Security Concept

Authentication grants access to Snowflake, but queries cannot execute without authorized compute resources.


Step 3 – Create the Lab Database

Objective

Create an isolated database for the authentication lab.

SQL

CREATE DATABASE IAM_LAB;

USE DATABASE IAM_LAB;

CREATE SCHEMA DEMO;

USE SCHEMA DEMO;

Result

Created a dedicated database and schema that will store the sample application data used throughout this lab.

What Happened

The lab now has its own isolated workspace separate from system databases.

Security Concept

Applications should access only the databases required for their function. Isolating application data simplifies permission management and supports the principle of least privilege.


Create Sample Data

Objective

Create a sample table that represents customer data an enterprise application will access after successfully authenticating to Snowflake.

Why are we doing this?

Machine-to-Machine authentication is only meaningful if an application has a protected resource to access.

In this part:

  • The Python application will authenticate to Snowflake.
  • Snowflake will verify its identity.
  • If authorized, the application will read data from the CUSTOMERS table.

This simulates a common enterprise scenario where a backend service retrieves customer information without any human interaction.


Create the Table

Run the following SQL:

Result

A table named CUSTOMERS is created inside:

IAM_LAB
└── DEMO
    └── CUSTOMERS

What Happened

The database now contains a protected resource that can later be secured using roles and permissions.

Security Concept

Authentication answers:

Who are you?

Authorization answers:

What are you allowed to access?

The CUSTOMERS table is the resource we'll protect with authorization.

Screenshot 2026-07-04 at 01 35 42

Step 2 – Insert Sample Data

Run:

INSERT INTO CUSTOMERS
VALUES
(1,'Nicole','nicole@test.com'),
(2,'Alice','alice@test.com'),
(3,'Bob','bob@test.com');

Result

Three sample customer records are inserted into the table.

What Happened

The table now contains data that our future machine-to-machine application will retrieve after authenticating successfully.


Step 3 – Verify the Data

Run:

SELECT * FROM CUSTOMERS;
Screenshot 2026-07-04 at 01 38 41

What Happened

The query confirms:

  • The table was created successfully.
  • The records were inserted successfully.
  • The environment is ready for RBAC configuration.

Security Concept

At the moment, you're logged in as ACCOUNTADMIN, which has broad privileges.

In a production environment, applications should never use an administrative account. Instead, they'll use a dedicated service identity with only the permissions it needs. That's what we'll build next.


Implement Role-Based Access Control (RBAC)

Objective

Create a dedicated application role that has permission to access only the resources required by the application.

This demonstrates the Principle of Least Privilege, one of the most important security concepts in enterprise IAM.

Why are we doing this?

Right now, you're using: ACCOUNTADMIN

That role can do almost anything:

  • Create users
  • Delete databases
  • Create warehouses
  • Change security settings
  • Read all data

No production application should ever run with these permissions.

Instead, applications should use a dedicated role with only the permissions required.

Enterprise Architecture

Today your environment looks like this:

ACCOUNTADMIN
        │
        ▼
IAM_LAB
        │
        ▼
CUSTOMERS

After this section it will look like:

Application
      │
      ▼
Service User
      │
      ▼
LAB_APP_ROLE
      │
      ▼
CUSTOMERS table

Notice that the application does not connect as ACCOUNTADMIN.


Step 1 – Create the Application Role

Run:

CREATE ROLE LAB_APP_ROLE;

Result

A new security role named LAB_APP_ROLE is created.

What Happened

Roles are collections of permissions.

Instead of assigning permissions directly to users, Snowflake recommends assigning permissions to roles and then assigning roles to users.

This makes permission management much easier.

Security Concept

This is Role-Based Access Control (RBAC).

Instead of:

User
 ├── Permission
 ├── Permission
 ├── Permission

Enterprise systems use:

User
    │
Role
    │
Permissions

Step 2 – Allow the Role to Use the Warehouse

Run:

GRANT USAGE
ON WAREHOUSE COMPUTE_WH
TO ROLE LAB_APP_ROLE;

What Happened

The role is now allowed to use the compute resources.

Without this permission, the application could authenticate successfully but still wouldn't be able to execute queries.

Security Concept

Authentication does not automatically grant access to compute resources.

Authorization determines what authenticated identities can actually use.


Step 3 – Grant Database Access

Run:

GRANT USAGE
ON DATABASE IAM_LAB
TO ROLE LAB_APP_ROLE;

Result

The role can now locate the database.


Step 4 – Grant Schema Access

Run:

GRANT USAGE
ON SCHEMA IAM_LAB.DEMO
TO ROLE LAB_APP_ROLE;

Result

The role can now access objects inside the schema.


Step 5 – Grant Read Access to the Table

Run:

GRANT SELECT
ON TABLE IAM_LAB.DEMO.CUSTOMERS
TO ROLE LAB_APP_ROLE;

Result

The role can read data from the CUSTOMERS table.

Notice what we didn't grant: ❌ INSERT ❌ UPDATE ❌ DELETE ❌ OWNERSHIP

Security Concept

This is the Principle of Least Privilege in action.

The future application only needs to read customer information.

Therefore, we grant only: SELECT

Nothing else.

Verify Your Work

Run: SHOW GRANTS TO ROLE LAB_APP_ROLE;

Screenshot 2026-07-04 at 02 39 19

Lab Summary

You have now created an enterprise RBAC model.


Create a Service Identity

Objective

Create a dedicated service user that represents a non-human application. This service account will eventually authenticate using an RSA key pair instead of a password.

Why are we doing this?

Your Python application should never connect using your personal administrator account.

Instead, enterprise applications use service accounts (also called service users or non-human identities).

For example:

Screenshot 2026-07-04 at 02 43 57

This is the identity your Python application will use.


Step 1 – Create the Service User

Run:

CREATE USER LAB_SERVICE_USER
PASSWORD = 'TempPassword123!'
DEFAULT_ROLE = LAB_APP_ROLE
DEFAULT_WAREHOUSE = COMPUTE_WH
DEFAULT_NAMESPACE = IAM_LAB.DEMO
MUST_CHANGE_PASSWORD = FALSE;

Result

A new user named LAB_SERVICE_USER is created.

What Happened

You've created a dedicated identity that applications can use instead of sharing a personal administrator account.

At the moment, it still has no effective permissions because it hasn't been assigned the application role.

Security Concept

Identity and Permissions are separate.

Creating a user only establishes who the identity is. Permissions come later by assigning roles.


Step 2 – Assign the Application Role

Run:

GRANT ROLE LAB_APP_ROLE
TO USER LAB_SERVICE_USER;

Result

The service user now inherits all permissions assigned to LAB_APP_ROLE.

Those permissions are limited to:

  • Using COMPUTE_WH
  • Accessing IAM_LAB
  • Accessing the DEMO schema
  • Reading the CUSTOMERS table

Nothing more.


Step 3 – Verify the Role Assignment

Run:

SHOW GRANTS TO USER LAB_SERVICE_USER;

You should see LAB_APP_ROLE in the results.


Lab Summary

Your security model now looks like this:

LAB_SERVICE_USER
        │
        ▼
LAB_APP_ROLE
        │
        ├── USAGE on COMPUTE_WH
        ├── USAGE on IAM_LAB
        ├── USAGE on DEMO
        └── SELECT on CUSTOMERS

This is a common RBAC design in enterprise Snowflake deployments.


Why create a password if keys are going to be used?

We need the user object to exist before we can configure key-pair authentication. Right now the password is simply a placeholder so the user can be created.

In the next section, we'll configure RSA key-pair authentication, and the Python application will authenticate using a private key instead of relying on that password.


Create a Service Identity

Objective

Create a dedicated service account for machine-to-machine authentication.

CREATE USER LAB_SERVICE_USER
PASSWORD='TempPassword123!'
DEFAULT_ROLE=LAB_APP_ROLE
DEFAULT_WAREHOUSE=COMPUTE_WH
DEFAULT_NAMESPACE=IAM_LAB.DEMO
MUST_CHANGE_PASSWORD=FALSE;

GRANT ROLE LAB_APP_ROLE
TO USER LAB_SERVICE_USER;

Result

A non-human service account was created and assigned the least-privilege application role.

What Happened

Instead of allowing applications to authenticate with an administrator account, a dedicated service identity was created. The service user inherits only the permissions granted through LAB_APP_ROLE, following Snowflake's RBAC model.

Security Concept

Service accounts isolate application access from human users, improving security, auditability, and credential management. This approach supports the Principle of Least Privilege and is the recommended pattern for enterprise machine-to-machine authentication.


Generate RSA Key Pair

##Objective

Create a private/public key pair so the service user can authenticate without a password.


Step 1 — Open Terminal

On your computer, open Terminal.

Run:

mkdir snowflake_m2m_lab
cd snowflake_m2m_lab

Step 2 — Generate Private Key

openssl genrsa 2048 | openssl pkcs8 -topk8 -inform PEM -out rsa_key.p8 -nocrypt

Step 3 — Generate Public Key

openssl rsa -in rsa_key.p8 -pubout -out rsa_key.pub

Step 4 — View Public Key

cat rsa_key.pub

Copy the key, we'll need it for Snowflake.


Add Public Key to Service User

In Snowflake worksheet, run this using your copied public key:

ALTER USER LAB_SERVICE_USER
SET RSA_PUBLIC_KEY = 'PASTE_PUBLIC_KEY_HERE';

Then verify:

DESC USER LAB_SERVICE_USER;
Screenshot 2026-07-04 at 03 19 17

Objective

Configure Snowflake to trust the service user’s RSA public key.

What Happened

Snowflake stored the public key on the service identity. The private key stays on your machine and will be used by the Python application to prove its identity.

Security Concept

This enables password-less machine-to-machine authentication using asymmetric cryptography.


Install Python Connector

In Terminal, inside your lab folder, run:

python3 -m venv venv
source venv/bin/activate
pip install snowflake-connector-python cryptography

Then create your app file:

touch app.py

Open app.py and type this:

import snowflake.connector

conn = snowflake.connector.connect(
    user="LAB_SERVICE_USER",
    account="YOUR_ACCOUNT_IDENTIFIER",
    private_key_file="rsa_key.p8",
    warehouse="COMPUTE_WH",
    database="IAM_LAB",
    schema="DEMO",
    role="LAB_APP_ROLE"
)

cur = conn.cursor()
cur.execute("SELECT * FROM CUSTOMERS")

for row in cur:
    print(row)

cur.close()
conn.close()

Replace:

YOUR_ACCOUNT_IDENTIFIER

with your Snowflake account identifier.

Then run:

python app.py
Screenshot 2026-07-04 at 03 44 16

Python M2M Authentication

Objective

Use a Python application to authenticate to Snowflake as LAB_SERVICE_USER using RSA key-pair authentication.

Result

The Python app connected to Snowflake and returned the three customer records from IAM_LAB.DEMO.CUSTOMERS.

Security Concept

The application authenticated without using a human admin account or password. Access was controlled through LAB_APP_ROLE, enforcing least privilege.


Security Hardening

Objective

Remove password reliance and make the service account more secure.


Step 1 – Disable Password Login

Run in Snowflake:

ALTER USER LAB_SERVICE_USER
SET PASSWORD = NULL;

Step 2 – Confirm Key Still Exists

DESC USER LAB_SERVICE_USER;

Confirm RSA_PUBLIC_KEY is still populated.

Screenshot 2026-07-04 at 03 52 13

Step 3 – Re-run Python App

In Terminal:

python app.py
Screenshot 2026-07-04 at 03 53 25

Objective

Harden the service account by removing password-based authentication.

Result

The service account can still authenticate using RSA key-pair authentication, but no longer relies on a password.

Security Concept

This reduces credential risk by preventing password-based login and enforcing machine authentication through asymmetric keys.


Prove Least Privilege

Objective

Confirm the service user can read data but cannot modify it.


Step 1 – Update app.py

Add this under your successful SELECT query:

print("\nTesting unauthorized INSERT...")

try:
    cur.execute("INSERT INTO CUSTOMERS VALUES (4, 'Eve', 'eve@test.com')")
except Exception as e:
    print("INSERT failed as expected:")
    print(e)
Screenshot 2026-07-04 at 04 01 33

Step 2 – Run the App

python app.py
Screenshot 2026-07-04 at 04 03 20

Result

The service account successfully read from CUSTOMERS but failed when attempting to insert a new record.

Security Concept

This proves least privilege. The service role has SELECT access only, not INSERT, UPDATE, or DELETE.


Key Rotation

Objective

Show how a service account key can be rotated without changing the application role or permissions.


Step 1 – Generate a Second Key Pair

In Terminal:

openssl genrsa 2048 | openssl pkcs8 -topk8 -inform PEM -out rsa_key_2.p8 -nocrypt
openssl rsa -in rsa_key_2.p8 -pubout -out rsa_key_2.pub
cat rsa_key_2.pub

Copy only the Base64 text between the BEGIN and END lines.

Verify the files were created

Run:

ls -l
Screenshot 2026-07-04 at 04 17 36

Step 2 – Display and copy the second public key

Run:

cat rsa_key_2.pub

Copy the key.


Step 3 – Add it to Snowflake

In your Snowflake worksheet:

ALTER USER LAB_SERVICE_USER
SET RSA_PUBLIC_KEY_2 = 'PASTE_SECOND_PUBLIC_KEY_HERE';

Then verify:

DESC USER LAB_SERVICE_USER;
Screenshot 2026-07-04 at 04 24 20

Validate Key Rotation

Objective

Verify that the application can authenticate using the new private key before retiring the old one.


Step 1 – Update app.py

Find:

private_key_file="rsa_key.p8",

Replace it with:

private_key_file="rsa_key_2.p8",
Screenshot 2026-07-04 at 04 28 51

Step 2 – Run the Application

python app.py

Result

The Python application authenticated successfully using RSA key-pair authentication and retrieved customer data from Snowflake. When the application attempted an unauthorized INSERT, Snowflake denied the request with an SQL access control error because the service role was granted only SELECT privileges.

Security Validation

This test confirmed that: ✅ Machine-to-machine authentication is functioning correctly. ✅ RBAC is correctly configured. ✅ Least privilege is enforced. ✅ Unauthorized write operations are blocked.


We have implemented:

✅ Snowflake account provisioning ✅ Warehouse usage ✅ Database and schema creation ✅ RBAC design ✅ Least privilege ✅ Service account creation ✅ RSA key-pair authentication ✅ Passwordless machine-to-machine authentication ✅ Python integration ✅ Permission validation ✅ Key rotation


Architecture

 Python Application
                          │
                          ▼
                RSA Private Key (.p8)
                          │
                          ▼
                 Snowflake Authentication
                          │
                          ▼
                 LAB_SERVICE_USER
                          │
                          ▼
                   LAB_APP_ROLE
                          │
        ┌─────────────────┴─────────────────┐
        │                                   │
 Warehouse: COMPUTE_WH             Database: IAM_LAB
                                          │
                                          ▼
                                   Schema: DEMO
                                          │
                                          ▼
                                   CUSTOMERS Table

Security Validation

Authorization Validation

After successfully authenticating, the application intentionally attempted an unauthorized INSERT operation.

Snowflake denied the request because the service role had only SELECT permissions.

This validates:

  • RBAC
  • Principle of Least Privilege
  • Authorization Enforcement
Screenshot 2026-07-04 at 04 50 42

Lessons Learned

  • Authentication and authorization are separate security controls.
  • RBAC is easier to manage than assigning permissions directly to users.
  • Service accounts should replace administrator accounts for application access.
  • RSA key-pair authentication reduces the risks associated with password-based authentication.
  • Snowflake supports seamless key rotation through dual public keys.

Clone this wiki locally