Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 17 additions & 1 deletion app/Services/Webserver/Nginx.php
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,7 @@ public function version(): string

public function logs(): array
{
return [
$logs = [
new ServiceLog(
key: 'nginx:error',
serviceLabel: 'NGINX',
Expand All @@ -291,5 +291,21 @@ public function logs(): array
target: '/var/log/nginx/access.log',
),
];

$sites = $this->service->server->relationLoaded('sites')
? $this->service->server->sites->sortBy('id')
: $this->service->server->sites()->orderBy('id')->get(['id', 'domain']);

foreach ($sites as $site) {
$logs[] = new ServiceLog(
key: 'nginx:site:'.$site->id.':error',
serviceLabel: 'NGINX',
label: $site->domain.' error log',
source: ServiceLog::SOURCE_FILE,
target: '/var/log/nginx/'.$site->domain.'-error.log',
);
}

return $logs;
}
}
15 changes: 14 additions & 1 deletion resources/views/ssh/services/webserver/nginx/vhost.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,20 @@ server {
listen [::]:80;

server_name{{#force_ssl_domains}} {{name}}{{/force_ssl_domains}};
return 301 https://$host$request_uri;

{{#verification_key}}
location ^~ /.well-known/vito/{{verification_key}}/ {
allow all;
default_type text/plain;
add_header Cache-Control "no-store" always;
alias /var/lib/vito/verify/{{verification_key}}/;
try_files $uri =404;
}
{{/verification_key}}

location / {
return 301 https://$host$request_uri;
}
}
{{/has_force_ssl_redirect}}

Expand Down
14 changes: 14 additions & 0 deletions tests/Feature/ServiceLogsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,20 @@ public function test_renders_catalogue_for_installed_services(): void
$this->assertContains('php:8.2:user:vito', $keys);
}

public function test_nginx_exposes_per_site_error_log(): void
{
$this->actingAs($this->user);

$response = $this->get(route('logs.services', $this->server));

$catalogue = $response->viewData('page')['props']['catalogue'];
$entries = collect($catalogue)->keyBy('key');

$key = 'nginx:site:'.$this->site->id.':error';
$this->assertTrue($entries->has($key));
$this->assertSame('/var/log/nginx/'.$this->site->domain.'-error.log', $entries[$key]['display_target']);
}

public function test_services_without_has_logs_are_skipped(): void
{
$this->actingAs($this->user);
Expand Down
36 changes: 36 additions & 0 deletions tests/Feature/Webserver/VerificationBlockTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@

use App\Actions\Webserver\GenerateNginxConfig;
use App\Enums\ServiceStatus;
use App\Enums\SslStatus;
use App\Models\HostedDomain;
use App\Models\Service;
use App\Models\Ssl;
use App\Services\Webserver\Caddy;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
Expand Down Expand Up @@ -89,6 +91,40 @@ public function test_caddy_renders_verification_handle_when_key_present(): void
$this->assertStringContainsString('root * /var/lib/vito/verify/caddyKey99', $vhost);
}

public function test_nginx_force_ssl_redirect_serves_and_exempts_verification_challenge(): void
{
$ssl = Ssl::factory()->create([
'server_id' => $this->server->id,
'site_id' => $this->site->id,
'status' => SslStatus::CREATED,
'type' => 'letsencrypt',
'domains' => [$this->site->domain],
]);

HostedDomain::factory()->primary()->create([
'site_id' => $this->site->id,
'domain' => $this->site->domain,
'ssl_id' => $ssl->id,
]);

$this->site->update([
'ssl_enabled' => true,
'force_ssl' => true,
'verification_key' => 'forcedKey55',
]);
$this->site->refresh();

$vhost = $this->site->webserver()->generateVhost($this->site);

$this->assertStringContainsString('location ^~ /.well-known/vito/forcedKey55/', $vhost);
$this->assertStringContainsString('alias /var/lib/vito/verify/forcedKey55/', $vhost);
$this->assertStringContainsString(
"location / {\n return 301 https://\$host\$request_uri;\n }",
$vhost,
'The force-SSL port-80 redirect must be scoped to location / so the verification path is served instead of 301-redirected.'
);
}

public function test_caddy_serves_verification_over_http_when_using_auto_https(): void
{
$this->switchToCaddy();
Expand Down
Loading