-
-
Save gilbitron/36d48eb751875bebdf43da0a91c9faec to your computer and use it in GitHub Desktop.
<?php | |
# app/Http/Controllers/CaddyController.php | |
namespace App\Http\Controllers; | |
use App\Store; | |
use Illuminate\Http\Request; | |
class CaddyController extends Controller | |
{ | |
public function check(Request $request) | |
{ | |
$authorizedDomains = [ | |
'laravel.test', | |
'www.laravel.test', | |
// Add subdomains here | |
]; | |
if (in_array($request->query('domain'), $authorizedDomains)) { | |
return response('Domain Authorized'); | |
} | |
// Abort if there's no 200 response returned above | |
abort(503); | |
} | |
} |
# docker/Caddyfile | |
{ | |
on_demand_tls { | |
ask http://laravel.test/caddy-check | |
} | |
local_certs | |
} | |
:443 { | |
tls internal { | |
on_demand | |
} | |
reverse_proxy laravel.test { | |
header_up Host {host} | |
header_up X-Real-IP {remote} | |
header_up X-Forwarded-For {remote} | |
header_up X-Forwarded-Port {server_port} | |
header_up X-Forwarded-Proto {scheme} | |
health_timeout 5s | |
} | |
} |
services: | |
caddy: | |
image: caddy:latest | |
restart: unless-stopped | |
ports: | |
- '80:80' | |
- '443:443' | |
volumes: | |
- './docker/Caddyfile:/etc/caddy/Caddyfile' | |
- sailcaddy:/data | |
- sailcaddy:/config | |
networks: | |
- sail | |
# Remove "ports" from laravel.test service | |
volumes: | |
sailcaddy: | |
driver: local |
<?php | |
# routes/web.php | |
Route::get('/caddy-check', 'CaddyController@check'); |
@Nilpo that sounds like you don't handle the caddy-check
route properly or it isn't configured properly.
I have this inside routes:
Route::get('caddy-check', [CaddyController::class, 'check']);
the CaddyController
looks like this:
class CaddyController extends Controller
{
public function check(Request $request)
{
$domain = $request->query('domain');
$domains = collect(config('app.domains'));
$domainsRegExp = regex_escape(collect($domains)->join('|'), '.');
if (preg_match("/$domainsRegExp/", $domain)) {
return response('Domain Authorized');
}
// Abort if there's no 200 response returned above
abort(503);
}
}
and inside Caddyfile
I have the on demand TLS like this (note that I'm only uing this with /~~local/
route prefix).
{
# debug
on_demand_tls {
ask http://{$HOST_DOMAIN}/~~local/caddy-check
}
local_certs
}
You should also check the logs and of course see what Caddy is doing by attaching the caddy container output using sail logs -f caddy
.
@Nilpo that sounds like you don't handle the
caddy-check
route properly or it isn't configured properly.I have this inside routes:
Route::get('caddy-check', [CaddyController::class, 'check']);
the
CaddyController
looks like this:class CaddyController extends Controller { public function check(Request $request) { $domain = $request->query('domain'); $domains = collect(config('app.domains')); $domainsRegExp = regex_escape(collect($domains)->join('|'), '.'); if (preg_match("/$domainsRegExp/", $domain)) { return response('Domain Authorized'); } // Abort if there's no 200 response returned above abort(503); } }
and inside
Caddyfile
I have the on demand TLS like this (note that I'm only uing this with/~~local/
route prefix).{ # debug on_demand_tls { ask http://{$HOST_DOMAIN}/~~local/caddy-check } local_certs }
You should also check the logs and of course see what Caddy is doing by attaching the caddy container output using
sail logs -f caddy
.
I'm using the exact code above. I posted the exact error from the logs. The configuration as posted creates a redirect loop.
It's not the handler, it never gets executed because Caddy won't process the redirect.
A call to http://laravel.test/caddy-check is handled by Caddy which transforms it to https but then refuses to follow it to complete the domain verification. So the request fails and Caddy sends the browser an SSL error.
@Nilpo Hi, it may be a late reply but I managed to solve the same error, here is what I did :
Inside CaddyController:
$authorizedDomains = [ 'localhost', // Add subdomains here ];
This way I can access via https://localhost
Then the error was because of the Caddyfile, as you said when this error occurs we don't even reach the controller.
Here is my Caddyfile:
{
on_demand_tls {
ask http://laravel.test/caddy-check
}
local_certs
}
:443 {
tls internal {
on_demand
}
reverse_proxy laravel.test {
header_up Host {host}
header_up X-Real-IP {remote}
header_up X-Forwarded-For {remote}
header_up X-Forwarded-Port {server_port}
header_up X-Forwarded-Proto {scheme}
health_timeout 5s
}
}
Notice that laravel.test is the name of the reverse proxy service !
My on_demand_tls was set to : ask http://localhost/caddy-check, which caused the failure ...
Don't forget to delete and rebuild your container after modifications on the Caddyfile, otherwise it will keep the old one.
Hi @Maxime-Missichini Thanks for the reply. This still causes the same redirect loop. I've even tried modifying the authorized domains as follows, which accounts for every possibility.
$authorizedDomains = [
env('APP_SERVICE'),
'www.' . env('APP_SERVICE'),
'localhost',
];
But as I mentioned in my last comment, this has no effect because the Caddy handler never gets called. Caddy kills the request with an SSL error and never hands the request off to Laravel for routing.
For the record, APP_SERVICE and the Sail service name are both the same (laravel.test, localhost, or other) and the test domain is resolvable. The failure is with Caddy.
You can view my fork for more details.
The problems is ultimately that the ask URL cannot contain an https protocol, but Caddy refuses to complete the request if it doesn't.
I'm using Laravel Sail in Laravel 9 based on the PHP 8.1 image and Ubuntu 22.04
I'll create an empty project that reproduces this error and create a GitHub repo.
EDIT:
Here's the sample repo.
@Nilpo
try changing your APP_PORT env variable (or whatever that new variable is called now) to a different port like
APP_PORT=8080
referencing the docker-compose.yml
services:
laravel.test:
ports:
- '${APP_PORT:-80}:80'
Well, here's a weird one. I wonder if this is a Mac issue.
If you clone the sample repo I made (or start with a clean Laravel project), the first build doesn't work. But if you immediately force a rebuild with no changes, it works. (I'm starting with a clean Docker install. No locally downloaded images or containers. The strange thing is that the rebuild shouldn't actually change anything. The volumes are all the same too since there's no file changes.
git clone https://github.com/Nilpo/caddy-laravelsail
cd caddy-laravelsail && composer install
./vendor/bin/sail up -d
# opening https://caddy-laravelsail.test fails
./vendor/bin/sail up --build -d
# opening https://caddy-laravelsail.test succeeds
If the domains are going to be hard-coded into that controller anyway, why not put them in the Caddyfile?
# docker/Caddyfile
laravel.test, www.laravel.test {
tls internal
reverse_proxy laravel.test {
header_up Host {host}
header_up X-Real-IP {remote}
header_up X-Forwarded-For {remote}
header_up X-Forwarded-Port {server_port}
header_up X-Forwarded-Proto {scheme}
health_timeout 5s
}
}
Is there any way to do it without creating code in the application? In theory development things should not have to be in the production project. Also, not everyone in the team necessarily has to use docker or sail. It causes me noise to create a route and a controller
I've been trying to get this solution working for days. The setup is simple enough, but Caddy can't verify the domains because it creates a redirect loop. Caddy sends a request to
http://laravel.test/caddy-check
, which it then redirects tohttps://laravel.test/caddy-check
causing the request to fail with the errorfollowing http redirects is not allowed
. Since there doesn't seem to be a way to exclude that single endpoint, the whole solution is a wash.