ws: Add --for-tls-proxy option

If cockpit-ws runs behind a reverse proxy that does the TLS termination,
it rejects non-GnuTLS connections with a https:// origin with

    received request from bad Origin: https://localhost:9090

This previously required setting `Origins` in cockpit.conf to make this
work. Introduce a new `--for-tls-proxy` option to cockpit-ws which
switches the default Origin checking to https-only. With that, no
cockpit.conf modifications are required for local proxies.

This is implemented by a new `for-tls-proxy` property in
`CockpitWebServer`, which gets plumbed through the Origins check in
`cockpit_web_service_create_socket()`.

This is mostly intended for the upcoming external "cockpit-tls" proxy
which externalizes TLS termination, so that this does not have to modify
cockpit.conf or inspect/modify the HTTP stream. But it's generally
applicable to any local reverse TLS proxy.

Fix test-handlers to not call cockpit_handler_socket() with a NULL
server, so that reading the property does not cause a warning.

Closes #11813
This commit is contained in:
Martin Pitt 2019-05-12 22:58:24 +02:00 committed by Martin Pitt
parent 3f8b4c9cbb
commit d1f531a160
14 changed files with 254 additions and 35 deletions

View File

@ -42,6 +42,7 @@
<arg><option>--port</option> <replaceable>PORT</replaceable></arg>
<arg><option>--address</option> <replaceable>ADDRESS</replaceable></arg>
<arg><option>--no-tls</option></arg>
<arg><option>--for-tls-proxy</option></arg>
<arg><option>--local-ssh</option></arg>
<arg><option>--local-session</option> <replaceable>BRIDGE</replaceable></arg>
</cmdsynopsis>
@ -158,6 +159,23 @@ getcert request -f ${CERT_FILE} -k ${KEY_FILE} -D $(hostname --fqdn) -C "sed -n
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--for-tls-proxy</option></term>
<listitem>
<para>
Tell <command>cockpit-ws</command> 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 <literal>Origins</literal> is set in the
<citerefentry>
<refentrytitle>cockpit.conf</refentrytitle><manvolnum>5</manvolnum>
</citerefentry>
configuration file, it will override this default.
</para>
<para>
This option implies <option>--no-tls</option>.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--local-ssh</option></term>
<listitem>

View File

@ -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)
{

View File

@ -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__ */

View File

@ -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 ();
}

View File

@ -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);

View File

@ -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

View File

@ -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
{

View File

@ -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);

View File

@ -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);

View File

@ -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: ");

View File

@ -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

View File

@ -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

View File

@ -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,

View File

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