Skip to content

Commit

Permalink
docs: add a guide for adding internal users to rocks (#653)
Browse files Browse the repository at this point in the history
* docs: add a guide for adding internal users to rocks

* apply Alex review changes

* address tiago review comments

* remove the requirement of passwd_bins slice

* fix linting issues

* address some review changes

* address cris review remarks

* fix linting issue

* fix tests

* fix test_lifecycle test

* address more review remarks

* fix link

* fix the spread test

* remove obsolete part

* switch to node service

* newline addition

* switch to a simple python http server example

* [REVIEW] Add a clean copy of the sample rockcraft YAML

Including the working rockcraft.yaml as-is displays the code breakpoints on the page, which is no good.

---------

Co-authored-by: Michael DuBelko <[email protected]>
Co-authored-by: Tiago Nobrega <[email protected]>
  • Loading branch information
3 people authored Aug 16, 2024
1 parent 034ca5c commit 86bc240
Show file tree
Hide file tree
Showing 8 changed files with 205 additions and 0 deletions.
33 changes: 33 additions & 0 deletions docs/how-to/code/internal-user/rockcraft-clean.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
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
37 changes: 37 additions & 0 deletions docs/how-to/code/internal-user/rockcraft.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
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:

# [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]
11 changes: 11 additions & 0 deletions docs/how-to/code/internal-user/serve_user.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#!/usr/bin/python3.12

import getpass

print(
f"""\
Content-Type: text/html
Serving by {getpass.getuser()} on port 8080\
"""
)
24 changes: 24 additions & 0 deletions docs/how-to/code/internal-user/task.yaml
Original file line number Diff line number Diff line change
@@ -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]
95 changes: 95 additions & 0 deletions docs/how-to/rocks/add-internal-user-to-a-rock.rst
Original file line number Diff line number Diff line change
@@ -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 <rockcraft_yaml_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 <lifecycle>` 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
1 change: 1 addition & 0 deletions docs/how-to/rocks/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

Use Rockcraft's GitHub Action <rockcraft-pack-action>
Convert an entrypoint to a Pebble layer <convert-to-pebble-layer>
Add a new internal user to a rock <add-internal-user-to-a-rock>
Publish a rock to a registry <publish-a-rock>
Migrate a Docker image to a chiselled rock <migrate-to-chiselled-rock>
Chisel an existing rock <chisel-existing-rock>
Expand Down
2 changes: 2 additions & 0 deletions docs/reference/rockcraft.yaml.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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``
------------

Expand Down
2 changes: 2 additions & 0 deletions docs/tutorial/node-app.rst
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
.. _bundle_a_nodejs_app_within_a_rock:

Bundle a Node.js app within a rock
**********************************

Expand Down

0 comments on commit 86bc240

Please sign in to comment.