-
Notifications
You must be signed in to change notification settings - Fork 48
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Websocket] Support exec #65
[Websocket] Support exec #65
Conversation
/cc @brendandburns |
@@ -16,6 +16,15 @@ CLIENT_REPO_ROOT=${PWD}/c | |||
# Install pre-requisites | |||
sudo apt-get install libssl-dev libcurl4-openssl-dev uncrustify libyaml-dev | |||
|
|||
# Build pre-requisite: libwebsockets |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we want to build from source? Or just apt-get install libwebsockets-dev?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need build from source for libwebsockets now.
Because the code in this PR uses the latest stable version v4.2
, but the version of libwebsockets-dev
in Ubuntu 20.04 LTS (docker image ubuntu-latest
) is v3.2.1
, which is not compatible.
Users using Ubuntu 20.10 or above can install libwebsockets-dev v4.x
by
apt-get install libwebsockets-dev
We can update it after Ubuntu 22.04 LTS
releases next year.
printf("%s: Received %ld bytes:\n%s", __func__, *p_data_received_len, (char *) (*p_data_received)); | ||
} | ||
|
||
static void escape_character_in_url(char *escaped, int escaped_buffer_size, const char chr) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
use curl_easy_escape(...)
instead?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
escape_character_in_url
is different with curl_easy_escape
, the former receives only one character e.g. +
and then returns an escaped string, e.g. %2B
at one time. But curl_easy_escape
receives a string and returns the full escaped string for the whole url string.
At the case of kube exec
, we need handle the characters one by one because the space (" ") should not be escaped, it needs to be converted to "&command=", e.g.
"ls /" will be converted to
"ls&command=/"
curl_easy_escape
cannot do this.
} | ||
} | ||
|
||
static int assemble_command_in_url(char *command_string_in_url, int url_command_string_buffer_size, const char *original_command_string) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If url_command_string_buffer_size is > ESCAPED_STRING_BUFFER_SIZE this will overflow the escaped_string buffer. We should test for this and return -1.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The function escape_character_in_url
is different with curl_easy_escape
in libcurl.
ESCAPED_STRING_BUFFER_SIZE
is not the size of the whole escaped string of url, it is the size of string that only one character
escaped, e.g. +
escapes to %2B
(length is 3) , actually ESCAPED_STRING_BUFFER_SIZE=4
is enough.
I have test command_string_length
in function to prevent the command_string_in_url from overflowing
if (command_string_length >= url_command_string_buffer_size) {
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Few comments.
Thank you @brendandburns |
1ca9149
to
d08f667
Compare
kubernetes/websocket/wsclient.c
Outdated
free(wsc); | ||
} | ||
|
||
static struct lws_context *g_lws_context; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should g_lws_context be a global variable?
Do we have to maintain ws context if I run a command with kube_exec?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's the usage in the samples in libweboscket repo.
Do you concern whether it can work in multi-thread env ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes. running in the multithread env, the static variable is not safe.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't investigate this but some searching results show multi-thread is not supported easily by libwebsockets.
For the first commit of the feature, I will not consider this case. Welcome do some research for this!
For now, the case that only one thread runs "kube_exec", and other threads use apiClient_t (libcurl) is OK.
i.context = g_lws_context; | ||
i.port = wsc->server_port; | ||
i.address = wsc->server_address; | ||
i.path = wsc->path; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is it wss:// connection? or ws:// connection?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It depends on the value of i.ssl_connection
i.ssl_connection = wsc->ssl_config ? LCCSCF_USE_SSL : 0;
d08f667
to
43d23b1
Compare
Review comments are addressed and replied. Could you please take a look again ? |
Thanks for addressing the comments. I had one clarification. Also, I think that the escaping of the chars code is a little complicated, I would suggest that we pre-allocate the entire escaped string and then run through it linearly rather than using If we clean up the unnecessary allocation by changing the ownership of the |
Thanks @brendandburns . I will address the comments after coming back from vacation. |
Have a good vacation! |
info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; | ||
info.port = CONTEXT_PORT_NO_LISTEN; /* we do not run any server */ | ||
info.protocols = protocols; | ||
info.fd_limit_per_thread = 1 + 1 + 1; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the purpose of this expression vs setting the limit to just 3?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This code comes from the sample of libwebsokets and it has below explanation for users :
/*
* since we know this lws context is only ever going to be used with
* one client wsis / fds / sockets at a time, let lws know it doesn't
* have to use the default allocations for fd tables up to ulimit -n.
* It will just allocate for 1 internal and 1 (+ 1 http2 nwsi) that we
* will use.
*/
info.fd_limit_per_thread = 1 + 1 + 1;
kubernetes/websocket/wsclient.c
Outdated
#define TTY_STDIN_NUMBER 0 | ||
#define TTY_STDOUT_NUMBER 1 | ||
#define WS_PROTOCOL_DELIM "://" | ||
#define WS_BASE_PATH_DELIM ":" /* ip:port */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It can't clarify the port in IPV6 address
for example, if address is [fd04::1]:443
the port is not distinguished.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's a problem in ipv6 env.
But the delim ":" is right even in the format [fd04::1]:443
.
The fix should use strrchr
in the function of get_server_address_and_port
.
I will do it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
const char *port_string = p + strlen(WS_BASE_PATH_DELIM); | ||
|
||
int ws_port = atoi(port_string); | ||
if (0 == ws_port) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's better to use strtol intead of using atoi
atoi is unsafe function.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is atoi
unsafe ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
afaik, atoi("some-non-numeric-string")
is undefined in the C spec, so the strto...
family are preferred.
I don't think it matters too much in this case, but it's a good habit to get into.
char *base_path = NULL; | ||
sslConfig_t *ssl_config = NULL; | ||
list_t *api_keys = NULL; | ||
int rc = load_kube_config(&base_path, &ssl_config, &api_keys, NULL); /* NULL means loading configuration from $HOME/.kube/config */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did you test with load_incluster_config?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This PR cannot support running in cluster now.
We may support it in future. ^-^
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm struggling with not establishing ssl connection while using load_incluster_config.
Do you know what kind of missing implementation will it to fail?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"ssl_config" returning from load_incluster_config
only has CA file but does not have the client certificate and private key (which are not provided in cluster) so it cannot pass authentication by Kubernetes API server.
"api_keys" holding the authentication credential should be passed to the websocket client. And the websocket client needs updating to use this credential.
7b289c6
to
be31e23
Compare
8217811
to
b3b8562
Compare
b3b8562
to
5d5e323
Compare
I have made below changes:
wsclient_t *wsclient_create(const char *base_path, sslConfig_t * ssl_config, list_t * apiKeys, int ws_log_mask) This parameter is not used now, but for in-cluster authentication in future. Could you please check whether this PR can merge now ? Thanks. |
@ityuhui this code looks ok to me, but I'd like to make sure that @clearday4 is ok with it also before we merge. Thanks! |
@ityuhui @brendanburns |
lwsl_user("%s: wsclient connection established\n", __func__); | ||
lws_callback_on_writable(wsi); | ||
break; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
for the incluster_config
I add the this case for adding token to header
case LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER:
{
const char *tpath = "/var/run/secrets/kubernetes.io/serviceaccount/token";
char *token = read_complete_file(tpath); // using internal function
char *token_header = strcat_copy("Bearer ", token); // using internal function
const char *acc = "*/*";
unsigned char **p = (unsigned char **)in, *end = (*p) + len;
if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_ACCEPT,
(unsigned char *)acc, (int)strlen(acc), p, end))
{
free(token_header);
return -1;
}
if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_AUTHORIZATION,
(unsigned char *)token_header, (int)strlen(token_header), p, end))
{
free(token_header);
return -1;
}
free(token_header);
break;
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks. It's helpful.
/lgtm We can merge this and take improvements as the next step. |
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: brendandburns, ityuhui The full list of commands accepted by this bot can be found here. The pull request process is described here
Needs approval from an approver in each of these files:
Approvers can indicate their approval by writing |
Thank you for your review @brendandburns @clearday4 |
websocket and pod exec support for the c client