diff --git a/README.md b/README.md index a053c60ce55..f42f20f3bc7 100644 --- a/README.md +++ b/README.md @@ -33,43 +33,44 @@ There are two stacks that get deployed: In order to deploy both the stacks the user needs to provide a set of required and optional parameters listed below: -| Name | Requirement | Type | Description | -|------------------------|:------------|:--------|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| distVersion | Required | string | The OpenSearch distribution version (released/un-released) the user wants to deploy | -| securityDisabled | Required | boolean | Enable or disable security plugin | -| minDistribution | Required | boolean | Is it the minimal OpenSearch distribution with no security and plugins | -| distributionUrl | Required | string | OpenSearch tar distribution url | -| cpuArch | Required | string | CPU platform for EC2, could be either `x64` or `arm64` | -| singleNodeCluster | Required | boolean | Set `true` for single-node cluster else `false` for multi-node | -| serverAccessType | Required | string | Restrict server access based on ip address (ipv4/ipv6), prefix list and/or security group. See [Restricting Server Access](#restricting-server-access) for more details. | -| restrictServerAccessTo | Required | string | The value for `serverAccessType`, e.g., 10.10.10.10/32, pl-12345, sg-12345. See [Restricting Server Access](#restricting-server-access) for more details. | -| dashboardsUrl | Optional | string | OpenSearch Dashboards tar distribution url | -| vpcId | Optional | string | Re-use existing vpc, provide vpc id | -| securityGroupId | Optional | boolean | Re-use existing security group, provide security group id | -| cidr | Optional | string | User provided CIDR block for new Vpc. Defaults to `10.0.0.0/16` | -| managerNodeCount | Optional | integer | Number of cluster manager nodes. Defaults to 3 | -| dataNodeCount | Optional | integer | Number of data nodes. Defaults to 2 | -| clientNodeCount | Optional | integer | Number of dedicated client nodes. Defaults to 0 | -| ingestNodeCount | Optional | integer | Number of dedicated ingest nodes. Defaults to 0 | -| mlNodeCount | Optional | integer | Number of dedicated machine learning nodes. Defaults to 0 | -| dataInstanceType | Optional | string | EC2 instance type for data node. Defaults to r5.xlarge. See options in `lib/opensearch-config/node-config.ts` for available options. E.g., `-c dataInstanceType=m5.xlarge` | -| mlInstanceType | Optional | string | EC2 instance type for ml node. Defaults to r5.xlarge. See options in `lib/opensearch-config/node-config.ts` for available options. E.g., `-c mlInstanceType=m5.xlarge` | -| jvmSysProps | Optional | string | A comma-separated list of key=value pairs that will be added to `jvm.options` as JVM system properties. | -| additionalConfig | Optional | string | Additional opensearch.yml config parameters passed as JSON. e.g., `--context additionalConfig='{"plugins.security.nodes_dn": ["CN=*.example.com, OU=SSL, O=Test, L=Test, C=DE", "CN=node.other.com, OU=SSL, O=Test, L=Test, C=DE"], "plugins.security.nodes_dn_dynamic_config_enabled": false}'` | -| additionalOsdConfig | Optional | string | Additional opensearch_dashboards.yml config parameters passed as JSON. e.g., `additionalOsdConfig='{"data.search.usageTelemetry.enabled": "true"}'` | -| suffix | Optional | string | An optional string identifier to be concatenated with infra stack name. | -| networkStackSuffix (Optional) | string | An optional string identifier to be concatenated with network stack name. | -| region | Optional | string | User provided aws region | -| account | Optional | string | User provided aws account | -| dataNodeStorage | Optional | string | User provided ebs block storage size. Defaults to 100Gb | -| mlNodeStorage | Optional | string | User provided ebs block storage size. Defaults to 100Gb | -| use50PercentHeap | Optional | boolean | Boolean flag to use 50% of physical memory as heap. Defaults to 1GB. e.g., `--context use50PercentHeap=true` | -| isInternal | Optional | boolean | Boolean flag to make network load balancer internal. Defaults to internet-facing e.g., `--context isInternal=true` | -| enableRemoteStore | Optional | boolean | Boolean flag to enable Remote Store feature e.g., `--context enableRemoteStore=true`. See [Enable Remote Store Feature](#enable-remote-store-feature) for more details. Defaults to false | -| storageVolumeType | Optional | string | EBS volume type for all the nodes (data, ml, cluster manager). Defaults to gp2. See `lib/opensearch-config/node-config.ts` for available options. E.g., `-c storageVolumeType=gp3`. For SSD based instance (i.e. i3 family), it is used for root volume configuration. | -| customRoleArn | Optional | string | User provided IAM role arn to be used as ec2 instance profile. `-c customRoleArn=arn:aws:iam:::role/` | -| customConfigFiles | Optional | string | You can provide an entire config file to be overwritten or added to OpenSearch and OpenSearch Dashboards. Pass string in the form of JSON with key as local path to the config file to read from and value as file on the server to overwrite/add. Note that the values in the JSON needs to have prefix of `opensearch` or `opensearch-dashboards`. Example: `-c customConfigFiles='{"opensearch-config/config.yml": "opensearch/config/opensearch-security/config.yml", "opensearch-config/role_mapping.yml":"opensearch/config/opensearch-security/roles_mapping.yml", "/roles.yml": "opensearch/config/opensearch-security/roles.yml"}'` | -| enableMonitoring | Optional | boolean | Boolean flag to enable monitoring and alarms for Infra Stack. See [InfraStackMonitoring class](./lib/monitoring/alarms.ts) for more details. Defaults to false e.g., `--context enableMonitoring=true` +| Name | Requirement | Type | Description | +|-------------------------------|:------------|:--------------------------------------------------------------------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| distVersion | Required | string | The OpenSearch distribution version (released/un-released) the user wants to deploy | +| securityDisabled | Required | boolean | Enable or disable security plugin | +| adminPassword | Optional | string | This value is required when security plugin is enabled and the cluster version | +| minDistribution | Required | boolean | Is it the minimal OpenSearch distribution with no security and plugins | +| distributionUrl | Required | string | OpenSearch tar distribution url | +| cpuArch | Required | string | CPU platform for EC2, could be either `x64` or `arm64` | +| singleNodeCluster | Required | boolean | Set `true` for single-node cluster else `false` for multi-node | +| serverAccessType | Required | string | Restrict server access based on ip address (ipv4/ipv6), prefix list and/or security group. See [Restricting Server Access](#restricting-server-access) for more details. | +| restrictServerAccessTo | Required | string | The value for `serverAccessType`, e.g., 10.10.10.10/32, pl-12345, sg-12345. See [Restricting Server Access](#restricting-server-access) for more details. | +| dashboardsUrl | Optional | string | OpenSearch Dashboards tar distribution url | +| vpcId | Optional | string | Re-use existing vpc, provide vpc id | +| securityGroupId | Optional | boolean | Re-use existing security group, provide security group id | +| cidr | Optional | string | User provided CIDR block for new Vpc. Defaults to `10.0.0.0/16` | +| managerNodeCount | Optional | integer | Number of cluster manager nodes. Defaults to 3 | +| dataNodeCount | Optional | integer | Number of data nodes. Defaults to 2 | +| clientNodeCount | Optional | integer | Number of dedicated client nodes. Defaults to 0 | +| ingestNodeCount | Optional | integer | Number of dedicated ingest nodes. Defaults to 0 | +| mlNodeCount | Optional | integer | Number of dedicated machine learning nodes. Defaults to 0 | +| dataInstanceType | Optional | string | EC2 instance type for data node. Defaults to r5.xlarge. See options in `lib/opensearch-config/node-config.ts` for available options. E.g., `-c dataInstanceType=m5.xlarge` | +| mlInstanceType | Optional | string | EC2 instance type for ml node. Defaults to r5.xlarge. See options in `lib/opensearch-config/node-config.ts` for available options. E.g., `-c mlInstanceType=m5.xlarge` | +| jvmSysProps | Optional | string | A comma-separated list of key=value pairs that will be added to `jvm.options` as JVM system properties. | +| additionalConfig | Optional | string | Additional opensearch.yml config parameters passed as JSON. e.g., `--context additionalConfig='{"plugins.security.nodes_dn": ["CN=*.example.com, OU=SSL, O=Test, L=Test, C=DE", "CN=node.other.com, OU=SSL, O=Test, L=Test, C=DE"], "plugins.security.nodes_dn_dynamic_config_enabled": false}'` | +| additionalOsdConfig | Optional | string | Additional opensearch_dashboards.yml config parameters passed as JSON. e.g., `additionalOsdConfig='{"data.search.usageTelemetry.enabled": "true"}'` | +| suffix | Optional | string | An optional string identifier to be concatenated with infra stack name. | +| networkStackSuffix (Optional) | string | An optional string identifier to be concatenated with network stack name. | | +| region | Optional | string | User provided aws region | +| account | Optional | string | User provided aws account | +| dataNodeStorage | Optional | string | User provided ebs block storage size. Defaults to 100Gb | +| mlNodeStorage | Optional | string | User provided ebs block storage size. Defaults to 100Gb | +| use50PercentHeap | Optional | boolean | Boolean flag to use 50% of physical memory as heap. Defaults to 1GB. e.g., `--context use50PercentHeap=true` | +| isInternal | Optional | boolean | Boolean flag to make network load balancer internal. Defaults to internet-facing e.g., `--context isInternal=true` | +| enableRemoteStore | Optional | boolean | Boolean flag to enable Remote Store feature e.g., `--context enableRemoteStore=true`. See [Enable Remote Store Feature](#enable-remote-store-feature) for more details. Defaults to false | +| storageVolumeType | Optional | string | EBS volume type for all the nodes (data, ml, cluster manager). Defaults to gp2. See `lib/opensearch-config/node-config.ts` for available options. E.g., `-c storageVolumeType=gp3`. For SSD based instance (i.e. i3 family), it is used for root volume configuration. | +| customRoleArn | Optional | string | User provided IAM role arn to be used as ec2 instance profile. `-c customRoleArn=arn:aws:iam:::role/` | +| customConfigFiles | Optional | string | You can provide an entire config file to be overwritten or added to OpenSearch and OpenSearch Dashboards. Pass string in the form of JSON with key as local path to the config file to read from and value as file on the server to overwrite/add. Note that the values in the JSON needs to have prefix of `opensearch` or `opensearch-dashboards`. Example: `-c customConfigFiles='{"opensearch-config/config.yml": "opensearch/config/opensearch-security/config.yml", "opensearch-config/role_mapping.yml":"opensearch/config/opensearch-security/roles_mapping.yml", "/roles.yml": "opensearch/config/opensearch-security/roles.yml"}'` | +| enableMonitoring | Optional | boolean | Boolean flag to enable monitoring and alarms for Infra Stack. See [InfraStackMonitoring class](./lib/monitoring/alarms.ts) for more details. Defaults to false e.g., `--context enableMonitoring=true` | * Before starting this step, ensure that your AWS CLI is correctly configured with access credentials. * Also ensure that you're running these commands in the current directory @@ -163,9 +164,9 @@ All the ec2 instances are hosted in private subnet and can only be accessed usin The ports to access the cluster are dependent on the `security` parameter value * If `security` is `disable` (HTTP), - * OpenSearch 9200 is mapped to port 80 on the LB + * OpenSearch 9200 is mapped to port 80 on the LB * If `security` is `enable` (HTTPS), - * OpenSearch 9200 is mapped to port 443 on the LB + * OpenSearch 9200 is mapped to port 443 on the LB * OpenSearch-Dasboards 5601 is always mapped to port 8443 on the LB (HTTP) ## Teardown diff --git a/lib/infra/infra-stack.ts b/lib/infra/infra-stack.ts index cd7b4c412a1..a778edeb41b 100644 --- a/lib/infra/infra-stack.ts +++ b/lib/infra/infra-stack.ts @@ -51,6 +51,7 @@ export interface infraProps extends StackProps { readonly cpuArch: string, readonly cpuType: AmazonLinuxCpuType, readonly securityDisabled: boolean, + readonly adminPassword: string, readonly minDistribution: boolean, readonly distributionUrl: string, readonly dashboardsUrl: string, @@ -658,7 +659,8 @@ export class InfraStack extends Stack { ignoreErrors: false, })); } else { - cfnInitConfig.push(InitCommand.shellCommand('set -ex;cd opensearch; sudo -u ec2-user nohup ./opensearch-tar-install.sh >> install.log 2>&1 &', + // eslint-disable-next-line max-len + cfnInitConfig.push(InitCommand.shellCommand(`set -ex;cd opensearch; sudo -u ec2-user nohup env OPENSEARCH_INITIAL_ADMIN_PASSWORD=${props.adminPassword} ./opensearch-tar-install.sh >> install.log 2>&1 &`, { cwd: '/home/ec2-user', ignoreErrors: false, diff --git a/lib/os-cluster-entrypoint.ts b/lib/os-cluster-entrypoint.ts index 89cd8e8b1a5..7ccad997e3e 100644 --- a/lib/os-cluster-entrypoint.ts +++ b/lib/os-cluster-entrypoint.ts @@ -84,7 +84,12 @@ export class OsClusterEntrypoint { if (securityDisabled !== 'true' && securityDisabled !== 'false') { throw new Error('securityEnabled parameter is required to be set as - true or false'); } - const security = securityDisabled === 'true'; + const insecure = securityDisabled === 'true'; + + const adminPassword: String = insecure ? '' : `${scope.node.tryGetContext('adminPassword')}`; + if (!insecure && Number.parseFloat(distVersion) >= 2.12 && adminPassword === 'undefined') { + throw new Error('adminPassword parameter is required to be set when security is enabled'); + } const minDistribution = `${scope.node.tryGetContext('minDistribution')}`; if (minDistribution !== 'true' && minDistribution !== 'false') { @@ -252,7 +257,8 @@ export class OsClusterEntrypoint { // @ts-ignore const infraStack = new InfraStack(scope, infraStackName, { vpc: this.vpc, - securityDisabled: security, + securityDisabled: insecure, + adminPassword, opensearchVersion: distVersion, clientNodeCount: clientCount, cpuArch, diff --git a/test/os-cluster.test.ts b/test/os-cluster.test.ts index b986c2f0a4a..20a80456365 100644 --- a/test/os-cluster.test.ts +++ b/test/os-cluster.test.ts @@ -527,3 +527,65 @@ test('Throw error on incorrect JSON', () => { expect(error.message).toEqual('Encountered following error while parsing customConfigFiles json parameter: SyntaxError: Unexpected token o in JSON at position 25'); } }); + +test('Throw error when security is enabled and adminPassword is not defined and dist version is greater than or equal to 2.12', () => { + const app = new App({ + context: { + securityDisabled: false, + minDistribution: false, + distributionUrl: 'www.example.com', + cpuArch: 'x64', + singleNodeCluster: false, + dashboardsUrl: 'www.example.com', + distVersion: '3.0.0', + serverAccessType: 'ipv4', + restrictServerAccessTo: 'all', + managerNodeCount: 0, + dataNodeCount: 3, + dataNodeStorage: 200, + customRoleArn: 'arn:aws:iam::12345678:role/customRoleName', + }, + }); + + try { + const testStack = new OsClusterEntrypoint(app, { + env: { account: 'test-account', region: 'us-east-1' }, + }); + + // eslint-disable-next-line no-undef + fail('Expected an error to be thrown'); + } catch (error) { + expect(error).toBeInstanceOf(Error); + // eslint-disable-next-line max-len + expect(error.message).toEqual('adminPassword parameter is required to be set when security is enabled'); + } +}); + +test('Should not throw error when security is enabled and adminPassword is defined and dist version is greater than or equal to 2.12', () => { + const app = new App({ + context: { + securityDisabled: false, + adminPassword: 'Admin_1234', + minDistribution: false, + distributionUrl: 'www.example.com', + cpuArch: 'x64', + singleNodeCluster: false, + dashboardsUrl: 'www.example.com', + distVersion: '2.12.0', + serverAccessType: 'ipv4', + restrictServerAccessTo: 'all', + managerNodeCount: 0, + dataNodeCount: 3, + dataNodeStorage: 200, + customRoleArn: 'arn:aws:iam::12345678:role/customRoleName', + }, + }); + + // WHEN + const testStack = new OsClusterEntrypoint(app, { + env: { account: 'test-account', region: 'us-east-1' }, + }); + + // THEN + expect(testStack.stacks).toHaveLength(2); +});