borgmatic run as a system service should not use /run/user/$UID #934
Labels
No labels
blocked
breaking
bug
data loss
design finalized
good first issue
new feature area
question / support
security
waiting for response
No milestone
No assignees
4 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
borgmatic-collective/borgmatic#934
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Why this could be a problem:
/run/user/$UIDis a tmpfs created by pam_systemd, meaning, if the user does not have any running sessions, it will not exist. Therefore, for example, it isn't possible to make this directory writable if usingProtectSystem=strict.Suggested solution
Add
RuntimeDirectory=borgmaticto the system service unit. This will make systemd create/run/borgmatic, make it writable (even withProtectSystem=strict), and setRUNTIME_DIRECTORYto that path.In borgmatic, check if the
RUNTIME_DIRECTORYenvironment variable is set, and if yes use that directory.Note: systemd will wipe any RuntimeDirectories when the unit finishes running, but I don't think this is a problem. See also the docs.
I ran into the same issue. It seems to have been introduced in borgmatic 1.9.0 through #562. The documentation for the new
user_runtime_directoryconfiguration setting describes the behaviour:When running from a systemd unit, none of these variables are set, so borgmatic falls back to
/run/user/$UID. I think this is the actual bug: ifXDG_RUNTIME_DIRdoes not point at this directory explicitly, borgmatic should not be trying to use or create it. Instead, borgmatic should fall back to/tmp, which is acceptable in POSIX. However, if$XDG_RUNTIME_DIRis set, there should be no harm in using it.@SimonPilkington's idea of adding
$RUNTIME_DIRECTORYto support systemd'sRuntimeDirectory=is a good one. I suspect most borgmatic users will be using systemd nowadays, and if not, it does little harm.So I think the order should be:
$XDG_RUNTIME_DIR,$RUNTIME_DIRECTORY,$TMPDIR,$TEMP,/tmp.Workaround: tell systemd to create a runtime directory, then point
TMPDIRat it:You can also just set
Environment="TMPDIR=/tmp"in the systemd unit, oruser_runtime_directory: /tmpin the borgmatic config, but then systemd won't clean up the directory automatically.Use cases
I'm going to co-opt this ticket to take into account both the use cases outlined here and the use cases from #928. So unless I'm missing any, here they are.
Running borgmatic as:
XDG_RUNTIME_DIRis set and/run/user/$UIDexists (specific to that user).ProtectSystem=strict.XDG_RUNTIME_DIRisn't set and/run/user/$UIDdoesn't exist. ButRUNTIME_DIRECTORYcould be set ifRuntimeDirectoryis configured in the systemd service. So ifRuntimeDircetory=borgmatic, thenRUNTIME_DIRECTORYwill be/run/borgmatic.TMPDIRis set to an existent temporary directory specific to that user, something like/var/folders/g7/7du81ti_b7mm84n184fn3k910000lg/T/.TMPDIRisn't set, but runninggetconf DARWIN_USER_TEMP_DIRmay get a directory like/var/folders/zz/zyxvpxvq6csfxvn_n0000000000000/T/. But there's also maybe convention to using a path like~/Library/Caches/TemporaryItems/$appname/$version?TMPDIRmay be set (but to a global directory like/tmp)?TEMPmay be set?Some additional considerations:
/tmpare not safe to write to without using something likemktempor Python'stempfile. User-specific temp directories should be safe to write to with or without using that./borgmaticto all user runtime directories. The rationale is that, this way, runtime files like database dumps end up stored in the Borg archive at a consistent path,/borgmatic. (borgmatic uses the Borg 1.4+ path stripping feature so only that/borgmatic/...portion of the path gets stored. That is, unless you're using Borg 1.2 or below.)Potential solutions
There is a Python platformdirs module that handles a lot of this, but it doesn't necessarily handle all of it and isn't customizable if needed. Additionally, it would introduce new Python dependencies to borgmatic.
So here's what I propose, riffing on @thomastc's ideas above. When constructing its runtime directory, borgmatic could probe for and use each of these in turn until finding one that is set:
user_runtime_directoryoption.XDG_RUNTIME_DIRRUNTIME_DIRECTORYTMPDIR+ random string (via Python'stempfile)TEMP+ random string"/tmp"+ random stringThere could be the additional optimization that if the path already ends in
/borgmatic(e.g. ifRUNTIME_DIRECTORYis already/run/borgmatic), then don't tack another/borgmaticonto the end of it.I think this handles all of the use cases:
XDG_RUNTIME_DIR.RUNTIME_DIRECTORY(assumingRuntimeDirectoryis configured in the systemd service).TMPDIR+ random string. The random string isn't strictly necessary for this use case, but see OpenBSD below./tmp+ random string. And I guess/tmpis a link to/private/tmp/?TMPDIR+ random string or/tmp+ random string. The random string is needed in the first case becauseTMPDIRcan just be the global/tmp.TEMP+ random string.Again,
/borgmaticimplicitly gets tacked onto all of these. And with this particular approach, everything is done without having to perform platform probing or run external executables or probe for paths on the filesystems. (Although I guess doing the random string thing withtempfiledoes hit the filesystem.)Phew.
Okay, what am I missing?
I think that covers everything.
I only speak for the Linux platform but
XDG_RUNTIME_DIRfor users andRUNTIME_DIRECTORYfor system unless overridden by borgmatic config LGTM. Falling back to/tmpif nothing is set also LGTM. If borgmatic is creating a subdirectory it should have mode0700to be XDG-compliant.However, now that I've seen mention of database dumps (which I don't use) I'm wondering if
XDG_RUNTIME_DIRis actually suitable.The spec says:
And a database dump seems like it could potentially get pretty large. (For reference on my system
XDG_RUNTIME_DIRis a ~6GiB tmpfs.)I believe an
endswith('/borgmatic')check should be okay for this. A directory called borgmatic is probably intended to be used by borgmatic.Understood. It's certainly the case for anything created by
tempfile.Good point. However in the common case, borgmatic doesn't put database dumps there; it puts named pipes there for streaming database dumps directly to Borg. And on this particular page about XDG, the description of
XDG_RUNTIME_DIRsays it's for (among other things) "non-essential, user-specific data files such as sockets, named pipes, etc."The one exception I can think of is for "directory" format databases, which by their nature can't be streamed directly to Borg and have to be dumped to disk. But most users don't use that format.
Oh I see, that's an implementation detail I didn't know. Pardon my nonsense then, pipes absolutely belong in
XDG_RUNTIME_DIR.I suppose there is only the caveat of an increased chance of eating an
ENOSPCwith the directory format compared to dumping to$HOME/.borgmatic. If you wish to mitigate this and be XDG-compliant I believeXDG_CACHE_HOMEwould be the correct choice.Thanks, I'll consider that as part of this. But it may not be worth writing to different places for different dump formats given the additional complexity. And a user can always set their own "runtime" directory via the borgmatic configuration file option if they do run out of space.
Implemented in main, and will part of the next release. The updated documentation should be online here shortly: https://torsion.org/borgmatic/docs/how-to/backup-your-databases/#runtime-directory
Thanks to everyone for weighing in on this one!
Thank you for the amazing response time on this!
Released in borgmatic 1.9.2!