Start use capabilities on linux

From: Kirill A. Korinskiy [email protected]

The nginx required privilege mode only on master process and only bind
ports <1024. In linux proccess can bind ports <1024 in not privilege
mode if the process does capset(CAP_NET_BIND_SERVICE).

auto/lib/capabilities/conf | 20 +++++++++++++
auto/os/linux | 3 ++
src/core/nginx.c | 7 +++±
src/core/ngx_cycle.c | 1 -
src/os/unix/ngx_process_cycle.c | 22 +±-----------
src/os/unix/ngx_user.c | 61
+++++++++++++++++++++++++++++++++++++++
src/os/unix/ngx_user.h | 14 +++++++++
7 files changed, 107 insertions(+), 21 deletions(-)
create mode 100644 auto/lib/capabilities/conf

diff --git a/auto/lib/capabilities/conf b/auto/lib/capabilities/conf
new file mode 100644
index
0000000000000000000000000000000000000000…484401c045a67cc0be523f64de2f177b14cae97b
— /dev/null
+++ b/auto/lib/capabilities/conf
@@ -0,0 +1,20 @@
+
+# Copyright (C) Kirill A. Korinskiy
+
+
+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=“-lcap”
+ngx_feature_test="cap_user_header_t header;

  • cap_user_data_t cap;
  • capset(header, cap);
  • return -1;"
    +. auto/feature

+if [ $ngx_found = yes ]; then

  • CORE_LIBS=“$CORE_LIBS $ngx_feature_libs”
    +fi
    diff --git a/auto/os/linux b/auto/os/linux
    index
    3c2c8419d58cafcfafb93d30032cc7c8553cccb8…e4e036c2aaafb59c174e665a4da9a2b2db8c3e2b
    100644
    — a/auto/os/linux
    +++ b/auto/os/linux
    @@ -121,3 +121,6 @@ ngx_feature_libs=
    ngx_feature_test=“long mask = 0;
    sched_setaffinity(0, 32, (cpu_set_t *) &mask)”
    . auto/feature

+# capabilities
+. auto/lib/capabilities/conf
diff --git a/src/core/nginx.c b/src/core/nginx.c
index
e6ebae54431f074fbc41974c35c44ac9ab4492cc…78e8227b8b1425a6aa7df65cc0d36f67f20ad7d9
100644
— a/src/core/nginx.c
+++ b/src/core/nginx.c
@@ -322,6 +322,12 @@ main(int argc, char *const *argv)

 ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, 

ngx_core_module);

+#if (NGX_HAVE_CAPABILITIES)

  • if (geteuid() == 0) {
  • ngx_set_capabilities(cycle);
  • }
    +#endif
  • ngx_process = ccf->master ? NGX_PROCESS_MASTER :
    NGX_PROCESS_SINGLE;

#if (NGX_WIN32)
@@ -334,7 +340,6 @@ main(int argc, char *const *argv)
if (ngx_service(cycle->log) != NGX_OK) {
return 1;
}

     return 0;
 }

#endif
diff --git a/src/core/ngx_cycle.c b/src/core/ngx_cycle.c
index
f3dc8619b484c9727770233b26e6580ca32fda80…ce8de0dd6d827235ace0c7f328d0db2c604af070
100644
— a/src/core/ngx_cycle.c
+++ b/src/core/ngx_cycle.c
@@ -14,7 +14,6 @@ static void ngx_destroy_cycle_pools(ngx_conf_t *conf);
static ngx_int_t ngx_cmp_sockaddr(struct sockaddr *sa1, struct sockaddr
*sa2);
static void ngx_clean_old_cycles(ngx_event_t *ev);

volatile ngx_cycle_t *ngx_cycle;
ngx_array_t ngx_old_cycles;

diff --git a/src/os/unix/ngx_process_cycle.c
b/src/os/unix/ngx_process_cycle.c
index
a14a03c748dd2793b5e2ea0b18f709267df01d3e…f0950e64a353d1c46c32bbb97964c7924b67e813
100644
— a/src/os/unix/ngx_process_cycle.c
+++ b/src/os/unix/ngx_process_cycle.c
@@ -854,27 +854,11 @@ ngx_worker_process_init(ngx_cycle_t *cycle,
ngx_uint_t priority)
}
#endif

+#ifndef NGX_HAVE_CAPABILITIES
if (geteuid() == 0) {

  •    if (setgid(ccf->group) == -1) {
    
  •        ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
    
  •                      "setgid(%d) failed", ccf->group);
    
  •        /* fatal */
    
  •        exit(2);
    
  •    }
    
  •    if (initgroups(ccf->username, ccf->group) == -1) {
    
  •        ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
    
  •                      "initgroups(%s, %d) failed",
    
  •                      ccf->username, ccf->group);
    
  •    }
    
  •    if (setuid(ccf->user) == -1) {
    
  •        ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
    
  •                      "setuid(%d) failed", ccf->user);
    
  •        /* fatal */
    
  •        exit(2);
    
  •    }
    
  •    ngx_switch_user(cycle);
    
    }
    +#endif

#if (NGX_HAVE_SCHED_SETAFFINITY)

diff --git a/src/os/unix/ngx_user.c b/src/os/unix/ngx_user.c
index
4bad1c3070e517fadb53007be7f90acb576fba4f…d5ebb6c3e22b96d66a5e816dbfa860635fa7a3d8
100644
— a/src/os/unix/ngx_user.c
+++ b/src/os/unix/ngx_user.c
@@ -7,6 +7,10 @@
#include <ngx_config.h>
#include <ngx_core.h>

+#if (NGX_HAVE_CAPABILITIES)
+#include <sys/capability.h>
+#endif
+

/*

  • Solaris has thread-safe crypt()
    @@ -106,3 +110,60 @@ ngx_crypt(ngx_pool_t *pool, u_char *key, u_char
    *salt, u_char **encrypted)
    #endif

#endif /* NGX_CRYPT */
+
+void ngx_switch_user(ngx_cycle_t *cycle)
+{

  • ngx_core_conf_t *ccf;
  • ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx,
    ngx_core_module);
  • if (setgid(ccf->group) == -1) {
  •    ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
    
  •                  "setgid(%d) failed", ccf->group);
    
  •    /* fatal */
    
  •    exit(2);
    
  • }
  • if (initgroups(ccf->username, ccf->group) == -1) {
  •    ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
    
  •                  "initgroups(%s, %d) failed",
    
  •                  ccf->username, ccf->group);
    
  • }
  • 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)
+
+void ngx_set_capabilities(ngx_cycle_t *cycle)
+{

  • ngx_core_conf_t *ccf;
  • struct __user_cap_header_struct header;
  • struct __user_cap_data_struct cap;
  • ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx,
    ngx_core_module);
  • prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0);
  • ngx_switch_user(cycle);
  • header.version = _LINUX_CAPABILITY_VERSION;
  • header.pid = 0;
  • cap.effective = cap.permitted = (1 << CAP_NET_BIND_SERVICE);
  • if (capset(&header, &cap) == -1) {
  •    ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
    
  •                  "capset(%d) failed", ccf->user);
    
  •    /* fatal */
    
  •    exit(2);
    
  • }
    +}

+#endif
diff --git a/src/os/unix/ngx_user.h b/src/os/unix/ngx_user.h
index
a24a66bf34ead7e9a1f8007e1c4be785601a937c…1ea5e360219af2dab4da5df1811c3a30eb783821
100644
— a/src/os/unix/ngx_user.h
+++ b/src/os/unix/ngx_user.h
@@ -15,10 +15,24 @@
typedef uid_t ngx_uid_t;
typedef gid_t ngx_gid_t;

+#if (NGX_CRYPT)
+
+#if (NGX_HAVE_GNU_CRYPT_R)
+

ngx_int_t ngx_crypt(ngx_pool_t *pool, u_char *key, u_char *salt,
u_char **encrypted);

+#endif
+
+#endif /* NGX_CRYPT */
+
+void ngx_switch_user(ngx_cycle_t *cycle);
+
+#if (NGX_HAVE_CAPABILITIES)
+
+void ngx_set_capabilities(ngx_cycle_t *cycle);

+#endif

#endif /* NGX_USER_H_INCLUDED */

Hello!

On Wed, Mar 18, 2009 at 12:36:47AM +0300, Kirill A. Korinskiy wrote:

From: Kirill A. Korinskiy [email protected]

The nginx required privilege mode only on master process and only bind
ports <1024. In linux proccess can bind ports <1024 in not privilege
mode if the process does capset(CAP_NET_BIND_SERVICE).

Note that using root for master process needed not only for
bind(), but also to access restricted configuration files (e.g.
private keys) during reconfiguration. So dropping root from
master should be at least configurable.

It’s also not clear what will happen on binary upgrade. Looks
like with current code capabilities will be lost on exec() and
upgraded binary won’t be able to bind() privileged ports anymore.
But I’m not really familiar will linux capabilites interface, so I
may be wrong.

Not even mentioning you are dropping root before writing pidfile.
:slight_smile:

Also there is a couple of unrelated changes and some whitespace
damage/style violations, but it doesn’t really matter.

Maxim D.

On Wed, Mar 18, 2009 at 04:23:48AM +0300, Maxim D. wrote:

Note that using root for master process needed not only for
Not even mentioning you are dropping root before writing pidfile.
:slight_smile:

The root privileges is also required to rotate logs if they are in
a directories where workers can not write: master opens files and
chown/chmod() them.

From: Kirill A. Korinskiy [email protected]

capset(CAP_CHOWN) grand the proccess rule to change arbitrary to file.

CAP_DAC_OVERRIDE and CAP_DAC_READ_SEARCH bypass file read, write and
execute permission checks.

CAP_SETUID need for rewert back to privileged processes (whose
effective user ID is 0).

src/os/unix/ngx_process.c | 9 +++++++++
src/os/unix/ngx_user.c | 13 ++++++++++±-
2 files changed, 20 insertions(+), 2 deletions(-)

diff --git a/src/os/unix/ngx_process.c b/src/os/unix/ngx_process.c
index
18b2601aff17895cc250e54621651d72aee09790…9f06fdfa460ceadac032a62aae18559238fc17c6
100644
— a/src/os/unix/ngx_process.c
+++ b/src/os/unix/ngx_process.c
@@ -247,6 +247,15 @@ ngx_execute_proc(ngx_cycle_t *cycle, void *data)
{
ngx_exec_ctx_t *ctx = data;

+#if (NGX_HAVE_CAPABILITIES)

  • if (setuid(0) == -1) {

  •    ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
    
  •                  "setuid(0) failed");
    
  •    /* fatal */
    
  •    exit(2);
    
  • }
    +#endif

  • if (execve(ctx->path, ctx->argv, ctx->envp) == -1) {
    ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
    “execve() failed while executing %s "%s"”,
    diff --git a/src/os/unix/ngx_user.c b/src/os/unix/ngx_user.c
    index
    d5ebb6c3e22b96d66a5e816dbfa860635fa7a3d8…d3f6b5bd1d7d379ff4de9418955bcc285f0696d4
    100644
    — a/src/os/unix/ngx_user.c
    +++ b/src/os/unix/ngx_user.c
    @@ -149,14 +149,23 @@ void ngx_set_capabilities(ngx_cycle_t *cycle)

    ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx,
    ngx_core_module);

  • prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0);
  • if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) == -1) {

  •    ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
    
  •                  "prctl(%d) failed", ccf->user);
    
  •    /* fatal */
    
  •    exit(2);
    
  • }

    ngx_switch_user(cycle);

    header.version = _LINUX_CAPABILITY_VERSION;
    header.pid = 0;

  • cap.effective = cap.permitted = (1 << CAP_NET_BIND_SERVICE);
  • cap.effective = cap.permitted = (1 << CAP_NET_BIND_SERVICE |

  •         1 << CAP_CHOWN |
    
  •         1 << CAP_DAC_OVERRIDE |
    
  •         1 << CAP_DAC_READ_SEARCH |
    
  •         1 << CAP_SETUID);
    

    if (capset(&header, &cap) == -1) {
    ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,

From: Kirill A. Korinskiy [email protected]


src/os/unix/ngx_user.c | 1 +
1 files changed, 1 insertions(+), 0 deletions(-)

diff --git a/src/os/unix/ngx_user.c b/src/os/unix/ngx_user.c
index
d3f6b5bd1d7d379ff4de9418955bcc285f0696d4…c6f0bdb7a16ef5d896998f56bfd9532ff6689dad
100644
— a/src/os/unix/ngx_user.c
+++ b/src/os/unix/ngx_user.c
@@ -8,6 +8,7 @@
#include <ngx_core.h>

#if (NGX_HAVE_CAPABILITIES)
+#undef _POSIX_SOURCE
#include <sys/capability.h>
#endif

From: Kirill A. Korinskiy [email protected]

capset(CAP_CHOWN) grand the proccess rule to change arbitrary to file.

CAP_DAC_OVERRIDE and CAP_DAC_READ_SEARCH bypass file read, write and
execute permission checks.

CAP_SETUID need for rewert back to privileged processes (whose
effective user ID is 0).

src/os/unix/ngx_process.c | 9 +++++++++
src/os/unix/ngx_user.c | 13 ++++++++++±-
2 files changed, 20 insertions(+), 2 deletions(-)

diff --git a/src/os/unix/ngx_process.c b/src/os/unix/ngx_process.c
index
18b2601aff17895cc250e54621651d72aee09790…9f06fdfa460ceadac032a62aae18559238fc17c6
100644
— a/src/os/unix/ngx_process.c
+++ b/src/os/unix/ngx_process.c
@@ -247,6 +247,15 @@ ngx_execute_proc(ngx_cycle_t *cycle, void *data)
{
ngx_exec_ctx_t *ctx = data;

+#if (NGX_HAVE_CAPABILITIES)

  • if (setuid(0) == -1) {

  •    ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
    
  •                  "setuid(0) failed");
    
  •    /* fatal */
    
  •    exit(2);
    
  • }
    +#endif

  • if (execve(ctx->path, ctx->argv, ctx->envp) == -1) {
    ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
    “execve() failed while executing %s "%s"”,
    diff --git a/src/os/unix/ngx_user.c b/src/os/unix/ngx_user.c
    index
    d5ebb6c3e22b96d66a5e816dbfa860635fa7a3d8…d3f6b5bd1d7d379ff4de9418955bcc285f0696d4
    100644
    — a/src/os/unix/ngx_user.c
    +++ b/src/os/unix/ngx_user.c
    @@ -149,14 +149,23 @@ void ngx_set_capabilities(ngx_cycle_t *cycle)

    ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx,
    ngx_core_module);

  • prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0);
  • if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) == -1) {

  •    ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
    
  •                  "prctl(%d) failed", ccf->user);
    
  •    /* fatal */
    
  •    exit(2);
    
  • }

    ngx_switch_user(cycle);

    header.version = _LINUX_CAPABILITY_VERSION;
    header.pid = 0;

  • cap.effective = cap.permitted = (1 << CAP_NET_BIND_SERVICE);
  • cap.effective = cap.permitted = (1 << CAP_NET_BIND_SERVICE |

  •         1 << CAP_CHOWN |
    
  •         1 << CAP_DAC_OVERRIDE |
    
  •         1 << CAP_DAC_READ_SEARCH |
    
  •         1 << CAP_SETUID);
    

    if (capset(&header, &cap) == -1) {
    ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,