Skip to content

Commit

Permalink
Added support for using a certificate and key.
Browse files Browse the repository at this point in the history
Signed-off-by: dblock <dblock@amazon.com>
  • Loading branch information
dblock committed Dec 13, 2024
1 parent ebe0f8a commit e34839f
Show file tree
Hide file tree
Showing 13 changed files with 178 additions and 4 deletions.
12 changes: 9 additions & 3 deletions .github/workflows/test-spec.yml
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,16 @@ jobs:
tests: plugins/workload-management
- version: 2.18.0
tests: plugins/analysis
- version: 2.18.0
tests: plugins/security
cert: plugins/security/kirk.pem
key: plugins/security/kirk-key.pem
- version: 2.19.0
hub: opensearchstaging
ref: '@sha256:4da23e0137b2b67206d23b36fcf0914cc39b3bf19310c782f536e4934b86f6cc'
ref: "@sha256:4da23e0137b2b67206d23b36fcf0914cc39b3bf19310c782f536e4934b86f6cc"

Check failure on line 62 in .github/workflows/test-spec.yml

View workflow job for this annotation

GitHub Actions / lint

Strings must use singlequote
- version: 3.0.0
hub: opensearchstaging
ref: '@sha256:727643acdfebed77bfdb26362dbcff536b7ea02a0cc4ae2da2521729171333de'
ref: "@sha256:727643acdfebed77bfdb26362dbcff536b7ea02a0cc4ae2da2521729171333de"

Check failure on line 65 in .github/workflows/test-spec.yml

View workflow job for this annotation

GitHub Actions / lint

Strings must use singlequote

name: test-opensearch-spec (version=${{ matrix.entry.version }}, hub=${{ matrix.entry.hub || 'opensearchproject' }}, tests=${{ matrix.entry.tests || 'default' }})
runs-on: ubuntu-latest
Expand All @@ -77,7 +81,7 @@ jobs:
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '20'
node-version: "20"

Check failure on line 84 in .github/workflows/test-spec.yml

View workflow job for this annotation

GitHub Actions / lint

Strings must use singlequote

- name: Install Dependencies
run: npm ci
Expand All @@ -97,6 +101,8 @@ jobs:
--opensearch-version=${{ matrix.entry.version }} \
--coverage coverage/test-spec-coverage-${{ steps.tests.outputs.hash }}.json \
--opensearch-url=${{ matrix.entry.url || 'https://localhost:9200'}} \
--opensearch-cert=${{ matrix.entry.cert }} \
--opensearch-key=${{ matrix.entry.key }} \
--tests=tests/${{ matrix.entry.tests || 'default' }}
- name: Get Container Logs
Expand Down
2 changes: 2 additions & 0 deletions DEVELOPER_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,8 @@ The dump-cluster-spec tool connects to an OpenSearch cluster which has the [open
- `--opensearch-insecure`: Disable SSL/TLS certificate verification, defaults to performing verification.
- `--opensearch-username <username>`: The username to authenticate with the cluster, defaults to `admin`, only used when `--opensearch-password` is set.
- `--opensearch-password <password>`: The password to authenticate with the cluster, also settable via the `OPENSEARCH_PASSWORD` environment variable.
- `--opensearch-cert <file>`: The OpenSSL certificate file, also settable via the `OPENSEARCH_CERT` environment variable.
- `--opensearch-key <file>`: The OpenSSL certificate private key, also settable via the `OPENSEARCH_KEY` environment variable.
- `--output <path>`: The path to write the dumped spec to, defaults to `<repository-root>/build/opensearch-openapi-CLUSTER.yaml`.

**Example**
Expand Down
14 changes: 14 additions & 0 deletions TESTING_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
- [Prerequisites](#prerequisites)
- [OpenSearch Cluster](#opensearch-cluster)
- [Run Tests](#run-tests)
- [Running Spec Tests that Require an Admin Certificate](#running-spec-tests-that-require-an-admin-certificate)
- [Running Spec Tests with Amazon OpenSearch](#running-spec-tests-with-amazon-opensearch)
- [Common Errors](#common-errors)
- [401 Unauthorized](#401-unauthorized)
Expand Down Expand Up @@ -76,6 +77,19 @@ Want to help with some missing tests? Choose from the remaining paths in the tes
npm run test:spec -- --opensearch-insecure --coverage-report
```
### Running Spec Tests that Require an Admin Certificate
Some tests may require an admin certificate for authorization. The certificate can be provided wth `--opensearch-cert` and the key with `opensearch-key`.
For example, run tests in [plugins/security](tests/plugins/security) as follows:
```bash
npm run test:spec--insecure -- \
--tests tests/plugins/security/api/nodesdn.yaml \
--opensearch-key tests/plugins/security/kirk-key.pem \
--opensearch-cert tests/plugins/security/kirk.pem \
--verbose
```
### Running Spec Tests with Amazon OpenSearch
Use an Amazon OpenSearch service instance.
Expand Down
7 changes: 7 additions & 0 deletions tests/plugins/security/api/nodesdn.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
$schema: ../../../../json_schemas/test_story.schema.yaml

description: Test retrieving `nodesdn` dynamically.
chapters:
- synopsis: Retrieve `nodesdn`.
path: /_plugins/_security/api/nodesdn
method: GET
16 changes: 16 additions & 0 deletions tests/plugins/security/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
version: '3'

services:
opensearch-cluster:
image: ${OPENSEARCH_DOCKER_HUB_PROJECT:-opensearchproject}/opensearch:${OPENSEARCH_VERSION:-latest}${OPENSEARCH_DOCKER_REF}
ports:
- 9200:9200
- 9600:9600
environment:
- OPENSEARCH_INITIAL_ADMIN_PASSWORD=${OPENSEARCH_PASSWORD:-myStrongPassword123!}
- OPENSEARCH_JAVA_OPTS=${OPENSEARCH_JAVA_OPTS}
- discovery.type=single-node
- plugins.security.nodes_dn_dynamic_config_enabled=true
volumes:
- ./kirk-key.pem:/usr/share/opensearch/config/kirk-key.pem
- ./kirk.pem:/usr/share/opensearch/config/kirk.pem
28 changes: 28 additions & 0 deletions tests/plugins/security/kirk-key.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCVXDgEJQorgfXp
gpY0TgF55bD2xuzxN5Dc9rDfgWxrsOvOloMpd7k6FR71bKWjJi1KptSmM/cDElky
AWYKSfYWGiGxsQ+EQW+6kwCfEOHXQldn+0+JcWqP+osSPjtJfwRvRN5kRqP69MPo
7U0N2kdqenqMWjmG1chDGLRSOEGU5HIBiDxsZtOcvMaJ8b1eaW0lvS+6gFQ80AvB
GBkDDCOHHLtDXBylrZk2CQP8AzxNicIZ4B8G3CG3OHA8+nBtEtxZoIihrrkqlMt+
b/5N8u8zB0Encew0kdrc4R/2wS//ahr6U+9Siq8T7WsUtGwKj3BJClg6OyDJRhlu
y2gFnxoPAgMBAAECggEAP5TOycDkx+megAWVoHV2fmgvgZXkBrlzQwUG/VZQi7V4
ZGzBMBVltdqI38wc5MtbK3TCgHANnnKgor9iq02Z4wXDwytPIiti/ycV9CDRKvv0
TnD2hllQFjN/IUh5n4thHWbRTxmdM7cfcNgX3aZGkYbLBVVhOMtn4VwyYu/Mxy8j
xClZT2xKOHkxqwmWPmdDTbAeZIbSv7RkIGfrKuQyUGUaWhrPslvYzFkYZ0umaDgQ
OAthZew5Bz3OfUGOMPLH61SVPuJZh9zN1hTWOvT65WFWfsPd2yStI+WD/5PU1Doo
1RyeHJO7s3ug8JPbtNJmaJwHe9nXBb/HXFdqb976yQKBgQDNYhpu+MYSYupaYqjs
9YFmHQNKpNZqgZ4ceRFZ6cMJoqpI5dpEMqToFH7tpor72Lturct2U9nc2WR0HeEs
/6tiptyMPTFEiMFb1opQlXF2ae7LeJllntDGN0Q6vxKnQV+7VMcXA0Y8F7tvGDy3
qJu5lfvB1mNM2I6y/eMxjBuQhwKBgQC6K41DXMFro0UnoO879pOQYMydCErJRmjG
/tZSy3Wj4KA/QJsDSViwGfvdPuHZRaG9WtxdL6kn0w1exM9Rb0bBKl36lvi7o7xv
M+Lw9eyXMkww8/F5d7YYH77gIhGo+RITkKI3+5BxeBaUnrGvmHrpmpgRXWmINqr0
0jsnN3u0OQKBgCf45vIgItSjQb8zonLz2SpZjTFy4XQ7I92gxnq8X0Q5z3B+o7tQ
K/4rNwTju/sGFHyXAJlX+nfcK4vZ4OBUJjP+C8CTjEotX4yTNbo3S6zjMyGQqDI5
9aIOUY4pb+TzeUFJX7If5gR+DfGyQubvvtcg1K3GHu9u2l8FwLj87sRzAoGAflQF
RHuRiG+/AngTPnZAhc0Zq0kwLkpH2Rid6IrFZhGLy8AUL/O6aa0IGoaMDLpSWUJp
nBY2S57MSM11/MVslrEgGmYNnI4r1K25xlaqV6K6ztEJv6n69327MS4NG8L/gCU5
3pEm38hkUi8pVYU7in7rx4TCkrq94OkzWJYurAkCgYATQCL/rJLQAlJIGulp8s6h
mQGwy8vIqMjAdHGLrCS35sVYBXG13knS52LJHvbVee39AbD5/LlWvjJGlQMzCLrw
F7oILW5kXxhb8S73GWcuMbuQMFVHFONbZAZgn+C9FW4l7XyRdkrbR1MRZ2km8YMs
/AHmo368d4PSNRMMzLHw8Q==
-----END PRIVATE KEY-----
27 changes: 27 additions & 0 deletions tests/plugins/security/kirk.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
-----BEGIN CERTIFICATE-----
MIIEmDCCA4CgAwIBAgIUaYSlET3nzsotWTrWueVPPh10yLcwDQYJKoZIhvcNAQEL
BQAwgY8xEzARBgoJkiaJk/IsZAEZFgNjb20xFzAVBgoJkiaJk/IsZAEZFgdleGFt
cGxlMRkwFwYDVQQKDBBFeGFtcGxlIENvbSBJbmMuMSEwHwYDVQQLDBhFeGFtcGxl
IENvbSBJbmMuIFJvb3QgQ0ExITAfBgNVBAMMGEV4YW1wbGUgQ29tIEluYy4gUm9v
dCBDQTAeFw0yNDAyMjAxNzA0MjRaFw0zNDAyMTcxNzA0MjRaME0xCzAJBgNVBAYT
AmRlMQ0wCwYDVQQHDAR0ZXN0MQ8wDQYDVQQKDAZjbGllbnQxDzANBgNVBAsMBmNs
aWVudDENMAsGA1UEAwwEa2lyazCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
ggEBAJVcOAQlCiuB9emCljROAXnlsPbG7PE3kNz2sN+BbGuw686Wgyl3uToVHvVs
paMmLUqm1KYz9wMSWTIBZgpJ9hYaIbGxD4RBb7qTAJ8Q4ddCV2f7T4lxao/6ixI+
O0l/BG9E3mRGo/r0w+jtTQ3aR2p6eoxaOYbVyEMYtFI4QZTkcgGIPGxm05y8xonx
vV5pbSW9L7qAVDzQC8EYGQMMI4ccu0NcHKWtmTYJA/wDPE2JwhngHwbcIbc4cDz6
cG0S3FmgiKGuuSqUy35v/k3y7zMHQSdx7DSR2tzhH/bBL/9qGvpT71KKrxPtaxS0
bAqPcEkKWDo7IMlGGW7LaAWfGg8CAwEAAaOCASswggEnMAwGA1UdEwEB/wQCMAAw
DgYDVR0PAQH/BAQDAgXgMBYGA1UdJQEB/wQMMAoGCCsGAQUFBwMCMB0GA1UdDgQW
BBSjMS8tgguX/V7KSGLoGg7K6XMzIDCBzwYDVR0jBIHHMIHEgBQXh9+gWutmEqfV
0Pi6EkU8tysAnKGBlaSBkjCBjzETMBEGCgmSJomT8ixkARkWA2NvbTEXMBUGCgmS
JomT8ixkARkWB2V4YW1wbGUxGTAXBgNVBAoMEEV4YW1wbGUgQ29tIEluYy4xITAf
BgNVBAsMGEV4YW1wbGUgQ29tIEluYy4gUm9vdCBDQTEhMB8GA1UEAwwYRXhhbXBs
ZSBDb20gSW5jLiBSb290IENBghQNZAmZZn3EFOxBR4630XlhI+mo4jANBgkqhkiG
9w0BAQsFAAOCAQEACEUPPE66/Ot3vZqRGpjDjPHAdtOq+ebaglQhvYcnDw8LOZm8
Gbh9M88CiO6UxC8ipQLTPh2yyeWArkpJzJK/Pi1eoF1XLiAa0sQ/RaJfQWPm9dvl
1ZQeK5vfD4147b3iBobwEV+CR04SKow0YeEEzAJvzr8YdKI6jqr+2GjjVqzxvRBy
KRVHWCFiR7bZhHGLq3br8hSu0hwjb3oGa1ZI8dui6ujyZt6nm6BoEkau3G/6+zq9
E6vX3+8Fj4HKCAL6i0SwfGmEpTNp5WUhqibK/fMhhmMT4Mx6MxkT+OFnIjdUU0S/
e3kgnG8qjficUr38CyEli1U0M7koIXUZI7r+LQ==
-----END CERTIFICATE-----
19 changes: 18 additions & 1 deletion tools/src/OpenSearchHttpClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import { Option } from '@commander-js/extra-typings'
import axios, { type AxiosInstance, type AxiosRequestConfig, type AxiosResponse, type ResponseType } from 'axios'
import * as https from 'node:https'
import fs from 'fs'
import { sleep } from './helpers'
import { Logger } from './Logger'
import { aws4Interceptor } from 'aws4-axios'
Expand All @@ -29,6 +30,12 @@ export const OPENSEARCH_USERNAME_OPTION = new Option('--opensearch-username <use
export const OPENSEARCH_PASSWORD_OPTION = new Option('--opensearch-password <password>', 'password to use when authenticating with OpenSearch')
.env('OPENSEARCH_PASSWORD')

export const OPENSEARCH_CERT_OPTION = new Option('--opensearch-cert <cert>', 'client certificate file to use when authenticating with OpenSearch')
.env('OPENSEARCH_CERT')

export const OPENSEARCH_KEY_OPTION = new Option('--opensearch-key <cert>', 'client certificate private key file name to use when authenticating with OpenSearch')
.env('OPENSEARCH_KEY')

export const OPENSEARCH_INSECURE_OPTION = new Option('--opensearch-insecure', 'disable SSL/TLS certificate verification when connecting to OpenSearch')
.default(DEFAULT_INSECURE)

Expand Down Expand Up @@ -65,6 +72,8 @@ export interface AwsAuth {
export interface OpenSearchHttpClientOptions {
url?: string
insecure?: boolean
cert?: string,
key?: string,
responseType?: ResponseType
logger?: Logger,
basic_auth?: BasicAuth
Expand All @@ -77,6 +86,8 @@ export type OpenSearchHttpClientCliOptions = {
opensearchUsername?: string
opensearchPassword?: string
opensearchInsecure?: boolean
opensearchCert?: string,
opensearchKey?: string,
awsAccessKeyId?: string
awsSecretAccessKey?: string
awsSessionToken?: string
Expand All @@ -90,6 +101,8 @@ export function get_opensearch_opts_from_cli (opts: OpenSearchHttpClientCliOptio
return {
url: opts.opensearchUrl,
insecure: opts.opensearchInsecure,
cert: opts.opensearchCert,
key: opts.opensearchKey,
basic_auth: opts.opensearchUsername !== undefined && opts.opensearchPassword !== undefined ? {
username: opts.opensearchUsername,
password: opts.opensearchPassword
Expand Down Expand Up @@ -164,7 +177,11 @@ export class OpenSearchHttpClient {

this._axios = axios.create({
baseURL: opts?.url ?? DEFAULT_URL,
httpsAgent: new https.Agent({ rejectUnauthorized: !(opts?.insecure ?? DEFAULT_INSECURE) }),
httpsAgent: new https.Agent({
rejectUnauthorized: !(opts?.insecure ?? DEFAULT_INSECURE),
cert: opts?.cert !== undefined ? fs.readFileSync(opts?.cert) : undefined,
key: opts?.key !== undefined ? fs.readFileSync(opts?.key) : undefined,
}),
responseType: opts?.responseType,
})

Expand Down
4 changes: 4 additions & 0 deletions tools/src/dump-cluster-spec/dump-cluster-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ import * as process from 'node:process'
import { write_yaml } from '../helpers'
import {
get_opensearch_opts_from_cli,
OPENSEARCH_CERT_OPTION,
OPENSEARCH_INSECURE_OPTION,
OPENSEARCH_KEY_OPTION,
OPENSEARCH_PASSWORD_OPTION,
OPENSEARCH_URL_OPTION,
OPENSEARCH_USERNAME_OPTION, OpenSearchHttpClient,
Expand Down Expand Up @@ -42,6 +44,8 @@ const command = new Command()
.addOption(OPENSEARCH_USERNAME_OPTION)
.addOption(OPENSEARCH_PASSWORD_OPTION)
.addOption(OPENSEARCH_INSECURE_OPTION)
.addOption(OPENSEARCH_CERT_OPTION)
.addOption(OPENSEARCH_KEY_OPTION)
.addOption(new Option('--output <path>', 'path to the output file').default(resolve(__dirname, '../../../build/opensearch-openapi-CLUSTER.yaml')))
.allowExcessArguments(false)
.parse()
Expand Down
4 changes: 4 additions & 0 deletions tools/src/tester/test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ import {
AWS_SERVICE_OPTION,
AWS_SESSION_TOKEN_OPTION,
get_opensearch_opts_from_cli,
OPENSEARCH_CERT_OPTION,
OPENSEARCH_INSECURE_OPTION,
OPENSEARCH_KEY_OPTION,
OPENSEARCH_PASSWORD_OPTION,
OPENSEARCH_URL_OPTION,
OPENSEARCH_USERNAME_OPTION,
Expand Down Expand Up @@ -54,6 +56,8 @@ const command = new Command()
.addOption(OPENSEARCH_USERNAME_OPTION)
.addOption(OPENSEARCH_PASSWORD_OPTION)
.addOption(OPENSEARCH_INSECURE_OPTION)
.addOption(OPENSEARCH_CERT_OPTION)
.addOption(OPENSEARCH_KEY_OPTION)
.addOption(AWS_ACCESS_KEY_ID_OPTION)
.addOption(AWS_SECRET_ACCESS_KEY_OPTION)
.addOption(AWS_SESSION_TOKEN_OPTION)
Expand Down
43 changes: 43 additions & 0 deletions tools/tests/tester/OpenSearchHttpClient.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,4 +73,47 @@ describe('OpenSearchHttpClient', () => {

expect((await client.get('/')).data).toEqual({ called: true })
})

it('defaults to rejectUnauthorized', async () => {
let client = new OpenSearchHttpClient({
url: 'https://localhost:9200'
})

mock.onAny().reply((config) => {
expect(config.httpsAgent.options.rejectUnauthorized).toBe(true)
return [200, { called: true }]
})

expect((await client.get('/')).data).toEqual({ called: true })
})

it('sets rejectUnauthorized to false', async () => {
let client = new OpenSearchHttpClient({
url: 'https://localhost:9200',
insecure: true
})

mock.onAny().reply((config) => {
expect(config.httpsAgent.options.rejectUnauthorized).toEqual(false)
return [200, { called: true }]
})

expect((await client.get('/')).data).toEqual({ called: true })
})

it('adds a certificate file and key', async () => {
let client = new OpenSearchHttpClient({
url: 'https://localhost:9200',
cert: './tools/tests/tester/fixtures/keys/kirk.pem',
key: './tools/tests/tester/fixtures/keys/kirk-key.pem'
})

mock.onAny().reply((config) => {
expect(config.httpsAgent.options.cert.toString()).toEqual("-----BEGIN CERTIFICATE-----\ncertificate\n-----END CERTIFICATE-----\n")
expect(config.httpsAgent.options.key.toString()).toEqual("-----BEGIN PRIVATE KEY-----\nprivate key\n-----END PRIVATE KEY-----\n")
return [200, { called: true }]
})

expect((await client.get('/')).data).toEqual({ called: true })
})
})
3 changes: 3 additions & 0 deletions tools/tests/tester/fixtures/keys/kirk-key.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
-----BEGIN PRIVATE KEY-----
private key
-----END PRIVATE KEY-----
3 changes: 3 additions & 0 deletions tools/tests/tester/fixtures/keys/kirk.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
-----BEGIN CERTIFICATE-----
certificate
-----END CERTIFICATE-----

0 comments on commit e34839f

Please sign in to comment.