How to add a new internal user to a rock¶
You can declare run-user in a rock’s project
file to specify which user you want to run a its services with. If you don’t
specify a user, the services run as root by default. The run-user key only
accepts a limited number of users, which could be a constraint for certain
container applications.
Mandatory packages or slices¶
To add a new internal user or group to a rock, two packages or their respective slices are needed:
- base-files: to create the base folders such as- /rootand- /home,
- base-passwd: to produce- /etc/passwdand- /etc/groupfiles.
Creating the user and/or group¶
Invoking the useradd and groupadd commands can take place in a part’s
build step. This can be done by writing those commands in
override-build key. However, the changes made by those commands will be
only applied on the build instance, and will not be available in the resulting
rock. Hence, $CRAFT_PART_INSTALL should be passed as the root directory to
those two commands.
A full example¶
The following project file illustrates how to use the useradd command
in the override-build key inside a part.
For an example of how to access the user inside the rock, here’s a simple Python web service. The script will return an HTTP response containing the user that is running the web service:
#!/usr/bin/python3.12
import getpass
print(
    f"""\
Content-Type: text/html
Serving by {getpass.getuser()} on port 8080\
"""
)
Then, add a service to that runs the web service:
services:
  web-service:
    override: replace
    command: python3.12 -m http.server --cgi 8080
    startup: enabled
    user: myuser
    working-dir: /
In the override-build section of the part, let’s create a new user and a
new group. We will also copy the Python script to a cgi-bin folder, and
give it the execute permission:
    override-build: |
      craftctl default
      groupadd --root ${CRAFT_PART_INSTALL} mygroup
      useradd -d /home/myuser -s /bin/bash --root ${CRAFT_PART_INSTALL} -g mygroup myuser
      mkdir ${CRAFT_PART_INSTALL}/cgi-bin/
      cp serve_user.py ${CRAFT_PART_INSTALL}/cgi-bin/
      chmod +x ${CRAFT_PART_INSTALL}/cgi-bin/serve_user.py
The final project file will look like this:
name: internal-user
base: bare
build-base: [email protected]
version: "latest"
summary: Add an internal user inside a rock
description: Add an internal user inside a rock, and run a service with it.
platforms:
  amd64:
services:
  web-service:
    override: replace
    command: python3.12 -m http.server --cgi 8080
    startup: enabled
    user: myuser
    working-dir: /
parts:
  my-part:
    plugin: nil
    source-type: local
    source: .
    stage-packages:
      - base-passwd_data
      - base-files_base
      - python3.12_standard
    override-build: |
      craftctl default
      groupadd --root ${CRAFT_PART_INSTALL} mygroup
      useradd -d /home/myuser -s /bin/bash --root ${CRAFT_PART_INSTALL} -g mygroup myuser
      mkdir ${CRAFT_PART_INSTALL}/cgi-bin/
      cp serve_user.py ${CRAFT_PART_INSTALL}/cgi-bin/
      chmod +x ${CRAFT_PART_INSTALL}/cgi-bin/serve_user.py
With the part and web service in place, build the rock:
rockcraft pack
Next, we will convert the rock from an OCI archive to a Docker image using Skopeo:
sudo rockcraft.skopeo --insecure-policy copy oci-archive:internal-user_latest_amd64.rock docker-daemon:internal-user:latest
We can now check which internal user is running the service by running the image container:
id=$(docker run -d -p 8080:8080 internal-user:latest --verbose)
sleep 5
curl -s http://127.0.0.1:8080/cgi-bin/serve_user.py | grep "Serving by myuser"
docker rm -f "$id"
The response should contain the new user name:
Serving by myuser on port 8080
