Skip to content

IAM authentication token does not work in Postgres connection string #340

Open
@ejmolinelli

Description

@ejmolinelli

First Check

  • I added a very descriptive title to this issue.
  • I used the GitHub search to find a similar issue and didn't find it.
  • I searched the SQLModel documentation, with the integrated search.
  • I already searched in Google "How to X in SQLModel" and didn't find any information.
  • I already read and followed all the tutorial in the docs and didn't find an answer.
  • I already checked if it is not related to SQLModel but to Pydantic.
  • I already checked if it is not related to SQLModel but to SQLAlchemy.

Commit to Help

  • I commit to help with one of those options 👆

Example Code

import os
import boto3
from sqlmodel import create_engine, Session

# postgres url variables from environment
PG_HOST = os.environ.get('PG_HOST', False)
PG_USER = os.environ.get('PG_USER', False)
PG_PORT = os.environ.get('PG_PORT', '5432')
PG_REGION = os.environ.get('PG_REGION', False)
PG_DBNAME = os.environ.get('PG_DBNAME', False)
assert PG_HOST  is not False
assert PG_USER is not False
assert PG_PORT == '5432'
assert PG_REGION is not False
assert PG_DBNAME is not False

# IAM authentication with user profile
# NOTE: requires that the runtime has access to an aws credential profile with the appropriate permissions to access an RDS instance or Proxy (e.g. 'postgres_admin_role')

# create token via IAM role
session = boto3.session.Session(profile_name='postgres_admin_role', region_name='us-east-1')
token = session.client('rds').generate_db_auth_token(
            DBHostname=PG_HOST, 
            Port=PG_PORT, 
            DBUsername=PG_USER, 
            Region=PG_REGION)

# insert token into password section of postgres url
url = f'postgresql://{PG_USER}:{token}@{PG_HOST}{PG_PORT}/{PG_DBNAME}'
DATABASE = create_engine(url)

# get connection and exec command
with Session(DATABASE) as session:
   print(session.exec('SELECT NOW()'))

# RESULT: Error "The IAM authentication failed for the role postgres. Check the IAM token for this role and try again."

Description

The above code results in a runtime error:

The IAM authentication failed for the role postgres. Check the IAM token for this role and try again.

However, the URL is correct. I can login in from the same environment using the psql command.

Wanted Solution

The above code should run as is.

I did however come up with a solution which I posted here on stackoverflow.

The solution is to use the 'creator' argument to the create_engine function which returns a raw psycopg2 connection. (see alternatives below)

Wanted Code

# SAME CODE AS ABOVE

url = f'postgresql://{PG_USER}:{token}@{PG_HOST}{PG_PORT}/{PG_DBNAME}'
DATABASE = create_engine(url)

# get connection and exec command
with Session(DATABASE) as session:
   print(session.exec('SELECT NOW()'))

Alternatives

The following code works as a workaround

# the url
def get_pg_url(token) -> str:
    ssl = '?sslmode=require'
    return f'postgresql://{PG_USER}{token}@{PG_HOST}:{PG_PORT}/{PG_DB}{ssl}'

# the connection 
def get_connection(token):
   return psycopg2.connect(host=PG_HOST, user=PG_USER, port=PG_PORT, database=PG_DBNAME, password=token)

# Create the database engine
DATABASE = create_engine(get_pg_url(token), creator=get_connection)

with Session(DATABASE) as session:
   session.exec('SELECT NOW()')

Operating System

Linux

Operating System Details

ubuntu EC2 image

SQLModel Version

0.0.6

Python Version

3.9

Additional Context

It can be a lot of setup to recreate this problem as it has to run in an AWS VPC (and I'd be happy to help ). Roughly the steps are

1 - Create a VPC
2 -Security Group 1 - Accept TCP traffic on port 5432 from Security Group 2
3 - Security Group 2 - Accept all incoming traffic and allow outgoing TCP traffic on port 5432
4 - RDS postgres instance and attach to security group 1 (same vpc and all subnets)
4a - add a new role with login and password (in addition to the master username and password)
5 - save master and new user username/password in secrets manager
6 - Create RDS proxy and attach Security Group 2 (same vpc and all subnets)
6a - proxy needs a role with policy to get secrets and decrypt secret
7 - Create an ec2 instance (same vpc)
7a - ec2 needs a role with policy to connect to rds-proxy and for both users (i.e. master user and new user)
7b - pip install psycopg2-binary
7c - pip install sqlmodel boto3
7d - run example code from python terminal on this instance

Metadata

Metadata

Assignees

No one assigned

    Labels

    featureNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions