Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RequestError: self-signed certificate #489

Closed
jagiella opened this issue Jan 30, 2023 · 26 comments
Closed

RequestError: self-signed certificate #489

jagiella opened this issue Jan 30, 2023 · 26 comments

Comments

@jagiella
Copy link

jagiella commented Jan 30, 2023

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:28AM] [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:28AM] [semantic-release] › ✔  Loaded plugin "verifyConditions" from "@semantic-release/changelog"
[10:19:28AM] [semantic-release] › ✔  Loaded plugin "verifyConditions" from "@semantic-release/git"
[10:19:28AM] [semantic-release] › ✔  Loaded plugin "verifyConditions" from "@semantic-release/gitlab"
[10:19:28AM] [semantic-release] › ✔  Loaded plugin "verifyConditions" from "@semantic-release/exec"
[10:19:28AM] [semantic-release] › ✔  Loaded plugin "analyzeCommits" from "@semantic-release/commit-analyzer"
[10:19:28AM] [semantic-release] › ✔  Loaded plugin "analyzeCommits" from "@semantic-release/exec"
[10:19:28AM] [semantic-release] › ✔  Loaded plugin "verifyRelease" from "@semantic-release/exec"
[10:19:28AM] [semantic-release] › ✔  Loaded plugin "prepare" from "@semantic-release/changelog"
[10:19:28AM] [semantic-release] › ✔  Loaded plugin "prepare" from "@semantic-release/git"
[10:19:28AM] [semantic-release] › ✔  Loaded plugin "prepare" from "@semantic-release/exec"
[10:19:28AM] [semantic-release] › ✔  Loaded plugin "publish" from "@semantic-release/gitlab"
[10:19:28AM] [semantic-release] › ✔  Loaded plugin "publish" from "@semantic-release/exec"
[10:19:28AM] [semantic-release] › ✔  Loaded plugin "addChannel" from "@semantic-release/exec"
[10:19:28AM] [semantic-release] › ✔  Loaded plugin "success" from "@semantic-release/gitlab"
[10:19:28AM] [semantic-release] › ✔  Loaded plugin "success" from "@semantic-release/exec"
[10:19:28AM] [semantic-release] › ✔  Loaded plugin "fail" from "@semantic-release/gitlab"
[10:19:28AM] [semantic-release] › ✔  Loaded plugin "fail" from "@semantic-release/exec"
[10:19:31AM] [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:32AM] [semantic-release] › ✔  Allowed to push to the Git repository
[10:19:32AM] [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:42AM] [semantic-release] ›   Running semantic-release version 20.1.0
[2:03:42AM] [semantic-release] › ✔  Loaded plugin "verifyConditions" from "@semantic-release/changelog"
[2:03:42AM] [semantic-release] › ✔  Loaded plugin "verifyConditions" from "@semantic-release/git"
[2:03:42AM] [semantic-release] › ✔  Loaded plugin "verifyConditions" from "@semantic-release/gitlab"
[2:03:42AM] [semantic-release] › ✔  Loaded plugin "verifyConditions" from "@semantic-release/exec"
[2:03:42AM] [semantic-release] › ✔  Loaded plugin "analyzeCommits" from "@semantic-release/commit-analyzer"
[2:03:42AM] [semantic-release] › ✔  Loaded plugin "analyzeCommits" from "@semantic-release/exec"
[2:03:42AM] [semantic-release] › ✔  Loaded plugin "verifyRelease" from "@semantic-release/exec"
[2:03:42AM] [semantic-release] › ✔  Loaded plugin "prepare" from "@semantic-release/changelog"
[2:03:42AM] [semantic-release] › ✔  Loaded plugin "prepare" from "@semantic-release/git"
[2:03:42AM] [semantic-release] › ✔  Loaded plugin "prepare" from "@semantic-release/exec"
[2:03:42AM] [semantic-release] › ✔  Loaded plugin "publish" from "@semantic-release/gitlab"
[2:03:42AM] [semantic-release] › ✔  Loaded plugin "publish" from "@semantic-release/exec"
[2:03:42AM] [semantic-release] › ✔  Loaded plugin "addChannel" from "@semantic-release/exec"
[2:03:42AM] [semantic-release] › ✔  Loaded plugin "success" from "@semantic-release/gitlab"
[2:03:42AM] [semantic-release] › ✔  Loaded plugin "success" from "@semantic-release/exec"
[2:03:42AM] [semantic-release] › ✔  Loaded plugin "fail" from "@semantic-release/gitlab"
[2:03:42AM] [semantic-release] › ✔  Loaded plugin "fail" from "@semantic-release/exec"
[2:03:46AM] [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:47AM] [semantic-release] › ✔  Allowed to push to the Git repository
[2:03:47AM] [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"
@geigervlad
Copy link

having same issue. Probably comes from here: ffa17a8

the only solution that I found was to downgrade the plugin version( npm install @semantic-release/gitlab@v10.0.1)

@geigervlad
Copy link

some more details here: sindresorhus/got#2149

@jagiella
Copy link
Author

having same issue. Probably comes from here: ffa17a8

the only solution that I found was to downgrade the plugin version( npm install @semantic-release/gitlab@v10.0.1)

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:

echo -n | openssl s_client -connect $SERVERNAME:443 -servername $SERVERNAME | openssl x509 | sudo tee /usr/local/share/ca-certificates/$SERVERNAME.crt
sudo update-ca-certificates

That works on my maschine 👍 , but still fails if run in a docker container 👎.

@fgreinacher
Copy link
Contributor

Thanks for bringing this up and sorry for the hassle. I reached out to the got maintainers. If they don't want to reconsider we can add a configuration option here.

/cc @travi @gr2m This might be relevant for other plugins.

@artem-shestakov
Copy link

artem-shestakov commented Feb 2, 2023

In container same issue. Adding certs by

echo | openssl s_client -servername <servername> -connect <servername>:443 |  sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > /usr/local/share/ca-certificates/<cert_name>.crt
update-ca-certificates   

or using NODE_TLS_REJECT_UNAUTHORIZED: 0 dose not work.

Verify GitLab authentication (https://....sys.local/api/v4)

(node:5186) Warning: Setting the NODE_TLS_REJECT_UNAUTHORIZED environment variable to '0' makes TLS connections and HTTPS requests insecure by disabling certificate verification.
[8:43:55 AM] [semantic-release] › ✘  Failed step "verifyConditions" of plugin "@semantic-release/gitlab"
[8:43:55 AM] [semantic-release] › ✘  An error occurred while running semantic-release: RequestError: unable to verify the first certificate
...
RequestError: unable to verify the first certificate
        at ClientRequest.<anonymous> (file:///fluent_bit/node_modules/got/dist/source/core/index.js:789:107)
        at Object.onceWrapper (node:events:627:26)
        at ClientRequest.emit (node:events:524:35)
        at TLSSocket.socketErrorListener (node:_http_client:496:9)
        at TLSSocket.emit (node:events:512: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:1540:34)
        at TLSSocket.emit (node:events:512:28)
        at TLSSocket._finishInit (node:_tls_wrap:959:8)
        at ssl.onhandshakedone (node:_tls_wrap:743:12) {
      input: undefined,
      code: 'UNABLE_TO_VERIFY_LEAF_SIGNATURE',

@semantic-release/gitlab@v10.0.1 works fine !!! Thanks @geigervlad

@travi
Copy link
Member

travi commented Feb 3, 2023

If they don't want to reconsider we can add a configuration option here.

i have a really hard time getting behind adding an option to disable verification of tls certificates. part of the decision to use a self-signed certificate is taking on the extra complexity of configuring systems to trust that certificate. i recognize that there used to be a way around this by directly telling node to skip verification, and i wouldnt expect us to take steps to prevent that. however, i think us defining an explicit option would encourage more folks to do this and suggests that we support avoiding security measures.

Warning: Setting the NODE_TLS_REJECT_UNAUTHORIZED environment variable to '0' makes TLS connections and HTTPS requests insecure by disabling certificate verification.

there is a reason that this warning is provided. i dont think we should be encouraging folks to ignore this.

@fgreinacher
Copy link
Contributor

@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.

@fgreinacher
Copy link
Contributor

If they don't want to reconsider we can add a configuration option here.

i have a really hard time getting behind adding an option to disable verification of tls certificates. part of the decision to use a self-signed certificate is taking on the extra complexity of configuring systems to trust that certificate. i recognize that there used to be a way around this by directly telling node to skip verification, and i wouldnt expect us to take steps to prevent that. however, i think us defining an explicit option would encourage more folks to do this and suggests that we support avoiding security measures.

Warning: Setting the NODE_TLS_REJECT_UNAUTHORIZED environment variable to '0' makes TLS connections and HTTPS requests insecure by disabling certificate verification.

there is a reason that this warning is provided. i dont think we should be encouraging folks to ignore this.

Full ack, but I also don’t want to break existing workflows if we can somehow avoid it. I agree that adding this as configuration option might send the wrong signal. Let’s see if there’s any response from the got folks.

@travi
Copy link
Member

travi commented Feb 3, 2023

would it be valid to handle this through documentation? does gitlab provide any official documentation for using self-signed certs and adding them to the trust chain that we could link to? or maybe recommend use of free certs from a vendor that is based on a trusted root CA?

i'm trying to be careful how i say this, but i dont consider a workflow that only works by following unsecure practices to be a valid approach and especially not one that is officially supported by semantic-release. i imagine we dont call that out in documentation currently, but i'm open to that being added if we feel we need to call it out. i see this more as encouragement to take steps to fill a security hole in the release process for the software supply chain of the projects that were following this approach.

another concern i have after reverting the upgrade to the latest version of got is that we are now a major version behind again. that means that consumers of this plugin would not get security fixes that are only applied to the latest channel. that puts all consumers of this plugin at risk rather than only those choosing to follow unsecure handling of their self-signed certs. we now have a couple of options for versions of this plugin that continue to work for those flows. folks that dont want to update their cert handling right away can choose to stay on one of those version, but i'd feel better keeping up with the latest channel of got.

if folks want to encourage the got team to revert their decision, i feel like that is the place to request a way around this situation. i dont see this being a responsibility of semantic-release.

@fgreinacher
Copy link
Contributor

OK, we need to updrade got soon to mitigate a vulnerability: #500

got maintainers don't seem to interested in reverting the change at their end.

I agree with your argumentation @travi that we should not encourage folks to apply bad security practices.

I'd suggest we perform the update, but indicate a breaking change. Any objections/concerns?

@jagiella
Copy link
Author

@travi @fgreinacher We use a gitlab instance exclusively in our intranet or via SSH tunnels / VPN access from home office. So our server is not and shall not ever be accessible from the public internet. We have chosen secure connection (https) using self-signed certificates over insecure connection (http) nevertheless and for now had no issues with it exept needing workarounds for the "self-signed" complaints of all kind of clients (browser, git), integrations and add-on (like semantic-release).

What would be the correct way of getting rid of "bad security practices" in your opinion?
... free certificates from letsencrypt won't work, because our server is not publically accessible ...

@fgreinacher
Copy link
Contributor

@jagiella Fully understood. I'd say the clean way to solve it is via internal root CAs that manage such certificates. Does something like that exist in your place?

Otherwise there should be a way to mark a specific self-signed certificate as trusted by the OS. I'd expected Node to accept such certificates.

@jagiella
Copy link
Author

jagiella commented Feb 14, 2023

@fgreinacher to my knowledge we don't have an internal root CA yet.

Marking the servers self-signed certificate as trusted by the OS works in general, but unfortunately fails if done inside a docker container (See my 2nd comment #489 (comment)).

@travi
Copy link
Member

travi commented Feb 14, 2023

@jagiella a self-signed certificate still needs to be verified to be considered secure. otherwise, you could be missing evidence of a compromised supply chain (your pipeline server). there are various ways to configure your system to enable verification of the signature that are beyond the scope of support for the semantic-release teams. the simplest is to use a cert from a trusted root CA. you've clarified that is not an option for you. because of that, you've chosen a more painful path and it is up to you and your team to work through the details of proper verification.

as mentioned above, if the security risks are not a concern to you, which your description of your desire to avoid certificate verification suggests, you can choose to use older versions of this plugin.

@travi
Copy link
Member

travi commented Feb 14, 2023

I'd suggest we perform the update, but indicate a breaking change. Any objections/concerns?

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.

fgreinacher pushed a commit that referenced this issue Feb 15, 2023
Bumps [cacheable-request](https://github.com/jaredwray/cacheable-request) to 10.2.7 and updates ancestor dependency [got](https://github.com/sindresorhus/got). These dependencies need to be updated together.

BREAKING CHANGE: This version drops support for accepting untrusted TLS certificates via `NODE_TLS_REJECT_UNAUTHORIZED`. See #489 for details.

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
@fgreinacher
Copy link
Contributor

fgreinacher commented Feb 15, 2023

I just merged the upgrade, producing https://github.com/semantic-release/gitlab/releases/tag/v11.0.0.

As mentioned above we are a critical part of many software supply chains and prioritize security over other qualities. I hope this is relatable for everyone, even if it causes some issues downstream.

I'll leave this issue open a bit in case folks want to mention alternative solution approaches.

@fgreinacher
Copy link
Contributor

I'll leave this issue open a bit in case folks want to mention alternative solution approaches.

Closing this now as there was no additional feedback :)

@christopherpickering
Copy link

Hey, thanks for this discussion. I'm commenting in case someone else finds this w/ the same problem I had.

  • I have an internal website (git.domain.com)
  • it was using a public wildcard cert (*.domain.com) from digicert (the domain.com is public)

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

<* cert>
<intermediate>
<root>

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 with sudo gitlab-ctl restart nginx

of course in your gitlab config (sudo nano /etc/gitlab/gitlab.rb) the nginx["ssl ... cert"] should be this file...

You can verify it after restarting nginx with echo | /opt/gitlab/embedded/bin/openssl s_client -connect git.domain.com:443

@christopherpickering
Copy link

@fgreinacher I agree with all the security arguments, you can find the discussion everywhere you look for ssl issues... however, there are a lot of guys runing gitlab in a box in their garage totally isolated from the outside world and just want stuff to work without the endless cert details. It would be cool if the code here could be tweaked to read that env var and pass it as the new parameter to got ;)

@Tri0L
Copy link

Tri0L commented Mar 23, 2023

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:

echo | openssl s_client -servername mydomain.local -connect mydomain.local:443 2>/dev/null | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > /usr/local/share/ca-certificates/mydomain.local.crt

And then

  • run update-ca-certificates
  • This is enough for usual tools like curl. Don't know why, but not enough for got.
  • We can add NODE_EXTRA_CA_CERTS=/etc/ssl/certs/ca-certificates.crt variable to env, and now all is working

This is really strange, why got not use system list of trusted certs. If any body know, please tell me =)

@Tri0L
Copy link

Tri0L commented Mar 24, 2023

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 obvious NODE_OPTIONS=--use-openssl-ca.

--use-openssl-ca - This setting forces the application to use the system's list of CAs. Thats all we need.

@leonardvgend
Copy link

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 use NODE_OPTIONS=--use-openssl-ca along with what was suggested here #489 (comment) 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 setting NODE_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.

@Teles1
Copy link

Teles1 commented Jan 9, 2024

NODE_TLS_REJECT_UNAUTHORIZED=0

I've been trying the same thing without much success.

release:
  image: node:lts
  stage: release
  script:
    - mkdir -p /usr/local/share/ca-certificates
    - echo | openssl s_client -servername domain.local -connect domain.local:443 2>/dev/null | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > /usr/local/share/ca-certificates/domain.local.crt
    - update-ca-certificates
    - export NODE_OPTIONS=--use-openssl-ca && npm i semantic-release @semantic-release/changelog @semantic-release/commit-analyzer @semantic-release/gitlab @semantic-release/git @semantic-release/npm @semantic-release/release-notes-generator semantic-release-slack-bot
    - export NODE_OPTIONS=--use-openssl-ca &&  npx semantic-release
  only:
    - main
  except:
    refs:
      - tags
    variables:
      - $CI_COMMIT_TITLE =~ /^RELEASE:.+$/

@leonardvgend
Copy link

Hi @Teles1

To have semantic-release/gitlab work with NODE_TLS_REJECT_UNAUTHORIZED=0 you need to explicitly install @semantic-release/gitlab@10.1.4 installing the latest version will not work because the underlying libraries have made changes and no longer utilize NODE_TLS_REJECT_UNAUTHORIZED=0

@Teles1
Copy link

Teles1 commented Jan 10, 2024 via email

@chris-lsn
Copy link

chris-lsn commented Feb 29, 2024

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 obvious NODE_OPTIONS=--use-openssl-ca.

--use-openssl-ca - This setting forces the application to use the system's list of CAs. Thats all we need.

Adding the --use-openssl-ca works for me, below is the code of ci job for reference:

release:
  stage: release
  image: node:lts
  rules:
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
  variables:
    NODE_OPTIONS: "--use-openssl-ca"
  script:
    - cp $CUSTOM_ROOT_CA /usr/local/share/ca-certificates/custom_ca.crt
    - update-ca-certificates
    - npm install semantic-release @semantic-release/gitlab @semantic-release/changelog @semantic-release/git
    - npx semantic-release

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

10 participants