Skip to content

Set up OpenSearch manual snapshots with the master user from an "internal user database"

0

Hi all,

We have an OpenSearch domain with manual snapshots, it was working fine until we enable fine grained access control with master user from "Internal user database".

This is the related CDK code:

this.domainPass = new secretsmanager.Secret(this, 'search-master-pass', {
  description: `${domainId} OpenSearch master user password`,
})

this.domain = new opensearch.Domain(scope, domainId, {
  fineGrainedAccessControl: {
    masterUserName: this.domainPass.secretValueFromJson('username').toString(),
    masterUserPassword: this.domainPass.secretValueFromJson('password'),
  },
  encryptionAtRest: {
    enabled: true,
  },
  nodeToNodeEncryption: true,
  enforceHttps: true,
  version: opensearch.EngineVersion.openSearch('2.13'),
  ...
})
this.domain.addAccessPolicies(
  new iam.PolicyStatement({
    actions: ['es:*'],
    principals: [new iam.AnyPrincipal()],
    resources: [`${this.domain.domainArn}`, `${this.domain.domainArn}/*`],
  })
)

// Lambda that connects OpenSearch
this.domainLambda = new nodejs.NodejsFunction(this, `domain-lambda`, {
  architecture: lambda.Architecture.ARM_64,
  handler: 'index.handler',
  runtime: lambda.Runtime.NODEJS_20_X,
  environment: {
      OPENSEARCH_USERNAME: this.domainPass.secretValueFromJson('username').toString(),
      OPENSEARCH_PASSWORD: this.domainPass.secretValueFromJson('password').toString(),
  },
})

this.domain.grantReadWrite(this.domainLambda)
this.domainLambda.node.addDependency(this.domain)

// Snapshot setup
const snapshotBucket = new s3.Bucket(this, 'os-snapshots', {
  blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL,
})
const snapshotRole = new iam.Role(this, 'snapshot-role', {
  assumedBy: new iam.ServicePrincipal('es.amazonaws.com'),
})
snapshotRole.addToPolicy(
  new iam.PolicyStatement({
    actions: ['s3:ListBucket'],
    resources: [`${snapshotBucket.bucketArn}`],
  })
)
snapshotRole.addToPolicy(
  new iam.PolicyStatement({
    actions: ['s3:GetObject', 's3:PutObject', 's3:DeleteObject'],
    resources: [snapshotBucket.arnForObjects('*')],
  })
)

this.domainLambda.node.addDependency(snapshotBucket)
this.domainLambda.addToRolePolicy(
  new iam.PolicyStatement({
    actions: ['iam:PassRole'],
    resources: [snapshotRole.roleArn],
  })
)

This is how we connect from domainLambda function:

const client = new Client({
  node: `https://${domainEndpoint}`,
  auth: {
    username: process.env.OPENSEARCH_USERNAME,
    password: process.env.OPENSEARCH_PASSWORD,
  },
})

This works fine for searching, indexing and updating cluster settings etc.

But it fails with the following error when setting up snapshots:

{
  "body": {
    "Message": "User: anonymous is not authorized to perform: iam:PassRole on resource: arn:aws:iam::1234567890:role/dev-search-snapshot-role because no resource-based policy allows the iam:PassRole action"
  },
  "statusCode": 403,
}

When we use AwsSigv4Signer domainLambda function:

const client = new Client({
  node: `https://${domainEndpoint}`,
  ...AwsSigv4Signer({
      region: process.env.AWS_REGION,
      getCredentials: () => defaultProvider()(),
    }),
})

We get the following error when setting up snapshots:

{
  "body": {
    "error": {
      "reason": "no permissions for [cluster:admin/repository/put] and User [name=arn:aws:iam::1234567890:role/dev-resources-searchdomainlambda-88MJ3WaAhb57, backend_roles=[arn:aws:iam::1234567890:role/dev-resources-searchdomainlambda-88MJ3WaAhb57], requestedTenant=null]"
    }
  },
  "statusCode": 403,
}

I think we need to replace the following code with master user instead of lambda but not sure how to do that as master user is just a plain username and password:

this.domainLambda.addToRolePolicy(
  new iam.PolicyStatement({
    actions: ['iam:PassRole'],
    resources: [snapshotRole.roleArn],
  })
)

We can't use "IAM Principal" instead of "Internal user database" because we have this issue mentioned here.

3 Answers
0

The error message indicates that the Lambda function does not have the necessary permissions to perform the cluster:admin/repository/put action in OpenSearch. This is likely due to the fine-grained access control not granting the required permissions to the IAM role associated with the Lambda function.

To resolve this, you need to ensure that the IAM role used by the Lambda function has the necessary OpenSearch permissions for snapshot operations. Here are the additional steps you can take to address this issue:

  1. Update the OpenSearch Domain Access Policies
  2. Create a Custom Role Mapping in OpenSearch
  3. Adjust the Lambda Function Configuration

Let me know if you need Step-by-Step Implementation Thanks

answered 2 years ago
  • Thanks fro the answer. Yes, lambda doesn't have permission to perform cluster:admin/repository/put.

    1. Domain already has very loose permissions, it allows any principal to do any actions, what else need to be done?
    2. Not sure how to do this for lambda function
    3. Not sure what you mean

    The only solution that works is to set lambda role ARN (arn:aws:iam::1234567890:role/dev-resources-searchdomainlambda-88MJ3WaAhb57) as "Set IAM ARN as master user" but obviously we can't use this approach as we have many lambdas that connect to OpenSearch and we need to login as master user to dashboard.

    So we need a way to enable master user and password and also be able to set up manual snapshots from a lambda.

0

Hi, im struggling with the same issue. Have you found a solution?

answered a year ago
  • Hey, no unfortunately I had to do it manually.

-1

Steps to Set Up Manual Snapshots with Master User from Internal User Database Ensure Master User Setup: Make sure your master user is correctly set up within the OpenSearch domain using your internal user database.

Update IAM Roles and Policies: Ensure the IAM role for the Lambda function has the appropriate permissions, especially for iam:PassRole.

Configure the OpenSearch Domain with Fine-Grained Access Control: Ensure the OpenSearch domain configuration includes fine-grained access control using the master user from the internal user database.

Detailed Implementation

  1. OpenSearch Domain Configuration Configure your OpenSearch domain in CDK with the necessary access control and encryption settings.
import * as cdk from '@aws-cdk/core';
import * as secretsmanager from '@aws-cdk/aws-secretsmanager';
import * as opensearch from '@aws-cdk/aws-opensearchservice';
import * as s3 from '@aws-cdk/aws-s3';
import * as iam from '@aws-cdk/aws-iam';
import * as lambda from '@aws-cdk/aws-lambda-nodejs';

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

    // Secret for OpenSearch master user
    const domainPass = new secretsmanager.Secret(this, 'search-master-pass', {
      description: 'OpenSearch master user password',
    });

    // OpenSearch Domain
    const domain = new opensearch.Domain(this, 'OpenSearchDomain', {
      version: opensearch.EngineVersion.OPENSEARCH_1_0,
      fineGrainedAccessControl: {
        masterUserName: domainPass.secretValueFromJson('username').toString(),
        masterUserPassword: domainPass.secretValueFromJson('password'),
      },
      encryptionAtRest: {
        enabled: true,
      },
      nodeToNodeEncryption: true,
      enforceHttps: true,
    });

    // Access Policies for OpenSearch Domain
    domain.addAccessPolicies(
      new iam.PolicyStatement({
        actions: ['es:*'],
        principals: [new iam.AnyPrincipal()],
        resources: [`${domain.domainArn}`, `${domain.domainArn}/*`],
      })
    );

    // Snapshot S3 Bucket
    const snapshotBucket = new s3.Bucket(this, 'os-snapshots', {
      blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL,
    });

    // Snapshot IAM Role
    const snapshotRole = new iam.Role(this, 'snapshot-role', {
      assumedBy: new iam.ServicePrincipal('es.amazonaws.com'),
    });

    snapshotRole.addToPolicy(
      new iam.PolicyStatement({
        actions: ['s3:ListBucket'],
        resources: [snapshotBucket.bucketArn],
      })
    );

    snapshotRole.addToPolicy(
      new iam.PolicyStatement({
        actions: ['s3:GetObject', 's3:PutObject', 's3:DeleteObject'],
        resources: [snapshotBucket.arnForObjects('*')],
      })
    );

    // Lambda Function
    const domainLambda = new lambda.NodejsFunction(this, 'domain-lambda', {
      runtime: lambda.Runtime.NODEJS_14_X,
      handler: 'index.handler',
      entry: 'lambda/index.js', // your lambda function entry file
      environment: {
        DOMAIN_ENDPOINT: domain.domainEndpoint,
        OPENSEARCH_USERNAME: domainPass.secretValueFromJson('username').toString(),
        OPENSEARCH_PASSWORD: domainPass.secretValueFromJson('password').toString(),
        AWS_REGION: this.region,
      },
    });

    domain.grantReadWrite(domainLambda);

    domainLambda.addToRolePolicy(
      new iam.PolicyStatement({
        actions: ['iam:PassRole'],
        resources: [snapshotRole.roleArn],
      })
    );

    snapshotBucket.grantReadWrite(domainLambda);
    domainLambda.node.addDependency(snapshotBucket);
    domainLambda.node.addDependency(snapshotRole);
  }
}

  1. Lambda Function Configuration Ensure your Lambda function uses AWS SigV4 signing for authenticated requests to OpenSearch:
const { Client } = require('@opensearch-project/opensearch');
const { AwsSigv4Signer } = require('@opensearch-project/opensearch/aws');
const { defaultProvider } = require('@aws-sdk/credential-provider-node');

const client = new Client({
  node: process.env.DOMAIN_ENDPOINT,
  ...AwsSigv4Signer({
    region: process.env.AWS_REGION,
    getCredentials: () => defaultProvider()(),
  }),
});

Key Points to Note Fine-Grained Access Control: Ensure your OpenSearch domain's fine-grained access control is properly configured with the internal user database and the master user has sufficient permissions.

IAM Policies: Make sure the IAM role associated with your Lambda function has the iam:PassRole permission for the snapshot-role.

Snapshot Role Trust Relationship: The snapshot-role should trust the es.amazonaws.com service principal to allow OpenSearch to assume the role.

This approach should help you set up manual snapshots while using the master user from your internal user database, resolving any permission issues encountered with IAM roles and policies.

answered 2 years ago
EXPERT
reviewed 2 years ago
  • Hi @ehsanonyx thanks for the quick reply. But whats different than my code? I have the same code and it gives the following error as I mentioned in the original post:

    "reason": "no permissions for [cluster:admin/repository/put] and User [name=arn:aws:iam::1234567890:role/dev-resources-searchdomainlambda-88MJ3WaAhb57, backend_roles=[arn:aws:iam::1234567890:role/dev-resources-searchdomainlambda-88MJ3WaAhb57], requestedTenant=null]"

  • It's appreciated you spent time to answer but can someone tell me how this solves the issue in the original post? It's basically copy-paste of my code above.

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.