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:34 UTC
[13/19] guacamole-server git commit: GUACAMOLE-623: Generate
Kubernetes API endpoint dynamically.
GUACAMOLE-623: Generate Kubernetes API endpoint dynamically.
Project: http://git-wip-us.apache.org/repos/asf/guacamole-server/repo
Commit: http://git-wip-us.apache.org/repos/asf/guacamole-server/commit/ed560938
Tree: http://git-wip-us.apache.org/repos/asf/guacamole-server/tree/ed560938
Diff: http://git-wip-us.apache.org/repos/asf/guacamole-server/diff/ed560938
Branch: refs/heads/master
Commit: ed560938886e92dbe656d610966585b8178c2d73
Parents: 34f8f8b
Author: Michael Jumper <mj...@apache.org>
Authored: Mon Sep 10 18:39:06 2018 -0700
Committer: Michael Jumper <mj...@apache.org>
Committed: Tue Sep 25 21:30:51 2018 -0700
----------------------------------------------------------------------
src/protocols/kubernetes/Makefile.am | 2 +
src/protocols/kubernetes/kubernetes.c | 27 ++++++-
src/protocols/kubernetes/settings.c | 53 ++++++++++--
src/protocols/kubernetes/settings.h | 24 ++++++
src/protocols/kubernetes/url.c | 126 +++++++++++++++++++++++++++++
src/protocols/kubernetes/url.h | 87 ++++++++++++++++++++
6 files changed, 311 insertions(+), 8 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/guacamole-server/blob/ed560938/src/protocols/kubernetes/Makefile.am
----------------------------------------------------------------------
diff --git a/src/protocols/kubernetes/Makefile.am b/src/protocols/kubernetes/Makefile.am
index d864967..9e50feb 100644
--- a/src/protocols/kubernetes/Makefile.am
+++ b/src/protocols/kubernetes/Makefile.am
@@ -29,6 +29,7 @@ libguac_client_kubernetes_la_SOURCES = \
pipe.c \
settings.c \
kubernetes.c \
+ url.c \
user.c
noinst_HEADERS = \
@@ -38,6 +39,7 @@ noinst_HEADERS = \
pipe.h \
settings.h \
kubernetes.h \
+ url.h \
user.h
libguac_client_kubernetes_la_CFLAGS = \
http://git-wip-us.apache.org/repos/asf/guacamole-server/blob/ed560938/src/protocols/kubernetes/kubernetes.c
----------------------------------------------------------------------
diff --git a/src/protocols/kubernetes/kubernetes.c b/src/protocols/kubernetes/kubernetes.c
index 53a8580..380d1d3 100644
--- a/src/protocols/kubernetes/kubernetes.c
+++ b/src/protocols/kubernetes/kubernetes.c
@@ -21,6 +21,7 @@
#include "common/recording.h"
#include "kubernetes.h"
#include "terminal/terminal.h"
+#include "url.h"
#include <guacamole/client.h>
#include <guacamole/protocol.h>
@@ -345,6 +346,28 @@ void* guac_kubernetes_client_thread(void* data) {
guac_kubernetes_settings* settings = kubernetes_client->settings;
pthread_t input_thread;
+ char endpoint_path[GUAC_KUBERNETES_MAX_ENDPOINT_LENGTH];
+
+ /* Verify that the pod name was specified (it's always required) */
+ if (settings->kubernetes_pod == NULL) {
+ guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR,
+ "The name of the Kubernetes pod is a required parameter.");
+ goto fail;
+ }
+
+ /* Generate endpoint for attachment URL */
+ if (guac_kubernetes_endpoint_attach(endpoint_path, sizeof(endpoint_path),
+ settings->kubernetes_namespace,
+ settings->kubernetes_pod,
+ settings->kubernetes_container)) {
+ guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR,
+ "Unable to generate path for Kubernetes API endpoint: "
+ "Resulting path too long");
+ goto fail;
+ }
+
+ guac_client_log(client, GUAC_LOG_DEBUG, "The endpoint for attaching to "
+ "the requested Kubernetes pod is \"%s\".", endpoint_path);
/* Set up screen recording, if requested */
if (settings->recording_path != NULL) {
@@ -434,9 +457,9 @@ void* guac_kubernetes_client_thread(void* data) {
goto fail;
}
- /* FIXME: Generate path dynamically */
+ /* Generate path dynamically */
connection_info.context = kubernetes_client->context;
- connection_info.path = "/api/v1/namespaces/default/pods/my-shell-68974bb7f7-rpjgr/attach?container=my-shell&stdin=true&stdout=true&tty=true";
+ connection_info.path = endpoint_path;
/* Open WebSocket connection to Kubernetes */
kubernetes_client->wsi = lws_client_connect_via_info(&connection_info);
http://git-wip-us.apache.org/repos/asf/guacamole-server/blob/ed560938/src/protocols/kubernetes/settings.c
----------------------------------------------------------------------
diff --git a/src/protocols/kubernetes/settings.c b/src/protocols/kubernetes/settings.c
index 50daa6f..4f9f3f2 100644
--- a/src/protocols/kubernetes/settings.c
+++ b/src/protocols/kubernetes/settings.c
@@ -32,6 +32,9 @@
const char* GUAC_KUBERNETES_CLIENT_ARGS[] = {
"hostname",
"port",
+ "namespace",
+ "pod",
+ "container",
"use-ssl",
"client-cert-file",
"client-key-file",
@@ -68,6 +71,24 @@ enum KUBERNETES_ARGS_IDX {
IDX_PORT,
/**
+ * The name of the Kubernetes namespace of the pod containing the container
+ * being attached to. If omitted, the default namespace will be used.
+ */
+ IDX_NAMESPACE,
+
+ /**
+ * The name of the Kubernetes pod containing with the container being
+ * attached to. Required.
+ */
+ IDX_POD,
+
+ /**
+ * The name of the container to attach to. If omitted, the first container
+ * in the pod will be used.
+ */
+ IDX_CONTAINER,
+
+ /**
* Whether SSL/TLS should be used. If omitted, SSL/TLS will not be used.
*/
IDX_USE_SSL,
@@ -215,11 +236,31 @@ guac_kubernetes_settings* guac_kubernetes_parse_args(guac_user* user,
guac_kubernetes_settings* settings =
calloc(1, sizeof(guac_kubernetes_settings));
- /* Read parameters */
+ /* Read hostname */
settings->hostname =
guac_user_parse_args_string(user, GUAC_KUBERNETES_CLIENT_ARGS, argv,
IDX_HOSTNAME, "");
+ /* Read port */
+ settings->port =
+ guac_user_parse_args_int(user, GUAC_KUBERNETES_CLIENT_ARGS, argv,
+ IDX_PORT, GUAC_KUBERNETES_DEFAULT_PORT);
+
+ /* Read Kubernetes namespace */
+ settings->kubernetes_namespace =
+ guac_user_parse_args_string(user, GUAC_KUBERNETES_CLIENT_ARGS, argv,
+ IDX_NAMESPACE, GUAC_KUBERNETES_DEFAULT_NAMESPACE);
+
+ /* Read name of Kubernetes pod (required) */
+ settings->kubernetes_pod =
+ guac_user_parse_args_string(user, GUAC_KUBERNETES_CLIENT_ARGS, argv,
+ IDX_POD, NULL);
+
+ /* Read container of pod (optional) */
+ settings->kubernetes_container =
+ guac_user_parse_args_string(user, GUAC_KUBERNETES_CLIENT_ARGS, argv,
+ IDX_CONTAINER, NULL);
+
/* Parse whether SSL should be used */
settings->use_ssl =
guac_user_parse_args_boolean(user, GUAC_KUBERNETES_CLIENT_ARGS, argv,
@@ -276,11 +317,6 @@ guac_kubernetes_settings* guac_kubernetes_parse_args(guac_user* user,
settings->height = user->info.optimal_height;
settings->resolution = user->info.optimal_resolution;
- /* Read port */
- settings->port =
- guac_user_parse_args_int(user, GUAC_KUBERNETES_CLIENT_ARGS, argv,
- IDX_PORT, GUAC_KUBERNETES_DEFAULT_PORT);
-
/* Read typescript path */
settings->typescript_path =
guac_user_parse_args_string(user, GUAC_KUBERNETES_CLIENT_ARGS, argv,
@@ -341,6 +377,11 @@ void guac_kubernetes_settings_free(guac_kubernetes_settings* settings) {
/* Free network connection information */
free(settings->hostname);
+ /* Free Kubernetes pod/container details */
+ free(settings->kubernetes_namespace);
+ free(settings->kubernetes_pod);
+ free(settings->kubernetes_container);
+
/* Free SSL/TLS details */
free(settings->client_cert_file);
free(settings->client_key_file);
http://git-wip-us.apache.org/repos/asf/guacamole-server/blob/ed560938/src/protocols/kubernetes/settings.h
----------------------------------------------------------------------
diff --git a/src/protocols/kubernetes/settings.h b/src/protocols/kubernetes/settings.h
index 8f42f8c..c2e479e 100644
--- a/src/protocols/kubernetes/settings.h
+++ b/src/protocols/kubernetes/settings.h
@@ -45,6 +45,12 @@
#define GUAC_KUBERNETES_DEFAULT_PORT 8080
/**
+ * The name of the Kubernetes namespace that should be used by default if no
+ * specific Kubernetes namespace is provided.
+ */
+#define GUAC_KUBERNETES_DEFAULT_NAMESPACE "default"
+
+/**
* The filename to use for the typescript, if not specified.
*/
#define GUAC_KUBERNETES_DEFAULT_TYPESCRIPT_NAME "typescript"
@@ -77,6 +83,24 @@ typedef struct guac_kubernetes_settings {
int port;
/**
+ * The name of the Kubernetes namespace of the pod containing the container
+ * being attached to.
+ */
+ char* kubernetes_namespace;
+
+ /**
+ * The name of the Kubernetes pod containing with the container being
+ * attached to.
+ */
+ char* kubernetes_pod;
+
+ /**
+ * The name of the container to attach to, or NULL to arbitrarily attach to
+ * the first container in the pod.
+ */
+ char* kubernetes_container;
+
+ /**
* Whether SSL/TLS should be used.
*/
bool use_ssl;
http://git-wip-us.apache.org/repos/asf/guacamole-server/blob/ed560938/src/protocols/kubernetes/url.c
----------------------------------------------------------------------
diff --git a/src/protocols/kubernetes/url.c b/src/protocols/kubernetes/url.c
new file mode 100644
index 0000000..cfd6f74
--- /dev/null
+++ b/src/protocols/kubernetes/url.c
@@ -0,0 +1,126 @@
+/*
+ * 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 "config.h"
+#include "url.h"
+
+#include <stdio.h>
+#include <string.h>
+
+static int guac_kubernetes_is_url_safe(char c) {
+ return (c >= 'A' && c <= 'Z')
+ || (c >= 'a' && c <= 'z')
+ || (c >= '0' && c <= '9')
+ || strchr("-_.!~*'()", c) != NULL;
+}
+
+int guac_kubernetes_escape_url_component(char* output, int length,
+ const char* str) {
+
+ char* current = output;
+ while (*str != '\0') {
+
+ char c = *str;
+
+ /* Store alphanumeric characters verbatim */
+ if (guac_kubernetes_is_url_safe(c)) {
+
+ /* Verify space exists for single character */
+ if (length < 1)
+ return 1;
+
+ *(current++) = c;
+ length--;
+
+ }
+
+ /* Escape EVERYTHING else as hex */
+ else {
+
+ /* Verify space exists for hex-encoded character */
+ if (length < 4)
+ return 1;
+
+ snprintf(current, 4, "%%%02X", (int) c);
+
+ current += 3;
+ length -= 3;
+ }
+
+ /* Next character */
+ str++;
+
+ }
+
+ /* Verify space exists for null terminator */
+ if (length < 1)
+ return 1;
+
+ /* Append null terminator */
+ *current = '\0';
+ return 0;
+
+}
+
+int guac_kubernetes_endpoint_attach(char* buffer, int length,
+ const char* kubernetes_namespace, const char* kubernetes_pod,
+ const char* kubernetes_container) {
+
+ int written;
+
+ char escaped_namespace[GUAC_KUBERNETES_MAX_ENDPOINT_LENGTH];
+ char escaped_pod[GUAC_KUBERNETES_MAX_ENDPOINT_LENGTH];
+ char escaped_container[GUAC_KUBERNETES_MAX_ENDPOINT_LENGTH];
+
+ /* Escape Kubernetes namespace */
+ if (guac_kubernetes_escape_url_component(escaped_namespace,
+ sizeof(escaped_namespace), kubernetes_namespace))
+ return 1;
+
+ /* Escape name of Kubernetes pod */
+ if (guac_kubernetes_escape_url_component(escaped_pod,
+ sizeof(escaped_pod), kubernetes_pod))
+ return 1;
+
+ /* Generate attachment endpoint URL */
+ if (kubernetes_container != NULL) {
+
+ /* Escape container name */
+ if (guac_kubernetes_escape_url_component(escaped_container,
+ sizeof(escaped_container), kubernetes_container))
+ return 1;
+
+ written = snprintf(buffer, length,
+ "/api/v1/namespaces/%s/pods/%s/attach"
+ "?container=%s&stdin=true&stdout=true&tty=true",
+ escaped_namespace, escaped_pod, escaped_container);
+ }
+ else {
+ written = snprintf(buffer, length,
+ "/api/v1/namespaces/%s/pods/%s/attach"
+ "?stdin=true&stdout=true&tty=true",
+ escaped_namespace, escaped_pod);
+ }
+
+ /* Endpoint URL was successfully generated if it was written to the given
+ * buffer without truncation */
+ return !(written < length - 1);
+
+}
+
http://git-wip-us.apache.org/repos/asf/guacamole-server/blob/ed560938/src/protocols/kubernetes/url.h
----------------------------------------------------------------------
diff --git a/src/protocols/kubernetes/url.h b/src/protocols/kubernetes/url.h
new file mode 100644
index 0000000..19084ee
--- /dev/null
+++ b/src/protocols/kubernetes/url.h
@@ -0,0 +1,87 @@
+/*
+ * 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_URL_H
+#define GUAC_KUBERNETES_URL_H
+
+#include "config.h"
+
+/**
+ * The maximum number of characters allowed in the full path for any Kubernetes
+ * endpoint.
+ */
+#define GUAC_KUBERNETES_MAX_ENDPOINT_LENGTH 1024
+
+/**
+ * Escapes the given string such that it can be included safely within a URL.
+ * This function duplicates the behavior of JavaScript's encodeURIComponent(),
+ * escaping all but the following characters: A-Z a-z 0-9 - _ . ! ~ * ' ( )
+ *
+ * @param output
+ * The buffer which should receive the escaped string. This buffer may be
+ * touched even if escaping is unsuccessful.
+ *
+ * @param length
+ * The number of bytes available in the given output buffer.
+ *
+ * @param str
+ * The string to escape.
+ *
+ * @return
+ * Zero if the string was successfully escaped and written into the
+ * provided output buffer without being truncated, including null
+ * terminator, non-zero otherwise.
+ */
+int guac_kubernetes_escape_url_component(char* output, int length,
+ const char* str);
+
+/**
+ * Generates the full path to the Kubernetes API endpoint which handles
+ * attaching to running containers within specific pods. Values within the path
+ * will be URL-escaped as necessary.
+ *
+ * @param buffer
+ * The buffer which should receive the endpoint path. This buffer may be
+ * touched even if the endpoint path could not be generated.
+ *
+ * @param length
+ * The number of bytes available in the given buffer.
+ *
+ * @param kubernetes_namespace
+ * The name of the Kubernetes namespace of the pod containing the container
+ * being attached to.
+ *
+ * @param kubernetes_pod
+ * The name of the Kubernetes pod containing with the container being
+ * attached to.
+ *
+ * @param kubernetes_container
+ * The name of the container to attach to, or NULL to arbitrarily attach
+ * to the first container in the pod.
+ *
+ * @return
+ * Zero if the endpoint path was successfully written to the provided
+ * buffer, non-zero if insufficient space exists within the buffer.
+ */
+int guac_kubernetes_endpoint_attach(char* buffer, int length,
+ const char* kubernetes_namespace, const char* kubernetes_pod,
+ const char* kubernetes_container);
+
+#endif
+