Как настроить песочницу systemd через ProtectSystem
Разбираем работу директив ProtectSystem, PrivateTmp, DynamicUser и NoNewPrivileges. Узнайте, какие ресурсы блокирует каждая настройка, что может сломаться и как отладить unit-файл.
Что такое песочница systemd
Песочница systemd — это вторая часть написания unit-файла. Type= определяет, как запускается служба, а директивы вроде ProtectSystem= и PrivateTmp= определяют, к чему она может обращаться после запуска. Это функции ядра, пространства имен монтирования (mount namespaces) и фильтры seccomp, которые systemd применяет до того, как процесс получает управление. Приложение не «видит» этих ограничений и не требует внесения изменений в код.
По умолчанию ограничения отсутствуют. Юнит без настроек песочницы работает от имени root, может записывать данные в любое место, читать любой файл в системе и загружать модули ядра. Если этот юнит — веб-приложение, доступное из Интернета, одна уязвимость при загрузке файлов превращает систему в полностью скомпрометированный сервер. При использовании приведенного ниже юнита то же приложение получает файловую систему только для чтения, пустой /home, /tmp, который не видит ни один другой процесс, и отсутствие доступа к правам root, даже если приложение обнаружит setuid-бинарный файл.
Все эти настройки применяются к каждому юниту по отдельности. Усиление безопасности одной службы никак не влияет на соседние, поэтому начинайте с тех сервисов, которые слушают публичные порты.
Файл юнита целиком
notes — это небольшой веб-сервис. Он слушает localhost, хранит базу данных SQLite в /var/lib/notes и работает за nginx. Type=exec подходит, так как бинарный файл работает в foreground, а разница между Type=simple, exec, forking и notify определяет, как systemd отслеживает запуск. Каждая директива ниже объясняется далее, включая то, от чего она защищает и что обычно нарушает.
[Unit]
Description=Notes web service
After=network-online.target
Wants=network-online.target
[Service]
Type=exec
ExecStart=/opt/notes/bin/notes --listen 127.0.0.1:8080
DynamicUser=yes
StateDirectory=notes
Environment=NOTES_DB=/var/lib/notes/notes.db
ProtectSystem=strict
ProtectHome=yes
PrivateTmp=yes
PrivateDevices=yes
ProtectProc=invisible
NoNewPrivileges=yes
CapabilityBoundingSet=
AmbientCapabilities=
RestrictSUIDSGID=yes
ProtectKernelTunables=yes
ProtectKernelModules=yes
ProtectKernelLogs=yes
ProtectControlGroups=yes
RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6
RestrictNamespaces=yes
LockPersonality=yes
[Install]
WantedBy=multi-user.targetЗапишите его в /etc/systemd/system/notes.service, выполните sudo systemctl daemon-reload, затем sudo systemctl restart notes.service. Если юнит был установлен из пакета, не редактируйте файл поставщика. sudo systemctl edit notes.service открывает drop-in файл в /etc/systemd/system/notes.service.d/override.conf, который содержит только ваши дополнения [Service] и сохраняется при обновлении пакетов. systemctl cat notes.service выводит объединенный результат, начиная с файла поставщика.
От имени какого пользователя запускается сервис
Без User= сервис запускается с правами root, и все остальные директивы в этом случае служат лишь для минимизации ущерба. Существует два способа избежать этого.
Статический системный пользователь. Создайте его с помощью sudo useradd --system --no-create-home --shell /usr/sbin/nologin notes, а затем укажите User=notes в юните. UID остается неизменным после перезапусков и перезагрузок системы. Это важно, если сервис владеет файлами вне своего каталога состояния или если их необходимо считывать задачей резервного копирования. Обоснование предоставления каждому сервису собственной непривилегированной учетной записи применимо здесь в полной мере.
DynamicUser=yes. systemd выделяет UID из зарезервированного диапазона при запуске сервиса и освобождает его при остановке. Учетная запись не создается на диске, поэтому после удаления сервиса не остается никаких следов. Пока сервис работает, getent passwd notes разрешает имя через модуль NSS (name service switch) в systemd. После остановки имя перестает существовать.
DynamicUser=yes также автоматически активирует ряд других настроек: RemoveIPC=yes, NoNewPrivileges=yes, RestrictSUIDSGID=yes, ProtectSystem=strict, ProtectHome=read-only и приватный /tmp. В актуальных версиях systemd этот приватный /tmp представляет собой PrivateTmp=disconnected — отдельный tmpfs, если только вы не настроили PrivateTmp=yes самостоятельно, как это сделано в примере юнита. Это позволяет создать полноценную «песочницу» одной строкой, поэтому пример юнита остается лаконичным.
Что это может нарушить. Динамический UID не может владеть файлами в произвольных местах, так как этот номер переиспользуется между разными сервисами. Постоянные данные должны размещаться в StateDirectory=, CacheDirectory= или LogsDirectory=, которые systemd создает и назначает владельцем текущий UID при каждом запуске. При использовании DynamicUser=yes реальный каталог находится в /var/lib/private/notes, а /var/lib/notes является символической ссылкой, указывающей на него. /var/lib/private имеет права доступа 0700 и принадлежит root, поэтому при попытке резервного копирования от имени обычного пользователя возникнет Permission denied на пути, который выглядит вполне доступным для root. Любые другие сценарии, требующие фиксированного владельца, SSH-ключа, NFS-экспорта или файла, который должен читать другой сервис, требуют использования статического пользователя.
Структура файловой системы: ProtectSystem, ProtectHome, PrivateTmp
ProtectSystem= принимает три значения. yes монтирует /usr и загрузочные директории в режиме только для чтения. full добавляет /etc. strict монтирует всю иерархию в режиме только для чтения, за исключением директорий API ядра /dev, /proc и /sys, которые регулируются другими директивами. Начинайте с strict и открывайте доступ по мере необходимости, так как практика «сначала разрешить всё, а потом ограничить» никогда не работает.
Исключения задаются через ReadWritePaths=/srv/notes/uploads. Их требуется меньше, чем вы ожидаете: StateDirectory=, LogsDirectory=, CacheDirectory= и RuntimeDirectory= остаются доступными для записи при ProtectSystem=strict автоматически, поэтому в примере юнита вообще нет строки ReadWritePaths=. При strict директория /tmp также становится доступной только для чтения, если PrivateTmp=yes не предоставляет юниту собственную область для записи.
Что это ломает. Любая попытка записи вне указанных путей теперь завершается ошибкой Read-only file system. Это затрагивает приложения, которые сохраняют конфигурацию обратно в /etc, создают PID-файлы прямо в /run или распаковывают плагины в /opt. Прочитайте путь из сообщения об ошибке и добавьте именно этот путь, а не его родительскую директорию. Если путь, указанный в ReadWritePaths=, не существует, сервис не запустится (это не просто предупреждение), поэтому перед необязательными путями ставьте дефис: ReadWritePaths=-/srv/notes/uploads.
Режим только для чтения не означает скрытие. При ProtectSystem=strict сервис по-прежнему может читать /etc/passwd и любые общедоступные секреты, принадлежащие другим приложениям. InaccessiblePaths=/etc/ssh /srv/otherapp полностью удаляет поддерево из области видимости юнита. Для собственных секретов сервиса LoadCredential=dbpass:/etc/notes/dbpass копирует файл в директорию юнита, которую может читать только этот сервис, и приложение находит его по пути $CREDENTIALS_DIRECTORY.
ProtectHome=yes делает /home, /root и /run/user пустыми. Веб-сервису нечего делать в домашней директории, и это предотвращает использование уязвимостей обхода пути (path traversal) для доступа к /root/.ssh. read-only и tmpfs — более мягкие значения. Это ломает всё, чьи данные действительно хранятся в домашней директории, что касается многих приложений, установленных вручную в /home/app. Перенесите данные в /var/lib или установите ProtectHome=read-only, смирившись с меньшим уровнем защиты.
PrivateTmp=yes предоставляет сервису собственные /tmp и /var/tmp, которые создаются при запуске и удаляются при остановке. Это устраняет целый класс состояний гонки (race conditions) при работе с временными файлами между сервисами, а аварийное завершение больше не оставляет секреты в директории, которую может просмотреть любой пользователь системы.
Что это ломает. Всё, что использует /tmp как точку взаимодействия. Сервис, настроенный на подключение к MySQL через /tmp/mysql.sock, теперь сообщает об ошибке Can't connect to local MySQL server through socket '/tmp/mysql.sock', так как база данных создала сокет в /tmp хоста, а сервис ищет его внутри своего пространства. Укажите путь к 127.0.0.1 или реальный путь к сокету в /run. Тот же сюрприз ждет при отладке: файлы, которые сервис записывает в /tmp, не видны в /tmp вашей оболочки. Чтобы заглянуть внутрь, войдите в пространство имен монтирования (mount namespace) сервиса.
pid=$(systemctl show --property=MainPID --value notes.service)
sudo nsenter --target "$pid" --mount ls -l /tmpPrivateDevices=yes заменяет /dev небольшим набором псевдоустройств, таких как /dev/null, /dev/zero и /dev/urandom. Физические устройства просто отсутствуют. Узлы дисков, /dev/kvm, /dev/net/tun, звуковые карты и GPU исчезают, поэтому медиасервер, выполняющий аппаратное перекодирование, не может открыть /dev/dri/renderD128 и либо переключается на программную обработку, либо завершает работу. Если сервису действительно нужен узел устройства, отключите PrivateDevices= для этого юнита и укажите узел через DeviceAllow=/dev/dri/renderD128 rw, что всё равно гораздо безопаснее, чем доступ ко всем устройствам системы по умолчанию.
Чем может стать сервис: NoNewPrivileges и capabilities
NoNewPrivileges=yes устанавливает флаг процесса, который ядро никогда не сбрасывает. С этого момента процесс и все его дочерние процессы не могут получить привилегии через setuid-бинарные файлы или файловые capabilities. Это самая важная строка в юните, так как она останавливает большинство цепочек локального повышения привилегий на первом же этапе.
Что это ломает. Любой вызов sudo изнутри сервиса. Современный sudo проверяет этот флаг перед всеми остальными действиями и завершается с ошибкой The "no new privileges" flag is set, which prevents sudo from running as root.. Более старая ошибка effective uid is not 0 ... 'nosuid' option, которую можно встретить в других источниках, указывает на проблемы с монтированием или установкой, а не на результат действия этой директивы. Проверки паролей PAM (pluggable authentication modules), которые обращаются к unix_chkpwd, завершаются неудачей по той же причине, как и инструменты для запуска rootless-контейнеров, которым требуется newuidmap. Если ваш сервис зависит от чего-то подобного, лучше устраните зависимость, а не удаляйте директиву.
CapabilityBoundingSet= ограничивает набор capabilities, которые может иметь любой процесс в юните; пустое значение отключает их все. Для сервиса, который уже запущен от имени пользователя без прав root, это второй замок, а не первый, так как NoNewPrivileges=yes блокирует стандартный способ получения capabilities. Оставляйте обе настройки, так как они работают по-разному, и лучше иметь двойную защиту.
Единственная capability, которая часто нужна веб-сервису — это CAP_NET_BIND_SERVICE для использования портов ниже 1024. Процессу без прав root её нужно именно предоставить, а не просто разрешить, поэтому требуются обе строки.
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
AmbientCapabilities=CAP_NET_BIND_SERVICEsystemd применяет ambient capabilities до сброса привилегий, поэтому это работает совместно с NoNewPrivileges=yes. Без этих строк сервис запускается и сразу завершается с ошибкой, указывающей на порт, например listen tcp :443: bind: permission denied. В большинстве конфигураций VPS лучше привязать сервис к 127.0.0.1:8080 и позволить Nginx или Caddy использовать 443 порт, что позволит полностью исключить эту capability из юнита. Знак ~ в начале списка инвертирует его, поэтому CapabilityBoundingSet=~CAP_SYS_ADMIN блокирует указанную capability и разрешает все остальные. Используйте разрешающий список: запрещающие списки быстро устаревают, так как постоянно добавляются новые capabilities.
Что открывает ядро
ProtectKernelTunables=yes делает /proc/sys, /sys и доступные для записи файлы внутри них доступными только для чтения для данного юнита. Это приводит к сбою любого сервиса, который пытается изменить параметры sysctl во время запуска: стартовый скрипт выводит sysctl: setting key "vm.max_map_count": Read-only file system и завершает работу. Укажите значение в /etc/sysctl.d/ — это правильное место, где настройки сохраняются после перезагрузки, после чего директиву можно оставить включенной.
ProtectKernelModules=yes блокирует загрузку модулей ядра. Юнит, который запускает modprobe, получит modprobe: ERROR: could not insert 'nf_conntrack': Operation not permitted. Загружайте модули при старте системы через /etc/modules-load.d/, а не средствами самого сервиса.
ProtectKernelLogs=yes отключает dmesg. ProtectControlGroups=yes переводит /sys/fs/cgroup в режим только для чтения, что немедленно заметят среды выполнения контейнеров и любое ПО, управляющее собственными cgroups. ProtectProc=invisible скрывает процессы других пользователей в /proc, поэтому скомпрометированный сервис не сможет прочитать командную строку другого демона и пароли, переданные в аргументах. Эту опцию нужно отключать только для агентов мониторинга, которые сканируют /proc.
RestrictNamespaces=yes запрещает сервису создавать новые пространства имен (namespaces), что необходимо для работы сред выполнения контейнеров и часто используется злоумышленниками для выхода из контейнера. LockPersonality=yes блокирует изменение домена выполнения ядра, а RestrictSUIDSGID=yes запрещает сервису создавать setuid-файлы. Обе эти настройки не требуют ресурсов и редко нарушают работу обычных приложений.
С чем может взаимодействовать сервис
RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6 разрешает использование локальных сокетов, а также IPv4 и IPv6, при этом заставляя socket() возвращать ошибку EAFNOSUPPORT для всех остальных типов соединений. Это фильтр seccomp, поэтому он работает на архитектурах x86-64 и arm64, что охватывает любой современный VPS.
Что это нарушает. AF_NETLINK, гораздо чаще, чем ожидают пользователи. Функция getifaddrs() в glibc открывает netlink-сокет и не имеет альтернативных механизмов работы, net.Interfaces() в Go считывает тот же дамп netlink напрямую, а .NET выполняет перечисление интерфейсов через getifaddrs(). В результате сервис, которому требовалось лишь узнать собственный IP-адрес, завершается с ошибкой OSError: [Errno 97] Address family not supported by protocol или её аналогом в соответствующем языке программирования. Java является исключением: java.net.NetworkInterface в OpenJDK на Linux использует ioctl для IPv4 и считывает /proc/net/if_inet6 для IPv6, поэтому продолжает работать без AF_NETLINK. В случае возникновения ошибки список становится AF_UNIX AF_INET AF_INET6 AF_NETLINK, что всё равно значительно уже, чем настройки по умолчанию. AF_PACKET требуется для захвата необработанных пакетов (raw packet capture), и почти никаким другим процессам этот доступ не нужен.
IPAddressDeny=any вместе с IPAddressAllow=localhost представляют собой другой механизм: BPF-фильтр, привязанный к cgroup юнита. Он действует на уровне юнита и невидим для nft list ruleset, что делает его подходящим для сервиса, который должен обращаться только к базе данных на том же хосте, но может сбить с толку того, кто будет отлаживать систему в будущем. В ядре без поддержки cgroup BPF systemd записывает в лог сообщение о том, что юнит настраивает IP-файрвол, в то время как локальная система не поддерживает BPF/cgroup firewalling, и правила молча игнорируются. Поэтому всегда проверяйте журнал, а не полагайтесь на предположения.
Почему перестает запускаться защищенный юнит?
Любая из приведенных выше директив может привести к сбою работающего сервиса, причем ошибка часто не указывает на вызвавшую её директиву. Алгоритм действий всегда один: изучите журнал, отключите ровно одну директиву, повторите тест.
sudo systemctl restart notes.service
systemctl status notes.service
sudo journalctl -u notes.service -n 50 --no-pagerСтроки, описывающие песочницу, выглядят так:
notes.service: Failed to set up mount namespacing: No such file or directory
notes.service: Failed at step NAMESPACE spawning /opt/notes/bin/notes: No such file or directory
notes.service: Main process exited, code=exited, status=226/NAMESPACE226/NAMESPACE означает, что systemd не смог построить представление файловой системы, поэтому ваш бинарный файл даже не был запущен. Чаще всего причина кроется в пути в ReadWritePaths=, BindPaths= или InaccessiblePaths=, который не существует. 228/SECCOMP указывает на ошибку применения SystemCallFilter= или SystemCallArchitectures=. code=killed, status=31/SYS — это другой случай: процесс запустился, но выполнил системный вызов, который был заблокирован фильтром, после чего ядро завершило его работу. Держите открытой справочную таблицу кодов завершения systemd для юнитов, которые не запускаются, пока работаете над этим, так как код ошибки — самый быстрый способ отличить проблему пространств имен от ошибки самого приложения.
Отключайте директивы по одной через drop-in файл, чтобы результат был информативным. Выполните sudo systemctl edit notes.service и добавьте в него одну строку:
[Service]
ProtectSystem=fullПерезапустите сервис. Если он запустился, вы поймете, какую директиву нужно уточнить, а не удалять. Верните её значение к strict, добавьте строку ReadWritePaths= для пути, который действительно необходим приложению, и снова перезапустите сервис. Удаление всего блока из-за того, что сервис не запускается — это путь к юниту без защиты и с комментариями, которые никто не помнит.
Чтобы протестировать песочницу без участия сервиса, запустите в ней оболочку:
sudo systemd-run --pty -p ProtectSystem=strict -p ProtectHome=yes -p PrivateTmp=yes /bin/bashВнутри этой оболочки touch /etc/test вернет Read-only file system, а ls /home ничего не покажет. Это самый быстрый способ узнать, что увидит приложение, так как вы можете запустить его команды вручную и увидеть, какая из них завершится с ошибкой.
Один из режимов сбоя не выдает вообще никаких ошибок. Опечатка в директиве — это лишь предупреждение, и сервис запускается без неё:
/etc/systemd/system/notes.service:14: Unknown key name 'ProtectSytem' in section 'Service', ignoring.Юнит работает, песочница отсутствует, и никаких дальнейших жалоб нет. Две команды позволяют это выявить. sudo systemd-analyze verify /etc/systemd/system/notes.service выводит то же предупреждение по запросу, а systemctl show показывает, что именно получил запущенный сервис:
systemctl show notes.service -p User -p ProtectSystem -p PrivateTmp -p NoNewPrivilegesЕсли команда возвращает ProtectSystem=no, хотя вы написали strict, значит, юнит загрузил не то, что вы ожидали.
Используйте systemd-analyze security как чек-лист
sudo systemd-analyze security notes.service выводит все настройки песочницы, которые может использовать юнит, текущее состояние каждой из них для данного юнита и оценку по каждой строке. Запустите команду без аргументов, чтобы вывести список всех загруженных сервисов на сервере, или добавьте --offline=true с путем к файлу юнита, чтобы проверить его перед установкой.
Воспринимайте вывод как список задач. Пройдитесь по строкам, отмеченным как проблемные, и ответьте на один вопрос для каждой: нужен ли сервису этот доступ? В большинстве случаев ответ — нет, и вы просто добавляете соответствующую строку. Иногда ответ — да. Медиа-серверу нужен доступ к узлу устройства. Агенту резервного копирования нужно читать /home. Эти строки останутся помеченными навсегда, и это правильный результат, а не ошибка.
Итоговая цифра в конце — это агрегированный показатель всех строк выше. Инструмент не знает, что делает ваш сервис, какие данные он хранит и есть ли в приложении уязвимости. Юнит может соответствовать всем критериям проверки и всё равно оставаться самым слабым звеном на сервере, так как инструмент измеряет настройки юнита, а не защищенность самого программного обеспечения. Самохостинг менеджера паролей наглядно демонстрирует этот разрыв: юнит Vaultwarden может пройти все проверки, в то время как его токен администратора и файл резервной копии по-прежнему определяют безопасность хранилища. Погоня за цифрой заставляет людей вставлять директивы, которые они не понимают, а именно они чаще всего ломаются после обновления пакетов, когда уже никто не может объяснить, зачем эта строка была добавлена.
Где заканчивается песочница
Эти директивы определяют, к каким ресурсам может обращаться сервис. Они не ограничивают потребление ресурсов, поэтому полностью изолированный юнит всё равно может занять все ядра процессора и всю оперативную память сервера. Это регулируется отдельным набором настроек, описанным в руководстве по лимитам CPU и памяти для systemd-сервисов.
Они также не заменяют мандатное управление доступом. Пространство имён (namespace) создаётся для каждого юнита отдельно тем, кто пишет файл юнита, в то время как SELinux применяет единую политику ко всей системе. Эти механизмы работают совместно, и наличие одного не отменяет необходимости в другом.
Наконец, данные настройки распространяются только на процессы, которые systemd запускает внутри этого юнита. Если сервис передаёт задачи вспомогательному демону через сокет, этот демон не оказывается в песочнице. Добавьте аналогичный блок в файл юнита этого демона и проверьте результат с помощью systemctl show, вместо того чтобы полагаться только на содержимое файла.
FAQ
Что именно делает доступным только для чтения параметр ProtectSystem=strict?
Всю иерархию файловой системы, за исключением /dev, /proc и /sys, которые регулируются параметрами PrivateDevices=, ProtectKernelTunables= и ProtectControlGroups=. Это включает в себя /etc, /var, /srv, /opt и /tmp. Исключения, которые systemd добавляет автоматически — это директории, которыми он управляет: StateDirectory=, CacheDirectory=, LogsDirectory= и RuntimeDirectory=. Для всего остального, куда сервису требуется запись, необходимо явное указание ReadWritePaths=. Обратите внимание: «только для чтения» не означает «недоступно для чтения», поэтому секретный файл в другом месте системы останется открытым для сервиса, если вы не ограничите его через InaccessiblePaths=.
Почему мой сервис завершается с ошибкой status=226/NAMESPACE?
systemd не смог создать пространство имен монтирования (mount namespace), поэтому исполняемый файл не был запущен. Строка в журнале перед этим обычно содержит Failed to set up mount namespacing: No such file or directory. Почти всегда причина в том, что путь, указанный в ReadWritePaths=, BindPaths= или InaccessiblePaths=, отсутствует на диске. Создайте директорию или добавьте дефис перед путем (ReadWritePaths=-/srv/notes/uploads), чтобы systemd пропускал его при отсутствии. Если путь существует, проверьте конфигурацию на наличие опечаток и подтвердите её через systemctl cat notes.service, так как drop-in файл может добавлять строку, которую вы не видите.
Может ли сервис с PrivateTmp обмениваться файлами через /tmp?
Нет, в этом и заключается смысл изоляции. Сервис получает собственные /tmp и /var/tmp, которые существуют только во время его работы, поэтому сокет или файл, созданный другим процессом в системном /tmp, для него невидимы. Чаще всего от этого страдает сокет базы данных в /tmp/mysql.sock; решение — подключение через 127.0.0.1 или указание клиенту пути к реальному сокету в /run. Чтобы изучить временные файлы сервиса, узнайте его основной PID через systemctl show --property=MainPID --value и войдите в его пространство имен монтирования с помощью sudo nsenter --target <pid> --mount.
Как изолированный сервис может слушать порт 443 без прав root?
Предоставьте одну конкретную возможность (capability) вместо прав суперпользователя. Установите AmbientCapabilities=CAP_NET_BIND_SERVICE вместе с CapabilityBoundingSet=CAP_NET_BIND_SERVICE, сохраните User= или DynamicUser=yes, и процесс сможет привязываться к низким портам, не имея других привилегий. Распространенная ошибка — настройка только ограничивающего набора (bounding set): в этом случае возможность разрешена, но не предоставлена, и сервис завершается с ошибкой доступа к порту. Если на VPS уже работает reverse proxy, более чистое решение — привязать сервис к 127.0.0.1:8080, а порт 443 оставить за Nginx, тогда юниту вообще не потребуются дополнительные возможности.
Стоит ли использовать DynamicUser вместо создания системного пользователя?
Используйте его, если сервис хранит все свои данные внутри StateDirectory=, CacheDirectory= или LogsDirectory=, что подходит для большинства небольших веб-приложений. Вы получаете UID, который существует только во время работы сервиса, а также автоматически включенные PrivateTmp=, ProtectSystem=strict, ProtectHome=read-only, NoNewPrivileges=, RestrictSUIDSGID= и RemoveIPC=. Используйте статического системного пользователя, если UID должен оставаться неизменным: например, при работе с файлами вне указанных директорий, SSH-ключами, NFS-монтированием или если данные читает второй процесс. Помните, что при DynamicUser=yes данные фактически находятся в /var/lib/private/notes, а эта директория имеет права 0700 и принадлежит root, из-за чего резервное копирование от имени обычного пользователя может завершаться ошибкой.