diff options
author | Sergey Poznyakoff <gray@gnu.org> | 2015-01-22 00:44:27 +0200 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org> | 2015-01-22 00:44:27 +0200 |
commit | 0c7bb2d5cb81eb6077a8907ce2049278fb8e47ce (patch) | |
tree | 7392dad30ac09b3ff32bfb2882710b5117fdde35 | |
parent | 03c5b9aac73c6a70b1c67f467bbd484d2a532f10 (diff) | |
download | eclat-0c7bb2d5cb81eb6077a8907ce2049278fb8e47ce.tar.gz eclat-0c7bb2d5cb81eb6077a8907ce2049278fb8e47ce.tar.bz2 |
authentication-provider instance-store does not require role name argument
* NEWS: Update.
* doc/eclat.conf.5: Update.
* lib/Makefile.am: Add new sources.
* lib/istore.c: New file.
* lib/path.c: New file.
* lib/libeclat.h (path_concat)
(instance_store_curl_new)
(instance_store_read): New protos.
* src/config.c (cb_authentication_provider): second argument is
optional for instance-store type.
New compound statement: instance-store.
* src/eclat.h (instance_store_base_url)
(instance_store_port,instance_store_document_path):
(instance_store_credentials_path): New externs.
* src/ispeek.c: Rewrite using new functions.
* src/util.c: Likewise.
-rw-r--r-- | NEWS | 7 | ||||
-rw-r--r-- | doc/eclat.conf.5 | 42 | ||||
-rw-r--r-- | lib/Makefile.am | 2 | ||||
-rw-r--r-- | lib/istore.c | 73 | ||||
-rw-r--r-- | lib/libeclat.h | 4 | ||||
-rw-r--r-- | lib/path.c | 37 | ||||
-rw-r--r-- | src/config.c | 72 | ||||
-rw-r--r-- | src/eclat.h | 5 | ||||
-rw-r--r-- | src/ispeek.c | 92 | ||||
-rw-r--r-- | src/util.c | 141 |
10 files changed, 301 insertions, 174 deletions
@@ -1,4 +1,4 @@ -Eclat NEWS -- history of user-visible changes. 2015-01-21 +Eclat NEWS -- history of user-visible changes. 2015-01-22 Copyright (C) 2012-2015 Sergey Poznyakoff See the end of file for copying conditions. @@ -28,7 +28,7 @@ Authentication provider is a service that supplies AWS access key ID and secret key. It is configured by the "authentication-provider" statement in the configuration file. The syntax is: - authentication-provider TYPE ARG; + authentication-provider TYPE [ARG]; TYPE can be one of: @@ -47,7 +47,8 @@ backward compatibility. - instance-store Credentials are obtained from the instance store. Second argument -supplies the name of the IAM role to use. +is optional. If present, it should be the name of the IAM role the +instance is launched with. * IAM support diff --git a/doc/eclat.conf.5 b/doc/eclat.conf.5 index dd9489c..8b20b48 100644 --- a/doc/eclat.conf.5 +++ b/doc/eclat.conf.5 @@ -13,7 +13,7 @@ .\" .\" You should have received a copy of the GNU General Public License .\" along with Eclat. If not, see <http://www.gnu.org/licenses/>. -.TH ECLAT.CONF 5 "January 21, 2015" "ECLAT" "Eclat User Reference" +.TH ECLAT.CONF 5 "January 22, 2015" "ECLAT" "Eclat User Reference" .SH NAME eclat.conf \- configuration file for .BR eclat (1). @@ -380,7 +380,7 @@ region us\-west\-2 ec2.us\-west\-2.amazonaws.com; .EE .SS AUTHENTICATION .TP -\fBauthentication\-provider\fR \fITYPE\fR \fIARG\fB;\fR +\fBauthentication\-provider\fR \fITYPE\fR [\fIARG\fR]\fB;\fR Defines authentication provider to use. \fIAuthentication provider\fR is a service that supplies AWS access key ID and secret key. See .BR eclat (1), @@ -397,9 +397,11 @@ If \fITYPE\fR is \fBfile\fR, the \fIARG\fR parameter is treated as a shell globbing pattern: all files matching this pattern are attempted in turn, until a keypair is found in one of them. -If \fITYPE\fR is \fBinstance\-store\fR, \fIARG\fR is the name of the -IAM role. Credentials will be obtained from the instance store for -that role. +If \fITYPE\fR is \fBinstance\-store\fR, credentials will be obtained +from the instance store. \fIARG\fR is optional. If supplied, it +should be the name of the IAM role this instance is launched with. +At the time of this writing, an instance can be associated with a +single role, which will be used by default. .TP \fBaccess\-file\fR \fIname\fR; This is a shortcut for \fBauthentication\-provider file \fIname\fR. @@ -446,6 +448,36 @@ By default the CA certificates shipped with .BR libcurl (3) will be used. You would rarely need to use \fBca\-file\fR or \fBca\-path\fR statements. +.SH INSTANCE STORE CONFIGURATION +The \fBinstance\-store\fR compound statement configures HTTP access to +the instance store. By default, \fBeclat\fR uses standard AWS values. +This statement is intended mainly as an aid in debugging: +.PP +.EX + instance\-store { + base\-url \fIURL\fR; + port \fINUMBER\fR; + document\-path \fISTRING\fR; + credentials\-path \fISTRING\fR; + } +.EE +.TP +.BI base\-url " URL" ; +Base URL to use, instead of +.BR http://169.254.169.254/latest . +.TP +.BI port " NUMBER" ; +Port to use (defaults to \fB80\fR). +.TP +.BI document\-path " STRING" ; +Pathname (relative to \fBbase\-url\fR) of the instance identity document +file. Default: +.BR dynamic/instance-identity/document . +.TP +.BI credentials\-path " STRING" ; +Pathname (relative to \fBbase\-url\fR) of the instance store +credentials directory. Default is +.BR meta-data/iam/security-credentials . .SH FORMAT DEFINITIONS This group of statements declares the formats to use. .TP diff --git a/lib/Makefile.am b/lib/Makefile.am index 445d949..5b47328 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -42,6 +42,7 @@ libeclat_a_SOURCES=\ forlangrm.h\ forlangrm.y\ forlanlex.l\ + istore.c\ json.h\ jsongrm.h\ jsongrm.y\ @@ -52,6 +53,7 @@ libeclat_a_SOURCES=\ hmac_sha256.c\ libeclat.h\ map.c\ + path.c\ q2url.c\ qaddparm.c\ qcreat.c\ diff --git a/lib/istore.c b/lib/istore.c new file mode 100644 index 0000000..dea3a53 --- /dev/null +++ b/lib/istore.c @@ -0,0 +1,73 @@ +/* This file is part of Eclat. + Copyright (C) 2012-2015 Sergey Poznyakoff. + + Eclat is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + Eclat is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Eclat. If not, see <http://www.gnu.org/licenses/>. */ + +#include "libeclat.h" +#include <sysexits.h> + +static size_t +acc_cb(void *ptr, size_t size, size_t nmemb, void *data) +{ + size_t realsize = size * nmemb; + struct grecs_txtacc *acc = data; + grecs_txtacc_grow(acc, ptr, realsize); + return realsize; +} + +CURL * +instance_store_curl_new(struct grecs_txtacc *acc) +{ + CURLcode res; + CURL *curl; + + curl = curl_easy_init(); + if (!curl) + die(EX_UNAVAILABLE, "curl_easy_init failed"); + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, acc_cb); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, acc); + return curl; +} + +int +instance_store_read(const char *url, CURL *curl) +{ + CURLcode res; + long http_resp; + char *text; + + curl_easy_setopt(curl, CURLOPT_URL, url); + + res = curl_easy_perform(curl); + if (res != CURLE_OK) + die(EX_UNAVAILABLE, "CURL: %s", curl_easy_strerror(res)); + + res = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_resp); + if (res != CURLE_OK) + die(EX_UNAVAILABLE, "CURL: %s", curl_easy_strerror(res)); + + switch (http_resp) { + case 200: + break; + + case 404: + return -1; + + default: + die(EX_UNAVAILABLE, "CURL: got response %3d, url %s", + http_resp, url); + } + return 0; +} diff --git a/lib/libeclat.h b/lib/libeclat.h index 4d6882e..e13864c 100644 --- a/lib/libeclat.h +++ b/lib/libeclat.h @@ -55,6 +55,10 @@ int eclat_trace_fun(CURL *handle, curl_infotype type, char *data, size_t size, void *userp); void eclat_set_curl_trace(CURL *curl, int lev); + +char *path_concat(const char *dir, const char *comp); +CURL *instance_store_curl_new(struct grecs_txtacc *acc); +int instance_store_read(const char *url, CURL *curl); void hmac_sha1(const void *text, size_t textlen, diff --git a/lib/path.c b/lib/path.c new file mode 100644 index 0000000..69ea292 --- /dev/null +++ b/lib/path.c @@ -0,0 +1,37 @@ +/* This file is part of Eclat. + Copyright (C) 2012-2015 Sergey Poznyakoff. + + Eclat is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + Eclat is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Eclat. If not, see <http://www.gnu.org/licenses/>. */ + +#include "libeclat.h" +#include <string.h> + +char * +path_concat(const char *dir, const char *comp) +{ + char *p; + size_t len = strlen(dir); + + while (len > 0 && dir[len-1] == '/') + --len; + + while (*comp == '/') + ++comp; + + p = grecs_malloc(len + strlen(comp) + 2); + memcpy(p, dir, len); + p[len++] = '/'; + strcpy(p + len, comp); + return p; +} diff --git a/src/config.c b/src/config.c index d3c8a66..ec676b7 100644 --- a/src/config.c +++ b/src/config.c @@ -261,37 +261,55 @@ cb_authentication_provider(enum grecs_callback_command cmd, grecs_value_t *value, void *cb_data) { - char *s; + char *type; + char *arg = NULL; if (cmd != grecs_callback_set_value) { grecs_error(locus, 0, "Unexpected block statement"); return 1; } - if (!value || value->type != GRECS_TYPE_ARRAY || value->v.arg.c != 2) { - grecs_error(locus, 0, "expected two values"); - return 1; - } - if (value->v.arg.v[0]->type != GRECS_TYPE_STRING) { - grecs_error(&value->v.arg.v[0]->locus, 0, - "first argument not a string"); - return 1; - } - if (value->v.arg.v[1]->type != GRECS_TYPE_STRING) { - grecs_error(&value->v.arg.v[1]->locus, 0, - "second argument not a string"); + if (!value) { + grecs_error(locus, 0, "expected one to two values"); return 1; } + if (value->type == GRECS_TYPE_ARRAY) { + if (value->v.arg.c != 2) { + grecs_error(locus, 0, "expected one to two values"); + return 1; + } + if (value->v.arg.v[0]->type != GRECS_TYPE_STRING) { + grecs_error(&value->v.arg.v[0]->locus, 0, + "first argument not a string"); + return 1; + } + if (value->v.arg.v[1]->type != GRECS_TYPE_STRING) { + grecs_error(&value->v.arg.v[1]->locus, 0, + "second argument not a string"); + return 1; + } - s = value->v.arg.v[0]->v.string; + type = value->v.arg.v[0]->v.string; + arg = value->v.arg.v[1]->v.string; + } else if (value->type == GRECS_TYPE_STRING) { + type = value->v.string; + arg = NULL; + } - if (strcmp(s, "file") == 0) { + if (strcmp(type, "file") == 0) { + if (arg) { + grecs_error(locus, 0, "requered argument missing"); + return 1; + } authentication_provider = authp_file; free(access_file_name); access_file_name = grecs_strdup(value->v.arg.v[1]->v.string); - } else if (strcmp(s, "instance-store") == 0) { + } else if (strcmp(type, "instance-store") == 0) { authentication_provider = authp_instance; free(access_key); - access_key = grecs_strdup(value->v.arg.v[1]->v.string); + if (arg) + access_key = grecs_strdup(arg); + else + access_key = NULL; } else { grecs_error(&value->locus, 0, "unknown provider"); } @@ -320,6 +338,22 @@ cb_access_file(enum grecs_callback_command cmd, return 0; } +static struct grecs_keyword instance_store_kw[] = { + { "base-url", "URL", + "Base URL of the instance store", + grecs_type_string, GRECS_DFLT, &instance_store_base_url }, + { "port", "NUMBER", + "Port number", + grecs_type_ushort, GRECS_DFLT, &instance_store_port }, + { "document-path", "PATH", + "Path to the instance identity document file", + grecs_type_ushort, GRECS_DFLT, &instance_store_document_path }, + { "credentials-path", "PATH", + "Path to the instance store credentials directory", + grecs_type_ushort, GRECS_DFLT, &instance_store_credentials_path }, + { NULL } +}; + static struct grecs_keyword eclat_kw[] = { { "default-endpoint", "hostname", "Set default EC2 endpoint", @@ -370,6 +404,10 @@ static struct grecs_keyword eclat_kw[] = { { "translate", NULL, "Use ID translation by default", grecs_type_bool, GRECS_DFLT, &translation_enabled }, + { "instance-store", NULL, + "Configure instance store parameters", + grecs_type_section, GRECS_DFLT, + NULL, 0, NULL, NULL, instance_store_kw }, { NULL } }; diff --git a/src/eclat.h b/src/eclat.h index e5a2c21..d83b8fa 100644 --- a/src/eclat.h +++ b/src/eclat.h @@ -64,6 +64,11 @@ extern int translation_enabled; extern char *custom_map; extern enum eclat_confirm_mode confirm_mode; +extern char *instance_store_base_url; +extern unsigned short instance_store_port; +extern char *instance_store_document_path; +extern char *instance_store_credentials_path; + typedef int (*config_finish_hook_t) (void*); void add_config_finish_hook(config_finish_hook_t fun, void *data); diff --git a/src/ispeek.c b/src/ispeek.c index 95e1a4c..2c518cb 100644 --- a/src/ispeek.c +++ b/src/ispeek.c @@ -40,81 +40,26 @@ char *delim = ":"; int recursive; long port; -static char * -make_url(const char *url, const char *path) -{ - char *p; - size_t len = strlen(url); - - while (len > 0 && url[len-1] == '/') - --len; - - while (*path == '/') - ++path; - - p = grecs_malloc(len + strlen(path) + 2); - memcpy(p, url, len); - p[len++] = '/'; - strcpy(p + len, path); - return p; -} - -static size_t -acc_cb(void *ptr, size_t size, size_t nmemb, void *data) -{ - size_t realsize = size * nmemb; - struct grecs_txtacc *acc = data; - - grecs_txtacc_grow(acc, ptr, realsize); - - return realsize; -} +struct closure { + char *text; + CURL *curl; + struct grecs_txtacc *acc; + char **argv; +}; static char * read_from(const char *path, CURL *curl, struct grecs_txtacc *acc) { - CURLcode res; - long http_resp; - char *text; char *url; - - url = make_url(base_url, path); - curl_easy_setopt(curl, CURLOPT_URL, url); - - res = curl_easy_perform(curl); - if (res != CURLE_OK) - die(EX_UNAVAILABLE, "CURL: %s", curl_easy_strerror(res)); - - res = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_resp); - if (res != CURLE_OK) - die(EX_UNAVAILABLE, "CURL: %s", curl_easy_strerror(res)); - - grecs_txtacc_grow_char(acc, 0); - text = grecs_txtacc_finish(acc, 0); - switch (http_resp) { - case 200: - break; + url = path_concat(base_url, path); - case 404: - grecs_txtacc_free_string(acc, text); + if (instance_store_read(url, curl)) return NULL; - - default: - die(EX_UNAVAILABLE, "CURL: got response %3d, url %s", - http_resp, url); - } - free(url); - return text; + grecs_txtacc_grow_char(acc, 0); + return grecs_txtacc_finish(acc, 0); } -struct closure { - char *text; - CURL *curl; - struct grecs_txtacc *acc; - char **argv; -}; - static void print_dir(const char *path, struct closure *cl); static void @@ -149,11 +94,11 @@ print_dir(const char *path, struct closure *cl) printf("%s%s\n", path, ws.ws_wordv[i]); if (recursive) { if (isroot) { - char *p = make_url(path, ws.ws_wordv[i]); - url = make_url(p, "/"); + char *p = path_concat(path, ws.ws_wordv[i]); + url = path_concat(p, "/"); free(p); } else if (ws.ws_wordv[i][strlen(ws.ws_wordv[i]) - 1] == '/') { - url = make_url(path, ws.ws_wordv[i]); + url = path_concat(path, ws.ws_wordv[i]); } else continue; list(url, cl); @@ -245,18 +190,11 @@ ispeek_do(char **argv) const char *path = *argv++; char *text; - curl = curl_easy_init(); - if (!curl) - die(EX_UNAVAILABLE, "curl_easy_init failed"); + acc = grecs_txtacc_create(); + curl = instance_store_curl_new(acc); if (port) curl_easy_setopt(curl, CURLOPT_PORT, (long) port); - curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); eclat_set_curl_trace(curl, debug_level(0)); - - acc = grecs_txtacc_create(); - - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, acc_cb); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, acc); text = read_from(path, curl, acc); if (text) { @@ -367,117 +367,113 @@ canonattrname(char **attrs, const char *arg, char *delim, size_t *plen) } return NULL; } - -static size_t -get_meta_data_cb(void *ptr, size_t size, size_t nmemb, void *data) -{ - size_t realsize = size * nmemb; - char *v; - - if (debug_level(ECLAT_DEBCAT_MAIN) >= 10) - debug_printf("Got %*.*s", realsize, realsize, ptr); - - v = grecs_malloc(realsize + 1); - memcpy(v, ptr, realsize); - v[realsize] = 0; - *(char**)data = v; - return realsize; -} - -char * -eclat_get_instance_meta_data(char *url) + +char *instance_store_base_url = "http://169.254.169.254/latest"; +unsigned short instance_store_port; +char *instance_store_document_path = "dynamic/instance-identity/document"; +char *instance_store_credentials_path = "meta-data/iam/security-credentials"; + +static CURL * +get_curl(struct grecs_txtacc *acc) { CURLcode res; - CURL *curl = curl_easy_init(); - char *retval = NULL; - long http_resp; - - if (!curl) - die(EX_UNAVAILABLE, "curl_easy_init failed"); + CURL *curl = instance_store_curl_new(acc); - if (debug_level(ECLAT_DEBCAT_CURL)) { - curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); - if (debug_level(ECLAT_DEBCAT_CURL) > 1) - curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, - eclat_trace_fun); - } - - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, get_meta_data_cb); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, &retval); - - curl_easy_setopt(curl, CURLOPT_URL, url); - - res = curl_easy_perform(curl); - - if (res != CURLE_OK) - die(EX_UNAVAILABLE, "CURL: %s", curl_easy_strerror(res)); - - res = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_resp); - if (res != CURLE_OK) - die(EX_UNAVAILABLE, "CURL: %s", curl_easy_strerror(res)); - - if (http_resp != 200) - die(EX_UNAVAILABLE, "CURL: unexpected error code %3ld", res); - - curl_easy_cleanup(curl); - - return retval; + eclat_set_curl_trace(curl, debug_level(ECLAT_DEBCAT_CURL)); + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); + if (instance_store_port) + curl_easy_setopt(curl, CURLOPT_PORT, + (long) instance_store_port); + return curl; } -#define URL_INST_DOCUMENT\ - "http://169.254.169.254/latest/dynamic/instance-identity/document" - char * eclat_get_instance_zone() { char *doc; struct json_object *obj, *p; char *retval = NULL; - - doc = eclat_get_instance_meta_data(URL_INST_DOCUMENT); - + CURL *curl; + char *url; + struct grecs_txtacc *acc; + + acc = grecs_txtacc_create(); + curl = get_curl(acc); + url = path_concat(instance_store_base_url, + instance_store_document_path); + if (instance_store_read(url, curl)) + doc = NULL; + else { + grecs_txtacc_grow_char(acc, 0); + doc = grecs_txtacc_finish(acc, 0); + } + free(url); + curl_easy_cleanup(curl); + if (!doc) + return NULL; obj = json_parse_string(doc, strlen(doc)); if (!obj) die(EX_DATAERR, "%s: near %s", json_err_diag, json_err_ptr); - free(doc); p = json_object_lookup(obj, "region"); if (p && p->type == json_string) retval = grecs_strdup(p->v.s); + grecs_txtacc_free(acc); json_object_free(obj); return retval; } -#define URL_INST_CREDS\ - "http://169.254.169.254/latest/meta-data/iam/security-credentials/" - void eclat_get_instance_creds(char *id, char **access_key_ptr, char **secret_key_ptr, char **token_ptr) { + CURL *curl; char *url = NULL; - size_t s; + char *s; char *doc; struct json_object *obj, *p; int err = 0; - - s = sizeof(URL_INST_CREDS) + strlen(id); - url = grecs_malloc(s); - strcpy(url, URL_INST_CREDS); - strcat(url, id); - - doc = eclat_get_instance_meta_data(url); - + struct grecs_txtacc *acc; + + acc = grecs_txtacc_create(); + curl = get_curl(acc); + url = path_concat(instance_store_base_url, + instance_store_credentials_path); + if (id) { + s = url; + url = path_concat(s, id); + free(s); + } + if (instance_store_read(url, curl)) + die(EX_UNAVAILABLE, "url %s: not found ", url); + else { + grecs_txtacc_grow_char(acc, 0); + doc = grecs_txtacc_finish(acc, 0); + } + if (!id) { + size_t len = strcspn(doc, "\r\n"); + doc[len] = 0; + s = url; + url = path_concat(s, doc); + free(s); + if (instance_store_read(url, curl)) + die(EX_UNAVAILABLE, "url %s: not found ", url); + else { + grecs_txtacc_grow_char(acc, 0); + doc = grecs_txtacc_finish(acc, 0); + } + } + free(url); + obj = json_parse_string(doc, strlen(doc)); if (!obj) die(EX_DATAERR, "%s: near %s", json_err_diag, json_err_ptr); - free(doc); p = json_object_lookup(obj, "AccessKeyId"); if (p && p->type == json_string) @@ -497,6 +493,7 @@ eclat_get_instance_creds(char *id, char **access_key_ptr, char **secret_key_ptr, else err = 1; + grecs_txtacc_free(acc); json_object_free(obj); if (err) |