Skip to main content

Extended Attributes and Filesystem Attributes

Extended attributes (xattrs) and filesystem attribute flags add control beyond standard Unix permissions and ACLs. On an Ubuntu WordPress server, they are most useful for locking down high-value files (like wp-config.php), preventing unexpected changes during incidents, and preserving metadata across backups and deploys. This page explains the mental model and shows safe, reversible commands for lsattr/chattr, getfattr/setfattr, and getcap/setcap.

Prerequisites

  • Ubuntu server access (root or sudo).
  • A filesystem and mount options that support xattrs and attribute flags (ext4 does on modern Ubuntu).
  • Packages:
install-attr-and-capability-tools.sh
sudo apt update
sudo apt install -y attr libcap2-bin rsync tar
  • Know your WordPress document root (example: /var/www/example.com/public_html).

Core Concepts

LayerWhat it controlsTools
PermissionsRead/write/execute for owner, group, and otherschmod, chown, chgrp
ACLsPer-user and per-group access rulesgetfacl, setfacl
Filesystem attributesFilesystem-enforced flags like immutable and append-onlylsattr, chattr
Extended attributes (xattrs)Key-value metadata attached to filesgetfattr, setfattr
CapabilitiesFine-grained privileges stored in security.capabilitygetcap, setcap
Mental Model

Permissions decide access, ACLs refine it, filesystem attributes can block changes until the flag is removed, and xattrs/capabilities attach metadata or narrowly scoped privileges.

Filesystem Attributes (lsattr, chattr)

lsattr: list attributes

Syntax

lsattr-syntax.txt
lsattr [OPTIONS] [files...]

Common options

OptionMeaning
-RRecurse into directories
-aInclude hidden files
-dList the directory itself, not its contents
-lShow long attribute names (where supported)
-pShow project number (if enabled)
-vShow file version/generation (if enabled)

Example

lsattr-check-wp-config.sh
lsattr /var/www/example.com/public_html/wp-config.php

Example output

lsattr-wp-config-output.txt
----i--------e----- /var/www/example.com/public_html/wp-config.php

The i flag indicates the file is immutable.

chattr: change attributes

Syntax

chattr-syntax.txt
chattr [+|-|=][FLAGS] [files...]

Common flags (ext4)

FlagMeaning
iImmutable (cannot modify, delete, rename, or link until removed)
aAppend-only (file can only be opened for appending)
ADo not update atime
SSynchronous updates
dExclude from dump
eExtents (usually default)
jData journaling
tNo tail-merging
uAllow undelete (historical; limited usefulness)
E, I, P, TImplementation/project-specific flags (rarely used on WP hosts)

Options

OptionMeaning
-RRecurse into directories
-VVerbose

Examples

chattr-make-wp-config-immutable.sh
sudo chattr +i /var/www/example.com/public_html/wp-config.php
chattr-freeze-theme-directory.sh
sudo chattr -R +i /var/www/example.com/public_html/wp-content/themes/my-theme
chattr-unfreeze-theme-directory.sh
sudo chattr -R -i /var/www/example.com/public_html/wp-content/themes/my-theme
Deployment Workflow

For updates and deploys, remove +i, perform the change, validate, then re-apply +i.

Extended Attributes (xattrs)

getfattr: read xattrs

Syntax

getfattr-syntax.txt
getfattr [OPTIONS] PATH...

Key options

OptionMeaning
-n <name>Show only a specific attribute
-dDump attribute names and values
-m <regex>Filter attribute names (default matches user..*)
--only-valuesPrint only the value
-e <enc>Value encoding: text or hex
-RRecurse into directories
-hAct on the symlink itself
-LFollow symlinks
-PDo not follow symlinks

Examples

getfattr-dump-wp-config.sh
getfattr -d /var/www/example.com/public_html/wp-config.php

Expected (if none set):

getfattr-empty-output.txt
# file: /var/www/example.com/public_html/wp-config.php
getfattr-read-specific-attr.sh
getfattr -n user.comment /path/to/file
getfattr-print-only-values.sh
getfattr --only-values -n user.comment /path/to/file

setfattr: write/remove xattrs

Syntax

setfattr-syntax.txt
setfattr [OPTIONS] PATH...

Key options

OptionMeaning
-n <name>Attribute name (example: user.comment)
-v <value>Attribute value
-x <name>Remove the attribute
-RRecurse into directories
-hAct on the symlink itself
-L / -PFollow / do not follow symlinks

Examples

setfattr-tag-theme-directory.sh
sudo setfattr -n user.comment -v "Frozen after audit 2025-10-31"
/var/www/example.com/public_html/wp-content/themes/my-theme
setfattr-remove-theme-tag.sh
sudo setfattr -x user.comment
/var/www/example.com/public_html/wp-content/themes/my-theme
setfattr-tag-plugins-with-release.sh
sudo setfattr -R -n user.release -v "2025.10.31"
/var/www/example.com/public_html/wp-content/plugins

Namespaces you will see

NamespaceTypical use
user.*Admin scripts and operational metadata
security.*Security frameworks and capabilities
system.*Kernel and low-level uses
trusted.*Admin-only metadata (often requires CAP_SYS_ADMIN)

Linux Capabilities (stored as xattr)

setcap / getcap

Syntax

setcap-and-getcap-syntax.txt
sudo setcap <caps> <path-to-binary>
getcap -r <dir>

Capabilities are stored in the security.capability extended attribute.

Examples (use only when you understand the impact)

Allow a non-root binary to bind to ports < 1024:

setcap-net-bind-service.sh
sudo setcap 'cap_net_bind_service=+ep' /usr/sbin/myproxy
getcap /usr/sbin/myproxy

Expected:

getcap-output.txt
/usr/sbin/myproxy = cap_net_bind_service+ep

Remove capabilities:

remove-capabilities.sh
sudo setcap -r /usr/sbin/myproxy
warning

Avoid setting capabilities on core web stack binaries (PHP-FPM, OpenLiteSpeed, Apache, Nginx) unless you have a specific, audited requirement.

Preserve Metadata in Copies and Backups

cp

cp-preserve-xattrs.sh
cp --preserve=xattr -a SRC DEST

rsync

rsync-preserve-acls-and-xattrs.sh
rsync -aAXH SRC/ DEST/

tar

tar-backup-with-xattrs-and-acls.sh
tar --xattrs --acls -cpf backup.tar /var/www/example.com
tar-restore-with-xattrs-and-acls.sh
tar --xattrs --acls -xpf backup.tar -C /
Immutable Files During Restore

If you restore over files or directories marked immutable, remove the flag first (sudo chattr -R -i PATH), restore, then re-apply the flag.

WordPress-Focused Hardening Patterns

Lock down wp-config.php (and .htaccess if used)

lock-wp-config-and-htaccess.sh
sudo chattr +i /var/www/example.com/public_html/wp-config.php
sudo chattr +i /var/www/example.com/public_html/.htaccess

Freeze theme/plugin code for production

freeze-themes-and-plugins.sh
sudo chattr -R +i /var/www/example.com/public_html/wp-content/themes/my-theme
sudo chattr -R +i /var/www/example.com/public_html/wp-content/plugins/critical-plugin

Append-only on a log file (when applicable)

make-access-log-append-only.sh
sudo chattr +a /path/to/webroot/logs/access.log

Provenance tags via xattrs

Tag a deploy:

tag-docroot-with-release-xattr.sh
sudo setfattr -R -n user.release -v "2025.10.31" /var/www/example.com/public_html

Audit:

audit-release-xattr-recursively.sh
getfattr -R -m 'user.release' -d /var/www/example.com/public_html | less

Production Workflow (safe and reversible)

warning

chattr -R can lock large trees and break updates and restores if you forget to remove +i. Apply it narrowly and keep a rollback command ready.

  1. Survey current state.
survey-attributes-and-xattrs.sh
lsattr -R /var/www/example.com/public_html | grep -E ' i | a ' || true
getfattr -R -m 'user..*' -d /var/www/example.com/public_html | less
  1. Baseline locks.
lock-baseline-config-files.sh
sudo chattr +i /var/www/example.com/public_html/wp-config.php
test -f /var/www/example.com/public_html/.htaccess && sudo chattr +i /var/www/example.com/public_html/.htaccess || true
  1. Freeze code; keep uploads writable.
freeze-code-keep-uploads-writable.sh
sudo chattr -R +i /var/www/example.com/public_html/wp-content/themes
sudo chattr -R +i /var/www/example.com/public_html/wp-content/plugins
sudo chattr -R -i /var/www/example.com/public_html/wp-content/uploads
  1. Tag the release.
tag-release-xattr.sh
sudo setfattr -R -n user.release -v "2025.10.31" /var/www/example.com/public_html
  1. Backups with metadata.
backup-with-metadata-rsync.sh
sudo chattr -R -i /var/www/example.com/public_html
sudo rsync -aAXH /var/www/example.com/public_html/ /backups/example.com_2025-10-31/

# Re-apply only the locks you actually want
sudo chattr +i /var/www/example.com/public_html/wp-config.php
test -f /var/www/example.com/public_html/.htaccess && sudo chattr +i /var/www/example.com/public_html/.htaccess || true
sudo chattr -R +i /var/www/example.com/public_html/wp-content/themes
sudo chattr -R +i /var/www/example.com/public_html/wp-content/plugins
sudo chattr -R -i /var/www/example.com/public_html/wp-content/uploads
  1. Document and monitor.
snapshot-lsattr-output.sh
lsattr -R /var/www/example.com/public_html > /root/example.lsattr.txt
detect-attribute-drift.sh
diff -u /root/example.lsattr.txt <(lsattr -R /var/www/example.com/public_html) || echo "Attribute drift detected"
note

The <( ... ) process substitution syntax requires bash.

Bulk Operations with find/xargs (and rollback)

Apply immutable to all .php under wp-includes/:

bulk-freeze-wp-includes-php.sh
find /var/www/example.com/public_html/wp-includes -type f -name '*.php' -print0
| sudo xargs -0 chattr +i

Remove immutable (rollback):

bulk-unfreeze-wp-includes-php.sh
find /var/www/example.com/public_html/wp-includes -type f -name '*.php' -print0
| sudo xargs -0 chattr -i

Add an xattr marker to all plugin files:

bulk-tag-plugin-files-with-release-xattr.sh
find /var/www/example.com/public_html/wp-content/plugins -type f -print0
| sudo xargs -0 -I{} setfattr -n user.release -v "2025.10.31" {}
tip

Use -print0 with xargs -0 to safely handle paths containing spaces and newlines.

Best Practices

  • Keep immutable scope minimal (config and code you truly want frozen).
  • Standardize deploy steps: remove +i -> deploy -> test -> re-apply +i.
  • Preserve metadata in backups (rsync -aAXH, tar --xattrs --acls).
  • Avoid capabilities unless you have a specific, justified need.
  • Snapshot attribute state (lsattr -R) and periodically check drift.

Troubleshooting Matrix

SymptomProbable causeCheckFix
Permission denied when editing wp-config.php (even as root)+i immutable flaglsattr wp-config.php shows isudo chattr -i wp-config.php, edit, then re-apply sudo chattr +i wp-config.php
Plugin/theme not updatingParent dir or files are immutablelsattr -R wp-content/pluginsTemporarily remove -i on the target, update, then re-apply +i
Backup/restore misses metadataBackup tool not preserving xattrs/ACLsReview backup commandUse rsync -aAXH or tar --xattrs --acls
setfattr fails with Operation not supportedFilesystem/mount options do not support xattrsCheck filesystem type and mount optionsUse a filesystem and mount options that support xattrs
Capabilities do not persistBinary is on a filesystem that does not support xattrs/capsgetcap <bin> returns nothing after setcapMove binary to a suitable filesystem and retry
Quick Lab (Hands-On, Safe)
  1. Create a sandbox file and make it immutable.
lab-create-and-lock-demo-file.sh
cd /tmp
echo "hello" > demo.txt
sudo chattr +i demo.txt
echo "world" >> demo.txt # should fail
  1. Inspect the attribute flags.
lab-inspect-demo-file.sh
lsattr demo.txt
  1. Remove the flag, edit, and add an xattr.
lab-edit-and-tag-demo-file.sh
sudo chattr -i demo.txt
echo "world" >> demo.txt
sudo setfattr -n user.comment -v "lab-note" demo.txt
getfattr -d demo.txt
  1. Clean up.
lab-cleanup-demo-file.sh
rm demo.txt

Expected highlights:

  • Append fails when +i is set.
  • getfattr -d shows user.comment="lab-note".
Cheat Sheet (Copy-Ready)
  • List attributes: lsattr [-R] [-adlpv] PATH
  • Make immutable: sudo chattr +i FILE|DIR
  • Remove immutable: sudo chattr -i FILE|DIR
  • Make append-only: sudo chattr +a FILE
  • List xattrs: getfattr -d [-m REGEX] PATH
  • Get one xattr: getfattr -n user.comment PATH
  • Set xattr: sudo setfattr -n user.comment -v "TEXT" PATH
  • Remove xattr: sudo setfattr -x user.comment PATH
  • Grant capability: sudo setcap 'cap_net_bind_service=+ep' /path/to/bin
  • List capabilities: getcap -r /
  • Rsync with metadata: rsync -aAXH SRC/ DEST/
  • Tar with xattrs/ACLs: tar --xattrs --acls -cpf backup.tar DIR
Mini-Quiz (Self-Check)
  1. What is the difference between xattrs and filesystem attributes?
  2. Why can root fail to edit an immutable file?
  3. Which rsync flags preserve xattrs and ACLs?
  4. What namespace do POSIX capabilities live in?
  5. What is a safe sequence to update a theme that is currently immutable?

Answers (brief):

  1. xattrs are key-value metadata; filesystem attributes are filesystem flags like immutable.
  2. The +i flag blocks modifications until it is removed.
  3. -X preserves xattrs and -A preserves ACLs (commonly -aAXH).
  4. security.capability.
  5. Remove -i -> update -> test -> re-apply +i.
Appendix: Advanced Notes
  • ext4 on Ubuntu typically has xattrs enabled by default; verify your filesystem and mount options if setfattr fails.
  • Ubuntu commonly uses AppArmor; SELinux labels are also xattrs (security.selinux) but are usually not active unless you enable SELinux.
  • Moving files across filesystems can drop xattrs/attributes; use tools that preserve them.
  • xattrs and attribute flags complement (not replace) a sound permissions and ACL strategy.

What's Next