<?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'); |
Bullshit. Not working
docker-compose up -d
ERROR: Service "caddy" uses an undefined network "sail"
ERROR: Service "caddy" uses an undefined network "sail"
This is not the full docker-compose.yml
file, just the changes you need to make to the one generated by sail.
Thanks ! But I ran in a Problem ...
The Caddyfile is a file in the "docker" subdirectory of the laravel project - correct ?
Or has the Caddyfile to be in a directory "Caddyfile" ?
I get an error :
ERROR: for test-project_caddy_1 Cannot start service caddy: OCI runtime create failed: container_linux.go:380: starting container process caused: process_linux.go:545: container init caused: rootfs_linux.go:76: mounting "/run/desktop/mnt/host/wsl/docker-desktop-bind-mounts/Ubuntu/30bced14b9bc1133143e6086f29c9ed8d5aa55c5aa4d4cd84a64037bbd9f28ef" to rootfs at "/etc/caddy/Caddyfile" caused: mount through procfd: not a directory: unknown: Are you trying to mount a directory onto a file (or vice-versa)? Check if the specified host path exists and is the expected type
ERROR: for caddy Cannot start service caddy: OCI runtime create failed: container_linux.go:380: starting container process caused: process_linux.go:545: container init caused: rootfs_linux.go:76: mounting "/run/desktop/mnt/host/wsl/docker-desktop-bind-mounts/Ubuntu/30bced14b9bc1133143e6086f29c9ed8d5aa55c5aa4d4cd84a64037bbd9f28ef" to rootfs at "/etc/caddy/Caddyfile" caused: mount through procfd: not a directory: unknown: Are you trying to mount a directory onto a file (or vice-versa)? Check if the specified host path exists and is the expected type
ERROR: Encountered errors while bringing up the project.
The Caddyfile should be in a ./docker
subdirectory yes. This line is how the file is mounted to the container:
volumes:
- './docker/Caddyfile:/etc/caddy/Caddyfile'
This all seems to work until the last two steps. Going to https://localdev.test doesn't work, chrome says "This site can’t provide a secure connection localdev.test sent an invalid response. ERR_SSL_PROTOCOL_ERROR". Any ideas?
UPDATE: Was able to get the root.crt into Keychain and set it to Always Trust. Still getting the same error when trying to access the site in chrome, not sure what I'm missing.
@kimcee ensure that you're pointing to port 443, I had the same issue @ https://localhost:80 but switching to https://localhost:443 resolved it.
This all seems to work until the last two steps. Going to https://localdev.test doesn't work, chrome says "This site can’t provide a secure connection localdev.test sent an invalid response. ERR_SSL_PROTOCOL_ERROR". Any ideas?
UPDATE: Was able to get the root.crt into Keychain and set it to Always Trust. Still getting the same error when trying to access the site in chrome, not sure what I'm missing.
So am I.
Have you changed the values of 'laravel.test' or 'www.laravel.test' in 'authorizedDomains' and 'Caddyfile' to the values you want? (ex: 'https://localdev.test')
// CaddyController.php
...
$authorizedDomains = [
'foo.bar.test',
];
...
// Caddyfile
...
reverse_proxy foo.bar.test {
...
but.. not working...
I see the message 'ERR_SSL_PROTOCOL_ERROR' in Chrome.
If you're seeing ERR_SSL_PROTOCOL_ERROR
it usually means Caddy hasn't been able to generate a certificate for the domain. A good place to start is checking the Caddy logs:
sail logs -f caddy
This usually happens when something either isn't right with your Docker config or something has broken the app itself. For example, Caddy's request to the CaddyController
has returned a 500 error. In this case, you can check your Laravel logs to see what's going. Some examples that I've run into include composer install
needing to be run, php artisan migrate
needing to be run, etc.
thank you. I was able to solve it well.
However, I have a question.
If there are multiple projects, docker volume will be created for each project(ex: foo_sailcaddy, bar_sailcaddy), and multiple caddy root certificates will need to be registered.
Are there any good alternatives to work around this?
Hello and thank you for the great solution. I upvoted and commented on stackoverflow asking about HMR.
I want to say that I got all working after 16 hours with lots of trial/error/debugging/coffee and wine!
The solution works great for local development using Laravel Sail with different service names, totally different ports and even Laravel Mix having Webpack DevServer loading React Fast Refresh through HRM module on different ports.
I've been tricked by the CaddyController
as it executes only once for each domain/subdomain and then it caches the result, so I thought it was not running, but there is no problem with it.
Then I've swapped ports from app service (in my case "someapp.test") to caddy container, change domain names, fetched/convert and install the root.crt
/root.key
> root.p12
and the app got load with HTTPS and with no errors.
For the Laravel Mix Webpack Server HMR and anyone that wants to have it, I'm having a setup like this...
Inside a Mix config file set the hmrOptions
and devServer
options:
mix.options({
hmrOptions: {
host: 'localhost',
port: process.env.DEV_HOT_PORT,
},
});
mix.webpackConfig({
output: {
chunkFilename: `[name].[chunkhash].js`,
},
devServer: {
host: '0.0.0.0',
port: process.env.DEV_HOT_PORT,
},
});
and I'm using @pmmmwh/react-refresh-webpack-plugin
for binding React Refresh with the Webpack dev-server and with .env
settings:
APP_DOMAIN="someapp.test"
DEV_DOMAIN="dev.someapp.test"
DEV_HOT_PORT=12181
DEV_URL="//${DEV_DOMAIN}"
someapp
service inside docker-compose.yml
:
someapp.test:
build:
context: ./docker/8.0
dockerfile: Dockerfile
args:
WWWGROUP: '${WWWGROUP}'
image: sail-8.0/app
ports:
- '${DEV_HOT_PORT:-8080}:${DEV_HOT_PORT:-8080}'
environment:
WWWUSER: '${WWWUSER}'
LARAVEL_SAIL: 1
LANG: 'C.UTF-8'
TERM: 'xterm-256color'
volumes:
- '.:/var/www/html'
- '${LIBRARY_PATH:?LIBRARY_PATH__NOTSET}:/var/www/lib'
networks:
- someappnet
depends_on:
- mariadb
- mongodb
- redis
- meilisearch
caddy:
image: caddy:latest
restart: unless-stopped
ports:
- '${APP_PORT:-80}:80'
- '${APP_SECURE_PORT:-443}:443'
environment:
LARAVEL_SAIL: 1
HOST_DOMAIN: ${APP_DOMAIN}
APP_SECURE_PORT: '${APP_SECURE_PORT}'
DEV_HOT_PORT: '${DEV_HOT_PORT}'
volumes:
- './docker/caddy/Caddyfile:/etc/caddy/Caddyfile'
- './docker/caddy/certificates:/data/caddy/certificates/local'
- './docker/caddy/authorities:/data/caddy/pki/authorities/local'
- 'caddy:/data:cached'
- 'caddy:/config:cached'
networks:
- someappnet
depends_on:
- someapp.test
and inside config/app.php
:
// \Illuminate\Foundation\Mix.php#L35
'mix_hot_proxy_url' => env('DEV_URL', null),
and loading the assets using just mix
Blade directive which is a binding of Illuminate\Foundation\Mix.php
class:
<script src="{{ mix('/js/platform.js') }}" defer></script>
all the scripts will have a URL like https://dev.someapp.test/js/platform.js
and these will be parsed by Webpack dev-server HMR module.
The HMR port has to stay on the service container "someapp.test", because there is where the Webpack dev-server is running and we just need to proxy pass our dev.someapp.test
domain there.
And then inside the Caddyfile
, which we have replace :423
with https://
and inside define macthers for each service subdomain and reverse proxy them to their internal ports:
{
# debug
on_demand_tls {
ask http://{$HOST_DOMAIN}/~~local/caddy-check
}
local_certs
}
https:// {
tls internal {
on_demand
}
# Webpack HMR
@devhmr {
host dev.{$HOST_DOMAIN}
}
reverse_proxy @devhmr {$HOST_DOMAIN}:{$DEV_HOT_PORT} {
# https://caddy.community/t/context-cancelled-when-webpack-hmr-sends-updates-fix/9850
flush_interval -1
}
# PMA
@pma {
host pma.{$HOST_DOMAIN}
}
reverse_proxy @pma phpmyadmin:80
# Mongo Express
@mongo {
host mongoexpress.{$HOST_DOMAIN}
}
reverse_proxy @mongo mongoexpress:8081
# Mailhog
@mailhog {
host mailhog.{$HOST_DOMAIN}
}
reverse_proxy @mailhog mailhog:8025
# Meilisearch
@meilisearch {
host meilisearch.{$HOST_DOMAIN}
}
reverse_proxy @meilisearch meilisearch:7700
# Proxy everything else to Laravel someapp.test service
reverse_proxy {$HOST_DOMAIN}
}
Everything is working now localy on "someapp.test" with valid SSL and React Fast Refresh working through Laravel Mix.
Thanks again!
In case someone is falling over the same thing, I needed to add the namespace for the route in web.php
:
Route::get('/caddy-check', 'App\Http\Controllers\CaddyController@check');
Because I did not have any other web routes or namespace usage in the file.
Thanks for your help.
To enable a real SSL certified you should:
#
# Config that should be executed in the server
#
{
email your_valid@email.com
}
your_domail.com {
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
}
}
I got stuck to understand that in the "reverse_proxy" you MUST define the name of the service you want to reverse proxy. It is not the real domain!
I'm also getting ERR_SSL_PROTOCOL_ERROR (Chrome) or SSL_ERROR_INTERNAL_ERROR_ALERT (Firefox).
It would be nice to know what @dvlpr91 did to solve the issue.
Looking through the caddy logs, the only thing that stands out is:
Warning: "certutil" is not available, install "certutil" with "apt install libnss3-tools" or "yum install nss-tools" and try again.
Neither apt nor yum are in the caddy container.
The /caddy-check end point seems to be working. I copied in all 4 files with little to no changes.
I didn't attempt to trust the keys yet since it doesn't seem to be getting far enough anyway.
Any ideas?
Same problem here. I don't know how to proceed.
I installed a fresh sail project
curl -s "https://laravel.build/example-app" | bash
Add the files, removed the ports of the laravel application, added the namespace in web.php . Changed the Caddy Image Version to caddy:2.4.2 .
Ran
sail composer install && sail composer update && sail artisan migrate
Logs of caddy are:
example-app-caddy-1 | {"level":"info","ts":1647520369.6394668,"msg":"using provided configuration","config_file":"/etc/caddy/Caddyfile","config_adapter":"caddyfile"}
example-app-caddy-1 | 2022/03/17 12:32:49 [WARNING] Unnecessary header_up ('X-Forwarded-Proto' field): the reverse proxy's default behavior is to pass headers to the upstream
example-app-caddy-1 | {"level":"warn","ts":1647520369.6403213,"msg":"input is not formatted with 'caddy fmt'","adapter":"caddyfile","file":"/etc/caddy/Caddyfile","line":2}
example-app-caddy-1 | {"level":"info","ts":1647520369.641571,"logger":"admin","msg":"admin endpoint started","address":"tcp/localhost:2019","enforce_origin":false,"origins":["localhost:2019","[::1]:2019","127.0.0.1:2019"]}
example-app-caddy-1 | {"level":"info","ts":1647520369.6419103,"logger":"tls.cache.maintenance","msg":"started background certificate maintenance","cache":"0xc0000de0e0"}
example-app-caddy-1 | {"level":"info","ts":1647520369.6480498,"logger":"http","msg":"server is listening only on the HTTPS port but has no TLS connection policies; adding one to enable TLS","server_name":"srv0","https_port":443}
example-app-caddy-1 | {"level":"info","ts":1647520369.6480796,"logger":"http","msg":"enabling automatic HTTP->HTTPS redirects","server_name":"srv0"}
example-app-caddy-1 | {"level":"warn","ts":1647520369.6753442,"logger":"pki.ca.local","msg":"installing root certificate (you might be prompted for password)","path":"storage:pki/authorities/local/root.crt"}
example-app-caddy-1 | 2022/03/17 12:32:49 Warning: "certutil" is not available, install "certutil" with "apt install libnss3-tools" or "yum install nss-tools" and try again
example-app-caddy-1 | 2022/03/17 12:32:49 define JAVA_HOME environment variable to use the Java trust
example-app-caddy-1 | 2022/03/17 12:32:49 certificate installed properly in linux trusts
example-app-caddy-1 | {"level":"info","ts":1647520369.699206,"logger":"tls","msg":"cleaning storage unit","description":"FileStorage:/data/caddy"}
example-app-caddy-1 | {"level":"info","ts":1647520369.6992402,"logger":"tls","msg":"finished cleaning storage units"}
example-app-caddy-1 | {"level":"info","ts":1647520369.699266,"msg":"autosaved config (load with --resume flag)","file":"/config/caddy/autosave.json"}
example-app-caddy-1 | {"level":"info","ts":1647520369.6992774,"msg":"serving initial configuration"}
```shell DEV_URL="//${DEV_DOMAIN}"
Hi @clytras ,
I've been trying to set up the HMR based on your comment.
The https://dev.someapp.test/js/platform.js
return 502 for me.
Do you have any idea what I am doing wrong?
Many thanks
ERROR: for fible_caddy_1 Cannot start service caddy: OCI runtime create failed: container_linux.go:380: starting container process caused: process_linux.go:545: container init caused: rootfs_linux.go:76: mounting "/run/desktop/mnt/host/wsl/docker-desktop-bind-mounts/Ubuntu/91c064ef9216261a3f192079b2607b6ab73173626592a27b5f976cffbb10215e" to rootfs at "/etc/caddy/Caddyfile" caused: mount through procfd: not a directory: unknown: Are you trying to mount a directory onto a file (or vice-versa)? Check if the specified host path exists and is the expected type
ERROR: for caddy Cannot start service caddy: OCI runtime create failed: container_linux.go:380: starting container process caused: process_linux.go:545: container init caused: rootfs_linux.go:76: mounting "/run/desktop/mnt/host/wsl/docker-desktop-bind-mounts/Ubuntu/91c064ef9216261a3f192079b2607b6ab73173626592a27b5f976cffbb10215e" to rootfs at "/etc/caddy/Caddyfile" caused: mount through procfd: not a directory: unknown: Are you trying to mount a directory onto a file (or vice-versa)? Check if the specified host path exists and is the expected type
ERROR: Encountered errors while bringing up the project.
I run into this problem shown above. I saw in some previous comment that the Caddyfile is supposed to be placed in the docker directory in the root of the project. So I did that, but still having the same exact error. Now my Caddyfile has the content shown above as in the Github file in the directory ProjectName/docker/Caddyfile.
Am I missing something else?
Thanks