GUACAMOLE-1174: Merge support for Kubernetes "exec" API call.

This commit is contained in:
Mike Jumper 2021-02-21 11:09:53 -08:00 committed by GitHub
commit 5428ac5057
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 128 additions and 28 deletions

View File

@ -213,10 +213,11 @@ void* guac_kubernetes_client_thread(void* data) {
} }
/* Generate endpoint for attachment URL */ /* Generate endpoint for attachment URL */
if (guac_kubernetes_endpoint_attach(endpoint_path, sizeof(endpoint_path), if (guac_kubernetes_endpoint_uri(endpoint_path, sizeof(endpoint_path),
settings->kubernetes_namespace, settings->kubernetes_namespace,
settings->kubernetes_pod, settings->kubernetes_pod,
settings->kubernetes_container)) { settings->kubernetes_container,
settings->exec_command)) {
guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR, guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR,
"Unable to generate path for Kubernetes API endpoint: " "Unable to generate path for Kubernetes API endpoint: "
"Resulting path too long"); "Resulting path too long");

View File

@ -31,6 +31,7 @@ const char* GUAC_KUBERNETES_CLIENT_ARGS[] = {
"namespace", "namespace",
"pod", "pod",
"container", "container",
"exec-command",
"use-ssl", "use-ssl",
"client-cert", "client-cert",
"client-key", "client-key",
@ -86,6 +87,11 @@ enum KUBERNETES_ARGS_IDX {
*/ */
IDX_CONTAINER, IDX_CONTAINER,
/**
* The command used by exec call. If omitted, attach call will be used.
*/
IDX_EXEC_COMMAND,
/** /**
* Whether SSL/TLS should be used. If omitted, SSL/TLS will not be used. * Whether SSL/TLS should be used. If omitted, SSL/TLS will not be used.
*/ */
@ -275,6 +281,11 @@ guac_kubernetes_settings* guac_kubernetes_parse_args(guac_user* user,
guac_user_parse_args_string(user, GUAC_KUBERNETES_CLIENT_ARGS, argv, guac_user_parse_args_string(user, GUAC_KUBERNETES_CLIENT_ARGS, argv,
IDX_CONTAINER, NULL); IDX_CONTAINER, NULL);
/* Read exec command (optional) */
settings->exec_command =
guac_user_parse_args_string(user, GUAC_KUBERNETES_CLIENT_ARGS, argv,
IDX_EXEC_COMMAND, NULL);
/* Parse whether SSL should be used */ /* Parse whether SSL should be used */
settings->use_ssl = settings->use_ssl =
guac_user_parse_args_boolean(user, GUAC_KUBERNETES_CLIENT_ARGS, argv, guac_user_parse_args_boolean(user, GUAC_KUBERNETES_CLIENT_ARGS, argv,
@ -406,6 +417,9 @@ void guac_kubernetes_settings_free(guac_kubernetes_settings* settings) {
free(settings->kubernetes_pod); free(settings->kubernetes_pod);
free(settings->kubernetes_container); free(settings->kubernetes_container);
/* Free Kubernetes exec command */
free(settings->exec_command);
/* Free SSL/TLS details */ /* Free SSL/TLS details */
free(settings->client_cert); free(settings->client_cert);
free(settings->client_key); free(settings->client_key);

View File

@ -97,6 +97,12 @@ typedef struct guac_kubernetes_settings {
*/ */
char* kubernetes_container; char* kubernetes_container;
/**
* The command to generate api endpoint for call exec.
* If omitted call attach will be used.
*/
char* exec_command;
/** /**
* Whether SSL/TLS should be used. * Whether SSL/TLS should be used.
*/ */

View File

@ -89,15 +89,56 @@ int guac_kubernetes_escape_url_component(char* output, int length,
} }
int guac_kubernetes_endpoint_attach(char* buffer, int length, int guac_kubernetes_append_endpoint_param(char* buffer, int length,
const char* kubernetes_namespace, const char* kubernetes_pod, const char* param_name, const char* param_value) {
const char* kubernetes_container) {
char escaped_param_value[GUAC_KUBERNETES_MAX_ENDPOINT_LENGTH];
/* Escape value */
if (guac_kubernetes_escape_url_component(escaped_param_value,
sizeof(escaped_param_value), param_value))
return 1;
char* str = buffer;
int str_len = 0;
int qmark = 0;
while (*str != '\0') {
/* Look for a question mark */
if (*str=='?') qmark = 1;
/* Compute the buffer string length */
str_len++;
/* Verify the buffer null terminated */
if (str_len >= length) return 1;
/* Next character */
str++;
}
/* Determine the parameter delimiter */
char delimiter = '?';
if (qmark) delimiter = '&';
/* Write the parameter to the buffer */
int written; int written;
written = snprintf(buffer + str_len, length - str_len,
"%c%s=%s", delimiter, param_name, escaped_param_value);
/* The parameter was successfully added if it was written to the given
* buffer without truncation */
return (written < 0 || written >= length);
}
int guac_kubernetes_endpoint_uri(char* buffer, int length,
const char* kubernetes_namespace, const char* kubernetes_pod,
const char* kubernetes_container, const char* exec_command) {
char escaped_namespace[GUAC_KUBERNETES_MAX_ENDPOINT_LENGTH]; char escaped_namespace[GUAC_KUBERNETES_MAX_ENDPOINT_LENGTH];
char escaped_pod[GUAC_KUBERNETES_MAX_ENDPOINT_LENGTH]; char escaped_pod[GUAC_KUBERNETES_MAX_ENDPOINT_LENGTH];
char escaped_container[GUAC_KUBERNETES_MAX_ENDPOINT_LENGTH];
/* Escape Kubernetes namespace */ /* Escape Kubernetes namespace */
if (guac_kubernetes_escape_url_component(escaped_namespace, if (guac_kubernetes_escape_url_component(escaped_namespace,
@ -109,29 +150,38 @@ int guac_kubernetes_endpoint_attach(char* buffer, int length,
sizeof(escaped_pod), kubernetes_pod)) sizeof(escaped_pod), kubernetes_pod))
return 1; return 1;
/* Generate attachment endpoint URL */ /* Determine the call type */
if (kubernetes_container != NULL) { char* call = "attach";
if (exec_command != NULL)
call = "exec";
/* Escape container name */ int written;
if (guac_kubernetes_escape_url_component(escaped_container,
sizeof(escaped_container), kubernetes_container)) /* Generate the endpoint path and write to the buffer */
written = snprintf(buffer, length,
"/api/v1/namespaces/%s/pods/%s/%s", escaped_namespace, escaped_pod, call);
/* Operation successful if the endpoint path was written to the given
* buffer without truncation */
if (written < 0 || written >= length)
return 1; return 1;
written = snprintf(buffer, length, /* Append exec command parameter */
"/api/v1/namespaces/%s/pods/%s/attach" if (exec_command != NULL) {
"?container=%s&stdin=true&stdout=true&tty=true", if (guac_kubernetes_append_endpoint_param(buffer,
escaped_namespace, escaped_pod, escaped_container); length, "command", exec_command))
} return 1;
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 /* Append kubernetes container parameter */
* buffer without truncation */ if (kubernetes_container != NULL) {
return !(written < length - 1); if (guac_kubernetes_append_endpoint_param(buffer,
length, "container", kubernetes_container))
return 1;
} }
/* Append stdin, stdout and tty parameters */
return (guac_kubernetes_append_endpoint_param(buffer, length, "stdin", "true"))
|| (guac_kubernetes_append_endpoint_param(buffer, length, "stdout", "true"))
|| (guac_kubernetes_append_endpoint_param(buffer, length, "tty", "true"));
}

View File

@ -49,6 +49,31 @@
int guac_kubernetes_escape_url_component(char* output, int length, int guac_kubernetes_escape_url_component(char* output, int length,
const char* str); const char* str);
/**
* Append the parameter to the endpoint path.
* Value within the path will be URL-escaped as necessary.
*
* @param buffer
* The buffer which should receive the parameter. It could contain the endpoint path.
* The parameter will be written to the end of the buffer.
*
* @param length
* The number of bytes available in the given buffer.
*
* @param param_name
* The name of the parameter.
*
* @param param_value
* The value of the parameter.
*
* @return
* Zero if the parameter was successfully attached to the buffer,
* non-zero if insufficient space exists within the buffer or
* buffer not null terminated.
*/
int guac_kubernetes_append_endpoint_param(char* buffer, int length,
const char* param_name, const char* param_value);
/** /**
* Generates the full path to the Kubernetes API endpoint which handles * Generates the full path to the Kubernetes API endpoint which handles
* attaching to running containers within specific pods. Values within the path * attaching to running containers within specific pods. Values within the path
@ -73,13 +98,17 @@ int guac_kubernetes_escape_url_component(char* output, int length,
* The name of the container to attach to, or NULL to arbitrarily attach * The name of the container to attach to, or NULL to arbitrarily attach
* to the first container in the pod. * to the first container in the pod.
* *
* @param exec_command
* The command used to run a new process and attach to it,
* instead of the main container process.
*
* @return * @return
* Zero if the endpoint path was successfully written to the provided * Zero if the endpoint path was successfully written to the provided
* buffer, non-zero if insufficient space exists within the buffer. * buffer, non-zero if insufficient space exists within the buffer.
*/ */
int guac_kubernetes_endpoint_attach(char* buffer, int length, int guac_kubernetes_endpoint_uri(char* buffer, int length,
const char* kubernetes_namespace, const char* kubernetes_pod, const char* kubernetes_namespace, const char* kubernetes_pod,
const char* kubernetes_container); const char* kubernetes_container, const char* exec_command);
#endif #endif