mirror of
https://git.proxmox.com/git/pve-http-server
synced 2025-05-28 08:25:55 +00:00
fix #4494: redirect HTTP to HTTPS
Allow HTTP connections up until the request's header has been parsed and processed. If no TLS handshake has been completed beforehand, the server now responds with either a '301 Moved Permanently' or a '308 Permanent Redirect' as noted in the MDN web docs[1]. This is done after the header was parsed; for the redirect to work, the `Host` header field of the request is used to create the `Location` field of the response. This makes redirections independent of how the server is accessed (e.g. via IP, localhost, FQDN, ...) possible. Upon redirection the client is immediately disconnected; otherwise, they would have to wait for the connection to time out until they may reconnect via TLS again. [1] https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/301 Signed-off-by: Max Carrara <m.carrara@proxmox.com>
This commit is contained in:
parent
f2e54bb78a
commit
933a4dbbaf
@ -1314,7 +1314,7 @@ sub unshift_read_header {
|
||||
if $state->{key};
|
||||
|
||||
$self->process_header($reqstate) or return;
|
||||
# header processing complete - authenticate now
|
||||
$self->ensure_tls_connection($reqstate) or return;
|
||||
$self->authenticate_and_handle_request($reqstate) or return;
|
||||
|
||||
} elsif ($line =~ /^([^:\s]+)\s*:\s*(.*)/) {
|
||||
@ -1384,6 +1384,43 @@ sub process_header {
|
||||
return 1;
|
||||
}
|
||||
|
||||
sub ensure_tls_connection {
|
||||
my ($self, $reqstate) = @_;
|
||||
|
||||
# Skip if server doesn't use TLS
|
||||
if (!$self->{tls_ctx}) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
# TLS session exists, so the handshake has succeeded
|
||||
if ($reqstate->{hdl}->{tls}) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
my $request = $reqstate->{request};
|
||||
my $method = $request->method();
|
||||
|
||||
my $h_host = $reqstate->{request}->header('Host');
|
||||
|
||||
die "Header field 'Host' not found in request\n"
|
||||
if !$h_host;
|
||||
|
||||
my $secure_host = "https://" . ($h_host =~ s/^http(s)?:\/\///r);
|
||||
|
||||
my $header = HTTP::Headers->new('Location' => $secure_host . $request->uri());
|
||||
|
||||
if ($method eq 'GET' || $method eq 'HEAD') {
|
||||
$self->error($reqstate, 301, 'Moved Permanently', $header);
|
||||
} else {
|
||||
$self->error($reqstate, 308, 'Permanent Redirect', $header);
|
||||
}
|
||||
|
||||
# disconnect the client so they may immediately connect again via HTTPS
|
||||
$self->client_do_disconnect($reqstate);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
sub authenticate_and_handle_request {
|
||||
my ($self, $reqstate) = @_;
|
||||
|
||||
@ -1791,11 +1828,16 @@ sub accept_connections {
|
||||
};
|
||||
if (my $err = $@) { syslog('err', "$err"); }
|
||||
},
|
||||
($self->{tls_ctx} ? (tls => "accept", tls_ctx => $self->{tls_ctx}) : ()));
|
||||
);
|
||||
$handle_creation = 0;
|
||||
|
||||
$self->dprint("ACCEPT FH" . $clientfh->fileno() . " CONN$self->{conn_count}");
|
||||
|
||||
if ($self->{tls_ctx}) {
|
||||
$self->dprint("Setting TLS to autostart");
|
||||
$reqstate->{hdl}->unshift_read(tls_autostart => $self->{tls_ctx}, "accept");
|
||||
}
|
||||
|
||||
$self->push_request_header($reqstate);
|
||||
}
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user