diff --git a/doc/configuration.txt b/doc/configuration.txt index b02a8f7cb..0b0f4176f 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -1775,6 +1775,7 @@ The following keywords are supported in the "global" section : - set-dumpable - set-var - setenv + - setenv-sd-listen-fds - ssl-default-bind-ciphers - ssl-default-bind-ciphersuites - ssl-default-bind-client-sigalgs @@ -3120,6 +3121,49 @@ setenv the configuration file sees the new value. See also "presetenv", "resetenv", and "unsetenv". +setenv-sd-listen-fds [ on | off ] + Sets environment variables automatically using the LISTEN_FDS and + LISTEN_FDNAMES environment variables described in the systemd documentation. + + These 2 variables are set by systemd.socket. Systemd is able to listen to a + TCP socket by itself and pass the socket to the service using the + ListenStream keyword. When using FileDescriptorName, it is possible to assign + a name to the corresponding FD which will be inherited by haproxy. + + When set to "on", environment variables are created with the prefix "FD_", + followed by the uppercase version of FileDescriptorName from systemd. + + When set to off, the FD_ environment variables derived from + FileDescriptorName are unset. + + Limitations: + - FileDescriptorName may contain alphanumerical characters and underscores. + - variables name cannot exceed 254 characters + + This feature is particularly useful when running HAProxy in a container, as + it allows you to avoid constraints from NATing or start containers without + network access. + + Example: + + haproxy.socket: + [Socket] + ListenStream=80 + FileDescriptorName=http1 + + ListenStream=443 + FileDescriptorName=https1 + + haproxy.cfg: + global + setenv-sd-listen-fds on + + frontend + bind fd@${FD_HTTP1} + bind fd@${FD_HTTPS1} ssl ... + + Default value is off. + shm-stats-file [ EXPERIMENTAL ] When this directive is set, it enables the use of shared memory for storing stats counters. is used as argument to shm_open() to open the shared diff --git a/include/haproxy/systemd.h b/include/haproxy/systemd.h index 2ad688413..241128fec 100644 --- a/include/haproxy/systemd.h +++ b/include/haproxy/systemd.h @@ -7,4 +7,7 @@ int sd_notifyf(int unset_environment, const char *format, ...); int sd_listen_fds_with_names(int unset_environment, char ***names); int sd_listen_fds(int unset_environment); +void setenv_listen_fds(); +void unsetenv_listen_fds(); + #endif diff --git a/src/cfgparse-global.c b/src/cfgparse-global.c index 4a9bcf000..e25a1df5e 100644 --- a/src/cfgparse-global.c +++ b/src/cfgparse-global.c @@ -26,6 +26,7 @@ #include #include #include +#include #include int cluster_secret_isset; @@ -1594,6 +1595,33 @@ static int cfg_parse_global_env_opts(char **args, int section_type, return 0; } +static int cfg_parse_global_listen_fds(char **args, int section_type, + struct proxy *curpx, const struct proxy *defpx, + const char *file, int line, char **err) +{ + if (strcmp(args[0], "setenv-sd-listen-fds") == 0) { + if (too_many_args(1, args, err, NULL)) + return -1; + if (*(args[1]) == 0) { + memprintf(err, "'%s' expects a 'on' or 'off' argument.\n", + args[0]); + return -1; + } + + if (strcmp(args[1], "on") == 0) { + setenv_listen_fds(); + } else if (strcmp(args[1], "off") == 0) { + unsetenv_listen_fds(); + } else { + memprintf(err, "'%s' expects 'on' or 'off' as argument.\n", args[0]); + return -1; + } + } + + return 0; +} + + static int cfg_parse_global_shm_stats_file(char **args, int section_type, struct proxy *curpx, const struct proxy *defpx, const char *file, int line, char **err) @@ -1852,6 +1880,7 @@ static struct cfg_kw_list cfg_kws = {ILH, { { CFG_GLOBAL, "quiet", cfg_parse_global_mode, KWF_DISCOVERY }, { CFG_GLOBAL, "resetenv", cfg_parse_global_env_opts, KWF_DISCOVERY }, { CFG_GLOBAL, "setenv", cfg_parse_global_env_opts, KWF_DISCOVERY }, + { CFG_GLOBAL, "setenv-sd-listen-fds", cfg_parse_global_listen_fds }, { CFG_GLOBAL, "shm-stats-file", cfg_parse_global_shm_stats_file }, { CFG_GLOBAL, "shm-stats-file-max-objects", cfg_parse_global_shm_stats_file_max_objects }, { CFG_GLOBAL, "stress-level", cfg_parse_global_stress_level }, diff --git a/src/systemd.c b/src/systemd.c index 262bbe6f9..5c734a4d8 100644 --- a/src/systemd.c +++ b/src/systemd.c @@ -255,3 +255,63 @@ end: return ret; } +/* + * Automatically sets environment variables based on LISTEN_FDS and LISTEN_FDNAMES. + * + * When cleanup is set, unset those environment variables instead. + */ +static void __setenv_listen_fds(int cleanup) +{ + char **names = NULL; + char **p; + char d[255] = "FD_"; + int fd = 3; + const char *fdstr = NULL; + int i; + + sd_listen_fds_with_names(0, &names); + + p = names; + while (names && *names) { + char *n; + + /* start to write after FD_ (i = 3) */ + for (n = *names, i = 3; *n && i < sizeof(d); i++, n++) { + if (!isalnum((int)*n) && *n != '_') + break; + d[i] = toupper(*n); + } + d[i] = '\0'; + + if (!cleanup) + fdstr = ultoa(fd); + + if (cleanup) + unsetenv(d); + else + setenv(d, fdstr, 1); + names++; + fd++; + } + + /* free names allocated by sd_listen_fds_with_names() */ + names = p; + while (names && *names) { + free(*names); + names++; + } + free(p); +} + + +/* Automatically sets environment variables based on LISTEN_FDS and LISTEN_FDNAMES. */ +void setenv_listen_fds() +{ + __setenv_listen_fds(0); +} + +/* Automatically UNsets environment variables based on LISTEN_FDS and LISTEN_FDNAMES. */ +void unsetenv_listen_fds() +{ + __setenv_listen_fds(1); +}