[GH-ISSUE #6358] docs: manpage should explain precedence of CLI options vs profile settings #3249

Closed
opened 2026-05-05 09:51:20 -06:00 by gitea-mirror · 7 comments
Owner

Originally created by @smheidrich on GitHub (May 26, 2024).
Original GitHub issue: https://github.com/netblue30/firejail/issues/6358

As far as I can tell, settings in a profile always take precedence over settings provided via CLI arguments. Is that right? And as far as I can tell, this isn't documented anywhere in the manpage. Even if I'm wrong, the inverse statement isn't documented in the manpage either (again: AFAICT).

Describe the solution you'd like

The precedence of profiles vs CLI options should be explained in the manpage. Probably in the section at the beginning, either just before or just after the profile discovery algorithm is explained.

Describe alternatives you've considered

None.

Additional context

None.

Relates to:

Originally created by @smheidrich on GitHub (May 26, 2024). Original GitHub issue: https://github.com/netblue30/firejail/issues/6358 ### Is your feature request related to a problem? Please describe. As far as I can tell, settings in a profile always take precedence over settings provided via CLI arguments. Is that right? And as far as I can tell, this isn't documented anywhere in the [manpage](https://github.com/netblue30/firejail/blob/master/src/man/firejail.1.in). Even if I'm wrong, the inverse statement isn't documented in the manpage either (again: AFAICT). ### Describe the solution you'd like The precedence of profiles vs CLI options should be explained in the manpage. Probably in the section at the beginning, either just before or just after the profile discovery algorithm is explained. ### Describe alternatives you've considered None. ### Additional context None. Relates to: * #4385
gitea-mirror 2026-05-05 09:51:20 -06:00
Author
Owner

@rusty-snake commented on GitHub (May 26, 2024):

Firejails options work by staring with a minimal sandbox and then adding stuff like mounts, seccomp filters, process attributes, …. The most options do not have an inverse, hence there is no precedence, either they are present somewhere or nowhere.

There is only a small number of options that have any kind of inverse or an single value option (most are flags or collections):

  • ignore
  • noblacklist/blacklist
  • nowhitelist/whitelist
  • private/private /foo/bar
  • private-cwd
  • protocol
  • caps*
  • dbus-{user,system}
  • hostname
  • join-or-start
  • name
  • oom
  • [not complete list]

The override behaviour of each of them has to be documented separately cause they do not follow any global logic. Secondly the usage of --profile influences the cli vs. profile semantic.

Documenting override behaviour is not limited to cli vs. profile. Also inside profiles (and include chains) it is non-intuitive. Given the following profile, how will ${DOCUMENTS} end up?

a) blacklisted (ro/rw does not matter)
b) not blacklisted and read-only
c) not blacklisted and read-write [right answer]

noblacklist ${DOCUMENTS}
blacklist ${DOCUMENTS}
read-only ${DOCUMENTS}
read-write ${DOCUMENTS}
<!-- gh-comment-id:2132271333 --> @rusty-snake commented on GitHub (May 26, 2024): Firejails options work by staring with a minimal sandbox and then adding stuff like mounts, seccomp filters, process attributes, …. The most options do not have an inverse, hence there is no precedence, either they are present somewhere or nowhere. There is only a small number of options that have any kind of inverse or an single value option (most are flags or collections): - `ignore` - `noblacklist`/`blacklist` - `nowhitelist`/`whitelist` - `private`/`private /foo/bar` - `private-cwd` - `protocol` - `caps*` - `dbus-{user,system}` - `hostname` - `join-or-start` - `name` - `oom` - [not complete list] The override behaviour of each of them has to be documented separately cause they do not follow any global logic. Secondly the usage of `--profile` influences the cli vs. profile semantic. Documenting override behaviour is not limited to cli vs. profile. Also inside profiles (and include chains) it is non-intuitive. Given the following profile, how will `${DOCUMENTS}` end up? a) blacklisted (ro/rw does not matter) b) not blacklisted and read-only c) not blacklisted and read-write [right answer] ``` noblacklist ${DOCUMENTS} blacklist ${DOCUMENTS} read-only ${DOCUMENTS} read-write ${DOCUMENTS} ```
Author
Owner

@ghost commented on GitHub (May 26, 2024):

As far as I can tell, settings in a profile always take precedence over settings provided via CLI arguments. Is that right?

Not what I'm seeing:

$ firejail --ignore=quiet --ignore='include disable-common.inc' echo "testing"
Reading profile /etc/firejail/default.profile
Reading profile /etc/firejail/globals.local
Reading profile /etc/firejail/disable-programs.inc

** Note: you can use --noprofile to disable default.profile **

firejail version 0.9.73

Parent pid 16527, child pid 16528

Warning: An abstract unix socket for session D-BUS might still be available. Use --net or remove unix from --protocol set.
Base filesystem installed in 52.42 ms
Child process initialized in 117.45 ms
testing

Parent is shutting down, bye...

Observations: default.profile contains include disable-common.inc and CLI can and does override that when desired...

Do you have a reproducers for your assumed behaviour?

I do agree this might need extra clarity in documentation, cfr. @rusty-snake's remarks.

<!-- gh-comment-id:2132274255 --> @ghost commented on GitHub (May 26, 2024): > As far as I can tell, settings in a profile always take precedence over settings provided via CLI arguments. Is that right? Not what I'm seeing: ```sh $ firejail --ignore=quiet --ignore='include disable-common.inc' echo "testing" Reading profile /etc/firejail/default.profile Reading profile /etc/firejail/globals.local Reading profile /etc/firejail/disable-programs.inc ** Note: you can use --noprofile to disable default.profile ** firejail version 0.9.73 Parent pid 16527, child pid 16528 Warning: An abstract unix socket for session D-BUS might still be available. Use --net or remove unix from --protocol set. Base filesystem installed in 52.42 ms Child process initialized in 117.45 ms testing Parent is shutting down, bye... ``` Observations: default.profile contains `include disable-common.inc` and CLI can and does override that when desired... Do you have a reproducers for your assumed behaviour? I do agree this might need extra clarity in documentation, cfr. @rusty-snake's remarks.
Author
Owner

@smheidrich commented on GitHub (May 26, 2024):

Thanks for the replies!

@rusty-snake

The most options do not have an inverse, hence there is no precedence, either they are present somewhere or nowhere.

There is only a small number of options that have any kind of inverse or an single value option (most are flags or collections):

So what happens for collections without an inverse like e.g. --protocol? Is the end result just the union of values given at each point where it was specified? If at least these collections follow a simple logic like that, that's something that could be explained in the manpage. And likewise for flags without an inverse ("if it's there once, it applies").

The override behaviour of each of them has to be documented separately cause they do not follow any global logic.

There is probably a way to say that in the manpage, too. Maybe something like:

If options that are neither simple flags nor simple collections are provided multiple times, the exact behavior is option-specific and usually explained in the option's own documentation.


@glitsj16 I arrived at that conclusion because I wasn't able to un-blacklist nc (blacklisted in disable-common.inc) by doing --noblacklist=/usr/bin/nc (plus --noblacklist for each of that symlink's targets, recursively). I similarly wasn't able to disallow UNIX domain socket connections via --protocol=inet, because default.profile already contains protocol unix,inet,inet6. In each case, commenting out those lines in the profiles resolved it.

But given @rusty-snake's comment, it seems it's much more complicated than I thought.


I'll write a PR with a draft of what I think might be an OK explanation, then we can refine it from there.

<!-- gh-comment-id:2132279149 --> @smheidrich commented on GitHub (May 26, 2024): Thanks for the replies! @rusty-snake > The most options do not have an inverse, hence there is no precedence, either they are present somewhere or nowhere. > There is only a small number of options that have any kind of inverse or an single value option (most are flags or collections): So what happens for collections without an inverse like e.g. `--protocol`? Is the end result just the union of values given at each point where it was specified? If at least these collections follow a simple logic like that, that's something that could be explained in the manpage. And likewise for flags without an inverse ("if it's there once, it applies"). >The override behaviour of each of them has to be documented separately cause they do not follow any global logic. There is probably a way to say that in the manpage, too. Maybe something like: >If options that are neither simple flags nor simple collections are provided multiple times, the exact behavior is option-specific and usually explained in the option's own documentation. ----- @glitsj16 I arrived at that conclusion because I wasn't able to un-blacklist `nc` (blacklisted in `disable-common.inc`) by doing `--noblacklist=/usr/bin/nc` (plus `--noblacklist` for each of that symlink's targets, recursively). I similarly wasn't able to disallow UNIX domain socket connections via `--protocol=inet`, because `default.profile` already contains `protocol unix,inet,inet6`. In each case, commenting out those lines in the profiles resolved it. But given @rusty-snake's comment, it seems it's much more complicated than I thought. ----- I'll write a PR with a draft of what I think might be an OK explanation, then we can refine it from there.
Author
Owner

@smheidrich commented on GitHub (May 26, 2024):

Sorry, I only now saw that at least for --protocol, the behavior is already specified in the manpage:

Multiple protocol commands are allowed and they accumulate.

And searching for accumulate in the manpage, I see a lot of other options have similar sentences. In that case, disregard what I wrote about collections - that is actually fine as-is.

Then maybe just 1 sentence to explain the precedence for multiple or conflicting options is explained in the option's own docs, done.

<!-- gh-comment-id:2132281916 --> @smheidrich commented on GitHub (May 26, 2024): Sorry, I only now saw that at least for `--protocol`, the behavior is already specified in the manpage: >Multiple protocol commands are allowed and they accumulate. And searching for `accumulate` in the manpage, I see a lot of other options have similar sentences. In that case, disregard what I wrote about collections - that is actually fine as-is. Then maybe just 1 sentence to explain the precedence for multiple or conflicting options is explained in the option's own docs, done.
Author
Owner

@ghost commented on GitHub (May 26, 2024):

I arrived at that conclusion because I wasn't able to un-blacklist nc (blacklisted in disable-common.inc) by doing --noblacklist=/usr/bin/nc (plus --noblacklist for each of that symlink's targets, recursively). I similarly wasn't able to disallow UNIX domain socket connections via --protocol=inet, because default.profile already contains protocol unix,inet,inet6. In each case, commenting out those lines in the profiles resolved it.

I can see the confusion.

For the nc case, it's important to note that disable-common.inc refers to it as blacklist ${PATH}/nc, not /usr/bin/nc. Once you keep that in mind things work fine on CLI:

$ firejail --ignore=quiet --ignore='blacklist ${PATH}/nc' nc -h
$ firejail --ignore=quiet --noblacklist='${PATH}/nc' nc -h

For overriding protocol you need (1) a new protocol option AND (2) stop that from being overriden by the profile via a additional --ignore=protocol.

HTH

<!-- gh-comment-id:2132282192 --> @ghost commented on GitHub (May 26, 2024): > I arrived at that conclusion because I wasn't able to un-blacklist nc (blacklisted in disable-common.inc) by doing --noblacklist=/usr/bin/nc (plus --noblacklist for each of that symlink's targets, recursively). I similarly wasn't able to disallow UNIX domain socket connections via --protocol=inet, because default.profile already contains protocol unix,inet,inet6. In each case, commenting out those lines in the profiles resolved it. I can see the confusion. For the `nc` case, it's important to note that disable-common.inc refers to it as `blacklist ${PATH}/nc`, not /usr/bin/nc. Once you keep that in mind things work fine on CLI: $ firejail --ignore=quiet --ignore='blacklist ${PATH}/nc' nc -h $ firejail --ignore=quiet --noblacklist='${PATH}/nc' nc -h For overriding protocol you need (1) a new protocol option AND (2) stop that from being overriden by the profile via a additional `--ignore=protocol`. HTH
Author
Owner

@rusty-snake commented on GitHub (May 26, 2024):

e.g. --protocol?

protocol is crazy, see e25596bfb7/src/firejail/profile.c (L1959-L1973)

caps* and seccomp vs. seccomp syscall might be too a bit complicated.

For the most collections it will be the union. But be careful what a collection is.

foo a,b,c
bar α
bar β
bar γ
baz א,ב,ג
  • foo is a collection where its option accepts multiple elements
  • bar is a collection where its option accepts a single element
  • baz is a scalar where its option accepts a collection

Think of List[Box(Object()), Box(Object()), Box(Object())] vs. Box(List[Object(), Object(), Object()]). I.e. internal representation != external representation.

I'll write a PR with a draft of what I think might be an OK explanation, then we can refine it from there.

Every bit of clarity helps. Even if only the simple case are covered first.

For overriding protocol you need (1) a new protocol option AND (2) stop that from being overriden by the profile via a additional --ignore=protocol.

That works. In general and for all options. protocol has a second way, see above.

<!-- gh-comment-id:2132283999 --> @rusty-snake commented on GitHub (May 26, 2024): > e.g. --protocol? `protocol` is crazy, see https://github.com/netblue30/firejail/blob/e25596bfb7a49c0a7d5d4ed428f92796a8e2f26e/src/firejail/profile.c#L1959-L1973 `caps*` and `seccomp` vs. `seccomp syscall` might be too a bit complicated. For the most collections it will be the union. But be careful what a collection is. ``` foo a,b,c bar α bar β bar γ baz א,ב,ג ``` - foo is a collection where its option accepts multiple elements - bar is a collection where its option accepts a single element - baz is a scalar where its option accepts a collection Think of `List[Box(Object()), Box(Object()), Box(Object())]` vs. `Box(List[Object(), Object(), Object()])`. I.e. internal representation != external representation. > I'll write a PR with a draft of what I think might be an OK explanation, then we can refine it from there. Every bit of clarity helps. Even if only the simple case are covered first. > For overriding protocol you need (1) a new protocol option AND (2) stop that from being overriden by the profile via a additional --ignore=protocol. That works. In general and for all options. `protocol` has a second way, see above.
Author
Owner

@smheidrich commented on GitHub (May 26, 2024):

Thanks for the replies again!

@glitsj16 That makes sense. And I just saw that --noblacklist='${PATH}/nc' works as well, so maybe the --noblacklist example in the manpage (which uses a hardcoded nc path and hence doesn't work for the default profile) could be updated to use that. I'll put that in the PR.

@rusty-snake I can see why this has been difficult to document 😮 The +/-/= syntax in that comment doesn't seem to work on the command line though (at least when I tried with --protocol=-unix just now), so is that something only available in profiles or internally?

<!-- gh-comment-id:2132293951 --> @smheidrich commented on GitHub (May 26, 2024): Thanks for the replies again! @glitsj16 That makes sense. And I just saw that `--noblacklist='${PATH}/nc'` works as well, so maybe the `--noblacklist` example in the manpage (which uses a hardcoded `nc` path and hence doesn't work for the default profile) could be updated to use that. I'll put that in the PR. @rusty-snake I can see why this has been difficult to document :open_mouth: The `+`/`-`/`=` syntax in that comment doesn't seem to work on the command line though (at least when I tried with `--protocol=-unix` just now), so is that something only available in profiles or internally?
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: github-starred/firejail#3249
No description provided.