Skip to content

Commit

Permalink
Support environment variables in the config
Browse files Browse the repository at this point in the history
* Adds support for environment variables in webdis.json
* Values starting with a `$` and in all caps are interpreted as
environment variables
  • Loading branch information
jessie-murray authored and nicolasff committed Jun 1, 2020
1 parent f22a295 commit 1872fac
Show file tree
Hide file tree
Showing 2 changed files with 84 additions and 11 deletions.
14 changes: 14 additions & 0 deletions README.markdown
Expand Up @@ -65,6 +65,7 @@ f0a2763fd456
* Connects to Redis using a TCP or UNIX socket.
* Restricted commands by IP range (CIDR subnet + mask) or HTTP Basic Auth, returning 403 errors.
* Possible Redis authentication in the config file.
* Environment variables can be used as values in the config file, starting with `$` and in all caps (e.g. `$REDIS_HOST`).
* Pub/Sub using `Transfer-Encoding: chunked`, works with JSONP as well. Webdis can be used as a Comet server.
* Drop privileges on startup.
* Custom Content-Type using a pre-defined file extension, or with `?type=some/thing`.
Expand Down Expand Up @@ -144,6 +145,19 @@ Examples:
</pre>
ACLs are interpreted in order, later authorizations superseding earlier ones if a client matches several. The special value "*" matches all commands.

# Environment variables

Environment variables can be used in `webdis.json` to read values from the environment instead of using constant values.
For this, the value must be a string starting with a dollar symbol and written in all caps. For example, to make the redis host and port configurable via environment variables, use the following:

<pre>
{
"redis_host": "$REDIS_HOST",
"redis_port": "$REDIS_PORT",
[...]
}
</pre>

# JSON output
JSON is the default output format. Each command returns a JSON object with the command as a key and the result as a value.

Expand Down
81 changes: 70 additions & 11 deletions src/conf.c
Expand Up @@ -16,6 +16,49 @@
static struct acl *
conf_parse_acls(json_t *jtab);

int
conf_str_allcaps(const char *s, const size_t sz) {
size_t i;
for(i = 0; i < sz; i++) {
if(s[i] != toupper(s[i])) {
return 0;
}
}
return 1;
}

char *
conf_string_or_envvar(const char *val) {
if(!val) {
return strdup(""); /* safe value for atoi/aol */
}
size_t val_len = strlen(val);
if(val_len >= 2 && val[0] == '$' && conf_str_allcaps(val+1, val_len-1)) {
char *env_val = getenv(val + 1);
if(env_val) { /* found in environment */
return strdup(env_val);
} else {
fprintf(stderr, "No value found for env var %s\n", val+1);
}
}
/* duplicate string coming from JSON parser */
return strdup(val);
}

int
atoi_free(char *s) {
int val = atoi(s);
free(s);
return val;
}

int
is_true_free(char *s) {
int val = strcasecmp(s, "true") == 0 ? 1 : 0;
free(s);
return val;
}

struct conf *
conf_read(const char *filename) {

Expand Down Expand Up @@ -52,51 +95,67 @@ conf_read(const char *filename) {

if(strcmp(json_object_iter_key(kv), "redis_host") == 0 && json_typeof(jtmp) == JSON_STRING) {
free(conf->redis_host);
conf->redis_host = strdup(json_string_value(jtmp));
conf->redis_host = conf_string_or_envvar(json_string_value(jtmp));
} else if(strcmp(json_object_iter_key(kv), "redis_port") == 0 && json_typeof(jtmp) == JSON_INTEGER) {
conf->redis_port = (int)json_integer_value(jtmp);
} else if(strcmp(json_object_iter_key(kv), "redis_port") == 0 && json_typeof(jtmp) == JSON_STRING) {
conf->redis_port = atoi_free(conf_string_or_envvar(json_string_value(jtmp)));
} else if(strcmp(json_object_iter_key(kv), "redis_auth") == 0 && json_typeof(jtmp) == JSON_STRING) {
conf->redis_auth = strdup(json_string_value(jtmp));
conf->redis_auth = conf_string_or_envvar(json_string_value(jtmp));
} else if(strcmp(json_object_iter_key(kv), "http_host") == 0 && json_typeof(jtmp) == JSON_STRING) {
free(conf->http_host);
conf->http_host = strdup(json_string_value(jtmp));
conf->http_host = conf_string_or_envvar(json_string_value(jtmp));
} else if(strcmp(json_object_iter_key(kv), "http_port") == 0 && json_typeof(jtmp) == JSON_INTEGER) {
conf->http_port = (int)json_integer_value(jtmp);
} else if(strcmp(json_object_iter_key(kv), "http_port") == 0 && json_typeof(jtmp) == JSON_STRING) {
conf->http_port = atoi_free(conf_string_or_envvar(json_string_value(jtmp)));
} else if(strcmp(json_object_iter_key(kv), "http_max_request_size") == 0 && json_typeof(jtmp) == JSON_INTEGER) {
conf->http_max_request_size = (size_t)json_integer_value(jtmp);
} else if(strcmp(json_object_iter_key(kv), "http_max_request_size") == 0 && json_typeof(jtmp) == JSON_STRING) {
conf->http_max_request_size = (size_t) atoi_free(conf_string_or_envvar(json_string_value(jtmp)));
} else if(strcmp(json_object_iter_key(kv), "threads") == 0 && json_typeof(jtmp) == JSON_INTEGER) {
conf->http_threads = (int)json_integer_value(jtmp);
} else if(strcmp(json_object_iter_key(kv), "threads") == 0 && json_typeof(jtmp) == JSON_STRING) {
conf->http_threads = atoi_free(conf_string_or_envvar(json_string_value(jtmp)));
} else if(strcmp(json_object_iter_key(kv), "acl") == 0 && json_typeof(jtmp) == JSON_ARRAY) {
conf->perms = conf_parse_acls(jtmp);
} else if(strcmp(json_object_iter_key(kv), "user") == 0 && json_typeof(jtmp) == JSON_STRING) {
struct passwd *u;
if((u = getpwnam(json_string_value(jtmp)))) {
if((u = getpwnam(conf_string_or_envvar(json_string_value(jtmp))))) {
conf->user = u->pw_uid;
}
} else if(strcmp(json_object_iter_key(kv), "group") == 0 && json_typeof(jtmp) == JSON_STRING) {
struct group *g;
if((g = getgrnam(json_string_value(jtmp)))) {
if((g = getgrnam(conf_string_or_envvar(json_string_value(jtmp))))) {
conf->group = g->gr_gid;
}
} else if(strcmp(json_object_iter_key(kv),"logfile") == 0 && json_typeof(jtmp) == JSON_STRING){
conf->logfile = strdup(json_string_value(jtmp));
conf->logfile = conf_string_or_envvar(json_string_value(jtmp));
} else if(strcmp(json_object_iter_key(kv),"verbosity") == 0 && json_typeof(jtmp) == JSON_INTEGER){
int tmp = json_integer_value(jtmp);
if(tmp < 0) conf->verbosity = WEBDIS_ERROR;
else if(tmp > (int)WEBDIS_DEBUG) conf->verbosity = WEBDIS_DEBUG;
else conf->verbosity = (log_level)tmp;
} else if(strcmp(json_object_iter_key(kv), "daemonize") == 0 && json_typeof(jtmp) == JSON_TRUE) {
conf->daemonize = 1;
} else if(strcmp(json_object_iter_key(kv), "daemonize") == 0 && json_typeof(jtmp) == JSON_STRING) {
conf->daemonize = is_true_free(conf_string_or_envvar(json_string_value(jtmp)));
} else if(strcmp(json_object_iter_key(kv),"pidfile") == 0 && json_typeof(jtmp) == JSON_STRING){
conf->pidfile = strdup(json_string_value(jtmp));
conf->pidfile = conf_string_or_envvar(json_string_value(jtmp));
} else if(strcmp(json_object_iter_key(kv), "websockets") == 0 && json_typeof(jtmp) == JSON_TRUE) {
conf->websockets = 1;
} else if(strcmp(json_object_iter_key(kv), "websockets") == 0 && json_typeof(jtmp) == JSON_STRING) {
conf->websockets = is_true_free(conf_string_or_envvar(json_string_value(jtmp)));
} else if(strcmp(json_object_iter_key(kv), "database") == 0 && json_typeof(jtmp) == JSON_INTEGER) {
conf->database = json_integer_value(jtmp);
} else if(strcmp(json_object_iter_key(kv), "database") == 0 && json_typeof(jtmp) == JSON_STRING) {
conf->database = atoi_free(conf_string_or_envvar(json_string_value(jtmp)));
} else if(strcmp(json_object_iter_key(kv), "pool_size") == 0 && json_typeof(jtmp) == JSON_INTEGER) {
conf->pool_size_per_thread = json_integer_value(jtmp);
} else if(strcmp(json_object_iter_key(kv), "pool_size") == 0 && json_typeof(jtmp) == JSON_STRING) {
conf->pool_size_per_thread = atoi_free(conf_string_or_envvar(json_string_value(jtmp)));
} else if(strcmp(json_object_iter_key(kv), "default_root") == 0 && json_typeof(jtmp) == JSON_STRING) {
conf->default_root = strdup(json_string_value(jtmp));
conf->default_root = conf_string_or_envvar(json_string_value(jtmp));
}
}

Expand Down Expand Up @@ -127,7 +186,7 @@ acl_read_commands(json_t *jlist, struct acl_commands *ac) {
json_t *jelem = json_array_get(jlist, i);
if(json_typeof(jelem) == JSON_STRING) {
size_t sz;
const char *s = json_string_value(jelem);
const char *s = conf_string_or_envvar(json_string_value(jelem));
sz = strlen(s);

ac->commands[cur] = calloc(1 + sz, 1);
Expand All @@ -150,7 +209,7 @@ conf_parse_acl(json_t *j) {
const char *s;
char *p, *ip;

s = json_string_value(jcidr);
s = conf_string_or_envvar(json_string_value(jcidr));
p = strchr(s, '/');
if(!p) {
ip = strdup(s);
Expand All @@ -172,7 +231,7 @@ conf_parse_acl(json_t *j) {
base64_encodestate b64;
int pos;
char *p;
const char *plain = json_string_value(jbasic);
const char *plain = conf_string_or_envvar(json_string_value(jbasic));
size_t len, plain_len = strlen(plain) + 0;
len = (plain_len + 8) * 8 / 6;
a->http_basic_auth = calloc(len, 1);
Expand Down

0 comments on commit 1872fac

Please sign in to comment.