gitlab: RequestError: self-signed certificate
Since last weekend I get an error due to self-signed certificate. The setup that did the same job until now was:
docker image: node:18
export GIT_SSL_NO_VERIFY=1
export NODE_TLS_REJECT_UNAUTHORIZED=0
npm install @semantic-release/gitlab @semantic-release/exec @semantic-release/changelog @semantic-release/git
npx semantic-release --generate-notes false --dry-run
ERROR:
$ npx semantic-release --generate-notes false --dry-run
[10:19:28 AM] [semantic-release] › ℹ Running semantic-release version 20.1.0
(node:47) ExperimentalWarning: Importing JSON modules is an experimental feature and might change at any time
(Use `node --trace-warnings ...` to show where the warning was created)
[10:19:28 AM] [semantic-release] › ✔ Loaded plugin "verifyConditions" from "@semantic-release/changelog"
[10:19:28 AM] [semantic-release] › ✔ Loaded plugin "verifyConditions" from "@semantic-release/git"
[10:19:28 AM] [semantic-release] › ✔ Loaded plugin "verifyConditions" from "@semantic-release/gitlab"
[10:19:28 AM] [semantic-release] › ✔ Loaded plugin "verifyConditions" from "@semantic-release/exec"
[10:19:28 AM] [semantic-release] › ✔ Loaded plugin "analyzeCommits" from "@semantic-release/commit-analyzer"
[10:19:28 AM] [semantic-release] › ✔ Loaded plugin "analyzeCommits" from "@semantic-release/exec"
[10:19:28 AM] [semantic-release] › ✔ Loaded plugin "verifyRelease" from "@semantic-release/exec"
[10:19:28 AM] [semantic-release] › ✔ Loaded plugin "prepare" from "@semantic-release/changelog"
[10:19:28 AM] [semantic-release] › ✔ Loaded plugin "prepare" from "@semantic-release/git"
[10:19:28 AM] [semantic-release] › ✔ Loaded plugin "prepare" from "@semantic-release/exec"
[10:19:28 AM] [semantic-release] › ✔ Loaded plugin "publish" from "@semantic-release/gitlab"
[10:19:28 AM] [semantic-release] › ✔ Loaded plugin "publish" from "@semantic-release/exec"
[10:19:28 AM] [semantic-release] › ✔ Loaded plugin "addChannel" from "@semantic-release/exec"
[10:19:28 AM] [semantic-release] › ✔ Loaded plugin "success" from "@semantic-release/gitlab"
[10:19:28 AM] [semantic-release] › ✔ Loaded plugin "success" from "@semantic-release/exec"
[10:19:28 AM] [semantic-release] › ✔ Loaded plugin "fail" from "@semantic-release/gitlab"
[10:19:28 AM] [semantic-release] › ✔ Loaded plugin "fail" from "@semantic-release/exec"
[10:19:31 AM] [semantic-release] › ⚠ Run automated release from branch main on repository https://gitlab-ci-token:[secure]@SERVER/PATH_TO_REPO.git in dry-run mode
[10:19:32 AM] [semantic-release] › ✔ Allowed to push to the Git repository
[10:19:32 AM] [semantic-release] › ℹ Start step "verifyConditions" of plugin "@semantic-release/changelog"
[10:19:32 AM] [semantic-release] › ✔ Completed step "verifyConditions" of plugin "@semantic-release/changelog"
[10:19:32 AM] [semantic-release] › ℹ Start step "verifyConditions" of plugin "@semantic-release/git"
[10:19:32 AM] [semantic-release] › ✔ Completed step "verifyConditions" of plugin "@semantic-release/git"
[10:19:32 AM] [semantic-release] › ℹ Start step "verifyConditions" of plugin "@semantic-release/gitlab"
[10:19:32 AM] [semantic-release] [@semantic-release/gitlab] › ℹ Verify GitLab authentication (https://SERVER/api/v4)
(node:47) Warning: Setting the NODE_TLS_REJECT_UNAUTHORIZED environment variable to '0' makes TLS connections and HTTPS requests insecure by disabling certificate verification.
[10:19:32 AM] [semantic-release] › ✘ Failed step "verifyConditions" of plugin "@semantic-release/gitlab"
[10:19:32 AM] [semantic-release] › ℹ Start step "verifyConditions" of plugin "@semantic-release/exec"
[10:19:32 AM] [semantic-release] › ✔ Completed step "verifyConditions" of plugin "@semantic-release/exec"
[10:19:32 AM] [semantic-release] › ✘ An error occurred while running semantic-release: RequestError: self-signed certificate
at ClientRequest.<anonymous> (file:///builds/PATH_TO_REPO/node_modules/got/dist/source/core/index.js:789:107)
at Object.onceWrapper (node:events:628:26)
at ClientRequest.emit (node:events:525:35)
at TLSSocket.socketErrorListener (node:_http_client:496:9)
at TLSSocket.emit (node:events:513:28)
at emitErrorNT (node:internal/streams/destroy:151:8)
at emitErrorCloseNT (node:internal/streams/destroy:116:3)
at process.processTicksAndRejections (node:internal/process/task_queues:82:21)
at TLSSocket.onConnectSecure (node:_tls_wrap:1545:34)
at TLSSocket.emit (node:events:513:28)
at TLSSocket._finishInit (node:_tls_wrap:959:8)
at ssl.onhandshakedone (node:_tls_wrap:743:12) {
input: undefined,
code: 'DEPTH_ZERO_SELF_SIGNED_CERT',
timings: {
start: 1675073972164,
socket: 1675073972166,
lookup: 1675073972167,
connect: 1675073972168,
secureConnect: undefined,
upload: undefined,
response: undefined,
end: undefined,
error: 1675073972170,
abort: undefined,
phases: {
wait: 2,
dns: 1,
tcp: 1,
tls: undefined,
request: undefined,
firstByte: undefined,
download: undefined,
total: 6
}
},
options: {
request: undefined,
agent: { http: undefined, https: undefined, http2: undefined },
h2session: undefined,
decompress: true,
timeout: {
connect: undefined,
lookup: undefined,
read: undefined,
request: undefined,
response: undefined,
secureConnect: undefined,
send: undefined,
socket: undefined
},
prefixUrl: '',
body: undefined,
form: undefined,
json: undefined,
cookieJar: undefined,
ignoreInvalidCookies: false,
searchParams: undefined,
dnsLookup: undefined,
dnsCache: undefined,
context: {},
hooks: {
init: [],
beforeRequest: [],
beforeError: [],
beforeRedirect: [],
beforeRetry: [],
afterResponse: []
},
followRedirect: true,
maxRedirects: 10,
cache: undefined,
throwHttpErrors: true,
username: '',
password: '',
http2: false,
allowGetBody: false,
headers: {
'user-agent': 'got (https://github.com/sindresorhus/got)',
'private-token': '[secure]',
accept: 'application/json',
'accept-encoding': 'gzip, deflate, br'
},
methodRewriting: false,
dnsLookupIpVersion: undefined,
parseJson: [Function: parse],
stringifyJson: [Function: stringify],
retry: {
limit: 2,
methods: [ 'GET', 'PUT', 'HEAD', 'DELETE', 'OPTIONS', 'TRACE' ],
statusCodes: [
408, 413, 429, 500,
502, 503, 504, 521,
522, 524
],
errorCodes: [
'ETIMEDOUT',
'ECONNRESET',
'EADDRINUSE',
'ECONNREFUSED',
'EPIPE',
'ENOTFOUND',
'ENETUNREACH',
'EAI_AGAIN'
],
maxRetryAfter: undefined,
calculateDelay: [Function: calculateDelay],
backoffLimit: Infinity,
noise: 100
},
localAddress: undefined,
method: 'GET',
createConnection: undefined,
cacheOptions: {
shared: undefined,
cacheHeuristic: undefined,
immutableMinTimeToLive: undefined,
ignoreCargoCult: undefined
},
https: {
alpnProtocols: undefined,
rejectUnauthorized: undefined,
checkServerIdentity: undefined,
certificateAuthority: undefined,
key: undefined,
certificate: undefined,
passphrase: undefined,
pfx: undefined,
ciphers: undefined,
honorCipherOrder: undefined,
minVersion: undefined,
maxVersion: undefined,
signatureAlgorithms: undefined,
tlsSessionLifetime: undefined,
dhparam: undefined,
ecdhCurve: undefined,
certificateRevocationLists: undefined
},
encoding: undefined,
resolveBodyOnly: false,
isStream: false,
responseType: 'text',
url: URL {
href: 'https://SERVER/api/v4/projects/PATH%2FTO%2FREPO',
origin: 'https://SERVER',
protocol: 'https:',
username: '',
password: '',
host: 'SERVER',
hostname: 'SERVER',
port: '',
pathname: '/api/v4/projects/PATH%2FTO%2FREPO',
search: '',
searchParams: URLSearchParams {},
hash: ''
},
pagination: {
transform: [Function: transform],
paginate: [Function: paginate],
filter: [Function: filter],
shouldContinue: [Function: shouldContinue],
countLimit: Infinity,
backoff: 0,
requestLimit: 10000,
stackAllItems: false
},
setHost: true,
maxHeaderSize: undefined,
signal: undefined,
enableUnixSockets: true
},
pluginName: '@semantic-release/gitlab'
}
AggregateError:
RequestError: self-signed certificate
at ClientRequest.<anonymous> (file:///builds/PATH_TO_REPO/node_modules/got/dist/source/core/index.js:789:107)
at file:///builds/PATH_TO_REPO/node_modules/semantic-release/lib/plugins/pipeline.js:54:11
at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
at async pluginsConfigAccumulator.<computed> [as verifyConditions] (file:///builds/PATH_TO_REPO/node_modules/semantic-release/lib/plugins/index.js:87:11)
at async run (file:///builds/PATH_TO_REPO/node_modules/semantic-release/index.js:106:3)
at async Module.default (file:///builds/PATH_TO_REPO/node_modules/semantic-release/index.js:275:22)
at async default (file:///builds/PATH_TO_REPO/node_modules/semantic-release/cli.js:55:5) {
errors: [
RequestError: self-signed certificate
at ClientRequest.<anonymous> (file:///builds/PATH_TO_REPO/node_modules/got/dist/source/core/index.js:789:107)
at Object.onceWrapper (node:events:628:26)
at ClientRequest.emit (node:events:525:35)
at TLSSocket.socketErrorListener (node:_http_client:496:9)
at TLSSocket.emit (node:events:513:28)
at emitErrorNT (node:internal/streams/destroy:151:8)
at emitErrorCloseNT (node:internal/streams/destroy:116:3)
at process.processTicksAndRejections (node:internal/process/task_queues:82:21)
at TLSSocket.onConnectSecure (node:_tls_wrap:1545:34)
at TLSSocket.emit (node:events:513:28)
at TLSSocket._finishInit (node:_tls_wrap:959:8)
at ssl.onhandshakedone (node:_tls_wrap:743:12) {
input: undefined,
code: 'DEPTH_ZERO_SELF_SIGNED_CERT',
timings: [Object],
options: {
request: undefined,
agent: { http: undefined, https: undefined, http2: undefined },
h2session: undefined,
decompress: true,
timeout: {
connect: undefined,
lookup: undefined,
read: undefined,
request: undefined,
response: undefined,
secureConnect: undefined,
send: undefined,
socket: undefined
},
prefixUrl: '',
body: undefined,
form: undefined,
json: undefined,
cookieJar: undefined,
ignoreInvalidCookies: false,
searchParams: undefined,
dnsLookup: undefined,
dnsCache: undefined,
context: {},
hooks: {
init: [],
beforeRequest: [],
beforeError: [],
beforeRedirect: [],
beforeRetry: [],
afterResponse: []
},
followRedirect: true,
maxRedirects: 10,
cache: undefined,
throwHttpErrors: true,
username: '',
password: '',
http2: false,
allowGetBody: false,
headers: {
'user-agent': 'got (https://github.com/sindresorhus/got)',
'private-token': '[secure]',
accept: 'application/json',
'accept-encoding': 'gzip, deflate, br'
},
methodRewriting: false,
dnsLookupIpVersion: undefined,
parseJson: [Function: parse],
stringifyJson: [Function: stringify],
retry: {
limit: 2,
methods: [ 'GET', 'PUT', 'HEAD', 'DELETE', 'OPTIONS', 'TRACE' ],
statusCodes: [
408, 413, 429, 500,
502, 503, 504, 521,
522, 524
],
errorCodes: [
'ETIMEDOUT',
'ECONNRESET',
'EADDRINUSE',
'ECONNREFUSED',
'EPIPE',
'ENOTFOUND',
'ENETUNREACH',
'EAI_AGAIN'
],
maxRetryAfter: undefined,
calculateDelay: [Function: calculateDelay],
backoffLimit: Infinity,
noise: 100
},
localAddress: undefined,
method: 'GET',
createConnection: undefined,
cacheOptions: {
shared: undefined,
cacheHeuristic: undefined,
immutableMinTimeToLive: undefined,
ignoreCargoCult: undefined
},
https: {
alpnProtocols: undefined,
rejectUnauthorized: undefined,
checkServerIdentity: undefined,
certificateAuthority: undefined,
key: undefined,
certificate: undefined,
passphrase: undefined,
pfx: undefined,
ciphers: undefined,
honorCipherOrder: undefined,
minVersion: undefined,
maxVersion: undefined,
signatureAlgorithms: undefined,
tlsSessionLifetime: undefined,
dhparam: undefined,
ecdhCurve: undefined,
certificateRevocationLists: undefined
},
encoding: undefined,
resolveBodyOnly: false,
isStream: false,
responseType: 'text',
url: URL {
href: 'https://SERVER/api/v4/projects/PATH%2FTO%2FREPO',
origin: 'https://SERVER',
protocol: 'https:',
username: '',
password: '',
host: 'SERVER',
hostname: 'SERVER',
port: '',
pathname: '/api/v4/projects/PATH%2FTO%2FREPO',
search: '',
searchParams: URLSearchParams {},
hash: ''
},
pagination: {
transform: [Function: transform],
paginate: [Function: paginate],
filter: [Function: filter],
shouldContinue: [Function: shouldContinue],
countLimit: Infinity,
backoff: 0,
requestLimit: 10000,
stackAllItems: false
},
setHost: true,
maxHeaderSize: undefined,
signal: undefined,
enableUnixSockets: true
},
pluginName: '@semantic-release/gitlab'
}
]
}
Expected behavior (until last week):
$ npx semantic-release --generate-notes false --dry-run
[2:03:42 AM] [semantic-release] › ℹ Running semantic-release version 20.1.0
[2:03:42 AM] [semantic-release] › ✔ Loaded plugin "verifyConditions" from "@semantic-release/changelog"
[2:03:42 AM] [semantic-release] › ✔ Loaded plugin "verifyConditions" from "@semantic-release/git"
[2:03:42 AM] [semantic-release] › ✔ Loaded plugin "verifyConditions" from "@semantic-release/gitlab"
[2:03:42 AM] [semantic-release] › ✔ Loaded plugin "verifyConditions" from "@semantic-release/exec"
[2:03:42 AM] [semantic-release] › ✔ Loaded plugin "analyzeCommits" from "@semantic-release/commit-analyzer"
[2:03:42 AM] [semantic-release] › ✔ Loaded plugin "analyzeCommits" from "@semantic-release/exec"
[2:03:42 AM] [semantic-release] › ✔ Loaded plugin "verifyRelease" from "@semantic-release/exec"
[2:03:42 AM] [semantic-release] › ✔ Loaded plugin "prepare" from "@semantic-release/changelog"
[2:03:42 AM] [semantic-release] › ✔ Loaded plugin "prepare" from "@semantic-release/git"
[2:03:42 AM] [semantic-release] › ✔ Loaded plugin "prepare" from "@semantic-release/exec"
[2:03:42 AM] [semantic-release] › ✔ Loaded plugin "publish" from "@semantic-release/gitlab"
[2:03:42 AM] [semantic-release] › ✔ Loaded plugin "publish" from "@semantic-release/exec"
[2:03:42 AM] [semantic-release] › ✔ Loaded plugin "addChannel" from "@semantic-release/exec"
[2:03:42 AM] [semantic-release] › ✔ Loaded plugin "success" from "@semantic-release/gitlab"
[2:03:42 AM] [semantic-release] › ✔ Loaded plugin "success" from "@semantic-release/exec"
[2:03:42 AM] [semantic-release] › ✔ Loaded plugin "fail" from "@semantic-release/gitlab"
[2:03:42 AM] [semantic-release] › ✔ Loaded plugin "fail" from "@semantic-release/exec"
[2:03:46 AM] [semantic-release] › ⚠ Run automated release from branch main on repository https://gitlab-ci-token:[secure]@SERVER/PATH_TO_REPO.git in dry-run mode
[2:03:47 AM] [semantic-release] › ✔ Allowed to push to the Git repository
[2:03:47 AM] [semantic-release] › ℹ Start step "verifyConditions" of plugin "@semantic-release/changelog"
[2:03:47 AM] [semantic-release] › ✔ Completed step "verifyConditions" of plugin "@semantic-release/changelog"
[2:03:47 AM] [semantic-release] › ℹ Start step "verifyConditions" of plugin "@semantic-release/git"
[2:03:47 AM] [semantic-release] › ✔ Completed step "verifyConditions" of plugin "@semantic-release/git"
[2:03:47 AM] [semantic-release] › ℹ Start step "verifyConditions" of plugin "@semantic-release/gitlab"
[2:03:47 AM] [semantic-release] [@semantic-release/gitlab] › ℹ Verify GitLab authentication (https://SERVER/api/v4)
(node:37) Warning: Setting the NODE_TLS_REJECT_UNAUTHORIZED environment variable to '0' makes TLS connections and HTTPS requests insecure by disabling certificate verification.
(Use `node --trace-warnings ...` to show where the warning was created)
[2:03:47 AM] [semantic-release] › ✔ Completed step "verifyConditions" of plugin "@semantic-release/gitlab"
About this issue
- Original URL
- State: closed
- Created a year ago
- Comments: 26 (11 by maintainers)
And for now I completely understand. NodeJS uses a built-in set of certificates: https://github.com/nodejs/node/blob/main/src/node_root_certs.h Because of this, addition to system list of CAs does not have effect for
got
.And I found solution! Instead of
NODE_EXTRA_CA_CERTS=/etc/ssl/certs/ca-certificates.crt
we can use more obviousNODE_OPTIONS=--use-openssl-ca
.--use-openssl-ca
- This setting forces the application to use the system’s list of CAs. Thats all we need.having same issue. Probably comes from here: https://github.com/semantic-release/gitlab/commit/ffa17a8dc16eb8b432e72935c1ac76ed1bfcfd8f
the only solution that I found was to downgrade the plugin version( npm install @semantic-release/gitlab@v10.0.1)
Another way without completely disabling security.
OS: Debian 11 (node:19 docker image)
If you have own CA and certificates signed with it.
Add CA to system trusted certs inside
/usr/local/share/ca-certificates/
, for example/usr/local/share/ca-certificates/MyCA.crt
If you have selfsigned certificates.
Add selfsigned cert:
And then
update-ca-certificates
curl
. Don’t know why, but not enough forgot
.NODE_EXTRA_CA_CERTS=/etc/ssl/certs/ca-certificates.crt
variable to env, and now all is workingThis is really strange, why got not use system list of trusted certs. If any body know, please tell me =)
@geigervlad @jagiella @artem-shestakov The previous behavior was restored in https://github.com/semantic-release/gitlab/releases/tag/v10.1.4.
I’ll leave this issue open until it’s clear how we solve this long-term.
Downgrading did the trick! Thanks!
But the whole way of using workarounds for dealing with self-signed certificates remains highly unsatisfying in general… An official mechanism, option etc. to enable self-signed certificates would be highly appreciated.
I even tried adding the server certificate to the trusted list:
That works on my maschine 👍 , but still fails if run in a docker container 👎.
I’m pretty sure I went as far back as 10.0.0 and it didn’t work. The error output was obviously different but when I dug down it pointed at my tls certificate not being valid. I’ll try again soon and let you know. Thank you for taking the time to look into this for me.
edit: Yep. It did indeed work. I had problems with my token itself that didn’t have API permissions. Thank you!
Hi Everyone, thanks for all the great info above.
From what I understand, this issue has been resolved and the current consesus is to either downgrade to semantic-release/gitlab v10.1.4 so that setting
NODE_TLS_REJECT_UNAUTHORIZED=0
will work, or useNODE_OPTIONS=--use-openssl-ca
along with what was suggested here https://github.com/semantic-release/gitlab/issues/489#issuecomment-1481225666 to get it to trust a self signed certificate? I’ve been trying to get this working on an Alpine distro with v21.5.0 of nodejs but I had absolutely no luck with trusting the self signed certificate. Although, downgrading to v10.1.4 and settingNODE_TLS_REJECT_UNAUTHORIZED=0
worked like a charm.I thought I would just leave this here for what it is worth, in case this is an Alpine specific issue and it can save someone with a similar setup to mine some time.
Hey, thanks for this discussion. I’m commenting in case someone else finds this w/ the same problem I had.
I had the node environment variable set, but now obviously not working. I should have not needed it… it turns out that when you send a cert through nginx it must be the full chain cert
If it does not come in this order then
got
will fail.It was as simple as adding the cert chain into the end my gitlab cert file (
/etc/gitlab/ssl/git.domain.com.crt
) and then restarting nginx withsudo gitlab-ctl restart nginx
You can verify it after restarting nginx with
echo | /opt/gitlab/embedded/bin/openssl s_client -connect git.domain.com:443
i would say that it isn’t necessary because this isn’t really a change to this plugin directly and isn’t actually an officially supported use case. that said, i think there is value in drawing attention to the fact that it is known that this will impact some consumers and this is an opportunity to encourage them to fix the gap in their supply chain security. there is also value in having a major bump to allow consumers to continue using an old version that continues to use the old got version without risk of an in-range version causing that to change. in short, i’m supportive of the change being treated as breaking.
i think it would be valuable to link to this thread from the release notes (even if it is a modification after the release) for additional context. i would also recommend calling out that it is an option for folks to use an older version if they choose to ignore the security implications of using a self-signed cert without taking the steps to handle it in a trust verifying way.
In container same issue. Adding certs by
or using
NODE_TLS_REJECT_UNAUTHORIZED: 0
dose not work.@semantic-release/gitlab@v10.0.1 works fine !!! Thanks @geigervlad