1
0
mirror of https://github.com/systemd/systemd.git synced 2025-03-13 00:58:27 +03:00

user-record: add fields for setting limits on /tmp/ and /dev/shm/

This commit is contained in:
Lennart Poettering 2025-01-10 11:33:59 +01:00
parent d15811d7e5
commit 72b932aac0
4 changed files with 138 additions and 9 deletions

View File

@ -619,6 +619,19 @@ is allowed to edit.
`selfModifiablePrivileged` → Similar to `selfModifiableFields`, but it lists fields in
the `privileged` section that the user is allowed to edit.
`tmpLimit` → A numeric value encoding a disk quota limit in bytes enforced on
`/tmp/` on login, in case it is backed by volatile file system (such as
`tmpfs`).
`tmpLimitScale` → Similar, but encodes a relative value, normalized to
`UINT32_MAX` as 100%. This value is applied relative to the file system
size. If both `tmpLimit` and `tmpLimitScale` are set, the lower of the two
should be enforced. If neither field is set the implementation might apply a
default limit.
`devShmLimit`, `devShmLimitScale` → Similar to the previous two, but apply to
`/dev/shm/` rather than `/tmp/`.
`privileged` → An object, which contains the fields of the `privileged` section
of the user record, see below.
@ -761,22 +774,26 @@ These two are the only two fields specific to this section.
All other fields that may be used in this section are identical to the equally named ones in the
`regular` section (i.e. at the top-level object). Specifically, these are:
`blobDirectory`, `blobManifest`, `iconName`, `location`, `shell`, `umask`, `environment`, `timeZone`,
`preferredLanguage`, `additionalLanguages`, `niceLevel`, `resourceLimits`, `locked`, `notBeforeUSec`,
`notAfterUSec`, `storage`, `diskSize`, `diskSizeRelative`, `skeletonDirectory`,
`accessMode`, `tasksMax`, `memoryHigh`, `memoryMax`, `cpuWeight`, `ioWeight`,
`blobDirectory`, `blobManifest`, `iconName`, `location`, `shell`, `umask`,
`environment`, `timeZone`, `preferredLanguage`, `additionalLanguages`,
`niceLevel`, `resourceLimits`, `locked`, `notBeforeUSec`, `notAfterUSec`,
`storage`, `diskSize`, `diskSizeRelative`, `skeletonDirectory`, `accessMode`,
`tasksMax`, `memoryHigh`, `memoryMax`, `cpuWeight`, `ioWeight`,
`mountNoDevices`, `mountNoSuid`, `mountNoExecute`, `cifsDomain`,
`cifsUserName`, `cifsService`, `cifsExtraMountOptions`, `imagePath`, `uid`,
`gid`, `memberOf`, `fileSystemType`, `partitionUuid`, `luksUuid`,
`fileSystemUuid`, `luksDiscard`, `luksOfflineDiscard`, `luksCipher`,
`luksCipherMode`, `luksVolumeKeySize`, `luksPbkdfHashAlgorithm`,
`luksPbkdfType`, `luksPbkdfForceIterations`, `luksPbkdfTimeCostUSec`, `luksPbkdfMemoryCost`,
`luksPbkdfParallelThreads`, `luksSectorSize`, `autoResizeMode`, `rebalanceWeight`,
`rateLimitIntervalUSec`, `rateLimitBurst`, `enforcePasswordPolicy`,
`autoLogin`, `preferredSessionType`, `preferredSessionLauncher`, `stopDelayUSec`, `killProcesses`,
`luksPbkdfType`, `luksPbkdfForceIterations`, `luksPbkdfTimeCostUSec`,
`luksPbkdfMemoryCost`, `luksPbkdfParallelThreads`, `luksSectorSize`,
`autoResizeMode`, `rebalanceWeight`, `rateLimitIntervalUSec`, `rateLimitBurst`,
`enforcePasswordPolicy`, `autoLogin`, `preferredSessionType`,
`preferredSessionLauncher`, `stopDelayUSec`, `killProcesses`,
`passwordChangeMinUSec`, `passwordChangeMaxUSec`, `passwordChangeWarnUSec`,
`passwordChangeInactiveUSec`, `passwordChangeNow`, `pkcs11TokenUri`,
`fido2HmacCredential`, `selfModifiableFields`, `selfModifiableBlobs`, `selfModifiablePrivileged`.
`fido2HmacCredential`, `selfModifiableFields`, `selfModifiableBlobs`,
`selfModifiablePrivileged`, `tmpLimit`, `tmpLimitScale`, `devShmLimit`,
`devShmLimitScale`.
## Fields in the `binding` section

View File

@ -7,6 +7,7 @@
#include "hashmap.h"
#include "hexdecoct.h"
#include "path-util.h"
#include "percent-util.h"
#include "pretty-print.h"
#include "process-util.h"
#include "rlimit-util.h"
@ -54,6 +55,26 @@ static void show_self_modifiable(
printf("%13s %s\n", i == value ? heading : "", *i);
}
static void show_tmpfs_limit(const char *tmpfs, const TmpfsLimit *limit, uint32_t scale) {
assert(tmpfs);
assert(limit);
if (!limit->is_set)
return;
printf(" %s Limit:", tmpfs);
if (limit->limit != UINT64_MAX)
printf(" %s", FORMAT_BYTES(limit->limit));
if (limit->limit == UINT64_MAX || limit->limit_scale != UINT32_MAX) {
if (limit->limit != UINT64_MAX)
printf(" or");
printf(" %i%%", UINT32_SCALE_TO_PERCENT(scale));
}
printf("\n");
}
void user_record_show(UserRecord *hr, bool show_full_group_info) {
_cleanup_strv_free_ char **langs = NULL;
const char *hd, *ip, *shell;
@ -368,6 +389,9 @@ void user_record_show(UserRecord *hr, bool show_full_group_info) {
if (hr->io_weight != UINT64_MAX)
printf(" IO Weight: %" PRIu64 "\n", hr->io_weight);
show_tmpfs_limit("TMP", &hr->tmp_limit, user_record_tmp_limit_scale(hr));
show_tmpfs_limit("SHM", &hr->dev_shm_limit, user_record_dev_shm_limit_scale(hr));
if (hr->access_mode != MODE_INVALID)
printf(" Access Mode: 0%03o\n", user_record_access_mode(hr));

View File

@ -15,6 +15,7 @@
#include "locale-util.h"
#include "memory-util.h"
#include "path-util.h"
#include "percent-util.h"
#include "pkcs11-util.h"
#include "rlimit-util.h"
#include "sha256.h"
@ -95,6 +96,8 @@ UserRecord* user_record_new(void) {
.drop_caches = -1,
.auto_resize_mode = _AUTO_RESIZE_MODE_INVALID,
.rebalance_weight = REBALANCE_WEIGHT_UNSET,
.tmp_limit = TMPFS_LIMIT_NULL,
.dev_shm_limit = TMPFS_LIMIT_NULL,
};
return h;
@ -982,6 +985,40 @@ static int dispatch_rebalance_weight(const char *name, sd_json_variant *variant,
return 0;
}
static int dispatch_tmpfs_limit(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
TmpfsLimit *limit = ASSERT_PTR(userdata);
int r;
if (sd_json_variant_is_null(variant)) {
*limit = TMPFS_LIMIT_NULL;
return 0;
}
r = sd_json_dispatch_uint64(name, variant, flags, &limit->limit);
if (r < 0)
return r;
limit->is_set = true;
return 0;
}
static int dispatch_tmpfs_limit_scale(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
TmpfsLimit *limit = ASSERT_PTR(userdata);
int r;
if (sd_json_variant_is_null(variant)) {
*limit = TMPFS_LIMIT_NULL;
return 0;
}
r = sd_json_dispatch_uint32(name, variant, flags, &limit->limit_scale);
if (r < 0)
return r;
limit->is_set = true;
return 0;
}
static int dispatch_privileged(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
static const sd_json_dispatch_field privileged_dispatch_table[] = {
@ -1275,6 +1312,10 @@ static int dispatch_per_machine(const char *name, sd_json_variant *variant, sd_j
{ "selfModifiableFields", SD_JSON_VARIANT_ARRAY, sd_json_dispatch_strv, offsetof(UserRecord, self_modifiable_fields), SD_JSON_STRICT },
{ "selfModifiableBlobs", SD_JSON_VARIANT_ARRAY, sd_json_dispatch_strv, offsetof(UserRecord, self_modifiable_blobs), SD_JSON_STRICT },
{ "selfModifiablePrivileged", SD_JSON_VARIANT_ARRAY, sd_json_dispatch_strv, offsetof(UserRecord, self_modifiable_privileged), SD_JSON_STRICT },
{ "tmpLimit", _SD_JSON_VARIANT_TYPE_INVALID, dispatch_tmpfs_limit, offsetof(UserRecord, tmp_limit), 0, },
{ "tmpLimitScale", _SD_JSON_VARIANT_TYPE_INVALID, dispatch_tmpfs_limit_scale, offsetof(UserRecord, tmp_limit), 0, },
{ "devShmLimit", _SD_JSON_VARIANT_TYPE_INVALID, dispatch_tmpfs_limit, offsetof(UserRecord, dev_shm_limit), 0, },
{ "devShmLimitScale", _SD_JSON_VARIANT_TYPE_INVALID, dispatch_tmpfs_limit_scale, offsetof(UserRecord, dev_shm_limit), 0, },
{},
};
@ -1625,6 +1666,10 @@ int user_record_load(UserRecord *h, sd_json_variant *v, UserRecordLoadFlags load
{ "selfModifiableFields", SD_JSON_VARIANT_ARRAY, sd_json_dispatch_strv, offsetof(UserRecord, self_modifiable_fields), SD_JSON_STRICT },
{ "selfModifiableBlobs", SD_JSON_VARIANT_ARRAY, sd_json_dispatch_strv, offsetof(UserRecord, self_modifiable_blobs), SD_JSON_STRICT },
{ "selfModifiablePrivileged", SD_JSON_VARIANT_ARRAY, sd_json_dispatch_strv, offsetof(UserRecord, self_modifiable_privileged), SD_JSON_STRICT },
{ "tmpLimit", _SD_JSON_VARIANT_TYPE_INVALID, dispatch_tmpfs_limit, offsetof(UserRecord, tmp_limit), 0, },
{ "tmpLimitScale", _SD_JSON_VARIANT_TYPE_INVALID, dispatch_tmpfs_limit_scale, offsetof(UserRecord, tmp_limit), 0, },
{ "devShmLimit", _SD_JSON_VARIANT_TYPE_INVALID, dispatch_tmpfs_limit, offsetof(UserRecord, dev_shm_limit), 0, },
{ "devShmLimitScale", _SD_JSON_VARIANT_TYPE_INVALID, dispatch_tmpfs_limit_scale, offsetof(UserRecord, dev_shm_limit), 0, },
{ "secret", SD_JSON_VARIANT_OBJECT, dispatch_secret, 0, 0 },
{ "privileged", SD_JSON_VARIANT_OBJECT, dispatch_privileged, 0, 0 },
@ -2138,6 +2183,32 @@ int user_record_languages(UserRecord *h, char ***ret) {
return 0;
}
uint32_t user_record_tmp_limit_scale(UserRecord *h) {
assert(h);
if (h->tmp_limit.is_set)
return h->tmp_limit.limit_scale;
/* By default grant regular users only 80% quota */
if (user_record_disposition(h) == USER_REGULAR)
return UINT32_SCALE_FROM_PERCENT(80);
return UINT32_MAX;
}
uint32_t user_record_dev_shm_limit_scale(UserRecord *h) {
assert(h);
if (h->dev_shm_limit.is_set)
return h->dev_shm_limit.limit_scale;
/* By default grant regular users only 80% quota */
if (user_record_disposition(h) == USER_REGULAR)
return UINT32_SCALE_FROM_PERCENT(80);
return UINT32_MAX;
}
const char** user_record_self_modifiable_fields(UserRecord *h) {
/* As a rule of thumb: a setting is safe if it cannot be used by a
* user to give themselves some unfair advantage over other users on

View File

@ -230,6 +230,19 @@ typedef enum AutoResizeMode {
#define REBALANCE_WEIGHT_MAX UINT64_C(10000)
#define REBALANCE_WEIGHT_UNSET UINT64_MAX
typedef struct TmpfsLimit {
/* Absolute and relative tmpfs limits */
uint64_t limit;
uint32_t limit_scale;
bool is_set;
} TmpfsLimit;
#define TMPFS_LIMIT_NULL \
(TmpfsLimit) { \
.limit = UINT64_MAX, \
.limit_scale = UINT32_MAX, \
} \
typedef struct UserRecord {
/* The following three fields are not part of the JSON record */
unsigned n_ref;
@ -389,6 +402,8 @@ typedef struct UserRecord {
char **self_modifiable_blobs;
char **self_modifiable_privileged;
TmpfsLimit tmp_limit, dev_shm_limit;
sd_json_variant *json;
} UserRecord;
@ -436,6 +451,8 @@ uint64_t user_record_rebalance_weight(UserRecord *h);
uint64_t user_record_capability_bounding_set(UserRecord *h);
uint64_t user_record_capability_ambient_set(UserRecord *h);
int user_record_languages(UserRecord *h, char ***ret);
uint32_t user_record_tmp_limit_scale(UserRecord *h);
uint32_t user_record_dev_shm_limit_scale(UserRecord *h);
const char **user_record_self_modifiable_fields(UserRecord *h);
const char **user_record_self_modifiable_blobs(UserRecord *h);