You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@guacamole.apache.org by vn...@apache.org on 2018/09/26 12:44:38 UTC
[17/19] guacamole-server git commit: GUACAMOLE-623: Add support for
SSL.
GUACAMOLE-623: Add support for SSL.
Project: http://git-wip-us.apache.org/repos/asf/guacamole-server/repo
Commit: http://git-wip-us.apache.org/repos/asf/guacamole-server/commit/83a531bc
Tree: http://git-wip-us.apache.org/repos/asf/guacamole-server/tree/83a531bc
Diff: http://git-wip-us.apache.org/repos/asf/guacamole-server/diff/83a531bc
Branch: refs/heads/master
Commit: 83a531bc89a5c79371f42d1ec5142c484a08479a
Parents: 2e50573
Author: Michael Jumper <mj...@apache.org>
Authored: Tue Sep 11 03:03:17 2018 -0700
Committer: Michael Jumper <mj...@apache.org>
Committed: Tue Sep 25 21:30:52 2018 -0700
----------------------------------------------------------------------
configure.ac | 1 +
src/protocols/kubernetes/Makefile.am | 3 +
src/protocols/kubernetes/client.c | 16 +--
src/protocols/kubernetes/client.h | 7 +
src/protocols/kubernetes/kubernetes.c | 46 +++----
src/protocols/kubernetes/settings.c | 48 +++----
src/protocols/kubernetes/settings.h | 24 ++--
src/protocols/kubernetes/ssl.c | 210 +++++++++++++++++++++++++++++
src/protocols/kubernetes/ssl.h | 41 ++++++
9 files changed, 326 insertions(+), 70 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/guacamole-server/blob/83a531bc/configure.ac
----------------------------------------------------------------------
diff --git a/configure.ac b/configure.ac
index df36eab..d26db39 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1225,6 +1225,7 @@ AC_ARG_ENABLE([kubernetes],
AM_CONDITIONAL([ENABLE_KUBERNETES], [test "x${enable_kubernetes}" = "xyes" \
-a "x${have_libwebsockets}" = "xyes" \
+ -a "x${have_ssl}" = "xyes" \
-a "x${have_terminal}" = "xyes"])
#
http://git-wip-us.apache.org/repos/asf/guacamole-server/blob/83a531bc/src/protocols/kubernetes/Makefile.am
----------------------------------------------------------------------
diff --git a/src/protocols/kubernetes/Makefile.am b/src/protocols/kubernetes/Makefile.am
index e818ff7..56db4d6 100644
--- a/src/protocols/kubernetes/Makefile.am
+++ b/src/protocols/kubernetes/Makefile.am
@@ -29,6 +29,7 @@ libguac_client_kubernetes_la_SOURCES = \
io.c \
pipe.c \
settings.c \
+ ssl.c \
kubernetes.c \
url.c \
user.c
@@ -40,6 +41,7 @@ noinst_HEADERS = \
io.h \
pipe.h \
settings.h \
+ ssl.h \
kubernetes.h \
url.h \
user.h
@@ -57,5 +59,6 @@ libguac_client_kubernetes_la_LIBADD = \
libguac_client_kubernetes_la_LDFLAGS = \
-version-info 0:0:0 \
@PTHREAD_LIBS@ \
+ @SSL_LIBS@ \
@WEBSOCKETS_LIBS@
http://git-wip-us.apache.org/repos/asf/guacamole-server/blob/83a531bc/src/protocols/kubernetes/client.c
----------------------------------------------------------------------
diff --git a/src/protocols/kubernetes/client.c b/src/protocols/kubernetes/client.c
index 58f4728..331e03d 100644
--- a/src/protocols/kubernetes/client.c
+++ b/src/protocols/kubernetes/client.c
@@ -32,12 +32,7 @@
#include <stdlib.h>
#include <string.h>
-/**
- * Static reference to the guac_client associated with the active Kubernetes
- * connection. As guacd guarantees that each main client connection is
- * isolated within its own process, this is safe.
- */
-static guac_client* guac_kubernetes_lws_log_client = NULL;
+guac_client* guac_kubernetes_lws_current_client = NULL;
/**
* Logging callback invoked by libwebsockets to log a single line of logging
@@ -53,15 +48,18 @@ static guac_client* guac_kubernetes_lws_log_client = NULL;
* The line of logging output to log.
*/
static void guac_kubernetes_log(int level, const char* line) {
- if (guac_kubernetes_lws_log_client != NULL)
- guac_client_log(guac_kubernetes_lws_log_client, GUAC_LOG_DEBUG,
+ if (guac_kubernetes_lws_current_client != NULL)
+ guac_client_log(guac_kubernetes_lws_current_client, GUAC_LOG_DEBUG,
"libwebsockets: %s", line);
}
int guac_client_init(guac_client* client) {
+ /* Ensure reference to main guac_client remains available in all
+ * libwebsockets contexts */
+ guac_kubernetes_lws_current_client = client;
+
/* Redirect libwebsockets logging */
- guac_kubernetes_lws_log_client = client;
lws_set_log_level(LLL_ERR | LLL_WARN | LLL_NOTICE | LLL_INFO,
guac_kubernetes_log);
http://git-wip-us.apache.org/repos/asf/guacamole-server/blob/83a531bc/src/protocols/kubernetes/client.h
----------------------------------------------------------------------
diff --git a/src/protocols/kubernetes/client.h b/src/protocols/kubernetes/client.h
index 0b847da..ec4ba32 100644
--- a/src/protocols/kubernetes/client.h
+++ b/src/protocols/kubernetes/client.h
@@ -28,6 +28,13 @@
#define GUAC_KUBERNETES_CLIPBOARD_MAX_LENGTH 262144
/**
+ * Static reference to the guac_client associated with the active Kubernetes
+ * connection. While libwebsockets provides some means of storing and
+ * retrieving custom data in some structures, this is not always available.
+ */
+extern guac_client* guac_kubernetes_lws_current_client;
+
+/**
* Free handler. Required by libguac and called when the guac_client is
* disconnected and must be cleaned up.
*/
http://git-wip-us.apache.org/repos/asf/guacamole-server/blob/83a531bc/src/protocols/kubernetes/kubernetes.c
----------------------------------------------------------------------
diff --git a/src/protocols/kubernetes/kubernetes.c b/src/protocols/kubernetes/kubernetes.c
index 4e7928e..f314c59 100644
--- a/src/protocols/kubernetes/kubernetes.c
+++ b/src/protocols/kubernetes/kubernetes.c
@@ -18,9 +18,11 @@
*/
#include "config.h"
+#include "client.h"
#include "common/recording.h"
#include "io.h"
#include "kubernetes.h"
+#include "ssl.h"
#include "terminal/terminal.h"
#include "url.h"
@@ -43,8 +45,9 @@
* The reason (event) that this callback was invoked.
*
* @param user
- * Arbitrary data assocated with the WebSocket session. This will always
- * be a pointer to the guac_client instance.
+ * Arbitrary data assocated with the WebSocket session. In some cases,
+ * this is actually event-specific data (such as the
+ * LWS_CALLBACK_OPENSSL_LOAD_EXTRA_CLIENT_VERIFY_CERT event).
*
* @param in
* A pointer to arbitrary, reason-specific data.
@@ -60,14 +63,19 @@ static int guac_kubernetes_lws_callback(struct lws* wsi,
enum lws_callback_reasons reason, void* user,
void* in, size_t length) {
- /* Request connection closure if client is stopped (note that the user
- * pointer passed by libwebsockets may be NULL for some events) */
- guac_client* client = (guac_client*) user;
- if (client != NULL && client->state != GUAC_CLIENT_RUNNING)
+ guac_client* client = guac_kubernetes_lws_current_client;
+
+ /* Do not handle any further events if connection is closing */
+ if (client->state != GUAC_CLIENT_RUNNING)
return lws_callback_http_dummy(wsi, reason, user, in, length);
switch (reason) {
+ /* Complete initialization of SSL */
+ case LWS_CALLBACK_OPENSSL_LOAD_EXTRA_CLIENT_VERIFY_CERTS:
+ guac_kubernetes_init_ssl(client, (SSL_CTX*) user);
+ break;
+
/* Failed to connect */
case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
guac_client_abort(client, GUAC_PROTOCOL_STATUS_UPSTREAM_NOT_FOUND,
@@ -256,29 +264,13 @@ void* guac_kubernetes_client_thread(void* data) {
};
/* If requested, use an SSL/TLS connection for communication with
- * Kubernetes */
+ * Kubernetes. Note that we disable hostname checks here because we
+ * do our own validation - libwebsockets does not validate properly if
+ * IP addresses are used. */
if (settings->use_ssl) {
-
- /* Enable use of SSL/TLS */
context_info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
- connection_info.ssl_connection = LCCSCF_USE_SSL;
-
- /* Bypass certificate checks if requested */
- if (settings->ignore_cert) {
- connection_info.ssl_connection |=
- LCCSCF_ALLOW_SELFSIGNED
- | LCCSCF_SKIP_SERVER_CERT_HOSTNAME_CHECK
- | LCCSCF_ALLOW_EXPIRED;
- }
-
- /* Otherwise use the given CA certificate to validate (if any) */
- else
- context_info.client_ssl_ca_filepath = settings->ca_cert_file;
-
- /* Certificate and key file for SSL/TLS client auth */
- context_info.client_ssl_cert_filepath = settings->client_cert_file;
- context_info.client_ssl_private_key_filepath = settings->client_key_file;
-
+ connection_info.ssl_connection = LCCSCF_USE_SSL
+ | LCCSCF_SKIP_SERVER_CERT_HOSTNAME_CHECK;
}
/* Create libwebsockets context */
http://git-wip-us.apache.org/repos/asf/guacamole-server/blob/83a531bc/src/protocols/kubernetes/settings.c
----------------------------------------------------------------------
diff --git a/src/protocols/kubernetes/settings.c b/src/protocols/kubernetes/settings.c
index 122a858..4f00a44 100644
--- a/src/protocols/kubernetes/settings.c
+++ b/src/protocols/kubernetes/settings.c
@@ -31,9 +31,9 @@ const char* GUAC_KUBERNETES_CLIENT_ARGS[] = {
"pod",
"container",
"use-ssl",
- "client-cert-file",
- "client-key-file",
- "ca-cert-file",
+ "client-cert",
+ "client-key",
+ "ca-cert",
"ignore-cert",
"font-name",
"font-size",
@@ -89,24 +89,26 @@ enum KUBERNETES_ARGS_IDX {
IDX_USE_SSL,
/**
- * The filename of the certificate to use if performing SSL/TLS client
- * authentication to authenticate with the Kubernetes server. If omitted,
- * SSL client authentication will not be performed.
+ * The certificate to use if performing SSL/TLS client authentication to
+ * authenticate with the Kubernetes server, in PEM format. If omitted, SSL
+ * client authentication will not be performed.
*/
- IDX_CLIENT_CERT_FILE,
+ IDX_CLIENT_CERT,
/**
- * The filename of the key to use if performing SSL/TLS client
- * authentication to authenticate with the Kubernetes server. If omitted,
- * SSL client authentication will not be performed.
+ * The key to use if performing SSL/TLS client authentication to
+ * authenticate with the Kubernetes server, in PEM format. If omitted, SSL
+ * client authentication will not be performed.
*/
- IDX_CLIENT_KEY_FILE,
+ IDX_CLIENT_KEY,
/**
- * The filename of the certificate of the certificate authority that signed
- * the certificate of the Kubernetes server.
+ * The certificate of the certificate authority that signed the certificate
+ * of the Kubernetes server, in PEM format. If omitted. verification of
+ * the Kubernetes server certificate will use the systemwide certificate
+ * authorities.
*/
- IDX_CA_CERT_FILE,
+ IDX_CA_CERT,
/**
* Whether the certificate used by the Kubernetes server for SSL/TLS should
@@ -264,17 +266,17 @@ guac_kubernetes_settings* guac_kubernetes_parse_args(guac_user* user,
/* Read SSL/TLS connection details only if enabled */
if (settings->use_ssl) {
- settings->client_cert_file =
+ settings->client_cert =
guac_user_parse_args_string(user, GUAC_KUBERNETES_CLIENT_ARGS,
- argv, IDX_CLIENT_CERT_FILE, NULL);
+ argv, IDX_CLIENT_CERT, NULL);
- settings->client_key_file =
+ settings->client_key =
guac_user_parse_args_string(user, GUAC_KUBERNETES_CLIENT_ARGS,
- argv, IDX_CLIENT_KEY_FILE, NULL);
+ argv, IDX_CLIENT_KEY, NULL);
- settings->ca_cert_file =
+ settings->ca_cert =
guac_user_parse_args_string(user, GUAC_KUBERNETES_CLIENT_ARGS,
- argv, IDX_CA_CERT_FILE, NULL);
+ argv, IDX_CA_CERT, NULL);
settings->ignore_cert =
guac_user_parse_args_boolean(user, GUAC_KUBERNETES_CLIENT_ARGS,
@@ -378,9 +380,9 @@ void guac_kubernetes_settings_free(guac_kubernetes_settings* settings) {
free(settings->kubernetes_container);
/* Free SSL/TLS details */
- free(settings->client_cert_file);
- free(settings->client_key_file);
- free(settings->ca_cert_file);
+ free(settings->client_cert);
+ free(settings->client_key);
+ free(settings->ca_cert);
/* Free display preferences */
free(settings->font_name);
http://git-wip-us.apache.org/repos/asf/guacamole-server/blob/83a531bc/src/protocols/kubernetes/settings.h
----------------------------------------------------------------------
diff --git a/src/protocols/kubernetes/settings.h b/src/protocols/kubernetes/settings.h
index a86d14a..6267a18 100644
--- a/src/protocols/kubernetes/settings.h
+++ b/src/protocols/kubernetes/settings.h
@@ -103,24 +103,26 @@ typedef struct guac_kubernetes_settings {
bool use_ssl;
/**
- * The filename of the certificate to use if performing SSL/TLS client
- * authentication to authenticate with the Kubernetes server. If omitted,
- * SSL client authentication will not be performed.
+ * The certificate to use if performing SSL/TLS client authentication to
+ * authenticate with the Kubernetes server, in PEM format. If omitted, SSL
+ * client authentication will not be performed.
*/
- char* client_cert_file;
+ char* client_cert;
/**
- * The filename of the key to use if performing SSL/TLS client
- * authentication to authenticate with the Kubernetes server. If omitted,
- * SSL client authentication will not be performed.
+ * The key to use if performing SSL/TLS client authentication to
+ * authenticate with the Kubernetes server, in PEM format. If omitted, SSL
+ * client authentication will not be performed.
*/
- char* client_key_file;
+ char* client_key;
/**
- * The filename of the certificate of the certificate authority that signed
- * the certificate of the Kubernetes server.
+ * The certificate of the certificate authority that signed the certificate
+ * of the Kubernetes server, in PEM format. If omitted. verification of
+ * the Kubernetes server certificate will use the systemwide certificate
+ * authorities.
*/
- char* ca_cert_file;
+ char* ca_cert;
/**
* Whether the certificate used by the Kubernetes server for SSL/TLS should
http://git-wip-us.apache.org/repos/asf/guacamole-server/blob/83a531bc/src/protocols/kubernetes/ssl.c
----------------------------------------------------------------------
diff --git a/src/protocols/kubernetes/ssl.c b/src/protocols/kubernetes/ssl.c
new file mode 100644
index 0000000..6ebafc6
--- /dev/null
+++ b/src/protocols/kubernetes/ssl.c
@@ -0,0 +1,210 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include "kubernetes.h"
+#include "settings.h"
+
+#include <guacamole/client.h>
+#include <openssl/asn1.h>
+#include <openssl/bio.h>
+#include <openssl/pem.h>
+#include <openssl/ssl.h>
+#include <openssl/x509v3.h>
+#include <openssl/x509_vfy.h>
+
+/**
+ * Tests whether the given hostname is, in fact, an IP address.
+ *
+ * @param hostname
+ * The hostname to test.
+ *
+ * @return
+ * Non-zero if the given hostname is an IP address, zero otherwise.
+ */
+static int guac_kubernetes_is_address(const char* hostname) {
+
+ /* Attempt to interpret the hostname as an IP address */
+ ASN1_OCTET_STRING* ip = a2i_IPADDRESS(hostname);
+
+ /* If unsuccessful, the hostname is not an IP address */
+ if (ip == NULL)
+ return 0;
+
+ /* Converted hostname must be freed */
+ ASN1_OCTET_STRING_free(ip);
+ return 1;
+
+}
+
+/**
+ * Parses the given PEM certificate, returning a new OpenSSL X509 structure
+ * representing that certificate.
+ *
+ * @param pem
+ * The PEM certificate.
+ *
+ * @return
+ * An X509 structure representing the given certificate, or NULL if the
+ * certificate was unreadable.
+ */
+static X509* guac_kubernetes_read_cert(char* pem) {
+
+ /* Prepare a BIO which provides access to the in-memory CA cert */
+ BIO* bio = BIO_new_mem_buf(pem, -1);
+ if (bio == NULL)
+ return NULL;
+
+ /* Read the CA cert as PEM */
+ X509* certificate = PEM_read_bio_X509(bio, NULL, NULL, NULL);
+ if (certificate == NULL) {
+ BIO_free(bio);
+ return NULL;
+ }
+
+ return certificate;
+
+}
+
+/**
+ * Parses the given PEM private key, returning a new OpenSSL EVP_PKEY structure
+ * representing that key.
+ *
+ * @param pem
+ * The PEM private key.
+ *
+ * @return
+ * An EVP_KEY representing the given private key, or NULL if the private
+ * key was unreadable.
+ */
+static EVP_PKEY* guac_kubernetes_read_key(char* pem) {
+
+ /* Prepare a BIO which provides access to the in-memory key */
+ BIO* bio = BIO_new_mem_buf(pem, -1);
+ if (bio == NULL)
+ return NULL;
+
+ /* Read the private key as PEM */
+ EVP_PKEY* key = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL);
+ if (key == NULL) {
+ BIO_free(bio);
+ return NULL;
+ }
+
+ return key;
+
+}
+
+void guac_kubernetes_init_ssl(guac_client* client, SSL_CTX* context) {
+
+ guac_kubernetes_client* kubernetes_client =
+ (guac_kubernetes_client*) client->data;
+
+ guac_kubernetes_settings* settings = kubernetes_client->settings;
+
+ /* Bypass certificate checks if requested */
+ if (settings->ignore_cert)
+ SSL_CTX_set_verify(context, SSL_VERIFY_NONE, NULL);
+
+ /* Otherwise use the given CA certificate to validate (if any) */
+ else if (settings->ca_cert != NULL) {
+
+ /* Read CA certificate from configuration data */
+ X509* ca_cert = guac_kubernetes_read_cert(settings->ca_cert);
+ if (ca_cert == NULL) {
+ guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR,
+ "Provided CA certificate is unreadable");
+ return;
+ }
+
+ /* Add certificate to CA store */
+ X509_STORE* ca_store = SSL_CTX_get_cert_store(context);
+ if (!X509_STORE_add_cert(ca_store, ca_cert)) {
+ guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR,
+ "Unable to add CA certificate to certificate store of "
+ "SSL context");
+ return;
+ }
+
+ }
+
+ /* Certificate for SSL/TLS client auth */
+ if (settings->client_cert != NULL) {
+
+ /* Read client certificate from configuration data */
+ X509* client_cert = guac_kubernetes_read_cert(settings->client_cert);
+ if (client_cert == NULL) {
+ guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR,
+ "Provided client certificate is unreadable");
+ return;
+ }
+
+ /* Use parsed certificate for authentication */
+ if (!SSL_CTX_use_certificate(context, client_cert)) {
+ guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR,
+ "Client certificate could not be used for SSL/TLS "
+ "client authentication");
+ return;
+ }
+
+ }
+
+ /* Private key for SSL/TLS client auth */
+ if (settings->client_key != NULL) {
+
+ /* Read client private key from configuration data */
+ EVP_PKEY* client_key = guac_kubernetes_read_key(settings->client_key);
+ if (client_key == NULL) {
+ guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR,
+ "Provided client private key is unreadable");
+ return;
+ }
+
+ /* Use parsed key for authentication */
+ if (!SSL_CTX_use_PrivateKey(context, client_key)) {
+ guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR,
+ "Client private key could not be used for SSL/TLS "
+ "client authentication");
+ return;
+ }
+
+ }
+
+ /* Enable hostname checking */
+ X509_VERIFY_PARAM *param = SSL_CTX_get0_param(context);
+ X509_VERIFY_PARAM_set_hostflags(param,
+ X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS);
+
+ /* Validate properly depending on whether hostname is an IP address */
+ if (guac_kubernetes_is_address(settings->hostname)) {
+ if (!X509_VERIFY_PARAM_set1_ip_asc(param, settings->hostname)) {
+ guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR,
+ "Server IP address validation could not be enabled");
+ return;
+ }
+ }
+ else {
+ if (!X509_VERIFY_PARAM_set1_host(param, settings->hostname, 0)) {
+ guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR,
+ "Server hostname validation could not be enabled");
+ return;
+ }
+ }
+
+}
+
http://git-wip-us.apache.org/repos/asf/guacamole-server/blob/83a531bc/src/protocols/kubernetes/ssl.h
----------------------------------------------------------------------
diff --git a/src/protocols/kubernetes/ssl.h b/src/protocols/kubernetes/ssl.h
new file mode 100644
index 0000000..cca02bd
--- /dev/null
+++ b/src/protocols/kubernetes/ssl.h
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef GUAC_KUBERNETES_SSL_H
+#define GUAC_KUBERNETES_SSL_H
+
+#include "settings.h"
+
+#include <openssl/ssl.h>
+
+/**
+ * Initializes the given SSL/TLS context using the configuration parameters
+ * associated with the given guac_client, setting up hostname/address
+ * validation and client authentication.
+ *
+ * @param client
+ * The guac_client associated with the Kubernetes connection.
+ *
+ * @param context
+ * The SSL_CTX in use by libwebsockets.
+ */
+void guac_kubernetes_init_ssl(guac_client* client, SSL_CTX* context);
+
+#endif
+