MEDIUM: cfgparse: expand environment variables

Environment variables were expandables only in adresses.
Now there are expandables everywhere in the configuration file within
double quotes.

This patch breaks compatibility with the previous behavior of
environment variables in adresses, you must enclose adresses with double
quotes to make it work.
This commit is contained in:
William Lallemand 2015-05-12 14:27:13 +02:00 committed by Willy Tarreau
parent 64e84516c4
commit b2f07451e5
2 changed files with 137 additions and 33 deletions

View File

@ -40,8 +40,9 @@ Summary
2. Configuring HAProxy
2.1. Configuration file format
2.2. Quoting and escaping
2.3. Time format
2.4. Examples
2.3. Environment variables
2.4. Time format
2.5. Examples
3. Global parameters
3.1. Process management and security
@ -397,7 +398,11 @@ the interpretation of:
' single quote as a strong quoting delimiter
# hash as a comment start
But interpretation of escaping and special characters are not prevented by weak
Weak quoting permits the interpretation of variables, if you want to use a non
-interpreted dollar within a double quoted string, you should escape it with a
backslash ("\$"), it does not work outside weak quoting.
Interpretation of escaping and special characters are not prevented by weak
quoting.
Strong quoting is achieved by using single quotes (''). Inside single quotes,
@ -421,7 +426,26 @@ equivalent, it allows you to perform concatenation.
reqrep "^([^ :]*)\ /static/(.*)" "\1\ /\2"
2.3. Time format
2.3. Environment variables
--------------------------
HAProxy's configuration supports environment variables. Those variables are
interpreted only within double quotes. Variables are expanded during the
configuration parsing. Variable names must be preceded by a dollar ("$") and
optionally enclosed with braces ("{}") similarly to what is done in Bourne
shell. Variable names can contain alphanumerical characters or the character
underscore ("_") but should not start with a digit.
Example:
bind "fd@${FD_APP1}"
log "${LOCAL_SYSLOG}:514" local0 notice # send to local server
user "$HAPROXY_USER"
2.4. Time format
----------------
Some parameters involve values representing time, such as timeouts. These
@ -640,10 +664,8 @@ log <address> [len <length>] <facility> [max level [min level]]
the chroot) and uid/gid (be sure the path is appropriately
writeable).
Any part of the address string may reference any number of environment
variables by preceding their name with a dollar sign ('$') and
optionally enclosing them with braces ('{}'), similarly to what is done
in Bourne shell.
You may want to reference some environment variables in the address
parameter, see section 2.3 about environment variables.
<length> is an optional maximum line length. Log lines larger than this value
will be truncated before being sent. The reason is that syslog
@ -1318,9 +1340,8 @@ peer <peername> <ip>:<port>
peer name. This makes it easier to maintain coherent configuration files
across all peers.
Any part of the address string may reference any number of environment
variables by preceding their name with a dollar sign ('$') and optionally
enclosing them with braces ('{}'), similarly to what is done in Bourne shell.
You may want to reference some environment variables in the address
parameter, see section 2.3 about environment variables.
Example:
peers mypeers
@ -2011,10 +2032,9 @@ bind /<path> [, ...] [param*]
- 'fd@<n>' -> use file descriptor <n> inherited from the
parent. The fd must be bound and may or may not already
be listening.
Any part of the address string may reference any number of
environment variables by preceding their name with a dollar
sign ('$') and optionally enclosing them with braces ('{}'),
similarly to what is done in Bourne shell.
You may want to reference some environment variables in the
address parameter, see section 2.3 about environment
variables.
<port_range> is either a unique TCP port, or a port range for which the
proxy will accept connections for the IP address specified
@ -2072,7 +2092,7 @@ bind /<path> [, ...] [param*]
bind unix@ssl-frontend.sock user root mode 600 accept-proxy
listen external_bind_app1
bind fd@${FD_APP1}
bind "fd@${FD_APP1}"
See also : "source", "option forwardfor", "unix-bind" and the PROXY protocol
documentation, and section 5 about bind options.
@ -3846,10 +3866,8 @@ no log
inside the chroot) and uid/gid (be sure the path is
appropriately writeable).
Any part of the address string may reference any number of
environment variables by preceding their name with a dollar
sign ('$') and optionally enclosing them with braces ('{}'),
similarly to what is done in Bourne shell.
You may want to reference some environment variables in the
address parameter, see section 2.3 about environment variables.
<length> is an optional maximum line length. Log lines larger than this
value will be truncated before being sent. The reason is that
@ -3896,7 +3914,7 @@ no log
log global
log 127.0.0.1:514 local0 notice # only send important events
log 127.0.0.1:514 local0 notice notice # same but limit output level
log ${LOCAL_SYSLOG}:514 local0 notice # send to local server
log "${LOCAL_SYSLOG}:514" local0 notice # send to local server
log-format <string>
@ -6525,10 +6543,9 @@ server <name> <address>[:[port]] [param*]
- 'ipv6@' -> address is always IPv6
- 'unix@' -> address is a path to a local unix socket
- 'abns@' -> address is in abstract namespace (Linux only)
Any part of the address string may reference any number of
environment variables by preceding their name with a dollar
sign ('$') and optionally enclosing them with braces ('{}'),
similarly to what is done in Bourne shell.
You may want to reference some environment variables in the
address parameter, see section 2.3 about environment
variables.
<port> is an optional port specification. If set, all connections will
be sent to this port. If unset, the same port the client
@ -6544,9 +6561,9 @@ server <name> <address>[:[port]] [param*]
server first 10.1.1.1:1080 cookie first check inter 1000
server second 10.1.1.2:1080 cookie second check inter 1000
server transp ipv4@
server backup ${SRV_BACKUP}:1080 backup
server www1_dc1 ${LAN_DC1}.101:80
server www1_dc2 ${LAN_DC2}.101:80
server backup "${SRV_BACKUP}:1080" backup
server www1_dc1 "${LAN_DC1}.101:80"
server www1_dc2 "${LAN_DC2}.101:80"
See also: "default-server", "http-send-name-header" and section 5 about
server options
@ -6572,10 +6589,8 @@ source <addr>[:<port>] [interface <name>]
- 'ipv6@' -> address is always IPv6
- 'unix@' -> address is a path to a local unix socket
- 'abns@' -> address is in abstract namespace (Linux only)
Any part of the address string may reference any number of
environment variables by preceding their name with a dollar
sign ('$') and optionally enclosing them with braces ('{}'),
similarly to what is done in Bourne shell.
You may want to reference some environment variables in the address
parameter, see section 2.3 about environment variables.
<port> is an optional port. It is normally not needed but may be useful
in some very specific contexts. The default value of zero means

View File

@ -6284,7 +6284,7 @@ int readcfgfile(const char *file)
int readbytes = 0;
if ((thisline = malloc(sizeof(*thisline) * linesize)) == NULL) {
Alert("parsing [%s:%d] : out of memory.\n", file, linenum);
Alert("parsing [%s] : out of memory.\n", file);
return -1;
}
@ -6304,6 +6304,7 @@ int readcfgfile(const char *file)
if ((f=fopen(file,"r")) == NULL)
return -1;
next_line:
while (fgets(thisline + readbytes, linesize - readbytes, f) != NULL) {
int arg, kwm = KWM_STD;
char *end;
@ -6404,6 +6405,9 @@ int readcfgfile(const char *file)
} else if (line[1] == '\'') {
*line = '\'';
skip = 1;
} else if (line[1] == '$' && dquote) { /* escaping of $ only inside double quotes */
*line = '$';
skip = 1;
}
if (skip) {
memmove(line + 1, line + 1 + skip, end - (line + skip));
@ -6423,10 +6427,95 @@ int readcfgfile(const char *file)
line++;
args[++arg] = line;
}
else if (dquote && *line == '$') {
/* environment variables are evaluated inside double quotes */
char *var_beg;
char *var_end;
char save_char;
char *value;
int val_len;
int newlinesize;
int braces = 0;
var_beg = line + 1;
var_end = var_beg;
if (*var_beg == '{') {
var_beg++;
var_end++;
braces = 1;
}
if (!isalpha((int)(unsigned char)*var_beg) && *var_beg != '_') {
Alert("parsing [%s:%d] : Variable expansion: Unrecognized character '%c' in variable name.\n", file, linenum, *var_beg);
err_code |= ERR_ALERT | ERR_FATAL;
goto next_line; /* skip current line */
}
while (isalnum((int)(unsigned char)*var_end) || *var_end == '_')
var_end++;
save_char = *var_end;
*var_end = '\0';
value = getenv(var_beg);
*var_end = save_char;
val_len = value ? strlen(value) : 0;
if (braces) {
if (*var_end == '}') {
var_end++;
braces = 0;
} else {
Alert("parsing [%s:%d] : Variable expansion: Mismatched braces.\n", file, linenum);
err_code |= ERR_ALERT | ERR_FATAL;
goto next_line; /* skip current line */
}
}
newlinesize = (end - thisline) - (var_end - line) + val_len + 1;
/* if not enough space in thisline */
if (newlinesize > linesize) {
char *newline;
newline = realloc(thisline, newlinesize * sizeof(*thisline));
if (newline == NULL) {
Alert("parsing [%s:%d] : Variable expansion: Not enough memory.\n", file, linenum);
err_code |= ERR_ALERT | ERR_FATAL;
goto next_line; /* slip current line */
}
/* recompute pointers if realloc returns a new pointer */
if (newline != thisline) {
int i;
int diff;
for (i = 0; i <= arg; i++) {
diff = args[i] - thisline;
args[i] = newline + diff;
}
diff = var_end - thisline;
var_end = newline + diff;
diff = end - thisline;
end = newline + diff;
diff = line - thisline;
line = newline + diff;
thisline = newline;
}
linesize = newlinesize;
}
/* insert value inside the line */
memmove(line + val_len, var_end, end - var_end + 1);
memcpy(line, value, val_len);
end += val_len - (var_end - line);
line += val_len;
}
else {
line++;
}
}
if (dquote) {
Alert("parsing [%s:%d] : Mismatched double quotes.\n", file, linenum);
err_code |= ERR_ALERT | ERR_FATAL;