File Permissions

Benutzer und Gruppen im System können Dateien und Verzeichnisse besitzen. Besitzer und Gruppen können Lese, Schreib- (und damit Lösch-) und/oder Ausführungsrechte auf Dateien besitzen; bei Verzeichnissen wird das Execute-Recht als Berechtigung zum Wechsel in das Verzeichnis genutzt.

Neben dem Benutzer („user“, „u“) und der Gruppe („group“, „g“) gibt es noch den Rest der Welt („others“, „o“). „a“ bezeichnet kurz „all“, die Kurzform für „ugo“.

User, Group und Others können wie erwähnt unterschiedliche Berechtigungen auf Dateien und Verzeichnisse besitzen: read, write, execute („rwx“).

Folgende Befehle verwalten die Datei-Berechtigungen:

  • chown: Change Owner, ändert den Besitzer einer Datei/eines Verzeichnisses:

  • chgrp: Change Group, ändert die Gruppe einer Datei/eines Verzeichnisses:

  • chmod: Change Mode, ändert die Berechtigungen für User, Group und Others

Die drei Berechtigungsstufen werden den drei Besitzergruppen wie folgt im Klartext zugeordnet:

su - linus

cd /tmp
touch myscript
chmod u=rwx,g=rw,o=r myscript

Die Ausgabe -rwxrw-r--. 1 linus sales 128 Dec 24 19:20  myscript von ls -l bedeutet:

-rwxrw-r--.
  • Lesen als - rwx rw- r-- .

  • Erste Stelle: Mit „-“ wird eine Datei symbolisiert. Andere Angaben wären „d“ für Directory oder „l“ für einen symbolischen Link.

  • Die nächsten drei Stellen beschreiben die Berechtigungen des Besitzer linus. Er besitzt alle Rechte („rwx“) auf die Datei.

  • Die nächsten drei Stellen beschreiben die Berechtigungen der Gruppe sales; sie besitzt Lese- und Schreibrechte.

  • Alle anderen besitzen Leserechte.

  • Letztes Zeichen: der „.“ bedeutet, dass keine erweiterten Berechtigungen (ACL) gesetzt sind („+“ = ACL gesetzt).

1

Anzahl der harten Links auf die Datei (dort immer mindestens 1); bei Verzeichnissen Summe der enthaltenen Dateien und Unterverzeichnisse auf der ersten Ebene.

linus

Besitzer der Datei.

sales

Gruppe der Datei.

128

Grösse in Bytes.

Dec 24 19:20

Zeitpunkt der letzten Änderung

myscript

Name der Datei bzw. des Verzeichnisses.

Die rwx-Berechtigungen lassen sich auch durch Werte ausdrücken, wobei 4 = read, 2 = write und 1 = execute entspricht. In der Summe ergibt sich demnach:

Wert (binär)

Ausprägung

0 (000)

---

1 (001)

--x

2 (010)

-w-

3 (011)

-wx

4 (100)

r--

5 (101)

r-x

6 (110)

rw-

7 (111)

rwx

Der Schlüssel zum Setzen von Datei- und Verzeichnisberechtigungen ist der Befehl chmod. Er kennt eine Klartext- als auch die Bitnotation:

chmod u=rwx,g=rw,o=r myscript
chmod 764 myscript

In der Klartext-Notation werden bestehende Rechte mit „=“ überschrieben, mit „+“ ergänzt und mit „-“ entfernt.

chmod uo+wx,g=r test.txt
chmod u+x,u-w test.sh

Den Besitzer und die Gruppe einer Datei oder eines Verzeichnisses wechselt man so (drei Beispiele):

chown new_user filename
chgrp new_group filename
chown new_user:new_group filename

Alle Befehle lassen sich rekursiv über den Parameter -R bzw. --recursive auf ganze Verzeichnisbäume anwenden. Für chmod sollte dann das Executable-Flag nur mit X (statt x) angegeben werden - es sorgt dafür, dass nur Verzeichnisse oder Dateien, die bereits in irgendeiner Form ausführbar sind, das Executable-Flag erhalten.

-h bzw. --no-dereference ändert den Besitzer/die Gruppenzugehörigkeit eines symbolischen Links. Ohne Angabe dieses Parameters wird die Eigentümerschaft der darunter liegenden Datei modifiziert.

Setuid, Setgid, Sticky Bit

Darüber hinaus existieren noch drei Sonderrechte: Setuid, Setgid und das Sticky Bit.

Eine ausführbare Datei, die das Setuid-Bit besitzt, läuft unter der User-ID des Besitzers der Datei; ein Verzeichnis mit Setuid-Bit setzt für neu angelegte Dateien den Besitzer auf den des Verzeichnisses (deswegen „set“uid).

Für Setgid gilt das gleiche wie für Setuid, nur auf Basis der Gruppe.

Das Sticky Bit verhindert bei Verzeichnissen, dass Benutzer die Dateien anderer Benutzer löschen dürfen. Oder anders: nur der Besitzer der Datei darf diese in einem Sticky-Verzeichnis löschen, alle anderen (auch mit Schreibberechtigung) nicht.

Die Bit-Wertigkeiten hier sind:

  • setuid: 4 = s

  • setgid: 2 = s

  • sticky: 1 = t

Beispiel, in dem das Setgid-Bit auf ein Verzeichnis gesetzt wird:

mkdir /share
useradd linus
groupadd sales
chown linus:sales /share
chmod u+rwx,g=rxs,o= /share
chmod 2750 /share

Besitzer linus = rwx, Gruppe sales = rx inkl. Setgid, alle anderen keine Rechte - in diesem Verzeichnis neu erstellte Inhalte gehören demnach der Gruppe sales.

Das gleiche Beispiel mit Setuid und Setgid-Bit:

chmod u+rwxs,g=rxs,o= /share
chmod 6750 /share

Man beachte die Ausgabe des ls -l-Kommandos: ein kleines s bedeutet „shared + execute“, ein grosses S bedeutet „shared, ohne execute-Recht“.

Man entzieht einer Datei oder einem Verzeichnis alle Rechte mit:

chmod a-rwxs /share

So kopiert man die Berechtigungen von file1 auf file2:

chmod --reference file1 file2

Datei-Berechtigungen am praktischen Beispiel

Alle Benutzer dürfen das Verzeichnis /share lesen, Benutzer linus besitzt darin den Ordner project-linus mit rwx-Rechten, auf das auch die sales-Gruppe schreibend zugreifen können soll.

Der Projekt-Ordner muss also der Gruppe sales gehören; mit dem Setgid-Bit wird sichergestellt, das auch in Zukunft neu angelegte Dateien diese Gruppenzugehörigkeit erben. Der root-User geht dafür folgendermassen vor:

mkdir -p /share/project-linus
chown linus:sales /share/project-linus

share besitzt direkt nach dem Anlegen „rwx, rx, rx, root:root“, project-linus nach dem chown „rwx, rx, rx, linus:sales“. Es wird noch das Setgid-Bit benötigt, sales soll noch schreiben dürfen, und alle anderen möchten wir ausschliessen:

chmod g+ws,o= /share/project-linus

Jede darin angelegte Datei wird fortan der Gruppe sales zugeordnet. Soll linus auch Dateien bearbeiten dürfen, die Benutzer aus der Gruppe sales angelegt haben, benötigt man entweder das Setuid-Bit (damit erben alle Dateien den Besitzer des übergeordneten Verzeichnisses, linus), oder man fügt linus der sales-Gruppe hinzu.

Warum 777 und 666 keine gute Idee ist

666 ermöglicht es jedem Benutzer des Systems, die damit geflaggten Dateien zu verändern, und im Fall von 777 sogar auszuführen.

Schlechtes Beispiel: Verzeichnis mit 777 und darin eine Datei mit 666 erstellen.

[user ~] $ mkdir test
[user ~] $ chmod 0777 test
[user ~] $ cd test/
[user ~/test] $ touch test.txt
[user ~/test] $ chmod 0666 test.txt

Ein anderer Benutzer des Systems kann diese Dateien nun manipulieren - neue Dateien ablegen, vorhandene Dateien verändern usw..

[hacker ~] $ cd /home/user/test
[hacker /home/user/test] $ ll
total 8.0K
drwxrwxrwx 2 user user 4.0K 2021-01-01 12:00 .
drwxr-xr-x 3 user user 4.0K 2021-01-01 12:00 ..
-rw-rw-rw- 1 user user    0 2021-01-01 12:00 test.txt
[hacker /home/user/test] $ touch bad
[hacker /home/user/test] $ echo "OVERWRITE" > test.txt
[hacker /home/user/test] $ cat test.txt
OVERWRITE

Dateien und Verzeichnisse schützen

Wer Dateien und Verzeichnisse vor (versehentlichem) Bearbeiten, Umbenennen, Verschieben oder Löschen schützen möchte - und das für alle Benutzer, auch für root - verwendet chattr. Dies ist zwar abhängig vom verwendeten Dateisystem, funktioniert aber zumindest auf Ext3, Ext4 und XFS identisch. Das i-Attribut verhindert das Modifizieren:

Attribut setzen:

chattr +i myfile
chattr +i mydir

Attribut entfernen:

chattr -i myfile
chattr -i mydir

Attribute auflisten:

lsattr

umask

Wird ein Verzeichnis neu erstellt, vergibt Linux zunächst die Berechtigung 0777 (also drwxrwxrwx), bei Dateien 0666 (-rw-rw-rw-). Die umask überschreibt anschliessend Berechtigungen auf neu erstellte Dateien und Verzeichnisse. Eine umask kann nur systemweit und nicht auf einzelne Verzeichnisse gesetzt werden.

Zur Erinnerung:

  • 1: Execute

  • 2: Write

  • 4: Read

In RHEL ist die umask für reguläre Benutzer auf 0002 gesetzt - es wird also das write-Bit bei „others“ gelöscht. Reguläre Benutzer sind Benutzer mit einer User-ID ab 200, bei denen der Benutzername und die Primary Group identisch gesetzt sind.

root besitzt die umask 0022, es wird also das write-Bit für „group“ und „others“ gelöscht.

umask abfragen - in oktaler und symbolischer Schreibweise:

# octal
umask
# symbolic
umask -S

Wer einfach nur die Standard-Berechtigungen für ein Verzeichnis ändern möchte, arbeitet besser mit ACLs (siehe weiter unten) - die umask gilt immer für das ganze System.

Hardening

Die umask kann für Benutzer und für root auf 0027 geändert werden (je nach Anforderung in /etc/login.defs, /etc/profile, /etc/bashrc, ~/.bash_profile oder ~/.bashrc), welche das write-Bit bei „group“ und sämtliche Berechtigungen für „others“ entfernt.

Erweitertes Beispiel für das Setzen einer umask:

/etc/profile.d/set_umask.sh
# Overrides default umask configuration
if [ $UID -gt 199 ] && [ "$(id -gn)" = "$(id -un)" ]; then
    umask 007
else
    umask 022
fi

Access Control Lists (ACL)

Die Datei-Berechtigungen („File Permissions“) sind einfach und effektiv, reichen aber nicht immer aus. Bei gehobenen Ansprüchen verwendet man Access Control Lists (ACL), um Benutzern und Gruppen den Zugriff auf Dateien feingranuliert zu erlauben oder zu verweigern. Um ACLs für ein Dateisystem zu aktivieren, musste man bis RHEL 6 dem entsprechenden Eintrag in der /etc/fstab in der Optionen-Spalte ein acl hinzufügen. Ab RHEL 7 sind ACLs für Ext4 und XFS bereits ab Werk aktiv.

Mit den File Permissions kann man nur drei verschiedene Rechte vergeben, für einen Besitzer, eine Gruppe, und für alle anderen. Man stösst schnell auf Anforderungen, bei denen dies nicht mehr ausreicht. Zum Beispiel, wenn eine Gruppe von Webentwicklern Zugriff auf die Dateien der Website haben soll, gleichzeitig aber auch die apache-Gruppe Zugriff benötigt. Solche Szenarien können mit ACLs umgesetzt werden.

Wie der Name impliziert, besteht eine ACL aus einer Liste von Einträgen. Jeder Eintrag vergibt einem bestimmten Benutzer, einer bestimmten Gruppe, oder allen anderen gewisse Rechte:

# "rwx" for user linus
user:linus:rwx

# "rw" for group sales
group:sales:rw

# "r" for others
other::r

Lässt man den expliziten Benutzer bzw. die explizite Gruppe weg, also user::rwx bzw. group::rw, beziehen sich die Einträge auf den Besitzer bzw. die Gruppe der traditionellen File Permissions.

Bemerkung

Heisst: Man kann über ACLs auch die File Permissions anpassen, analog zu chmod - und umgekehrt wird jede Anpassung per chmod im Hintergrund automatisch auch als ACL gespeichert.

Am Beispiel einer neu angelegten Datei:

touch /testfile
ll /testfile
-rw-r--r--. 1 root root 0 Dec 27 14:49 /testfile
# the file has ACL entries, even though they were never explicitly set
getfacl /testfile
# file: testfile
# owner: root
# group: root
user::rw-
group::r--
other::r--
# let's change the file permissions using chmod
chmod u=rwx /testfile
ll /testfile
-rwxr--r--. 1 root root 0 Dec 27 14:49 /testfile
# the change is reflected in the ACL, again without any explicit modification
getfacl /testfile
# file: testfile
# owner: root
# group: root
user::rwx
group::r--
other::r--
# the other way around also works, let's change the file permissions with setfacl.
setfacl --modify group::rw /testfile
getfacl /testfile
# file: testfile
# owner: root
# group: root
user::rwx
group::rw-
other::r--
# note that the dot indicates that there are no special ACL set, even after calling setfacl
ll /testfile
-rwxrw-r--. 1 root root 0 Dec 27 14:49 /testfile

Wenn nun ein Benutzer auf eine Datei zugreift (genauer: wenn der System-Call open(const char *pathname, int flags); mit den gewünschten Permissions r, w oder rw abgesetzt wird), wird nach einem ACL-Eintrag gesucht - zuerst für den Benutzer, dann für die Gruppen des Benutzers. Wird ein passender Eintrag mit den erforderlichen Rechten gefunden, wird der Zugriff erlaubt, andernfalls nicht.

Im folgenden Beispiel erhält der Benutzer linus Lese- und Schreibrechte auf /data/:

mkdir /data/
mkdir /data/folder/
touch /data/file

# first make sure that others cannot read via the traditional file permissions
setfacl --recursive --modify other::- /data/

# grant rwx for linus on folders, rw for files
find /data -type d -exec setfacl --modify user:linus:rwx {} \;
find /data -type f -exec setfacl --modify user:linus:rw {} \;
getfacl /data/
# file: data/
# owner: root
# group: root
user::rwx
user:linus:rwx
group::r-x
mask::rwx
other::---
getfacl /data/folder/
# file: data/folder/
# owner: root
# group: root
user::rwx
user:linus:rwx
group::r-x
mask::rwx
other::---
getfacl /data/file
# file: data/file
# owner: root
# group: root
user::rw-
user:linus:rw-
group::r--
mask::rw-
other::---

Wenn nun neue Dateien und Ordner angelegt werden, fehlen dort die Rechte für linus:

touch /data/new
getfacl /data/new
# file: data/new
# owner: root
# group: root
user::rw-
group::r--
other::r--

Der Grund: Es fehlen „default“-ACL-Einträge. Wenn für einen Ordner eine Standard-ACL gesetzt wird, wird diese auf neu angelegte Dateien und Unterordner vererbt. Für das obige Beispiel:

setfacl --default --recursive --modify user:linus:rwx /data/

mkdir /data/new-folder
getfacl /data/new-folder
# file: data/new-folder
# owner: root
# group: root
user::rwx
user:linus:rwx
group::r-x
mask::rwx
other::---
default:user::rwx
default:user:linus:rwx
default:group::r-x
default:mask::rwx
default:other::---
touch /data/new-file
getfacl /data/new-file
# file: data/new-file
# owner: root
# group: root
user::rw-
user:linus:rwx                  #effective:rw-
group::r-x                      #effective:r--
mask::rw-
other::---

Wie in der Ausgabe von getfacl zu sehen ist, tauchen nun ACL-Eintrag namens „mask“ auf. Die Maske schränkt die Zugriffsrechte aller Einträge ein, mit Ausnahme des Besitzers.

Bemerkung

Mit ACLs kann man keinen Besitzer („Owner“) für Dateien und Verzeichnisse setzen. Das geschieht traditionell über chown, chgrp usw.

Im Beispiel hat der Benutzer linus eigentlich rwx-Rechte. Da aber mask::rw- gesetzt ist, erhält der Benutzer letztendlich nur rw-Rechte - diese werden von getfacl als „effective“ angezeigt.

Beim Aufruf von setfacl wird die Maske automatisch berechnet, wenn sie nicht explizit angegeben wurde:

  • Die Maske ist immer das Maximum aller Rechte, die sowohl über File Permissions als auch über die ACL gesetzt wurden. Wenn also eine Datei ohne ACL die Rechte g=rw hat und eine ACL für group:sales:rwx erstellt wird, wird die Maske auf rwx gesetzt.

  • setfacl berechnet auch bereits bestehende Masken neu. Wenn also zuerst keine Gruppenrechte vergeben werden (g=) und dann group:sales:rx gefolgt von group:marketing:rwx gesetzt wird, ist die Maske nach dem ersten Aufruf rx, nach dem zweiten rwx.

  • Wenn setfacl mit dem Parameter --no-mask aufgerufen wird, wird die Maske nicht angepasst - es sei denn, es gibt noch keine Maske. In diesem Fall wird die Maske auf die aktuellen Gruppen-Rechte gesetzt, auch wenn gerade eine Benutzer-ACL gesetzt wird.

Zu letztem Punkt als Beispiel:

getfacl testfile
# file: testfile
# owner: root
# group: root
user::rw-
group::---
other::---
setfacl --modify user:linus:rwx --no-mask testfile
getfacl testfile
# file: testfile
# owner: root
# group: root
user::rw-
user:linus:rwx                #effective:---
group::---
mask::---
other::---

In Kombination mit der Standard-ACL verhält sich die Berechnung der Maske etwas anders:

  • Beim Anlegen neuer Dateien und Ordner werden per System-Call die gewünschten Berechtigungen angegeben. Unter Linux sind dies standardmässig 0777 für Ordner und 0666 für Dateien.

  • Bei der Vererbung der Standard-ACL wird die Maske so angepasst, dass keine Rechte vergeben werden, die nicht in den gewünschten Berechtigungen enthalten sind.

  • Dadurch wird das Executable-Bit beim Erstellen von Dateien nicht gesetzt, auch wenn es in der Standard-ACL gesetzt ist.

  • Dieses Verhalten ist sehr nützlich, da so die Standard-ACL für neu erstellte Dateien und Ordner passt, ohne dass alle Dateien versehentlich das Executable-Bit gesetzt haben.

  • Anmerkung: umask hat keinen Einfluss auf die Berechnung der Maske.

Soweit zur Theorie. Möchte man nun einer Gruppe von Webentwicklern ausgewählte Rechte auf das Apache-Verzeichnis /var/www/html geben, lässt sich das beispielsweise so umsetzen:

# apache user: rwx for folder, rw for files
find /var/www/html -type d -exec setfacl --modify user:apache:rwx {} \;
find /var/www/html -type f -exec setfacl --modify user:apache:rw {} \;
setfacl --recursive --default --modify user:apache:rwx /var/www/html

# apache group: rx for folder, r for files
find /var/www/html -type d -exec setfacl --modify group:apache:rx {} \;
find /var/www/html -type f -exec setfacl --modify group:apache:r {} \;
setfacl --recursive --default --modify group:apache:rx /var/www/html

# webdev group: rwx for folder, rw for files
find /var/www/html -type d -exec setfacl --modify group:webdev:rwx {} \;
find /var/www/html -type f -exec setfacl --modify group:webdev:rw {} \;
setfacl --recursive --default --modify group:webdev:rwx /var/www/html

# double check all entries, especially the mask
getfacl /var/www/html
# file: var/www/html/
# owner: apache
# group: apache
user::rwx
user:apache:rwx
group::r-x
group:apache:r-x
group:webdev:rwx
mask::rwx
other::r-x
default:user::rwx
default:user:apache:rwx
default:group::r-x
default:group:apache:r-x
default:group:webdev:rwx
default:mask::rwx
default:other::r-x
# adjust if required
setfacl --modify mask::rwx /var/www/html
setfacl --default --modify mask::rwx /var/www/html

Traditionelle File Permissions per chmod (und auch der Besitzer / Gruppe) sind somit irrelevant, da sowohl der apache als auch die Webentwickler über die ACLs zugreifen können.

Bemerkung

Wie bei traditionellen File Permissions auch muss man bei ACLs rx-Rechte auf alle übergeordneten Ordner haben, um am Ende tatsächlich auf die Dateien zugreifen zu können.

Ein weiteres Beispiel aus der Praxis. Webentwickler benötigen oft Leserechte auf bestimmte Logfiles, um ihre Anwendung zu debuggen. Eine mögliche Implementierung könnte wie folgt aussehen:

setfacl --modify group:webdev:rx /var/log/httpd
setfacl --modify group:webdev:r /var/log/httpd/*

# we also want to make sure webdev is allowed to read newly created files
# to be precise, we want `group:webdev:r` for new files, and `group:webdev:rx` for new folders
setfacl --default --modify group:webdev:rx /var/log/httpd

# same for /var/log/php-fpm
setfacl --modify group:webdev:rx /var/log/php-fpm
setfacl --modify group:webdev:r /var/log/php-fpm/*
setfacl --default --modify group:webdev:rx /var/log/php-fpm

# and for /var/log/messages
setfacl --modify group:webdev:r /var/log/messages

# double check all entries, especially the mask
getfacl /var/log/messages

# adjust if required
setfacl --modify mask::r /var/log/messages

Da für /var/log/messages keine Standard-ACL gesetzt werden kann, nicht vergessen, die entsprechenden logrotate-Konfigurationsdateien anzupassen, damit der Zugriff nach dem Rotieren wieder erlaubt wird (siehe auch Advanced Logrotating):

/var/log/messages
{
    postrotate
        /usr/bin/setfacl --modify group:customer_dev:r /var/log/messages
    endscript
}

Weitere nützliche Kommandos:

getfacl filename.txt

# remove just one entry
setfacl --remove group:sales filename.txt

# remove all ACLs
setfacl --remove-all filename.txt

# find all files with active ACLs
getfacl --recursive --skip-base --absolute-names / | sed --quiet 's/^# file: //p'

Bemerkung

In RHEL 8+ sind ACLs nur bei Verzeichnissen und Dateien von Journald aktiv. Beispiel:

  • /run/log/journal/b1f9cb121bef4b4bae309d91b95b3257

  • /run/log/journal/b1f9cb121bef4b4bae309d91b95b3257/system.journal

  • /run/log/journal/b1f9cb121bef4b4bae309d91b95b3257/system@*.journal~

  • /run/log/journal/b1f9cb121bef4b4bae309d91b95b3257/system@0005f37eb1b747d2-d54e9076fb6d2125.journal~

  • /run/log/journal/60c72a5c2d9e4835a50576c0069ea0a0

  • /run/log/journal/60c72a5c2d9e4835a50576c0069ea0a0/system.journal

Troubleshooting

So setzt man Berechtigungen systemweit auf Dateien und Verzeichnisse:
# Directories: rwxr-xr-x (755)
find . -type d -exec chmod u=rwx,g=rx,o=rx "{}" \;

# Files: rw-r--r-- (644)
find . -type f -exec chmod u=rw,g=r,o=r "{}" \;
Was macht man, wenn man chmod die eigenen Ausführungsrechte über chmod -x chmod entzogen hat?

Dann „borgt“ man sich die Ausführungsrechte beispielsweise von ls:

cp --preserve /usr/bin/ls /tmp/chmod.tmp
cat /usr/bin/chmod > /tmp/chmod.tmp
cp --preserve /tmp/chmod.tmp /usr/bin/chmod

Built on 2024-10-08