diff --git a/doc/man/cockpit-ws.xml b/doc/man/cockpit-ws.xml
index d40fa964f..d9d56166a 100644
--- a/doc/man/cockpit-ws.xml
+++ b/doc/man/cockpit-ws.xml
@@ -42,6 +42,7 @@
PORT
ADDRESS
+
BRIDGE
@@ -158,6 +159,23 @@ getcert request -f ${CERT_FILE} -k ${KEY_FILE} -D $(hostname --fqdn) -C "sed -n
+
+
+
+
+ Tell cockpit-ws that it is running behind a local reverse proxy that
+ does the TLS termination. Then Cockpit accepts only https:// origins, instead of http:
+ ones by default. However, if Origins is set in the
+
+ cockpit.conf5
+
+ configuration file, it will override this default.
+
+
+ This option implies .
+
+
+
diff --git a/src/common/cockpitwebserver.c b/src/common/cockpitwebserver.c
index 865ce9a8a..b11fb5f01 100644
--- a/src/common/cockpitwebserver.c
+++ b/src/common/cockpitwebserver.c
@@ -56,6 +56,7 @@ struct _CockpitWebServer {
gint request_timeout;
gint request_max;
gboolean redirect_tls;
+ gboolean for_tls_proxy;
GSocketService *socket_service;
GMainContext *main_context;
@@ -88,6 +89,7 @@ enum
PROP_SSL_EXCEPTION_PREFIX,
PROP_SOCKET_ACTIVATED,
PROP_REDIRECT_TLS,
+ PROP_FOR_TLS_PROXY,
PROP_URL_ROOT,
};
@@ -183,6 +185,10 @@ cockpit_web_server_get_property (GObject *object,
g_value_set_boolean (value, server->redirect_tls);
break;
+ case PROP_FOR_TLS_PROXY:
+ g_value_set_boolean (value, server->for_tls_proxy);
+ break;
+
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -247,6 +253,10 @@ cockpit_web_server_set_property (GObject *object,
server->redirect_tls = g_value_get_boolean (value);
break;
+ case PROP_FOR_TLS_PROXY:
+ server->for_tls_proxy = g_value_get_boolean (value);
+ break;
+
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -432,6 +442,13 @@ cockpit_web_server_class_init (CockpitWebServerClass *klass)
g_param_spec_boolean ("redirect-tls", NULL, NULL, TRUE,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (gobject_class, PROP_FOR_TLS_PROXY,
+ g_param_spec_boolean ("for-tls-proxy", NULL, NULL, TRUE,
+ G_PARAM_READABLE |
+ G_PARAM_WRITABLE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+
sig_handle_stream = g_signal_new ("handle-stream",
G_OBJECT_CLASS_TYPE (klass),
G_SIGNAL_RUN_LAST,
@@ -462,12 +479,13 @@ cockpit_web_server_class_init (CockpitWebServerClass *klass)
COCKPIT_TYPE_WEB_RESPONSE);
}
-CockpitWebServer *
-cockpit_web_server_new (const gchar *address,
- gint port,
- GTlsCertificate *certificate,
- GCancellable *cancellable,
- GError **error)
+static CockpitWebServer *
+cockpit_web_server_new_internal (const gchar *address,
+ gint port,
+ GTlsCertificate *certificate,
+ gboolean for_tls_proxy,
+ GCancellable *cancellable,
+ GError **error)
{
GInitable *initable;
initable = g_initable_new (COCKPIT_TYPE_WEB_SERVER,
@@ -476,6 +494,7 @@ cockpit_web_server_new (const gchar *address,
"port", port,
"address", address,
"certificate", certificate,
+ "for-tls-proxy", for_tls_proxy,
NULL);
if (initable != NULL)
return COCKPIT_WEB_SERVER (initable);
@@ -483,6 +502,26 @@ cockpit_web_server_new (const gchar *address,
return NULL;
}
+CockpitWebServer *
+cockpit_web_server_new (const gchar *address,
+ gint port,
+ GTlsCertificate *certificate,
+ GCancellable *cancellable,
+ GError **error)
+{
+ return cockpit_web_server_new_internal (address, port, certificate, FALSE, cancellable, error);
+}
+
+CockpitWebServer *
+cockpit_web_server_new_for_tls_proxy (const gchar *address,
+ gint port,
+ GTlsCertificate *certificate,
+ GCancellable *cancellable,
+ GError **error)
+{
+ return cockpit_web_server_new_internal (address, port, certificate, TRUE, cancellable, error);
+}
+
void
cockpit_web_server_start (CockpitWebServer *self)
{
@@ -522,6 +561,14 @@ cockpit_web_server_get_redirect_tls (CockpitWebServer *self)
return self->redirect_tls;
}
+gboolean
+cockpit_web_server_get_for_tls_proxy (CockpitWebServer *self)
+{
+ g_return_val_if_fail (COCKPIT_IS_WEB_SERVER (self), FALSE);
+
+ return self->for_tls_proxy;
+}
+
GHashTable *
cockpit_web_server_new_table (void)
{
diff --git a/src/common/cockpitwebserver.h b/src/common/cockpitwebserver.h
index 1d7e9b86a..f856c14c4 100644
--- a/src/common/cockpitwebserver.h
+++ b/src/common/cockpitwebserver.h
@@ -41,6 +41,12 @@ CockpitWebServer * cockpit_web_server_new (const gchar *address,
GCancellable *cancellable,
GError **error);
+CockpitWebServer * cockpit_web_server_new_for_tls_proxy (const gchar *address,
+ gint port,
+ GTlsCertificate *certificate,
+ GCancellable *cancellable,
+ GError **error);
+
void cockpit_web_server_start (CockpitWebServer *self);
gboolean cockpit_web_server_add_socket (CockpitWebServer *self,
@@ -64,6 +70,8 @@ void cockpit_web_server_set_redirect_tls (CockpitWebServer *se
gboolean cockpit_web_server_get_redirect_tls (CockpitWebServer *self);
+gboolean cockpit_web_server_get_for_tls_proxy (CockpitWebServer *self);
+
G_END_DECLS
#endif /* __COCKPIT_WEB_SERVER_H__ */
diff --git a/src/common/test-webserver.c b/src/common/test-webserver.c
index c73bb715d..46c919c5d 100644
--- a/src/common/test-webserver.c
+++ b/src/common/test-webserver.c
@@ -39,6 +39,7 @@ typedef struct {
const gchar *cert_file;
gboolean local_only;
gboolean inet_only;
+ gboolean for_tls_proxy;
} TestFixture;
#define SKIP_NO_HOSTPORT if (!tc->hostport) { cockpit_test_skip ("No non-loopback network interface available"); return; }
@@ -73,7 +74,10 @@ setup (TestCase *tc,
else
address = NULL;
- tc->web_server = cockpit_web_server_new (address, 0, cert, NULL, &error);
+ if (fixture && fixture->for_tls_proxy)
+ tc->web_server = cockpit_web_server_new_for_tls_proxy (address, 0, NULL, NULL, &error);
+ else
+ tc->web_server = cockpit_web_server_new (address, 0, cert, NULL, &error);
g_assert_no_error (error);
g_clear_object (&cert);
@@ -458,6 +462,8 @@ test_webserver_redirect_notls (TestCase *tc,
SKIP_NO_HOSTPORT;
+ g_assert (!cockpit_web_server_get_for_tls_proxy (tc->web_server));
+
g_signal_connect (tc->web_server, "handle-resource", G_CALLBACK (on_shell_index_html), NULL);
resp = perform_http_request (tc->hostport, "GET /shell/index.html HTTP/1.0\r\nHost:test\r\n\r\n", NULL);
cockpit_assert_strmatch (resp, "HTTP/* 301 *\r\nLocation: https://*");
@@ -856,6 +862,28 @@ test_bad_address (TestCase *tc,
g_object_unref (server);
}
+static const TestFixture fixture_for_tls_proxy = {
+ .cert_file = SRCDIR "/src/ws/mock_cert",
+ .for_tls_proxy = TRUE
+};
+
+static void
+test_webserver_for_tls_proxy (TestCase *tc,
+ gconstpointer data)
+{
+ gchar *resp;
+
+ SKIP_NO_HOSTPORT;
+
+ g_assert (cockpit_web_server_get_for_tls_proxy (tc->web_server));
+
+ /* should not redirect even with a certificate present */
+ g_signal_connect (tc->web_server, "handle-resource", G_CALLBACK (on_shell_index_html), NULL);
+ resp = perform_http_request (tc->hostport, "GET /shell/index.html HTTP/1.0\r\nHost:test\r\n\r\n", NULL);
+ cockpit_assert_strmatch (resp, "HTTP/* 200 *\r\n*");
+ g_free (resp);
+}
+
static const TestFixture fixture_inet_address = {
.inet_only = TRUE
};
@@ -920,5 +948,8 @@ main (int argc,
g_test_add ("/web-server/bad-address", TestCase, NULL,
NULL, test_bad_address, NULL);
+ g_test_add ("/web-server/for-tls-proxy", TestCase, &fixture_for_tls_proxy,
+ setup, test_webserver_for_tls_proxy, teardown);
+
return g_test_run ();
}
diff --git a/src/ws/cockpitchannelsocket.c b/src/ws/cockpitchannelsocket.c
index 9950a6d68..f4dbdcf1a 100644
--- a/src/ws/cockpitchannelsocket.c
+++ b/src/ws/cockpitchannelsocket.c
@@ -191,7 +191,8 @@ cockpit_channel_socket_open (CockpitWebService *service,
const gchar *path,
GIOStream *io_stream,
GHashTable *headers,
- GByteArray *input_buffer)
+ GByteArray *input_buffer,
+ gboolean for_tls_proxy)
{
CockpitChannelSocket *self = NULL;
WebSocketDataType data_type;
@@ -225,7 +226,7 @@ cockpit_channel_socket_open (CockpitWebService *service,
self->data_type = data_type;
self->socket = cockpit_web_service_create_socket ((const gchar **)protocols, original_path,
- io_stream, headers, input_buffer);
+ io_stream, headers, input_buffer, for_tls_proxy);
self->socket_open = g_signal_connect (self->socket, "open", G_CALLBACK (on_socket_open), self);
self->socket_message = g_signal_connect (self->socket, "message", G_CALLBACK (on_socket_message), self);
self->socket_close = g_signal_connect (self->socket, "close", G_CALLBACK (on_socket_close), self);
diff --git a/src/ws/cockpitchannelsocket.h b/src/ws/cockpitchannelsocket.h
index a9912afd4..8e283b7ea 100644
--- a/src/ws/cockpitchannelsocket.h
+++ b/src/ws/cockpitchannelsocket.h
@@ -31,7 +31,8 @@ void cockpit_channel_socket_open (CockpitWebService *service
const gchar *path,
GIOStream *io_stream,
GHashTable *headers,
- GByteArray *input_buffer);
+ GByteArray *input_buffer,
+ gboolean for_tls_proxy);
G_END_DECLS
diff --git a/src/ws/cockpithandlers.c b/src/ws/cockpithandlers.c
index 53d770e36..6832c4cf4 100644
--- a/src/ws/cockpithandlers.c
+++ b/src/ws/cockpithandlers.c
@@ -66,11 +66,12 @@ static void
handle_noauth_socket (GIOStream *io_stream,
const gchar *path,
GHashTable *headers,
- GByteArray *input_buffer)
+ GByteArray *input_buffer,
+ gboolean for_tls_proxy)
{
WebSocketConnection *connection;
- connection = cockpit_web_service_create_socket (NULL, path, io_stream, headers, input_buffer);
+ connection = cockpit_web_service_create_socket (NULL, path, io_stream, headers, input_buffer, for_tls_proxy);
g_signal_connect (connection, "open", G_CALLBACK (on_web_socket_noauth), NULL);
@@ -113,12 +114,12 @@ cockpit_handler_socket (CockpitWebServer *server,
service = cockpit_auth_check_cookie (ws->auth, path, headers);
if (service)
{
- cockpit_web_service_socket (service, path, io_stream, headers, input);
+ cockpit_web_service_socket (service, path, io_stream, headers, input, cockpit_web_server_get_for_tls_proxy (server));
g_object_unref (service);
}
else
{
- handle_noauth_socket (io_stream, path, headers, input);
+ handle_noauth_socket (io_stream, path, headers, input, cockpit_web_server_get_for_tls_proxy (server));
}
return TRUE;
@@ -210,7 +211,8 @@ cockpit_handler_external (CockpitWebServer *server,
upgrade = g_hash_table_lookup (headers, "Upgrade");
if (upgrade && g_ascii_strcasecmp (upgrade, "websocket") == 0)
{
- cockpit_channel_socket_open (service, open, original_path, path, io_stream, headers, input);
+ cockpit_channel_socket_open (service, open, original_path, path, io_stream, headers, input,
+ cockpit_web_server_get_for_tls_proxy (server));
}
else
{
diff --git a/src/ws/cockpitwebservice.c b/src/ws/cockpitwebservice.c
index 84c401a92..6bbffb9eb 100644
--- a/src/ws/cockpitwebservice.c
+++ b/src/ws/cockpitwebservice.c
@@ -1298,7 +1298,8 @@ cockpit_web_service_create_socket (const gchar **protocols,
const gchar *path,
GIOStream *io_stream,
GHashTable *headers,
- GByteArray *input_buffer)
+ GByteArray *input_buffer,
+ gboolean for_tls_proxy)
{
WebSocketConnection *connection;
const gchar *host = NULL;
@@ -1307,7 +1308,7 @@ cockpit_web_service_create_socket (const gchar **protocols,
gchar *allocated = NULL;
gchar *origin = NULL;
gchar *defaults[2];
- gboolean secure;
+ gboolean is_https;
gchar *url;
g_return_val_if_fail (path != NULL, NULL);
@@ -1328,17 +1329,19 @@ cockpit_web_service_create_socket (const gchar **protocols,
protocol = cockpit_connection_get_protocol (io_stream, headers);
}
- secure = g_strcmp0 (protocol, "https") == 0;
+ g_debug("cockpit_web_service_create_socket: host %s, protocol %s, for_tls_proxy %i", host, protocol, for_tls_proxy);
+
+ is_https = g_strcmp0 (protocol, "https") == 0 || for_tls_proxy;
url = g_strdup_printf ("%s://%s%s",
- secure ? "wss" : "ws",
+ is_https ? "wss" : "ws",
host ? host : "localhost",
path);
origins = cockpit_conf_strv ("WebService", "Origins", ' ');
if (origins == NULL)
{
- origin = g_strdup_printf ("%s://%s", secure ? "https" : "http", host);
+ origin = g_strdup_printf ("%s://%s", is_https ? "https" : "http", host);
defaults[0] = origin;
defaults[1] = NULL;
origins = (const gchar **)defaults;
@@ -1360,6 +1363,8 @@ cockpit_web_service_create_socket (const gchar **protocols,
* @input_buffer: optional bytes already parsed after headers
* @auth: authentication object
* @creds: credentials of user or NULL for failed auth
+ * @for_tls_proxy: Assume that the Browser is making TLS connections that are terminated
+ * in a reverse proxy in front of cockpit-ws
*
* Serves the WebSocket on the given web service. Holds an extra
* reference to the web service until the socket is closed.
@@ -1369,12 +1374,13 @@ cockpit_web_service_socket (CockpitWebService *self,
const gchar *path,
GIOStream *io_stream,
GHashTable *headers,
- GByteArray *input_buffer)
+ GByteArray *input_buffer,
+ gboolean for_tls_proxy)
{
const gchar *protocols[] = { "cockpit1", NULL };
WebSocketConnection *connection;
- connection = cockpit_web_service_create_socket (protocols, path, io_stream, headers, input_buffer);
+ connection = cockpit_web_service_create_socket (protocols, path, io_stream, headers, input_buffer, for_tls_proxy);
g_signal_connect (connection, "open", G_CALLBACK (on_web_socket_open), self);
g_signal_connect (connection, "closing", G_CALLBACK (on_web_socket_closing), self);
diff --git a/src/ws/cockpitwebservice.h b/src/ws/cockpitwebservice.h
index 37f9e7c34..c6d1ca606 100644
--- a/src/ws/cockpitwebservice.h
+++ b/src/ws/cockpitwebservice.h
@@ -47,7 +47,8 @@ void cockpit_web_service_socket (CockpitWebService *self,
const gchar *path,
GIOStream *io_stream,
GHashTable *headers,
- GByteArray *input_buffer);
+ GByteArray *input_buffer,
+ gboolean for_tls_proxy);
CockpitCreds * cockpit_web_service_get_creds (CockpitWebService *self);
@@ -57,7 +58,8 @@ WebSocketConnection * cockpit_web_service_create_socket (const gchar **prot
const gchar *path,
GIOStream *io_stream,
GHashTable *headers,
- GByteArray *input_buffer);
+ GByteArray *input_buffer,
+ gboolean for_tls_proxy);
gchar * cockpit_web_service_unique_channel (CockpitWebService *self);
diff --git a/src/ws/main.c b/src/ws/main.c
index 2cbf8e7c6..3a6123a77 100644
--- a/src/ws/main.c
+++ b/src/ws/main.c
@@ -44,6 +44,7 @@
static gint opt_port = 9090;
static gchar *opt_address = NULL;
static gboolean opt_no_tls = FALSE;
+static gboolean opt_for_tls_proxy = FALSE;
static gboolean opt_local_ssh = FALSE;
static gchar *opt_local_session = NULL;
static gboolean opt_version = FALSE;
@@ -52,6 +53,9 @@ static GOptionEntry cmd_entries[] = {
{"port", 'p', 0, G_OPTION_ARG_INT, &opt_port, "Local port to bind to (9090 if unset)", NULL},
{"address", 'a', 0, G_OPTION_ARG_STRING, &opt_address, "Address to bind to (binds on all addresses if unset)", "ADDRESS"},
{"no-tls", 0, 0, G_OPTION_ARG_NONE, &opt_no_tls, "Don't use TLS", NULL},
+ {"for-tls-proxy", 0, 0, G_OPTION_ARG_NONE, &opt_for_tls_proxy,
+ "Act behind a https-terminating proxy: accept only https:// origins by default; implies --no-tls",
+ NULL},
{"local-ssh", 0, 0, G_OPTION_ARG_NONE, &opt_local_ssh, "Log in locally via SSH", NULL },
{"local-session", 0, 0, G_OPTION_ARG_STRING, &opt_local_session,
"Launch a bridge in the local session (path to cockpit-bridge or '-' for stdin/out); implies --no-tls",
@@ -155,6 +159,9 @@ main (int argc,
goto out;
}
+ if (opt_for_tls_proxy)
+ opt_no_tls = TRUE;
+
/*
* This process talks on stdin/stdout. However lots of stuff wants to write
* to stdout, such as g_debug, and uses fd 1 to do that. Reroute fd 1 so that
@@ -195,11 +202,22 @@ main (int argc,
login_po_html = g_strdup (DATADIR "/cockpit/static/login.po.html");
data.login_po_html = (const gchar *)login_po_html;
- server = cockpit_web_server_new (opt_address,
- opt_port,
- certificate,
- NULL,
- error);
+ if (opt_for_tls_proxy)
+ {
+ server = cockpit_web_server_new_for_tls_proxy (opt_address,
+ opt_port,
+ certificate,
+ NULL,
+ error);
+ }
+ else
+ {
+ server = cockpit_web_server_new (opt_address,
+ opt_port,
+ certificate,
+ NULL,
+ error);
+ }
if (server == NULL)
{
g_prefix_error (error, "Error starting web server: ");
diff --git a/src/ws/test-handlers.c b/src/ws/test-handlers.c
index 2d9b873da..6ac47a550 100644
--- a/src/ws/test-handlers.c
+++ b/src/ws/test-handlers.c
@@ -764,6 +764,7 @@ on_message_get_bytes (WebSocketConnection *ws,
static void
test_socket_unauthenticated (void)
{
+ CockpitWebServer *server;
WebSocketConnection *client;
GBytes *received = NULL;
GIOStream *io_a, *io_b;
@@ -773,9 +774,13 @@ test_socket_unauthenticated (void)
const gchar *unused;
gchar *channel;
JsonObject *options;
+ GError *error = NULL;
make_io_streams (&io_a, &io_b);
+ server = cockpit_web_server_new (NULL, 0, NULL, NULL, &error);
+ g_assert_no_error (error);
+
client = g_object_new (WEB_SOCKET_TYPE_CLIENT,
"url", "ws://127.0.0.1/unused",
"origin", "http://127.0.0.1",
@@ -787,7 +792,7 @@ test_socket_unauthenticated (void)
/* Matching the above origin */
cockpit_ws_default_host_header = "127.0.0.1";
- g_assert (cockpit_handler_socket (NULL, "/cockpit/socket", "/cockpit/socket",
+ g_assert (cockpit_handler_socket (server, "/cockpit/socket", "/cockpit/socket",
"GET", io_b, NULL, NULL, NULL));
g_signal_connect (client, "message", G_CALLBACK (on_message_get_bytes), &received);
@@ -818,6 +823,7 @@ test_socket_unauthenticated (void)
g_object_unref (io_a);
g_object_unref (io_b);
+ g_object_unref (server);
}
int
diff --git a/src/ws/test-server.c b/src/ws/test-server.c
index d4c681524..bbdc84799 100644
--- a/src/ws/test-server.c
+++ b/src/ws/test-server.c
@@ -390,7 +390,7 @@ on_handle_stream_socket (CockpitWebServer *server,
g_signal_handler_disconnect (transport, handler);
}
- cockpit_web_service_socket (service, path, io_stream, headers, input);
+ cockpit_web_service_socket (service, path, io_stream, headers, input, FALSE /* for_tls_proxy */);
/* Keeps ref on itself until it closes */
g_object_unref (service);
@@ -506,7 +506,7 @@ on_handle_stream_external (CockpitWebServer *server,
upgrade = g_hash_table_lookup (headers, "Upgrade");
if (upgrade && g_ascii_strcasecmp (upgrade, "websocket") == 0)
{
- cockpit_channel_socket_open (service, open, path, path, io_stream, headers, input);
+ cockpit_channel_socket_open (service, open, path, path, io_stream, headers, input, FALSE /* for_tls_proxy */);
handled = TRUE;
}
else
diff --git a/src/ws/test-webservice.c b/src/ws/test-webservice.c
index e716310a4..012072a6d 100644
--- a/src/ws/test-webservice.c
+++ b/src/ws/test-webservice.c
@@ -81,6 +81,7 @@ typedef struct {
const char *config;
const char *forward;
const char *bridge;
+ gboolean for_tls_proxy;
} TestFixture;
static gboolean
@@ -432,7 +433,7 @@ start_web_service_and_create_client (TestCase *test,
g_main_context_iteration (NULL, TRUE);
/* Note, we are forcing the websocket to parse its own headers */
- cockpit_web_service_socket (*service, "/unused", test->io_b, NULL, NULL);
+ cockpit_web_service_socket (*service, "/unused", test->io_b, NULL, NULL, fixture ? fixture->for_tls_proxy : FALSE);
g_signal_handler_disconnect (test->mock_bridge, handler);
}
@@ -863,6 +864,11 @@ static const TestFixture fixture_allowed_origin_proto_header = {
.config = SRCDIR "/src/ws/mock-config/cockpit/cockpit-alt.conf"
};
+static const TestFixture fixture_allowed_origin_tls_proxy = {
+ .origin = "https://127.0.0.1",
+ .for_tls_proxy = TRUE,
+};
+
static const TestFixture fixture_bad_origin_proto_no_header = {
.origin = "https://127.0.0.1",
.config = SRCDIR "/src/ws/mock-config/cockpit/cockpit-alt.conf"
@@ -874,6 +880,11 @@ static const TestFixture fixture_bad_origin_proto_no_config = {
.config = NULL
};
+static const TestFixture fixture_bad_origin_tls_proxy = {
+ .origin = "http://127.0.0.1",
+ .for_tls_proxy = TRUE,
+};
+
static void
test_bad_origin (TestCase *test,
gconstpointer data)
@@ -1107,7 +1118,7 @@ test_idling (TestCase *test,
g_signal_connect (service, "idling", G_CALLBACK (on_idling_set_flag), &flag);
g_assert (cockpit_web_service_get_idling (service));
- cockpit_web_service_socket (service, "/unused", test->io_b, NULL, NULL);
+ cockpit_web_service_socket (service, "/unused", test->io_b, NULL, NULL, FALSE);
g_assert (!cockpit_web_service_get_idling (service));
while (web_socket_connection_get_ready_state (client) == WEB_SOCKET_STATE_CONNECTING)
@@ -1157,7 +1168,7 @@ test_dispose (TestCase *test,
g_object_unref (transport);
g_object_unref (pipe);
- cockpit_web_service_socket (service, "/unused", test->io_b, NULL, NULL);
+ cockpit_web_service_socket (service, "/unused", test->io_b, NULL, NULL, FALSE);
while (web_socket_connection_get_ready_state (client) == WEB_SOCKET_STATE_CONNECTING)
g_main_context_iteration (NULL, TRUE);
@@ -1515,6 +1526,12 @@ main (int argc,
g_test_add ("/web-service/allowed-origin/protocol-header", TestCase,
&fixture_allowed_origin_proto_header, setup_for_socket,
test_handshake_and_auth, teardown_for_socket);
+ g_test_add ("/web-service/allowed-origin/tls-proxy", TestCase,
+ &fixture_allowed_origin_tls_proxy, setup_for_socket,
+ test_handshake_and_auth, teardown_for_socket);
+ g_test_add ("/web-service/bad-origin/tls-proxy", TestCase,
+ &fixture_bad_origin_tls_proxy, setup_for_socket,
+ test_bad_origin, teardown_for_socket);
g_test_add ("/web-service/close-error", TestCase,
NULL, setup_for_socket,
diff --git a/test/verify/check-connection b/test/verify/check-connection
index cd54a1cc5..837c3d56c 100755
--- a/test/verify/check-connection
+++ b/test/verify/check-connection
@@ -437,6 +437,68 @@ G_MESSAGES_DEBUG=all XDG_CONFIG_DIRS=/usr/local %s -p 9999 -a 127.0.0.90 --local
self.allow_journal_messages("couldn't register polkit authentication agent.*")
+ @skipImage("missing socat", "fedora-atomic", "rhel-atomic", "continuous-atomic", "centos-7")
+ @skipImage("Added in PR #11813", "rhel-7-6-distropkg", "rhel-8-0-distropkg")
+ def testReverseTlsProxy(self):
+ m = self.machine
+ b = self.browser
+
+ # set up a poor man's reverse TLS proxy with socat
+ m.upload(["../src/bridge/mock-server.crt", "../src/bridge/mock-server.key"], "/tmp")
+ m.spawn("socat OPENSSL-LISTEN:9090,reuseaddr,fork,cert=/tmp/mock-server.crt,"
+ "key=/tmp/mock-server.key,verify=0 TCP:localhost:9099",
+ "socat.log")
+
+ # ws with plain --no-tls should fail after login with mismatching Origin (expected http, got https)
+ m.spawn("su -s /bin/sh -c '%s --no-tls -p 9099 -a 127.0.0.1' cockpit-ws" % self.ws_executable,
+ "ws-notls.log")
+ m.wait_for_cockpit_running(tls=True)
+
+ b.ignore_ssl_certificate_errors(True)
+ b.open("https://%s:%s/system" % (b.address, b.port))
+ b.wait_visible("#login")
+ b.set_val("#login-user-input", "admin")
+ b.set_val("#login-password-input", "foobar")
+ b.click('#login-button')
+ b.expect_load()
+
+ def check_wss_log():
+ for log in self.browser.get_js_log():
+ if 'Error during WebSocket handshake: Unexpected response code: 403' in log:
+ return True
+ return False
+ wait(check_wss_log)
+
+ wait(lambda: "received request from bad Origin" in m.execute("journalctl -b -t cockpit-ws"))
+
+ # sanity check: HTTP does not work
+ m.execute("! curl http://localhost:9090")
+
+ m.execute("pkill -e cockpit-ws; while pgrep -a cockpit-ws; do sleep 1; done")
+ # this page failure is reeally noisy
+ self.allow_authorize_journal_messages()
+ self.allow_journal_messages(".*No authentication agent found.*")
+ self.allow_journal_messages("couldn't register polkit authentication agent.*")
+ self.allow_journal_messages("received request from bad Origin.*")
+ self.allow_journal_messages(".*invalid handshake.*")
+ self.allow_browser_errors(".*received unsupported version in init message.*")
+ self.allow_browser_errors(".*received message before init.*")
+ self.allow_browser_errors("Error reading machine id")
+
+ # ws with --for-tls-proxy accepts only https origins, thus should work
+ m.spawn("su -s /bin/sh -c '%s --for-tls-proxy -p 9099 -a 127.0.0.1' cockpit-ws" % self.ws_executable,
+ "ws-fortlsproxy.log")
+ m.wait_for_cockpit_running(tls=True)
+ b.open("https://%s:%s/system" % (b.address, b.port))
+ b.wait_visible("#login")
+ b.set_val("#login-user-input", "admin")
+ b.set_val("#login-password-input", "foobar")
+ b.click('#login-button')
+ b.expect_load()
+ b.wait_visible('#content')
+ b.enter_page("/system")
+ b.logout()
+
if __name__ == '__main__':
test_main()