lwsjs / local-web-server

A lean, modular web server for rapid full-stack development.
MIT License
1.21k stars 85 forks source link

Error: unable to get local issuer certificate #65

Closed djKianoosh closed 7 years ago

djKianoosh commented 7 years ago

Using versions:

$ ws --version && node --version && npm --version
2.0.0-pre.1
v8.0.0
5.0.3

Say I have a backend server whose ssl cert was signed by my internal org's root ca. I have a rewrite rule here like so:

$ ws -r '/path/* -> https://__internal.hostname__/path/$1' -v --config
{ port: 8000,
  stack:
   [ [Function: Log],
     [Function: Cors],
     [Function: Json],
     [Function: Rewrite],
     [Function: BodyParser],
     [Function: Blacklist],
     [Function: ConditionalGet],
     [Function: Mime],
     [Function: Compress],
     [Function: MockResponse],
     [Function: SPA],
     [Function: Static],
     [Function: Index] ],
  rewrite: [ '/path/* -> https:/__internal.hostname__/path/$1' ],
  verbose: true,
  config: true }

We get the following errors:

$ ws -r '/ta/* -> https://__internal.hostname__/path/$1' -v --https
Serving at https://__MY_HOST__:8000, https://__IP ADDR__:8000, https://__IP ADDR__:8000, https://__IP ADDR__:8000, https://127.0.0.1:8000
GET /example/ 200 9.899 ms - 927
GET /favicon.ico 404 6.632 ms - 9
Rewrite 'POST /path/to/api -> POST https://__internal.hostname__/path/to/api'
Rewrite 'POST /path/to/api -> POST https://__internal.hostname__/path/to/api'
Error: unable to get local issuer certificate
POST /path/to/api 500 34.836 ms - 21
Rewrite 'POST /path/to/api -> POST https://__internal.hostname__/path/to/api'
Error: unable to get local issuer certificate
POST /path/to/api 500 46.020 ms - 21
Rewrite 'POST /path/to/api -> POST https://__internal.hostname__/path/to/api'
Error: unable to get local issuer certificate
POST /path/to/api 500 52.791 ms - 21
Rewrite 'POST /path/to/api -> POST https://__internal.hostname__/path/to/api'
Error: unable to get local issuer certificate
POST /path/to/api 500 60.247 ms - 21
Rewrite 'POST /path/to/api -> POST https://__internal.hostname__/path/to/api'
Error: unable to get local issuer certificate
POST /path/to/api 500 43.301 ms - 21
Rewrite 'POST /path/to/api -> POST https://__internal.hostname__/path/to/api'
Error: unable to get local issuer certificate
POST /path/to/api 500 43.349 ms - 21
Rewrite 'POST /path/to/api -> POST https://__internal.hostname__/path/to/api'
Error: unable to get local issuer certificate
POST /path/to/api 500 46.108 ms - 21
Error: unable to get local issuer certificate
POST /path/to/api 500 1065.275 ms - 21

I think the problem is that node apparently hard codes its certificate authorities:

And there's a global option to supply your own ca certs file: https.globalAgent.options.ca

Do you think we could have something like:

ws -ca '/path/to/ca'

which would set that global option?

djKianoosh commented 7 years ago

https://github.com/nodejs/node/pull/8334 looks like is a compile time option :-1:

75lb commented 7 years ago

hi, thanks for the report! I'm very close to a release candidate which will have cli options to set TLS options like ca, ciphers etc. Watch this space.. Let me know if you find anything else, now would be a good time..

djKianoosh commented 7 years ago

That's awesome. I'll definitely try it as soon as it's available.

There is one awful workaround I hesitate to mention but it is setting the env var:

# DO NOT USE THIS.... 
NODE_TLS_REJECT_UNAUTHORIZED="0"

I wish Node itself would let us set an env variable to specify CA, but that is another story...

Cheers!

75lb commented 7 years ago

Hi, I'm planning to look at this tonight.

The issue comes from the rewrite middleware which is proxying '/ta/* -> https://__internal.hostname__/path/$1' for you.

The proxy request is complaining as the certificate supplied by your internal API server was issued by a CA it can't verify. There are two solutions i can think of:

  1. Fix the rewrite middleware to ignore TLS/certificate issues (rejectUnauthorized: false see here)
  2. Extend the rewrite middleware with options enabling the user to pass key, cert, pfx, ciphers, ca and other config to try help them achieve a 100% verified TLS link.

Option 1 is the clear favourite, unless you have any reasons otherwise. Let me know.

djKianoosh commented 7 years ago

hmmm... something about ignoring TLS/cert issues just doesn't sit well, but I guess the point of this tool is to run an app locally, and you should know what you're proxying to for your backend APIs, so in a way, you really should know better. Or at least, you ought to know what you're proxying to...

Given that assumption and option 1 is probably a heck of a lot easier to implement, yeah I can see where that would be a better choice.

The only argument really is of course, as you say, 100% verified TLS link. How important is that when developing a JS lib locally against a set of backend APIs? Only users of this library can say for sure.

I'll be good with whatever you decide :) :+1:

75lb commented 7 years ago

i released v2.0.0-pre.4 which ignores CA verification issues by default.. as ws is only a development server (not production) I have a feeling this will be the preferred default for most users, who want proxied rewrites to "just work" and not spend hours wrestling with TLS/OpenSSL..

$ npm install local-web-server@next

However, i'm not finished there. I will make proxy requests fully configurable in the future so users that want strict TLS can have it, passing in CA certificate chains etc.

djKianoosh commented 7 years ago

Just tested it and it works great. Kudos! :100:

btw, love the more verbose output when using -v :+1: