I have setup an AWS opensearch serverless collection and have one index.
In my opensearch serverless I have the following data access policy:
[
{
"Rules": [
{
"Resource": [
"collection/*"
],
"Permission": [
"aoss:CreateCollectionItems",
"aoss:DeleteCollectionItems",
"aoss:UpdateCollectionItems",
"aoss:DescribeCollectionItems"
],
"ResourceType": "collection"
},
{
"Resource": [
"index/*/*"
],
"Permission": [
"aoss:*"
],
"ResourceType": "index"
}
],
"Principal": [
"arn:aws:iam::MY_ACCOUNT:role/my_lambda_role",
]
}
]
(For testing purposes I have given my_lambda_role full access to all resources including aoss:APIAccessAll etc)
When I run the following lambda function (using my_lambda_role as the execution role):
const AWS = require('aws-sdk');
exports.handler = async (event) => {
const endpoint = new AWS.Endpoint('1234abcd.us-east-1.aoss.amazonaws.com');
const region = 'us-east-1';
const index = 'cats_and_dogs';
// Create the HTTP request
const request = new AWS.HttpRequest(endpoint, region);
request.method = 'POST';
request.path = `/${index}/_search`;
request.headers['host'] = endpoint.host;
request.headers['Content-Type'] = 'application/json';
request.body = JSON.stringify({
query: {
match: {
description: "farm"
}
}
});
// Sign the request using AWS Signature Version 4
const signer = new AWS.Signers.V4(request, 'aoss');
signer.addAuthorization(AWS.config.credentials, new Date());
// Send the request using AWS HttpClient
return new Promise((resolve, reject) => {
const client = new AWS.HttpClient();
client.handleRequest(
request,
null,
(response) => {
let responseBody = '';
response.on('data', (chunk) => {
responseBody += chunk;
});
response.on('end', () => {
resolve({
statusCode: response.statusCode,
body: JSON.parse(responseBody),
});
});
},
(error) => {
console.error('Request error:', error);
reject({
statusCode: 500,
body: JSON.stringify({ error: 'Failed to execute search' }),
});
}
);
});
};
I get 403 Forbidden:
{
"statusCode": 403,
"body": {
"status": 403,
"error": {
"reason": "403 Forbidden",
"type": "Forbidden"
}
}
}
The problem was that AWS.Signers.V4() was not adding the 'X-Amz-Content-Sha256' header which is required in opensearch serverless but not opensearch self managed it seems (I never head it when using standard opensearch self managed):
Solution:
// Sign the request using AWS Signature Version 4
const signer = new AWS.Signers.V4(request, 'aoss');
signer.addAuthorization(AWS.config.credentials, new Date());
request.headers['X-Amz-Content-Sha256'] = signer.hexEncodedHash(request.body);