Problem with cross-account IAM-authorized HTTP API Gateway

0

I have created, as per the CDK example at this link, an HTTP API Gateway protected by IAM authorizers. However, I cannot get any requests to pass through it.

The producer lambda function simply returns a Harry Potter title and is omitted for brevity.

Here is the CDK code deployed to the producer account:

const authorizer = new authorizers.HttpIamAuthorizer();

    const booksFunction = new lambda.Function(this, "BooksFunction", {
      runtime: lambda.Runtime.NODEJS_18_X,
      code: lambda.Code.fromAsset(
        path.join(__dirname, "..", "handlers", "books")
      ),
      handler: "books.handler",
    });

    const booksIntegration = new integrations.HttpLambdaIntegration(
      "LambdaIntegration",
      booksFunction
    );

    const principal = new iam.AccountPrincipal("CONSUMER_ACCOUNT_ID");

    const httpApi = new apigwv2.HttpApi(this, "HttpProxyApi", {
      createDefaultStage: true,
      defaultAuthorizer: authorizer,
    });

    const routes = httpApi.addRoutes({
      integration: booksIntegration,
      methods: [apigwv2.HttpMethod.GET],
      path: "/books",
      // authorizer,
    });

    routes[0].grantInvoke(principal);

I tried adding a stage and CORS options to it, but no difference.

My lambda on the consumer account has the required privileges to invoke our API gateway. Here's the CDK code:

    const lambdaRole = new iam.Role(this, "Role", {
      roleName: "example-role",
      assumedBy: new iam.ServicePrincipal("lambda.amazonaws.com"),
    });

    lambdaRole.addToPolicy(
      new iam.PolicyStatement({
        effect: iam.Effect.ALLOW,
        actions: ["execute-api:Invoke"],
        resources: [
          this.formatArn({
            account: "PRODUCER_ACCOUNT_ID",
            service: "execute-api",
            resource: "API_NAME",
            resourceName: "*",
          }),
        ],
      })
    );

    const booksFunction = new nodejs.NodejsFunction(this, "GetBookFunction", {
      runtime: lambda.Runtime.NODEJS_18_X,
      entry: path.join(__dirname, "..", "handlers", "get_book", "get_book.ts"),
      handler: "handler",
      role: lambdaRole,
      bundling: {
        nodeModules: ["@aws-crypto/sha256-js", "url"],
      },
    });
  }

The consumer function signs requests with v4 signatures:

const v4 = new SignatureV4({
  service: "execute-api",
  region: process.env.AWS_DEFAULT_REGION || "",
  credentials: {
    accessKeyId: process.env.AWS_ACCESS_KEY_ID || "",
    secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY || "",
    sessionToken: process.env.AWS_SESSION_TOKEN || "",
  },
  sha256: Sha256,
});

const signRequest = async (request: HttpRequest): Promise<HttpRequest> => {
  const signedRequest = await v4.sign(request);
  return signedRequest;
};

export const handler = async (event, context) => {
  const url = new URL(
    "https://API_ID.execute-api.us-east-1.amazonaws.com/books"
  );
  const request = new HttpRequest({
    hostname: url.host,
    method: "GET",
    headers: {
      host: url.host,
    },
    path: url.pathname,
  });
  const results: Record<string, any> = {
    error: {},
    response: {},
  };
  try {
    const signedRequest = await signRequest(request);
    const response = await fetch(url.href, signedRequest);
    const responseJson = await response.json();
    results.response = responseJson;
  } catch (e) {
    results.error = e;
  }
  return results;
};

Calling producer's API Gateway from the consumer lambda returns:

{
  "error": {},
  "response": {
    "message": "Forbidden"
  }
}

This message means an exception didn't occur.

I can also confirm via logging that the API Gateway is being hit, but it returns a 403 forbidden error.

What am I doing wrong here? I'm at my wits' end.

1 Answer
0

It seems as if you try to use the consumer Lambda execution directly to invoke the API. This is possible in REST APIs (with according permissions in the API Gateway's resource policy), but doesn't work for HTTP APIs. Here, you need to assume a role in your producer account to fulfill the requirements of your IAM authorizer.

You can find a step-by-step guide in this knowledge base article. Hope that helps!

profile pictureAWS
Michael
answered 5 months ago

You are not logged in. Log in to post an answer.

A good answer clearly answers the question and provides constructive feedback and encourages professional growth in the question asker.

Guidelines for Answering Questions