Open chazsconi opened 2 years ago
Hi again!
I’m missing something – if you’re running everything on your laptop, why can’t you use like any random port for elm-watch then? Why does it need to be behind the proxy?
Hi - thanks for responding so quickly!
I didn't try using a random port before as I wanted to avoid the self-signed certificate as I already had a valid certificate for the domain which is handled by `haproxy and does the SSL handoff.
However, I have now tried with a random port and this work on Firefox after accepting the self-signed cert from elm-watch
.
However, it does not work in Chrome (which is my preferred browser for development) even after accepting the self-signed cert. I think this is because Chrome only accepts the certificate for the current page, and going to a different URL on the same domain or even just refreshing the page requires re-accepting the self-signed cert every time.
I just keep getting logs like this in the browser console:
elm.js:988 WebSocket connection to 'wss://foo.bar.com:53853/elm-watch?elmWatchVersion=1.1.0&targetName=Foo&elmCompiledTimestamp=1667594645328' failed:
The Chrome version is 107.0.5304.87 on an M1 Mac.
Oh, you don’t use localhost? I only tested Chrome (and other browsers) with localhost. Good to know. Edit: I added 127.0.0.1 foo.bar.com
to /etc/hosts
(on macOS) and went to https://foo.bar.com:8001/
in Chrome, and went through the self-signed certificate stuff. elm-watch then connected as expected. I used the example to test it. So I’m not sure why it didn’t work for you?
Do you have to files on your computer – like a .key
and a .crt
– for haproxy? I’ve been waiting for someone with this setup to see if an idea I’ve had makes sense. I have been thinking of adding configuration in elm-watch.json that lets you point out those two files so elm-watch can use the same certificate for its SSL stuff. Does that make sense?
Finally, I’m curious why you are running HTTPS locally. We used to do that at work, because we thought it was good to have the local environment be as similar as possible as the production environment. However, we noticed that the HTTPS part never caught any bugs or anything for us, so we tried switching to HTTP and everything got simpler (save for having to conditionally set the Secure
flag of two session cookies).
I'll test the example later to see if this works for me.
However, to answer your other questions:
Do you have to files on your computer – like a .key and a .crt – for haproxy?
Yes, I have the Let's Encrypt .key
and .crt
files in a ssl/le-certs
folder on my laptop that is referenced by haproxy. Similar to what you tried, I also set the domain to point to 127.0.0.1
in /etc/hosts
It's a pretty simple haproxy setup - something like:
frontend https-in
bind *:443 ssl crt ssl/le-certs
# I would like to use the following line, but would
# need the to configure elm-watch server to run on port 4001
# use_backend elm-watch if { path_reg ^/elm-watch }
use_backend phoenix if { path_reg ^/ }
backend phoenix
server node1 0.0.0.0:4000
# backend elm-watch
# server node 1 0.0.0.0:4001
I have been thinking of adding configuration in elm-watch.json that lets you point out those two files so elm-watch can use the same certificate for its SSL stuff. Does that make sense?
I'm not sure. I know that you do support SSL hand-off in elm-watch currently, but do you think it is necessary? As you say in "What elm-watch is not", it is "not a file server" and "Let elm-watch excel at compiling Elm quickly and reliably, and own the rest of the stack yourself."
If a user is using HTTPS for development they will need to have the file server/web server that they use for the non-Elm stuff do SSL hand off anyway, so maybe it's simpler to just let them do this in one place with a proxy like nginx or haproxy. (Although of course to do this, you need to be able to configure elm-watch
to run on port 443
in the client and a different port on the server :) )
Although haproxy lets you put all your .key
and .crt
files in the same folder, I expect different web servers/proxies rely on different setups, e.g. putting the files in different folders, joining the files, or using .pem
files instead. With Let's Encrypt certs you need to renew them frequently, so a user will need to regularly update the files in the correct format and location for their own web server/proxy and elm-watch (although of course they could script this).
I’m curious why you are running HTTPS locally
I have found that browser features increasingly only work with HTTPS and so this is the only option for testing in a development environment. These include:
navigator.clipboard
)I have also read that Chrome (although I cannot find the original source of the story from Google :( ) will eventually stop supporting HTTP completely (although I suppose for localhost
it will always be allowed). I suspect that sites with self-signed certs will also not be supported in the future - so you won't be able to click the "Advanced" button and agree to continue. I think this is already the case for real certs that have expired, which up to a few months ago, allowed you to still click the "Advanced" button.
In my use case I cannot use localhost
(even if I wasn't testing the browser HTTPS only features) as I have a couple of microsites running on different domains with a single sign on between them.
If a user is using HTTPS for development they will need to have the file server/web server that they use for the non-Elm stuff do SSL hand off anyway, so maybe it's simpler to just let them do this in one place with a proxy like nginx or haproxy.
Yeah, I’m not sure what’s simpler either. elm-watch currently contains a hardcoded certificate, so being able to point out two files is not far from what we have today. And that might be easier to do for end users than proxying? Supporting different ports for backend and frontend also sounds easy (I think), but from my experience with Docker it’s easy to confuse oneself with port mappings (which port is used where).
I have found that browser features increasingly only work with HTTPS and so this is the only option for testing in a development environment.
FYI: As far as I understand, http://localhost
is an exception. I know for sure that clipboard and service workers work there, but I haven’t tested push notifications.
Either way, I’m not sure which path I want to take here and don’t feel like thinking more about it right now, so I’m going to let this one sit for a while. Maybe some other HTTPS-locally user pops up and gives more insight.
For now, you can use a hack like this:
<script>
window.WebSocket = class HackWebSocket extends WebSocket {
constructor(urlParam) {
const url = new URL(urlParam);
if (looksLikeElmWatch(url)) {
url.port = "1234";
}
super(url);
}
}
function looksLikeElmWatch(url) {
return url.port === "4321";
// or return url.pathname === "/elm-watch";
}
</script>
Ok, one more question: If I were to add cert configuration to elm-watch.json, would it look something like this for you then?
{
"sslCertificate": {
"cert": "./ssl/le-certs/foo.crt",
"key": "./ssl/le-certs/foo.key"
},
"targets": {}
}
In other words, the paths are relative to the elm-watch.json file, and should work for any contributor, not just on your machine. (Config in elm-watch.json should be correct for everyone; things that differ for different contributors go in environment variables (ELM_WATCH_OPEN_EDITOR
) or can be changed via the browser UI (compilation mode, UI position, error overlay).)
I checked Node.js, Go, Ruby, Python, nginx, Apache, and they all take two files as input. So far, I’ve only found HAProxy that concatenates them. And even in that case, couldn’t you “just” keep three files on disk?
For now, you can use a hack like this:
<script> window.WebSocket = class HackWebSocket extends WebSocket { constructor(urlParam) { const url = new URL(urlParam); if (looksLikeElmWatch(url)) { url.port = "1234"; } super(url); } }
function looksLikeElmWatch(url) { return url.port === "4321"; // or return url.pathname === "/elm-watch"; }
This works great - thanks for the tip!
> Ok, one more question: If I were to add cert configuration to elm-watch.json, would it look something like this for you then?
{ "sslCertificate": { "cert": "./ssl/le-certs/foo.crt", "key": "./ssl/le-certs/foo.key" }, "targets": {} }
Yes, this would be perfect.
> So far, I’ve only found HAProxy that concatenates them. And even in that case, couldn’t you “just” keep three files on disk?
In fact HAProxy doesn't concatenate them - it just expects all the files in the same folder.
Just some extra info for anyone else using a setup with HAProxy: To prevent elm-watch
from frequently reloading, you will need to add something like this to your HAProxy config in the backend
section for elm-watch
timeout tunnel 1h
The current plan is to solve this via https://github.com/lydell/elm-watch/issues/74
This is related to https://github.com/lydell/elm-watch/issues/39 which solved my problem for SSL hand off being done by an external server (An
nginx
Ingress in Kubernetes in this case).I was able to have
elm-watch
listening on port443
(but serving HTTP not HTTPS) as this port was not being used by anything else as thenginx
was running elsewhere.However, in another use case I have, I am running everything on my laptop - I use a local
haproxy
to do the SSL handoff. This of course must listen on port443
thus not allowingelm-watch
to also listen on this port.Therefore I would really like to be able to configure the
elm-watch
client port to be443
but set the server port to something else. Inhaproxy
I would then rewrite/elm-watch
to the server port.I know that you are not keen on extra configuration options, so perhaps this could be done via an environment variable either to change the client port or the server port?