Error 403: Invalid Signature upon verification
LinkStack version
4.7.1
Description
E-Mail verification does not work. It always results in an 403 error. Probably same issue as in https://github.com/LinkStackOrg/LinkStack/issues/591, which is not fixed, but where you can find some possible workarounds.
Details about your system
LEMP stack on a debian bookworm system (nginx 1.22.1, php 8.2.7) with smtp mailing system.
Front end nginx proxy (ssl termination) configuration, listens on https and passes requests to backend, listening on http with IP address 123.123.123.1.
server {
listen 80;
listen [::]:80;
server_name domain.tld www.domain.tld;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name domain.tld www.domain.tld;
location / {
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_pass http://123.123.123.123;
proxy_set_header X-VerifiedViaNginx yes;
proxy_read_timeout 60;
proxy_connect_timeout 60;
proxy_redirect off;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_cache_bypass $http_upgrade;
add_header 'Content-Security-Policy' 'upgrade-insecure-requests';
}
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl_certificate /etc/letsencrypt/live/domain.tld/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/domain.tld/privkey.pem; # managed by Certbot
}
Back end nginx configuration for LinkStack, only listens on http with IP address 123.123.123.1
server {
listen 80;
server_name domain.tld www.domain.tld;
root /var/www/domain.tld/linkstack;
index index.php;
charset utf-8;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location = /favicon.ico { access_log off; log_not_found off; }
location = /robots.txt { access_log off; log_not_found off; }
error_page 404 /index.php;
location ~ \.php$ {
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_index index.php;
include /etc/nginx/fastcgi_params;
fastcgi_pass unix:/run/php/php8.2-fpm.sock;
}
location ~ /\.(?!well-known).* {
deny all;
}
location ~ ^\. {
deny all;
}
location ~ \.sqlite$ {
deny all;
}
location ~ \.env$ {
deny all;
}
location ~ /\.htaccess {
allow all;
}
}
How to reproduce
Configure system as described (debian bookworm with lemp stack)
- Install LinkStack on a backend server (http only)
- Enable FORCE_HTTPS on backend server
- Configure front end server (https termination)
Ugly workarounds
- Disable "FORCE_HTTPS" and let front end nginx handle all the ssl redirects. Unfortunately then you have http urls in the mails and not https.
- Edit
app/Http/Middleware/TrustProxies.phpwithprotected $proxies = '*';(or set specific IP address of backend server instead of *)
Possible Solution
Make LinkStack aware that it could be behind a (ssl) proxy.
You need to forword all traffic with https (ssl)
internet 443-> nginx 443-> docker
After then you need to set FORCE_HTTPS=true
should you not use Docker then I wonder why you specify an ip in the config ?
You need to forword all traffic with https (ssl)
It should be possible to forward internal traffic on http only. There is no need for an encrypted connection to the backend server in my environment.
internet 443-> nginx 443-> docker
I don't use docker.
After then you need to set FORCE_HTTPS=true
should you not use Docker then I wonder why you specify an ip in the config ?
Because it's the IP address of the internal backend app server where LinkStack is running.
how do you installed linkstack? if you have a webserver so you don't need to forward anything. Like this for http to https
server {
listen 80 default_server;
server_name _;
return 301 https://$host$request_uri;
}
https
server {
listen 443;
listen [::]:443;
server_name example.com;
root /path/to/example.com;
add_header X-Frame-Options "SAMEORIGIN";
add_header X-Content-Type-Options "nosniff";
index index.php;
charset utf-8;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location = /favicon.ico { access_log off; log_not_found off; }
location = /robots.txt { access_log off; log_not_found off; }
error_page 404 /index.php;
location ~ \.php$ {
fastcgi_pass unix:/var/run/php/php7.4-fpm.sock;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
include fastcgi_params;
}
location ~ /\.(?!well-known).* {
deny all;
}
location ~ ^\. {
deny all;
}
location ~ \.sqlite$ {
deny all;
}
location ~ \.env$ {
deny all;
}
location ~ /\.htaccess {
allow all;
}
}
I have two webservers: One as public front end server, responsible for ssl termination (https connections) and another one as an internal app server, responsible for the applications (LimeStack, php stuff).
It has to be an issue with the LinkStack karavel proxy configuration or proxy detection. When I set the proxy IP address of the front end server in app/Http/Middleware/TrustProxies.php with protected $proxies = [ '123.123.123.1']; where 123.123.123.1 is the IP address of the front end server, the verification links work as expected. So does it with protected $proxies = '*';
See: https://laravel.com/docs/10.x/requests#configuring-trusted-proxies
Setting the trusted proxies via a variable would be desirable.
I've made this change:
<?php
namespace App\Http\Middleware;
use Fideloper\Proxy\TrustProxies as Middleware;
use Illuminate\Http\Request;
class TrustProxies extends Middleware
{
/**
* The trusted proxies for this application.
*
* @var array|string|null
*/
protected $proxies;
/**
* The headers that should be used to detect proxies.
*
* @var int
*/
protected $headers = Request::HEADER_X_FORWARDED_FOR
| Request::HEADER_X_FORWARDED_HOST
| Request::HEADER_X_FORWARDED_PORT
| Request::HEADER_X_FORWARDED_PROTO
| Request::HEADER_X_FORWARDED_AWS_ELB;
/**
* Create a new middleware instance.
*
* @return void
*/
public function __construct()
{
$this->proxies = $this->parseProxies(env('TRUSTED_PROXIES'));
}
/**
* Parse the environment variable containing the trusted proxies.
*
* @param string|null $proxies
* @return array|string|null
*/
protected function parseProxies($proxies)
{
if ($proxies === null) {
return null;
}
if ($proxies === '*') {
return '*'; // Explicitly handle the wildcard case
}
return array_map('trim', explode(',', $proxies));
}
}