aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRoman Arutyunyan <arut@nginx.com>2017-12-13 20:40:53 +0300
committerRoman Arutyunyan <arut@nginx.com>2017-12-13 20:40:53 +0300
commit752f66bf7d70fae2bf05fbf5941ff4be52b2b9a5 (patch)
tree78fc3bcc2f52d8fc71fa4ec12080fdf891e0a113
parentd2d737e70b46429ef9ed71b99402a9151f3c2e1f (diff)
downloadnginx-752f66bf7d70fae2bf05fbf5941ff4be52b2b9a5.tar.gz
nginx-752f66bf7d70fae2bf05fbf5941ff4be52b2b9a5.zip
Retain CAP_NET_RAW capability for transparent proxying.
The capability is retained automatically in unprivileged worker processes after changing UID if transparent proxying is enabled at least once in nginx configuration. The feature is only available in Linux.
-rw-r--r--auto/os/linux31
-rw-r--r--src/core/ngx_cycle.h2
-rw-r--r--src/http/ngx_http_upstream.c6
-rw-r--r--src/os/unix/ngx_linux_config.h5
-rw-r--r--src/os/unix/ngx_process_cycle.c32
-rw-r--r--src/stream/ngx_stream_proxy_module.c6
6 files changed, 82 insertions, 0 deletions
diff --git a/auto/os/linux b/auto/os/linux
index a0c8795bb..e4aa0e549 100644
--- a/auto/os/linux
+++ b/auto/os/linux
@@ -157,6 +157,37 @@ ngx_feature_test="if (prctl(PR_SET_DUMPABLE, 1, 0, 0, 0) == -1) return 1"
. auto/feature
+# prctl(PR_SET_KEEPCAPS)
+
+ngx_feature="prctl(PR_SET_KEEPCAPS)"
+ngx_feature_name="NGX_HAVE_PR_SET_KEEPCAPS"
+ngx_feature_run=yes
+ngx_feature_incs="#include <sys/prctl.h>"
+ngx_feature_path=
+ngx_feature_libs=
+ngx_feature_test="if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) == -1) return 1"
+. auto/feature
+
+
+# capabilities
+
+ngx_feature="capabilities"
+ngx_feature_name="NGX_HAVE_CAPABILITIES"
+ngx_feature_run=no
+ngx_feature_incs="#include <sys/capability.h>"
+ngx_feature_path=
+ngx_feature_libs=
+ngx_feature_test="struct __user_cap_data_struct data;
+ struct __user_cap_header_struct header;
+
+ header.version = _LINUX_CAPABILITY_VERSION_3;
+ data.effective = CAP_TO_MASK(CAP_NET_RAW);
+ data.permitted = 0;
+
+ (void) capset(&header, &data)"
+. auto/feature
+
+
# crypt_r()
ngx_feature="crypt_r()"
diff --git a/src/core/ngx_cycle.h b/src/core/ngx_cycle.h
index 2b48ccbd6..a825da2f9 100644
--- a/src/core/ngx_cycle.h
+++ b/src/core/ngx_cycle.h
@@ -114,6 +114,8 @@ typedef struct {
ngx_array_t env;
char **environment;
+
+ ngx_uint_t transparent; /* unsigned transparent:1; */
} ngx_core_conf_t;
diff --git a/src/http/ngx_http_upstream.c b/src/http/ngx_http_upstream.c
index 6d0f4ee52..f8d5707d3 100644
--- a/src/http/ngx_http_upstream.c
+++ b/src/http/ngx_http_upstream.c
@@ -6078,6 +6078,12 @@ ngx_http_upstream_bind_set_slot(ngx_conf_t *cf, ngx_command_t *cmd,
if (cf->args->nelts > 2) {
if (ngx_strcmp(value[2].data, "transparent") == 0) {
#if (NGX_HAVE_TRANSPARENT_PROXY)
+ ngx_core_conf_t *ccf;
+
+ ccf = (ngx_core_conf_t *) ngx_get_conf(cf->cycle->conf_ctx,
+ ngx_core_module);
+
+ ccf->transparent = 1;
local->transparent = 1;
#else
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
diff --git a/src/os/unix/ngx_linux_config.h b/src/os/unix/ngx_linux_config.h
index 2f6129d88..b22ea4378 100644
--- a/src/os/unix/ngx_linux_config.h
+++ b/src/os/unix/ngx_linux_config.h
@@ -99,6 +99,11 @@ typedef struct iocb ngx_aiocb_t;
#endif
+#if (NGX_HAVE_CAPABILITIES)
+#include <sys/capability.h>
+#endif
+
+
#define NGX_LISTEN_BACKLOG 511
diff --git a/src/os/unix/ngx_process_cycle.c b/src/os/unix/ngx_process_cycle.c
index a20a515c7..40654b3a2 100644
--- a/src/os/unix/ngx_process_cycle.c
+++ b/src/os/unix/ngx_process_cycle.c
@@ -839,12 +839,44 @@ ngx_worker_process_init(ngx_cycle_t *cycle, ngx_int_t worker)
ccf->username, ccf->group);
}
+#if (NGX_HAVE_PR_SET_KEEPCAPS && NGX_HAVE_CAPABILITIES)
+ if (ccf->transparent && ccf->user) {
+ if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) == -1) {
+ ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
+ "prctl(PR_SET_KEEPCAPS, 1) failed");
+ /* fatal */
+ exit(2);
+ }
+ }
+#endif
+
if (setuid(ccf->user) == -1) {
ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
"setuid(%d) failed", ccf->user);
/* fatal */
exit(2);
}
+
+#if (NGX_HAVE_CAPABILITIES)
+ if (ccf->transparent && ccf->user) {
+ struct __user_cap_data_struct data;
+ struct __user_cap_header_struct header;
+
+ ngx_memzero(&header, sizeof(struct __user_cap_header_struct));
+ ngx_memzero(&data, sizeof(struct __user_cap_data_struct));
+
+ header.version = _LINUX_CAPABILITY_VERSION_3;
+ data.effective = CAP_TO_MASK(CAP_NET_RAW);
+ data.permitted = data.effective;
+
+ if (capset(&header, &data) == -1) {
+ ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
+ "capset() failed");
+ /* fatal */
+ exit(2);
+ }
+ }
+#endif
}
if (worker >= 0) {
diff --git a/src/stream/ngx_stream_proxy_module.c b/src/stream/ngx_stream_proxy_module.c
index ad81cc8b7..818d7329b 100644
--- a/src/stream/ngx_stream_proxy_module.c
+++ b/src/stream/ngx_stream_proxy_module.c
@@ -2155,6 +2155,12 @@ ngx_stream_proxy_bind(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
if (cf->args->nelts > 2) {
if (ngx_strcmp(value[2].data, "transparent") == 0) {
#if (NGX_HAVE_TRANSPARENT_PROXY)
+ ngx_core_conf_t *ccf;
+
+ ccf = (ngx_core_conf_t *) ngx_get_conf(cf->cycle->conf_ctx,
+ ngx_core_module);
+
+ ccf->transparent = 1;
local->transparent = 1;
#else
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,