Linux に No New Privileges という、子プロセスが新しい特権を取得できないようにする仕組みがある。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/prctl.h>

int main(int argc, char * argv[])
{
  if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) {
    return EXIT_FAILURE;
  }
  return execvp(argv[1], &argv[1]);
}

上記をコンパイルすると子プロセスは CAP_NEW_RAW を持てないので ping が失敗する。

$ ./a.out ping -c 3 localhost
ping: socket: Operation not permitted

Docker でもこの仕組みを --secuirty-opt=no-new-privileges:true フラグを付与することで利用できる。

FROM ubuntu:18.04

RUN cp /bin/bash /bin/bash2 && chmod 4755 /bin/bash2
RUN useradd -ms /bin/bash newuser
USER newuser
CMD ["/bin/bash"]

setuid した bash を用意しておくと、newuser は特権を持つことができる。

$ docker build -t test:latest .
$ docker run -it --rm test:latest
newuser@985fc45b58c1:/$ id
uid=1000(newuser) gid=1000(newuser) groups=1000(newuser)
newuser@985fc45b58c1:/$ /bin/bash2 -p
bash2-4.4# id
uid=1000(newuser) gid=1000(newuser) euid=0(root) groups=1000(newuser)

続いて no-new-privileges:true で起動してみる。

$ docker run -it --security-opt=no-new-privileges:true --rm test:latest
newuser@d96857132041:/$ id
uid=1000(newuser) gid=1000(newuser) groups=1000(newuser)
newuser@d96857132041:/$ /bin/bash2 -p
newuser@d96857132041:/$ id
uid=1000(newuser) gid=1000(newuser) groups=1000(newuser)

No New Privileges によって子プロセスが追加の権限を得ることは禁止されているので、特権を得ることができないことが確認できる。