-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathonionjuggler-cli
executable file
·1442 lines (1305 loc) · 68.3 KB
/
onionjuggler-cli
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
#!/usr/bin/env sh
## This script lets you manage your hidden services to all its capability
##
## Lines that begin with "## " try to explain what's going on. Lines
## that begin with just "#" are disabled commands.
me="${0##*/}"
## colors
nocolor="\033[0m"
bold="\033[1m"
#nobold="\033[22m"
underline="\033[4m"
nounderline="\033[24m"
red="\033[31m"
green="\033[32m"
yellow="\033[33m"
blue="\033[34m"
magenta="\033[35m"
cyan="\033[36m"
get_intr="$(stty -a | sed -n '/.*intr = / {s///;s/;.*$//;p;}')"
## display error message with instructions to use the script correctly.
#notice(){ printf %s"${me}: ${1}\n" 1>&2; }
notice(){ printf %s"${1}\n" 1>&2; }
error_msg(){ notice "${red}error: ${1}${nocolor}"; exit 1; }
usage(){
printf %s"${magenta}
';: -'''
':' -=l.:>l_
'^';z;_|J/':'
'|;l''?7xc:'
r1ztJtv;':'
'xxxtx|-'
':' '|ttxv:
7Or:{tx|'
O?':,Jom,
vN'_'i9@;
'g9'_'j2Qg-
_xBt-':'>USMQv'
;uQDc.'';_:'lOjoOBNu:
~yQ6|.'''':;_:,';DSjjj6#QX;
?BQv''''''-=>'_'l'''8yjjjujqQQi
:R#>'''''->|r-''_'.7'';gjjuujjjPQN:
7@7'''''rl>-''''.;:'-1.'OPjuuuujjjM@l
i@:''''*i'''''''.;_*''.u'lQjuuuuuujjK@r
~@1''''u,'''''',::'_-J,',1~@SjjuuuujjjQQ'
}@'''.a.'''':>;.'''_''/,'e.@Ejjuuuuuujh@>
Z@'''],'''.v^'''''';_''u'l:@Kjjuuuuuuja@v
}@.''a'''-o''''''->^;;'*l>/@OjjuuuuuujX@|
_@v'':i''L,'''''_{'_'y''kJu@6jjuuuujjjN@-
v@:''7r'l;'''''x''_'o':Eqe@ojjuuuujjE@L
F@c''|=.R'''''u''_'o:lSjQQjjjuujjuO@}
?Qq,'rcr8,'''e,'_'q-kZv@EjuujujoNQ|
'iQO^,xlm7-''E'_~g;DO@OjjjujhBQ1'
:jWkmycjl^:FxPXQ@@kjSEDQQe;
-;vmdgN@@Q@@@QROsev^-
${nocolor}
Usage: ${me} command [--option <ARGUMENT>]
\nComplete options:
activate [--service <SERVICE>] [--socket <unix>] [--version <3>] [--port <VIRTPORT [VIRTPORT2]>]
enable a service listening with tcp sockets
deactivate [--service <SERVICE>] [--socket <tcp>] [--version <3>] [--port <VIRTPORT[,TARGET] [,VIRTPORT2][,TARGET2]>]
enable a service listening on unix sockets
deactivate [--purge] [--service <@all|SERV1,SERV2,...> disable a service and optionally purge its directory
info [--quiet] [--service <@all|SERV1,SERV2,...>] see credentials from indicated services
renew [--service <@all|SERV1,SERV2,...>] renew indicated services addresses
auth-server --on [--service <SERVICE>] [--client <CLIENT>] [--client-pub-key <CLIENT_PUB_KEY>]
add client authorization, optionally add client's public key
auth-server --on [--service <@all|SERV1,SERV2,...>] [--client <CLIENT1,CLIENT2,...>]
add client authorization
auth-server --off [--service <@all|SERV1,SERV2,...>] [--client <@all|CLIENT1,CLIENT2,...>]
remove client authorization
auth-server --list [--service <@all|SERV1,SERV2,...>] list authorized clients for indicated service
auth-client --on [--onion <ONION>] [--client-priv-key <CLIENT_PRIV_KEY>]
add authorization of client access, optionally add client's private key
auth-client --off [--onion <ONION>] remove authorization of client access
auth-client --list list your keys as a client
web --on [--service <SERVICE>] [--folder <SITE_PATH>] start serving a website for certain service and its folder
web --off [--service <SERVICE>] stop serving a website for certain service and its folder
web --list list enabled websites
location [--nginx|--apache2|--html] [--service <SERVICE>] onion-location guide, no execution
backup [--create|--integrate] backup onion services or integrate the backup
vanguards [--on|--list|--off] install or upgrade, remove or see logs for vanguards addon
-h|--help display this help message
Options:
--getconf print configuration
--getopt print options given by the command line
on activate an onion service
-s, --service <SERVICE> service to activate
-S, --socket <tcp|unix> define a socket for the new onion service
-v, --version 3 define a version for the new onion service
-p, --port <VIRTPORT[,TARGET],VIRTPORT2[,TARGET2]> define ports for the new onion service
-g, --whonix-gateway define target 127.0.0.1 (Whonix Gateway IP) (does not overwrite specified target)
off deactivate an onion service configuration
-s, --service <SERV1,SERV2,...> service to deactivate
-P, --purge purge the onion service data
info list existing services and their configuration, clients
-s, --service <@all|SERV1,SERV2,...> list all services or a indicate them comma separated
-q, --quiet don't QR encode the hostname
auth-server manage authorized_clients
-s, --service <@all|SERV1,SERV2,...> authorize or remove authorization to indicated services
-n, --on authorized aclient
-f, --off remove authorization from a client
-l, --list list authorized clients
-c, --client <CLIENT> choose client
-K, --client-pub-key <CLIENT_PUB_KEY> specify client pub key to authorize to one service
auth-client manage your authorizations to an onion
-n, --on add authorization
-f, --off remove authorization
-l, --list list current authorizations
-o, --onion <ONION> specify onion to authenticate
-k, --client-priv-key <CLIENT_PRIV_KEY> specify your client private key
web manage web server configuration
-s, --service <SERVICE> service to enable or disable website
-n, --on activate website to an onion service
-f, --off deactivate website to an onion service
-l, --list list active websites
-w, --folder <SITE_PATH> specify website files
location guide to add onion-location to redirect users to your onion domain
-s, --service <SERVICE> indicate service to see onion-location string
--nginx Nginx webserver header for onion-location
--apache2 Apache2 webserver header for onion-location
--html HTML header for onion-location
backup complete backup of onion services
-M, --make make a backup
-I, --integrate integrate backup to your system
vanguards manage Vanguards protection
-n, --on install Vanguards addon, if already installed, upgrade.
-f, --off remove Vanguards
-l, --list see Vanguards logs
Advanced:
-C, --config specify an alternative onionjuggler.conf to be parsed
-R, --restart signal tor to restart when the daemon status is failed or inactive
-r, --reload signal tor to reload (default option)
Option names:
main activate, deactivate, info, renew, auth-server, auth-client, web, location, vanguards
status --on, --off, --list
action --purge, --quiet, --nginx, --apache2, --html, --make, --integrate
signal --restart, --reload
Positional arguments:
TARGET [addr:]port
@all all services or clients available
SERV1,SERV2... specify services
CLIENT1,CLIENT2,... specify clients
'# Done': You should always see it at the end, else something unexpected occured.
It does not imply the code worked, you should always pay attention for errors in the logs.
If your services are unreacheable, restart tor.
\nReport bugs to: https://github.com/nyxnor/onionsjuggler/issues\n"
exit 1
}
[ -z "${1}" ] && usage
########################
#### OPTION PARSING ####
## this getopts might seem complex, so check this template
## https://github.com/nyxnor/scripts/blob/master/getopts.sh
## check if argument is within range
## usage:
## $ range_arg key "1-5"
## $ range_arg key "1" "2" "3" "4" "5"
## $ range_arg key "a-cA-C"
## $ range_arg key "a" "b" "c" "A" "B" "C"
range_arg(){
list="${*}"
eval var='$'"${1}"
range="${list#"${1} "}"
if [ -n "${var:-}" ]; then
success=0
for tests in ${range}; do
## it needs to expand for ranges 'a-z' to be evaluated, and not considered as a value to be used
# shellcheck disable=SC2295
[ "${var%%*[^${tests}]*}" ] && success=1
done
## if not withing range, fail and show the fixed range that can be used
[ ${success} -ne 1 ] && error_msg "Option '${opt_orig}'can not be '${var}'! It can only be: ${range}."
fi
}
## if option requires argument, check if it was provided, if yes, assign the arg to the opt
## $arg was already assigned, and if valid, will use it for the key value
## usage: get_arg key
get_arg(){
## if argument is empty or starts with '-', fail as it possibly is an option
case "${arg}" in ""|-*) error_msg "Option '${opt_orig}' requires an argument.";; esac
## assign
value="${arg}"
## Escaping quotes is needed because else it will fail if the argument is quoted
# shellcheck disable=SC2140
eval "${1}"="\"${value}\""
## shift positional argument two times, as this option demands argument, unless they are separated by equal sign '='
## shift_n default value was assigned when trimming hifens '--' from the options
## if shift_n is equal to zero, '--option arg'
## if shift_n is not equal to zero, '--option=arg'
[ -z "${shift_n}" ] && shift_n=2
}
## hacky getopts
## accepts long (--option) and short (-o) options
## accept argument assignment with space (--option arg | -o arg) or equal sign (--option=arg | -o=arg)
while :; do
## '--option=value' should shift once and '--option value' should shift twice
## but at this point it is not possible to be sure if option requires an argument
## reset shift to zero, at the end, if it is still 0, it will be assigned to one
## has to be zero here so we can check later if option argument is separated by space ' ' or equal sign '='
shift_n=""
opt_orig="${1}" ## save opt orig for error message to understand which opt failed
case "${opt_orig}" in
--) shift 1; break;; ## stop option parsing
--*=*) opt="${1%=*}"; opt="${opt#*--}"; arg="${1#*=}"; shift_n=1;; ## long option '--sleep=1'
-*=*) opt="${1%=*}"; opt="${opt#*-}"; arg="${1#*=}"; shift_n=1;; ## short option '-s=1'
--*) opt="${1#*--}"; arg="${2}";; ## long option '--sleep 1'
-*) opt="${1#*-}"; arg="${2}";; ## short option '-s 1'
"") break;; ## options ended
*) usage;; ## not an option
esac
case "${opt}" in
activate|deactivate|info|renew|auth-server|auth-client|web|location|backup|vanguards) main="${opt}";;
getopt|getopts|getconf) dev="${opt}";;
on|off|list|n|f|l) status="${opt}";;
R|restart|r|reload) signal="${opt}";;
P|purge|nginx|apache2|html|q|quiet|M|make|I|integrate) action="${opt}";;
s|service|s=*|service=*) get_arg service;;
c|client|c=*|client=*) get_arg client;;
o|onion|o=*|onion=*) get_arg onion;;
v|version|v=*|version=*) get_arg version;;
S|socket|S=*|socket=*) get_arg socket;;
p|port|p=*|port=*) get_arg port;;
g|whonix-gateway) whonix_gateway_service=1;;
w|folder|w=*|folder=*) get_arg folder;;
k|client-priv-key|k=*|client-priv-key=*) get_arg client_priv_key;;
K|client-pub-key|K=*|client-pub-key=*) get_arg client_pub_key;;
C|config|C=*|config=*) get_arg ONIONJUGGLER_CONF;;
h|help) usage;;
*) error_msg "Invalid option: '${opt_orig}'";;
esac
## shift as many times as demanded
## if empty, shift at least once to pass to next option
shift "${shift_n:-1}"
done
###################
#### VARIABLES ####
## 1. source default configuration file first
## 2. source local (user made) configuration files to override the default values
## 3. source the ONIONJUGGLER_CONF specified by the cli argument and if it empty, use the environment variable
[ ! -f /etc/onionjuggler/onionjuggler.conf ] && error_msg "Default configuration file not found: /etc/onionjuggler/onionjuggler.conf"
[ -r /etc/onionjuggler/onionjuggler.conf ] && . /etc/onionjuggler/onionjuggler.conf
for file in /etc/onionjuggler/conf.d/*.conf; do [ -f "${file}" ] && . "${file}"; done
[ -r "${ONIONJUGGLER_CONF}" ] && . "${ONIONJUGGLER_CONF}"
## : ${var:="value"} -> initialize the variable (SC2154) and if empty or unset, use default values
## var=${var%*/} -> removes the trailing slash "/" at the end of directories variables
## system
: "${su_cmd:="sudo"}"
: "${openssl_cmd:="openssl"}"
: "${webserver:="nginx"}"
: "${webserver_conf:="/etc/nginx/sites-enabled"}"
: "${website_dir:="/var/www"}"; website_dir="${website_dir%*/}"
: "${vanguards_commit:="10942de93f6578f8303f60014f34de2fca345545"}"
## tor defaults
: "${daemon_control:="systemctl"}"; daemon_control="${daemon_control%*/}"
: "${tor_daemon:="tor@default"}"
: "${tor_user:="debian-tor"}"
: "${tor_conf_user_group:="root:root"}"
: "${tor_data_dir:="/var/lib/tor"}"; tor_data_dir="${tor_data_dir%*/}"
: "${tor_data_dir_services:="${tor_data_dir}/services"}"; tor_data_dir_services="${tor_data_dir_services%*/}"
: "${tor_data_dir_auth:="${tor_data_dir}/onion_auth"}"; tor_data_dir_auth="${tor_data_dir_auth%*/}"
: "${tor_conf_dir:="/etc/tor"}"; tor_conf_dir="${tor_conf_dir%*/}"
: "${tor_conf:="${tor_conf_dir}/torrc"}"
: "${tor_control_port:="9051"}" ## only the port, not the host
: "${tor_backup_dir:="${HOME}/.onionjuggler/backup"}"; tor_backup_dir="${tor_backup_dir%*/}"
###################
#### FUNCTIONS ####
## Elegantly modify files on a temporary directory. Test the configuration with another function.
## If correct, then save file back to its original location. This avoids running with an invalid
## configuration that can make a daemon fail to reload or even start
## Limitation is file name cannot start with a number.
## $ safe_edit tmp "${file}" && file_tmp="$(safe_edit getver "${file}")"
## modify the "${file_tmp}"
## use the daemon option to verify config -f "${file_tmp}"
## $ safe_edit save "${file}"
safe_edit(){
[ -w "${TMPDIR:="/tmp"}" ] || export TMPDIR="~"
TMPDIR="${TMPDIR%*/}"
file="${2}"
## get base file: /etc/tor/vanguards.conf.sample -> vanguards.conf.sample.
## replace dot and hifen for underscore so it can be evaluated as a key (variable name)
file_name="$(printf %s"${file##*/}" | tr "." "_" | tr "-" "_")"
[ -z "${file_name}" ] && error_msg "file_name is empty"
case "${1}" in
tmp)
file_name_tmp="$(printf %s"mkstemp(${TMPDIR}/${file_name}.XXXXXX)" | m4)"
eval "${file_name}"_tmp="${file_name_tmp}"
notice "Saving a copy of ${file} to ${file_name_tmp}"
chown "${tor_conf_user_group}" "${file_name_tmp}"
cp "${file}" "${file_name_tmp}"
# shellcheck disable=SC2064
trap "printf %s\"Exiting script ${me}\nDeleting ${file_name_tmp}\n\"; rm -f ${file_name_tmp}" EXIT
;;
getvar) eval file_name_tmp='$'"${file_name}_tmp" && printf %s"${file_name_tmp}";;
save)
eval file_name_tmp='$'"${file_name}_tmp"
if cmp -s "${file_name_tmp}" "${file}"; then
notice "File ${file_name_tmp} do not differ from ${file}"
notice "Not writing back to original location.${nocolor}"
rm -f "${file_name_tmp}"
else
notice "Moving ${file_name_tmp} back to its original location ${file}"
mv "${file_name_tmp}" "${file}"
fi
;;
esac
}
## Verify tor configuration of the temporary file and if variable is empty, use the main configuration, if wrong, exit.
verify_config_tor(){
config="${tor_conf_tmp:-"${tor_conf}"}"
## if User is set on the config, then run tor as root
"${su_cmd}" grep -q "^User" "${config}" && su_tor_cmd="${su_cmd}"
## user may not be on this config, but on another, so run tor as its user if $su_tor_cmd is empty
: "${su_tor_cmd:="${su_cmd} -u ${tor_user}"}"
notice "Verifying tor configuration file ${config}"
! ${su_tor_cmd} tor -f "${config}" --verify-config --hush && error_msg "aborting: configuration is invalid"
notice "${green}Configuration OK${nocolor}"
[ -n "${tor_conf_tmp}" ] && safe_edit save "${tor_conf}"
}
## set correct permissions for tor directories and files
## find helps do the job because it can segreggate directories from files
set_owner_permission(){
## data
chown -R "${tor_user}:${tor_user}" "${tor_data_dir}"
find "${tor_data_dir}" -type d -exec chmod 700 {} \;
find "${tor_data_dir}" -type f -exec chmod 600 {} \;
## conf
chown -R "${tor_conf_user_group}" "${tor_conf_dir}"
find "${tor_conf_dir}" -type d -exec chmod 755 {} \;
find "${tor_conf_dir}" -type f -exec chmod 644 {} \;
}
# reloads tor by default or forces to restart if $1 is not empty
# shellcheck disable=SC2120
signal_tor(){
verify_config_tor
set_owner_permission
## default signal is to reload, but if restart was specified, use it
: "${signal:="reload"}"
[ "${signal}" = "r" ] && signal="reload"
[ "${signal}" = "R" ] && signal="restart"
printf "\n"
notice "${signal}ing tor, please be patient."
notice "Process hanged? Press (${get_intr}) to abort and maintain previous configuration."
case "${daemon_control}" in
systemctl|sv|rcctl) "${daemon_control}" "${signal}" "${tor_daemon}";;
service) "${daemon_control}" "${tor_daemon}" "${signal}";;
/etc/rc.d) "${daemon_control}"/"${tor_daemon}" "${signal}";;
*) error_msg "daemon_control value not supported: ${daemon_control}"
esac
[ "${?}" -eq 1 ] && error_msg "Failed to ${signal} tor. Check logs first, correct the problem them restart tor."
notice "${green}${signal}ed tor succesfully!${nocolor}"
printf "\n"
}
## check if variable is integer
is_integer(){ printf %d "${1}" >/dev/null 2>&1 || error_msg "Not an integer: ${1}" ; }
## checks if the target is valid.
## Address range from 0.0.0.0 to 255.255.255.255. Port ranges from 0 to 65535
## this is not perfect but it is better than nothing
is_addr_port(){
addr_port="${1}"
port="${addr_port##*:}"
addr="${addr_port%%:*}"
printf %d "${port}" >/dev/null 2>&1 || error_msg "'${port}' is not a valid port, not an integer"
{ [ "${port}" -gt 0 ] && [ "${port}" -le 65535 ]; } || \
error_msg "${port} is not a valid port, not within range: 0-65535"
for quad in $(printf '%s\n' "${addr}" | tr "." " "); do
printf %d "${quad}" >/dev/null 2>&1 || error_msg "${addr} is not a valid address, ${quad} is not and integer"
{ [ "${quad}" -ge 0 ] && [ "${quad}" -le 255 ]; } || \
error_msg "${addr} is not a valid address, ${quad} is not within range: 0-255"
done
}
## returns 1 if is not empty
## no better way to do with posix utilities
check_folder_is_not_empty(){
dir="${1}"
if [ -d "${dir}" ] && files=$(ls -qAH -- "${dir}") && [ -z "${files}" ]; then
return 1
else
return 0
fi
}
is_service_dir_empty(){
check_folder_is_not_empty "${tor_data_dir_services}" || error_msg "Onion services directory is empty. Create a service first before running this command again."
}
## test if service exists to continue the script or output error logs.
## if the service exists, will save the hostname for when requested.
test_service_exists(){
service="${1}"
onion_hostname=$(grep ".onion" "${tor_data_dir_services}"/"${service}"/hostname 2>/dev/null)
[ -z "${onion_hostname}" ] && error_msg "Service does not exist: ${service}"
}
## save the clients names that are inside the <HiddenServiceDir>/authorized_clients/ in list format (CLIENT1,CLIENT2,...)
create_client_list(){
service="${1}"
client_name_list=""
for client_listed in "${tor_data_dir_services}/${service}/authorized_clients"/*; do
client_listed="${client_listed##*/}"
[ "${client_listed}" = "*" ] && break
client_listed="${client_listed%*.auth}"
client_name_list="$(printf '%s\n%s\n' "${client_name_list}" "${client_listed}")"
done
[ -n "${client_name_list}" ] && client_name_list="$(printf '%s\n' "${client_name_list}" | tr "\n" "," | sed "s/\,$//" | sed "s/^,//")"
client_count=""
# shellcheck disable=SC2086
[ -n "${client_name_list}" ] && client_count="$(IFS=','; set -f -- ${client_name_list}; printf %s"${#}")"
}
## save the service names that have a <HiddenServiceDir> in list format (SERV1,SERV2,...)
create_service_list(){
for hs in "${tor_data_dir_services}"/*; do
hs="${hs##*/}"
service_name_list="$(printf '%s\n' "${service_name_list}" "${hs}")"
done
}
## loops the parameters
## $1 must be the function to loop
## $2 normally is service, but can be any other parameter (accepts list -> SERV1,SERV2,...)
## $3 normally is client, but can be any other (accepts list -> client1,client2...)
## $ loop_list function_name ssh,xmpp,web [alice,bob]
loop_list(){
for item in $(printf %s"${2}" | tr "," " "); do
case "${3}" in
"") "${1}" "${item}";;
*) for subitem in $(printf %s"${3}" | tr "," " "); do "${1}" "${item}" "${subitem}"; done;;
esac
done
}
## https://github.com/koalaman/shellcheck/wiki/SC3050
escape_printf_percent() { printf "%s\n" "$(printf '%s' "${1}" | sed "s/\%/\%/g")"; }
## TODO: find a better way to handle commented lines and empty lines
## the problem is that the script only stop at the next HiddenServiceDir,
## but discard every line not starting with HiddenServiceDir
## https://github.com/nyxnor/onionjuggler/issues/51
service_block(){
process="${1}"
service="${2}"
file="${3:-"${tor_conf_tmp}"}"
i=0
## print the exact match HiddenServiceDir of the requested service that must end with the service name or with "/", also prit n lines below it
match="HiddenServiceDir ${tor_data_dir_services}/${service}"
hs_found=""
while IFS="$(printf '\n')" read -r line; do
[ -z "${hs_found}" ] && printf '%s\n' "${line}" | grep -q -E "^${match}$|^${match}/$" && hs_found="1"
if [ "${hs_found}" = "1" ]; then
i=$((i+1))
case "${line}" in
"HiddenServiceStatistics"*) :;; ## relays only
"HiddenService"*)
## break on next HiddenService configuration
{ [ ${i} -gt 1 ] && [ "${line%% *}" = "HiddenServiceDir" ]; } && break
case "${process}" in
print|printf) printf '%s\n' "${line}";;
delete)
## delete only works if hs lines are consecutive,
## meaning no blank lines or commented lines between the wanted hs
if [ -z "${hs_lines_delete}" ]; then
hs_lines_delete="$(printf '%s\n' "${line}")"
else
hs_lines_delete="$(printf '%s\n%s\n' "${hs_lines_delete}" "${line}")"
fi
;;
esac
;;
esac
fi
done < "${file}"
if [ -n "${hs_lines_delete}" ]; then
## sed is a stream line editor, so lets make the file a single line transforming new lines to carriage return
hs_lines_delete="$(printf '%s\n' "${hs_lines_delete}" | tr "\n" "\r")"
## then convert the file also as done above, so sed can see the file and pattern on the same format
tr "\n" "\r" < "${file}" | sed "s|${hs_lines_delete}||" | tr "\r" "\n" | tee tmpfile >/dev/null
mv tmpfile "${file}"
fi
}
## TODO: finish: https://github.com/nyxnor/onionjuggler/issues/32
httpd_service_block(){
process="${1}"
service="${2}"
file="${3:-"/etc/httpd.conf"}"
i=0
test_service_exists "${service}"
grep -A 10 "server \"${onion_hostname}\"" "${file}" | while IFS= read -r line; do
case "${process}" in
print|printf) escape_printf_percent "${line}";;
delete) [ -n "${line}" ] && sed -i'' "s|${line}||" "${file}";;
esac
escape_printf_percent "${line}" | grep -q "^}" && break
done
cat_squeeze_blank "${file}"
}
## http://sed.sourceforge.net/local/docs/emulating_unix.txt
## tac is not posix
tac(){
sed '1!G;h;$!d' "${1}"
}
## 'cat -s' is not posix
cat_squeeze_blank(){
while :; do
case "${1}" in
"/"*|[[:alnum:]]*) files="${files} ${1}"; shift;; ## only consider path starting with "/" or alphanumeric
*) break;; ## made to break on pipes and everything else
esac
done
# shellcheck disable=SC2086
sed '1s/^$//p;/./,/^$/!d' ${files}
}
## error_msg self explanatory, tor breaks with special chars on the dir name
check_service_name(){
[ "${service%%*[^a-zA-Z0-9_.-]*}" ] || {
error_msg "Service name \"${service}\" is invalid\nIt must only contain the characters that are:
- letters (a-z A-Z)
- numbers (0-9)
- punctuations limited to hifen (-), underscore (_), dot (.)"
}
}
###########################
########## MAIN ###########
## development options
case "${dev}" in
## execute or modify nothing, just print the configuration values
## usefult to see if there is nothing messed up and if there is, can be checked before running the cli.
getconf)
for key in ONIONJUGGLER_CONF su_cmd openssl_cmd webserver webserver_conf website_dir vanguards_commit \
tor_daemon tor_user tor_conf_user_group tor_data_dir tor_data_dir_services tor_data_dir_auth tor_conf_dir \
tor_control_port tor_backup_dir; do
eval val='$'"${key}"
[ -n "${val:-}" ] && printf '%s\n' "${key}=\"${val}\""
done
exit 0
;;
## only print the options given on the command line, mostly for development purposes to check the argument
## but can be useful to see if the command is correct before running it.
getopt|getopts)
for key in signal main service client onion status action version socket port folder \
client_priv_key client_pub_key whonix_gateway_service; do
eval val='$'"${key}"
[ -n "${val}" ] && printf '%s\n' "${key}=\"${val}\""
done
exit 0
;;
esac
## user option
[ "$(id -u)" -ne 0 ] && error_msg "run as root"
case "${main}" in
## disable a service by removing service torrc's block.
## it is raw, services variables should be separated by an empty line per service, else you might get other non-related configuration deleted.
## purge is optional, it deletes the <HiddenServiceDir>
## will not check if folder or configuration exist, this is cleanup mode
## will not use '@all'. Purge is dangerous, purging all service is even more dangerous. Always backup.
deactivate)
[ -z "${service}" ] && usage
delete_service(){
service="${1}"
printf "\n"
## remove service service data
case "${action:-}" in
purge|P)
notice "${red}Deleting HiddenServiceDir ${underline}${tor_data_dir_services}/${service}${nocolor}"
rm -rfv "${tor_data_dir_services:?}"/"${service:?}"
;;
*) notice "${yellow}Keeping HiddenServiceDir ${underline}${tor_data_dir_services}/${service}${nocolor}";;
esac
## remove service paragraph in torrc
notice "Deleting HiddenService configuration in ${underline}${tor_conf_tmp}${nounderline}"
service_block delete "${service}" "${tor_conf_tmp}"
## substitute multiple sequential empty lines to a single one per sequence
cat_squeeze_blank "${tor_conf_tmp}" | tee "${tor_conf_tmp}".tmp >/dev/null && mv "${tor_conf_tmp}".tmp "${tor_conf_tmp}"
notice "Disabled service: ${bold}${service}${magenta}${nocolor}"
}
safe_edit tmp "${tor_conf}" && tor_conf_tmp="$(safe_edit getvar "${tor_conf}")"
loop_list delete_service "${service}"
printf "\n"
signal_tor
notice "${green}done!${nocolor}"
;;
## enable a service by configure its own torrc's block, consequentially the <HiddenServiceDir> will be created.
## tcp-socket uses addr:port, which can be remote or localhost. It leaks onion address to the local network
## unix-socket uses unix:path, which is create a unique name for it. It does not leak onion address to the local network.
## virtport is the port to be used by the client when visiting the service.
## empty socket will default to tcp
## empty version will default to 3
## target is where the incoming traffic from virtport gets redirected. This option is abscent on unix-socket because the script completes it.
## if target is not specified, will use the same port from virtport and bind to localhost.
## if target only contains the port number and not the address, will bind to localhost.
## virtport2 and target 2 are optional
activate)
[ -z "${service}" ] && error_msg "service name can not be empty"
check_service_name
: "${version:=3}"; [ "${version}" != "3" ] && error_msg "version ${version} is not available" ## wait for v4 to change this
: "${socket:=tcp}"
[ -z "${port}" ] && error_msg "port can not be empty"
if test -f /usr/share/anon-gw-base-files/gateway; then ## Whonix Gateway
if [ "${whonix_gateway_service}" = "1" ]; then
target_ip_default="127.0.0.1" ## Service should use Gateway ip (127.0.0.1)
elif command -v qubesdb-read >/dev/null; then
target_ip_default="$(qubesdb-read /qubes-ip)" ## Qubes-Whonix
else
target_ip_default=10.152.152.11 ## Non-Qubes-Whonix
fi
elif test -f /usr/share/anon-ws-base-files/workstation; then ## Whonix Workstation
error_msg "Create onion services on Whonix Gateway, not on Whonix Workstation"
else
target_ip_default="127.0.0.1" ## Common target
fi
## backup torrc
safe_edit tmp "${tor_conf}" && tor_conf_tmp="$(safe_edit getvar "${tor_conf}")"
notice "Including Hidden Service configuration to ${tor_conf_tmp}"
printf %s"\nHiddenServiceDir ${tor_data_dir_services}/${service}\nHiddenServiceVersion ${version}\n" | tee -a "${tor_conf_tmp}"
finish_service_activation(){
## remove double empty lines
cat_squeeze_blank "${tor_conf_tmp}" | tee "${tor_conf_tmp}".tmp >/dev/null && mv "${tor_conf_tmp}".tmp "${tor_conf_tmp}"
signal_tor
virtport="$(service_block print "${service}" "${tor_conf}" | grep "HiddenServicePort" | sed "s/HiddenServicePort //;s/ .*//" | tr "\n" "," | sed "s/\,$//")"
## show the Hidden Service address
test_service_exists "${service}"
notice "Hidden Service information:"
notice "Service name = ${service}"
notice "Service address = ${magenta}${onion_hostname}${nocolor}"
notice "Virtual port = ${virtport}"
command -v qrencode >/dev/null && qrencode -m 2 -t ANSIUTF8 "${onion_hostname}"
}
case "${socket}" in
tcp)
## tor-manual: By default, this option maps the virtual port to the same port on target_ip_default over TCP
## Because of that, this project lets the user leave target="" and write target as $target_ip_default:$virtport
## Also, substitutes localhost:port for $target_ip_default:$port to make exact math for target always, as localhost and target_ip_default mean the same thing
## This measures avoid using the same local port for different services
## Sanity check
port="$(printf %s"${port}" | tr " " "\n" | tr "," " " | tr -s " ")"
fail_log="$(mktemp)"
printf '%s\n' "${port}" | while IFS="$(printf '\n')" read -r port_line; do
IFS=" " read -r virtport target <<-EOF
$(printf '%s\n' "${port_line}")
EOF
## virtport is necessary
[ -z "${virtport}" ] && break
## target if empty will tcp local host on interface target_ip_default using the same port as virtport
[ -z "${target}" ] && target="${target_ip_default}:${virtport}"
target_addr="${target%%:*}"
target_port="${target##*:}"
## the first check is in case target was provided as an integer, not as a target
## the second check is for uniformity, convert localhost to target_ip_default
{ [ "${target_addr}" = "${target_port}" ] || [ "${target_addr}" = "localhost" ]; } && target="${target_ip_default}:${target_port}"
is_integer "${virtport}"
is_addr_port "${target}"
## check if the tager is already used in the torrc
## the first part check if the target is present on the tor configuration file
## the second part checks if the target port is used on a hs port without a target, meaning using the same port from virtual port
if grep -q "^HiddenServicePort .* ${target}$" "${tor_conf}" || grep -q "^HiddenServicePort ${target_port}$" "${tor_conf}"; then
printf %s"HiddenServicePort ${virtport} ${target}\n"
echo "1" | tee -a "${fail_log}" >/dev/null
error_msg "Target '${target}' is already in use.\nINFO: Choose another port or disable the service that is using the wanted port."
fi
## this check is the same as from above, but instead, it check the temporary configuration file that is being modified now
if grep -q "^HiddenServicePort .* ${target}$" "${tor_conf_tmp}" || grep -q "^HiddenServicePort ${target_port}$" "${tor_conf_tmp}"; then
printf %s"HiddenServicePort ${virtport} ${target}\n"
echo "1" | tee -a "${fail_log}" >/dev/null
error_msg "Target '${target}' was specified multiple times, but it can only happen once."
fi
printf %s"HiddenServicePort ${virtport} ${target}\n" | tee -a "${tor_conf_tmp}"
done
printf '\n' | tee -a "${tor_conf_tmp}"
test -s "${fail_log}" && { rm -f -- "${fail_log}"; exit 1; }
## get info
finish_service_activation
;;
unix)
port="$(printf %s"${port}" | tr "," " " | tr " " "\n" | tr -s " ")"
## /var/run/ because it exists on Debian and OpenBSD, so respecting standards
unix_path="unix:/var/run/${service}"
fail_log="$(mktemp)"
printf '%s\n' "${port}" | while IFS="$(printf '\n')" read -r port_line; do
IFS=" " read -r virtport <<-EOF
$(printf '%s\n' "${port_line}")
EOF
[ -z "${virtport}" ] && break
## use a key="-onion" on the target to facilitate discovering it later and distinction if there is a plain net site
target="${unix_path}-${virtport}-onion.sock"
is_integer "${virtport}"
## check wheter target is already in use on the tor configuration file
if grep -q "^HiddenServicePort .* ${target}$" "${tor_conf}"; then
printf %s"HiddenServicePort ${virtport} ${target}\n"
echo "1" | tee -a "${fail_log}" >/dev/null
error_msg "Target '${target}' is already in use.\nINFO: Choose another port or disable the service that is using the wanted port."
fi
## check wheter target is already in use on the temporary copy of the tor configuration file
if grep -q "^HiddenServicePort .* ${target}$" "${tor_conf_tmp}"; then
printf %s"HiddenServicePort ${virtport} ${target}\n"
echo "1" | tee -a "${fail_log}" >/dev/null
error_msg "Target '${target}' was specified multiple times, but it can only happen once."
fi
printf %s"HiddenServicePort ${virtport} ${target}\n" | tee -a "${tor_conf_tmp}"
done
printf '\n' | tee -a "${tor_conf_tmp}"
test -s "${fail_log}" && { rm -f -- "${fail_log}"; exit 1; }
## get info
finish_service_activation
;;
*)
error_msg "Invalid argument: socket=${socket}"
esac
;;
## manage client authorization server side (HiddenServiceDir/authorized_clients/) or client side (ClientOnionAuthDir)
auth-server|auth-client)
host="${main#*-}"
[ -z "${status:-}" ] && usage
case "${host}" in
server)
[ -z "${service}" ] && usage
[ "${service}" != "@all" ] && check_service_name
is_service_dir_empty
case "${status}" in
## as the onion service operator, make your onion authenticated by generating a pair or public and private keys,
## the client pub key is automatically saved inside <HiddenServiceDir>/authorized_clients/alice.auth
## the client private key is shown in the screen and the key file deleted
## the onion service operator should send the private key for the desired client
n|on)
[ -z "${client}" ] && usage
#printf "\n# Generating keys to access onion service (Client Authorization) ...\n"
auth_server_add(){
service="${1}"
client="${2}"
test_service_exists "${service}"
## Generate pem and derive pub and priv keys
"${openssl_cmd}" genpkey -algorithm x25519 -out /tmp/k1.prv.pem
grep -v " PRIVATE KEY" /tmp/k1.prv.pem | base64pem -d | tail -c 32 | base32 | sed "s/=//g" > /tmp/k1.prv.key
"${openssl_cmd}" pkey -in /tmp/k1.prv.pem -pubout | grep -v " PUBLIC KEY" | base64pem -d | tail -c 32 | base32 | sed "s/=//g" > /tmp/k1.pub.key
## save variables
client_pub_key=$(cat /tmp/k1.pub.key)
client_priv_key=$(cat /tmp/k1.prv.key)
onion_hostaname_without_onion="${onion_hostname%.onion}"
client_priv_key_config="${onion_hostname%.onion}:descriptor:x25519:${client_priv_key}"
client_pub_key_config="descriptor:x25519:${client_pub_key}"
# Server side configuration
printf %s"${client_pub_key_config}\n" | tee "${tor_data_dir_services}"/"${service}"/authorized_clients/"${client}".auth >/dev/null
## Client side configuration
printf "<><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><>\n"
printf %s"service=${bold}${service}${nocolor}\n"
printf %s"client=${bold}${client}${nocolor}\n"
printf %s"onion_hostname=${bold}${magenta}${onion_hostname}${nocolor}\n"
printf %s"client_pub_key=${bold}${client_pub_key}${nocolor}\n"
printf %s"client_pub_key_config=${bold}${client_pub_key_config}${nocolor}\n"
printf %s"client_priv_key=${bold}${client_priv_key}${nocolor}\n"
printf %s"client_priv_key_config=${bold}${client_priv_key_config}${nocolor}\n"
printf "<><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><>\n"
## Delete pem and keys
rm -f /tmp/k1.pub.key /tmp/k1.prv.key /tmp/k1.prv.pem
}
[ "${service}" = "@all" ] && { create_service_list ; service="${service_name_list}" ; }
[ "${client}" = "@all" ] && error_msg "Client name cannot be @all, it is a restricted wildcard for referring to all clients, not a name per se."
if [ -n "${client_pub_key}" ]; then
test_service_exists "${service}"
client_pub_key_config="descriptor:x25519:${client_pub_key}"
printf %s"${client_pub_key_config}" | tee "${tor_data_dir_services}"/"${service}"/authorized_clients/"${client}".auth >/dev/null
notice "\nServer side authorization configured\n"
printf %s" client_pub_key_config=${client_pub_key_config}\n"
notice "\nAs you inserted the public key manually, we expect that the client already has the private key"
else
loop_list auth_server_add "${service}" "${client}"
fi
signal_tor
;;
## as the onion service operator, after making your onion service authenticated, you can also remove a specific client authorization
## if no clients are present, the service will be available to anyone that has the onion service address
f|off)
auth_server_remove_clients(){
service="${1}"
client="${2}"
#notice "Service: ${service}"
if [ "${client}" = "@all" ]; then
rm -fv "${tor_data_dir_services}"/"${service}"/authorized_clients/*.auth
else
rm -fv "${tor_data_dir_services}"/"${service}"/authorized_clients/"${client}".auth
fi
}
[ -z "${client}" ] && usage
[ -z "${service}" ] && error_msg "service is missing"
[ "${service}" != "@all" ] && check_service_name
if [ "${service}" = "@all" ]; then
notice "Removing client authorization for:"
notice "Service: @all"
create_service_list; service="${service_name_list}"
if [ "${client}" = "@all" ]; then
notice "Clients: @all.\nThe service is now accessible for anyone with the onion address.\n"
else
notice "If any client remains, the service will still be authenticated."
fi
else
notice "Removing client authorization for:"
notice "Service: ${service}"
if [ "${client}" = "@all" ]; then
notice "Clients: @all.\nThe service is now accessible for anyone with the onion address.\n"
else
notice "If any client remains, the service will still be authenticated."
fi
fi
loop_list auth_server_remove_clients "${service}" "${client}"
printf "\n"
signal_tor
;;
l|list)
auth_server_list(){
service="${1}"
test_service_exists "${service}"
create_client_list "${service}"
notice "\nService: ${service}"
if [ -n "${client_count}" ]; then
[ -n "${client_name_list}" ] && printf %s"Clients: ${client_name_list} (${client_count})\n"
for auth in "${tor_data_dir_services}/${service}/authorized_clients"/*; do
auth="${auth##*/}"
notice "${auth}: $(grep "descriptor:x25519:" "${tor_data_dir_services}"/"${service}"/authorized_clients/"${auth}")${nocolor}"
done
else
notice "Clients: NONE (0)"
fi
}
notice "${blue}Authorized clients for Hidden Services${nocolor}"
[ "${service}" = "@all" ] && { create_service_list; service="${service_name_list}"; }
loop_list auth_server_list "${service}"
;;
*)
error_msg "Invalid argument: status=${status}"
esac
;;
client)
case "${status}" in
## as the onion service client, add a key given by the onion service operator to authenticate yourself inside ClientOnionAuthDir
## The suffix '.auth_private' should not be mentioned, it will be automatically inserted when mentioning the name of the file.
## private key format must be: <onion-addr-without-.onion-part>:descriptor:x25519:<private-key>
## use the onion hostname as the file name, this avoid overriding the file by mistake and it indicates outside of the file for which service it refers to (of course it is written inside also)
## adding to Tor Browser automatically not supported yet
n|on)
[ -z "${onion}" ] && usage
## removes protocol such as http(s)://, ssh:// and git:// from the front of the address and trailing / at the end of the onion to clean it and only show the hostname (address.onion)
onion="$(printf %s"${onion}\n" | sed "s|.*://||" | sed "s|/$||")"
onion_hostaname_without_onion="${onion%.onion}"
[ "${onion_hostaname_without_onion%%*[^a-z2-7]*}" ] || error_msg "Onion domain is invalid, it is not within base32 alphabet lower-case encoding [a-z][2-7]"
[ "${#onion}" = "56" ] || error_msg "Onion domain is invalid, LENGTH=${#onion} is different than 56 characters (<56-char-base32>.onion)"
safe_edit tmp "${tor_conf}" && tor_conf_tmp="$(safe_edit getvar "${tor_conf}")"
grep -q "ClientOnionAuthDir" "${tor_conf_tmp}" && { printf %s"\nClientOnionAuthDir ${tor_data_dir_auth}\n\n" | tee -a "${tor_conf_tmp}"; }
"${su_cmd}" -u "${tor_user}" mkdir -p "${tor_data_dir_auth}"
if [ -z "${client_priv_key}" ]; then
## Generate pem and derive pub and priv keys
"${openssl_cmd}" genpkey -algorithm x25519 -out /tmp/k1.prv.pem
grep -v "PRIVATE KEY" /tmp/k1.prv.pem | base64pem -d | tail -c 32 | base32 | sed 's/=//g' > /tmp/k1.prv.key
"${openssl_cmd}" pkey -in /tmp/k1.prv.pem -pubout | grep -v "PUBLIC KEY" | base64pem -d | tail -c 32 | base32 | sed 's/=//g' > /tmp/k1.pub.key
## save variables
client_pub_key=$(cat /tmp/k1.pub.key)
client_priv_key=$(cat /tmp/k1.prv.key)
client_priv_key_config="${onion_hostaname_without_onion}:descriptor:x25519:${client_priv_key}"
client_pub_key_config="descriptor:x25519:${client_pub_key}"
## Delete pem and keys
rm -f /tmp/k1.pub.key /tmp/k1.prv.key /tmp/k1.prv.pem
# Client side configuration
printf %s"${client_priv_key_config}\n" | tee "${tor_data_dir_auth}"/"${onion}".auth_private >/dev/null
notice "${bold}Client side authorization configured${nocolor}"
notice "This is your private key, keep it safe, keep it hidden:"
notice "client_priv_key=${client_priv_key}"
notice "client_priv_key_config=${client_priv_key_config}"
notice "\n${bold}Now it depends on the service operator to authorize your client public key${nocolor}"
## Server side configuration
notice "Send the public key and instructions to the onion service operator of ${onion}"
notice "client_pub_key=${client_pub_key}"
notice "client_pub_key_config=descriptor:x25519:${client_pub_key}"
else
client_priv_key_config="${onion_hostaname_without_onion}:descriptor:x25519:${client_priv_key}"
printf %s"${client_priv_key_config}\n" | tee "${tor_data_dir_auth}"/"${onion}".auth_private >/dev/null
notice "\n${bold}Client side authorization configured${nocolor}"
notice "As you inserted the private key manually, it ise expected that you have already sent/received the public key to/from the onion service operator"
notice "client_priv_key_config=${client_priv_key_config}"
fi
;;
## as the onion service client, delete '.auth_private' files from ClientOnionAuthDir that are not valid or has no use anymore
f|off)
[ -z "${onion}" ] && usage
onion="$(printf %s"${onion}\n" | sed "s|.*://||" | sed "s|/.*$||")"
auth_client_remove(){
onion="${1}"
notice "\n${red}Removing ${tor_data_dir_auth}/${onion}.auth_private${nocolor}"
rm -fv "${tor_data_dir_auth}"/"${onion}".auth_private
}
if ! check_folder_is_not_empty "${tor_data_dir_auth}"; then
loop_list auth_client_remove "${onion}"
else
error_msg "ClientOnionAuthDir is empty"
fi
;;
l|list)
if ! check_folder_is_not_empty "${tor_data_dir_auth}"; then
notice "ClientOnionAuthDir ${tor_data_dir_auth}"
for auth in "${tor_data_dir_auth}"/*; do
auth="${auth##*/}"
notice "\nFile name: ${bold}${auth}${nocolor}"
notice "Content: ${bold}$(grep "descriptor:x25519:" "${tor_data_dir_auth}"/"${auth}")${nocolor}"
done
printf "\n"
else
error_msg "ClientOnionAuthDir is empty"
fi
;;
*)
error_msg "Invalid argument: status=${status}"
esac
;;
*)
error_msg "Invalid argument: host=${host}"
esac
;;