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()