Last week I had to continue the work that someone else had started on the creation of a Rock image. In particular, the OpenTelemetry Collector image.
It had been a while since I had written a Rock image and this time I wanted to ‘play’ a bit with the permissions and ownership of the configuration and binary files that the image uses.
User who runs the service
This time I wanted the service to be run by a user with as few permissions as possible. This way root
and ubuntu
users were ruled out.
Fortunately, we can do that easily: The run-user
directive let us use the _daemon_
user.
So if we define the service like this:
services:
otelcol:
override: replace
summary: "Entry point for opentelemetry-collector oci-image"
startup: enabled
command: "/usr/bin/otelcol --config /etc/otelcol/config.yaml"
run-user: _daemon_
The Pebble and the services are run by the _daemon_
user :
_daemon_@76699472ac6f:/$ ps axu | head -n 3
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
_daemon_ 1 0.0 0.0 1232928 9696 ? Ssl 22:46 0:00 /usr/bin/pebble enter
_daemon_ 15 0.2 0.5 1336192 80932 ? Sl 22:46 0:00 /usr/bin/otelcol --config /etc/otelcol/config.yaml
Permissions and Ownership
The OpenTelemetry Collector image has two fundamental files:
- The binary that Pebble runs:
/usr/bin/otelcol
- The config file:
/etc/otelcol/config.yaml
Which I wanted to be owned by _daemon_
as well. I also wanted to set specific permissions for each one:
500
to/usr/bin/otelcol
600
to/etc/otelcol/config.yaml
Permissions
In other rock images we have set permissions like this, as an argument to the install
command in the part
that build the image:
parts:
otelcol:
...
override-build: |
make build DISTRIBUTIONS="otelcol"
install -D -m500 distributions/otelcol/_build/otelcol ${CRAFT_PART_INSTALL}/opt/otelcol/otelcol
install -D -m600 distributions/otelcol/config.yaml ${CRAFT_PART_INSTALL}/opt/otelcol/config.yaml
...
This works, but it is an (ugly and) imperative way of doing it. Can’t it be declarative? Sure, the permissions
key to the rescue:
parts:
otelcol:
...
override-build: |
make build DISTRIBUTIONS="otelcol"
install -D distributions/otelcol/_build/otelcol ${CRAFT_PART_INSTALL}/opt/otelcol/otelcol
install -D distributions/otelcol/config.yaml ${CRAFT_PART_INSTALL}/opt/otelcol/config.yaml
organize:
opt/otelcol/otelcol: usr/bin/otelcol
opt/otelcol/config.yaml: etc/otelcol/config.yaml
permissions:
# _daemon_ user has UID/GID = 584792
# Ref: https://documentation.ubuntu.com/rockcraft/en/stable/reference/rockcraft.yaml/#run-user
- path: etc/otelcol/config.yaml
owner: 584792
group: 584792
mode: "600"
- path: usr/bin/otelcol
owner: 584792
group: 584792
mode: "500"
Note that owner
and group
keys are integers. I’ve set 584792
since it is the _daemon_
UID/GUI. Now you may ask:
Can I assume that always the
_daemon_
user will have the584792
id?
Yes, that’s precisely the concept behind shared usernames - anywhere, everywhere that UID is reserved for that username
You can verify in the container that everything is OK:
_daemon_@164bde59aa53:/$ ls -l /usr/bin/otelcol
-r-x------ 1 _daemon_ _daemon_ 110141592 Jan 13 22:43 /usr/bin/otelcol
_daemon_@164bde59aa53:/$ ls -l /etc/otelcol/config.yaml
-rw------- 1 _daemon_ _daemon_ 1461 Jan 13 22:43 /etc/otelcol/config.yaml