skip to Main Content

I’m trying to build a stack using AWS CDK, which includes an RDS PostgreSQL instance and a Lambda function. From the Lambda, I’m trying to access the PostgreSQL, but I’m not succeeding in any way. My problem is that no matter what I do, I always get this error:

FATAL: no pg_hba.conf entry for host "IP", user "postgres",
database "codeSageVector", no encryption

Could someone help me figure out where the error is? I think it’s related to the security groups, but I have no clue. I’m starting to go crazy.

This is my Stack:

import * as cdk from 'aws-cdk-lib';
import {
  aws_cloudfront as cloudfront,
  aws_dynamodb as dynamodb,
  aws_ec2 as ec2,
  aws_events as events,
  aws_events_targets as targets,
  aws_iam as iam,
  aws_lambda as lambda,
  aws_lambda_destinations,
  aws_rds as rds,
  aws_s3 as s3,
  aws_secretsmanager as secretsmanager,
  Duration
} from 'aws-cdk-lib';
import { Construct } from 'constructs';
import { AccountRootPrincipal, AnyPrincipal, Effect, PolicyStatement } from 'aws-cdk-lib/aws-iam';
import { BlockPublicAccess, BucketAccessControl, ObjectOwnership } from 'aws-cdk-lib/aws-s3';
import * as path from 'path';
import * as cognito from 'aws-cdk-lib/aws-cognito';
import * as appsync from 'aws-cdk-lib/aws-appsync';
import { CustomStackProps } from '../types/custom-stack-props.type';
import { OriginAccessIdentity } from 'aws-cdk-lib/aws-cloudfront';
import { Peer, Port } from 'aws-cdk-lib/aws-ec2';

export class CodeSageStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps, customStackProps?: CustomStackProps) {
    super(scope, id, props);

    /**
     * Postgres
     */
    const postgresDatabaseName = 'codeSageVector';

    // Create a VPC with 2 availability zones
    const vpc = new ec2.Vpc(this, 'Vpc', {
      maxAzs: 2
    });

    // Create a security group for the database
    const postgresSecurityGroup = new ec2.SecurityGroup(this, 'postgresSecurityGroup', {
      vpc,
      allowAllOutbound: true
    });

    // Allow inbound traffic on port 5432 (PostgreSQL) from the VPC CIDR range
    postgresSecurityGroup.addIngressRule(Peer.anyIpv4(), Port.tcp(5432));

    // Create a secret for the database credentials
    const postgresDbSecret = new secretsmanager.Secret(this, 'postgresDbSecret', {
      generateSecretString: {
        secretStringTemplate: JSON.stringify({
          username: 'postgres'
        }),
        generateStringKey: 'password',
        excludePunctuation: true,
        includeSpace: false
      }
    });

    // Create a PostgreSQL database instance
    const postgresDb = new rds.DatabaseInstance(this, 'postgresDb', {
      engine: rds.DatabaseInstanceEngine.postgres({
        version: rds.PostgresEngineVersion.VER_15_2
      }),
      databaseName: postgresDatabaseName,
      instanceType: ec2.InstanceType.of(
        ec2.InstanceClass.BURSTABLE3,
        ec2.InstanceSize.SMALL
      ),
      vpc: vpc,
      vpcSubnets: {
        subnetType: ec2.SubnetType.PUBLIC
      },
      multiAz: false,
      securityGroups: [postgresSecurityGroup],
      credentials: rds.Credentials.fromSecret(postgresDbSecret),
      allocatedStorage: 20,
      storageType: rds.StorageType.GP2,
      deletionProtection: false
    });

    /**
     * CodeBase Bucket
     */
    const codeBaseBucket = new s3.Bucket(this, 'code-base', {
      removalPolicy: cdk.RemovalPolicy.DESTROY,
      autoDeleteObjects: true,
      eventBridgeEnabled: true,
      cors: [
        {
          allowedHeaders: ['*'],
          allowedMethods: [s3.HttpMethods.PUT],
          allowedOrigins: [...(customStackProps?.s3?.codeBaseBucket?.allowedOrigins || []), `https://${distribution.distributionDomainName}`]
        }
      ]
    });

    /**
     * Lambda Layers
     */

    const ulidLayer3_9 = new cdk.aws_lambda.LayerVersion(this, 'codeSageUlid3_9', {
      layerVersionName: 'CodeSageUlid3_9',
      code: cdk.aws_lambda.Code.fromAsset(path.join(__dirname, `../lambda/layers/ulid/`)),
      description: 'A ULID is a universally unique lexicographically sortable identifier',
      compatibleRuntimes: [cdk.aws_lambda.Runtime.PYTHON_3_9],
      removalPolicy: cdk.RemovalPolicy.DESTROY
    });

    const psycopg2Layer = new cdk.aws_lambda.LayerVersion(this, 'codeSagePsycopg2Layer', {
      layerVersionName: 'codeSagePsycopg2Layer',
      code: cdk.aws_lambda.Code.fromAsset(path.join(__dirname, `../lambda/layers/psycopg2/`)),
      description: 'Psycopg is the most popular PostgreSQL database adapter for the Python programming language',
      compatibleRuntimes: [cdk.aws_lambda.Runtime.PYTHON_3_9],
      removalPolicy: cdk.RemovalPolicy.DESTROY
    });

    /**
     * Lambda Functions
     */

    // Defines the CreateProject AWS Lambda resource
    const createProjectFunctionSG = new ec2.SecurityGroup(this, 'createProjectFunctionSG', {
      vpc
    });

    const createProjectFunction = new lambda.Function(this, 'CreateProject', {
      runtime: lambda.Runtime.PYTHON_3_9,
      code: cdk.aws_lambda.Code.fromAsset(path.join(__dirname, `../lambda/functions/create-project/`)),
      handler: 'app.lambda_handler',
      layers: [psycopg2Layer, ulidLayer3_9],
      environment: {
        APP_TABLE_NAME: appDynamoDbTable.tableName,
        S3_CODE_BASE_BUCKET: codeBaseBucket.bucketName,
        REGION: cdk.Stack.of(this).region,
        POSTGRES_DB_ENDPOINT: postgresDb.dbInstanceEndpointAddress,
        POSTGRES_DB_NAME: postgresDatabaseName,
        POSTGRES_SECRET_ARN: postgresDb.secret?.secretFullArn || ''
      }
    });
    postgresDb.connections.allowDefaultPortFrom(createProjectFunctionSG);
    postgresDb.secret?.grantRead(createProjectFunction);

    codeBaseBucket.grantReadWrite(createProjectFunction);

   
  }
}

And this is my Lambda:

import os
import boto3
import logging
from datetime import datetime
import psycopg2
import psycopg2.extras
import psycopg2.sql
import json

region= os.environ['REGION']
postgres_db_endpoint = os.environ['POSTGRES_DB_ENDPOINT']
postgres_db_name = os.environ['POSTGRES_DB_NAME']
postgres_secret_arn = os.environ['POSTGRES_SECRET_ARN']

def lambda_handler(event, context):
    secretManager = boto3.client('secretsmanager', region_name=region)

    secretParams = {
       'SecretId': postgres_secret_arn
    }

    dbSecret = secretManager.get_secret_value(**secretParams)
    secretString = dbSecret['SecretString']

    if not secretString:
       raise Exception('Secret string is empty')

    secretData = json.loads(secretString)
    password = secretData['password']

    conn = psycopg2.connect(
        user='postgres',
        host=postgres_db_endpoint,
        database=postgres_db_name,
        password=password,
        port=5432
    )

    cursor = conn.cursor(cursor_factory=psycopg2.extras.RealDictCursor)
    cursor.execute(psycopg2.sql.SQL('SELECT * FROM users'))
    result = cursor.fetchone()
    print(result['message'])
    cursor.close()
    conn.close()

    return {
        
    }

<!-- end snippet -->

2

Answers


  1. Chosen as BEST ANSWER

    The reason for the error was that SSL usage is mandatory for Postgre databases on AWS, and the Psycopg2 library I was using did not support SSL.

    In the end, I used this library and it worked: https://github.com/anuragdev101/psycopg2


  2. Suggesting to configure both the vpc and vpcSubnets of your function to make sure it gets access to the database.

    const createProjectFunction = new lambda.Function(this, 'CreateProject', {
          runtime: lambda.Runtime.PYTHON_3_9,
          code: cdk.aws_lambda.Code.fromAsset(path.join(__dirname, `../lambda/functions/create-project/`)),
          handler: 'app.lambda_handler',
          layers: [psycopg2Layer, ulidLayer3_9],
          environment: {
            APP_TABLE_NAME: appDynamoDbTable.tableName,
            S3_CODE_BASE_BUCKET: codeBaseBucket.bucketName,
            REGION: cdk.Stack.of(this).region,
            POSTGRES_DB_ENDPOINT: postgresDb.dbInstanceEndpointAddress,
            POSTGRES_DB_NAME: postgresDatabaseName,
            POSTGRES_SECRET_ARN: postgresDb.secret?.secretFullArn || ''
          },
          // 👇 place lambda in the VPC
          vpc,
          // 👇 place lambda in the same subnet where your DB is
          //    NOTE: You can improve your security posture by simply moving your
          //          database to the private subnet. If you decide to move with that
          //          use ec2.SubnetType.PRIVATE_WITH_EGRESS instead.
          vpcSubnets: {
            subnetType: ec2.SubnetType.PUBLIC,
          },
        });
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search