skip to content

You clear an SELinux denial by running `chcon -t httpd_sys_content_t` on a data directory and the service starts working; weeks later, after the machine is relabelled, it breaks again. Where do SELinux file labels live, and what should you have done instead?

level: middleimportance: should knowfreq 52%

answer

  1. actual label versus expected label
  2. the xattr on the inode
  3. a database of path regexes
  4. restorecon replays, it does not remember
  5. mv keeps the label, cp does not

basics

~20 s

A label lives in the file's security.selinux extended attribute, and chcon writes it directly. A relabel replays the policy's default-context database, which still says the old type, so it overwrites your change. Record the intent with semanage fcontext, then apply it with restorecon.

solid answer

~50 s

`chcon` sets the label directly on the inode, in the `security.selinux` extended attribute. It works immediately, but it does not tell the system *why* that path should have that type. The policy keeps a separate default-context database — the file-contexts rules, managed with `semanage fcontext` — and `restorecon`, a package relabel, or a full `/.autorelabel` pass all rewrite labels from that database. Since the database was never updated, the relabel dutifully put your directory back to its default type and the service broke again. The durable fix is two steps: add the rule with `semanage fcontext -a -t httpd_sys_content_t "/srv/data(/.*)?"`, then apply it with `restorecon -Rv /srv/data`. After that, any future relabel reproduces the same result. Treat `chcon` as a temporary probe you use to confirm a hypothesis, not as a fix you leave in place.

code

bash · 13 lines
bash
# temporary probe: does the label explain the failure?
chcon -t httpd_sys_content_t /srv/data/index.html

# durable fix: record the rule, then apply it from the database
semanage fcontext -a -t httpd_sys_content_t "/srv/data(/.*)?"
restorecon -Rv /srv/data

# or declare the new path equivalent to a standard one
semanage fcontext -a -e /var/www /srv/www
restorecon -Rv /srv/www

# verify: a dry run should propose no changes
restorecon -n -v -R /srv/data

go deeper

for a junior

Know that ls -Z shows a file's SELinux label and that restorecon puts a file back to the label the system expects for that location.

for a middle

Explain the split between the label on the inode and the policy's database of default contexts, and give the two-step durable fix: semanage fcontext to record the rule, restorecon to apply it.

for a senior

Show that you convert every ad-hoc chcon into a database rule captured in configuration management, and that you verify with a restorecon dry run so a package update or autorelabel cannot resurrect the outage.

for a principal

Set the expectation that labelling for non-standard data paths is part of the service's build definition, not tribal knowledge, so a rebuilt or restored host comes up correctly labelled without human intervention.

## Two places a label can be said to live It helps to separate the *actual* label from the *expected* label. The actual label is on the object: an extended attribute called `security.selinux` on the inode, which `ls -Z` prints and which travels with the file across renames and hard links because it belongs to the inode, not to the name. The expected label is in the policy: a database of regular expressions mapping pathnames to default contexts, shipped by the policy package and extended by the administrator. On a targeted-policy system it is compiled from the file-contexts rules under `/etc/selinux/targeted/contexts/files/`. Nothing consults this database at access time — the kernel only ever looks at the actual label. The database exists so the system can *recompute* correct labels on demand. ## What each tool does `chcon` writes the actual label and touches nothing else. It is the SELinux equivalent of `chmod`: an immediate, unexplained change to one object. `restorecon` does the opposite: it reads the expected label out of the database and writes it onto the object, discarding whatever was there. `restorecon -Rv` walks a tree and reports every change; `restorecon -n -v` previews without writing, which is the safe way to find out what a relabel would do before you run it. `semanage fcontext` edits the database itself. `semanage fcontext -a -t <type> "<regex>"` adds a local rule; `-l` lists them, `-d` deletes one. Local rules are stored separately from the policy's shipped rules and survive policy package updates. So the sequence in the question is now obvious: `chcon` changed the object, the database still disagreed, and the first thing to relabel that path — an explicit `restorecon`, a package update touching the tree, or a boot-time `/.autorelabel` — resolved the disagreement in the database's favour. ```bash # durable: teach the policy, then apply it semanage fcontext -a -t httpd_sys_content_t "/srv/data(/.*)?" restorecon -Rv /srv/data ``` Note the regex form. `semanage fcontext` takes a regular expression, not a glob, and `(/.*)?` is the idiom meaning "this directory and everything under it". Anchoring it wrongly is the usual way this step silently does nothing. ## Why files arrive mislabelled in the first place The most common cause is moving data instead of copying it. `mv` preserves the inode and therefore preserves the extended attribute, so a file relocated from a home directory into a service's data directory keeps its home-directory type and the service cannot read it. A plain `cp` creates a new inode, which gets a label derived from the destination and the policy's transition rules — usually the right one. `cp -a` or `cp --preserve=context` deliberately carries the source label across, which reintroduces the same problem. Other routine causes: unpacking an archive that restores stored attributes, restoring from a backup taken on a differently-labelled system, or creating a service's data directory in a location the policy has no rule for at all, which lands it in a generic default type. ## When a path genuinely has no correct type If you have relocated a service's data to a non-standard path, there may be no shipped rule that covers it. Two clean options exist. Either add the `semanage fcontext` rule as above, mapping your path to the type the policy already knows about, or use `semanage fcontext -a -e /var/www /srv/www` to declare the new path *equivalent* to the standard one, so every rule that applies under the original applies under yours. The equivalency form is preferable when the tree contains several different types under it, because it inherits the whole set rather than flattening everything to one type. ## What good practice looks like Use `chcon` to test a hypothesis quickly, confirm the service recovers, then immediately convert the finding into a `semanage fcontext` rule plus a `restorecon`, and put that pair into whatever configuration management builds the host. Verify by running `restorecon -n -v` over the tree and seeing no proposed changes — that is the check that your fix is actually durable. If you ever find yourself scripting `chcon` in a cron job or a service start-up hook to keep labels correct, that is a sign the database rule is missing.

  • Why does moving a file into a service's data directory often break it, while copying the same file works?
    `mv` within a filesystem keeps the same inode, so the `security.selinux` extended attribute — and therefore the type — moves with it unchanged. A plain `cp` creates a new inode whose label is derived from the destination and the policy's transition rules, so it usually comes out correct. Beware `cp -a` and `cp --preserve=context`, which explicitly carry the old label over.
  • You add a semanage fcontext rule and nothing changes on disk. Why?
    Because `semanage fcontext` only edits the database of expected contexts; it never touches existing files. You still have to run `restorecon` over the path to write the new expectation onto the inodes. The other common cause is a rule whose regular expression does not match — remember it is a regex, not a glob, and that a directory tree needs the `(/.*)?` suffix.
  • How would you confirm that a labelling fix will actually survive the next relabel?
    Run `restorecon -n -v -R` over the tree. That reports what a relabel would change without changing anything, so a clean run means the on-disk labels and the policy's expected contexts already agree. If it proposes reverting your labels, the database rule is missing or its regex does not match the paths you fixed.

chcon repaints one door; semanage fcontext updates the building's paint schedule. The next time the painters come round with the schedule, only the schedule's colour survives.

saying these in an interview costs you the question

  • chcon is the permanent way to set a label
  • Labels are computed from the path on every access
  • semanage fcontext relabels the files by itself
  • cp -a is the safe way to move files into a service directory
  • A relabel only runs if you ask for one explicitly

context