Open Astro-Otter-Space opened 10 months ago
Try to disable TLS on Mercure. For instance, set SERVER_NAME
to http://localhost
(notice the http://
prefix).
ok I'll do that. I changed SERVER_NAME
value in /etc/environment
$ echo $SERVER_NAME
http://localhost
and changed Caddyfile like this :
{
{$DEBUG:debug}
{$CADDY_GLOBAL_OPTIONS}
order mercure after encode
# Ports
http_port 3000
auto_https off
{$GLOBAL_OPTIONS}
}
{$CADDY_EXTRA_CONFIG}
{$SERVER_NAME:localhost} {
# tls /etc/letsencrypt/live/mercure.exemple.space/fullchain.pem /etc/letsencrypt/live/mercure.exemple.space/privkey.pem
line tls ...
commented and disabled like i saw here or here
Is it enough ?
I have no more error in console (a good point ^^).
$ MERCURE_PUBLISHER_JWT_KEY=$MERCURE_PUBLISHER_JWT_KEY \
MERCURE_SUBSCRIBER_JWT_KEY=$MERCURE_SUBSCRIBER_JWT_KEY \
SERVER_NAME=$SERVER_NAME \
DEBUG=debug \
/usr/bin/mercure run --config Caddyfile
2024/01/04 08:32:28.347 INFO using provided configuration {"config_file": "Caddyfile", "config_adapter": ""}
2024/01/04 08:32:28.350 WARN Caddyfile input is not formatted; run 'caddy fmt --overwrite' to fix inconsistencies {"adapter": "caddyfile", "file": "Caddyfile", "line": 2}
2024/01/04 08:32:28.351 INFO admin admin endpoint started {"address": "localhost:2019", "enforce_origin": false, "origins": ["//localhost:2019", "//[::1]:2019", "//127.0.0.1:2019"]}
2024/01/04 08:32:28.352 WARN http.auto_https automatic HTTPS is completely disabled for server {"server_name": "srv0"}
2024/01/04 08:32:28.352 DEBUG http.auto_https adjusted config {"tls": {"automation":{"policies":[{}]}}, "http": {"http_port":3000,"servers":{"srv0":{"listen":[":3000"],"routes":[{"handle":[{"handler":"subroute","routes":[{"handle":[{"handler":"headers","response":{"set":{"Content-Type":["text/html; charset=utf-8"]}}}],"match":[{"path":["/"]}]},{"handle":[{"encodings":{"gzip":{},"zstd":{}},"handler":"encode","prefer":["zstd","gzip"]},{"anonymous":true,"cors_origins":["https://news.exemple.space","https://exemple.local:8080","https://localhost:8080"],"handler":"mercure","publish_origins":["*"],"publisher_jwt":{"alg":"{env.MERCURE_PUBLISHER_JWT_ALG}","key":"{env.MERCURE_PUBLISHER_JWT_KEY}"},"subscriber_jwt":{"alg":"{env.MERCURE_SUBSCRIBER_JWT_ALG}","key":"{env.MERCURE_SUBSCRIBER_JWT_KEY}"},"subscriptions":true,"transport_url":"bolt://mercure.db"}]},{"handle":[{"handler":"static_response","status_code":200}],"match":[{"path":["/healthz"]}]},{"handle":[{"body":"\u003c!DOCTYPE html\u003e\n\t\u003chtml lang=en\u003e\n\t\u003cmeta charset=\"utf-8\"\u003e\n\t\u003cmeta name=\"robots\" content=\"noindex\"\u003e\n\t\u003ctitle\u003eWelcome to Mercure\u003c/title\u003e\n\t\u003ch1\u003eWelcome to Mercure\u003c/h1\u003e\n\t\u003cp\u003eThe URL of your hub is \u003ccode\u003e/.well-known/mercure\u003c/code\u003e.\n\tRead the documentation on \u003ca href=\"https://mercure.rocks\"\u003eMercure.rocks, real-time apps made easy\u003c/a\u003e.","handler":"static_response"}],"match":[{"path":["/"]}]},{"handle":[{"body":"Not Found","handler":"static_response","status_code":404}]}]}],"terminal":true}],"automatic_https":{"disable":true},"logs":{"logger_names":{"localhost":"log0"}}}}}}
2024/01/04 08:32:28.354 DEBUG http starting server loop {"address": "[::]:3000", "tls": false, "http3": false}
2024/01/04 08:32:28.355 INFO http.log server running {"name": "srv0", "protocols": ["h1", "h2", "h3"]}
2024/01/04 08:32:28.355 INFO autosaved config (load with --resume flag) {"file": "/home/stephane/.config/caddy/autosave.json"}
2024/01/04 08:32:28.355 INFO serving initial configuration
2024/01/04 08:32:28.357 INFO tls.cache.maintenance started background certificate maintenance {"cache": "0xc0004c0000"}
2024/01/04 08:32:28.359 WARN tls storage cleaning happened too recently; skipping for now {"storage": "FileStorage:/home/stephane/.local/share/caddy", "instance": "359fed6e-f64a-4f98-a8cb-e2b7bb24d40c", "try_again": "2024/01/05 08:32:28.359", "try_again_in": 86399.999999583}
2024/01/04 08:32:28.359 INFO tls finished cleaning storage units
And i saw my request in log :
2024/01/04 08:37:37.796 INFO http.log.access handled request {"request": {"remote_ip": "127.0.0.1", "remote_port": "36484", "client_ip": "127.0.0.1", "proto": "HTTP/1.1", "method": "GET", "host": "127.0.0.1:3000", "uri": "/.well-known/mercure?topic=https%3A%2F%2Fapi.exemple.space%2Fnotifications%2Fdso", "headers": {"Sec-Fetch-Site": ["cross-site"], "Sec-Fetch-Mode": ["cors"], "Referer": ["https://localhost:8080/"], "Accept-Encoding": ["gzip, deflate, br"], "X-Forwarded-For": ["185.101.209.57"], "Cache-Control": ["no-cache"], "Sec-Ch-Ua-Mobile": ["?0"], "User-Agent": ["Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"], "Accept-Language": ["fr-FR,fr;q=0.9,en-US;q=0.8,en;q=0.7"], "Sec-Ch-Ua-Platform": ["\"Linux\""], "Sec-Fetch-Dest": ["empty"], "X-Forwarded-Host": ["mercure.exemple.space"], "X-Forwarded-Proto": ["https"], "Sec-Ch-Ua": ["\"Not_A Brand\";v=\"8\", \"Chromium\";v=\"120\", \"Google Chrome\";v=\"120\""], "Accept": ["text/event-stream"], "Origin": ["https://localhost:8080"]}}, "bytes_read": 0, "user_id": "", "duration": 0.000004338, "size": 0, "status": 0, "resp_headers": {"Server": ["Caddy"]}}
If i curl from local :
curl -k -I -X GET https://mercure.exemple.space/.well-known/mercure
HTTP/2 200
server: nginx
date: Thu, 04 Jan 2024 08:33:39 GMT
content-length: 0
JS side, i got 200 too but CORS error
Access to resource at 'https://mercure.exemple.space/.well-known/mercure?topic=https%3A%2F%2Fapi.astro-otter.space%2Fnotifications%2Fall' from origin 'https://localhost:8080' has been blocked by CORS policy: The value of the 'Access-Control-Allow-Credentials' header in the response is '' which must be 'true' when the request's credentials mode is 'include'.
mercure:1
GET https://mercure.exemple.space/.well-known/mercure?topic=https%3A%2F%2Fapi.exemple.space%2Fnotifications%2Fall net::ERR_FAILED 200 (OK)
home:1 EventSource's response has a MIME type ("text/plain") that is not "text/event-stream". Aborting the connection.
It seems it's on a good way ^^
I added in my nginx vhost these lines :
location / {
if ($http_origin ~ '^https?://(localhost(:[0-9]+)?|[^/]*\.exemple\.space)') {
set $cors "true";
}
if ($cors = "true") {
add_header 'Access-Control-Allow-Origin' '$http_origin' always;
add_header 'Access-Control-Allow-Credentials' 'true';
add_header 'Access-Control-Allow-Headers' 'Authorization,Accept,Origin,DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range';
add_header 'Access-Control-Allow-Methods' 'GET,POST,OPTIONS';
add_header "Content-Type" "text/event-stream";
}
proxy_pass http://127.0.0.1:3000;
proxy_read_timeout 24h;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_connect_timeout 300s;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
}
And no more CORS errors.
This shouldn't be necessary but thanks for the workaround! I'll take a look.
Without Access-Control-Allow-Origin
, Access-Control-Allow-Credentials
and Content-Type
, i have CORS error JS-side.
With this nginx configuration, what should be values of cors_origins
and publish_origins
in my Caddyfile ?
You told me to set http://localhost
for env variable SERVER_NAME
. Should i set proxy_pass http://localhost:3000;
instead of proxy_pass http://127.0.0.1:3000;
?
UPDATE 09/01/2024 :
i've changed proxy_pass http://127.0.0.1:3000;
into proxy_pass http://localhost:3000;
in nginx.
Now when i do curl http://localhost:3000
(on server) and curl https://mercure.astro-otter.space
in local, i have the response from Caddy same as defined in Caddyfile,good point (youpi)
If i'm publishing from my Symfony controller or POST curl request:
HTTP/1.1 401 Unauthorized returned for "http://localhost:3000/.well-known/mercure"."
with symfonyTopic selectors not matched, not provided or authorization error {"remote_addr": "127.0.0.1:59610", "error": "unable to parse JWT: signature is invalid"}
Finally i found a way to make it working correctly. I resume :
First, i'm working with binary, not docker image.
Env file /etc/environment
:
MERCURE_PUBLISHER_JWT_KEY="mySecretKey"
MERCURE_SUBSCRIBER_JWT_KEY="mySecretKey"
# Mercure URL
SERVER_NAME=http://localhost
MERCURE_PUBLIC_URL=https://mercure.exemple.space/.well-known/mercure
My nginx vhost :
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name mercure.exemple.space;
ssl_certificate /etc/letsencrypt/live/mercure.exemple.space/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/mercure.exemple.space/privkey.pem; # managed by Certbot
location / {
if ($http_origin ~ '^https?://(localhost(:[0-9]+)?|127.0.0.1(:[0-9]+)?|[^/]*\.exemple\.space)') {
set $cors "true";
}
if ($cors = "true") {
add_header 'Access-Control-Allow-Origin' '$http_origin' always;
add_header 'Access-Control-Allow-Credentials' 'true';
add_header 'Access-Control-Allow-Headers' 'Authorization,Accept,Origin,DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range';
add_header 'Access-Control-Allow-Methods' 'GET,POST,OPTIONS';
add_header 'Content-Type' 'text/event-stream';
}
proxy_pass http://localhost:3000;
proxy_read_timeout 24h;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_connect_timeout 300s;
#proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
}
# Configuration des logs
access_log /var/log/nginx/mercure_access.log;
error_log /var/log/nginx/mercure_error.log;
I create a systemd file for running mercure as service /etc/systemd/system/mercure.service
:
[Unit]
Description=Mercure.Rocks service
After=network.target
StartLimitBurst=5
StartLimitIntervalSec=33
[Service]
Type=simple
WorkingDirectory=/tmp
EnvironmentFile=-/etc/environment
ExecStart=/usr/bin/bash -c "MERCURE_PUBLISHER_JWT_KEY=$MERCURE_PUBLISHER_JWT_KEY MERCURE_SUBSCRIBER_JWT_KEY=$MERCURE_SUBSCRIBER_JWT_KEY SERVER_NAME=$SERVER_NAME /usr/bin/mercure run --config /var/www/mercure/Caddyfile"
StandardOutput=file:/var/log/nginx/mercure.log
StandardError=file:/var/log/nginx/mercure.log
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
In /var/www/mercure
, here's my caddyfile :
{
{$DEBUG:debug}
{$CADDY_GLOBAL_OPTIONS}
order mercure after encode
# Ports
http_port 3000
auto_https off
{$GLOBAL_OPTIONS}
}
{$CADDY_EXTRA_CONFIG}
{$SERVER_NAME:localhost} {
log {
output file /var/log/caddy/mercure.log {
roll true
roll_size_mb 10
roll_keep 5
}
format filter {
wrap console
fields {
uri query {
replace authorization REDACTED
}
}
}
level INFO
}
encode zstd gzip
mercure {
# Transport to use (default to Bolt)
transport_url {$MERCURE_TRANSPORT_URL:bolt://mercure.db}
# Publisher JWT key
publisher_jwt {env.MERCURE_PUBLISHER_JWT_KEY} {env.MERCURE_PUBLISHER_JWT_ALG}
# Subscriber JWT key
subscriber_jwt {env.MERCURE_SUBSCRIBER_JWT_KEY} {env.MERCURE_SUBSCRIBER_JWT_ALG}
# Extra directives
# CORS
cors_origins /** add here URL **/
publish_origins *
anonymous
subscriptions
{$MERCURE_EXTRA_DIRECTIVES}
}
{$CADDY_SERVER_EXTRA_DIRECTIVES}
header / Content-Type "text/html; charset=utf-8"
respond / `<!DOCTYPE html>
<html lang=en>
<meta charset="utf-8">
<meta name="robots" content="noindex">
<title>Welcome to Mercure</title>
<h1>Welcome to Mercure</h1>
<p>The URL of your hub is <code>/.well-known/mercure</code>.
Read the documentation on <a href="https://mercure.rocks">Mercure.rocks, real-time apps made easy</a>.`
respond /healthz 200
Code-side
My backend is a symfony (upgrade to API Platform ne day ^^).
/path/to/backend/c/.env
MERCURE_URL=http://localhost:3000/.well-known/mercure
MERCURE_PUBLIC_URL=https://mercure.exemple.space/.well-known/mercure # use variable from /etc/environment ?
MERCURE_JWT_SECRET="PutYoutJWTHere"
/path/to/backend/config/packages/mercure.yaml
mercure:
hubs:
default:
url: '%env(MERCURE_URL)%'
public_url: '%env(MERCURE_PUBLIC_URL)%'
jwt:
value: '%env(MERCURE_JWT_SECRET)%'
publish: '*'
Front-side is a VueJS application, using EventSource, nothing exotic.
I think i need some adjustments but with all these it's working :).
Hello,
I'm deploying Mercure hub on my server (VPS hosted by OVH) where an API REST (Symfony+FOSRestBundle) and a front app (VueJS 3, vue-cli) are installed yet, served with Nginx. API and FO have their own domains (https://api.exemple.space/ and https://www.exemple.space/). I've created a domain for Mercure (https://mercure.exemple.com/). Mercure (version 0.15) is installed with binary, not with docker.
I added an nginx host configuration for working as reverse-proxy (https://mercure.rocks/docs/hub/nginx):
i set public and private keys and set them as env variables in /etc/profile.d/mercure.sh
I run with command
MERCURE_PUBLISHER_JWT_KEY=$MERCURE_PUBLISHER_JWT_KEY MERCURE_SUBSCRIBER_JWT_KEY=$MERCURE_SUBSCRIBER_JWT_KEY /usr/bin/mercure run --config /home/stephane/mercure/Caddyfile
My Caddyfile :
In log i have :
If i run curl from my local env :
In my JS app (from local or prod env), i have CORS error with this code :
I need help for wrtting good configuration for Nginx and Caddyfile. I'll fix CORS errors later. Thank you for help :)