Is there an easy way to change to a non-root user in Bitbucket Pipelines Docker container?
Asked Answered
M

4

13

Bitbucket Pipelines is using Docker containers to executes tasks and by default Docker containers run as root. This is a problem for NPM's lifecycle scripts because NPM tries to downgrade its privileges when it runs scripts.

When executing the postinstall script, NPM throws an error that it cannot run in wd %s %s (wd=%s). The simplest solution is to run npm install with the --unsafe-perm flag, but I don't like this approach.

Docker's best practices for writing Dockerfiles states that:

If a service can run without privileges, use USER to change to a non-root user.

When configuring a typical Docker container I would create a new, non-root user and run my npm scripts as this user.

After reading Pipelines documentation I couldn't find any equivalent to Docker's USER command. I might be able to use useradd, chown and su (didn't test that yet) but is there a simpler solution?

Unfortunately adding useradd, chown and su to bitbucket-pipelines.yml script section breaks Pipelines and results in failing repo:push webhook.

image: node:6.2

pipelines:
  default:
    - step:
        script:
          - useradd --user-group --create-home --shell /bin/false node
          - chown -R node: /opt/atlassian/bitbucketci/agent/build
          - su -s /bin/sh -c "npm install" node
          - su -s /bin/sh -c "npm run test:coverage --silent" node

Pipelines responds with

{
  "code": 500,
  "message": "There was an error processing your request. It has been logged (ID <removed>)."
}
Mcnalley answered 22/8, 2016 at 11:3 Comment(0)
M
1

The most comfortable solution I've found is to create a non-root user account only if it's not already included in the image and use the gosu utility to set it for executed commands.

Pipelines' build step is already setting the chmod 777 on the $BUILD_DIR so additional chown is not required.

So, to be able to change to a non-root user in Bitbucket Pipelines Docker container you have to:

  1. add an additional shell script to your repository that installs the gosu utility (it can also be included directly as a step in Pipelies config)
  2. call the install-gosu.sh script as the first step in Pipelines config,
  3. create a non-root user (checking whether it already exists first) with id -u {user} &>/dev/null || useradd ...,
  4. use gosu to run commands as a non-root user.

install-gosu.sh

#!/bin/bash

GOSU_VERSION=1.10
GNUPGHOME="$(mktemp -d)"

set -x

apt-get update && apt-get install -y --no-install-recommends ca-certificates wget && rm -rf /var/lib/apt/lists/* \
&& dpkgArch="$(dpkg --print-architecture | awk -F- '{ print $NF }')" \
&& wget -O /usr/local/bin/gosu "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$dpkgArch" \
&& wget -O /usr/local/bin/gosu.asc "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$dpkgArch.asc" \
&& gpg --keyserver ha.pool.sks-keyservers.net --recv-keys B42F6819007F00F88E364FD4036A9C25BF357DD4 \
&& gpg --batch --verify /usr/local/bin/gosu.asc /usr/local/bin/gosu \
&& rm -r "$GNUPGHOME" /usr/local/bin/gosu.asc \
&& chmod +x /usr/local/bin/gosu \
&& gosu nobody true \
&& apt-get purge -y --auto-remove ca-certificates wget

bitbucket-pipelines.yml

image: node:6

pipelines:
  default:
    - step:
        script:
          - bash $BITBUCKET_CLONE_DIR/install-gosu.sh
          - id -u node &>/dev/null || useradd --user-group --create-home --shell /bin/false node
          - gosu node npm install
          - gosu node npm test

This can easily be adapted for other languages/users/commands. Just swap the node user and npm commands to whatever you need.

I've tested this method with nodejs and python images.

Mcnalley answered 23/3, 2017 at 23:5 Comment(0)
B
3

There's two things to address in this question.


To run as a non-root user in Bitbucket Pipelines, you can do exactly what you have suggested and use the USER Docker command. The node:6.2 image does not map to a non-root user, so if you would like to do so you can create a new Docker image with the following Dockerfile:

FROM node:6.2

USER foo

The 500 error you are receiving appears to be an issue with YAML parsing on this line:

- chown -R node: /opt/atlassian/bitbucketci/agent/build

The ':' is a special character in the YAML format. Indicating a key-value pair. In order to fix this, put the contents on that line inside of quotes instead like this:

- "chown -R node: /opt/atlassian/bitbucketci/agent/build"

I would also suggest you use the new default environment variable for the build path now. $BITBUCKET_CLONE_DIR. So change the line to instead be

- "chown -R node: $BITBUCKET_CLONE_DIR"
Ballad answered 12/10, 2016 at 13:37 Comment(1)
Question. I keep seeing references to this /opt/atlassian/bitbucketci/agent/build in other PipeLines questions. Does anyone know where to find the agent info for the container that is running so i can grab the generated build?Weakness
C
2

Since the node image already creates a node user (at least in 6.9+), you don't need the useradd. It also seems to work well without the chown. In the end, I have a script looking like this - and it appears to be just fine:

image: node:7

pipelines:
  default:
    - step:
        script:
          - su -s /bin/bash -c "npm install" node
          - su -s /bin/bash -c "npm run build" node
Cy answered 5/1, 2017 at 8:7 Comment(0)
M
1

The most comfortable solution I've found is to create a non-root user account only if it's not already included in the image and use the gosu utility to set it for executed commands.

Pipelines' build step is already setting the chmod 777 on the $BUILD_DIR so additional chown is not required.

So, to be able to change to a non-root user in Bitbucket Pipelines Docker container you have to:

  1. add an additional shell script to your repository that installs the gosu utility (it can also be included directly as a step in Pipelies config)
  2. call the install-gosu.sh script as the first step in Pipelines config,
  3. create a non-root user (checking whether it already exists first) with id -u {user} &>/dev/null || useradd ...,
  4. use gosu to run commands as a non-root user.

install-gosu.sh

#!/bin/bash

GOSU_VERSION=1.10
GNUPGHOME="$(mktemp -d)"

set -x

apt-get update && apt-get install -y --no-install-recommends ca-certificates wget && rm -rf /var/lib/apt/lists/* \
&& dpkgArch="$(dpkg --print-architecture | awk -F- '{ print $NF }')" \
&& wget -O /usr/local/bin/gosu "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$dpkgArch" \
&& wget -O /usr/local/bin/gosu.asc "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$dpkgArch.asc" \
&& gpg --keyserver ha.pool.sks-keyservers.net --recv-keys B42F6819007F00F88E364FD4036A9C25BF357DD4 \
&& gpg --batch --verify /usr/local/bin/gosu.asc /usr/local/bin/gosu \
&& rm -r "$GNUPGHOME" /usr/local/bin/gosu.asc \
&& chmod +x /usr/local/bin/gosu \
&& gosu nobody true \
&& apt-get purge -y --auto-remove ca-certificates wget

bitbucket-pipelines.yml

image: node:6

pipelines:
  default:
    - step:
        script:
          - bash $BITBUCKET_CLONE_DIR/install-gosu.sh
          - id -u node &>/dev/null || useradd --user-group --create-home --shell /bin/false node
          - gosu node npm install
          - gosu node npm test

This can easily be adapted for other languages/users/commands. Just swap the node user and npm commands to whatever you need.

I've tested this method with nodejs and python images.

Mcnalley answered 23/3, 2017 at 23:5 Comment(0)
A
0

In many cases the best way to run any pipeline is not as root. This is because your end-users, most probably, are not going to use your software as root.

Therefor, the container you are running the pipeline on can be running as non-root by creating your own user during the image build and still make it part of the sudo group in case you need sudo at some point. I would also recommend adding a password to your user as shown below. These are the commands for the DockerFile in case you want to stick to Docker.

RUN adduser --ingroup sudo userName
RUN echo userName:password | chpasswd

Finally you can change user with the following command.

USER userName

If the last user being used in your DockerFile is userName then the pipeline will run with that same user.

In order to run sudo commands, I would recommend adding a Repository variable in Bitbucket with the userName's password and call the sudo command as such:

echo $USERNAME_PASSWORD | sudo -S $commandToRun
Arminius answered 23/10, 2023 at 7:4 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.