diff --git a/docs/usage/configuration-options.md b/docs/usage/configuration-options.md index b0fbe64344c200d2b8a49006aed4f615aee17b0b..695b889a168509e597456372c7b6c93969876121 100644 --- a/docs/usage/configuration-options.md +++ b/docs/usage/configuration-options.md @@ -1556,6 +1556,24 @@ To adjust it down to 10s for all queries, do this: } ``` +### httpsCertificateAuthority + +By default, Renovate uses the curated list of well-known [CA](https://en.wikipedia.org/wiki/Certificate_authority)s by Mozilla. +You may use another Certificate Authority instead, by setting it in the `httpsCertificateAuthority` config option. + +### httpsPrivateKey + +Specifies the private key in [PEM format](https://en.wikipedia.org/wiki/Privacy-Enhanced_Mail) for mTLS authentication. + +<!-- prettier-ignore --> +!!! warning + Do _not_ put your private key into this field, to avoid losing confidentiality completely. + You must use [secrets](https://docs.renovatebot.com/self-hosted-configuration/#secrets) to pass it down securely instead. + +### httpsCertificate + +Specifies the [Certificate chains](https://en.wikipedia.org/wiki/X.509#Certificate_chains_and_cross-certification) in [PEM format](https://en.wikipedia.org/wiki/Privacy-Enhanced_Mail) for mTLS authentication. + ## ignoreDeprecated By default, Renovate won't update a dependency version to a deprecated release unless the current version was _itself_ deprecated. diff --git a/lib/config/options/index.ts b/lib/config/options/index.ts index d7ae7c55ef92f5a95b92b0062304f9ecdb4e0fa6..a3cdb0c41f62440a4e0ddf6ea0eb9e7f0babe0fa 100644 --- a/lib/config/options/index.ts +++ b/lib/config/options/index.ts @@ -2348,6 +2348,36 @@ const options: RenovateOptions[] = [ cli: false, env: false, }, + { + name: 'httpsCertificateAuthority', + description: 'The overriding trusted CA certificate.', + type: 'string', + stage: 'repository', + parent: 'hostRules', + default: null, + cli: false, + env: false, + }, + { + name: 'httpsPrivateKey', + description: 'The private key in PEM format.', + type: 'string', + stage: 'repository', + parent: 'hostRules', + default: null, + cli: false, + env: false, + }, + { + name: 'httpsCertificate', + description: 'The certificate chains in PEM format.', + type: 'string', + stage: 'repository', + parent: 'hostRules', + default: null, + cli: false, + env: false, + }, { name: 'cacheHardTtlMinutes', description: diff --git a/lib/types/host-rules.ts b/lib/types/host-rules.ts index ac02a813c38a0bde06588ca1985272ea89df55fd..058ed21e1e6d306df4e353a6ccc4999e339e63f1 100644 --- a/lib/types/host-rules.ts +++ b/lib/types/host-rules.ts @@ -15,6 +15,9 @@ export interface HostRuleSearchResult { dnsCache?: boolean; keepalive?: boolean; artifactAuth?: string[] | null; + httpsCertificateAuthority?: string; + httpsPrivateKey?: string; + httpsCertificate?: string; } export interface HostRule extends HostRuleSearchResult { diff --git a/lib/util/http/host-rules.spec.ts b/lib/util/http/host-rules.spec.ts index aeff4e65d113930a59ad4de1b8aaa47574f2e732..6ad1f655b5a02ce5e7e2c45a1fcf2f36c95b4aa8 100644 --- a/lib/util/http/host-rules.spec.ts +++ b/lib/util/http/host-rules.spec.ts @@ -148,6 +148,71 @@ describe('util/http/host-rules', () => { `); }); + it('certificateAuthority', () => { + hostRules.add({ + hostType: 'maven', + matchHost: 'https://custom.datasource.ca', + httpsCertificateAuthority: 'ca-cert', + }); + + expect( + applyHostRules('https://custom.datasource.ca/data/path', { + ...options, + hostType: 'maven', + }) + ).toMatchInlineSnapshot(` + { + "hostType": "maven", + "https": { + "certificateAuthority": "ca-cert", + }, + } + `); + }); + + it('privateKey', () => { + hostRules.add({ + hostType: 'maven', + matchHost: 'https://custom.datasource.key', + httpsPrivateKey: 'key', + }); + expect( + applyHostRules('https://custom.datasource.key/data/path', { + ...options, + hostType: 'maven', + }) + ).toMatchInlineSnapshot(` + { + "hostType": "maven", + "https": { + "key": "key", + }, + } + `); + }); + + it('certificate', () => { + hostRules.add({ + hostType: 'maven', + matchHost: 'https://custom.datasource.cert', + httpsCertificate: 'cert', + }); + + expect( + applyHostRules('https://custom.datasource.cert/data/path', { + ...options, + hostType: 'maven', + }) + ).toMatchInlineSnapshot(` + { + "hostType": "maven", + "https": { + "certificate": "cert", + }, + } + `); + }); + it('no fallback to github', () => { hostRules.add({ hostType: 'github-tags', diff --git a/lib/util/http/host-rules.ts b/lib/util/http/host-rules.ts index bf88e5e888ccccc218451841e1f7cf9c145a30c3..577afb5976e667614247d6d86e2ea52f191f9f1f 100644 --- a/lib/util/http/host-rules.ts +++ b/lib/util/http/host-rules.ts @@ -30,6 +30,7 @@ export type HostRulesGotOptions = Pick< | 'lookup' | 'agent' | 'http2' + | 'https' >; export function findMatchingRules<GotOptions extends HostRulesGotOptions>( @@ -162,6 +163,28 @@ export function applyHostRules<GotOptions extends HostRulesGotOptions>( if (!hasProxy() && foundRules.enableHttp2 === true) { options.http2 = true; } + + if (is.nonEmptyString(foundRules.httpsCertificateAuthority)) { + options.https = { + ...(options.https ?? {}), + certificateAuthority: foundRules.httpsCertificateAuthority, + }; + } + + if (is.nonEmptyString(foundRules.httpsPrivateKey)) { + options.https = { + ...(options.https ?? {}), + key: foundRules.httpsPrivateKey, + }; + } + + if (is.nonEmptyString(foundRules.httpsCertificate)) { + options.https = { + ...(options.https ?? {}), + certificate: foundRules.httpsCertificate, + }; + } + return options; }