diff --git a/docs/how-to/code/internal-user/rockcraft-clean.yaml b/docs/how-to/code/internal-user/rockcraft-clean.yaml new file mode 100644 index 000000000..bd63c1593 --- /dev/null +++ b/docs/how-to/code/internal-user/rockcraft-clean.yaml @@ -0,0 +1,33 @@ +name: internal-user +base: bare +build-base: ubuntu@24.04 +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 diff --git a/docs/how-to/code/internal-user/rockcraft.yaml b/docs/how-to/code/internal-user/rockcraft.yaml new file mode 100644 index 000000000..9d1792ecc --- /dev/null +++ b/docs/how-to/code/internal-user/rockcraft.yaml @@ -0,0 +1,37 @@ +name: internal-user +base: bare +build-base: ubuntu@24.04 +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: + +# [docs:rock-services] +services: + web-service: + override: replace + command: python3.12 -m http.server --cgi 8080 + startup: enabled + user: myuser + working-dir: / +# [docs:rock-services-end] + +parts: + my-part: + plugin: nil + source-type: local + source: . + stage-packages: + - base-passwd_data + - base-files_base + - python3.12_standard +# [docs:rock-build] + 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 +# [docs:rock-build-end] diff --git a/docs/how-to/code/internal-user/serve_user.py b/docs/how-to/code/internal-user/serve_user.py new file mode 100644 index 000000000..8b8b31e86 --- /dev/null +++ b/docs/how-to/code/internal-user/serve_user.py @@ -0,0 +1,11 @@ +#!/usr/bin/python3.12 + +import getpass + +print( + f"""\ +Content-Type: text/html + +Serving by {getpass.getuser()} on port 8080\ +""" +) diff --git a/docs/how-to/code/internal-user/task.yaml b/docs/how-to/code/internal-user/task.yaml new file mode 100644 index 000000000..20725aba5 --- /dev/null +++ b/docs/how-to/code/internal-user/task.yaml @@ -0,0 +1,24 @@ +########################################### +# IMPORTANT +# Comments matter! +# The docs use the wrapping comments as +# markers for including said instructions +# as snippets in the docs. +########################################### +summary: test "How to add an internal user" guide + +execute: | + # [docs:pack-rock] + rockcraft pack + # [docs:pack-rock-end] + + # [docs:skopeo] + sudo rockcraft.skopeo --insecure-policy copy oci-archive:internal-user_latest_amd64.rock docker-daemon:internal-user:latest + # [docs:skopeo-end] + + # [docs:check-user] + 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" + # [docs:check-user-end] diff --git a/docs/how-to/rocks/add-internal-user-to-a-rock.rst b/docs/how-to/rocks/add-internal-user-to-a-rock.rst new file mode 100644 index 000000000..07909eb49 --- /dev/null +++ b/docs/how-to/rocks/add-internal-user-to-a-rock.rst @@ -0,0 +1,95 @@ +.. _add_internal_user_to_a_rock: + +How to add an new internal user to a rock +***************************************** + +You can declare :ref:`run-user ` in ``rockcraft.yaml`` +to specify which user you want to run a rock's 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 ``/root`` and ``/home``, +- ``base-passwd``: to produce ``/etc/passwd`` and ``/etc/group`` files. + +Creating the user and/or group +------------------------------ + +Invoking the ``useradd`` and ``groupadd`` commands can take place in a part's +:ref:`build ` step. This can be done by writing those commands in +``override-build`` field. 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 ``rockcraft.yaml`` illustrates how to use the ``useradd`` command +in the ``override-build`` field 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: + +.. literalinclude:: ../code/internal-user/serve_user.py + :language: python + +Then, add a service to ``rockcraft.yaml`` that runs the web service: + +.. literalinclude:: ../code/internal-user/rockcraft.yaml + :language: yaml + :start-after: [docs:rock-services] + :end-before: [docs:rock-services-end] + +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: + +.. literalinclude:: ../code/internal-user/rockcraft.yaml + :language: yaml + :start-after: [docs:rock-build] + :end-before: [docs:rock-build-end] + +The final ``rockcraft.yaml`` file will look like this: + +.. literalinclude:: ../code/internal-user/rockcraft-clean.yaml + :language: yaml + +With the part and web service in place, build the rock: + +.. literalinclude:: ../code/internal-user/task.yaml + :language: bash + :start-after: [docs:pack-rock] + :end-before: [docs:pack-rock-end] + :dedent: 2 + +Next, we will convert the rock from an OCI archive to a Docker image using +skopeo: + +.. literalinclude:: ../code/internal-user/task.yaml + :language: bash + :start-after: [docs:skopeo] + :end-before: [docs:skopeo-end] + :dedent: 2 + +We can now check which internal user is running the service by running the +image container: + +.. literalinclude:: ../code/internal-user/task.yaml + :language: bash + :start-after: [docs:check-user] + :end-before: [docs:check-user-end] + :dedent: 2 + +The response should contain the new user name: + +.. code-block:: + + Serving by myuser on port 8080 diff --git a/docs/how-to/rocks/index.rst b/docs/how-to/rocks/index.rst index 24c75c7e6..43b3ac9df 100644 --- a/docs/how-to/rocks/index.rst +++ b/docs/how-to/rocks/index.rst @@ -5,6 +5,7 @@ Use Rockcraft's GitHub Action Convert an entrypoint to a Pebble layer + Add a new internal user to a rock Publish a rock to a registry Migrate a Docker image to a chiselled rock Chisel an existing rock diff --git a/docs/reference/rockcraft.yaml.rst b/docs/reference/rockcraft.yaml.rst index 3c52d306c..05a6bf393 100644 --- a/docs/reference/rockcraft.yaml.rst +++ b/docs/reference/rockcraft.yaml.rst @@ -121,6 +121,8 @@ The license of the software packaged inside the rock. This must either be "proprietary" or match the SPDX format. It is case insensitive (e.g. both ``MIT`` and ``mit`` are valid). +.. _rockcraft_yaml_run_user: + ``run-user`` ------------ diff --git a/docs/tutorial/node-app.rst b/docs/tutorial/node-app.rst index fb1e78306..1543cf53e 100644 --- a/docs/tutorial/node-app.rst +++ b/docs/tutorial/node-app.rst @@ -1,3 +1,5 @@ +.. _bundle_a_nodejs_app_within_a_rock: + Bundle a Node.js app within a rock **********************************