# HG changeset patch # User Roman Arutyunyan # Date 1513186853 -10800 # Node ID 84e53e4735a4930de47f92e7d5765c8489a87cf6 # Parent 057adb2a9d2353a61caa9555866b90b0f0940547 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. diff --git a/auto/os/linux b/auto/os/linux --- a/auto/os/linux +++ b/auto/os/linux @@ -157,6 +157,37 @@ ngx_feature_test="if (prctl(PR_SET_DUMPA . 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 " +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 " +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 --- 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 --- 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 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 --- 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 +#endif + + #define NGX_LISTEN_BACKLOG 511 diff --git a/src/os/unix/ngx_process_cycle.c b/src/os/unix/ngx_process_cycle.c --- 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 *cyc 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 --- 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, ng 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,