aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--NEWS7
-rw-r--r--doc/eclat.conf.542
-rw-r--r--lib/Makefile.am2
-rw-r--r--lib/istore.c73
-rw-r--r--lib/libeclat.h4
-rw-r--r--lib/path.c37
-rw-r--r--src/config.c72
-rw-r--r--src/eclat.h5
-rw-r--r--src/ispeek.c92
-rw-r--r--src/util.c141
10 files changed, 301 insertions, 174 deletions
diff --git a/NEWS b/NEWS
index c30d3fb..c8eb0e2 100644
--- a/NEWS
+++ b/NEWS
@@ -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) {
diff --git a/src/util.c b/src/util.c
index 88e3465..9220a11 100644
--- a/src/util.c
+++ b/src/util.c
@@ -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)

Return to:

Send suggestions and report system problems to the System administrator.