[Configuration]

1. Authentication provider plugin framework

2. An instance of authentication provider plugin for OIDC (OpenID Connect)
This commit is contained in:
Hui Yu
2020-08-18 00:15:20 +08:00
parent 14c502c284
commit 43aaf95cd1
21 changed files with 1612 additions and 97 deletions

View File

@@ -23,6 +23,13 @@ jobs:
cd build
cmake ..
make
- name: Build authentication plugin - oidc
run: |
cd kubernetes/config/authn_plugin/plugins/oidc
mkdir build
cd build
cmake ..
make
- name: Build examples
run: |
cd examples/

View File

@@ -4,6 +4,7 @@ all:
cd list_pod_incluster; make
cd exec_provider; make
cd generic; make
cd auth_provider; make
clean:
cd create_pod; make clean
@@ -11,3 +12,4 @@ clean:
cd list_pod_incluster; make clean
cd exec_provider; make clean
cd generic; make clean
cd auth_provider; make clean

3
examples/auth_provider/.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
list_pod_by_auth_provider_bin
config_with_auth_provider
config_with_auth_provider.*

View File

@@ -0,0 +1,8 @@
INCLUDE:=-I../../kubernetes/include -I../../kubernetes/model -I../../kubernetes/api -I../../kubernetes/config
LIBS:=-L../../kubernetes/build -lkubernetes -lcurl -lyaml -lpthread -lssl -lz
CFLAGS:=-g
all:
gcc main.c $(CFLAGS) $(INCLUDE) $(LIBS) -o list_pod_by_auth_provider_bin
clean:
rm ./list_pod_by_auth_provider_bin

View File

@@ -0,0 +1,32 @@
---
apiVersion: v1
clusters:
- cluster:
certificate-authority-data:
server: https://host:6443
name: kubernetes
contexts:
- context:
cluster: kubernetes
user: kubernetes-admin
name: kubernetes-admin@kubernetes
- context:
cluster: kubernetes
namespace: default
user: theone
name: theone@kubernetes
current-context: theone@kubernetes
kind: Config
preferences: {}
users:
- name: theone
user:
auth-provider:
name: oidc
config:
client-id:
client-secret:
id-token:
idp-certificate-authority:
idp-issuer-url:
refresh-token:

View File

@@ -0,0 +1,64 @@
#include <kube_config.h>
#include <apiClient.h>
#include <CoreV1API.h>
#include <malloc.h>
#include <stdio.h>
#include <errno.h>
void list_pod(apiClient_t * apiClient)
{
v1_pod_list_t *pod_list = NULL;
pod_list = CoreV1API_listNamespacedPod(apiClient, "default", /*namespace */
NULL, /* pretty */
0, /* allowWatchBookmarks */
NULL, /* continue */
NULL, /* fieldSelector */
NULL, /* labelSelector */
0, /* limit */
NULL, /* resourceVersion */
0, /* timeoutSeconds */
0 /* watch */
);
printf("The return code of HTTP request=%ld\n", apiClient->response_code);
if (pod_list) {
printf("Get pod list:\n");
listEntry_t *listEntry = NULL;
v1_pod_t *pod = NULL;
list_ForEach(listEntry, pod_list->items) {
pod = listEntry->data;
printf("\tThe pod name: %s\n", pod->metadata->name);
}
v1_pod_list_free(pod_list);
pod_list = NULL;
} else {
printf("Cannot get any pod.\n");
}
}
int main(int argc, char *argv[])
{
char *basePath = NULL;
sslConfig_t *sslConfig = NULL;
list_t *apiKeys = NULL;
int rc = load_kube_config(&basePath, &sslConfig, &apiKeys, "./config_with_auth_provider");
if (rc != 0) {
printf("Cannot load kubernetes configuration.\n");
return -1;
}
apiClient_t *apiClient = apiClient_create_with_base_path(basePath, sslConfig, apiKeys);
if (!apiClient) {
printf("Cannot create a kubernetes client.\n");
return -1;
}
list_pod(apiClient);
apiClient_free(apiClient);
apiClient = NULL;
free_client_config(basePath, sslConfig, apiKeys);
basePath = NULL;
sslConfig = NULL;
apiKeys = NULL;
return 0;
}

View File

@@ -34,9 +34,12 @@ endif()
set(SRCS
config/kube_config_model.c
config/kube_config_yaml.c
config/kube_config_util.c
config/kube_config.c
config/incluster_config.c
config/exec_provider.c
config/authn_plugin/authn_plugin_util.c
config/authn_plugin/authn_plugin.c
src/list.c
src/apiKey.c
src/apiClient.c
@@ -793,9 +796,12 @@ set(HDRS
config/kube_config_common.h
config/kube_config_model.h
config/kube_config_yaml.h
config/kube_config_util.h
config/kube_config.h
config/incluster_config.h
config/exec_provider.h
config/authn_plugin/authn_plugin_util.h
config/authn_plugin/authn_plugin.h
include/apiClient.h
include/list.h
include/binary.h

View File

@@ -0,0 +1,81 @@
#include "authn_plugin.h"
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>
#include <errno.h>
#define PLUGIN_NAME_TEMPLATE "libkubernetes_%s.so"
#define PLUGIN_LIB_NAME_SIZE 64
#define PLUGIN_FUNCTION_GET_TOKEN "get_token"
#define PLUGIN_FUNCTION_IS_EXPIRED "is_expired"
#define PLUGIN_FUNCTION_REFRESH "refresh"
authn_plugin_t *create_authn_plugin(const char *name)
{
static char fname[] = "create_authn_plugin()";
authn_plugin_t *plugin = calloc(1, sizeof(authn_plugin_t));
if (!plugin) {
fprintf(stderr, "%s: Cannot allocate memory for plugin library %s.[%s]\n", fname, name, strerror(errno));
return NULL;
}
char plugin_lib_name[PLUGIN_LIB_NAME_SIZE];
memset(plugin_lib_name, 0, sizeof(plugin_lib_name));
snprintf(plugin_lib_name, sizeof(plugin_lib_name), PLUGIN_NAME_TEMPLATE, name);
void *dlhandler = dlopen(plugin_lib_name, RTLD_LAZY);
if (!dlhandler) {
fprintf(stderr, "%s: Cannot load the library %s.[%s]\n", fname, plugin_lib_name, dlerror());
goto error;
}
plugin->name = strdup(name);
plugin->dlhandler = dlhandler;
plugin->get_token = dlsym(dlhandler, PLUGIN_FUNCTION_GET_TOKEN);
if (!plugin->get_token) {
fprintf(stderr, "%s: Cannot find the function %s in library %s.[%s]\n", fname, PLUGIN_FUNCTION_GET_TOKEN, plugin_lib_name, dlerror());
goto error;
}
plugin->is_expired = dlsym(dlhandler, PLUGIN_FUNCTION_IS_EXPIRED);
if (!plugin->is_expired) {
fprintf(stderr, "%s: Cannot find the function %s in library %s.[%s]\n", fname, PLUGIN_FUNCTION_IS_EXPIRED, plugin_lib_name, dlerror());
goto error;
}
plugin->refresh = dlsym(dlhandler, PLUGIN_FUNCTION_REFRESH);
if (!plugin->refresh) {
fprintf(stderr, "%s: Cannot find the function %s in library %s.[%s]\n", fname, PLUGIN_FUNCTION_REFRESH, plugin_lib_name, dlerror());
goto error;
}
return plugin;
error:
free_authn_plugin(plugin);
plugin = NULL;
return plugin;
}
void free_authn_plugin(authn_plugin_t * plugin)
{
if (!plugin) {
return;
}
if (plugin->dlhandler) {
dlclose(plugin->dlhandler);
plugin->dlhandler = NULL;
}
if (plugin->name) {
free(plugin->name);
plugin->name = NULL;
}
plugin->get_token = NULL;
plugin->is_expired = NULL;
plugin->refresh = NULL;
free(plugin);
}

View File

@@ -0,0 +1,25 @@
#ifndef _AUTHN_PLUGIN_H
#define _AUTHN_PLUGIN_H
#include <stdbool.h>
#include "../kube_config_model.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct authn_plugin_t {
char *name;
void *dlhandler;
char *(*get_token) (kubeconfig_property_t *);
bool (*is_expired) (kubeconfig_property_t *);
int (*refresh) (kubeconfig_property_t *);
} authn_plugin_t;
authn_plugin_t *create_authn_plugin(const char *);
void free_authn_plugin(authn_plugin_t *);
#ifdef __cplusplus
}
#endif
#endif /* _AUTHN_PLUGIN_H */

View File

@@ -0,0 +1,76 @@
#include "authn_plugin_util.h"
#include <errno.h>
int shc_request(char **p_http_response, int *p_http_response_length, char *type, const char *url, sslConfig_t * sc, list_t * apiKeys, list_t * contentType, char *post_data)
{
static char fname[] = "shc_request()";
apiClient_t *http_client = apiClient_create_with_base_path(url, sc, apiKeys);
if (!http_client) {
fprintf(stderr, "%s: Cannot create http client. [%s].\n", fname, strerror(errno));
return -1;
}
apiClient_invoke(http_client, NULL, NULL, NULL, NULL, NULL, contentType, post_data, type);
int rc = http_client->response_code;
switch (rc) {
case HTTP_RC_OK:
*p_http_response = strndup((char *)http_client->dataReceived, http_client->dataReceivedLen);
*p_http_response_length = http_client->dataReceivedLen;
break;
default:
printf("%s: response_code=%ld\n", fname, http_client->response_code);
if (http_client->dataReceived) {
printf("%s: %s\n", fname, (char *)http_client->dataReceived);
}
break;
}
if (http_client->dataReceived) {
free(http_client->dataReceived);
http_client->dataReceived = NULL;
http_client->dataReceivedLen = 0;
}
if (http_client) {
apiClient_free(http_client);
http_client = NULL;
}
return rc;
}
char *shc_get_string_from_json(const char *json_string, const char *key)
{
static char fname[] = "shc_get_string_from_json()";
char *res = NULL;
if (!json_string || !key) {
return NULL;
}
cJSON *json = cJSON_Parse(json_string);
if (!json) {
fprintf(stderr, "%s: Cannot create JSON from string.[%s].\n", fname, cJSON_GetErrorPtr());
return NULL;
}
cJSON *value = cJSON_GetObjectItem(json, key);
if (!value) {
fprintf(stderr, "%s: Cannot get the value for %s.\n", fname, key);
goto end;
}
if (value->type != cJSON_String && value->type != cJSON_Object) {
fprintf(stderr, "%s: The value for %s is invalid.\n", fname, key);
goto end;
}
res = strdup(value->valuestring);
end:
if (json) {
cJSON_Delete(json);
json = NULL;
}
return res;
}

View File

@@ -0,0 +1,75 @@
#ifndef _AUTHN_PLUGIN_UTIL_H
#define _AUTHN_PLUGIN_UTIL_H
#include "../../include/apiClient.h"
#ifdef __cplusplus
extern "C" {
#endif
#define HTTP_REQUEST_GET "GET"
#define HTTP_REQUEST_POST "POST"
#define HTTP_REQUEST_DELETE "DELETE"
#define HTTP_REQUEST_PATCH "PATCH"
typedef enum shc_http_rc_t {
HTTP_RC_OK = 200,
HTTP_RC_UNAUTHORIZED = 400
} shc_http_rc_t;
/*
* shc_request
*
* Description:
*
* Issue a http(s) request
*
* Return:
*
* HTTP request return code
*
* Parameter:
*
* IN:
* request_type : HTTP_REQUEST_GET,HTTP_REQUEST_POST,HTTP_REQUEST_DELETE or HTTP_REQUEST_PATCH
* url : http request url
* sc : security context for SSL/TLS
* apiKeys : key-value list including basic or bearer token for HTTP authentication
* contentType : string list including HTTP content type
* post_data : post data string when request_type is "POST"
*
* OUT:
* p_http_response : pointer that pointing to HTTP response raw string, the memory is allocated in shc_request, but user should free it out of shc_request after it is useless.
* p_http_response_length : pointer that pointing to the length of p_http_response
*
*/
int shc_request(char **p_http_response, int *p_http_response_length, char *request_type, const char *url, sslConfig_t * sc, list_t * apiKeys, list_t * contentType, char *post_data);
/*
* shc_get_string_from_json
*
* Description:
*
* Get a string value from json by a key
*
* Return:
*
* char * : the value string
* NULL : cannot find the value by key
*
* Parameter:
*
* IN:
* json_string : a json string that includes key-value string
* key : the key needs to find
*
* OUT:
* None
*
*/
char *shc_get_string_from_json(const char *json_string, const char *key);
#ifdef __cplusplus
}
#endif
#endif /* _AUTHN_PLUGIN_UTIL_H */

View File

@@ -0,0 +1,15 @@
cmake_minimum_required (VERSION 2.6)
project (KubernetesClientAuthnPluginOIDC)
set(CMAKE_BUILD_TYPE Debug)
set(BIN_NAME "kubernetes_oidc")
include_directories(../../)
include_directories(../../../)
include_directories(../../../../include/)
include_directories(../../../../external/)
aux_source_directory(. DIR_SRCS)
add_library(${BIN_NAME} SHARED ${DIR_SRCS})

View File

@@ -0,0 +1,318 @@
#include "authn_plugin.h"
#include "authn_plugin_util.h"
#include "kube_config_util.h"
#include "kube_config_common.h"
#include "binary.h"
#include "cJSON.h"
#include <time.h>
#include <errno.h>
#define OIDC_ID_TOKEN_DELIM "."
#define OIDC_ID_TOKEN_EXP "exp"
#define OIDC_CONFIGURATION_URL_TEMPLATE "%s/.well-known/openid-configuration"
#define OIDC_TOKEN_ENDPOINT "token_endpoint"
#define OIDC_ID_TOKEN "id_token"
#define OIDC_REFRESH_TOKEN "refresh_token"
#define JWT_PART2_BUFFER_SIZE 1024
#define BASE64_PADDING_STRING_SIZE 4
#define BASE64_PADDING_CHAR "="
#define REFRESH_TOKEN_CONTENT_TYPE "application/x-www-form-urlencoded"
#define REFRESH_TOKEN_CREDENTIAL_TEMPLATE "%s:%s"
#define REFRESH_TOKEN_POST_DATA_TEMPLATE "refresh_token=%s&grant_type=refresh_token"
#define OIDC_CONFIGURATION_URL_BUFFER_SIZE 1024
#define REFRESH_TOKEN_CREDENTIAL_BUFFER_SIZE 1024
#define REFRESH_TOKEN_POST_DATA_BUFFER_SIZE 1024
static time_t get_token_expiration_time(const char *token_string)
{
static char fname[] = "get_token_expiration_time()";
time_t expiration_time = 0;
if (!token_string || '\0' == token_string[0]) {
return 0;
}
char *dup_token_string = strdup(token_string);
if (!dup_token_string) {
return 0;
}
char *p = NULL;
p = strtok(dup_token_string, OIDC_ID_TOKEN_DELIM); /* jwt header */
if (!p) {
fprintf(stderr, "%s: The token <%s> is not a valid JWT token.\n", fname, token_string);
goto end;
}
p = strtok(NULL, OIDC_ID_TOKEN_DELIM); /* jwt part2 */
if (!p) {
fprintf(stderr, "%s: The token <%s> is not a valid JWT token.\n", fname, token_string);
goto end;
}
int base64_padding_length = 4 - strlen(p) % 4;
char base64_padding_string[BASE64_PADDING_STRING_SIZE];
memset(base64_padding_string, 0, sizeof(base64_padding_string));
for (int i = 0; i < base64_padding_length; i++) {
strncat(base64_padding_string, BASE64_PADDING_CHAR, 1);
}
char jwt_part2_string[JWT_PART2_BUFFER_SIZE];
memset(jwt_part2_string, 0, sizeof(jwt_part2_string));
snprintf(jwt_part2_string, sizeof(jwt_part2_string), "%s%s", p, base64_padding_string);
int decoded_bytes = 0;
char *b64decode = base64decode(jwt_part2_string, strlen(jwt_part2_string), &decoded_bytes);
if (!b64decode || 0 == decoded_bytes) {
fprintf(stderr, "%s: Base64 decodes failed.\n", fname);
goto end;
}
cJSON *payload_JSON = cJSON_Parse(b64decode);
if (!payload_JSON) {
fprintf(stderr, "%s: Cannot create JSON from string.[%s].\n", fname, cJSON_GetErrorPtr());
goto end;
}
cJSON *json_value = cJSON_GetObjectItem(payload_JSON, OIDC_ID_TOKEN_EXP);
if (!json_value || json_value->type != cJSON_Number) {
fprintf(stderr, "%s: Cannot get expiration time in id token.\n", fname);
goto end;
}
expiration_time = json_value->valueint;
end:
if (payload_JSON) {
cJSON_Delete(payload_JSON);
payload_JSON = NULL;
}
if (b64decode) {
free(b64decode);
b64decode = NULL;
}
if (dup_token_string) {
free(dup_token_string);
dup_token_string = NULL;
}
return expiration_time;
}
char *get_token(kubeconfig_property_t * auth_provider)
{
return auth_provider->id_token;
}
bool is_expired(kubeconfig_property_t * auth_provider)
{
static char fname[] = "is_expired()";
if (NULL == auth_provider->id_token) {
fprintf(stderr, "%s: The id token is NULL.\n", fname);
return true;
} else if ('\0' == auth_provider->id_token[0]) {
fprintf(stderr, "%s: The id token is empty.\n", fname);
return true;
}
time_t exp_time = get_token_expiration_time(auth_provider->id_token);
if (exp_time < time(NULL)) {
return true;
}
return false;
}
char *get_token_endpoint(const char *idp_issuer_url, sslConfig_t * sc)
{
static char fname[] = "get_token_endpoint()";
char *token_endpoint = NULL;
if (!idp_issuer_url || '\0' == idp_issuer_url[0]) {
fprintf(stderr, "%s: The parameters idp_issuer_url is NULL or empty.\n", fname);
return NULL;
}
if (!sc) {
fprintf(stderr, "%s: SSL configuration is required.\n", fname);
return NULL;
}
char oidc_configuration_url[OIDC_CONFIGURATION_URL_BUFFER_SIZE];
memset(oidc_configuration_url, 0, sizeof(oidc_configuration_url));
snprintf(oidc_configuration_url, sizeof(oidc_configuration_url), OIDC_CONFIGURATION_URL_TEMPLATE, idp_issuer_url);
char *http_response = NULL;
int http_response_length = 0;
int rc = shc_request(&http_response, &http_response_length, HTTP_REQUEST_GET, oidc_configuration_url, sc, NULL, NULL, NULL);
if (HTTP_RC_OK != rc) {
fprintf(stderr, "%s: Failed to get token endpoint.\n", fname);
goto end;
}
token_endpoint = shc_get_string_from_json(http_response, OIDC_TOKEN_ENDPOINT);
end:
if (http_response) {
free(http_response);
http_response_length = 0;
}
return token_endpoint;
}
static int refresh_oidc_token(kubeconfig_property_t * auth_provider, const char *token_endpoint, sslConfig_t * sc)
{
static char fname[] = "refresh_oidc_token()";
int rc = 0;
if (!auth_provider || !token_endpoint || !sc) {
fprintf(stderr, "%s: The parameters are not valid.\n", fname);
return -1;
}
list_t *content_type = list_create();
if (!content_type) {
fprintf(stderr, "%s: Cannot create list for content type.[%s]\n", fname, strerror(errno));
return -1;
}
list_addElement(content_type, strdup(REFRESH_TOKEN_CONTENT_TYPE));
char refresh_token_credential[REFRESH_TOKEN_CREDENTIAL_BUFFER_SIZE];
memset(refresh_token_credential, 0, sizeof(refresh_token_credential));
snprintf(refresh_token_credential, sizeof(refresh_token_credential), REFRESH_TOKEN_CREDENTIAL_TEMPLATE, auth_provider->client_id, auth_provider->client_secret);
char *base64_credential = base64encode(refresh_token_credential, strlen(refresh_token_credential));
if (!base64_credential) {
fprintf(stderr, "%s: Cannot encode refresh token with base64.\n", fname);
rc = -1;
goto end;
}
char basic_token_buffer[BASIC_TOKEN_BUFFER_SIZE];
memset(basic_token_buffer, 0, sizeof(basic_token_buffer));
snprintf(basic_token_buffer, sizeof(basic_token_buffer), BASIC_TOKEN_TEMPLATE, base64_credential);
list_t *api_keys = list_create();
if (!api_keys) {
fprintf(stderr, "%s: Cannot create list for refresh token.[%s]\n", fname, strerror(errno));
rc = -1;
goto end;
}
keyValuePair_t *keyPairToken = keyValuePair_create(strdup(AUTH_TOKEN_KEY), strdup(basic_token_buffer));
list_addElement(api_keys, keyPairToken);
char refresh_token_post_data[REFRESH_TOKEN_POST_DATA_BUFFER_SIZE];
memset(refresh_token_post_data, 0, sizeof(refresh_token_post_data));
snprintf(refresh_token_post_data, sizeof(refresh_token_post_data), REFRESH_TOKEN_POST_DATA_TEMPLATE, auth_provider->refresh_token);
char *http_response = NULL;
int http_response_length = 0;
rc = shc_request(&http_response, &http_response_length, HTTP_REQUEST_POST, token_endpoint, sc, api_keys, content_type, refresh_token_post_data);
if (HTTP_RC_OK == rc) {
rc = 0; // update return code
} else {
fprintf(stderr, "%s: Failed to refresh OIDC token.\n", fname);
rc = -1;
goto end;
}
char *new_id_token = shc_get_string_from_json(http_response, OIDC_ID_TOKEN);
if (new_id_token) {
if (auth_provider->id_token) {
free(auth_provider->id_token);
auth_provider->id_token = NULL;
}
auth_provider->id_token = new_id_token;
} else {
fprintf(stderr, "%s: Failed to get the new OIDC token from the response.\n", fname);
rc = -1;
goto end;
}
char *new_refresh_token = shc_get_string_from_json(http_response, OIDC_REFRESH_TOKEN);
if (new_refresh_token) {
if (auth_provider->refresh_token) {
free(auth_provider->refresh_token);
auth_provider->refresh_token = NULL;
}
auth_provider->refresh_token = new_refresh_token;
} else {
rc = -1;
fprintf(stderr, "%s: Failed to get the new refresh token from the response.\n", fname);
goto end;
}
end:
if (http_response) {
free(http_response);
http_response = NULL;
http_response_length = 0;
}
if (base64_credential) {
free(base64_credential);
base64_credential = NULL;
}
if (api_keys) {
clear_and_free_string_pair_list(api_keys);
api_keys = NULL;
}
if (content_type) {
clear_and_free_string_list(content_type);
content_type = NULL;
}
return rc;
}
int refresh(kubeconfig_property_t * auth_provider)
{
static char fname[] = "refresh()";
int rc = 0;
sslConfig_t *sc = NULL;
if (auth_provider->idp_certificate_authority && '\0' != auth_provider->idp_certificate_authority[0]) {
sc = sslConfig_create(NULL, NULL, auth_provider->idp_certificate_authority, 0);
} else if (auth_provider->idp_certificate_authority_data && '\0' != auth_provider->idp_certificate_authority_data[0]) {
char *idp_certificate_file = kubeconfig_mk_cert_key_tempfile(auth_provider->idp_certificate_authority_data);
if (!idp_certificate_file) {
fprintf(stderr, "%s: Failed to create the temporary file for certificate.\n", fname);
return -1;
}
sc = sslConfig_create(NULL, NULL, idp_certificate_file, 0);
free(idp_certificate_file);
idp_certificate_file = NULL;
} else {
sc = sslConfig_create(NULL, NULL, NULL, 1);
}
if (!sc) {
fprintf(stderr, "%s: Cannot create the SSL configuration.\n", fname);
return -1;
}
char *token_endpoint = get_token_endpoint(auth_provider->idp_issuer_url, sc);
if (!token_endpoint) {
fprintf(stderr, "%s: Cannot get the token endpoint.\n", fname);
rc = -1;
goto end;
}
rc = refresh_oidc_token(auth_provider, token_endpoint, sc);
if (-1 == rc) {
fprintf(stderr, "%s: Failed to refresh OIDC token.\n", fname);
goto end;
}
end:
if (token_endpoint) {
free(token_endpoint);
token_endpoint = NULL;
}
if (sc) {
if (NULL == auth_provider->idp_certificate_authority && auth_provider->idp_certificate_authority_data && '\0' != auth_provider->idp_certificate_authority_data[0]) {
unsetSslConfig(sc);
}
sslConfig_free(sc);
sc = NULL;
}
return rc;
}

View File

@@ -8,12 +8,13 @@
#include "kube_config.h"
#include "kube_config_yaml.h"
#include "kube_config_common.h"
#include "kube_config_util.h"
#include "exec_provider.h"
#include "authn_plugin/authn_plugin.h"
#define ENV_KUBECONFIG "KUBECONFIG"
#define ENV_HOME "HOME"
#define KUBE_CONFIG_DEFAULT_LOCATION "%s/.kube/config"
#define KUBE_CONFIG_TEMPFILE_NAME_TEMPLATE "/tmp/kubeconfig-XXXXXX"
static int setBasePath(char **pBasePath, char *basePath)
{
@@ -25,82 +26,6 @@ static int setBasePath(char **pBasePath, char *basePath)
return -1;
}
static bool is_cert_or_key_base64_encoded(const char *data)
{
if (NULL == strstr(data, "BEGIN")) {
return true;
} else {
return false; // e.g. "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----"
}
}
static char *kubeconfig_mk_cert_key_tempfile(const char *data)
{
static char fname[] = "kubeconfig_mk_tempfile()";
const char *cert_key_data = NULL;
int cert_key_data_bytes = 0;
bool is_data_base64_encoded = is_cert_or_key_base64_encoded(data);
if (true == is_data_base64_encoded) {
int decoded_bytes = 0;
char *b64decode = base64decode(data, strlen(data), &decoded_bytes);
if (!b64decode || 0 == decoded_bytes) {
fprintf(stderr, "%s: Base64 decodes failed.\n", fname);
return NULL;
}
cert_key_data = b64decode;
cert_key_data_bytes = decoded_bytes;
} else { // plain text, no need base64 decode
cert_key_data = data;
cert_key_data_bytes = strlen(cert_key_data);
}
char tempfile_name_template[] = KUBE_CONFIG_TEMPFILE_NAME_TEMPLATE;
int fd = mkstemp(tempfile_name_template);
if (-1 == fd) {
fprintf(stderr, "%s: Creating temp file for kubeconfig failed with error [%s]\n", fname, strerror(errno));
return NULL;
}
int rc = write(fd, cert_key_data, cert_key_data_bytes);
close(fd);
if (true == is_data_base64_encoded && cert_key_data) {
free((char *) cert_key_data); // cast "const char *" to "char *"
cert_key_data = NULL;
}
if (-1 == rc) {
fprintf(stderr, "%s: Writing temp file failed with error [%s]\n", fname, strerror(errno));
return NULL;
}
return strdup(tempfile_name_template);
}
static void kubeconfig_rm_tempfile(const char *filename)
{
if (filename) {
unlink(filename);
}
}
static void unsetSslConfig(sslConfig_t * sslConfig)
{
if (!sslConfig) {
return;
}
if (sslConfig->clientCertFile) {
kubeconfig_rm_tempfile(sslConfig->clientCertFile);
}
if (sslConfig->clientKeyFile) {
kubeconfig_rm_tempfile(sslConfig->clientKeyFile);
}
if (sslConfig->CACertFile) {
kubeconfig_rm_tempfile(sslConfig->CACertFile);
}
}
static int setSslConfig(sslConfig_t ** pSslConfig, const kubeconfig_property_t * cluster, const kubeconfig_property_t * user)
{
int rc = 0;
@@ -290,6 +215,53 @@ static int kubeconfig_update_exec_command_path(kubeconfig_property_t * exec, con
return rc;
}
static int kuberconfig_auth_provider(kubeconfig_property_t * current_user, kubeconfig_t * kubeconfig)
{
static char fname[] = "kuberconfig_auth_provider()";
if (!current_user || !current_user->auth_provider || !kubeconfig) {
return 0;
}
kubeconfig_property_t *auth_provider = current_user->auth_provider;
if (!auth_provider->name) {
fprintf(stderr, "%s: The name of auth provider is not specified.\n", fname);
return -1;
}
authn_plugin_t *plugin = create_authn_plugin(auth_provider->name);
if (!plugin) {
fprintf(stderr, "%s: Cannot instantiate the auth provider plugin for %s.\n", fname, auth_provider->name);
return -1;
}
int rc = 0;
if (plugin->is_expired(auth_provider)) {
rc = plugin->refresh(auth_provider);
if (0 != rc) {
fprintf(stderr, "%s: Cannot refresh token of auth provider <%s>.\n", fname, auth_provider->name);
goto end;
}
rc = kubeyaml_save_kubeconfig(kubeconfig);
if (0 != rc) {
fprintf(stderr, "%s: Cannot persist to kubeconfig file: %s.\n", fname, kubeconfig->fileName);
goto end;
}
}
const char *token = plugin->get_token(auth_provider);
if (!token) {
fprintf(stderr, "%s: Cannot get token from auth provider <%s>.\n", fname, auth_provider->name);
rc = -1;
goto end;
}
current_user->token = strdup(token);
end:
free_authn_plugin(plugin);
plugin = NULL;
return rc;
}
int load_kube_config(char **pBasePath, sslConfig_t ** pSslConfig, list_t ** pApiKeys, const char *configFileName)
{
static char fname[] = "load_kube_config()";
@@ -346,6 +318,14 @@ int load_kube_config(char **pBasePath, sslConfig_t ** pSslConfig, list_t ** pApi
}
}
if (current_user && current_user->auth_provider) {
rc = kuberconfig_auth_provider(current_user, kubeconfig);
if (0 != rc) {
fprintf(stderr, "%s: Cannot get token from authentication provider.\n", fname);
goto end;
}
}
if (current_cluster && current_cluster->server) {
rc = setBasePath(pBasePath, current_cluster->server);
if (0 != rc) {
@@ -388,17 +368,6 @@ void free_client_config(char *basePath, sslConfig_t * sslConfig, list_t * apiKey
}
if (apiKeys) {
listEntry_t *listEntry = NULL;
list_ForEach(listEntry, apiKeys) {
keyValuePair_t *pair = listEntry->data;
if (pair->key) {
free(pair->key);
}
if (pair->value) {
free(pair->value);
}
keyValuePair_free(pair);
}
list_free(apiKeys);
clear_and_free_string_pair_list(apiKeys);
}
}

View File

@@ -8,6 +8,8 @@ extern "C" {
#define AUTH_TOKEN_KEY "Authorization"
#define BEARER_TOKEN_TEMPLATE "Bearer %s"
#define BEARER_TOKEN_BUFFER_SIZE 1024
#define BASIC_TOKEN_TEMPLATE "Basic %s"
#define BASIC_TOKEN_BUFFER_SIZE 1024
#ifdef __cplusplus
}

View File

@@ -83,6 +83,10 @@ void kubeconfig_property_free(kubeconfig_property_t * property)
free(property->password);
property->password = NULL;
}
if (property->token) {
free(property->token);
property->token = NULL;
}
if (property->auth_provider) {
kubeconfig_property_free(property->auth_provider);
property->auth_provider = NULL;
@@ -98,6 +102,10 @@ void kubeconfig_property_free(kubeconfig_property_t * property)
free(property->cluster);
property->cluster = NULL;
}
if (property->namespace) {
free(property->namespace);
property->namespace = NULL;
}
if (property->user) {
free(property->user);
property->user = NULL;
@@ -146,10 +154,30 @@ void kubeconfig_property_free(kubeconfig_property_t * property)
free(property->expiry);
property->expiry = NULL;
}
if (property->idp_certificate_authority) {
free(property->idp_certificate_authority);
property->idp_certificate_authority = NULL;
}
if (property->idp_certificate_authority_data) {
free(property->idp_certificate_authority_data);
property->idp_certificate_authority_data = NULL;
}
if (property->client_id) {
free(property->client_id);
property->client_id = NULL;
}
if (property->client_secret) {
free(property->client_secret);
property->client_secret = NULL;
}
if (property->idp_issuer_url) {
free(property->idp_issuer_url);
property->idp_issuer_url = NULL;
}
if (property->refresh_token) {
free(property->refresh_token);
property->refresh_token = NULL;
}
}
free(property);

View File

@@ -45,6 +45,7 @@ extern "C" {
union {
struct { /* context */
char *cluster;
char *namespace;
char *user;
};
struct { /* cluster */
@@ -70,12 +71,17 @@ extern "C" {
int args_count;
};
struct { /* user auth provider */
char *id_token;
char *cmd_path;
char *access_token;
char *client_id;
char *client_secret;
char *cmd_path;
char *expires_on;
char *expiry;
char *id_token;
char *idp_certificate_authority;
char *idp_certificate_authority_data;
char *idp_issuer_url;
char *refresh_token;
};
};
} kubeconfig_property_t;

View File

@@ -0,0 +1,123 @@
#include <stdio.h>
#include <errno.h>
#include <stdbool.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include "../include/apiClient.h"
#define KUBE_CONFIG_TEMPFILE_NAME_TEMPLATE "/tmp/kubeconfig-XXXXXX"
static bool is_cert_or_key_base64_encoded(const char *data)
{
if (NULL == strstr(data, "BEGIN")) {
return true;
} else {
return false; // e.g. "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----"
}
}
char *kubeconfig_mk_cert_key_tempfile(const char *data)
{
static char fname[] = "kubeconfig_mk_tempfile()";
const char *cert_key_data = NULL;
int cert_key_data_bytes = 0;
bool is_data_base64_encoded = is_cert_or_key_base64_encoded(data);
if (true == is_data_base64_encoded) {
int decoded_bytes = 0;
char *b64decode = base64decode(data, strlen(data), &decoded_bytes);
if (!b64decode || 0 == decoded_bytes) {
fprintf(stderr, "%s: Base64 decodes failed.\n", fname);
return NULL;
}
cert_key_data = b64decode;
cert_key_data_bytes = decoded_bytes;
} else { // plain text, no need base64 decode
cert_key_data = data;
cert_key_data_bytes = strlen(cert_key_data);
}
char tempfile_name_template[] = KUBE_CONFIG_TEMPFILE_NAME_TEMPLATE;
int fd = mkstemp(tempfile_name_template);
if (-1 == fd) {
fprintf(stderr, "%s: Creating temp file for kubeconfig failed with error [%s]\n", fname, strerror(errno));
return NULL;
}
int rc = write(fd, cert_key_data, cert_key_data_bytes);
close(fd);
if (true == is_data_base64_encoded && cert_key_data) {
free((char *) cert_key_data); // cast "const char *" to "char *"
cert_key_data = NULL;
}
if (-1 == rc) {
fprintf(stderr, "%s: Writing temp file failed with error [%s]\n", fname, strerror(errno));
return NULL;
}
return strdup(tempfile_name_template);
}
static void kubeconfig_rm_tempfile(const char *filename)
{
if (filename) {
unlink(filename);
}
}
void unsetSslConfig(sslConfig_t * sslConfig)
{
if (!sslConfig) {
return;
}
if (sslConfig->clientCertFile) {
kubeconfig_rm_tempfile(sslConfig->clientCertFile);
}
if (sslConfig->clientKeyFile) {
kubeconfig_rm_tempfile(sslConfig->clientKeyFile);
}
if (sslConfig->CACertFile) {
kubeconfig_rm_tempfile(sslConfig->CACertFile);
}
}
void clear_and_free_string_pair_list(list_t * list)
{
if (!list) {
return;
}
listEntry_t *listEntry = NULL;
list_ForEach(listEntry, list) {
keyValuePair_t *pair = listEntry->data;
if (pair->key) {
free(pair->key);
pair->key = NULL;
}
if (pair->value) {
free(pair->value);
pair->value = NULL;
}
keyValuePair_free(pair);
pair = NULL;
}
list_free(list);
}
void clear_and_free_string_list(list_t * list)
{
if (!list) {
return;
}
listEntry_t *listEntry = NULL;
list_ForEach(listEntry, list) {
char *list_item = listEntry->data;
free(list_item);
list_item = NULL;
}
list_free(list);
}

View File

@@ -0,0 +1,100 @@
#ifndef _KUBE_CONFIG_UTIL_H
#define _KUBE_CONFIG_UTIL_H
#ifdef __cplusplus
extern "C" {
#endif
/*
* kubeconfig_mk_cert_key_tempfile
*
* Description:
*
* Create a temporary file to persist SSL/TLS certificate or key
*
* Return:
*
* char * : File name of created temporary file
* NULL : Failed to create temporary file
*
* Parameter:
*
* IN:
* data: raw data of SSL/TLS certificate or key
*
* OUT:
* None
*
*/
char *kubeconfig_mk_cert_key_tempfile(const char *data);
/*
* unsetSslConfig
*
* Description:
*
* unset the SSL configuration
*
* Return:
*
* None
*
* Parameter:
*
* IN:
* sslConfig: The SSL configuration
*
* OUT:
* None
*
*/
void unsetSslConfig(sslConfig_t * sslConfig);
/*
* clear_and_free_string_pair_list
*
* Description:
*
* clear the content and free the memory for a string pair list
*
* Return:
*
* None
*
* Parameter:
*
* IN:
* list: The string pair list needs to clear and free
*
* OUT:
* None
*
*/
void clear_and_free_string_pair_list(list_t * list);
/*
* clear_and_free_string_list
*
* Description:
*
* clear the content and free the memory for a string list
*
* Return:
*
* None
*
* Parameter:
*
* IN:
* list: The string list needs to clear and free
*
* OUT:
* None
*
*/
void clear_and_free_string_list(list_t * list);
#ifdef __cplusplus
}
#endif
#endif /* _KUBE_CONFIG_UTIL_H */

View File

@@ -15,9 +15,12 @@ mapping :: = MAPPING - START(node node) * MAPPING - END
#define KEY_APIVERSION "apiVersion"
#define KEY_KIND "kind"
#define KEY_CURRENT_CONTEXT "current-context"
#define KEY_PREFERENCES "preferences"
#define KEY_CLUSTERS "clusters"
#define KEY_CLUSTER "cluster"
#define KEY_CONTEXTS "contexts"
#define KEY_CONTEXT "context"
#define KEY_NAMESPACE "namespace"
#define KEY_USERS "users"
#define KEY_USER "user"
#define KEY_NAME "name"
@@ -27,6 +30,19 @@ mapping :: = MAPPING - START(node node) * MAPPING - END
#define KEY_USER_EXEC_ENV_KEY "name"
#define KEY_USER_EXEC_ENV_VALUE "value"
#define KEY_USER_EXEC_ARGS "args"
#define KEY_USER_AUTH_PROVIDER "auth-provider"
#define KEY_USER_AUTH_PROVIDER_CONFIG "config"
#define KEY_USER_AUTH_PROVIDER_CONFIG_ACCESS_TOKEN "access-token"
#define KEY_USER_AUTH_PROVIDER_CONFIG_CLIENT_ID "client-id"
#define KEY_USER_AUTH_PROVIDER_CONFIG_CLIENT_SECRET "client-secret"
#define KEY_USER_AUTH_PROVIDER_CONFIG_CMD_PATH "cmd-path"
#define KEY_USER_AUTH_PROVIDER_CONFIG_EXPIRES_ON "expires-on"
#define KEY_USER_AUTH_PROVIDER_CONFIG_EXPIRY "expiry"
#define KEY_USER_AUTH_PROVIDER_CONFIG_ID_TOKEN "id-token"
#define KEY_USER_AUTH_PROVIDER_CONFIG_IDP_CERTIFICATE_AUTHORITY "idp-certificate-authority"
#define KEY_USER_AUTH_PROVIDER_CONFIG_IDP_CERTIFICATE_AUTHORITY_DATA "idp-certificate-authority-data"
#define KEY_USER_AUTH_PROVIDER_CONFIG_IDP_ISSUE_URL "idp-issuer-url"
#define KEY_USER_AUTH_PROVIDER_CONFIG_REFRESH_TOKEN "refresh-token"
#define KEY_CERTIFICATE_AUTHORITY_DATA "certificate-authority-data"
#define KEY_SERVER "server"
#define KEY_CLIENT_CERTIFICATE_DATA "client-certificate-data"
@@ -180,6 +196,8 @@ static int parse_kubeconfig_yaml_property_mapping(kubeconfig_property_t * proper
} else if (KUBECONFIG_PROPERTY_TYPE_CONTEXT == property->type) {
if (0 == strcmp(key->data.scalar.value, KEY_CLUSTER)) {
property->cluster = strdup(value->data.scalar.value);
} else if (0 == strcmp(key->data.scalar.value, KEY_NAMESPACE)) {
property->namespace = strdup(value->data.scalar.value);
} else if (0 == strcmp(key->data.scalar.value, KEY_USER)) {
property->user = strdup(value->data.scalar.value);
}
@@ -189,19 +207,58 @@ static int parse_kubeconfig_yaml_property_mapping(kubeconfig_property_t * proper
} else if (0 == strcmp(key->data.scalar.value, KEY_USER_EXEC_COMMAND)) {
property->command = strdup(value->data.scalar.value);
}
} else if (KUBECONFIG_PROPERTY_TYPE_USER_AUTH_PROVIDER == property->type) {
if (0 == strcmp(key->data.scalar.value, KEY_USER_AUTH_PROVIDER_CONFIG_ACCESS_TOKEN)) {
property->access_token = strdup(value->data.scalar.value);
} else if (0 == strcmp(key->data.scalar.value, KEY_USER_AUTH_PROVIDER_CONFIG_CLIENT_ID)) {
property->client_id = strdup(value->data.scalar.value);
} else if (0 == strcmp(key->data.scalar.value, KEY_USER_AUTH_PROVIDER_CONFIG_CLIENT_SECRET)) {
property->client_secret = strdup(value->data.scalar.value);
} else if (0 == strcmp(key->data.scalar.value, KEY_USER_AUTH_PROVIDER_CONFIG_CMD_PATH)) {
property->cmd_path = strdup(value->data.scalar.value);
} else if (0 == strcmp(key->data.scalar.value, KEY_USER_AUTH_PROVIDER_CONFIG_EXPIRES_ON)) {
property->expires_on = strdup(value->data.scalar.value);
} else if (0 == strcmp(key->data.scalar.value, KEY_USER_AUTH_PROVIDER_CONFIG_EXPIRY)) {
property->expiry = strdup(value->data.scalar.value);
} else if (0 == strcmp(key->data.scalar.value, KEY_USER_AUTH_PROVIDER_CONFIG_ID_TOKEN)) {
property->id_token = strdup(value->data.scalar.value);
} else if (0 == strcmp(key->data.scalar.value, KEY_USER_AUTH_PROVIDER_CONFIG_IDP_CERTIFICATE_AUTHORITY)) {
property->idp_certificate_authority = strdup(value->data.scalar.value);
} else if (0 == strcmp(key->data.scalar.value, KEY_USER_AUTH_PROVIDER_CONFIG_IDP_CERTIFICATE_AUTHORITY_DATA)) {
property->idp_certificate_authority_data = strdup(value->data.scalar.value);
} else if (0 == strcmp(key->data.scalar.value, KEY_USER_AUTH_PROVIDER_CONFIG_IDP_ISSUE_URL)) {
property->idp_issuer_url = strdup(value->data.scalar.value);
} else if (0 == strcmp(key->data.scalar.value, KEY_USER_AUTH_PROVIDER_CONFIG_REFRESH_TOKEN)) {
property->refresh_token = strdup(value->data.scalar.value);
}
}
} else if (value->type == YAML_MAPPING_NODE) {
if ((KUBECONFIG_PROPERTY_TYPE_USER == property->type) && (0 == strcmp(key->data.scalar.value, KEY_USER_EXEC))) {
property->exec = kubeconfig_property_create(KUBECONFIG_PROPERTY_TYPE_USER_EXEC);
if (property->exec) {
int rc = parse_kubeconfig_yaml_property_mapping(property->exec, document, value);
if (KUBECONFIG_PROPERTY_TYPE_USER == property->type) {
int rc = 0;
if (0 == strcmp(key->data.scalar.value, KEY_USER_EXEC)) {
property->exec = kubeconfig_property_create(KUBECONFIG_PROPERTY_TYPE_USER_EXEC);
if (!property->exec) {
fprintf(stderr, "Cannot allocate memory for kubeconfig exec for user %s.\n", property->name);
return -1;
}
rc = parse_kubeconfig_yaml_property_mapping(property->exec, document, value);
if (0 != rc) {
fprintf(stderr, "Cannot parse kubeconfig exec for user %s.\n", property->name);
return -1;
}
} else if (0 == strcmp(key->data.scalar.value, KEY_USER_AUTH_PROVIDER)) {
property->auth_provider = kubeconfig_property_create(KUBECONFIG_PROPERTY_TYPE_USER_AUTH_PROVIDER);
if (!property->auth_provider) {
fprintf(stderr, "Cannot allocate memory for kubeconfig auth provider for user %s.\n", property->name);
return -1;
}
rc = parse_kubeconfig_yaml_property_mapping(property->auth_provider, document, value);
if (0 != rc) {
fprintf(stderr, "Cannot parse kubeconfig auth provider for user %s.\n", property->name);
return -1;
}
} else {
fprintf(stderr, "Cannot allocate memory for kubeconfig exec for user %s.\n", property->name);
return -1;
parse_kubeconfig_yaml_property_mapping(property, document, value);
}
} else {
parse_kubeconfig_yaml_property_mapping(property, document, value);
@@ -530,3 +587,501 @@ int kubeyaml_parse_exec_crendential(ExecCredential_t * exec_credential, const ch
yaml_parser_delete(&parser);
return -1;
}
static int append_key_stringvalue_to_mapping_node(yaml_document_t * output_document, int parent_node, const char *key_string, const char *value_string)
{
int key = yaml_document_add_scalar(output_document, NULL, (yaml_char_t *) key_string, -1, YAML_PLAIN_SCALAR_STYLE);
if (!key) {
return -1;
}
int value = yaml_document_add_scalar(output_document, NULL, (yaml_char_t *) value_string, -1, YAML_PLAIN_SCALAR_STYLE);
if (!value) {
return -1;
}
if (!yaml_document_append_mapping_pair(output_document, parent_node, key, value)) {
return -1;
}
return 0;
}
int append_key_stringseq_to_mapping_node(yaml_document_t * output_document, int parent_node, const char *key_string, char **strings, int strings_count)
{
int key = yaml_document_add_scalar(output_document, NULL, (yaml_char_t *) key_string, -1, YAML_PLAIN_SCALAR_STYLE);
if (!key) {
return -1;
}
int seq = yaml_document_add_sequence(output_document, NULL, YAML_BLOCK_SEQUENCE_STYLE);
if (!seq) {
return -1;
}
if (!yaml_document_append_mapping_pair(output_document, parent_node, key, seq)) {
return -1;
}
for (int i = 0; i < strings_count; i++) {
int value = yaml_document_add_scalar(output_document, NULL, (yaml_char_t *) strings[i], -1, YAML_PLAIN_SCALAR_STYLE);
if (!value) {
return -1;
}
if (!yaml_document_append_sequence_item(output_document, seq, value)) {
return -1;
}
}
return 0;
}
int append_auth_provider_config_to_mapping_node(yaml_document_t * output_document, int parent_node, const kubeconfig_property_t * auth_provider_config)
{
/* Add 'name': '' */
if (auth_provider_config->name) {
if (-1 == append_key_stringvalue_to_mapping_node(output_document, parent_node, KEY_NAME, auth_provider_config->name)) {
return -1;
}
}
/* Add 'config': {} */
int key = yaml_document_add_scalar(output_document, NULL, (yaml_char_t *) KEY_USER_AUTH_PROVIDER_CONFIG, -1, YAML_PLAIN_SCALAR_STYLE);
if (!key) {
return -1;
}
int map = yaml_document_add_mapping(output_document, NULL, YAML_BLOCK_MAPPING_STYLE);
if (!map) {
return -1;
}
if (!yaml_document_append_mapping_pair(output_document, parent_node, key, map)) {
return -1;
}
/* Add 'access-token': '' */
if (auth_provider_config->access_token) {
if (-1 == append_key_stringvalue_to_mapping_node(output_document, map, KEY_USER_AUTH_PROVIDER_CONFIG_ACCESS_TOKEN, auth_provider_config->access_token)) {
return -1;
}
}
/* Add 'client-id': '' */
if (auth_provider_config->client_id) {
if (-1 == append_key_stringvalue_to_mapping_node(output_document, map, KEY_USER_AUTH_PROVIDER_CONFIG_CLIENT_ID, auth_provider_config->client_id)) {
return -1;
}
}
/* Add 'client-secret': '' */
if (auth_provider_config->client_secret) {
if (-1 == append_key_stringvalue_to_mapping_node(output_document, map, KEY_USER_AUTH_PROVIDER_CONFIG_CLIENT_SECRET, auth_provider_config->client_secret)) {
return -1;
}
}
/* Add 'cmd-path': '' */
if (auth_provider_config->cmd_path) {
if (-1 == append_key_stringvalue_to_mapping_node(output_document, map, KEY_USER_AUTH_PROVIDER_CONFIG_CMD_PATH, auth_provider_config->cmd_path)) {
return -1;
}
}
/* Add 'expires-on': '' */
if (auth_provider_config->expires_on) {
if (-1 == append_key_stringvalue_to_mapping_node(output_document, map, KEY_USER_AUTH_PROVIDER_CONFIG_EXPIRES_ON, auth_provider_config->expires_on)) {
return -1;
}
}
/* Add 'expiry': '' */
if (auth_provider_config->expiry) {
if (-1 == append_key_stringvalue_to_mapping_node(output_document, map, KEY_USER_AUTH_PROVIDER_CONFIG_EXPIRY, auth_provider_config->expiry)) {
return -1;
}
}
/* Add 'id-token': '' */
if (auth_provider_config->id_token) {
if (-1 == append_key_stringvalue_to_mapping_node(output_document, map, KEY_USER_AUTH_PROVIDER_CONFIG_ID_TOKEN, auth_provider_config->id_token)) {
return -1;
}
}
/* Add 'idp-certificate-authority': '' */
if (auth_provider_config->idp_certificate_authority) {
if (-1 == append_key_stringvalue_to_mapping_node(output_document, map, KEY_USER_AUTH_PROVIDER_CONFIG_IDP_CERTIFICATE_AUTHORITY, auth_provider_config->idp_certificate_authority)) {
return -1;
}
}
/* Add 'idp-certificate-authority-data': '' */
if (auth_provider_config->idp_certificate_authority_data) {
if (-1 == append_key_stringvalue_to_mapping_node(output_document, map, KEY_USER_AUTH_PROVIDER_CONFIG_IDP_CERTIFICATE_AUTHORITY_DATA, auth_provider_config->idp_certificate_authority_data)) {
return -1;
}
}
/* Add 'idp-issuer-url': '' */
if (auth_provider_config->idp_issuer_url) {
if (-1 == append_key_stringvalue_to_mapping_node(output_document, map, KEY_USER_AUTH_PROVIDER_CONFIG_IDP_ISSUE_URL, auth_provider_config->idp_issuer_url)) {
return -1;
}
}
/* Add 'refresh-token': '' */
if (auth_provider_config->refresh_token) {
if (-1 == append_key_stringvalue_to_mapping_node(output_document, map, KEY_USER_AUTH_PROVIDER_CONFIG_REFRESH_TOKEN, auth_provider_config->refresh_token)) {
return -1;
}
}
return 0;
}
int append_key_kvpseq_to_mapping_node(yaml_document_t * output_document, int parent_node, const char *key_string, keyValuePair_t ** kvps, int kvps_count)
{
int key = yaml_document_add_scalar(output_document, NULL, (yaml_char_t *) key_string, -1, YAML_PLAIN_SCALAR_STYLE);
if (!key) {
return -1;
}
int seq = yaml_document_add_sequence(output_document, NULL, YAML_BLOCK_SEQUENCE_STYLE);
if (!seq) {
return -1;
}
if (!yaml_document_append_mapping_pair(output_document, parent_node, key, seq)) {
return -1;
}
for (int i = 0; i < kvps_count; i++) {
int map = yaml_document_add_mapping(output_document, NULL, YAML_BLOCK_MAPPING_STYLE);
if (!map) {
return -1;
}
if (!yaml_document_append_sequence_item(output_document, seq, map)) {
return -1;
}
/* Add 'name': '' */
if (-1 == append_key_stringvalue_to_mapping_node(output_document, map, KEY_USER_EXEC_ENV_KEY, kvps[i]->key)) {
return -1;
}
/* Add 'value': '' */
if (-1 == append_key_stringvalue_to_mapping_node(output_document, map, KEY_USER_EXEC_ENV_VALUE, kvps[i]->value)) {
return -1;
}
}
return 0;
}
static int append_key_map_to_mapping_node(yaml_document_t * output_document, int parent_node, const char *key_string, const kubeconfig_property_t * property)
{
int key = yaml_document_add_scalar(output_document, NULL, (yaml_char_t *) key_string, -1, YAML_PLAIN_SCALAR_STYLE);
if (!key) {
return -1;
}
int map = yaml_document_add_mapping(output_document, NULL, YAML_BLOCK_MAPPING_STYLE);
if (!map) {
return -1;
}
if (!yaml_document_append_mapping_pair(output_document, parent_node, key, map)) {
return -1;
}
if (!property) {
return 0;
}
if (KUBECONFIG_PROPERTY_TYPE_CONTEXT == property->type) {
/* Add 'cluster': '' */
if (property->cluster) {
if (-1 == append_key_stringvalue_to_mapping_node(output_document, map, KEY_CLUSTER, property->cluster)) {
return -1;
}
}
/* Add 'namespace': '' */
if (property->namespace) {
if (-1 == append_key_stringvalue_to_mapping_node(output_document, map, KEY_NAMESPACE, property->namespace)) {
return -1;
}
}
/* Add 'user': '' */
if (property->user) {
if (-1 == append_key_stringvalue_to_mapping_node(output_document, map, KEY_USER, property->user)) {
return -1;
}
}
} else if (KUBECONFIG_PROPERTY_TYPE_CLUSTER == property->type) {
/* Add 'certificate-authority-data': '' */
if (property->certificate_authority_data) {
if (-1 == append_key_stringvalue_to_mapping_node(output_document, map, KEY_CERTIFICATE_AUTHORITY_DATA, property->certificate_authority_data)) {
return -1;
}
}
/* Add 'server': '' */
if (property->server) {
if (-1 == append_key_stringvalue_to_mapping_node(output_document, map, KEY_SERVER, property->server)) {
return -1;
}
}
} else if (KUBECONFIG_PROPERTY_TYPE_USER == property->type) {
/* Add 'auth-provider': {} */
if (property->auth_provider) {
if (-1 == append_key_map_to_mapping_node(output_document, map, KEY_USER_AUTH_PROVIDER, property->auth_provider)) {
return -1;
}
}
/* Add 'client-certificate-data': '' */
if (property->client_certificate_data) {
if (-1 == append_key_stringvalue_to_mapping_node(output_document, map, KEY_CLIENT_CERTIFICATE_DATA, property->client_certificate_data)) {
return -1;
}
}
/* Add 'client-key-data': '' */
if (property->client_key_data) {
if (-1 == append_key_stringvalue_to_mapping_node(output_document, map, KEY_CLIENT_KEY_DATA, property->client_key_data)) {
return -1;
}
}
/* Add 'exec': {} */
if (property->exec) {
if (-1 == append_key_map_to_mapping_node(output_document, map, KEY_USER_EXEC, property->exec)) {
return -1;
}
}
} else if (KUBECONFIG_PROPERTY_TYPE_USER_EXEC == property->type) {
/* Add 'apiVersion': '' */
if (property->apiVersion) {
if (-1 == append_key_stringvalue_to_mapping_node(output_document, map, KEY_APIVERSION, property->apiVersion)) {
return -1;
}
}
/* Add 'args': [] */
if (property->args && property->args_count > 0) {
if (-1 == append_key_stringseq_to_mapping_node(output_document, map, KEY_USER_EXEC_ARGS, property->args, property->args_count)) {
return -1;
}
}
/* Add 'command': '' */
if (property->command) {
if (-1 == append_key_stringvalue_to_mapping_node(output_document, map, KEY_USER_EXEC_COMMAND, property->command)) {
return -1;
}
}
/* Add 'env': [] */
if (property->envs && property->envs_count > 0) {
if (-1 == append_key_kvpseq_to_mapping_node(output_document, map, KEY_USER_EXEC_ENV, property->envs, property->envs_count)) {
return -1;
}
}
} else if (KUBECONFIG_PROPERTY_TYPE_USER_AUTH_PROVIDER == property->type) {
if (-1 == append_auth_provider_config_to_mapping_node(output_document, map, property)) {
return -1;
}
}
return 0;
}
static int append_key_seq_to_top_mapping_node(yaml_document_t * output_document, int parent_node, const char *first_level_key_string, const char *second_level_key_string,
kubeconfig_property_t ** properites, int properites_count)
{
int key = yaml_document_add_scalar(output_document, NULL, (yaml_char_t *) first_level_key_string, -1, YAML_PLAIN_SCALAR_STYLE);
if (!key) {
return -1;
}
int seq = yaml_document_add_sequence(output_document, NULL, YAML_BLOCK_SEQUENCE_STYLE);
if (!seq) {
return -1;
}
if (!yaml_document_append_mapping_pair(output_document, parent_node, key, seq)) {
return -1;
}
for (int i = 0; i < properites_count; i++) {
/* Add {}. */
int map = yaml_document_add_mapping(output_document, NULL, YAML_BLOCK_MAPPING_STYLE);
if (!map) {
return -1;
}
if (!yaml_document_append_sequence_item(output_document, seq, map)) {
return -1;
}
if (NULL != strstr(second_level_key_string, KEY_CLUSTER) || NULL != strstr(second_level_key_string, KEY_CONTEXT)) {
/* Add 'cluster/context': {} */
if (-1 == append_key_map_to_mapping_node(output_document, map, second_level_key_string, properites[i])) {
return -1;
}
}
/* Add 'name': '' */
if (-1 == append_key_stringvalue_to_mapping_node(output_document, map, KEY_NAME, properites[i]->name)) {
return -1;
}
if (NULL != strstr(second_level_key_string, KEY_USER)) {
/* Add 'user': {} */
if (-1 == append_key_map_to_mapping_node(output_document, map, second_level_key_string, properites[i])) {
return -1;
}
}
}
return 0;
}
static int fill_yaml_document(yaml_document_t * output_document, const kubeconfig_t * kubeconfig)
{
int root = yaml_document_add_mapping(output_document, NULL, YAML_BLOCK_MAPPING_STYLE);
if (!root) {
return -1;
}
/* Add 'apiVersion': '' */
if (-1 == append_key_stringvalue_to_mapping_node(output_document, root, KEY_APIVERSION, kubeconfig->apiVersion)) {
return -1;
}
/* Add 'clusters': {} */
if (-1 == append_key_seq_to_top_mapping_node(output_document, root, KEY_CLUSTERS, KEY_CLUSTER, kubeconfig->clusters, kubeconfig->clusters_count)) {
return -1;
}
/* Add 'contexts': {} */
if (-1 == append_key_seq_to_top_mapping_node(output_document, root, KEY_CONTEXTS, KEY_CONTEXT, kubeconfig->contexts, kubeconfig->contexts_count)) {
return -1;
}
/* Add 'current-context': '' */
if (-1 == append_key_stringvalue_to_mapping_node(output_document, root, KEY_CURRENT_CONTEXT, kubeconfig->current_context)) {
return -1;
}
/* Add 'kind': 'Config' */
if (-1 == append_key_stringvalue_to_mapping_node(output_document, root, KEY_KIND, kubeconfig->kind)) {
return -1;
}
/* Add 'preferences': {} */
if (-1 == append_key_map_to_mapping_node(output_document, root, KEY_PREFERENCES, NULL)) {
return -1;
}
/* Add 'users': {} */
if (-1 == append_key_seq_to_top_mapping_node(output_document, root, KEY_USERS, KEY_USER, kubeconfig->users, kubeconfig->users_count)) {
return -1;
}
return 0;
}
int kubeyaml_save_kubeconfig(const kubeconfig_t * kubeconfig)
{
static char fname[] = "kubeyaml_save_kubeconfig()";
if (!kubeconfig) {
return 0;
}
/* Set a file output. */
FILE *output = NULL;
if (kubeconfig->fileName) {
output = fopen(kubeconfig->fileName, "wb");
if (!output) {
fprintf(stderr, "%s: Cannot open the file %s.[%s]\n", fname, kubeconfig->fileName, strerror(errno));
return -1;
}
} else {
fprintf(stderr, "%s: The kubeconf file name needs be set by kubeconfig->fileName .\n", fname);
return -1;
}
yaml_emitter_t emitter;
yaml_document_t output_document;
memset(&emitter, 0, sizeof(emitter));
memset(&output_document, 0, sizeof(output_document));
/* Initialize the emitter object. */
if (!yaml_emitter_initialize(&emitter)) {
fprintf(stderr, "%s: Could not initialize the emitter object\n", fname);
return -1;
}
/* Set the emitter parameters. */
yaml_emitter_set_canonical(&emitter, 0);
yaml_emitter_set_unicode(&emitter, 1);
yaml_emitter_set_output_file(&emitter, output);
/* Create and emit the STREAM-START event. */
if (!yaml_emitter_open(&emitter)) {
goto emitter_error;
}
/* Create a output_document object. */
if (!yaml_document_initialize(&output_document, NULL, NULL, NULL, 0, 0)) {
goto document_error;
}
if (-1 == fill_yaml_document(&output_document, kubeconfig)) {
goto document_error;
}
/* Emit the event. */
if (!yaml_emitter_dump(&emitter, &output_document)) {
goto emitter_error;
}
yaml_emitter_flush(&emitter);
if (!yaml_emitter_close(&emitter)) {
goto emitter_error;
}
yaml_emitter_delete(&emitter);
yaml_document_delete(&output_document);
fclose(output);
return 0;
emitter_error:
switch (emitter.error) {
case YAML_MEMORY_ERROR:
fprintf(stderr, "%s: Memory error: Not enough memory for emitting\n", fname);
break;
case YAML_WRITER_ERROR:
fprintf(stderr, "%s: Writer error: %s\n", fname, emitter.problem);
break;
case YAML_EMITTER_ERROR:
fprintf(stderr, "%s: Emitter error: %s\n", fname, emitter.problem);
break;
default:
/* Couldn't happen. */
fprintf(stderr, "%s: Internal error\n", fname);
break;
}
yaml_document_delete(&output_document);
yaml_emitter_delete(&emitter);
fclose(output);
return -1;
document_error:
fprintf(stderr, "%s: Memory error: Not enough memory for creating a document\n", fname);
yaml_document_delete(&output_document);
yaml_emitter_delete(&emitter);
fclose(output);
return -1;
}

View File

@@ -54,6 +54,26 @@ extern "C" {
*/
int kubeyaml_parse_exec_crendential(ExecCredential_t * exec_credential, const char *exec_credential_string);
/*
* kubeyaml_save_kubeconfig
*
* Description:
*
* Save the kubeconfig to the file specified by kubeconfig->fileName
*
* Return:
*
* 0 Success
* -1 Failed
*
* Parameter:
*
* INT:
* kubeconfig: kubernetes cluster configuration including kubeconfig->fileName: kubernetes cluster configuration file name
*
*/
int kubeyaml_save_kubeconfig(const kubeconfig_t * kubeconfig);
#ifdef __cplusplus
}
#endif /* __cplusplus */