From cd0af7956811f7ecb22b1fb1b590769aa9081a3b Mon Sep 17 00:00:00 2001 From: Rico Huijbers Date: Wed, 5 Sep 2018 13:08:12 +0200 Subject: [PATCH 1/4] feat(aws-cdk): add support for HTTPS_PROXY Also fix usage of CredentialProviderChain in the SDK creation. --- packages/aws-cdk/lib/api/util/sdk.ts | 46 ++++++++++++++++++++++------ packages/aws-cdk/package.json | 1 + 2 files changed, 37 insertions(+), 10 deletions(-) diff --git a/packages/aws-cdk/lib/api/util/sdk.ts b/packages/aws-cdk/lib/api/util/sdk.ts index 84f4046e5c0b4..61586f99f60e4 100644 --- a/packages/aws-cdk/lib/api/util/sdk.ts +++ b/packages/aws-cdk/lib/api/util/sdk.ts @@ -19,26 +19,35 @@ import { SharedIniFile } from './sdk_ini_file'; * to the requested account. */ export class SDK { - private readonly userAgent: string; private readonly defaultAwsAccount: DefaultAWSAccount; private readonly credentialsCache: CredentialsCache; + private readonly defaultClientArgs: any = {}; constructor(private readonly profile: string | undefined) { - // Find the package.json from the main toolkit - const pkg = (require.main as any).require('../package.json'); - this.userAgent = `${pkg.name}/${pkg.version}`; - const defaultCredentialProvider = makeCLICompatibleCredentialProvider(profile); this.defaultAwsAccount = new DefaultAWSAccount(defaultCredentialProvider); this.credentialsCache = new CredentialsCache(this.defaultAwsAccount, defaultCredentialProvider); + + // Find the package.json from the main toolkit + const pkg = (require.main as any).require('../package.json'); + this.defaultClientArgs.userAgent = `${pkg.name}/${pkg.version}`; + + // https://aws.amazon.com/blogs/developer/using-the-aws-sdk-for-javascript-from-behind-a-proxy/ + const proxyAddress = httpsProxyAddress(); + if (proxyAddress) { + debug('Using proxy server: %s', proxyAddress); + this.defaultClientArgs.httpOptions = { + agent: require('proxy-agent')(proxyAddress) + }; + } } public async cloudFormation(environment: Environment, mode: Mode): Promise { return new AWS.CloudFormation({ region: environment.region, credentials: await this.credentialsCache.get(environment.account, mode), - customUserAgent: this.userAgent + ...this.defaultClientArgs }); } @@ -46,7 +55,7 @@ export class SDK { return new AWS.EC2({ region, credentials: await this.credentialsCache.get(awsAccountId, mode), - customUserAgent: this.userAgent + ...this.defaultClientArgs }); } @@ -54,7 +63,7 @@ export class SDK { return new AWS.SSM({ region, credentials: await this.credentialsCache.get(awsAccountId, mode), - customUserAgent: this.userAgent + ...this.defaultClientArgs }); } @@ -62,7 +71,7 @@ export class SDK { return new AWS.S3({ region: environment.region, credentials: await this.credentialsCache.get(environment.account, mode), - customUserAgent: this.userAgent + ...this.defaultClientArgs }); } @@ -109,7 +118,11 @@ class CredentialsCache { const defaultAccount = await this.defaultAwsAccount.get(); if (!awsAccountId || awsAccountId === defaultAccount) { debug(`Using default AWS SDK credentials for account ${awsAccountId}`); - return this.defaultCredentialProvider; + + // CredentialProviderChain extends Credentials, but that is a lie. + // https://github.com/aws/aws-sdk-js/issues/2235 + // Call resolve() instead. + return (await this.defaultCredentialProvider).resolvePromise(); } const triedSources: CredentialProviderSource[] = []; @@ -256,3 +269,16 @@ async function getCLICompatibleDefaultRegion(profile: string | undefined): Promi return region; } + +/** + * Find and return the configured HTTPS proxy address + */ +function httpsProxyAddress(): string | undefined { + if (process.env.http_proxy) { + return process.env.http_proxy; + } + if (process.env.https_proxy) { + return process.env.https_proxy; + } + return undefined; +} \ No newline at end of file diff --git a/packages/aws-cdk/package.json b/packages/aws-cdk/package.json index 9eed25f940f32..fbc129b655ae1 100644 --- a/packages/aws-cdk/package.json +++ b/packages/aws-cdk/package.json @@ -56,6 +56,7 @@ "json-diff": "^0.3.1", "minimatch": ">=3.0", "promptly": "^0.2.0", + "proxy-agent": "^3.0.1", "request": "^2.83.0", "source-map-support": "^0.5.6", "yamljs": "^0.2.0", From f8c9831e7414280c9b15c584f50a1f3e020863af Mon Sep 17 00:00:00 2001 From: Rico Huijbers Date: Wed, 5 Sep 2018 13:16:39 +0200 Subject: [PATCH 2/4] Add documentation on proxy configuration --- docs/src/tools.rst | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/docs/src/tools.rst b/docs/src/tools.rst index 1d9c5aca3dced..f9467ad3e7621 100644 --- a/docs/src/tools.rst +++ b/docs/src/tools.rst @@ -85,6 +85,33 @@ Below are the actions you can take on your CDK app: If one of cdk.json or ~/.cdk.json exists, options specified there will be used as defaults. Settings in cdk.json take precedence. +.. _using-a-proxy: + +Using a proxy +~~~~~~~~~~~~~ + +If you use a proxy to connect to the Internet, configure it by setting either the `HTTPS_PROXY` or +`https_proxy` environment variable. + +On Linux and macOS, you set environment variables like this: + +.. code-block:: sh + + export HTTPS_PROXY=http://10.0.0.10:8080/ + +Put this line in your ``~/.bashrc`` file or similar to make it persistent. + +On Windows, you set an environment variable like this: + +.. code-block:: sh + + set HTTPS_PROXY=http://10.0.0.10:8080/ + +To make the setting persistent on Windows, right-click **My Computer** ~> **Properties** > **Advanced System Settings** +> **Environment Variables** > **New**, fill in the name and value in separate fields, click **OK** and restart your +command prompt. + + .. _version-reporting: Version Reporting From 08b9fa1594e6e8f2e91fc60c28739654957a9205 Mon Sep 17 00:00:00 2001 From: Rico Huijbers Date: Wed, 5 Sep 2018 13:30:32 +0200 Subject: [PATCH 3/4] Add backwards compatbility check for old plugins --- packages/aws-cdk/lib/api/util/sdk.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/packages/aws-cdk/lib/api/util/sdk.ts b/packages/aws-cdk/lib/api/util/sdk.ts index 61586f99f60e4..3c207bf5a4913 100644 --- a/packages/aws-cdk/lib/api/util/sdk.ts +++ b/packages/aws-cdk/lib/api/util/sdk.ts @@ -135,7 +135,14 @@ class CredentialsCache { triedSources.push(source); if (!(await source.canProvideCredentials(awsAccountId))) { continue; } debug(`Using ${source.name} credentials for account ${awsAccountId}`); - return await source.getProvider(awsAccountId, mode); + const providerOrCreds = await source.getProvider(awsAccountId, mode); + + // Backwards compatibility: if the plugin returns a ProviderChain, resolve that chain. + // Otherwise it must have returned credentials. + if ((providerOrCreds as any).resolvePromise) { + return await (providerOrCreds as any).resolvePromise(); + } + return providerOrCreds; } const sourceNames = ['default credentials'].concat(triedSources.map(s => s.name)).join(', '); throw new Error(`Need to perform AWS calls for account ${awsAccountId}, but no credentials found. Tried: ${sourceNames}.`); From abfefa06c855ef4abc1563e0807ed4491e8877c5 Mon Sep 17 00:00:00 2001 From: Rico Huijbers Date: Wed, 5 Sep 2018 14:05:00 +0200 Subject: [PATCH 4/4] Make --proxy an command-line argument, read correct environment variables --- docs/src/tools.rst | 63 ++++++++++------------------ packages/aws-cdk/bin/cdk.ts | 3 +- packages/aws-cdk/lib/api/util/sdk.ts | 16 +++---- 3 files changed, 34 insertions(+), 48 deletions(-) diff --git a/docs/src/tools.rst b/docs/src/tools.rst index f9467ad3e7621..5610c9a6144d7 100644 --- a/docs/src/tools.rst +++ b/docs/src/tools.rst @@ -64,54 +64,37 @@ Below are the actions you can take on your CDK app: init [TEMPLATE] Create a new, empty CDK project from a template. Invoked without TEMPLATE, the app template will be used. + doctor Check your set-up for potential problems Options: - --help Show help [boolean] - --app, -a REQUIRED: Command-line of cloud executable (e.g. "node - bin/my-app.js") [string] - --context, -c Add contextual string parameter. [array] - --plugin, -p Name or path of a node package that extend the CDK features. - Can be specified multiple times [array] - --rename Rename stack name if different then the one defined in the - cloud executable [string] - --trace Print trace for stack warnings [boolean] - --strict Do not construct stacks with warnings [boolean] - --json, -j Use JSON output instead of YAML [boolean] - --verbose, -v Show debug logs [boolean] - --version Show version number [boolean] + --app, -a REQUIRED: Command-line for executing your CDK app (e.g. + "node bin/my-app.js") [string] + --context, -c Add contextual string parameter. [array] + --plugin, -p Name or path of a node package that extend the CDK + features. Can be specified multiple times [array] + --rename Rename stack name if different then the one defined in + the cloud executable [string] + --trace Print trace for stack warnings [boolean] + --strict Do not construct stacks with warnings [boolean] + --ignore-errors Ignores synthesis errors, which will likely produce an + invalid output [boolean] [default: false] + --json, -j Use JSON output instead of YAML [boolean] + --verbose, -v Show debug logs [boolean] + --profile Use the indicated AWS profile as the default environment + [string] + --proxy Use the indicated proxy. Will read from HTTPS_PROXY + environment variable if not specified. [string] + --version-reporting Disable insersion of the CDKMetadata resource in + synthesized templates [boolean] + --version Show version number [boolean] + --help Show help [boolean] + If your app has a single stack, there is no need to specify the stack name If one of cdk.json or ~/.cdk.json exists, options specified there will be used as defaults. Settings in cdk.json take precedence. -.. _using-a-proxy: - -Using a proxy -~~~~~~~~~~~~~ - -If you use a proxy to connect to the Internet, configure it by setting either the `HTTPS_PROXY` or -`https_proxy` environment variable. - -On Linux and macOS, you set environment variables like this: - -.. code-block:: sh - - export HTTPS_PROXY=http://10.0.0.10:8080/ - -Put this line in your ``~/.bashrc`` file or similar to make it persistent. - -On Windows, you set an environment variable like this: - -.. code-block:: sh - - set HTTPS_PROXY=http://10.0.0.10:8080/ - -To make the setting persistent on Windows, right-click **My Computer** ~> **Properties** > **Advanced System Settings** -> **Environment Variables** > **New**, fill in the name and value in separate fields, click **OK** and restart your -command prompt. - - .. _version-reporting: Version Reporting diff --git a/packages/aws-cdk/bin/cdk.ts b/packages/aws-cdk/bin/cdk.ts index 2fd6f28632b1e..9b83f1b2b1dbd 100644 --- a/packages/aws-cdk/bin/cdk.ts +++ b/packages/aws-cdk/bin/cdk.ts @@ -47,6 +47,7 @@ async function parseCommandLineArguments() { .option('json', { type: 'boolean', alias: 'j', desc: 'Use JSON output instead of YAML' }) .option('verbose', { type: 'boolean', alias: 'v', desc: 'Show debug logs' }) .option('profile', { type: 'string', desc: 'Use the indicated AWS profile as the default environment' }) + .option('proxy', { type: 'string', desc: 'Use the indicated proxy. Will read from HTTPS_PROXY environment variable if not specified.' }) // tslint:disable-next-line:max-line-length .option('version-reporting', { type: 'boolean', desc: 'Disable insersion of the CDKMetadata resource in synthesized templates', default: undefined }) .command([ 'list', 'ls' ], 'Lists all stacks in the app', yargs => yargs @@ -108,7 +109,7 @@ async function initCommandLine() { debug('Command line arguments:', argv); - const aws = new SDK(argv.profile); + const aws = new SDK(argv.profile, argv.proxy); const availableContextProviders: contextplugins.ProviderMap = { 'availability-zones': new contextplugins.AZContextProviderPlugin(aws), diff --git a/packages/aws-cdk/lib/api/util/sdk.ts b/packages/aws-cdk/lib/api/util/sdk.ts index 3c207bf5a4913..9da3f1adb1d19 100644 --- a/packages/aws-cdk/lib/api/util/sdk.ts +++ b/packages/aws-cdk/lib/api/util/sdk.ts @@ -23,7 +23,7 @@ export class SDK { private readonly credentialsCache: CredentialsCache; private readonly defaultClientArgs: any = {}; - constructor(private readonly profile: string | undefined) { + constructor(private readonly profile: string | undefined, proxyAddress: string | undefined) { const defaultCredentialProvider = makeCLICompatibleCredentialProvider(profile); this.defaultAwsAccount = new DefaultAWSAccount(defaultCredentialProvider); @@ -34,8 +34,10 @@ export class SDK { this.defaultClientArgs.userAgent = `${pkg.name}/${pkg.version}`; // https://aws.amazon.com/blogs/developer/using-the-aws-sdk-for-javascript-from-behind-a-proxy/ - const proxyAddress = httpsProxyAddress(); - if (proxyAddress) { + if (proxyAddress === undefined) { + proxyAddress = httpsProxyFromEnvironment(); + } + if (proxyAddress) { // Ignore empty string on purpose debug('Using proxy server: %s', proxyAddress); this.defaultClientArgs.httpOptions = { agent: require('proxy-agent')(proxyAddress) @@ -280,12 +282,12 @@ async function getCLICompatibleDefaultRegion(profile: string | undefined): Promi /** * Find and return the configured HTTPS proxy address */ -function httpsProxyAddress(): string | undefined { - if (process.env.http_proxy) { - return process.env.http_proxy; - } +function httpsProxyFromEnvironment(): string | undefined { if (process.env.https_proxy) { return process.env.https_proxy; } + if (process.env.HTTPS_PROXY) { + return process.env.HTTPS_PROXY; + } return undefined; } \ No newline at end of file