From f3a01d09fbe984e3c7d750c8f677a053815dcfc1 Mon Sep 17 00:00:00 2001 From: Clark Laughlin Date: Thu, 25 Aug 2022 13:40:07 -0500 Subject: [PATCH] add proxy support using hpagent --- dist/index.js | 150 ++++++++++++++++++++++++++++++++++++++++++++++ package-lock.json | 14 +++++ package.json | 1 + src/action.js | 22 +++++++ 4 files changed, 187 insertions(+) diff --git a/dist/index.js b/dist/index.js index b0e9bb6..9fddd17 100644 --- a/dist/index.js +++ b/dist/index.js @@ -14552,6 +14552,134 @@ module.exports = header => { }; +/***/ }), + +/***/ 737: +/***/ (function(module, __unusedexports, __webpack_require__) { + +"use strict"; + + +const https = __webpack_require__(211) +const http = __webpack_require__(605) +const { URL } = __webpack_require__(835) + +class HttpProxyAgent extends http.Agent { + constructor (options) { + const { proxy, ...opts } = options + super(opts) + this.proxy = typeof proxy === 'string' + ? new URL(proxy) + : proxy + } + + createConnection (options, callback) { + const requestOptions = { + method: 'CONNECT', + host: this.proxy.hostname, + port: this.proxy.port, + path: `${options.host}:${options.port}`, + setHost: false, + headers: { connection: this.keepAlive ? 'keep-alive' : 'close', host: `${options.host}:${options.port}` }, + agent: false, + timeout: options.timeout || 0 + } + + if (this.proxy.username || this.proxy.password) { + const base64 = Buffer.from(`${decodeURIComponent(this.proxy.username || '')}:${decodeURIComponent(this.proxy.password || '')}`).toString('base64') + requestOptions.headers['proxy-authorization'] = `Basic ${base64}` + } + + if (this.proxy.protocol === 'https:') { + requestOptions.servername = this.proxy.hostname + } + + const request = (this.proxy.protocol === 'http:' ? http : https).request(requestOptions) + request.once('connect', (response, socket, head) => { + request.removeAllListeners() + socket.removeAllListeners() + if (response.statusCode === 200) { + callback(null, socket) + } else { + callback(new Error(`Bad response: ${response.statusCode}`), null) + } + }) + + request.once('timeout', () => { + request.destroy(new Error('Proxy timeout')) + }) + + request.once('error', err => { + request.removeAllListeners() + callback(err, null) + }) + + request.end() + } +} + +class HttpsProxyAgent extends https.Agent { + constructor (options) { + const { proxy, ...opts } = options + super(opts) + this.proxy = typeof proxy === 'string' + ? new URL(proxy) + : proxy + } + + createConnection (options, callback) { + const requestOptions = { + method: 'CONNECT', + host: this.proxy.hostname, + port: this.proxy.port, + path: `${options.host}:${options.port}`, + setHost: false, + headers: { connection: this.keepAlive ? 'keep-alive' : 'close', host: `${options.host}:${options.port}` }, + agent: false, + timeout: options.timeout || 0 + } + + if (this.proxy.username || this.proxy.password) { + const base64 = Buffer.from(`${decodeURIComponent(this.proxy.username || '')}:${decodeURIComponent(this.proxy.password || '')}`).toString('base64') + requestOptions.headers['proxy-authorization'] = `Basic ${base64}` + } + + // Necessary for the TLS check with the proxy to succeed. + if (this.proxy.protocol === 'https:') { + requestOptions.servername = this.proxy.hostname + } + + const request = (this.proxy.protocol === 'http:' ? http : https).request(requestOptions) + request.once('connect', (response, socket, head) => { + request.removeAllListeners() + socket.removeAllListeners() + if (response.statusCode === 200) { + const secureSocket = super.createConnection({ ...options, socket }) + callback(null, secureSocket) + } else { + callback(new Error(`Bad response: ${response.statusCode}`), null) + } + }) + + request.once('timeout', () => { + request.destroy(new Error('Proxy timeout')) + }) + + request.once('error', err => { + request.removeAllListeners() + callback(err, null) + }) + + request.end() + } +} + +module.exports = { + HttpProxyAgent, + HttpsProxyAgent +} + + /***/ }), /***/ 738: @@ -16438,6 +16566,7 @@ exports.default = createRejection; const core = __webpack_require__(470); const command = __webpack_require__(431); const got = __webpack_require__(77).default; +const { HttpProxyAgent, HttpsProxyAgent } = __webpack_require__(737); const jsonata = __webpack_require__(350); const { auth: { retrieveToken }, secrets: { getSecrets } } = __webpack_require__(676); @@ -16463,6 +16592,7 @@ async function exportSecrets() { prefixUrl: vaultUrl, headers: {}, https: {}, + agent: {}, retry: { statusCodes: [ ...got.defaults.options.retry.statusCodes, @@ -16473,6 +16603,26 @@ async function exportSecrets() { } } + if (process.env['http_proxy'] || process.env['HTTP_PROXY']) { + defaultOptions.agent.http = new HttpProxyAgent({ + keepAlive: true, + keepAliveMsecs: 1000, + maxSockets: 256, + maxFreeSockets: 256, + proxy: process.env['http_proxy'] || process.env['HTTP_PROXY'] + }) + } + + if (process.env['https_proxy'] || process.env['HTTPS_PROXY']) { + defaultOptions.agent.https = new HttpsProxyAgent({ + keepAlive: true, + keepAliveMsecs: 1000, + maxSockets: 256, + maxFreeSockets: 256, + proxy: process.env['https_proxy'] || process.env['HTTPS_PROXY'] + }) + } + const tlsSkipVerify = (core.getInput('tlsSkipVerify', { required: false }) || 'false').toLowerCase() != 'false'; if (tlsSkipVerify === true) { defaultOptions.https.rejectUnauthorized = false; diff --git a/package-lock.json b/package-lock.json index 524c13a..8259639 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "license": "MIT", "dependencies": { "got": "^11.8.5", + "hpagent": "^1.0.0", "jsonata": "^1.8.6", "jsrsasign": "^10.5.25" }, @@ -3602,6 +3603,14 @@ "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", "dev": true }, + "node_modules/hpagent": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/hpagent/-/hpagent-1.0.0.tgz", + "integrity": "sha512-SCleE2Uc1bM752ymxg8QXYGW0TWtAV4ZW3TqH1aOnyi6T6YW2xadCcclm5qeVjvMvfQ2RKNtZxO7uVb9CTPt1A==", + "engines": { + "node": ">=14" + } + }, "node_modules/html-escaper": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", @@ -13458,6 +13467,11 @@ "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", "dev": true }, + "hpagent": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/hpagent/-/hpagent-1.0.0.tgz", + "integrity": "sha512-SCleE2Uc1bM752ymxg8QXYGW0TWtAV4ZW3TqH1aOnyi6T6YW2xadCcclm5qeVjvMvfQ2RKNtZxO7uVb9CTPt1A==" + }, "html-escaper": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", diff --git a/package.json b/package.json index 8b5bd14..19a0b8d 100644 --- a/package.json +++ b/package.json @@ -45,6 +45,7 @@ "homepage": "https://github.com/hashicorp/vault-action#readme", "dependencies": { "got": "^11.8.5", + "hpagent": "^1.0.0", "jsonata": "^1.8.6", "jsrsasign": "^10.5.25" }, diff --git a/src/action.js b/src/action.js index b52bba3..9d330de 100644 --- a/src/action.js +++ b/src/action.js @@ -2,6 +2,7 @@ const core = require('@actions/core'); const command = require('@actions/core/lib/command'); const got = require('got').default; +const { HttpProxyAgent, HttpsProxyAgent } = require('hpagent'); const jsonata = require('jsonata'); const { auth: { retrieveToken }, secrets: { getSecrets } } = require('./index'); @@ -27,6 +28,7 @@ async function exportSecrets() { prefixUrl: vaultUrl, headers: {}, https: {}, + agent: {}, retry: { statusCodes: [ ...got.defaults.options.retry.statusCodes, @@ -37,6 +39,26 @@ async function exportSecrets() { } } + if (process.env['http_proxy'] || process.env['HTTP_PROXY']) { + defaultOptions.agent.http = new HttpProxyAgent({ + keepAlive: true, + keepAliveMsecs: 1000, + maxSockets: 256, + maxFreeSockets: 256, + proxy: process.env['http_proxy'] || process.env['HTTP_PROXY'] + }) + } + + if (process.env['https_proxy'] || process.env['HTTPS_PROXY']) { + defaultOptions.agent.https = new HttpsProxyAgent({ + keepAlive: true, + keepAliveMsecs: 1000, + maxSockets: 256, + maxFreeSockets: 256, + proxy: process.env['https_proxy'] || process.env['HTTPS_PROXY'] + }) + } + const tlsSkipVerify = (core.getInput('tlsSkipVerify', { required: false }) || 'false').toLowerCase() != 'false'; if (tlsSkipVerify === true) { defaultOptions.https.rejectUnauthorized = false;