changeset 7174:84e53e4735a4

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.
author Roman Arutyunyan <arut@nginx.com>
date Wed, 13 Dec 2017 20:40:53 +0300
parents 057adb2a9d23
children 56923e8e01a5
files auto/os/linux src/core/ngx_cycle.h src/http/ngx_http_upstream.c src/os/unix/ngx_linux_config.h src/os/unix/ngx_process_cycle.c src/stream/ngx_stream_proxy_module.c
diffstat 6 files changed, 82 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/auto/os/linux	Mon Dec 11 16:28:11 2017 +0000
+++ b/auto/os/linux	Wed Dec 13 20:40:53 2017 +0300
@@ -157,6 +157,37 @@
 . 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()"
--- a/src/core/ngx_cycle.h	Mon Dec 11 16:28:11 2017 +0000
+++ b/src/core/ngx_cycle.h	Wed Dec 13 20:40:53 2017 +0300
@@ -114,6 +114,8 @@
 
     ngx_array_t               env;
     char                    **environment;
+
+    ngx_uint_t                transparent;  /* unsigned  transparent:1; */
 } ngx_core_conf_t;
 
 
--- a/src/http/ngx_http_upstream.c	Mon Dec 11 16:28:11 2017 +0000
+++ b/src/http/ngx_http_upstream.c	Wed Dec 13 20:40:53 2017 +0300
@@ -6078,6 +6078,12 @@
     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,
--- a/src/os/unix/ngx_linux_config.h	Mon Dec 11 16:28:11 2017 +0000
+++ b/src/os/unix/ngx_linux_config.h	Wed Dec 13 20:40:53 2017 +0300
@@ -99,6 +99,11 @@
 #endif
 
 
+#if (NGX_HAVE_CAPABILITIES)
+#include <sys/capability.h>
+#endif
+
+
 #define NGX_LISTEN_BACKLOG        511
 
 
--- a/src/os/unix/ngx_process_cycle.c	Mon Dec 11 16:28:11 2017 +0000
+++ b/src/os/unix/ngx_process_cycle.c	Wed Dec 13 20:40:53 2017 +0300
@@ -839,12 +839,44 @@
                           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) {
--- a/src/stream/ngx_stream_proxy_module.c	Mon Dec 11 16:28:11 2017 +0000
+++ b/src/stream/ngx_stream_proxy_module.c	Wed Dec 13 20:40:53 2017 +0300
@@ -2155,6 +2155,12 @@
     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,