feat: add no-proxy support (#1482)

This commit is contained in:
Tom Keller
2025-09-09 16:23:05 -07:00
committed by GitHub
parent 5ebd15afc6
commit dde9b22a8e
10 changed files with 635 additions and 117 deletions
+10 -3
View File
@@ -2,14 +2,16 @@ import { info } from '@actions/core';
import { STSClient } from '@aws-sdk/client-sts';
import type { AwsCredentialIdentity } from '@aws-sdk/types';
import { NodeHttpHandler } from '@smithy/node-http-handler';
import { HttpsProxyAgent } from 'https-proxy-agent';
import { ProxyAgent } from 'proxy-agent';
import { errorMessage, getCallerIdentity } from './helpers';
import { ProxyResolver } from './ProxyResolver';
const USER_AGENT = 'configure-aws-credentials-for-github-actions';
export interface CredentialsClientProps {
region?: string;
proxyServer?: string;
noProxy?: string;
}
export class CredentialsClient {
@@ -21,10 +23,15 @@ export class CredentialsClient {
this.region = props.region;
if (props.proxyServer) {
info('Configuring proxy handler for STS client');
const handler = new HttpsProxyAgent(props.proxyServer);
const getProxyForUrl = new ProxyResolver({
httpProxy: props.proxyServer,
httpsProxy: props.proxyServer,
noProxy: props.noProxy,
}).getProxyForUrl;
const handler = new ProxyAgent({ getProxyForUrl });
this.requestHandler = new NodeHttpHandler({
httpAgent: handler,
httpsAgent: handler,
httpAgent: handler,
});
}
}
+70
View File
@@ -0,0 +1,70 @@
// Based on https://github.com/Rob--W/proxy-from-env/tree/caf8c32301afdac8b5feaf346028bd8240690144
// See https://github.com/Rob--W/proxy-from-env/blob/caf8c32301afdac8b5feaf346028bd8240690144/LICENSE
import type * as http from 'node:http';
const DEFAULT_PORTS: Record<string, number> = {
http: 80,
https: 443,
};
export interface ProxyOptions {
readonly noProxy?: string;
readonly httpsProxy?: string;
readonly httpProxy?: string;
}
export class ProxyResolver {
options: ProxyOptions;
constructor(options: ProxyOptions) {
this.options = options;
}
getProxyForUrl(url: string, _req: http.ClientRequest): string {
return this.getProxyForUrlOptions(url, this.options);
}
private getProxyForUrlOptions(url: string | URL, options?: ProxyOptions): string {
let parsedUrl: URL;
try {
parsedUrl = typeof url === 'string' ? new URL(url) : url;
} catch (_) {
return ''; // Don't proxy invalid URLs.
}
const proto = parsedUrl.protocol.split(':', 1)[0];
if (!proto) return ''; // Don't proxy URLs without a protocol.
const hostname = parsedUrl.host;
const port = parseInt(parsedUrl.port || '') || DEFAULT_PORTS[proto] || 0;
if (options?.noProxy && !this.shouldProxy(hostname, port, options.noProxy)) return '';
if (proto === 'http' && options?.httpProxy) return options.httpProxy;
if (proto === 'https' && options?.httpsProxy) return options.httpsProxy;
return ''; // No proxy configured for this protocol or unknown protocol
}
private shouldProxy(hostname: string, port: number, noProxy: string): boolean {
if (!noProxy) return true;
if (noProxy === '*') return false; // Never proxy if wildcard is set.
return noProxy.split(/[,\s]/).every((proxy) => {
if (!proxy) return true; // Skip zero-length hosts.
const parsedProxy = proxy.match(/^(.+):(\d+)$/);
const parsedProxyHostname = parsedProxy ? parsedProxy[1] : proxy;
const parsedProxyPort = parsedProxy?.[2] ? parseInt(parsedProxy[2]) : 0;
if (parsedProxyPort && parsedProxyPort !== port) return true; // Skip if ports don't match.
if (parsedProxyHostname && !/^[.*]/.test(parsedProxyHostname)) {
// No wildcards, so stop proxying if there is an exact match.
return hostname !== parsedProxyHostname;
}
let cleanProxyHostname = parsedProxyHostname;
if (parsedProxyHostname && parsedProxyHostname.charAt(0) === '*') {
// Remove leading wildcard.
cleanProxyHostname = parsedProxyHostname.slice(1);
}
// Stop proxying if the hostname ends with the no_proxy host.
return !cleanProxyHostname || !hostname.endsWith(cleanProxyHostname);
});
}
}
+1
View File
@@ -28,6 +28,7 @@ export function translateEnvVariables() {
'RETRY_MAX_ATTEMPTS',
'SPECIAL_CHARACTERS_WORKAROUND',
'USE_EXISTING_CREDENTIALS',
'NO_PROXY',
];
// Treat HTTPS_PROXY as HTTP_PROXY. Precedence is HTTPS_PROXY > HTTP_PROXY
if (process.env.HTTPS_PROXY) process.env.HTTP_PROXY = process.env.HTTPS_PROXY;
+2 -1
View File
@@ -56,6 +56,7 @@ export async function run() {
.split(',')
.map((s) => s.trim());
const forceSkipOidc = getBooleanInput('force-skip-oidc', { required: false });
const noProxy = core.getInput('no-proxy', { required: false });
if (forceSkipOidc && roleToAssume && !AccessKeyId && !webIdentityTokenFile) {
throw new Error(
@@ -109,7 +110,7 @@ export async function run() {
exportRegion(region, outputEnvCredentials);
// Instantiate credentials client
const credentialsClient = new CredentialsClient({ region, proxyServer });
const credentialsClient = new CredentialsClient({ region, proxyServer, noProxy });
let sourceAccountId: string;
let webIdentityToken: string;