-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathjanitor.1
More file actions
1286 lines (1263 loc) · 31.6 KB
/
Copy pathjanitor.1
File metadata and controls
1286 lines (1263 loc) · 31.6 KB
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
'\" t
.TH JANITOR 1 "2026-04-21" "janitor 0.1.0" "User Commands"
.SH NAME
janitor \- hierarchical Unix permissions manager with snapshots and ACL support
.SH SYNOPSIS
.B janitor
[\fIGLOBAL OPTIONS\fR]
\fICOMMAND\fR
[\fIPATH\fR]
[\fIOPTIONS\fR]
.SH DESCRIPTION
.B janitor
grants, revokes, audits, and reverts Unix filesystem permissions with a
hierarchical model: granting access to a file also opens the minimum required
traverse-only (\fB\-\-x\fR) bits on every parent directory, so siblings stay
invisible. Every mutating command writes a MessagePack snapshot to
.I /var/lib/janitor/backups
(root) or
.I ~/.local/share/janitor/backups
(non-root) before touching anything, so any change can be reverted exactly with
.BR "janitor restore " ID .
.PP
Symlinks' own permission bits are never modified;
.BR lchown (2)
is used so symlinks are repointed by owner without following them. This
holds for a symlink named directly on the command line as well, which is
where janitor deliberately differs from coreutils: GNU
.BR chmod (1)
and
.BR chown (1)
dereference their operands.
.PP
Most subcommands take the target
.I PATH
as a positional argument, followed by options. Short single-letter flags
are provided for the most common options, following the conventions of
.BR chmod (1)
and
.BR chown (1)
(in particular
.BR \-R
for recursive).
.SH GLOBAL OPTIONS
.TP
.BR \-n ", " \-\-dry\-run
Print what would be done without touching the filesystem. Safe to run as root.
.TP
.BR \-j ", " \-\-json
Emit machine-readable JSON where the subcommand supports it
.RB ( audit ", " find-orphans ", " who-can ", " diff ", " export ", " list-backups ).
.TP
.BR \-h ", " \-\-help
Show help and exit.
.TP
.BR \-V ", " \-\-version
Show version and exit.
.SH COMMAND OVERVIEW
.TS
l l.
\fBCommand\fR \fBPurpose\fR
grant (g) Hierarchical grant with auto-snapshot
revoke (rv) Soft revoke (remove user from managed group)
tree (t) Colored permission tree, honors \fB-U USER\fR
backup (b) Manual snapshot without changes
restore (r) Full revert of a specific backup
undo (u) Restore the most recent backup (one-shot revert)
history (h) Backups whose target contains PATH (newest first)
copy-perms (cp) Copy mode + owner + group (+ ACL) from SRC to DST
list-backups (ls) List saved snapshots (filterable by \fB-p SUBSTR\fR)
prune-backups (prune) Keep the N most recent backups
diff Show what \fBrestore\fR would change
export Dump a backup as text or JSON
chmod chmod (octal + symbolic, setuid/setgid/sticky, \fB--reference\fR) with auto-snapshot
chown chown (\fIuser\fR, \fIuser:group\fR, \fI:group\fR, numeric, \fB--reference\fR) with auto-snapshot
info (i) One-shot summary of a path (type, owner, mode, ACLs)
audit (a) Scan a tree for suspicious / matching permissions
find-orphans Files whose UID / GID is not in /etc/passwd /group
who-can (w) Reverse query: which users can read / write / exec a path
acl POSIX ACL management (\fBgrant\fR|\fBrevoke\fR|\fBshow\fR|\fBstrip\fR)
preset (p) Named presets (\fBpreset list\fR, \fBpreset apply NAME PATH\fR)
seal Lock down a tree to a uniform baseline with surgical pinholes
explain (e) Human-readable read/write/exec verdict for a path
compare Side-by-side diff of two paths (or two trees with \fB\-R\fR)
lock / unlock / locks Per-path mutation guard (prevent accidental changes)
policy Declarative YAML policy (\fBapply\fR | \fBverify\fR)
batch Run many ops from a file under a single snapshot
attr Extended attributes / immutable flags (\fBchattr\fR wrapper)
completions Emit shell completion script
man Emit a roff man page to stdout
.TE
.SH COMMANDS
.SS grant
.B janitor grant
.I PATH
.RB [ \-u
.IR USER ]
.RB [ \-g
.IR GROUP ]
.RB [ \-r ]
.RB [ \-w ]
.RB [ \-x ]
.RB [ \-a
.IR ACCESS ]
.RB [ \-L
.IR N ]
.RB [ \-R ]
.RB [ \-\-force\-all\-parents ]
.RB [ \-\-no\-acl ]
.PP
Grant
.I USER
(or
.IR GROUP )
access to
.IR PATH .
A managed group is auto-created (named
.BI pm_tmp_ <owner>_<pathhash> )
unless
.B \-g
is given. Parents are opened with
.B \-\-x
only. Exactly one of
.B \-u
or
.B \-g
must be provided.
.PP
Access is specified either with the boolean flags
.BR \-r ", " \-w ", " \-x
or with the string
.BR \-a / \-\-access " \fIACCESS\fR" .
If no access flag is given, defaults to read-only. The two forms cannot be mixed.
.TP
.BR \-u ", " \-\-user " \fIUSER\fR"
User to grant access to.
.TP
.BR \-g ", " \-\-group " \fIGROUP\fR"
Group to grant access to (instead of a user, or as the managed group when combined with
.BR \-u ).
.TP
.BR \-r ", " \-\-read
Add the read bit.
.TP
.BR \-w ", " \-\-write
Add the write bit.
.TP
.BR \-x ", " \-\-exec
Add the execute / traverse bit.
.TP
.BR \-a ", " \-\-access " \fIACCESS\fR"
String form of the bits above
.RB ( r ", " rw ", " rwx ).
Mutually exclusive with
.BR \-r / \-w / \-x .
.TP
.BR \-L ", " \-\-max\-level " \fIN\fR"
Limit how many parent directory levels above
.I PATH
are made traversable.
.TP
.BR \-R ", " \-\-recursive
Also apply the grant to every file and subdirectory under
.I PATH
(the directory tree is walked).
.TP
.B \-\-force\-all\-parents
Grant even if a parent is world-readable (normally a warning is emitted and the grant
continues; this flag suppresses the warning check).
.TP
.B \-\-no\-acl
Do not capture POSIX ACLs in the pre-change snapshot (by default ACLs are
always recorded, so
.B restore
brings them back along with mode and owner).
.SS revoke
.B janitor revoke
.I PATH
.BR \-u
.I USER
.RB [ \-g
.IR GROUP ]
.PP
Remove
.I USER
from the managed group of
.IR PATH .
This is an
.B all-or-nothing
revoke: the user loses every bit of access that was granted via that group. The
file's mode, ACLs, and the managed group itself are untouched; other members of
the group keep their access.
.PP
For
.B bit-level
revocation use one of:
.TP
.B janitor chmod \fIMODE\fR \fIPATH\fR
Adjust traditional mode bits (also auto-snapshotted).
.TP
.B janitor acl revoke \fIPATH\fR \-u \fIUSER\fR
Remove a specific ACL entry.
.TP
.B janitor restore \fIID\fR
Undo a specific earlier grant exactly.
.SS tree
.B janitor tree
.I PATH
.RB [ \-L
.IR DEPTH ]
.RB [ \-P ]
.RB [ \-H
.IR SUBSTR ]
.RB [ \-U
.IR USER ]
.RB [ \-c
.IR auto|always|never ]
.RB [ \-A ]
.PP
Print a colored permission tree. With
.B \-U USER
entries are tinted green, yellow, or red depending on whether that user can
read, can traverse but not read, or is denied. With
.B \-A
a
.B +
marker is appended to the mode of entries that carry POSIX ACLs.
.SS chmod
.B janitor chmod
.I MODE PATH
.RB [ \-R ]
.RB [ \-\-no\-acl ]
.RB [ \-F\ FILE " | " \-\-reference\ FILE ]
.PP
Change the mode using octal or symbolic notation. An auto-snapshot is taken
first so the change is revertible via
.BR restore .
.PP
.B Note on naming.
.B janitor chmod
is not a wrapper around the coreutils
.BR chmod (1)
binary. It invokes the
.BR chmod (2)
syscall directly via
.BR std::fs::set_permissions .
The file-system effect is identical to POSIX chmod; the value added is the
automatic snapshot, lock-check, variadic paths,
.BR \-\-exclude ,
.BR \-\-stdin0 ,
.BR \-\-from\-file ,
and
.BR \-\-reference .
.PP
.B Umask and symbolic modes.
A symbolic mode with no
.I who
part behaves as if
.B a
were given, except that bits set in the process umask are left alone \(em as
POSIX requires. Under
.BR "umask 077" ,
.B +x
on a
.I 0600
file yields
.IR 0700 ,
not
.IR 0711 .
Spell out
.B a
.RB ( a+x )
to ignore the umask.
.PP
A recursive chmod applies the deepest paths first, so tightening a tree does
not remove the traverse bit from a directory whose children still have to be
changed.
.PP
.B Octal forms
support the full 4-digit syntax with special bits:
.IP \(bu 2
\fB755\fR, standard rwxr-xr-x
.IP \(bu 2
\fB4755\fR, setuid (run as owner)
.IP \(bu 2
\fB2755\fR, setgid (inherit group, or run as group)
.IP \(bu 2
\fB1777\fR, sticky bit (directory: only owner can delete)
.IP \(bu 2
\fB6755\fR, setuid + setgid
.IP \(bu 2
\fB7777\fR, all special bits + rwxrwxrwx
.PP
.B Symbolic forms
accept comma-separated clauses of \fB[ugoa][+\-=][rwxXst]\fR, e.g.
.BR "u+rw,go\-w" ,
.BR "a+X"
(add x only to dirs/already-exec),
.BR "u+s"
(setuid),
.BR "g+s"
(setgid),
.BR "+t"
(sticky),
.BR "u\-s,g\-s,\-t"
(clear all special bits).
.PP
With
.B \-F
/ \fB\-\-reference FILE\fR the mode is copied from
.I FILE
and the
.I MODE
argument is ignored (pass
.B \-
as a placeholder).
.SS chown
.B janitor chown
.I SPEC PATH
.RB [ \-R ]
.RB [ \-\-no\-acl ]
.RB [ \-F\ FILE " | " \-\-reference\ FILE ]
.PP
Change owner/group.
.I SPEC
is one of
.IR user ", " user:group ", " :group ", " user:
(owner + that owner's primary group) or numeric
.IR UID:GID .
Symlinks are always handled with
.BR lchown (2);
targets are never followed, even under
.BR \-R .
With
.B \-F
/ \fB\-\-reference FILE\fR the owner and group are copied from
.IR FILE .
.PP
.B Note on naming.
.B janitor chown
is not a wrapper around the coreutils
.BR chown (1)
binary. It invokes the
.BR lchown (2)
syscall directly via
.BR libc::lchown .
The file-system effect is identical to
.BR "chown -h" ;
the value added is the automatic snapshot, lock-check, variadic paths,
.BR \-\-exclude ,
.BR \-\-stdin0 ,
.BR \-\-from\-file ,
and
.BR \-\-reference .
.SS info
.B janitor info
.I PATH
.RB [ \-U\ USER " | " \-\-for\-user\ USER ]
.PP
One-shot summary of a path: type, owner (name + uid), group, mode (octal +
symbolic, including setuid/setgid/sticky bits), size, mtime, symlink target,
and ACL entries. With
.B \-U
also prints the effective
.IR rwx
access for
.I USER
on that inode. Alias:
.BR i .
.SS backup
.B janitor backup
.I PATH
.RB [ \-R ]
.RB [ \-\-no\-acl ]
.PP
Snapshot current mode, owner, group, and ACLs without making any change.
Use
.B \-\-no\-acl
to omit POSIX ACLs from the snapshot. Output includes the backup id.
.SS restore
.B janitor restore
.I BACKUP_ID
.RB [ \-\-yes ]
.RB [ \-\-skip\-missing ]
.RB [ \-\-allow\-replaced ]
.PP
Revert a snapshot. Mode, owner, group, and (if captured) ACLs are restored
atomically per path. Symlinks use
.BR lchown (2).
If the backup recorded a
.B grant
that created a managed group or added a user to one, those account changes
are reverted too.
.PP
Each entry is checked against the snapshot before it is written: an entry
whose file type or inode changed is refused, so a path replaced by a symlink
or hard link since the backup cannot redirect the change to another file.
Paths that no longer exist count as errors unless
.B \-\-skip\-missing
is given.
.PP
An ordinary editor save is write-then-rename, which produces a new inode too,
so restoring a file you have since edited reports it as replaced. Pass
.B \-\-allow\-replaced
when that is what happened; the file-type check still applies, so a path
swapped for a symlink is refused either way.
.SS undo
.B janitor undo
.RB [ \-\-yes ]
.RB [ \-\-skip\-missing ]
.RB [ \-\-allow\-replaced ]
.PP
Restore the most recent backup. Shortcut for looking up the newest snapshot
and running
.B restore
on it: an editor-style one-shot undo after any
.BR grant ", " chmod ", " chown ", or " acl
operation. Alias:
.BR u .
Combine with
.B \-\-dry\-run
to preview what would be reverted.
.SS list-backups
.B janitor list-backups
.RB [ \-p
.IR SUBSTR ]
.PP
List saved snapshots, most recent first. With
.B \-p
/ \fB\-\-path\fR only entries whose target path contains the given substring
are shown. With
.B \-\-json
produces an array of objects
.RI { id ", " timestamp ", " type ", " user ", " group ", " target ", " entries }.
.SS prune-backups
.B janitor prune-backups
.RB [ \-k
.IR N ]
.PP
Keep only the
.I N
most recent backups (default:
.BR 50 ).
.SS diff
.B janitor diff
.I BACKUP_ID
.PP
Show what
.B restore
of that backup would change versus the current state (mode, owner, group,
ACLs).
.SS export
.B janitor export
.I BACKUP_ID
.PP
Dump a backup as text or JSON (with
.BR \-j ).
.SS audit
.B janitor audit
.I PATH
.RB [ \-W ]
.RB [ \-r ]
.RB [ \-x ]
.RB [ \-s ]
.RB [ \-S ]
.RB [ \-t ]
.RB [ \-o
.IR USER ]
.RB [ \-g
.IR GROUP ]
.RB [ \-m
.IR OCTAL ]
.RB [ \-A ]
.RB [ \-\-no\-owner ]
.RB [ \-\-no\-group ]
.RB [ \-\-best\-effort ]
.PP
Scan a tree for entries matching one or more security-relevant filters.
Multiple filters are AND-ed.
.PP
Paths the scan cannot read are listed and make the command exit non-zero:
a clean result would otherwise cover only whatever happened to be
reachable, which for a security scan is the one answer that must never be
given silently. Pass
.B \-\-best\-effort
to accept a partial scan and exit 0 anyway.
.TP
.BR \-W ", " \-\-world\-writable
Entries with the other+w bit set.
.TP
.BR \-r ", " \-\-world\-readable
Entries with the other+r bit set.
.TP
.BR \-x ", " \-\-world\-executable
Entries with the other+x bit set.
.TP
.BR \-s ", " \-\-setuid
setuid binaries.
.TP
.BR \-S ", " \-\-setgid
setgid binaries and directories.
.TP
.BR \-t ", " \-\-sticky
Sticky-bit directories.
.TP
.BR \-o ", " \-\-owner " \fIUSER\fR"
Only entries owned by
.IR USER .
.TP
.BR \-g ", " \-\-group " \fIGROUP\fR"
Only entries whose group is
.IR GROUP .
.TP
.BR \-m ", " \-\-mode " \fIOCTAL\fR"
Only entries whose mode exactly equals
.IR OCTAL .
.TP
.BR \-A ", " \-\-has\-acl
Only entries that carry POSIX ACLs (\fB+\fR marker in
.BR "ls -l" ).
.TP
.B \-\-no\-owner
Only entries whose UID is not in
.IR /etc/passwd .
.TP
.B \-\-no\-group
Only entries whose GID is not in
.IR /etc/group .
.SS history
.B janitor history
.I PATH
.RB [ \-s
.IR DUR ]
.PP
List every backup whose target path contains
.IR PATH ,
newest first. With
.B \-\-since
.I DUR
only backups newer than DUR are shown (e.g.
.BR 30m ", " 1h ", " 2d ", " 1w ).
With
.B \-\-json
emits an array of
.RI { id ", " timestamp ", " type ", " target ", " entries }
objects suitable for
.BR jq (1)
pipelines. Alias:
.BR h .
.SS copy-perms
.B janitor copy-perms
.I SRC
.I DST
.RB [ \-R ]
.RB [ \-A ]
.PP
Atomically copy mode + owner + group from
.I SRC
to
.IR DST ,
with an automatic snapshot of
.I DST
before the change.
.TP
.BR \-R ", " \-\-recursive
Also apply to every entry under
.IR DST .
.TP
.BR \-A ", " \-\-acl
Also copy POSIX ACLs. If
.I SRC
has no extended ACL, any ACL on
.I DST
is stripped so the two end up identical.
.PP
Symlinks in
.I DST
have their ownership updated (\fBlchown\fR) but their mode is left alone, matching
.BR chmod (1)
semantics. Alias:
.BR cp .
.SS find-orphans
.B janitor find-orphans
.I PATH
.PP
List files whose owner UID or group GID is not resolvable in
.I /etc/passwd
or
.IR /etc/group .
.SS who-can
.B janitor who-can
.I PATH
.PP
Report which known users (from
.IR /etc/passwd )
can read, write, or execute
.I PATH
based on the full parent traversal chain plus POSIX mode bits and group
memberships.
.B root
is always listed.
.SS acl
.B janitor acl
.IR grant | revoke | show | strip
.I PATH
[\fIOPTIONS\fR]
.PP
Wrap
.BR getfacl (1)
and
.BR setfacl (1)
with auto-snapshots.
.PP
.B acl grant
adds an ACL entry;
.B acl revoke
removes one;
.B acl show
prints the ACL in human-readable form; and
.B acl strip
removes all ACL entries (keeping traditional mode bits).
.B grant
and
.B revoke
accept:
.TP
.BR \-u ", " \-\-user " \fIUSER\fR"
User entry to modify.
.TP
.BR \-g ", " \-\-group " \fIGROUP\fR"
Group entry to modify. Mutually exclusive with
.BR \-u .
.TP
.BR \-r ", " \-w ", " \-x
Boolean access flags (combinable, any order).
.TP
.BR \-a ", " \-\-access " \fIACCESS\fR"
String form of the bits (e.g.
.BR rwx ).
Mutually exclusive with
.BR \-r / \-w / \-x .
If none given, defaults to read-only.
.TP
.BR \-d ", " \-\-default
Operate on the default ACL (directories only). Default ACLs are inherited by
.I new
children created under the directory; existing children are not modified.
.TP
.BR \-R ", " \-\-recursive
Apply to every file and subdirectory under
.I PATH
(the tree is walked; combine with
.B \-d
to set the default ACL on every directory).
.PP
Examples:
.PP
.EX
# Alice rwx on a shared dir, and inherited by new children, applied to whole tree.
sudo janitor acl grant /srv/shared -u alice -rwx -d -R
# Read-only ACL entry for a group on a single file.
sudo janitor acl grant /etc/app/secret.conf -g auditors -r
# Remove a specific ACL entry.
sudo janitor acl revoke /srv/shared -u alice
# Inspect ACLs.
janitor acl show /srv/shared
# Remove every ACL entry from a tree (keep mode bits).
sudo janitor acl strip /srv/shared -R
.EE
.SS preset
.B janitor preset
.I NAME PATH
.RB [ \-R ]
.PP
Apply a named mode preset. See
.B janitor presets
for the full list. A snapshot is taken first; revert with
.B janitor restore <id>.
.TP
.BR \-R ", " \-\-recursive
Also apply the preset mode to every file and subdirectory under
.I PATH
(the whole tree is walked). Without
.BR \-R ,
only
.I PATH
itself is changed.
.PP
Examples:
.PP
.EX
# Lock down a single config file (600).
sudo janitor preset private-file /etc/app/secrets.yml
# Make /srv/team a group-shared dir (770) and recurse into its contents.
sudo janitor preset group-shared /srv/team -R
# Set a setgid directory so new children inherit the group (2775).
sudo janitor preset setgid-dir /srv/team
# Public-readable tree (755 dirs / 644 files -- pick the preset that matches).
sudo janitor preset public-read /srv/www -R
.EE
.PP
Typical presets:
.TP
.B private
700, owner only.
.TP
.B private-file
600.
.TP
.B group-shared
770, owner + group, no other.
.TP
.B group-read
750.
.TP
.B public-read
755.
.TP
.B public-file
644.
.TP
.B secret
400, read-only for owner, nobody else.
.TP
.B setgid-dir
2775, children inherit the group.
.TP
.B sticky-dir
1777,
.I /tmp
style.
.TP
.B secret-dir
500, readable & traversable by owner only (read-only).
.TP
.B exec-only
711, owner rwx; others may traverse but not list.
.TP
.B ssh-key
600, matches what
.BR sshd (8)
demands for private keys.
.TP
.B ssh-dir
700, for
.IR ~/.ssh .
.TP
.B config
640, config readable by the owner's group.
.TP
.B log-file
640, log readable by owner + group.
.TP
.B systemd-unit
644, matches the expected mode for
.IR /etc/systemd/system/*.service .
.TP
.B read-only
444, readable by everyone and writable by no one.
.TP
.B no-access
000, a "panic button" to freeze a path until it is investigated.
.SS completions
.B janitor completions
.I SHELL
.PP
Emit a shell completion script for
.IR bash ", " zsh ", " fish ", " powershell ", or " elvish .
.SS man
.B janitor man
.PP
Emit a roff man page to stdout. Useful for generating the installed man page
at build or package time.
.SH WORKFLOWS
.SS Inspecting a single path
One command for "what is going on with this file?":
.PP
.RS
.EX
# Type, owner, group, mode (octal + symbolic), setuid/setgid/sticky,
# size, mtime, symlink target, and ACLs -- all in one view.
janitor info /usr/bin/sudo
# Also show the effective rwx access for user alice.
janitor info /srv/app/config.yml -U alice
.EE
.RE
.SS Granting access to a shared project directory
A team of developers needs rwx access to
.I /srv/app
without the project becoming world-readable.
.PP
.RS
.EX
# 1. Create a team group if one does not exist.
sudo groupadd devs
sudo usermod -aG devs alice
sudo usermod -aG devs bob
# 2. Set the group on the directory tree and give it rwx, owner untouched.
sudo janitor chown :devs /srv/app -R
sudo janitor preset group-shared /srv/app -R
# 3. Make new files inherit the group and a sane mode.
sudo janitor preset setgid-dir /srv/app -R
sudo janitor acl grant /srv/app -g devs -rwx -d -R
.EE
.RE
.PP
Any developer can now create files under
.I /srv/app
that are automatically group-owned by
.B devs
and rwx-accessible to the group, without being visible to other system users.
.SS One-off access to a nested file
Auditor Carol needs read access to
.I /var/backups/db/dump.sql
for one afternoon, without exposing the rest of
.IR /var/backups .
.PP
.RS
.EX
# Grant (default is read-only), take note of the backup id it prints.
sudo janitor grant /var/backups/db/dump.sql -u carol -r
# ... auditor does her work ...
# Full revert when she's done.
sudo janitor ls | head -1 # newest-first list, one line per backup
sudo janitor restore <ID>
.EE
.RE
.PP
Siblings in
.I /var/backups
and
.I /var/backups/db
remained invisible throughout; Carol could
.BR cat (1)
the dump file but not list the directories.
.SS Auditing a server before handover
.PP
.RS
.EX
# World-writable, setuid, setgid, and sticky-bit surprises.
sudo janitor audit / -W -s -S -t --json | jq
# Files whose owners no longer exist.
sudo janitor find-orphans / --json
# Who exactly can read /etc/shadow today?
janitor who-can /etc/shadow
# Files with unexpected POSIX ACLs.
sudo janitor audit / -A --json | jq '.[].path'
.EE
.RE
.SS Safe chmod / chown with rollback
Every
.B chmod
and
.B chown
is snapshotted first:
.PP
.RS
.EX
sudo janitor chmod 640 /etc/nginx/conf.d/site.conf
sudo janitor chown root:www-data /var/www -R
# Oops, wrong group? Find the latest backup id and revert it.
# `janitor ls` lists backups newest-first, one per line, with the id as
# the first whitespace-separated column; `head -1` picks the newest line
# and `awk '{print $1}'` extracts just the id:
sudo janitor ls | head -1 # full line for a human
BID=$(sudo janitor ls | head -1 | awk '{print $1}')
sudo janitor restore "$BID"
.EE
.RE
.SS Deploying ACL-based permissions
.PP
.RS
.EX
# Give bob rwx on a shared dir, inherited by every new child.
sudo janitor acl grant /srv/shared -u bob -rwx -d -R
# Remove bob's ACL (keeps the default for new files).
sudo janitor acl revoke /srv/shared -u bob -R
# Nuke all ACLs, keep only the traditional mode bits.
sudo janitor acl strip /srv/shared -R
.EE
.RE
.SS Previewing before committing
Every mutating command supports
.BR \-n / \-\-dry\-run :
.PP
.RS
.EX
sudo janitor -n grant /srv/data/report.txt -u alice -r
sudo janitor -n chown alice:devs /srv/app -R
sudo janitor -n preset setgid-dir /srv/team -R
.EE
.RE
.SS Backup management
.PP
.RS
.EX
# List human-readable (newest first, one line per backup).
janitor ls
# Find the newest backup id programmatically (for scripting).
janitor ls | head -1 | awk '{print $1}'
# Or use the JSON form and jq, e.g. ids of every backup touching /srv.
janitor -j ls | jq -r '.[] | select(.target | startswith("/srv")) | .id'
# Diff a backup against the current state (shows exactly what restore would undo).
janitor diff <ID>
# Dump a backup as structured JSON (all original mode/owner/ACL triples).
janitor -j export <ID>
# Keep the 20 most recent backups, delete the rest.
sudo janitor prune -k 20
# Full revert of the most recent change in one line.
sudo janitor restore "$(sudo janitor ls | head -1 | awk '{print $1}')"
.EE
.RE
.SH EXAMPLES CATALOG
Every command the tool supports, with at least one example per flag
combination. All mutating commands snapshot first and can be reverted with
.B janitor undo
or
.BR "janitor restore " <id> .
.SS grant / revoke
.PP
.RS
.EX
sudo janitor grant /srv/docs/secret.txt -u alice -r
sudo janitor grant /srv/project -u alice -r -w -R
sudo janitor grant /srv/project -g devs -a rw -R
sudo janitor grant /srv/docs -u alice -x
sudo janitor -n grant /srv/project -u alice -rw -R # dry-run
sudo janitor revoke /srv/project -u alice
.EE
.RE
.SS chmod
.PP
.RS
.EX
sudo janitor chmod 644 /etc/nginx/nginx.conf
sudo janitor chmod 755 /usr/local/bin/tool
sudo janitor chmod 4755 /usr/local/bin/suid-helper
sudo janitor chmod 2755 /srv/shared
sudo janitor chmod 1777 /srv/tmp
sudo janitor chmod 6755 /usr/local/bin/suid-sgid
sudo janitor chmod u+rwx,g=rx,o= /srv/private
sudo janitor chmod a+X /srv/project -R
sudo janitor chmod -F /etc/ssh/sshd_config /etc/ssh/sshd_config.bak
.EE
.RE
.SS chown
.PP
.RS
.EX
sudo janitor chown alice /srv/alice
sudo janitor chown alice:devs /srv/project -R
sudo janitor chown :devs /srv/project
sudo janitor chown alice: /srv/alice
sudo janitor chown 1000:1000 /srv/legacy
sudo janitor chown -F /etc/passwd /srv/new-file
.EE
.RE
.SS info
.PP
.RS
.EX
janitor info /etc/shadow
janitor info /etc/shadow -U alice
janitor -j info /srv/project | jq
.EE
.RE
.SS audit
.PP
.RS
.EX
janitor audit / -W # world-writable
janitor audit / -r # world-readable
janitor audit / -x # world-executable
janitor audit / -s # setuid
janitor audit / -S # setgid
janitor audit / -t # sticky-bit directories
janitor audit / -A # entries with POSIX ACLs
janitor audit / -m 777 # exact-mode