1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
|
---
title: JSON User Records
category: Interfaces
layout: default
---
# JSON User Records
systemd optionally processes user records that go beyond the classic UNIX (or
glibc NSS) `struct passwd`. Various components of systemd are able to provide
and consume records in a more extensible format of a dictionary of key/value
pairs, encoded as JSON. Specifically:
1. [`systemd-homed.service`](https://www.freedesktop.org/software/systemd/man/systemd-homed.service.html)
manages `human` user home directories and embeds these JSON records
directly in the home directory images (see [Home
Directories](https://systemd.io/HOME_DIRECTORY)) for details.
2. [`pam_systemd`](https://www.freedesktop.org/software/systemd/man/pam_systemd.html)
processes these JSON records for users that log in, and applies various
settings to the activated session, including environment variables, nice
levels and more.
3. [`systemd-logind.service`](https://www.freedesktop.org/software/systemd/man/systemd-logind.service.html)
processes these JSON records of users that log in, and applies various
resource management settings to the per-user slice units it manages. This
allows setting global limits on resource consumption by a specific user.
4. [`nss-systemd`](https://www.freedesktop.org/software/systemd/man/nss-systemd.html)
is a glibc NSS module that synthesizes classic NSS records from these JSON
records, providing full backwards compatibility with the classic UNIX APIs
both for look-up and enumeration.
5. The service manager (PID 1) exposes dynamic users (i.e. users synthesized as
effect of `DynamicUser=` in service unit files) as these advanced JSON
records, making them discoverable to the rest of the system.
6. [`systemd-userdbd.service`](https://www.freedesktop.org/software/systemd/man/systemd-userdbd.service.html)
is a small service that can translate UNIX/glibc NSS records to these JSON
user records. It also provides a unified [Varlink](https://varlink.org/) API
for querying and enumerating records of this type, optionally acquiring them
from various other services.
JSON user records may contain various fields that are not available in `struct
passwd`, and are extensible for other applications. For example, the record may
contain information about:
1. Additional security credentials (PKCS#11 security token information,
biometrical authentication information, SSH public key information)
2. Additional user metadata, such as a picture, email address, location string,
preferred language or timezone
3. Resource Management settings (such as CPU/IO weights, memory and tasks
limits, classic UNIX resource limits or nice levels)
4. Runtime parameters such as environment variables or the `nodev`, `noexec`,
`nosuid` flags to use for the home directory
5. Information about where to mount the home directory from
And various other things. The record is intended to be extensible, for example
the following extensions are envisioned:
1. Windows network credential information
2. Information about default IMAP, SMTP servers to use for this user
3. Parental control information to enforce on this user
4. Default parameters for backup applications and similar
Similar to JSON User Records there are also [JSON Group
Records](https://systemd.io/GROUP_RECORD) that encapsulate UNIX groups.
JSON User Records may be transferred or written to disk in various protocols
and formats. To inquire about such records defined on the local system use the
[User/Group Lookup API via Varlink](https://systemd.io/USER_GROUP_API).
## Why JSON?
JSON is nicely extensible and widely used. In particular it's easy to
synthesize and process with numerous programming languages. It's particularly
popular in the web communities, which hopefully should make it easy to link
user credential data from the web and from local systems more closely together.
## General Structure
The JSON user records generated and processed by systemd follow a general
structure, consisting of seven distinct "sections". Specifically:
1. Various fields are placed at the top-level of user record (the `regular`
section). These are generally fields that shall apply unconditionally to the
user in all contexts, are portable and not security sensitive.
2. A number of fields are located in the `privileged` section (a sub-object of
the user record). Fields contained in this object are security sensitive,
i.e. contain information that the user and the administrator should be able
to see, but other users should not. In many ways this matches the data
stored in `/etc/shadow` in classic Linux user accounts, i.e. includes
password hashes and more. Algorithmically, when a user record is passed to
an untrusted client, by monopolizing such sensitive records in a single
object field we can easily remove it from view.
3. A number of fields are located in objects inside the `perMachine` section
(an array field of the user record). Primarily these are resource
management-related fields, as those tend to make sense on a specific system
only, e.g. limiting a user's memory use to 1G only makes sense on a specific
system that has more than 1G of memory. Each object inside the `perMachine`
array comes with a `matchMachineId` or `matchHostname` field which indicate
which systems to apply the listed settings to. Note that many fields
accepted in the `perMachine` section can also be set at the top level (the
`regular` section), where they define the fallback if no matching object in
`perMachine` is found.
4. Various fields are located in the `binding` section (a sub-sub-object of the
user record; an intermediary object is inserted which is keyed by the
machine ID of the host). Fields included in this section "bind" the object
to a specific system. They generally include non-portable information about
paths or UID assignments, that are true on a specific system, but not
necessarily on others, and which are managed automatically by some user
record manager (such as `systemd-homed`). Data in this section is considered
part of the user record only in the local context, and is generally not
ported to other systems. Due to that it is not included in the reduced user
record the cryptographic signature defined in the `signature` section is
calculated on. In `systemd-homed` this section is also removed when the
user's record is stored in the `~/.identity` file in the home directory, so
that every system with access to the home directory can manage these
`binding` fields individually. Typically, the binding section is persisted
to the local disk.
5. Various fields are located in the `status` section (a sub-sub-object of the
user record, also with an intermediary object between that is keyed by the
machine ID, similar to the way the `binding` section is organized). This
section is augmented during runtime only, and never persisted to disk. The
idea is that this section contains information about current runtime
resource usage (for example: currently used disk space of the user), that
changes dynamically but is otherwise immediately associated with the user
record and for many purposes should be considered to be part of the user
record.
6. The `signature` section contains one or more cryptographic signatures of a
reduced version of the user record. This is used to ensure that only user
records defined by a specific source are accepted on a system, by validating
the signature against the set of locally accepted signature public keys. The
signature is calculated from the JSON user record with all sections removed,
except for `regular`, `privileged`, `perMachine`. Specifically, `binding`,
`status`, `signature` itself and `secret` are removed first and thus not
covered by the signature. This section is optional, and is only used when
cryptographic validation of user records is required (as it is by
`systemd-homed.service` for example).
7. The `secret` section contains secret user credentials, such as password or
PIN information. This data is never persisted, and never returned when user
records are inquired by a client, privileged or not. This data should only
be included in a user record very briefly, for example when certain very
specific operations are executed. For example, in tools such as
`systemd-homed` this section may be included in user records, when creating
a new home directory, as passwords and similar credentials need to be
provided to encrypt the home directory with.
Here's a tabular overview of the sections and their properties:
| Section | Included in Signature | Persistent | Security Sensitive | Contains Host-Specific Data |
|------------|-----------------------|------------|--------------------|-----------------------------|
| regular | yes | yes | no | no |
| privileged | yes | yes | yes | no |
| perMachine | yes | yes | no | yes |
| binding | no | yes | no | yes |
| status | no | no | no | yes |
| signature | no | yes | no | no |
| secret | no | no | yes | no |
Note that services providing user records to the local system are free to
manage only a subset of these sections and never include the others in
them. For example, a service that has no concept of signed records (for example
because the records it manages are inherently trusted anyway) does not have to
bother with the `signature` section. A service that only defines records in a
strictly local context and without signatures doesn't have to deal with the
`perMachine` or `binding` sections and can include its data exclusively in the
regular section. A service that uses a separate, private channel for
authenticating users (or that doesn't have a concept of authentication at all)
does not need to to be concerned with the `secret` section of user records, as
the fields included therein are only useful when executing authentication
operations natively against JSON user records.
The `systemd-homed` manager uses all seven sections for various
purposes. Inside the home directories (and if the LUKS2 backend is used, also
in the LUKS2 header) a user record containing the `regular`, `privileged`,
`perMachine` and `signature` sections is stored. `systemd-homed` also stores a
version of the record on the host, with the same four sections and augmented
with an additional, fifth `binding` section. When a local client enquires about
a user record managed by `systemd-homed` the service will add in some
additional information about the user and home directory in the `status`
section — this version is only transferred via IPC and never written to
disk. Finally the `secret` section is used during authentication operations via
IPC to transfer the user record along with its authentication tokens in one go.
## Fields in the `regular` section
As mentioned, the `regular` section's fields are placed at the top level
object. The following fields are currently defined:
`userName` → The UNIX user name for this record. Takes a string with a valid
UNIX user name. This field is the only mandatory field, all others are
optional. Corresponds with the `pw_name` field of of `struct passwd` and the
`sp_namp` field of `struct spwd` (i.e. the shadow user record stored in
`/etc/shadow`).
`realm` → The "realm" a user is defined in. This concept allows distinguishing
users with the same name that originate in different organizations or
installations. This should take a string in DNS domain syntax, but doesn't have
to refer to an actual DNS domain (though it is recommended to use one for
this). The idea is that the user `lpoetter` in the `redhat.com` realm might be
distinct from the same user in the `poettering.hq` realm. User records for the
same user name that have different realm fields are considered referring to
different users. When updating a user record it is required that any new
version has to match in both `userName` and `realm` field. This field is
optional, when unset the user should not be considered part of any realm. A
user record with a realm set is never compatible (for the purpose of updates,
see above) with a user record without one set, even if the `userName` field matches.
`realName` → The real name of the user, a string. This should contain the user's
real ("human") name, and corresponds loosely to the GECOS field of classic UNIX
user records. When converting a `struct passwd` to a JSON user record this
field is initialized from GECOS (i.e. the `pw_gecos` field), and vice versa
when converting back. That said, unlike GECOS this field is supposed to contain
only the real name and no other information.
`emailAddress` → The email address of the user, formatted as
string. [`pam_systemd`](https://www.freedesktop.org/software/systemd/man/pam_systemd.html)
initializes the `$EMAIL` environment variable from this value for all login
sessions.
`iconName` → The name of an icon picked by the user, for example for the
purpose of an avatar. This must be a string, and should follow the semantics
defined in the [Icon Naming
Specification](https://standards.freedesktop.org/icon-naming-spec/icon-naming-spec-latest.html).
`location` → A free-form location string describing the location of the user,
if that is applicable. It's probably wise to use a location string processable
by geo-location subsystems, but this is not enforced nor required. Example:
`Berlin, Germany` or `Basement, Room 3a`.
`disposition` → A string, one of `intrinsic`, `system`, `dynamic`, `regular`,
`container`, `reserved`. If specified clarifies the disposition of the user,
i.e. the context it is defined in. For regular, "human" users this should be
`regular`, for system users (i.e. users that system services run under, and
similar) this should be `system`. The `intrinsic` disposition should be used
only for the two users that have special meaning to the OS kernel itself,
i.e. the `root` and `nobody` users. The `container` string should be used for
users that are used by an OS container, and hence will show up in `ps` listings
and such, but are only defined in container context. Finally `reserved` should
be used for any users outside of these use-cases. Note that this property is
entirely optional and applications are assumed to be able to derive the
disposition of a user automatically from a record even in absence of this
field, based on other fields, for example the numeric UID. By setting this
field explicitly applications can override this default determination.
`lastChangeUSec` → An unsigned 64bit integer value, referring to a timestamp in µs
since the epoch 1970, indicating when the user record (specifically, any of the
`regular`, `privileged`, `perMachine` sections) was last changed. This field is
used when comparing two records of the same user to identify the newer one, and
is used for example for automatic updating of user records, where appropriate.
`lastPasswordChangeUSec` → Similar, also an unsigned 64bit integer value,
indicating the point in time the password (or any authentication token) of the
user was last changed. This corresponds to the `sp_lstchg` field of `struct
spwd`, i.e. the matching field in the user shadow database `/etc/shadow`,
though provides finer resolution.
`shell` → A string, referring to the shell binary to use for terminal logins of
this user. This corresponds with the `pw_shell` field of `struct passwd`, and
should contain an absolute file system path. For system users not suitable for
terminal log-in this field should not be set.
`umask` → The `umask` to set for the user's login sessions. Takes an
integer. Note that usually on UNIX the umask is noted in octal, but JSON's
integers are generally written in decimal, hence in this context we denote it
umask in decimal too. The specified value should be in the valid range for
umasks, i.e. 0000…0777 (in octal as typical in UNIX), or 0…511 (in decimal, how
it actually appears in the JSON record). This `umask` is automatically set by
[`pam_systemd`](https://www.freedesktop.org/software/systemd/man/pam_systemd.html)
for all login sessions of the user.
`environment` → An array of strings, each containing an environment variable
and its value to set for the user's login session, in a format compatible with
[`putenv()`](http://man7.org/linux/man-pages/man3/putenv.3.html). Any
environment variable listed here is automatically set by
[`pam_systemd`](https://www.freedesktop.org/software/systemd/man/pam_systemd.html)
for all login sessions of the user.
`timeZone` → A string indicating a preferred timezone to use for the user. When
logging in
[`pam_systemd`](https://www.freedesktop.org/software/systemd/man/pam_systemd.html)
will automatically initialize the `$TZ` environment variable from this
string. The string should be a `tzdata` compatible location string, for
example: `Europe/Berlin`.
`preferredLanguage` → A string indicating the preferred language/locale for the
user. When logging in
[`pam_systemd`](https://www.freedesktop.org/software/systemd/man/pam_systemd.html)
will automatically initialize the `$LANG` environment variable from this
string. The string hence should be in a format compatible with this environment
variable, for example: `de_DE.UTF8`.
`niceLevel` → An integer value in the range -20…19. When logging in
[`pam_systemd`](https://www.freedesktop.org/software/systemd/man/pam_systemd.html)
will automatically initialize the login process' nice level to this value with,
which is then inherited by all the user's processes, see
[`setpriority()`](http://man7.org/linux/man-pages/man2/setpriority.2.html) for
more information.
`resourceLimits` → An object, where each key refers to a Linux resource limit
(such as `RLIMIT_NOFILE` and similar). Their values should be an object with
two keys `cur` and `max` for the soft and hard resource limit. When logging in
[`pam_systemd`](https://www.freedesktop.org/software/systemd/man/pam_systemd.html)
will automatically initialize the login process' resource limits to these
values, which is then inherited by all the user's processes, see
[`setrlimit()`](http://man7.org/linux/man-pages/man2/setrlimit.2.html) for more
information.
`locked` → A boolean value. If true the user account is locked, the user may
not log in. If this field is missing it should be assumed to be false,
i.e. logins are permitted. This field corresponds to the `sp_expire` field of
`struct spwd` (i.e. the `/etc/shadow` data for a user) being set to zero or
one.
`notBeforeUSec` → An unsigned 64bit integer value, indicating a time in µs since
the UNIX epoch (1970) before which the record should be considered invalid for
the purpose of logging in.
`notAfterUSec` → Similar, but indicates the point in time *after* which logins
shall not be permitted anymore. This corresponds to the `sp_expire` field of
`struct spwd`, when it is set to a value larger than one, but provides finer
granularity.
`storage` → A string, one of `classic`, `luks`, `directory`, `subvolume`,
`fscrypt`, `cifs`. Indicates the storage mechanism for the user's home
directory. If `classic` the home directory is a plain directory as in classic
UNIX. When `directory`, the home directory is a regular directory, but the
`~/.identity` file in it contains the user's user record, so that the directory
is self-contained. Similar, `subvolume` is a `btrfs` subvolume that also
contains a `~/.identity` user record; `fscrypt` is an `fscrypt`-encrypted
directory, also containing the `~/.identity` user record; `luks` is a per-user
LUKS volume that is mounted as home directory, and `cifs` a home directory
mounted from a Windows File Share. The five latter types are primarily used by
`systemd-homed` when managing home directories, but may be used if other
managers are used too. If this is not set `classic` is the implied default.
`diskSize` → An unsigned 64bit integer, indicating the intended home directory
disk space in bytes to assign to the user. Depending on the selected storage
type this might be implement differently: for `luks` this is the intended size
of the file system and LUKS volume, while for the others this likely translates
to classic file system quota settings.
`diskSizeRelative` → Similar to `diskSize` but takes a relative value, but
specifies a fraction of the available disk space on the selected storage medium
to assign to the user. This unsigned integer value is normalized to 2^32 =
100%.
`skeletonDirectory` → Takes a string with the absolute path to the skeleton
directory to populate a new home directory from. This is only used when a home
directory is first created, and defaults to `/etc/skel` if not defined.
`accessMode` → Takes an unsigned integer in the range 0…511 indicating the UNIX
access mask for the home directory when it is first created.
`tasksMax` → Takes an unsigned 64bit integer indicating the maximum number of
tasks the user may start in parallel during system runtime. This value is
enforced on all tasks (i.e. processes and threads) the user starts or that are
forked off these processes regardless if the change user identity (for example
by setuid binaries/`su`/`sudo` and
similar). [`systemd-logind.service`](https://www.freedesktop.org/software/systemd/man/systemd-logind.service.html)
enforces this by setting the `TasksMax` slice property for the user's slice
`user-$UID.slice`.
`memoryHigh`/`memoryMax` → These take unsigned 64bit integers indicating upper
memory limits for all processes of the user (plus all processes forked off them
that might have changed user identity), in bytes. Enforced by
[`systemd-logind.service`](https://www.freedesktop.org/software/systemd/man/systemd-logind.service.html),
similar to `tasksMax`.
`cpuWeight`/`ioWeight` → These take unsigned integers in the range 100…10000
and configure the CPU and IO scheduling weights for the user's processes as a
whole. Also enforced by
[`systemd-logind.service`](https://www.freedesktop.org/software/systemd/man/systemd-logind.service.html),
similar to `tasksMax`, `memoryHigh` and `memoryMax`.
`mountNoDevices`/`mountNoSuid`/`mountNoExecute` → Three booleans that control
the `nodev`, `nosuid`, `noexec` mount flags of the user's home
directories. Note that these booleans are only honored if the home directory
is managed by a subsystem such as `systemd-homed.service` that automatically
mounts home directories on login.
`cifsDomain` → A string indicating the Windows File Sharing domain (CIFS) to
use. This is generally useful, but particularly when `cifs` is used as storage
mechanism for the user's home directory, see above.
`cifsUserName` → A string indicating the Windows File Sharing user name (CIFS)
to associate this user record with. This is generally useful, but particularly
useful when `cifs` is used as storage mechanism for the user's home directory,
see above.
`cifsService` → A string indicating the Windows File Share service (CIFS) to
mount as home directory of the user on login.
`imagePath` → A string with an absolute file system path to the file, directory
or block device to use for storage backing the home directory. If the `luks`
storage is used this refers to the loopback file or block device node to store
the LUKS volume on. For `fscrypt`, `directory`, `subvolume` this refers to the
directory to bind mount as home directory on login. Not defined for `classic`
or `cifs`.
`homeDirectory` → A string with an absolute file system path to the home
directory. This is where the image indicated in `imagePath` is mounted to on
login and thus indicates the application facing home directory while the home
directory is active, and is what the user's `$HOME` environment variable is set
to during log-in. It corresponds to the `pw_dir` field of `struct passwd`.
`uid` → An unsigned integer in the range 0…4294967295: the numeric UNIX user ID (UID) to
use for the user. This corresponds to the `pw_uid` field of `struct passwd`.
`gid` → An unsigned integer in the range 0…4294967295: the numeric UNIX group
ID (GID) to use for the user. This corresponds to the `pw_gid` field of
`struct passwd`.
`memberOf` → An array of strings, each indicating a UNIX group this user shall
be a member of. The listed strings must be valid group names, but it is not
required that all groups listed exist in all contexts: any entry for which no
group exists should be silently ignored.
`fileSystemType` → A string, one of `ext4`, `xfs`, `btrfs` (possibly others) to
use as file system for the user's home directory. This is primarily relevant
when the storage mechanism used is `luks` as a file system to use inside the
LUKS container must be selected.
`partitionUuid` → A string containing a lower-case, text-formatted UUID, referencing
the GPT partition UUID the home directory is located in. This is primarily
relevant when the storage mechanism used is `luks`.
`luksUuid` → A string containing a lower-case, text-formatted UUID, referencing
the LUKS volume UUID the home directory is located in. This is primarily
relevant when the storage mechanism used is `luks`.
`fileSystemUuid` → A string containing a lower-case, text-formatted UUID,
referencing the file system UUID the home directory is located in. This is
primarily relevant when the storage mechanism used is `luks`.
`luksDiscard` → A boolean. If true and `luks` storage is used controls whether
the loopback block devices, LUKS and the file system on top shall be used in
`discard` mode, i.e. erased sectors should always be returned to the underlying
storage. If false and `luks` storage is used turns this behavior off. In
addition, depending on this setting an `FITRIM` or `fallocate()` operation is
executed to make sure the image matches the selected option.
`luksCipher` → A string, indicating the cipher to use for the LUKS storage mechanism.
`luksCipherMode` → A string, selecting the cipher mode to use for the LUKS storage mechanism.
`luksVolumeKeySize` → An unsigned integer, indicating the volume key length in
bytes to use for the LUKS storage mechanism.
`luksPbkdfHashAlgorithm` → A string, selecting the hash algorithm to use for
the PBKDF operation for the LUKS storage mechanism.
`luksPbkdfType` → A string, indicating the PBKDF type to use for the LUKS storage mechanism.
`luksPbkdfTimeCostUSec` → An unsigned 64bit integer, indicating the intended
time cost for the PBKDF operation, when the LUKS storage mechanism is used, in
µs.
`luksPbkdfMemoryCost` → An unsigned 64bit integer, indicating the intended
memory cost for the PBKDF operation, when LUKS storage is used, in bytes.
`luksPbkdfParallelThreads` → An unsigned 64bit integer, indicating the intended
required parallel threads for the PBKDF operation, when LUKS storage is used.
`service` → A string declaring the service that defines or manages this user
record. It is recommended to use reverse domain name notation for this. For
example, if `systemd-homed` manages a user a string of `io.systemd.Home` is
used for this.
`rateLimitIntervalUSec` → An unsigned 64bit integer that configures the
authentication rate limiting enforced on the user account. This specifies a
timer interval (in µs) within which to count authentication attempts. When the
counter goes above the value configured n `rateLimitIntervalBurst` log-ins are
temporarily refused until the interval passes.
`rateLimitIntervalBurst` → An unsigned 64bit integer, closely related to
`rateLimitIntervalUSec`, that puts a limit on authentication attempts within
the configured time interval.
`enforcePasswordPolicy` → A boolean. Configures whether to enforce the system's
password policy when creating the home directory for the user or changing the
user's password. By default the policy is enforced, but if this field is false
it is bypassed.
`autoLogin` → A boolean. If true the user record is marked as suitable for
auto-login. Systems are supposed to automatically log in a user marked this way
during boot, if there's exactly one user on it defined this way.
`stopDelayUSec` → An unsigned 64bit integer, indicating the time in µs the
per-user service manager is kept around after the user fully logged out. This
value is honored by
[`systemd-logind.service`](https://www.freedesktop.org/software/systemd/man/systemd-logind.service.html). If
set to zero the per-user service manager is immediately terminated when the
user logs out, and longer values optimize high-frequency log-ins as the
necessary work to set up and tear down a log-in is reduced if the service
manager stays running.
`killProcesses` → A boolean. If true all processes of the user are
automatically killed when the user logs out. This is enforced by
[`systemd-logind.service`](https://www.freedesktop.org/software/systemd/man/systemd-logind.service.html). If
false any processes left around when the user logs out are left running.
`passwordChangeMinUSec`/`passwordChangeMaxUSec` → An unsigned 64bit integer,
encoding how much time has to pass at least/at most between password changes of
the user. This corresponds with the `sp_min` and `sp_max` fields of `struct
spwd` (i.e. the `/etc/shadow` entries of the user), but offers finer
granularity.
`passwordChangeWarnUSec` → An unsigned 64bit integer, encoding how much time to
warn the user before their password expires, in µs. This corresponds with the
`sp_warn` field of `struct spwd`.
`passwordChangeInactiveUSec` → An unsigned 64bit integer, encoding how much
time has to pass after the password expired that the account is
deactivated. This corresponds with the `sp_inact` field of `struct spwd`.
`passwordChangeNow` → A boolean. If true the user has to change their password
on next login. This corresponds with the `sp_lstchg` field of `struct spwd`
being set to zero.
`pkcs11TokenUri` → An array of strings, each with an RFC 7512 compliant PKCS#11
URI referring to security token (or smart card) of some form, that shall be
associated with the user and may be used for authentication. The URI is used to
search for an X.509 certificate and associated private key that may be used to
decrypt an encrypted secret key that is used to unlock the user's account (see
below). It's undefined how precise the URI is: during log-in it is tested
against all plugged in security tokens and if there's exactly one matching
private key found with it it is used.
`privileged` → An object, which contains the fields of he `privileged` section
of the user record, see below.
`perMachine` → An array of objects, which contain the `perMachine` section of
the user record, and thus fields to apply on specific systems only, see below.
`binding` → An object, keyed by machine IDs formatted as strings, pointing
to objects that contain the `binding` section of the user record,
i.e. additional fields that bind the user record to a specific machine, see
below.
`status` → An object, keyed by machine IDs formatted as strings, pointing to
objects that contain the `status` section of the user record, i.e. additional
runtime fields that expose the current status of the user record on a specific
system, see below.
`signature` → An array of objects, which contain cryptographic signatures of
the user record, i.e. the fields of the `signature` section of the user record,
see below.
`secret` → An object, which contains the fields of the `secret` section of the
user record, see below.
## Fields in the `privileged` section
As mentioned, the `privileged` section is encoded in a sub-object of the user
record top-level object, in the `privileged` field. Any data included in this
object shall only be visible to the administrator and the user themselves, and
be suppressed implicitly when other users get access to a user record. It thus
takes the role of the `/etc/shadow` records for each user, which has similarly
restrictive access semantics. The following fields are currently defined:
`passwordHint` → A user-selected password hint in free-form text. This should
be a string like "What's the name of your first pet?", but is entirely for the
user to choose.
`hashPassword` → An array of strings, each containing a hashed UNIX password
string, in the format
[`crypt(3)`](http://man7.org/linux/man-pages/man3/crypt.3.html) generates. This
corresponds with `sp_pwdp` field of `struct spwd` (and in a way the `pw_passwd`
field of `struct passwd`).
`sshAuthorizedKeys` → An array of strings, each listing an SSH public key that
is authorized to access the account. The strings should follow the same format
as the lines in the traditional `~/.ssh/authorized_key` file.
`pkcs11EncryptedKey` → An array of objects. Each element of the array should be
an object consisting of three string fields: `uri` shall contain a PKCS#11
security token URI, `data` shall contain a Base64 encoded encrypted key and
`hashedPassword` shall contain a UNIX password hash to test the key
against. Authenticating with a security token against this account shall work
as follows: the encrypted secret key is converted from its Base64
representation into binary, then decrypted with the PKCS#11 `C_Decrypt()`
function of the PKCS#11 module referenced by the specified URI, using the
private key found on the same token. The resulting decrypted key is then
Base64-encoded and tested against the specified UNIX hashed password. The
Base64-enceded decrypted key may also be used to unlock further resources
during log-in, for example the LUKS or `fscrypt` storage backend. It is
generally recommended that for each entry in `pkcs11EncryptedKey` there's also
a matching one in `pkcs11TokenUri` and vice versa, with the same URI, appearing
in the same order, but this should not be required by applications processing
user records.
## Fields in the `perMachine` section
As mentioned, the `perMachine` section contains settings that shall apply to
specific systems only. This is primarily interesting for resource management
properties as they tend to require a per-system focus, however they may be used
for other purposes too.
The `perMachine` field in the top-level object is an array of objects. When
processing the user record first the various fields on the top-level object
should be used. Then this array should be iterated in order, and the various
settings be applied that match either the indicated machine ID or host
name. There may be multiple array entries that match a specific system, in
which case all the object's setting should be applied. If the same option is
set in the top-level object as in a per-machine object the latter wins and
entirely undoes the setting in the top-level object (i.e. no merging of
properties that are arrays themselves is done). If the same option is set in
multiple per-machine objects the one specified later in the array wins (and
here too no merging of individual fields is done, the later field always wins
in full).
The following fields are defined in this section:
`matchMachineId` → An array of strings with each a formatted 128bit ID in
hex. If any of the specified IDs match the system's local machine ID
(i.e. matches `/etc/machine-id`) the fields in this object are honored.
`matchHostname` → An array of string with a each a valid hostname. If any of
the specified hostnames match the system's local hostname, the fields in this
object are honored. If both `matchHostname` and `matchMachineId` are used
within the same array entry, the object is honored when either match succeeds,
i.e. the two match types are combined in OR, not in AND.
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:
`iconName`, `location`, `shell`, `umask`, `environment`, `timeZone`,
`preferredLanguage`, `niceLevel`, `resourceLimits`, `locked`, `notBeforeUSec`,
`notAfterUSec`, `storage`, `diskSize`, `diskSizeRelative`, `skeletonDirectory`,
`accessMode`, `tasksMax`, `memoryHigh`, `memoryMax`, `cpuWeight`, `ioWeight`,
`mountNoDevices`, `mountNoSuid`, `mountNoExecute`, `cifsDomain`,
`cifsUserName`, `cifsService`, `imagePath`, `uid`, `gid`, `memberOf`,
`fileSystemType`, `partitionUuid`, `luksUuid`, `fileSystemUuid`, `luksDiscard`,
`luksCipher`, `luksCipherMode`, `luksVolumeKeySize`, `luksPbkdfHashAlgorithm`,
`luksPbkdfType`, `luksPbkdfTimeCostUSec`, `luksPbkdfMemoryCost`,
`luksPbkdfParallelThreads`, `rateLimitIntervalUSec`, `rateLimitBurst`,
`enforcePasswordPolicy`, `autoLogin`, `stopDelayUSec`, `killProcesses`,
`passwordChangeMinUSec`, `passwordChangeMaxUSec`, `passwordChangeWarnUSec`,
`passwordChangeInactiveUSec`, `passwordChangeNow`, `pkcs11TokenUri`.
## Fields in the `binding` section
As mentioned, the `binding` section contains additional fields about the user
record, that bind it to the local system. These fields are generally used by a
local user manager (such as `systemd-homed.service`) to add in fields that make
sense in a local context but not necessarily in a global one. For example, a
user record that contains no `uid` field in the regular section is likely
extended with one in the `binding` section to assign a local UID if no global
UID is defined.
All fields in the `binding` section only make sense in a local context and are
suppressed when the user record is ported between systems. The `binding` section
is generally persisted on the system but not in the home directories themselves
and the home directory is supposed to be fully portable and thus not contain
the information that `binding` is supposed to contain that binds the portable
record to a specific system.
The `binding` sub-object on the top-level user record object is keyed by the
machine ID the binding is intended for, which point to an object with the
fields of the bindings. These fields generally match fields that may also be
defined in the `regular` and `perMachine` sections, however override
both. Usually, the `binding` value should not contain settings different from
those set via `regular` or `perMachine`, however this might happen if some
settings are not supported locally (think: `fscrypt` is recorded as intended
storage mechanism in the `regular` section, but the local kernel does not
support `fscrypt`, hence `directory` was chosen as implicit fallback), or have
been changed in the `regular` section through updates (e.g. a home directory
was created with `luks` as storage mechanism but later the user record was
updated to prefer `subvolume`, which however doesn't change the actual storage
used already which is pinned in the `binding` section).
The following fields are defined in the `binding` section. They all have an
identical format and override their equally named counterparts in the `regular`
and `perMachine` sections:
`imagePath`, `homeDirectory`, `partitionUuid`, `luksUuid`, `fileSystemUuid`,
`uid`, `gid`, `storage`, `fileSystemType`, `luksCipher`, `luksCipherMode`,
`luksVolumeKeySize`.
## Fields in the `status` section
As mentioned, the `status` section contains additional fields about the user
record that are exclusively acquired during runtime, and that expose runtime
metrics of the user and similar metadata that shall not be persisted but are
only acquired "on-the-fly" when requested.
This section is arranged similarly to the `binding` section: the `status`
sub-object of the top-level user record object is keyed by the machine ID,
which points to the object with the fields defined here. The following fields
are defined:
`diskUsage` → An unsigned 64bit integer. The currently used disk space of the
home directory in bytes. This value might be determined in different ways,
depending on the selected storage mechanism. For LUKS storage this is the file
size of the loopback file or block device size. For the
directory/subvolume/fscrypt storage this is the current disk space used as
reported by the file system quota subsystem.
`diskFree` → An unsigned 64bit integer, denoting the number of "free" bytes in
the disk space allotment, i.e. usually the difference between the disk size as
reported by `diskSize` and the used already as reported in `diskFree`, but
possibly skewed by metadata sizes, disk compression and similar.
`diskSize` → An unsigned 64bit integer, denoting the disk space currently
allotted to the user, in bytes. Depending on the storage mechanism this can mean
different things (see above). In contrast to the top-level field of the same
(or the one in the `perMachine` section), this field reports the current size
allotted to the user, not the intended one. The values may differ when user
records are updated without the home directory being re-sized.
`diskCeiling`/`diskFloor` → Unsigned 64bit integers indicating upper and lower
bounds when changing the `diskSize` value, in bytes. These values are typically
derived from the underlying data storage, and indicate in which range the home
directory may be re-sized in, i.e. in which sensible range the `diskSize` value
should be kept.
`state` → A string indicating the current state of the home directory. The
precise set of values exposed here are up to the service managing the home
directory to define (i.e. are up to the service identified with the `service`
field below). However, it is recommended to stick to a basic vocabulary here:
`inactive` for a home directory currently not mounted, `absent` for a home
directory that cannot be mounted currently because it does not exist on the
local system, `active` for a home directory that is currently mounted and
accessible.
`service` → A string identifying the service that manages this user record. For
example `systemd-homed.service` sets this to `io.systemd.Home` to all user
records it manages. This is particularly relevant to define clearly the context
in which `state` lives, see above. Note that this field also exists on the
top-level object (i.e. in the `regular` section), which it overrides. The
`regular` field should be used if conceptually the user record can only be
managed by the specified service, and this `status` field if it can
conceptually be managed by different managers, but currently is managed by the
specified one.
`signedLocally` → A boolean. If true indicates that the user record is signed
by a public key for which the private key is available locally. This means that
the user record may be modified locally as it can be re-signed with the private
key. If false indicates that the user record is signed by a public key
recognized by the local manager but whose private key is not available
locally. This means the user record cannot be modified locally as it couldn't
be signed afterwards.
`goodAuthenticationCounter` → An unsigned 64bit integer. This counter is
increased by one on every successful authentication attempt, i.e. an
authentication attempt where a security token of some form was presented and it
was correct.
`badAuthenticationCounter` → An unsigned 64bit integer. This counter is
increased by one on every unsuccessfully authentication attempt, i.e. an
authentication attempt where a security token of some form was presented and it
was incorrect.
`lastGoodAuthenticationUSec` → An unsigned 64bit integer, indicating the time
of the last successful authentication attempt in µs since the UNIX epoch (1970).
`lastBadAuthenticationUSec` → Similar, but the timestamp of the last
unsuccessfully authentication attempt.
`rateLimitBeginUSec` → An unsigned 64bit integer: the µs timestamp since the
UNIX epoch (1970) where the most recent rate limiting interval has been
started, as configured with `rateLimitIntervalUSec`.
`rateLimitCount` → An unsigned 64bit integer, counting the authentication
attempts in the current rate limiting interval, see above. If this counter
grows beyond the value configured in `rateLimitBurst` authentication attempts
are temporarily refused.
`removable` → A boolean value. If true the manager of this user record
determined the home directory being on removable media. If false it was
determined the home directory is in internal built-in media. (This is used by
`systemd-logind.service` to automatically pick the right default value for
`stopDelayUSec` if the field is not explicitly specified: for home directories
on removable media the delay is selected very low to minimize the chance the
home directory remains in unclean state if the storage device is removed from
the system by the user).
## Fields in the `signature` section
As mentioned, the `signature` section of the user record may contain one or
more cryptographic signatures of the user record. Like all others, this section
is optional, and only used when cryptographic validation of user records shall
be used. Specifically, all user records managed by `systemd-homed.service` will
carry such signatures and the service refuses managing user records that come
without signature or with signatures not recognized by any locally defined
public key.
The `signature` field in the top-level user record object is an array of
objects. Each object encapsulates one signature and has two fields: `data` and
`key` (both are strings). The `data` field contains the actual signature,
encoded in base64, the `key` field contains a copy of the public key whose
private key was used to make the signature, in PEM format. Currently only
signatures with Ed25519 keys are defined.
Before signing the user record should be brought into "normalized" form,
i.e. the keys in all objects should be sorted alphabetically. All redundant
white-space and newlines should be removed and the JSON text then signed.
The signatures only cover the `regular`, `perMachine` and `privileged` sections
of the user records, all other sections (include `signature` itself), are
removed before the signature is calculated.
Rationale for signing and threat model: while a multi-user operating system
like Linux strives for being sufficiently secure even after a user acquired a
local login session reality tells us this is not the case. Hence it is
essential to restrict carefully which users may gain access to a system and
which ones shall not. A minimal level of trust must be established between
system, user record and the user themselves before a log-in request may be
permitted. In particular if the home directory is provided in its own LUKS2
encapsulated file system it is essential this trust is established before the
user logs in (and hence the file system mounted), since file system
implementations on Linux are well known to be relatively vulnerable to rogue
disk images. User records and home directories in many context are expected to
be something shareable between multiple systems, and the transfer between them
might not happen via exclusively trusted channels. Hence it's essential that
the user record is not manipulated between uses. Finally, resource management
(which may be done by the various fields of the user record) is security
sensitive, since it should forcefully lock the user into the assigned resource
usage and not allow them to use more. The requirement of being able to trust
the user record data combined with the potential transfer over untrusted
channels suggest a cryptographic signature mechanism where only user records
signed by a recognized key are permitted to log in locally.
Note that other mechanisms for establishing sufficient trust exist too, and are
perfectly valid as well. For example, systems like LDAP/ActiveDirectory
generally insist on user record transfer from trusted servers via encrypted TLS
channels only. Or traditional UNIX users created locally in `/etc/passwd` never
exist outside of the local trusted system, hence transfer and trust in the
source are not an issue. The major benefit of operating with signed user
records is that they are self-sufficiently trusted, not relying on a secure
channel for transfer, and thus being compatible with a more distributed model
of home directory transfer, including on USB sticks and such.
## Fields in the `secret` section
As mentioned, the `secret` section of the user record should never be persisted
nor transferred across machines. It is only defined in short-lived operations,
for example when a user record is first created or registered, as the secret
key data needs to be available to derive encryption keys from and similar.
The `secret` field of the top-level user record contains the following fields:
`password` → an array of strings, each containing a plain text password.
`pkcs11Pin` → an array of strings, each containing a plain text PIN, suitable
for unlocking PKCS#11 security tokens that require that.
`pkcs11ProtectedAuthenticationPathPermitted` → a boolean. If set to true allows
the receiver to use the PKCS#11 "protected authentication path" (i.e. a
physical button/touch element on the security token) for authenticating the
user. If false or unset authentication this way shall not be attempted.
## Mapping to `struct passwd` and `struct spwd`
When mapping classic UNIX user records (i.e. `struct passwd` and `struct spwd`)
to JSON user records the following mappings should be applied:
| Structure | Field | Section | Field | Condition |
|-----------------|-------------|--------------|------------------------------|----------------------------|
| `struct passwd` | `pw_name` | `regular` | `userName` | |
| `struct passwd` | `pw_passwd` | `privileged` | `password` | (See notes below) |
| `struct passwd` | `pw_uid` | `regular` | `uid` | |
| `struct passwd` | `pw_gid` | `regular` | `gid` | |
| `struct passwd` | `pw_gecos` | `regular` | `realName` | |
| `struct passwd` | `pw_dir` | `regular` | `homeDirectory` | |
| `struct passwd` | `pw_shell` | `regular` | `shell` | |
| `struct spwd` | `sp_namp` | `regular` | `userName` | |
| `struct spwd` | `sp_pwdp` | `privileged` | `password` | (See notes below) |
| `struct spwd` | `sp_lstchg` | `regular` | `lastPasswordChangeUSec` | (if `sp_lstchg` > 0) |
| `struct spwd` | `sp_lstchg` | `regular` | `passwordChangeNow` | (if `sp_lstchg` == 0) |
| `struct spwd` | `sp_min` | `regular` | `passwordChangeMinUSec` | |
| `struct spwd` | `sp_max` | `regular` | `passwordChangeMaxUSec` | |
| `struct spwd` | `sp_warn` | `regular` | `passwordChangeWarnUSec` | |
| `struct spwd` | `sp_inact` | `regular` | `passwordChangeInactiveUSec` | |
| `struct spwd` | `sp_expire` | `regular` | `locked` | (if `sp_expire` in [0, 1]) |
| `struct spwd` | `sp_expire` | `regular` | `notAfterUSec` | (if `sp_expire` > 1) |
At this time almost all Linux machines employ shadow passwords, thus the
`pw_passwd` field in `struct passwd` is set to `"x"`, and the actual password
is stored in the shadow entry `struct spwd`'s field `sp_pwdp`.
## Extending These Records
User records following this specifications are supposed to be extendable for
various applications. In general, subsystems are free to introduce their own
keys, as long as:
* Care should be taken to place the keys in the right section, i.e. the most
appropriate for the data field.
* Care should be taken to avoid namespace clashes. Please prefix your fields
with a short identifier of your project to avoid ambiguities and
incompatibilities.
* This specification is supposed to be a living specification. If you need
additional fields, please consider submitting them upstream for inclusion in
this specification. If they are reasonably universally useful, it would be
best to list them here.
## Examples
The shortest valid user record looks like this:
```json
{
"userName" : "u"
}
```
A reasonable user record for a system user might look like this:
```json
{
"userName" : "httpd",
"uid" : 473,
"gid" : 473,
"disposition" : "system",
"locked" : true
}
```
A fully featured user record associated with a home directory managed by
`systemd-homed.service` might look like this:
```json
{
"autoLogin" : true,
"binding" : {
"15e19cf24e004b949ddaac60c74aa165" : {
"fileSystemType" : "ext4",
"fileSystemUuid" : "758e88c8-5851-4a2a-b88f-e7474279c111",
"gid" : 60232,
"homeDirectory" : "/home/grobie",
"imagePath" : "/home/grobie.home",
"luksCipher" : "aes",
"luksCipherMode" : "xts-plain64",
"luksUuid" : "e63581ba-79fb-4226-b9de-1888393f7573",
"luksVolumeKeySize" : 32,
"partitionUuid" : "41f9ce04-c827-4b74-a981-c669f93eb4dc",
"storage" : "luks",
"uid" : 60232
}
},
"disposition" : "regular",
"enforcePasswordPolicy" : false,
"lastChangeUSec" : 1565950024279735,
"memberOf" : [
"wheel"
],
"privileged" : {
"hashedPassword" : [
"$6$WHBKvAFFT9jKPA4k$OPY4D4TczKN/jOnJzy54DDuOOagCcvxxybrwMbe1SVdm.Bbr.zOmBdATp.QrwZmvqyr8/SafbbQu.QZ2rRvDs/"
]
},
"signature" : [
{
"data" : "LU/HeVrPZSzi3MJ0PVHwD5m/xf51XDYCrSpbDRNBdtF4fDVhrN0t2I2OqH/1yXiBidXlV0ptMuQVq8KVICdEDw==",
"key" : "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEA/QT6kQWOAMhDJf56jBmszEQQpJHqDsGDMZOdiptBgRk=\n-----END PUBLIC KEY-----\n"
}
],
"userName" : "grobie",
"status" : {
"15e19cf24e004b949ddaac60c74aa165" : {
"goodAuthenticationCounter" : 16,
"lastGoodAuthenticationUSec" : 1566309343044322,
"rateLimitBeginUSec" : 1566309342340723,
"rateLimitCount" : 1,
"state" : "inactive",
"service" : "io.systemd.Home",
"diskSize" : 161118667776,
"diskCeiling" : 190371729408,
"diskFloor" : 5242880,
"signedLocally" : true
}
}
}
```
When `systemd-homed.service` manages a home directory it will also include a
version of the user record in the home directory itself in the `~/.identity`
file. This version lacks the `binding` and `status` sections which are used for
local management of the user, but are not intended to be portable between
systems. It would hence look like this:
```json
{
"autoLogin" : true,
"disposition" : "regular",
"enforcePasswordPolicy" : false,
"lastChangeUSec" : 1565950024279735,
"memberOf" : [
"wheel"
],
"privileged" : {
"hashedPassword" : [
"$6$WHBKvAFFT9jKPA4k$OPY4D4TczKN/jOnJzy54DDuOOagCcvxxybrwMbe1SVdm.Bbr.zOmBdATp.QrwZmvqyr8/SafbbQu.QZ2rRvDs/"
]
},
"signature" : [
{
"data" : "LU/HeVrPZSzi3MJ0PVHwD5m/xf51XDYCrSpbDRNBdtF4fDVhrN0t2I2OqH/1yXiBidXlV0ptMuQVq8KVICdEDw==",
"key" : "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEA/QT6kQWOAMhDJf56jBmszEQQpJHqDsGDMZOdiptBgRk=\n-----END PUBLIC KEY-----\n"
}
],
"userName" : "grobie",
}
```
|