@push.rocks/smartdaemon 2.12.0 → 2.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,7 +3,7 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: '@push.rocks/smartdaemon',
6
- version: '2.12.0',
6
+ version: '2.13.0',
7
7
  description: 'Start scripts as long running daemons and manage them.'
8
8
  };
9
9
  //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMDBfY29tbWl0aW5mb19kYXRhLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvMDBfY29tbWl0aW5mb19kYXRhLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOztHQUVHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sVUFBVSxHQUFHO0lBQ3hCLElBQUksRUFBRSx5QkFBeUI7SUFDL0IsT0FBTyxFQUFFLFFBQVE7SUFDakIsV0FBVyxFQUFFLHdEQUF3RDtDQUN0RSxDQUFBIn0=
@@ -1,5 +1,6 @@
1
- import { type TSystemdServiceStartup, type ISystemdServiceDependencies, type ISystemdServiceInstallTargets } from './systemd.definition.js';
2
- export type { ISystemdServiceStartup, ISystemdServiceNotifyStartup, TSystemdServiceStartup, ISystemdServiceDependencies, ISystemdServiceInstallTargets } from './systemd.definition.js';
1
+ import { type TSystemdServiceStartup, type ISystemdServiceDependencies, type ISystemdServiceInstallTargets, type ISystemdServiceCredential, type TSystemdCapability, type TSystemdAddressFamily } from './systemd.definition.js';
2
+ export type { ISystemdServiceStartup, ISystemdServiceNotifyStartup, TSystemdServiceStartup, ISystemdServiceDependencies, ISystemdServiceInstallTargets, ISystemdServiceCredential, TSystemdCapability, TSystemdAddressFamily } from './systemd.definition.js';
3
+ export { systemdCapabilities, systemdAddressFamilies } from './systemd.definition.js';
3
4
  interface ISystemdServiceDefinitionBaseOptions {
4
5
  unitName: string;
5
6
  description: string;
@@ -29,6 +30,53 @@ interface ISystemdServiceDefinitionBaseOptions {
29
30
  * 64 variables and 8192 UTF-8 bytes of `NAME=value` in total. They are taken literally: `%` is escaped, and `Environment=` never expands `$`.
30
31
  */
31
32
  environment?: Readonly<Record<string, string>>;
33
+ /**
34
+ * System scope only. Runs the service as a transient user and group allocated at start
35
+ * (`DynamicUser=`), named by `user` and `group`, which then must not be `root` and have at most
36
+ * 31 characters. Systemd enforces `ProtectSystem=strict`, `PrivateTmp=`, `NoNewPrivileges=` and
37
+ * `RestrictSUIDSGID=` for such a service, so a definition that weakens any of them is refused.
38
+ */
39
+ dynamicUser?: boolean;
40
+ /** System scope only. Credentials read in plaintext, one `LoadCredential=` line each, ordered by name. */
41
+ loadCredentials?: readonly ISystemdServiceCredential[];
42
+ /** System scope only. Credentials sealed with `systemd-creds encrypt`, one `LoadCredentialEncrypted=` line each, ordered by name. */
43
+ loadCredentialsEncrypted?: readonly ISystemdServiceCredential[];
44
+ /** System scope only (`NoNewPrivileges=`). */
45
+ noNewPrivileges?: boolean;
46
+ /** System scope only (`ProtectSystem=`): `true` mounts `/usr` and the boot loader read-only, `'full'` also `/etc`, `'strict'` the whole file system but the API file systems. */
47
+ protectSystem?: boolean | 'full' | 'strict';
48
+ /** System scope only (`ProtectHome=`). */
49
+ protectHome?: boolean | 'read-only' | 'tmpfs';
50
+ /** System scope only (`PrivateTmp=`). */
51
+ privateTmp?: boolean;
52
+ /** System scope only (`PrivateDevices=`). */
53
+ privateDevices?: boolean;
54
+ /** System scope only (`ProtectKernelTunables=`). */
55
+ protectKernelTunables?: boolean;
56
+ /** System scope only (`ProtectKernelModules=`). */
57
+ protectKernelModules?: boolean;
58
+ /** System scope only (`ProtectKernelLogs=`). */
59
+ protectKernelLogs?: boolean;
60
+ /** System scope only (`ProtectControlGroups=`). */
61
+ protectControlGroups?: boolean;
62
+ /** System scope only (`ProtectClock=`). */
63
+ protectClock?: boolean;
64
+ /** System scope only (`ProtectHostname=`). */
65
+ protectHostname?: boolean;
66
+ /** System scope only (`RestrictNamespaces=`): `true` forbids creating every kind of namespace. */
67
+ restrictNamespaces?: boolean;
68
+ /** System scope only (`RestrictRealtime=`). */
69
+ restrictRealtime?: boolean;
70
+ /** System scope only (`RestrictSUIDSGID=`). */
71
+ restrictSUIDSGID?: boolean;
72
+ /** System scope only (`LockPersonality=`). */
73
+ lockPersonality?: boolean;
74
+ /** System scope only. The only capabilities the service's processes can ever hold (`CapabilityBoundingSet=`); empty drops all. */
75
+ capabilityBoundingSet?: readonly TSystemdCapability[];
76
+ /** System scope only. Capabilities a non-root service's processes receive (`AmbientCapabilities=`); within `capabilityBoundingSet` when both are given. */
77
+ ambientCapabilities?: readonly TSystemdCapability[];
78
+ /** System scope only. The only socket families the service may create (`RestrictAddressFamilies=`); at least one, and `AF_UNIX` for a notify service. */
79
+ restrictAddressFamilies?: readonly TSystemdAddressFamily[];
32
80
  }
33
81
  export type ISystemdServiceDefinitionOptions = ISystemdServiceDefinitionBaseOptions & ({
34
82
  scope?: 'system';
@@ -39,8 +87,14 @@ export type ISystemdServiceDefinitionOptions = ISystemdServiceDefinitionBaseOpti
39
87
  user?: never;
40
88
  group?: never;
41
89
  });
90
+ /**
91
+ * The option a refusal names: a key of the definition, or `'options'` for the definition object
92
+ * itself (not a plain object, an unknown or accessor key, or a unit over 16384 bytes).
93
+ */
94
+ export type TSystemdServiceDefinitionOption = keyof ISystemdServiceDefinitionOptions | 'options';
42
95
  export declare class SystemdServiceDefinitionError extends Error {
43
- constructor();
96
+ readonly option: TSystemdServiceDefinitionOption;
97
+ constructor(option: TSystemdServiceDefinitionOption);
44
98
  }
45
99
  /** Literal direct-execution definition; never introduces a shell or environment expansion. */
46
100
  export declare class SystemdServiceDefinition {
@@ -1,27 +1,50 @@
1
1
  import * as plugins from './smartdaemon.plugins.js';
2
- import { environmentByteLimit, environmentVariableLimit, isCleanText, renderServiceRelations } from './systemd.definition.js';
2
+ import { environmentByteLimit, environmentVariableLimit, isCleanText, renderServiceRelations, systemdAddressFamilies, systemdCapabilities } from './systemd.definition.js';
3
+ export { systemdCapabilities, systemdAddressFamilies } from './systemd.definition.js';
3
4
  export class SystemdServiceDefinitionError extends Error {
4
- constructor() {
5
- super('Invalid direct systemd service definition.');
5
+ constructor(option) {
6
+ super(`Invalid direct systemd service definition: ${option}.`);
6
7
  this.name = 'SystemdServiceDefinitionError';
8
+ this.option = option;
7
9
  }
8
10
  }
11
+ /** Sandboxing switches in their rendering order, each with its directive. */
12
+ const sandboxSwitches = [
13
+ ['privateTmp', 'PrivateTmp'],
14
+ ['privateDevices', 'PrivateDevices'],
15
+ ['protectKernelTunables', 'ProtectKernelTunables'],
16
+ ['protectKernelModules', 'ProtectKernelModules'],
17
+ ['protectKernelLogs', 'ProtectKernelLogs'],
18
+ ['protectControlGroups', 'ProtectControlGroups'],
19
+ ['protectClock', 'ProtectClock'],
20
+ ['protectHostname', 'ProtectHostname'],
21
+ ['restrictNamespaces', 'RestrictNamespaces'],
22
+ ['restrictRealtime', 'RestrictRealtime'],
23
+ ['restrictSUIDSGID', 'RestrictSUIDSGID'],
24
+ ['lockPersonality', 'LockPersonality'],
25
+ ];
26
+ /** Options a user manager's service cannot carry: they need the system manager's privileges. */
27
+ const systemOnlyOptions = ['dynamicUser', 'loadCredentials', 'loadCredentialsEncrypted', 'noNewPrivileges',
28
+ 'protectSystem', 'protectHome', ...sandboxSwitches.map(([option]) => option), 'capabilityBoundingSet',
29
+ 'ambientCapabilities', 'restrictAddressFamilies'];
9
30
  /** Literal direct-execution definition; never introduces a shell or environment expansion. */
10
31
  export class SystemdServiceDefinition {
11
32
  constructor(options) {
12
- const requireValue = (value) => {
33
+ const fail = (option) => {
34
+ throw new SystemdServiceDefinitionError(option);
35
+ };
36
+ const requireValue = (value, option) => {
13
37
  if (!value)
14
- throw new SystemdServiceDefinitionError();
38
+ fail(option);
15
39
  };
16
40
  requireValue(options && !plugins.util.types.isProxy(options) &&
17
- Object.getPrototypeOf(options) === Object.prototype);
41
+ Object.getPrototypeOf(options) === Object.prototype, 'options');
18
42
  const scopeDescriptor = Object.getOwnPropertyDescriptor(options, 'scope');
19
43
  requireValue(scopeDescriptor === undefined ||
20
- scopeDescriptor.enumerable && 'value' in scopeDescriptor);
44
+ scopeDescriptor.enumerable && 'value' in scopeDescriptor, 'scope');
21
45
  const scopeValue = scopeDescriptor?.value ?? 'system';
22
- if (scopeValue !== 'system' && scopeValue !== 'user') {
23
- throw new SystemdServiceDefinitionError();
24
- }
46
+ if (scopeValue !== 'system' && scopeValue !== 'user')
47
+ fail('scope');
25
48
  const scope = scopeValue;
26
49
  // Systemd ignores an assignment whose text is not clean UTF-8, leaving the unit unloadable.
27
50
  const text = (value, maximum = 4096) => typeof value === 'string' && value.length <= maximum && isCleanText(value);
@@ -38,55 +61,64 @@ export class SystemdServiceDefinition {
38
61
  'timeoutStopSeconds',
39
62
  ...(scope === 'system' ? ['user', 'group'] : []),
40
63
  ];
41
- const optionalKeys = ['scope', 'startup', 'dependencies', 'install', 'restartSeconds', 'delegate',
42
- 'limitNoFile', 'tasksMax', 'oomScoreAdjust', 'environment'];
43
- requireValue(Reflect.ownKeys(options).every(key => typeof key === 'string' && [...keys, ...optionalKeys].includes(key)
44
- && Object.getOwnPropertyDescriptor(options, key)?.enumerable && 'value' in Object.getOwnPropertyDescriptor(options, key)) && keys.every((key) => {
64
+ const optionalKeys = ['scope', 'startup', 'dependencies', 'install',
65
+ 'restartSeconds', 'delegate', 'limitNoFile', 'tasksMax', 'oomScoreAdjust', 'environment',
66
+ ...systemOnlyOptions];
67
+ // Every own key is a known option held by an enumerable data property, so no accessor or trap runs.
68
+ for (const key of Reflect.ownKeys(options)) {
69
+ requireValue(typeof key === 'string' && [...keys, ...optionalKeys, 'user', 'group'].includes(key), 'options');
70
+ const option = key;
45
71
  const descriptor = Object.getOwnPropertyDescriptor(options, key);
46
- return descriptor && 'value' in descriptor;
47
- }));
72
+ requireValue(keys.includes(option) || optionalKeys.includes(option), option);
73
+ requireValue(descriptor?.enumerable && 'value' in descriptor, option);
74
+ }
75
+ for (const key of keys)
76
+ requireValue(Object.getOwnPropertyDescriptor(options, key), key);
48
77
  requireValue(typeof options.unitName === 'string' &&
49
- /^[a-zA-Z0-9][a-zA-Z0-9_.-]{0,230}\.service$/.test(options.unitName));
78
+ /^[a-zA-Z0-9][a-zA-Z0-9_.-]{0,230}\.service$/.test(options.unitName), 'unitName');
50
79
  requireValue(text(options.description, 1024) && options.description.length > 0 &&
51
- options.description.trim() === options.description && !options.description.includes('\\'));
80
+ options.description.trim() === options.description && !options.description.includes('\\'), 'description');
52
81
  requireValue(absolute(options.executable) && options.executable !== '/' &&
53
- !/[\\"']/.test(options.executable) && !options.executable.endsWith('/') &&
54
- absolute(options.workingDirectory) &&
82
+ !/[\\"']/.test(options.executable) && !options.executable.endsWith('/'), 'executable');
83
+ requireValue(absolute(options.workingDirectory) &&
55
84
  options.workingDirectory.trim() === options.workingDirectory &&
56
- !options.workingDirectory.includes('\\'));
85
+ !options.workingDirectory.includes('\\'), 'workingDirectory');
57
86
  requireValue(Array.isArray(options.args) && !plugins.util.types.isProxy(options.args) && Object.getPrototypeOf(options.args) === Array.prototype &&
58
87
  options.args.length <= 128 &&
59
88
  Reflect.ownKeys(options.args).length === options.args.length + 1 &&
60
89
  Array.from({ length: options.args.length }, (_, index) => Object.getOwnPropertyDescriptor(options.args, String(index)))
61
- .every((descriptor) => descriptor && 'value' in descriptor && text(descriptor.value)));
90
+ .every((descriptor) => descriptor && 'value' in descriptor && text(descriptor.value)), 'args');
62
91
  if (scope === 'system') {
63
- requireValue([options.user, options.group].every((value) => typeof value === 'string' && /^[a-zA-Z_][a-zA-Z0-9_-]{0,63}$/.test(value)));
92
+ for (const key of ['user', 'group']) {
93
+ requireValue(typeof options[key] === 'string' && /^[a-zA-Z_][a-zA-Z0-9_-]{0,63}$/.test(options[key]), key);
94
+ }
64
95
  }
65
- requireValue(['no', 'on-failure', 'always'].includes(options.restart) &&
66
- ['mixed', 'control-group', 'process'].includes(options.killMode) &&
67
- Number.isSafeInteger(options.timeoutStopSeconds) &&
68
- options.timeoutStopSeconds >= 1 && options.timeoutStopSeconds <= 600);
96
+ requireValue(['no', 'on-failure', 'always'].includes(options.restart), 'restart');
97
+ requireValue(['mixed', 'control-group', 'process'].includes(options.killMode), 'killMode');
98
+ requireValue(Number.isSafeInteger(options.timeoutStopSeconds) &&
99
+ options.timeoutStopSeconds >= 1 && options.timeoutStopSeconds <= 600, 'timeoutStopSeconds');
69
100
  const integer = (value, minimum, maximum) => Number.isSafeInteger(value) && value >= minimum && value <= maximum;
70
101
  const limit = (value) => value === 'infinity' || integer(value, 1, Number.MAX_SAFE_INTEGER);
102
+ const yesNo = (value) => value ? 'yes' : 'no';
71
103
  // Each optional directive is absent unless supplied, so existing definitions keep their bytes.
72
104
  const service = [];
73
105
  if (options.restartSeconds !== undefined) {
74
- requireValue(integer(options.restartSeconds, 1, 600) && options.restart !== 'no');
106
+ requireValue(integer(options.restartSeconds, 1, 600) && options.restart !== 'no', 'restartSeconds');
75
107
  }
76
108
  if (options.delegate !== undefined) {
77
- requireValue(typeof options.delegate === 'boolean');
78
- service.push(`Delegate=${options.delegate ? 'yes' : 'no'}`);
109
+ requireValue(typeof options.delegate === 'boolean', 'delegate');
110
+ service.push(`Delegate=${yesNo(options.delegate)}`);
79
111
  }
80
112
  if (options.limitNoFile !== undefined) {
81
- requireValue(limit(options.limitNoFile));
113
+ requireValue(limit(options.limitNoFile), 'limitNoFile');
82
114
  service.push(`LimitNOFILE=${options.limitNoFile}`);
83
115
  }
84
116
  if (options.tasksMax !== undefined) {
85
- requireValue(limit(options.tasksMax));
117
+ requireValue(limit(options.tasksMax), 'tasksMax');
86
118
  service.push(`TasksMax=${options.tasksMax}`);
87
119
  }
88
120
  if (options.oomScoreAdjust !== undefined) {
89
- requireValue(integer(options.oomScoreAdjust, -1000, 1000));
121
+ requireValue(integer(options.oomScoreAdjust, -1000, 1000), 'oomScoreAdjust');
90
122
  service.push(`OOMScoreAdjust=${options.oomScoreAdjust}`);
91
123
  }
92
124
  // Quoting and percent/dollar escaping follow systemd.syntax and systemd.service.
@@ -94,26 +126,130 @@ export class SystemdServiceDefinition {
94
126
  .replaceAll('\\', '\\\\').replaceAll('"', '\\"').replaceAll('%', '%%')
95
127
  .replaceAll('$', () => command ? '$$' : '$') +
96
128
  '"';
129
+ const plainObject = (value, option) => requireValue(value !== null && typeof value === 'object' && !plugins.util.types.isProxy(value) &&
130
+ Object.getPrototypeOf(value) === Object.prototype, option);
97
131
  if (options.environment !== undefined) {
98
132
  // Read each variable once through its own data descriptor, so no accessor or trap runs.
99
133
  const variables = options.environment;
100
- requireValue(variables !== null && typeof variables === 'object' &&
101
- !plugins.util.types.isProxy(variables) && Object.getPrototypeOf(variables) === Object.prototype);
134
+ plainObject(variables, 'environment');
102
135
  const names = Reflect.ownKeys(variables);
103
- requireValue(names.length <= environmentVariableLimit);
136
+ requireValue(names.length <= environmentVariableLimit, 'environment');
104
137
  const assignments = new Map();
105
138
  for (const name of names) {
106
139
  const descriptor = Object.getOwnPropertyDescriptor(variables, name);
107
140
  requireValue(typeof name === 'string' && /^[A-Za-z_][A-Za-z0-9_]{0,127}$/.test(name) &&
108
- descriptor?.enumerable && 'value' in descriptor && text(descriptor.value));
141
+ descriptor?.enumerable && 'value' in descriptor && text(descriptor.value), 'environment');
109
142
  assignments.set(name, `${name}=${descriptor.value}`);
110
143
  }
111
- requireValue(Buffer.byteLength([...assignments.values()].join('')) <= environmentByteLimit);
144
+ requireValue(Buffer.byteLength([...assignments.values()].join('')) <= environmentByteLimit, 'environment');
112
145
  // Environment= unquotes and unescapes each word and expands `%` specifiers, never `$`.
113
146
  service.push(...[...assignments.keys()].sort()
114
147
  .map((name) => `Environment=${quote(assignments.get(name))}`));
115
148
  }
116
- const relations = renderServiceRelations(options, options.unitName, options.restart, scope, () => { throw new SystemdServiceDefinitionError(); });
149
+ // Every system-only option is refused by name in a user manager's service.
150
+ for (const option of systemOnlyOptions) {
151
+ requireValue(scope === 'system' || options[option] === undefined, option);
152
+ }
153
+ /** Reads a plain array once through its data descriptors: distinct items, sorted. */
154
+ const list = (value, option, maximum, item, identity) => {
155
+ requireValue(Array.isArray(value) && !plugins.util.types.isProxy(value) &&
156
+ Object.getPrototypeOf(value) === Array.prototype && value.length <= maximum &&
157
+ Reflect.ownKeys(value).length === value.length + 1, option);
158
+ const result = Array.from({ length: value.length }, (_, index) => {
159
+ const descriptor = Object.getOwnPropertyDescriptor(value, String(index));
160
+ requireValue(descriptor?.enumerable && 'value' in descriptor, option);
161
+ return item(descriptor.value);
162
+ });
163
+ const identities = result.map(identity);
164
+ requireValue(new Set(identities).size === identities.length, option);
165
+ return result.sort((left, right) => identity(left) < identity(right) ? -1 : 1);
166
+ };
167
+ // Credential names and sources hold no `%`, quote, backslash, whitespace or `:`, so systemd's
168
+ // `NAME[:SOURCE]` parser and its specifier expansion read them exactly as written.
169
+ const credentialName = (value) => typeof value === 'string' && /^[A-Za-z0-9_@-][A-Za-z0-9_.@-]{0,254}$/.test(value);
170
+ const credentials = (value, option) => list(value, option, 64, (entry) => {
171
+ plainObject(entry, option);
172
+ const entryKeys = Reflect.ownKeys(entry);
173
+ requireValue(entryKeys.includes('name') && entryKeys.every((key) => (key === 'name' || key === 'source') &&
174
+ Object.getOwnPropertyDescriptor(entry, key)?.enumerable &&
175
+ 'value' in Object.getOwnPropertyDescriptor(entry, key)), option);
176
+ const { name, source } = entry;
177
+ requireValue(credentialName(name), option);
178
+ if (entryKeys.includes('source')) {
179
+ requireValue(source !== name && (credentialName(source) || absolute(source) && !source.endsWith('/') &&
180
+ /^[A-Za-z0-9_.@/+-]+$/.test(source)), option);
181
+ }
182
+ return entryKeys.includes('source') ? { name: name, source: source } : { name: name };
183
+ }, (entry) => entry.name);
184
+ const plainCredentials = options.loadCredentials === undefined ? [] : credentials(options.loadCredentials, 'loadCredentials');
185
+ const encryptedCredentials = options.loadCredentialsEncrypted === undefined
186
+ ? [] : credentials(options.loadCredentialsEncrypted, 'loadCredentialsEncrypted');
187
+ // Systemd keeps both kinds in one table by name, so a name loads from exactly one of them.
188
+ requireValue(encryptedCredentials.every((entry) => !plainCredentials.some(({ name }) => name === entry.name)), 'loadCredentialsEncrypted');
189
+ const credential = ({ name, source }) => source === undefined ? name : `${name}:${source}`;
190
+ service.push(...plainCredentials.map((entry) => `LoadCredential=${credential(entry)}`));
191
+ service.push(...encryptedCredentials.map((entry) => `LoadCredentialEncrypted=${credential(entry)}`));
192
+ const dynamicUser = options.dynamicUser;
193
+ if (dynamicUser !== undefined) {
194
+ requireValue(typeof dynamicUser === 'boolean', 'dynamicUser');
195
+ // A static account of the same name would be used instead, so the name must not be root's,
196
+ // and systemd allocates only names that fit utmp.
197
+ requireValue(!dynamicUser || [options.user, options.group].every((name) => name !== 'root' && typeof name === 'string' && name.length <= 31), 'dynamicUser');
198
+ }
199
+ // Settings systemd forces on for a dynamic user, so the unit never states a weaker one.
200
+ const forced = (option, weakened) => requireValue(!(dynamicUser === true && weakened), option);
201
+ if (options.noNewPrivileges !== undefined) {
202
+ requireValue(typeof options.noNewPrivileges === 'boolean', 'noNewPrivileges');
203
+ forced('noNewPrivileges', !options.noNewPrivileges);
204
+ service.push(`NoNewPrivileges=${yesNo(options.noNewPrivileges)}`);
205
+ }
206
+ if (options.protectSystem !== undefined) {
207
+ requireValue([true, false, 'full', 'strict'].includes(options.protectSystem), 'protectSystem');
208
+ forced('protectSystem', options.protectSystem !== 'strict');
209
+ service.push(`ProtectSystem=${typeof options.protectSystem === 'boolean'
210
+ ? yesNo(options.protectSystem) : options.protectSystem}`);
211
+ }
212
+ if (options.protectHome !== undefined) {
213
+ requireValue([true, false, 'read-only', 'tmpfs'].includes(options.protectHome), 'protectHome');
214
+ forced('protectHome', options.protectHome === false);
215
+ service.push(`ProtectHome=${typeof options.protectHome === 'boolean'
216
+ ? yesNo(options.protectHome) : options.protectHome}`);
217
+ }
218
+ for (const [option, directive] of sandboxSwitches) {
219
+ const value = options[option];
220
+ if (value === undefined)
221
+ continue;
222
+ requireValue(typeof value === 'boolean', option);
223
+ forced(option, (option === 'privateTmp' || option === 'restrictSUIDSGID') && !value);
224
+ service.push(`${directive}=${yesNo(value)}`);
225
+ }
226
+ const capabilities = (value, option) => list(value, option, systemdCapabilities.length, (entry) => {
227
+ requireValue(systemdCapabilities.includes(entry), option);
228
+ return entry;
229
+ }, (entry) => entry);
230
+ const bounding = options.capabilityBoundingSet === undefined
231
+ ? undefined : capabilities(options.capabilityBoundingSet, 'capabilityBoundingSet');
232
+ const ambient = options.ambientCapabilities === undefined
233
+ ? undefined : capabilities(options.ambientCapabilities, 'ambientCapabilities');
234
+ // An ambient capability outside the bounding set would fail every start of the service.
235
+ requireValue(!bounding || !ambient || ambient.every((entry) => bounding.includes(entry)), 'ambientCapabilities');
236
+ // An empty CapabilityBoundingSet= empties the bounding set; an empty AmbientCapabilities= grants none.
237
+ if (bounding)
238
+ service.push(`CapabilityBoundingSet=${bounding.join(' ')}`);
239
+ if (ambient)
240
+ service.push(`AmbientCapabilities=${ambient.join(' ')}`);
241
+ const families = options.restrictAddressFamilies === undefined ? undefined
242
+ : list(options.restrictAddressFamilies, 'restrictAddressFamilies', systemdAddressFamilies.length, (entry) => {
243
+ requireValue(systemdAddressFamilies.includes(entry), 'restrictAddressFamilies');
244
+ return entry;
245
+ }, (entry) => entry);
246
+ // An empty RestrictAddressFamilies= would lift the restriction instead of denying every family.
247
+ requireValue(!families || families.length > 0, 'restrictAddressFamilies');
248
+ if (families)
249
+ service.push(`RestrictAddressFamilies=${families.join(' ')}`);
250
+ const relations = renderServiceRelations(options, options.unitName, options.restart, scope, fail);
251
+ // A notify service sends READY=1 over an AF_UNIX socket it creates itself.
252
+ requireValue(!families || !relations.service.includes('Type=notify') || families.includes('AF_UNIX'), 'restrictAddressFamilies');
117
253
  this.unitName = options.unitName;
118
254
  this.scope = scope;
119
255
  this.content = [
@@ -124,6 +260,7 @@ export class SystemdServiceDefinition {
124
260
  '[Service]',
125
261
  ...relations.service,
126
262
  ...(scope === 'system' ? [`User=${options.user}`, `Group=${options.group}`] : []),
263
+ ...(dynamicUser !== undefined ? [`DynamicUser=${yesNo(dynamicUser)}`] : []),
127
264
  `ExecStart=${[quote(options.executable), ...options.args.map((value) => quote(value, true))].join(' ')}`,
128
265
  // WorkingDirectory is a raw path value, not an ExecStart word list.
129
266
  `WorkingDirectory=${options.workingDirectory.replaceAll('%', '%%')}`,
@@ -144,9 +281,9 @@ export class SystemdServiceDefinition {
144
281
  ...relations.install,
145
282
  '',
146
283
  ].join('\n');
147
- requireValue(Buffer.byteLength(this.content) <= 16384);
284
+ requireValue(Buffer.byteLength(this.content) <= 16384, 'options');
148
285
  this.sha256 = plugins.crypto.createHash('sha256').update(this.content).digest('hex');
149
286
  Object.freeze(this);
150
287
  }
151
288
  }
152
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5zeXN0ZW1kc2VydmljZWRlZmluaXRpb24uanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9jbGFzc2VzLnN5c3RlbWRzZXJ2aWNlZGVmaW5pdGlvbi50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLDBCQUEwQixDQUFDO0FBQ3BELE9BQU8sRUFBRSxvQkFBb0IsRUFBRSx3QkFBd0IsRUFBRSxXQUFXLEVBQUUsc0JBQXNCLEVBQ3BCLE1BQU0seUJBQXlCLENBQUM7QUF3Q3hHLE1BQU0sT0FBTyw2QkFBOEIsU0FBUSxLQUFLO0lBQ3REO1FBQ0UsS0FBSyxDQUFDLDRDQUE0QyxDQUFDLENBQUM7UUFDcEQsSUFBSSxDQUFDLElBQUksR0FBRywrQkFBK0IsQ0FBQztJQUM5QyxDQUFDO0NBQ0Y7QUFFRCw4RkFBOEY7QUFDOUYsTUFBTSxPQUFPLHdCQUF3QjtJQU1uQyxZQUFZLE9BQXlDO1FBQ25ELE1BQU0sWUFBWSxHQUFHLENBQUMsS0FBYyxFQUFRLEVBQUU7WUFDNUMsSUFBSSxDQUFDLEtBQUs7Z0JBQUUsTUFBTSxJQUFJLDZCQUE2QixFQUFFLENBQUM7UUFDeEQsQ0FBQyxDQUFDO1FBQ0YsWUFBWSxDQUFDLE9BQU8sSUFBSSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLE9BQU8sQ0FBQyxPQUFPLENBQUM7WUFDMUQsTUFBTSxDQUFDLGNBQWMsQ0FBQyxPQUFPLENBQUMsS0FBSyxNQUFNLENBQUMsU0FBUyxDQUFDLENBQUM7UUFDdkQsTUFBTSxlQUFlLEdBQUcsTUFBTSxDQUFDLHdCQUF3QixDQUFDLE9BQU8sRUFBRSxPQUFPLENBQUMsQ0FBQztRQUMxRSxZQUFZLENBQUMsZUFBZSxLQUFLLFNBQVM7WUFDeEMsZUFBZSxDQUFDLFVBQVUsSUFBSSxPQUFPLElBQUksZUFBZSxDQUFDLENBQUM7UUFDNUQsTUFBTSxVQUFVLEdBQVksZUFBZSxFQUFFLEtBQUssSUFBSSxRQUFRLENBQUM7UUFDL0QsSUFBSSxVQUFVLEtBQUssUUFBUSxJQUFJLFVBQVUsS0FBSyxNQUFNLEVBQUUsQ0FBQztZQUNyRCxNQUFNLElBQUksNkJBQTZCLEVBQUUsQ0FBQztRQUM1QyxDQUFDO1FBQ0QsTUFBTSxLQUFLLEdBQUcsVUFBVSxDQUFDO1FBQ3pCLDRGQUE0RjtRQUM1RixNQUFNLElBQUksR0FBRyxDQUFDLEtBQWMsRUFBRSxPQUFPLEdBQUcsSUFBSSxFQUFtQixFQUFFLENBQy9ELE9BQU8sS0FBSyxLQUFLLFFBQVEsSUFBSSxLQUFLLENBQUMsTUFBTSxJQUFJLE9BQU8sSUFBSSxXQUFXLENBQUMsS0FBSyxDQUFDLENBQUM7UUFDN0UsTUFBTSxRQUFRLEdBQUcsQ0FBQyxLQUFjLEVBQUUsRUFBRSxDQUNsQyxJQUFJLENBQUMsS0FBSyxDQUFDLElBQUksT0FBTyxDQUFDLElBQUksQ0FBQyxVQUFVLENBQUMsS0FBSyxDQUFDO1lBQzdDLE9BQU8sQ0FBQyxJQUFJLENBQUMsU0FBUyxDQUFDLEtBQUssQ0FBQyxLQUFLLEtBQUssQ0FBQztRQUMxQyxNQUFNLElBQUksR0FBRztZQUNYLFVBQVU7WUFDVixhQUFhO1lBQ2IsWUFBWTtZQUNaLE1BQU07WUFDTixrQkFBa0I7WUFDbEIsU0FBUztZQUNULFVBQVU7WUFDVixvQkFBb0I7WUFDcEIsR0FBRyxDQUFDLEtBQUssS0FBSyxRQUFRLENBQUMsQ0FBQyxDQUFDLENBQUMsTUFBTSxFQUFFLE9BQU8sQ0FBQyxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUM7U0FDakQsQ0FBQztRQUNGLE1BQU0sWUFBWSxHQUFHLENBQUMsT0FBTyxFQUFFLFNBQVMsRUFBRSxjQUFjLEVBQUUsU0FBUyxFQUFFLGdCQUFnQixFQUFFLFVBQVU7WUFDL0YsYUFBYSxFQUFFLFVBQVUsRUFBRSxnQkFBZ0IsRUFBRSxhQUFhLENBQUMsQ0FBQztRQUM5RCxZQUFZLENBQ1YsT0FBTyxDQUFDLE9BQU8sQ0FBQyxPQUFPLENBQUMsQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxPQUFPLEdBQUcsS0FBSyxRQUFRLElBQUksQ0FBQyxHQUFHLElBQUksRUFBRSxHQUFHLFlBQVksQ0FBQyxDQUFDLFFBQVEsQ0FBQyxHQUFHLENBQUM7ZUFDbEcsTUFBTSxDQUFDLHdCQUF3QixDQUFDLE9BQU8sRUFBRSxHQUFHLENBQUMsRUFBRSxVQUFVLElBQUksT0FBTyxJQUFJLE1BQU0sQ0FBQyx3QkFBd0IsQ0FBQyxPQUFPLEVBQUUsR0FBRyxDQUFFLENBQUMsSUFBSSxJQUFJLENBQUMsS0FBSyxDQUFDLENBQUMsR0FBRyxFQUFFLEVBQUU7WUFDakosTUFBTSxVQUFVLEdBQUcsTUFBTSxDQUFDLHdCQUF3QixDQUFDLE9BQU8sRUFBRSxHQUFHLENBQUMsQ0FBQztZQUNqRSxPQUFPLFVBQVUsSUFBSSxPQUFPLElBQUksVUFBVSxDQUFDO1FBQzdDLENBQUMsQ0FBQyxDQUNMLENBQUM7UUFDRixZQUFZLENBQ1YsT0FBTyxPQUFPLENBQUMsUUFBUSxLQUFLLFFBQVE7WUFDbEMsNkNBQTZDLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxRQUFRLENBQUMsQ0FDdkUsQ0FBQztRQUNGLFlBQVksQ0FDVixJQUFJLENBQUMsT0FBTyxDQUFDLFdBQVcsRUFBRSxJQUFJLENBQUMsSUFBSSxPQUFPLENBQUMsV0FBVyxDQUFDLE1BQU0sR0FBRyxDQUFDO1lBQy9ELE9BQU8sQ0FBQyxXQUFXLENBQUMsSUFBSSxFQUFFLEtBQUssT0FBTyxDQUFDLFdBQVcsSUFBSSxDQUFDLE9BQU8sQ0FBQyxXQUFXLENBQUMsUUFBUSxDQUFDLElBQUksQ0FBQyxDQUM1RixDQUFDO1FBQ0YsWUFBWSxDQUNWLFFBQVEsQ0FBQyxPQUFPLENBQUMsVUFBVSxDQUFDLElBQUksT0FBTyxDQUFDLFVBQVUsS0FBSyxHQUFHO1lBQ3hELENBQUMsUUFBUSxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsVUFBVSxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsVUFBVSxDQUFDLFFBQVEsQ0FBQyxHQUFHLENBQUM7WUFDdkUsUUFBUSxDQUFDLE9BQU8sQ0FBQyxnQkFBZ0IsQ0FBQztZQUNsQyxPQUFPLENBQUMsZ0JBQWdCLENBQUMsSUFBSSxFQUFFLEtBQUssT0FBTyxDQUFDLGdCQUFnQjtZQUM1RCxDQUFDLE9BQU8sQ0FBQyxnQkFBZ0IsQ0FBQyxRQUFRLENBQUMsSUFBSSxDQUFDLENBQzNDLENBQUM7UUFDRixZQUFZLENBQ1YsS0FBSyxDQUFDLE9BQU8sQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxPQUFPLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyxJQUFJLE1BQU0sQ0FBQyxjQUFjLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyxLQUFLLEtBQUssQ0FBQyxTQUFTO1lBQ2pJLE9BQU8sQ0FBQyxJQUFJLENBQUMsTUFBTSxJQUFJLEdBQUc7WUFDMUIsT0FBTyxDQUFDLE9BQU8sQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLENBQUMsTUFBTSxLQUFLLE9BQU8sQ0FBQyxJQUFJLENBQUMsTUFBTSxHQUFHLENBQUM7WUFDaEUsS0FBSyxDQUFDLElBQUksQ0FDUixFQUFFLE1BQU0sRUFBRSxPQUFPLENBQUMsSUFBSSxDQUFDLE1BQU0sRUFBRSxFQUMvQixDQUFDLENBQUMsRUFBRSxLQUFLLEVBQUUsRUFBRSxDQUFDLE1BQU0sQ0FBQyx3QkFBd0IsQ0FBQyxPQUFPLENBQUMsSUFBSSxFQUFFLE1BQU0sQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUMzRTtpQkFDRSxLQUFLLENBQUMsQ0FBQyxVQUFVLEVBQUUsRUFBRSxDQUFDLFVBQVUsSUFBSSxPQUFPLElBQUksVUFBVSxJQUFJLElBQUksQ0FBQyxVQUFVLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FDMUYsQ0FBQztRQUNGLElBQUksS0FBSyxLQUFLLFFBQVEsRUFBRSxDQUFDO1lBQ3ZCLFlBQVksQ0FDVixDQUFDLE9BQU8sQ0FBQyxJQUFJLEVBQUUsT0FBTyxDQUFDLEtBQUssQ0FBQyxDQUFDLEtBQUssQ0FBQyxDQUFDLEtBQUssRUFBRSxFQUFFLENBQzVDLE9BQU8sS0FBSyxLQUFLLFFBQVEsSUFBSSxnQ0FBZ0MsQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLENBQzFFLENBQ0YsQ0FBQztRQUNKLENBQUM7UUFDRCxZQUFZLENBQ1YsQ0FBQyxJQUFJLEVBQUUsWUFBWSxFQUFFLFFBQVEsQ0FBQyxDQUFDLFFBQVEsQ0FBQyxPQUFPLENBQUMsT0FBTyxDQUFDO1lBQ3RELENBQUMsT0FBTyxFQUFFLGVBQWUsRUFBRSxTQUFTLENBQUMsQ0FBQyxRQUFRLENBQUMsT0FBTyxDQUFDLFFBQVEsQ0FBQztZQUNoRSxNQUFNLENBQUMsYUFBYSxDQUFDLE9BQU8sQ0FBQyxrQkFBa0IsQ0FBQztZQUNoRCxPQUFPLENBQUMsa0JBQWtCLElBQUksQ0FBQyxJQUFJLE9BQU8sQ0FBQyxrQkFBa0IsSUFBSSxHQUFHLENBQ3ZFLENBQUM7UUFDRixNQUFNLE9BQU8sR0FBRyxDQUFDLEtBQWMsRUFBRSxPQUFlLEVBQUUsT0FBZSxFQUFtQixFQUFFLENBQ3BGLE1BQU0sQ0FBQyxhQUFhLENBQUMsS0FBSyxDQUFDLElBQUssS0FBZ0IsSUFBSSxPQUFPLElBQUssS0FBZ0IsSUFBSSxPQUFPLENBQUM7UUFDOUYsTUFBTSxLQUFLLEdBQUcsQ0FBQyxLQUFjLEVBQWdDLEVBQUUsQ0FDN0QsS0FBSyxLQUFLLFVBQVUsSUFBSSxPQUFPLENBQUMsS0FBSyxFQUFFLENBQUMsRUFBRSxNQUFNLENBQUMsZ0JBQWdCLENBQUMsQ0FBQztRQUNyRSwrRkFBK0Y7UUFDL0YsTUFBTSxPQUFPLEdBQWEsRUFBRSxDQUFDO1FBQzdCLElBQUksT0FBTyxDQUFDLGNBQWMsS0FBSyxTQUFTLEVBQUUsQ0FBQztZQUN6QyxZQUFZLENBQUMsT0FBTyxDQUFDLE9BQU8sQ0FBQyxjQUFjLEVBQUUsQ0FBQyxFQUFFLEdBQUcsQ0FBQyxJQUFJLE9BQU8sQ0FBQyxPQUFPLEtBQUssSUFBSSxDQUFDLENBQUM7UUFDcEYsQ0FBQztRQUNELElBQUksT0FBTyxDQUFDLFFBQVEsS0FBSyxTQUFTLEVBQUUsQ0FBQztZQUNuQyxZQUFZLENBQUMsT0FBTyxPQUFPLENBQUMsUUFBUSxLQUFLLFNBQVMsQ0FBQyxDQUFDO1lBQ3BELE9BQU8sQ0FBQyxJQUFJLENBQUMsWUFBWSxPQUFPLENBQUMsUUFBUSxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLElBQUksRUFBRSxDQUFDLENBQUM7UUFDOUQsQ0FBQztRQUNELElBQUksT0FBTyxDQUFDLFdBQVcsS0FBSyxTQUFTLEVBQUUsQ0FBQztZQUN0QyxZQUFZLENBQUMsS0FBSyxDQUFDLE9BQU8sQ0FBQyxXQUFXLENBQUMsQ0FBQyxDQUFDO1lBQ3pDLE9BQU8sQ0FBQyxJQUFJLENBQUMsZUFBZSxPQUFPLENBQUMsV0FBVyxFQUFFLENBQUMsQ0FBQztRQUNyRCxDQUFDO1FBQ0QsSUFBSSxPQUFPLENBQUMsUUFBUSxLQUFLLFNBQVMsRUFBRSxDQUFDO1lBQ25DLFlBQVksQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLFFBQVEsQ0FBQyxDQUFDLENBQUM7WUFDdEMsT0FBTyxDQUFDLElBQUksQ0FBQyxZQUFZLE9BQU8sQ0FBQyxRQUFRLEVBQUUsQ0FBQyxDQUFDO1FBQy9DLENBQUM7UUFDRCxJQUFJLE9BQU8sQ0FBQyxjQUFjLEtBQUssU0FBUyxFQUFFLENBQUM7WUFDekMsWUFBWSxDQUFDLE9BQU8sQ0FBQyxPQUFPLENBQUMsY0FBYyxFQUFFLENBQUMsSUFBSSxFQUFFLElBQUksQ0FBQyxDQUFDLENBQUM7WUFDM0QsT0FBTyxDQUFDLElBQUksQ0FBQyxrQkFBa0IsT0FBTyxDQUFDLGNBQWMsRUFBRSxDQUFDLENBQUM7UUFDM0QsQ0FBQztRQUNELGlGQUFpRjtRQUNqRixNQUFNLEtBQUssR0FBRyxDQUFDLEtBQWEsRUFBRSxPQUFPLEdBQUcsS0FBSyxFQUFFLEVBQUUsQ0FDL0MsR0FBRyxHQUFHLEtBQUs7YUFDUixVQUFVLENBQUMsSUFBSSxFQUFFLE1BQU0sQ0FBQyxDQUFDLFVBQVUsQ0FBQyxHQUFHLEVBQUUsS0FBSyxDQUFDLENBQUMsVUFBVSxDQUFDLEdBQUcsRUFBRSxJQUFJLENBQUM7YUFDckUsVUFBVSxDQUFDLEdBQUcsRUFBRSxHQUFHLEVBQUUsQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsR0FBRyxDQUFDO1lBQzlDLEdBQUcsQ0FBQztRQUNOLElBQUksT0FBTyxDQUFDLFdBQVcsS0FBSyxTQUFTLEVBQUUsQ0FBQztZQUN0Qyx3RkFBd0Y7WUFDeEYsTUFBTSxTQUFTLEdBQVksT0FBTyxDQUFDLFdBQVcsQ0FBQztZQUMvQyxZQUFZLENBQUMsU0FBUyxLQUFLLElBQUksSUFBSSxPQUFPLFNBQVMsS0FBSyxRQUFRO2dCQUM5RCxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLE9BQU8sQ0FBQyxTQUFTLENBQUMsSUFBSSxNQUFNLENBQUMsY0FBYyxDQUFDLFNBQVMsQ0FBQyxLQUFLLE1BQU0sQ0FBQyxTQUFTLENBQUMsQ0FBQztZQUNuRyxNQUFNLEtBQUssR0FBRyxPQUFPLENBQUMsT0FBTyxDQUFDLFNBQW1CLENBQUMsQ0FBQztZQUNuRCxZQUFZLENBQUMsS0FBSyxDQUFDLE1BQU0sSUFBSSx3QkFBd0IsQ0FBQyxDQUFDO1lBQ3ZELE1BQU0sV0FBVyxHQUFHLElBQUksR0FBRyxFQUFrQixDQUFDO1lBQzlDLEtBQUssTUFBTSxJQUFJLElBQUksS0FBSyxFQUFFLENBQUM7Z0JBQ3pCLE1BQU0sVUFBVSxHQUFHLE1BQU0sQ0FBQyx3QkFBd0IsQ0FBQyxTQUFTLEVBQUUsSUFBSSxDQUFDLENBQUM7Z0JBQ3BFLFlBQVksQ0FBQyxPQUFPLElBQUksS0FBSyxRQUFRLElBQUksZ0NBQWdDLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQztvQkFDbEYsVUFBVSxFQUFFLFVBQVUsSUFBSSxPQUFPLElBQUksVUFBVSxJQUFJLElBQUksQ0FBQyxVQUFVLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQztnQkFDN0UsV0FBVyxDQUFDLEdBQUcsQ0FBQyxJQUFjLEVBQUUsR0FBRyxJQUFjLElBQUksVUFBVyxDQUFDLEtBQWUsRUFBRSxDQUFDLENBQUM7WUFDdEYsQ0FBQztZQUNELFlBQVksQ0FBQyxNQUFNLENBQUMsVUFBVSxDQUFDLENBQUMsR0FBRyxXQUFXLENBQUMsTUFBTSxFQUFFLENBQUMsQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDLENBQUMsSUFBSSxvQkFBb0IsQ0FBQyxDQUFDO1lBQzVGLHVGQUF1RjtZQUN2RixPQUFPLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxHQUFHLFdBQVcsQ0FBQyxJQUFJLEVBQUUsQ0FBQyxDQUFDLElBQUksRUFBRTtpQkFDM0MsR0FBRyxDQUFDLENBQUMsSUFBSSxFQUFFLEVBQUUsQ0FBQyxlQUFlLEtBQUssQ0FBQyxXQUFXLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBRSxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUM7UUFDcEUsQ0FBQztRQUNELE1BQU0sU0FBUyxHQUFHLHNCQUFzQixDQUFDLE9BQU8sRUFBRSxPQUFPLENBQUMsUUFBUSxFQUFFLE9BQU8sQ0FBQyxPQUFPLEVBQUUsS0FBSyxFQUFFLEdBQUcsRUFBRSxHQUFHLE1BQU0sSUFBSSw2QkFBNkIsRUFBRSxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUM7UUFDbEosSUFBSSxDQUFDLFFBQVEsR0FBRyxPQUFPLENBQUMsUUFBUSxDQUFDO1FBQ2pDLElBQUksQ0FBQyxLQUFLLEdBQUcsS0FBSyxDQUFDO1FBQ25CLElBQUksQ0FBQyxPQUFPLEdBQUc7WUFDYixRQUFRO1lBQ1IsZUFBZSxPQUFPLENBQUMsV0FBVyxDQUFDLFVBQVUsQ0FBQyxHQUFHLEVBQUUsSUFBSSxDQUFDLEVBQUU7WUFDMUQsR0FBRyxTQUFTLENBQUMsSUFBSTtZQUNqQixFQUFFO1lBQ0YsV0FBVztZQUNYLEdBQUcsU0FBUyxDQUFDLE9BQU87WUFDcEIsR0FBRyxDQUFDLEtBQUssS0FBSyxRQUFRLENBQUMsQ0FBQyxDQUFDLENBQUMsUUFBUSxPQUFPLENBQUMsSUFBSSxFQUFFLEVBQUUsU0FBUyxPQUFPLENBQUMsS0FBSyxFQUFFLENBQUMsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDO1lBQ2pGLGFBQ0UsQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLFVBQVUsQ0FBQyxFQUFFLEdBQUcsT0FBTyxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQyxLQUFLLEVBQUUsRUFBRSxDQUFDLEtBQUssQ0FBQyxLQUFLLEVBQUUsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxHQUFHLENBQzFGLEVBQUU7WUFDRixvRUFBb0U7WUFDcEUsb0JBQW9CLE9BQU8sQ0FBQyxnQkFBZ0IsQ0FBQyxVQUFVLENBQUMsR0FBRyxFQUFFLElBQUksQ0FBQyxFQUFFO1lBQ3BFLFdBQVcsT0FBTyxDQUFDLE9BQU8sRUFBRTtZQUM1QixHQUFHLENBQUMsT0FBTyxDQUFDLGNBQWMsS0FBSyxTQUFTLENBQUMsQ0FBQyxDQUFDLENBQUMsY0FBYyxPQUFPLENBQUMsY0FBYyxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDO1lBQzFGLFlBQVksT0FBTyxDQUFDLFFBQVEsRUFBRTtZQUM5QixvQkFBb0I7WUFDcEIseUJBQXlCO1lBQ3pCLGlCQUFpQjtZQUNqQixlQUFlO1lBQ2Ysa0JBQWtCLE9BQU8sQ0FBQyxrQkFBa0IsR0FBRztZQUMvQyxHQUFHLE9BQU87WUFDVixvQkFBb0I7WUFDcEIsd0JBQXdCO1lBQ3hCLHVCQUF1QjtZQUN2QixFQUFFO1lBQ0YsV0FBVztZQUNYLEdBQUcsU0FBUyxDQUFDLE9BQU87WUFDcEIsRUFBRTtTQUNILENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDO1FBQ2IsWUFBWSxDQUFDLE1BQU0sQ0FBQyxVQUFVLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxJQUFJLEtBQUssQ0FBQyxDQUFDO1FBQ3ZELElBQUksQ0FBQyxNQUFNLEdBQUcsT0FBTyxDQUFDLE1BQU0sQ0FBQyxVQUFVLENBQUMsUUFBUSxDQUFDLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsQ0FBQyxNQUFNLENBQUMsS0FBSyxDQUFDLENBQUM7UUFDckYsTUFBTSxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsQ0FBQztJQUN0QixDQUFDO0NBQ0YifQ==
289
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5zeXN0ZW1kc2VydmljZWRlZmluaXRpb24uanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9jbGFzc2VzLnN5c3RlbWRzZXJ2aWNlZGVmaW5pdGlvbi50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLDBCQUEwQixDQUFDO0FBQ3BELE9BQU8sRUFBRSxvQkFBb0IsRUFBRSx3QkFBd0IsRUFBRSxXQUFXLEVBQUUsc0JBQXNCLEVBQUUsc0JBQXNCLEVBQ2xILG1CQUFtQixFQUNrRSxNQUFNLHlCQUF5QixDQUFDO0FBSXZILE9BQU8sRUFBRSxtQkFBbUIsRUFBRSxzQkFBc0IsRUFBRSxNQUFNLHlCQUF5QixDQUFDO0FBMkZ0RixNQUFNLE9BQU8sNkJBQThCLFNBQVEsS0FBSztJQUd0RCxZQUFZLE1BQXVDO1FBQ2pELEtBQUssQ0FBQyw4Q0FBOEMsTUFBTSxHQUFHLENBQUMsQ0FBQztRQUMvRCxJQUFJLENBQUMsSUFBSSxHQUFHLCtCQUErQixDQUFDO1FBQzVDLElBQUksQ0FBQyxNQUFNLEdBQUcsTUFBTSxDQUFDO0lBQ3ZCLENBQUM7Q0FDRjtBQUVELDZFQUE2RTtBQUM3RSxNQUFNLGVBQWUsR0FBRztJQUN0QixDQUFDLFlBQVksRUFBRSxZQUFZLENBQUM7SUFDNUIsQ0FBQyxnQkFBZ0IsRUFBRSxnQkFBZ0IsQ0FBQztJQUNwQyxDQUFDLHVCQUF1QixFQUFFLHVCQUF1QixDQUFDO0lBQ2xELENBQUMsc0JBQXNCLEVBQUUsc0JBQXNCLENBQUM7SUFDaEQsQ0FBQyxtQkFBbUIsRUFBRSxtQkFBbUIsQ0FBQztJQUMxQyxDQUFDLHNCQUFzQixFQUFFLHNCQUFzQixDQUFDO0lBQ2hELENBQUMsY0FBYyxFQUFFLGNBQWMsQ0FBQztJQUNoQyxDQUFDLGlCQUFpQixFQUFFLGlCQUFpQixDQUFDO0lBQ3RDLENBQUMsb0JBQW9CLEVBQUUsb0JBQW9CLENBQUM7SUFDNUMsQ0FBQyxrQkFBa0IsRUFBRSxr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
@@ -27,6 +27,29 @@ export interface ISystemdServiceInstallTargets {
27
27
  wantedBy: readonly string[];
28
28
  requiredBy: readonly string[];
29
29
  }
30
+ /**
31
+ * One credential a system service reads from `$CREDENTIALS_DIRECTORY/<name>` (`LoadCredential=` or
32
+ * `LoadCredentialEncrypted=`). The value never appears in the unit, its environment or
33
+ * `systemctl show`; it is readable by the service's user only.
34
+ */
35
+ export interface ISystemdServiceCredential {
36
+ /** The credential's name inside the service: printable ASCII from `[A-Za-z0-9_.@-]`, 1–255 characters, not starting with `.`. */
37
+ name: string;
38
+ /**
39
+ * Where systemd reads it: an absolute normalized path (a file, a directory of files or an
40
+ * `AF_UNIX` socket), or a credential name that systemd searches among its own credentials and
41
+ * in the credstore directories (`/etc/credstore/`, `/run/credstore/`, `/usr/lib/credstore/`, and
42
+ * for encrypted credentials also their `.encrypted` counterparts). Omitted, systemd searches
43
+ * under `name` itself.
44
+ */
45
+ source?: string;
46
+ }
47
+ /** Linux capabilities systemd 255 knows by name, for `capabilityBoundingSet` and `ambientCapabilities`. */
48
+ export declare const systemdCapabilities: readonly ["CAP_AUDIT_CONTROL", "CAP_AUDIT_READ", "CAP_AUDIT_WRITE", "CAP_BLOCK_SUSPEND", "CAP_BPF", "CAP_CHECKPOINT_RESTORE", "CAP_CHOWN", "CAP_DAC_OVERRIDE", "CAP_DAC_READ_SEARCH", "CAP_FOWNER", "CAP_FSETID", "CAP_IPC_LOCK", "CAP_IPC_OWNER", "CAP_KILL", "CAP_LEASE", "CAP_LINUX_IMMUTABLE", "CAP_MAC_ADMIN", "CAP_MAC_OVERRIDE", "CAP_MKNOD", "CAP_NET_ADMIN", "CAP_NET_BIND_SERVICE", "CAP_NET_BROADCAST", "CAP_NET_RAW", "CAP_PERFMON", "CAP_SETFCAP", "CAP_SETGID", "CAP_SETPCAP", "CAP_SETUID", "CAP_SYSLOG", "CAP_SYS_ADMIN", "CAP_SYS_BOOT", "CAP_SYS_CHROOT", "CAP_SYS_MODULE", "CAP_SYS_NICE", "CAP_SYS_PACCT", "CAP_SYS_PTRACE", "CAP_SYS_RAWIO", "CAP_SYS_RESOURCE", "CAP_SYS_TIME", "CAP_SYS_TTY_CONFIG", "CAP_WAKE_ALARM"];
49
+ export type TSystemdCapability = typeof systemdCapabilities[number];
50
+ /** Socket address families a definition may allow with `restrictAddressFamilies`. */
51
+ export declare const systemdAddressFamilies: readonly ["AF_INET", "AF_INET6", "AF_NETLINK", "AF_PACKET", "AF_UNIX", "AF_VSOCK"];
52
+ export type TSystemdAddressFamily = typeof systemdAddressFamilies[number];
30
53
  /**
31
54
  * Internal bounds of a definition's `environment`: variables, and UTF-8 bytes of `NAME=value` in
32
55
  * total. SystemdUnit sizes its systemctl output limit from them, so every accepted definition reads back.
@@ -46,7 +69,7 @@ export declare const renderServiceRelations: (options: {
46
69
  startup?: TSystemdServiceStartup;
47
70
  dependencies?: ISystemdServiceDependencies;
48
71
  install?: ISystemdServiceInstallTargets;
49
- }, unitName: string, restart: string, scope: "system" | "user", fail: () => never) => {
72
+ }, unitName: string, restart: string, scope: "system" | "user", fail: (option: "startup" | "dependencies" | "install") => never) => {
50
73
  unit: string[];
51
74
  service: string[];
52
75
  install: string[];
@@ -1,4 +1,17 @@
1
1
  import * as plugins from './smartdaemon.plugins.js';
2
+ /** Linux capabilities systemd 255 knows by name, for `capabilityBoundingSet` and `ambientCapabilities`. */
3
+ export const systemdCapabilities = [
4
+ 'CAP_AUDIT_CONTROL', 'CAP_AUDIT_READ', 'CAP_AUDIT_WRITE', 'CAP_BLOCK_SUSPEND', 'CAP_BPF',
5
+ 'CAP_CHECKPOINT_RESTORE', 'CAP_CHOWN', 'CAP_DAC_OVERRIDE', 'CAP_DAC_READ_SEARCH', 'CAP_FOWNER',
6
+ 'CAP_FSETID', 'CAP_IPC_LOCK', 'CAP_IPC_OWNER', 'CAP_KILL', 'CAP_LEASE', 'CAP_LINUX_IMMUTABLE',
7
+ 'CAP_MAC_ADMIN', 'CAP_MAC_OVERRIDE', 'CAP_MKNOD', 'CAP_NET_ADMIN', 'CAP_NET_BIND_SERVICE',
8
+ 'CAP_NET_BROADCAST', 'CAP_NET_RAW', 'CAP_PERFMON', 'CAP_SETFCAP', 'CAP_SETGID', 'CAP_SETPCAP',
9
+ 'CAP_SETUID', 'CAP_SYSLOG', 'CAP_SYS_ADMIN', 'CAP_SYS_BOOT', 'CAP_SYS_CHROOT', 'CAP_SYS_MODULE',
10
+ 'CAP_SYS_NICE', 'CAP_SYS_PACCT', 'CAP_SYS_PTRACE', 'CAP_SYS_RAWIO', 'CAP_SYS_RESOURCE',
11
+ 'CAP_SYS_TIME', 'CAP_SYS_TTY_CONFIG', 'CAP_WAKE_ALARM',
12
+ ];
13
+ /** Socket address families a definition may allow with `restrictAddressFamilies`. */
14
+ export const systemdAddressFamilies = ['AF_INET', 'AF_INET6', 'AF_NETLINK', 'AF_PACKET', 'AF_UNIX', 'AF_VSOCK'];
2
15
  /**
3
16
  * Internal bounds of a definition's `environment`: variables, and UTF-8 bytes of `NAME=value` in
4
17
  * total. SystemdUnit sizes its systemctl output limit from them, so every accepted definition reads back.
@@ -19,8 +32,10 @@ export const isCleanText = (value) => [...value].every((character) => {
19
32
  });
20
33
  /** Internal validated renderer; no systemd/process/filesystem effects. */
21
34
  export const renderServiceRelations = (options, unitName, restart, scope, fail) => {
35
+ // Every check names the option it belongs to; `option` is set before each option is read.
36
+ let option = 'startup';
22
37
  const requireValue = (value) => { if (!value)
23
- fail(); };
38
+ fail(option); };
24
39
  const plain = (value) => requireValue(value !== null && typeof value === 'object'
25
40
  && !plugins.util.types.isProxy(value) && Object.getPrototypeOf(value) === Object.prototype);
26
41
  const record = (value, keys) => {
@@ -69,6 +84,7 @@ export const renderServiceRelations = (options, unitName, restart, scope, fail)
69
84
  }
70
85
  }
71
86
  if (options.dependencies !== undefined) {
87
+ option = 'dependencies';
72
88
  const value = options.dependencies;
73
89
  record(value, ['defaultDependencies', 'after', 'before', 'requires', 'wants', 'conflicts']);
74
90
  requireValue(typeof value.defaultDependencies === 'boolean');
@@ -79,6 +95,7 @@ export const renderServiceRelations = (options, unitName, restart, scope, fail)
79
95
  ...line('Before', before), ...line('Requires', requires), ...line('Wants', wants), ...line('Conflicts', conflicts)];
80
96
  }
81
97
  if (options.install !== undefined) {
98
+ option = 'install';
82
99
  const value = options.install;
83
100
  record(value, ['wantedBy', 'requiredBy']);
84
101
  const wanted = units(value.wantedBy), required = units(value.requiredBy);
@@ -87,4 +104,4 @@ export const renderServiceRelations = (options, unitName, restart, scope, fail)
87
104
  }
88
105
  return result;
89
106
  };
90
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3lzdGVtZC5kZWZpbml0aW9uLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvc3lzdGVtZC5kZWZpbml0aW9uLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0sMEJBQTBCLENBQUM7QUFvQ3BEOzs7R0FHRztBQUNILE1BQU0sQ0FBQyxNQUFNLHdCQUF3QixHQUFHLEVBQUUsQ0FBQztBQUMzQyxNQUFNLENBQUMsTUFBTSxvQkFBb0IsR0FBRyxJQUFJLENBQUM7QUFFekM7Ozs7OztHQU1HO0FBQ0gsTUFBTSxDQUFDLE1BQU0sV0FBVyxHQUFHLENBQUMsS0FBYSxFQUFFLEVBQUUsQ0FBQyxDQUFDLEdBQUcsS0FBSyxDQUFDLENBQUMsS0FBSyxDQUFDLENBQUMsU0FBUyxFQUFFLEVBQUU7SUFDM0UsTUFBTSxLQUFLLEdBQUcsU0FBUyxDQUFDLFdBQVcsQ0FBQyxDQUFDLENBQUUsQ0FBQztJQUN4QyxPQUFPLEtBQUssR0FBRyxJQUFJLElBQUksS0FBSyxLQUFLLElBQUksSUFBSSxDQUFDLEtBQUssR0FBRyxNQUFNLElBQUksS0FBSyxHQUFHLE1BQU0sQ0FBQztRQUN6RSxDQUFDLEtBQUssR0FBRyxNQUFNLElBQUksS0FBSyxHQUFHLE1BQU0sQ0FBQyxJQUFJLENBQUMsS0FBSyxHQUFHLE1BQU0sQ0FBQyxLQUFLLE1BQU0sQ0FBQztBQUN0RSxDQUFDLENBQUMsQ0FBQztBQUVILDBFQUEwRTtBQUMxRSxNQUFNLENBQUMsTUFBTSxzQkFBc0IsR0FBRyxDQUFDLE9BSXRDLEVBQUUsUUFBZ0IsRUFBRSxPQUFlLEVBQUUsS0FBd0IsRUFBRSxJQUFpQixFQUUvRSxFQUFFO0lBQ0YsTUFBTSxZQUFZLEdBQUcsQ0FBQyxLQUFjLEVBQVEsRUFBRSxHQUFHLElBQUksQ0FBQyxLQUFLO1FBQUUsSUFBSSxFQUFFLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFDdkUsTUFBTSxLQUFLLEdBQUcsQ0FBQyxLQUFjLEVBQVEsRUFB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
107
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3lzdGVtZC5kZWZpbml0aW9uLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvc3lzdGVtZC5kZWZpbml0aW9uLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0sMEJBQTBCLENBQUM7QUFzRHBELDJHQUEyRztBQUMzRyxNQUFNLENBQUMsTUFBTSxtQkFBbUIsR0FBRztJQUNqQyxtQkFBbUIsRUFBRSxnQkFBZ0IsRUFBRSxpQkFBaUIsRUFBRSxtQkFBbUIsRUFBRSxTQUFTO0lBQ3hGLHdCQUF3QixFQUFFLFdBQVcsRUFBRSxrQkFBa0IsRUFBRSxxQkFBcUIsRUFBRSxZQUFZO0lBQzlGLFlBQVksRUFBRSxjQUFjLEVBQUUsZUFBZSxFQUFFLFVBQVUsRUFBRSxXQUFXLEVBQUUscUJBQXFCO0lBQzdGLGVBQWUsRUFBRSxrQkFBa0IsRUFBRSxXQUFXLEVBQUUsZUFBZSxFQUFFLHNCQUFzQjtJQUN6RixtQkFBbUIsRUFBRSxhQUFhLEVBQUUsYUFBYSxFQUFFLGFBQWEsRUFBRSxZQUFZLEVBQUUsYUFBYTtJQUM3RixZQUFZLEVBQUUsWUFBWSxFQUFFLGVBQWUsRUFBRSxjQUFjLEVBQUUsZ0JBQWdCLEVBQUUsZ0JBQWdCO0lBQy9GLGNBQWMsRUFBRSxlQUFlLEVBQUUsZ0JBQWdCLEVBQUUsZUFBZSxFQUFFLGtCQUFrQjtJQUN0RixjQUFjLEVBQUUsb0JBQW9CLEVBQUUsZ0JBQWdCO0NBQzlDLENBQUM7QUFHWCxxRkFBcUY7QUFDckYsTUFBTSxDQUFDLE1BQU0sc0JBQXNCLEdBQUcsQ0FBQyxTQUFTLEVBQUUsVUFBVSxFQUFFLFlBQVksRUFBRSxXQUFXLEVBQUUsU0FBUyxFQUFFLFVBQVUsQ0FBVSxDQUFDO0FBR3pIOzs7R0FHRztBQUNILE1BQU0sQ0FBQyxNQUFNLHdCQUF3QixHQUFHLEVBQUUsQ0FBQztBQUMzQyxNQUFNLENBQUMsTUFBTSxvQkFBb0IsR0FBRyxJQUFJLENBQUM7QUFFekM7Ozs7OztHQU1H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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@push.rocks/smartdaemon",
3
- "version": "2.12.0",
3
+ "version": "2.13.0",
4
4
  "private": false,
5
5
  "description": "Start scripts as long running daemons and manage them.",
6
6
  "main": "dist_ts/index.js",
package/readme.md CHANGED
@@ -464,6 +464,93 @@ and `%` doubled, so systemd applies no specifier; `Environment=` never expands `
464
464
  taken literally. An empty object renders nothing. Systemd merges these variables over its own
465
465
  (`HOME`, `USER`, `INVOCATION_ID`, …), so a name such as `NOTIFY_SOCKET` overrides the manager's value.
466
466
 
467
+ #### Credentials, a dynamic user and the sandbox
468
+
469
+ A system service can receive secrets as systemd credentials instead of variables, run as a
470
+ transient account and be confined. Each option below renders nothing when omitted, so existing
471
+ definitions keep their exact bytes; `false` renders the directive as `no`. A user service refuses
472
+ all of them, since a user manager cannot grant them. `DynamicUser=` follows `Group=`; the others
473
+ follow the `environment` lines in this order:
474
+
475
+ | Option | Directive | Accepted values |
476
+ | --- | --- | --- |
477
+ | `dynamicUser` | `DynamicUser=` | boolean; with `true`, `user` and `group` name the allocated account |
478
+ | `loadCredentials` | one `LoadCredential=` line per credential, ordered by name | array of `{ name, source? }` |
479
+ | `loadCredentialsEncrypted` | one `LoadCredentialEncrypted=` line per credential, ordered by name | array of `{ name, source? }` |
480
+ | `noNewPrivileges` | `NoNewPrivileges=` | boolean |
481
+ | `protectSystem` | `ProtectSystem=` | boolean, `'full'` or `'strict'` |
482
+ | `protectHome` | `ProtectHome=` | boolean, `'read-only'` or `'tmpfs'` |
483
+ | `privateTmp`, `privateDevices`, `protectKernelTunables`, `protectKernelModules`, `protectKernelLogs`, `protectControlGroups`, `protectClock`, `protectHostname`, `restrictNamespaces`, `restrictRealtime`, `restrictSUIDSGID`, `lockPersonality` | the directive of the same name, in this order | boolean |
484
+ | `capabilityBoundingSet` | `CapabilityBoundingSet=` | distinct capabilities from `systemdCapabilities`, sorted; empty drops every capability |
485
+ | `ambientCapabilities` | `AmbientCapabilities=` | distinct capabilities, sorted; within `capabilityBoundingSet` when both are given |
486
+ | `restrictAddressFamilies` | `RestrictAddressFamilies=` | at least one distinct family of `AF_INET`, `AF_INET6`, `AF_NETLINK`, `AF_PACKET`, `AF_UNIX`, `AF_VSOCK`, sorted; includes `AF_UNIX` for a notify service |
487
+
488
+ ```typescript
489
+ const relay = new SystemdServiceDefinition({
490
+ unitName: 'relay.service',
491
+ description: 'Cluster relay',
492
+ executable: '/opt/relay/current/relay',
493
+ args: [],
494
+ workingDirectory: '/opt/relay/current',
495
+ user: 'relay',
496
+ group: 'relay',
497
+ dynamicUser: true,
498
+ restart: 'always',
499
+ restartSeconds: 2,
500
+ killMode: 'mixed',
501
+ timeoutStopSeconds: 60,
502
+ dependencies: {
503
+ defaultDependencies: true,
504
+ after: ['network-online.target'],
505
+ before: ['node.service'],
506
+ requires: [],
507
+ wants: ['network-online.target'],
508
+ conflicts: [],
509
+ },
510
+ environment: { RELAY_BIND_PORT: '8443' },
511
+ loadCredentials: [{ name: 'RELAY_AUTHORIZATION' }],
512
+ noNewPrivileges: true,
513
+ protectSystem: 'strict',
514
+ protectHome: true,
515
+ privateTmp: true,
516
+ privateDevices: true,
517
+ capabilityBoundingSet: [],
518
+ restrictAddressFamilies: ['AF_INET', 'AF_INET6', 'AF_UNIX'],
519
+ });
520
+ // DynamicUser=yes
521
+ // LoadCredential=RELAY_AUTHORIZATION
522
+ // CapabilityBoundingSet=
523
+ // RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX
524
+ ```
525
+
526
+ A credential's value never appears in the unit, in the service's environment (so child processes
527
+ do not inherit it) or in `systemctl show`: the service reads it from
528
+ `$CREDENTIALS_DIRECTORY/<name>`, readable by the service's user only. `name` is 1–255 characters of
529
+ `[A-Za-z0-9_.@-]`, not starting with `.`. `source` is either an absolute normalized path of
530
+ `[A-Za-z0-9_.@/+-]` characters without a trailing `/` (a file, a directory of files or an `AF_UNIX`
531
+ socket), or a credential name that systemd looks up among its own credentials and then in
532
+ `/etc/credstore/`, `/run/credstore/` and `/usr/lib/credstore/` (for encrypted credentials also the
533
+ `credstore.encrypted` directories). Without `source`, systemd looks up `name` itself, so
534
+ `{ name: 'RELAY_AUTHORIZATION' }` reads `/etc/credstore/RELAY_AUTHORIZATION`; a `source` equal to
535
+ `name` is refused, as it only spells the same lookup twice. These characters need no quoting and
536
+ hold no `%` specifier, `:` separator or escape, so systemd reads each line exactly as written.
537
+ Encrypted credentials are sealed with `systemd-creds encrypt` (host key or TPM2). Systemd keeps both
538
+ kinds in one table by name, so a name appears in at most one list; at most 64 per list. The
539
+ directory search needs systemd 250 or later; an absolute `source` works from systemd 247.
540
+
541
+ `dynamicUser: true` allocates the account when the service starts. Systemd would use a static
542
+ account of the same name instead, so `user` and `group` must not be `root`, and they have at most
543
+ 31 characters, the longest name systemd allocates. For such a service systemd enforces
544
+ `ProtectSystem=strict`, `PrivateTmp=yes`, `NoNewPrivileges=yes`, `RestrictSUIDSGID=yes` and at least
545
+ `ProtectHome=read-only`; a definition that states any of them weaker is refused, so the unit never
546
+ reads as less confined than it is.
547
+
548
+ Each refusal throws `SystemdServiceDefinitionError`, whose `option` names the offending option (for
549
+ every option of a definition, not only these) and whose message is
550
+ `Invalid direct systemd service definition: <option>.`. `option` is `'options'` for the definition
551
+ object itself: not a plain object, an unknown or accessor key, or a unit over 16384 bytes. No
552
+ refusal carries the offending value.
553
+
467
554
  Optional `dependencies` describes the complete literal runtime relationships:
468
555
 
469
556
  ```typescript
@@ -3,6 +3,6 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: '@push.rocks/smartdaemon',
6
- version: '2.12.0',
6
+ version: '2.13.0',
7
7
  description: 'Start scripts as long running daemons and manage them.'
8
8
  }
@@ -1,8 +1,11 @@
1
1
  import * as plugins from './smartdaemon.plugins.js';
2
- import { environmentByteLimit, environmentVariableLimit, isCleanText, renderServiceRelations, type TSystemdServiceStartup,
3
- type ISystemdServiceDependencies, type ISystemdServiceInstallTargets } from './systemd.definition.js';
2
+ import { environmentByteLimit, environmentVariableLimit, isCleanText, renderServiceRelations, systemdAddressFamilies,
3
+ systemdCapabilities, type TSystemdServiceStartup, type ISystemdServiceDependencies, type ISystemdServiceInstallTargets,
4
+ type ISystemdServiceCredential, type TSystemdCapability, type TSystemdAddressFamily } from './systemd.definition.js';
4
5
  export type { ISystemdServiceStartup, ISystemdServiceNotifyStartup, TSystemdServiceStartup,
5
- ISystemdServiceDependencies, ISystemdServiceInstallTargets } from './systemd.definition.js';
6
+ ISystemdServiceDependencies, ISystemdServiceInstallTargets, ISystemdServiceCredential, TSystemdCapability,
7
+ TSystemdAddressFamily } from './systemd.definition.js';
8
+ export { systemdCapabilities, systemdAddressFamilies } from './systemd.definition.js';
6
9
 
7
10
  interface ISystemdServiceDefinitionBaseOptions {
8
11
  unitName: string;
@@ -33,6 +36,53 @@ interface ISystemdServiceDefinitionBaseOptions {
33
36
  * 64 variables and 8192 UTF-8 bytes of `NAME=value` in total. They are taken literally: `%` is escaped, and `Environment=` never expands `$`.
34
37
  */
35
38
  environment?: Readonly<Record<string, string>>;
39
+ /**
40
+ * System scope only. Runs the service as a transient user and group allocated at start
41
+ * (`DynamicUser=`), named by `user` and `group`, which then must not be `root` and have at most
42
+ * 31 characters. Systemd enforces `ProtectSystem=strict`, `PrivateTmp=`, `NoNewPrivileges=` and
43
+ * `RestrictSUIDSGID=` for such a service, so a definition that weakens any of them is refused.
44
+ */
45
+ dynamicUser?: boolean;
46
+ /** System scope only. Credentials read in plaintext, one `LoadCredential=` line each, ordered by name. */
47
+ loadCredentials?: readonly ISystemdServiceCredential[];
48
+ /** System scope only. Credentials sealed with `systemd-creds encrypt`, one `LoadCredentialEncrypted=` line each, ordered by name. */
49
+ loadCredentialsEncrypted?: readonly ISystemdServiceCredential[];
50
+ /** System scope only (`NoNewPrivileges=`). */
51
+ noNewPrivileges?: boolean;
52
+ /** System scope only (`ProtectSystem=`): `true` mounts `/usr` and the boot loader read-only, `'full'` also `/etc`, `'strict'` the whole file system but the API file systems. */
53
+ protectSystem?: boolean | 'full' | 'strict';
54
+ /** System scope only (`ProtectHome=`). */
55
+ protectHome?: boolean | 'read-only' | 'tmpfs';
56
+ /** System scope only (`PrivateTmp=`). */
57
+ privateTmp?: boolean;
58
+ /** System scope only (`PrivateDevices=`). */
59
+ privateDevices?: boolean;
60
+ /** System scope only (`ProtectKernelTunables=`). */
61
+ protectKernelTunables?: boolean;
62
+ /** System scope only (`ProtectKernelModules=`). */
63
+ protectKernelModules?: boolean;
64
+ /** System scope only (`ProtectKernelLogs=`). */
65
+ protectKernelLogs?: boolean;
66
+ /** System scope only (`ProtectControlGroups=`). */
67
+ protectControlGroups?: boolean;
68
+ /** System scope only (`ProtectClock=`). */
69
+ protectClock?: boolean;
70
+ /** System scope only (`ProtectHostname=`). */
71
+ protectHostname?: boolean;
72
+ /** System scope only (`RestrictNamespaces=`): `true` forbids creating every kind of namespace. */
73
+ restrictNamespaces?: boolean;
74
+ /** System scope only (`RestrictRealtime=`). */
75
+ restrictRealtime?: boolean;
76
+ /** System scope only (`RestrictSUIDSGID=`). */
77
+ restrictSUIDSGID?: boolean;
78
+ /** System scope only (`LockPersonality=`). */
79
+ lockPersonality?: boolean;
80
+ /** System scope only. The only capabilities the service's processes can ever hold (`CapabilityBoundingSet=`); empty drops all. */
81
+ capabilityBoundingSet?: readonly TSystemdCapability[];
82
+ /** System scope only. Capabilities a non-root service's processes receive (`AmbientCapabilities=`); within `capabilityBoundingSet` when both are given. */
83
+ ambientCapabilities?: readonly TSystemdCapability[];
84
+ /** System scope only. The only socket families the service may create (`RestrictAddressFamilies=`); at least one, and `AF_UNIX` for a notify service. */
85
+ restrictAddressFamilies?: readonly TSystemdAddressFamily[];
36
86
  }
37
87
 
38
88
  export type ISystemdServiceDefinitionOptions = ISystemdServiceDefinitionBaseOptions & (
@@ -40,13 +90,43 @@ export type ISystemdServiceDefinitionOptions = ISystemdServiceDefinitionBaseOpti
40
90
  { scope: 'user'; user?: never; group?: never }
41
91
  );
42
92
 
93
+ /**
94
+ * The option a refusal names: a key of the definition, or `'options'` for the definition object
95
+ * itself (not a plain object, an unknown or accessor key, or a unit over 16384 bytes).
96
+ */
97
+ export type TSystemdServiceDefinitionOption = keyof ISystemdServiceDefinitionOptions | 'options';
98
+
43
99
  export class SystemdServiceDefinitionError extends Error {
44
- constructor() {
45
- super('Invalid direct systemd service definition.');
100
+ public readonly option: TSystemdServiceDefinitionOption;
101
+
102
+ constructor(option: TSystemdServiceDefinitionOption) {
103
+ super(`Invalid direct systemd service definition: ${option}.`);
46
104
  this.name = 'SystemdServiceDefinitionError';
105
+ this.option = option;
47
106
  }
48
107
  }
49
108
 
109
+ /** Sandboxing switches in their rendering order, each with its directive. */
110
+ const sandboxSwitches = [
111
+ ['privateTmp', 'PrivateTmp'],
112
+ ['privateDevices', 'PrivateDevices'],
113
+ ['protectKernelTunables', 'ProtectKernelTunables'],
114
+ ['protectKernelModules', 'ProtectKernelModules'],
115
+ ['protectKernelLogs', 'ProtectKernelLogs'],
116
+ ['protectControlGroups', 'ProtectControlGroups'],
117
+ ['protectClock', 'ProtectClock'],
118
+ ['protectHostname', 'ProtectHostname'],
119
+ ['restrictNamespaces', 'RestrictNamespaces'],
120
+ ['restrictRealtime', 'RestrictRealtime'],
121
+ ['restrictSUIDSGID', 'RestrictSUIDSGID'],
122
+ ['lockPersonality', 'LockPersonality'],
123
+ ] as const;
124
+
125
+ /** Options a user manager's service cannot carry: they need the system manager's privileges. */
126
+ const systemOnlyOptions = ['dynamicUser', 'loadCredentials', 'loadCredentialsEncrypted', 'noNewPrivileges',
127
+ 'protectSystem', 'protectHome', ...sandboxSwitches.map(([option]) => option), 'capabilityBoundingSet',
128
+ 'ambientCapabilities', 'restrictAddressFamilies'] as const;
129
+
50
130
  /** Literal direct-execution definition; never introduces a shell or environment expansion. */
51
131
  export class SystemdServiceDefinition {
52
132
  public readonly unitName: string;
@@ -55,26 +135,27 @@ export class SystemdServiceDefinition {
55
135
  public readonly sha256: string;
56
136
 
57
137
  constructor(options: ISystemdServiceDefinitionOptions) {
58
- const requireValue = (value: unknown): void => {
59
- if (!value) throw new SystemdServiceDefinitionError();
138
+ const fail = (option: TSystemdServiceDefinitionOption): never => {
139
+ throw new SystemdServiceDefinitionError(option);
140
+ };
141
+ const requireValue = (value: unknown, option: TSystemdServiceDefinitionOption): void => {
142
+ if (!value) fail(option);
60
143
  };
61
144
  requireValue(options && !plugins.util.types.isProxy(options) &&
62
- Object.getPrototypeOf(options) === Object.prototype);
145
+ Object.getPrototypeOf(options) === Object.prototype, 'options');
63
146
  const scopeDescriptor = Object.getOwnPropertyDescriptor(options, 'scope');
64
147
  requireValue(scopeDescriptor === undefined ||
65
- scopeDescriptor.enumerable && 'value' in scopeDescriptor);
148
+ scopeDescriptor.enumerable && 'value' in scopeDescriptor, 'scope');
66
149
  const scopeValue: unknown = scopeDescriptor?.value ?? 'system';
67
- if (scopeValue !== 'system' && scopeValue !== 'user') {
68
- throw new SystemdServiceDefinitionError();
69
- }
70
- const scope = scopeValue;
150
+ if (scopeValue !== 'system' && scopeValue !== 'user') fail('scope');
151
+ const scope = scopeValue as 'system' | 'user';
71
152
  // Systemd ignores an assignment whose text is not clean UTF-8, leaving the unit unloadable.
72
153
  const text = (value: unknown, maximum = 4096): value is string =>
73
154
  typeof value === 'string' && value.length <= maximum && isCleanText(value);
74
- const absolute = (value: unknown) =>
155
+ const absolute = (value: unknown): value is string =>
75
156
  text(value) && plugins.path.isAbsolute(value) &&
76
157
  plugins.path.normalize(value) === value;
77
- const keys = [
158
+ const keys: TSystemdServiceDefinitionOption[] = [
78
159
  'unitName',
79
160
  'description',
80
161
  'executable',
@@ -83,31 +164,40 @@ export class SystemdServiceDefinition {
83
164
  'restart',
84
165
  'killMode',
85
166
  'timeoutStopSeconds',
86
- ...(scope === 'system' ? ['user', 'group'] : []),
167
+ ...(scope === 'system' ? ['user', 'group'] as const : []),
87
168
  ];
88
- const optionalKeys = ['scope', 'startup', 'dependencies', 'install', 'restartSeconds', 'delegate',
89
- 'limitNoFile', 'tasksMax', 'oomScoreAdjust', 'environment'];
90
- requireValue(
91
- Reflect.ownKeys(options).every(key => typeof key === 'string' && [...keys, ...optionalKeys].includes(key)
92
- && Object.getOwnPropertyDescriptor(options, key)?.enumerable && 'value' in Object.getOwnPropertyDescriptor(options, key)!) && keys.every((key) => {
93
- const descriptor = Object.getOwnPropertyDescriptor(options, key);
94
- return descriptor && 'value' in descriptor;
95
- }),
96
- );
169
+ const optionalKeys: TSystemdServiceDefinitionOption[] = ['scope', 'startup', 'dependencies', 'install',
170
+ 'restartSeconds', 'delegate', 'limitNoFile', 'tasksMax', 'oomScoreAdjust', 'environment',
171
+ ...systemOnlyOptions];
172
+ // Every own key is a known option held by an enumerable data property, so no accessor or trap runs.
173
+ for (const key of Reflect.ownKeys(options)) {
174
+ requireValue(typeof key === 'string' && [...keys, ...optionalKeys, 'user', 'group'].includes(key as never), 'options');
175
+ const option = key as TSystemdServiceDefinitionOption;
176
+ const descriptor = Object.getOwnPropertyDescriptor(options, key);
177
+ requireValue(keys.includes(option) || optionalKeys.includes(option), option);
178
+ requireValue(descriptor?.enumerable && 'value' in descriptor, option);
179
+ }
180
+ for (const key of keys) requireValue(Object.getOwnPropertyDescriptor(options, key), key);
97
181
  requireValue(
98
182
  typeof options.unitName === 'string' &&
99
183
  /^[a-zA-Z0-9][a-zA-Z0-9_.-]{0,230}\.service$/.test(options.unitName),
184
+ 'unitName',
100
185
  );
101
186
  requireValue(
102
187
  text(options.description, 1024) && options.description.length > 0 &&
103
188
  options.description.trim() === options.description && !options.description.includes('\\'),
189
+ 'description',
104
190
  );
105
191
  requireValue(
106
192
  absolute(options.executable) && options.executable !== '/' &&
107
- !/[\\"']/.test(options.executable) && !options.executable.endsWith('/') &&
108
- absolute(options.workingDirectory) &&
193
+ !/[\\"']/.test(options.executable) && !options.executable.endsWith('/'),
194
+ 'executable',
195
+ );
196
+ requireValue(
197
+ absolute(options.workingDirectory) &&
109
198
  options.workingDirectory.trim() === options.workingDirectory &&
110
199
  !options.workingDirectory.includes('\\'),
200
+ 'workingDirectory',
111
201
  );
112
202
  requireValue(
113
203
  Array.isArray(options.args) && !plugins.util.types.isProxy(options.args) && Object.getPrototypeOf(options.args) === Array.prototype &&
@@ -118,43 +208,44 @@ export class SystemdServiceDefinition {
118
208
  (_, index) => Object.getOwnPropertyDescriptor(options.args, String(index)),
119
209
  )
120
210
  .every((descriptor) => descriptor && 'value' in descriptor && text(descriptor.value)),
211
+ 'args',
121
212
  );
122
213
  if (scope === 'system') {
123
- requireValue(
124
- [options.user, options.group].every((value) =>
125
- typeof value === 'string' && /^[a-zA-Z_][a-zA-Z0-9_-]{0,63}$/.test(value)
126
- ),
127
- );
214
+ for (const key of ['user', 'group'] as const) {
215
+ requireValue(typeof options[key] === 'string' && /^[a-zA-Z_][a-zA-Z0-9_-]{0,63}$/.test(options[key]), key);
216
+ }
128
217
  }
218
+ requireValue(['no', 'on-failure', 'always'].includes(options.restart), 'restart');
219
+ requireValue(['mixed', 'control-group', 'process'].includes(options.killMode), 'killMode');
129
220
  requireValue(
130
- ['no', 'on-failure', 'always'].includes(options.restart) &&
131
- ['mixed', 'control-group', 'process'].includes(options.killMode) &&
132
- Number.isSafeInteger(options.timeoutStopSeconds) &&
221
+ Number.isSafeInteger(options.timeoutStopSeconds) &&
133
222
  options.timeoutStopSeconds >= 1 && options.timeoutStopSeconds <= 600,
223
+ 'timeoutStopSeconds',
134
224
  );
135
225
  const integer = (value: unknown, minimum: number, maximum: number): value is number =>
136
226
  Number.isSafeInteger(value) && (value as number) >= minimum && (value as number) <= maximum;
137
227
  const limit = (value: unknown): value is number | 'infinity' =>
138
228
  value === 'infinity' || integer(value, 1, Number.MAX_SAFE_INTEGER);
229
+ const yesNo = (value: boolean) => value ? 'yes' : 'no';
139
230
  // Each optional directive is absent unless supplied, so existing definitions keep their bytes.
140
231
  const service: string[] = [];
141
232
  if (options.restartSeconds !== undefined) {
142
- requireValue(integer(options.restartSeconds, 1, 600) && options.restart !== 'no');
233
+ requireValue(integer(options.restartSeconds, 1, 600) && options.restart !== 'no', 'restartSeconds');
143
234
  }
144
235
  if (options.delegate !== undefined) {
145
- requireValue(typeof options.delegate === 'boolean');
146
- service.push(`Delegate=${options.delegate ? 'yes' : 'no'}`);
236
+ requireValue(typeof options.delegate === 'boolean', 'delegate');
237
+ service.push(`Delegate=${yesNo(options.delegate)}`);
147
238
  }
148
239
  if (options.limitNoFile !== undefined) {
149
- requireValue(limit(options.limitNoFile));
240
+ requireValue(limit(options.limitNoFile), 'limitNoFile');
150
241
  service.push(`LimitNOFILE=${options.limitNoFile}`);
151
242
  }
152
243
  if (options.tasksMax !== undefined) {
153
- requireValue(limit(options.tasksMax));
244
+ requireValue(limit(options.tasksMax), 'tasksMax');
154
245
  service.push(`TasksMax=${options.tasksMax}`);
155
246
  }
156
247
  if (options.oomScoreAdjust !== undefined) {
157
- requireValue(integer(options.oomScoreAdjust, -1000, 1000));
248
+ requireValue(integer(options.oomScoreAdjust, -1000, 1000), 'oomScoreAdjust');
158
249
  service.push(`OOMScoreAdjust=${options.oomScoreAdjust}`);
159
250
  }
160
251
  // Quoting and percent/dollar escaping follow systemd.syntax and systemd.service.
@@ -163,26 +254,135 @@ export class SystemdServiceDefinition {
163
254
  .replaceAll('\\', '\\\\').replaceAll('"', '\\"').replaceAll('%', '%%')
164
255
  .replaceAll('$', () => command ? '$$' : '$') +
165
256
  '"';
257
+ const plainObject = (value: unknown, option: TSystemdServiceDefinitionOption) =>
258
+ requireValue(value !== null && typeof value === 'object' && !plugins.util.types.isProxy(value) &&
259
+ Object.getPrototypeOf(value) === Object.prototype, option);
166
260
  if (options.environment !== undefined) {
167
261
  // Read each variable once through its own data descriptor, so no accessor or trap runs.
168
262
  const variables: unknown = options.environment;
169
- requireValue(variables !== null && typeof variables === 'object' &&
170
- !plugins.util.types.isProxy(variables) && Object.getPrototypeOf(variables) === Object.prototype);
263
+ plainObject(variables, 'environment');
171
264
  const names = Reflect.ownKeys(variables as object);
172
- requireValue(names.length <= environmentVariableLimit);
265
+ requireValue(names.length <= environmentVariableLimit, 'environment');
173
266
  const assignments = new Map<string, string>();
174
267
  for (const name of names) {
175
268
  const descriptor = Object.getOwnPropertyDescriptor(variables, name);
176
269
  requireValue(typeof name === 'string' && /^[A-Za-z_][A-Za-z0-9_]{0,127}$/.test(name) &&
177
- descriptor?.enumerable && 'value' in descriptor && text(descriptor.value));
270
+ descriptor?.enumerable && 'value' in descriptor && text(descriptor.value), 'environment');
178
271
  assignments.set(name as string, `${name as string}=${descriptor!.value as string}`);
179
272
  }
180
- requireValue(Buffer.byteLength([...assignments.values()].join('')) <= environmentByteLimit);
273
+ requireValue(Buffer.byteLength([...assignments.values()].join('')) <= environmentByteLimit, 'environment');
181
274
  // Environment= unquotes and unescapes each word and expands `%` specifiers, never `$`.
182
275
  service.push(...[...assignments.keys()].sort()
183
276
  .map((name) => `Environment=${quote(assignments.get(name)!)}`));
184
277
  }
185
- const relations = renderServiceRelations(options, options.unitName, options.restart, scope, () => { throw new SystemdServiceDefinitionError(); });
278
+ // Every system-only option is refused by name in a user manager's service.
279
+ for (const option of systemOnlyOptions) {
280
+ requireValue(scope === 'system' || options[option] === undefined, option);
281
+ }
282
+ /** Reads a plain array once through its data descriptors: distinct items, sorted. */
283
+ const list = <T>(value: unknown, option: TSystemdServiceDefinitionOption, maximum: number,
284
+ item: (entry: unknown) => T, identity: (entry: T) => string): T[] => {
285
+ requireValue(Array.isArray(value) && !plugins.util.types.isProxy(value) &&
286
+ Object.getPrototypeOf(value) === Array.prototype && value.length <= maximum &&
287
+ Reflect.ownKeys(value).length === value.length + 1, option);
288
+ const result = Array.from({ length: (value as unknown[]).length }, (_, index) => {
289
+ const descriptor = Object.getOwnPropertyDescriptor(value, String(index));
290
+ requireValue(descriptor?.enumerable && 'value' in descriptor, option);
291
+ return item(descriptor!.value);
292
+ });
293
+ const identities = result.map(identity);
294
+ requireValue(new Set(identities).size === identities.length, option);
295
+ return result.sort((left, right) => identity(left) < identity(right) ? -1 : 1);
296
+ };
297
+ // Credential names and sources hold no `%`, quote, backslash, whitespace or `:`, so systemd's
298
+ // `NAME[:SOURCE]` parser and its specifier expansion read them exactly as written.
299
+ const credentialName = (value: unknown): value is string =>
300
+ typeof value === 'string' && /^[A-Za-z0-9_@-][A-Za-z0-9_.@-]{0,254}$/.test(value);
301
+ const credentials = (value: unknown, option: 'loadCredentials' | 'loadCredentialsEncrypted') =>
302
+ list(value, option, 64, (entry): ISystemdServiceCredential => {
303
+ plainObject(entry, option);
304
+ const entryKeys = Reflect.ownKeys(entry as object);
305
+ requireValue(entryKeys.includes('name') && entryKeys.every((key) => (key === 'name' || key === 'source') &&
306
+ Object.getOwnPropertyDescriptor(entry, key)?.enumerable &&
307
+ 'value' in Object.getOwnPropertyDescriptor(entry, key)!), option);
308
+ const { name, source } = entry as Record<'name' | 'source', unknown>;
309
+ requireValue(credentialName(name), option);
310
+ if (entryKeys.includes('source')) {
311
+ requireValue(source !== name && (credentialName(source) || absolute(source) && !source.endsWith('/') &&
312
+ /^[A-Za-z0-9_.@/+-]+$/.test(source)), option);
313
+ }
314
+ return entryKeys.includes('source') ? { name: name as string, source: source as string } : { name: name as string };
315
+ }, (entry) => entry.name);
316
+ const plainCredentials = options.loadCredentials === undefined ? [] : credentials(options.loadCredentials, 'loadCredentials');
317
+ const encryptedCredentials = options.loadCredentialsEncrypted === undefined
318
+ ? [] : credentials(options.loadCredentialsEncrypted, 'loadCredentialsEncrypted');
319
+ // Systemd keeps both kinds in one table by name, so a name loads from exactly one of them.
320
+ requireValue(encryptedCredentials.every((entry) => !plainCredentials.some(({ name }) => name === entry.name)),
321
+ 'loadCredentialsEncrypted');
322
+ const credential = ({ name, source }: ISystemdServiceCredential) => source === undefined ? name : `${name}:${source}`;
323
+ service.push(...plainCredentials.map((entry) => `LoadCredential=${credential(entry)}`));
324
+ service.push(...encryptedCredentials.map((entry) => `LoadCredentialEncrypted=${credential(entry)}`));
325
+ const dynamicUser = options.dynamicUser;
326
+ if (dynamicUser !== undefined) {
327
+ requireValue(typeof dynamicUser === 'boolean', 'dynamicUser');
328
+ // A static account of the same name would be used instead, so the name must not be root's,
329
+ // and systemd allocates only names that fit utmp.
330
+ requireValue(!dynamicUser || [options.user, options.group].every((name) =>
331
+ name !== 'root' && typeof name === 'string' && name.length <= 31), 'dynamicUser');
332
+ }
333
+ // Settings systemd forces on for a dynamic user, so the unit never states a weaker one.
334
+ const forced = (option: TSystemdServiceDefinitionOption, weakened: boolean) =>
335
+ requireValue(!(dynamicUser === true && weakened), option);
336
+ if (options.noNewPrivileges !== undefined) {
337
+ requireValue(typeof options.noNewPrivileges === 'boolean', 'noNewPrivileges');
338
+ forced('noNewPrivileges', !options.noNewPrivileges);
339
+ service.push(`NoNewPrivileges=${yesNo(options.noNewPrivileges)}`);
340
+ }
341
+ if (options.protectSystem !== undefined) {
342
+ requireValue([true, false, 'full', 'strict'].includes(options.protectSystem), 'protectSystem');
343
+ forced('protectSystem', options.protectSystem !== 'strict');
344
+ service.push(`ProtectSystem=${typeof options.protectSystem === 'boolean'
345
+ ? yesNo(options.protectSystem) : options.protectSystem}`);
346
+ }
347
+ if (options.protectHome !== undefined) {
348
+ requireValue([true, false, 'read-only', 'tmpfs'].includes(options.protectHome), 'protectHome');
349
+ forced('protectHome', options.protectHome === false);
350
+ service.push(`ProtectHome=${typeof options.protectHome === 'boolean'
351
+ ? yesNo(options.protectHome) : options.protectHome}`);
352
+ }
353
+ for (const [option, directive] of sandboxSwitches) {
354
+ const value = options[option];
355
+ if (value === undefined) continue;
356
+ requireValue(typeof value === 'boolean', option);
357
+ forced(option, (option === 'privateTmp' || option === 'restrictSUIDSGID') && !value);
358
+ service.push(`${directive}=${yesNo(value)}`);
359
+ }
360
+ const capabilities = (value: unknown, option: 'capabilityBoundingSet' | 'ambientCapabilities') =>
361
+ list(value, option, systemdCapabilities.length, (entry) => {
362
+ requireValue((systemdCapabilities as readonly unknown[]).includes(entry), option);
363
+ return entry as TSystemdCapability;
364
+ }, (entry) => entry);
365
+ const bounding = options.capabilityBoundingSet === undefined
366
+ ? undefined : capabilities(options.capabilityBoundingSet, 'capabilityBoundingSet');
367
+ const ambient = options.ambientCapabilities === undefined
368
+ ? undefined : capabilities(options.ambientCapabilities, 'ambientCapabilities');
369
+ // An ambient capability outside the bounding set would fail every start of the service.
370
+ requireValue(!bounding || !ambient || ambient.every((entry) => bounding.includes(entry)), 'ambientCapabilities');
371
+ // An empty CapabilityBoundingSet= empties the bounding set; an empty AmbientCapabilities= grants none.
372
+ if (bounding) service.push(`CapabilityBoundingSet=${bounding.join(' ')}`);
373
+ if (ambient) service.push(`AmbientCapabilities=${ambient.join(' ')}`);
374
+ const families = options.restrictAddressFamilies === undefined ? undefined
375
+ : list(options.restrictAddressFamilies, 'restrictAddressFamilies', systemdAddressFamilies.length, (entry) => {
376
+ requireValue((systemdAddressFamilies as readonly unknown[]).includes(entry), 'restrictAddressFamilies');
377
+ return entry as TSystemdAddressFamily;
378
+ }, (entry) => entry);
379
+ // An empty RestrictAddressFamilies= would lift the restriction instead of denying every family.
380
+ requireValue(!families || families.length > 0, 'restrictAddressFamilies');
381
+ if (families) service.push(`RestrictAddressFamilies=${families.join(' ')}`);
382
+ const relations = renderServiceRelations(options, options.unitName, options.restart, scope, fail);
383
+ // A notify service sends READY=1 over an AF_UNIX socket it creates itself.
384
+ requireValue(!families || !relations.service.includes('Type=notify') || families.includes('AF_UNIX'),
385
+ 'restrictAddressFamilies');
186
386
  this.unitName = options.unitName;
187
387
  this.scope = scope;
188
388
  this.content = [
@@ -193,6 +393,7 @@ export class SystemdServiceDefinition {
193
393
  '[Service]',
194
394
  ...relations.service,
195
395
  ...(scope === 'system' ? [`User=${options.user}`, `Group=${options.group}`] : []),
396
+ ...(dynamicUser !== undefined ? [`DynamicUser=${yesNo(dynamicUser)}`] : []),
196
397
  `ExecStart=${
197
398
  [quote(options.executable), ...options.args.map((value) => quote(value, true))].join(' ')
198
399
  }`,
@@ -215,7 +416,7 @@ export class SystemdServiceDefinition {
215
416
  ...relations.install,
216
417
  '',
217
418
  ].join('\n');
218
- requireValue(Buffer.byteLength(this.content) <= 16384);
419
+ requireValue(Buffer.byteLength(this.content) <= 16384, 'options');
219
420
  this.sha256 = plugins.crypto.createHash('sha256').update(this.content).digest('hex');
220
421
  Object.freeze(this);
221
422
  }
@@ -34,6 +34,41 @@ export interface ISystemdServiceInstallTargets {
34
34
  requiredBy: readonly string[];
35
35
  }
36
36
 
37
+ /**
38
+ * One credential a system service reads from `$CREDENTIALS_DIRECTORY/<name>` (`LoadCredential=` or
39
+ * `LoadCredentialEncrypted=`). The value never appears in the unit, its environment or
40
+ * `systemctl show`; it is readable by the service's user only.
41
+ */
42
+ export interface ISystemdServiceCredential {
43
+ /** The credential's name inside the service: printable ASCII from `[A-Za-z0-9_.@-]`, 1–255 characters, not starting with `.`. */
44
+ name: string;
45
+ /**
46
+ * Where systemd reads it: an absolute normalized path (a file, a directory of files or an
47
+ * `AF_UNIX` socket), or a credential name that systemd searches among its own credentials and
48
+ * in the credstore directories (`/etc/credstore/`, `/run/credstore/`, `/usr/lib/credstore/`, and
49
+ * for encrypted credentials also their `.encrypted` counterparts). Omitted, systemd searches
50
+ * under `name` itself.
51
+ */
52
+ source?: string;
53
+ }
54
+
55
+ /** Linux capabilities systemd 255 knows by name, for `capabilityBoundingSet` and `ambientCapabilities`. */
56
+ export const systemdCapabilities = [
57
+ 'CAP_AUDIT_CONTROL', 'CAP_AUDIT_READ', 'CAP_AUDIT_WRITE', 'CAP_BLOCK_SUSPEND', 'CAP_BPF',
58
+ 'CAP_CHECKPOINT_RESTORE', 'CAP_CHOWN', 'CAP_DAC_OVERRIDE', 'CAP_DAC_READ_SEARCH', 'CAP_FOWNER',
59
+ 'CAP_FSETID', 'CAP_IPC_LOCK', 'CAP_IPC_OWNER', 'CAP_KILL', 'CAP_LEASE', 'CAP_LINUX_IMMUTABLE',
60
+ 'CAP_MAC_ADMIN', 'CAP_MAC_OVERRIDE', 'CAP_MKNOD', 'CAP_NET_ADMIN', 'CAP_NET_BIND_SERVICE',
61
+ 'CAP_NET_BROADCAST', 'CAP_NET_RAW', 'CAP_PERFMON', 'CAP_SETFCAP', 'CAP_SETGID', 'CAP_SETPCAP',
62
+ 'CAP_SETUID', 'CAP_SYSLOG', 'CAP_SYS_ADMIN', 'CAP_SYS_BOOT', 'CAP_SYS_CHROOT', 'CAP_SYS_MODULE',
63
+ 'CAP_SYS_NICE', 'CAP_SYS_PACCT', 'CAP_SYS_PTRACE', 'CAP_SYS_RAWIO', 'CAP_SYS_RESOURCE',
64
+ 'CAP_SYS_TIME', 'CAP_SYS_TTY_CONFIG', 'CAP_WAKE_ALARM',
65
+ ] as const;
66
+ export type TSystemdCapability = typeof systemdCapabilities[number];
67
+
68
+ /** Socket address families a definition may allow with `restrictAddressFamilies`. */
69
+ export const systemdAddressFamilies = ['AF_INET', 'AF_INET6', 'AF_NETLINK', 'AF_PACKET', 'AF_UNIX', 'AF_VSOCK'] as const;
70
+ export type TSystemdAddressFamily = typeof systemdAddressFamilies[number];
71
+
37
72
  /**
38
73
  * Internal bounds of a definition's `environment`: variables, and UTF-8 bytes of `NAME=value` in
39
74
  * total. SystemdUnit sizes its systemctl output limit from them, so every accepted definition reads back.
@@ -59,10 +94,13 @@ export const renderServiceRelations = (options: {
59
94
  startup?: TSystemdServiceStartup;
60
95
  dependencies?: ISystemdServiceDependencies;
61
96
  install?: ISystemdServiceInstallTargets;
62
- }, unitName: string, restart: string, scope: 'system' | 'user', fail: () => never): {
97
+ }, unitName: string, restart: string, scope: 'system' | 'user',
98
+ fail: (option: 'startup' | 'dependencies' | 'install') => never): {
63
99
  unit: string[]; service: string[]; install: string[];
64
100
  } => {
65
- const requireValue = (value: unknown): void => { if (!value) fail(); };
101
+ // Every check names the option it belongs to; `option` is set before each option is read.
102
+ let option: 'startup' | 'dependencies' | 'install' = 'startup';
103
+ const requireValue = (value: unknown): void => { if (!value) fail(option); };
66
104
  const plain = (value: unknown): void => requireValue(value !== null && typeof value === 'object'
67
105
  && !plugins.util.types.isProxy(value) && Object.getPrototypeOf(value) === Object.prototype);
68
106
  const record = (value: unknown, keys: readonly string[]): void => {
@@ -110,6 +148,7 @@ export const renderServiceRelations = (options: {
110
148
  }
111
149
  }
112
150
  if (options.dependencies !== undefined) {
151
+ option = 'dependencies';
113
152
  const value = options.dependencies;
114
153
  record(value, ['defaultDependencies', 'after', 'before', 'requires', 'wants', 'conflicts']);
115
154
  requireValue(typeof value.defaultDependencies === 'boolean');
@@ -120,6 +159,7 @@ export const renderServiceRelations = (options: {
120
159
  ...line('Before', before), ...line('Requires', requires), ...line('Wants', wants), ...line('Conflicts', conflicts)];
121
160
  }
122
161
  if (options.install !== undefined) {
162
+ option = 'install';
123
163
  const value = options.install;
124
164
  record(value, ['wantedBy', 'requiredBy']);
125
165
  const wanted = units(value.wantedBy), required = units(value.requiredBy);