Skip to content

Commit

Permalink
Retain CAP_NET_RAW capability for transparent proxying.
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
arut committed Dec 13, 2017
1 parent d2d737e commit 752f66b
Show file tree
Hide file tree
Showing 6 changed files with 82 additions and 0 deletions.
31 changes: 31 additions & 0 deletions auto/os/linux
Original file line number Diff line number Diff line change
Expand Up @@ -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()"
Expand Down
2 changes: 2 additions & 0 deletions src/core/ngx_cycle.h
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,8 @@ typedef struct {

ngx_array_t env;
char **environment;

ngx_uint_t transparent; /* unsigned transparent:1; */
} ngx_core_conf_t;


Expand Down
6 changes: 6 additions & 0 deletions src/http/ngx_http_upstream.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
5 changes: 5 additions & 0 deletions src/os/unix/ngx_linux_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -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


Expand Down
32 changes: 32 additions & 0 deletions src/os/unix/ngx_process_cycle.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
6 changes: 6 additions & 0 deletions src/stream/ngx_stream_proxy_module.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down

0 comments on commit 752f66b

Please sign in to comment.