@@ -99,7 +99,7 @@ prompt_yn() {
9999 while true ; do
100100 echo " "
101101 echo -ne " ${BOLD}${question}${NC} ${yn_hint} ${DIM} [h=help]${NC} "
102- read -r answer
102+ read -r answer < /dev/tty 2> /dev/null || read -r answer
103103 answer=${answer:- $default }
104104 if [[ " $answer " =~ ^[Hh]$ ]]; then
105105 show_help_menu
@@ -123,7 +123,7 @@ prompt_input() {
123123 else
124124 echo -ne " ${BOLD}${question}${NC} ${DIM} (h=help)${NC} : " >&2
125125 fi
126- read -r result
126+ read -r result < /dev/tty 2> /dev/null || read -r result
127127 result=${result:- $default }
128128 if [[ " $result " =~ ^[Hh]$ ]]; then
129129 show_help_menu >&2
@@ -178,7 +178,7 @@ help_topic_header() {
178178help_press_enter () {
179179 echo " "
180180 echo -ne " ${DIM} Press Enter to go back...${NC} "
181- read -r
181+ read -r < /dev/tty 2> /dev/null || read -r || true
182182}
183183
184184help_topic_domain () {
@@ -1220,7 +1220,11 @@ do_harden() {
12201220 fi
12211221
12221222 configure_systemd_resolved_no_stub || true
1223- apply_service_hardening
1223+ if apply_service_hardening; then
1224+ print_ok " Runtime hardening applied"
1225+ else
1226+ print_warn " Runtime hardening reported issues; review systemctl status for dnstm units"
1227+ fi
12241228
12251229 echo " "
12261230 print_info " Current unit users:"
@@ -1802,19 +1806,66 @@ do_manage_users() {
18021806 continue
18031807 fi
18041808 echo " "
1809+ local user_created=false
18051810 if [[ -n " $new_pass " ]]; then
18061811 if timeout 30 sshtun-user create " $new_user " --insecure-password " $new_pass " 2>&1 ; then
18071812 print_ok " User '${new_user} ' created"
1813+ user_created=true
18081814 else
18091815 print_fail " Failed to create user '${new_user} ' (command timed out or failed)"
18101816 fi
18111817 else
18121818 if timeout 30 sshtun-user create " $new_user " < /dev/null 2>&1 ; then
18131819 print_ok " User '${new_user} ' created (random password assigned)"
1820+ user_created=true
18141821 else
18151822 print_fail " Failed to create user '${new_user} ' (command timed out or failed)"
18161823 fi
18171824 fi
1825+
1826+ # Generate slipnet:// URLs for SSH tunnels
1827+ if [[ " $user_created " == true ]]; then
1828+ # Get the actual password (if auto-generated, read it back)
1829+ local final_pass=" $new_pass "
1830+ if [[ -z " $final_pass " ]]; then
1831+ final_pass=$( sshtun-user show " $new_user " 2> /dev/null | grep -i pass | awk ' {print $NF}' || true)
1832+ fi
1833+ if [[ -n " $final_pass " ]]; then
1834+ echo " "
1835+ print_info " SlipNet SSH config URLs for user '${new_user} ':"
1836+ echo " "
1837+ # Find all SSH tunnels and generate URLs
1838+ local s_user=" " s_pass=" "
1839+ if detect_socks_auth; then
1840+ s_user=" $SOCKS_USER "
1841+ s_pass=" $SOCKS_PASS "
1842+ fi
1843+ local tunnel_domains
1844+ tunnel_domains=$( dnstm tunnel list 2> /dev/null || true)
1845+ # Get all unique base domains from tunnels
1846+ local domains
1847+ domains=$( echo " $tunnel_domains " | grep -o ' domain=[^ ]*' | sed ' s/domain=//;s/^[a-z]*\.//' | sort -u || true)
1848+ for dom in $domains ; do
1849+ DOMAIN=" $dom "
1850+ local pubkey=" "
1851+ # Find DNSTT pubkey for this domain
1852+ local dnstt_tag_name
1853+ dnstt_tag_name=$( echo " $tunnel_domains " | grep " domain=d\.${dom} " | grep -o ' tag=[^ ]*' | sed ' s/tag=//' || true)
1854+ if [[ -n " $dnstt_tag_name " && -f " /etc/dnstm/tunnels/${dnstt_tag_name} /server.pub" ]]; then
1855+ pubkey=$( cat " /etc/dnstm/tunnels/${dnstt_tag_name} /server.pub" 2> /dev/null || true)
1856+ fi
1857+ # Slipstream + SSH
1858+ local url
1859+ url=$( generate_slipnet_url " slipstream_ssh" " s" " " " $new_user " " $final_pass " " $s_user " " $s_pass " )
1860+ echo -e " ${GREEN} s.${dom} :${NC} ${url} "
1861+ # DNSTT + SSH
1862+ if [[ -n " $pubkey " ]]; then
1863+ url=$( generate_slipnet_url " dnstt_ssh" " ds" " $pubkey " " $new_user " " $final_pass " " $s_user " " $s_pass " )
1864+ echo -e " ${GREEN} ds.${dom} :${NC} ${url} "
1865+ fi
1866+ done
1867+ fi
1868+ fi
18181869 ;;
18191870 3)
18201871 echo " "
@@ -1850,10 +1901,15 @@ do_manage_users() {
18501901 echo " "
18511902 local del_user
18521903 del_user=$( prompt_input " Enter username to delete" )
1904+ del_user=$( echo " $del_user " | sed ' s/^[[:space:]]*//;s/[[:space:]]*$//' )
18531905 if [[ -z " $del_user " ]]; then
18541906 print_fail " Username cannot be empty"
18551907 continue
18561908 fi
1909+ if [[ " $del_user " == * " |" * ]]; then
1910+ print_fail " Username cannot contain the | character"
1911+ continue
1912+ fi
18571913 if prompt_yn " Are you sure you want to delete '${del_user} '?" " n" ; then
18581914 if timeout 30 sshtun-user delete " $del_user " 2>&1 ; then
18591915 print_ok " User '${del_user} ' deleted"
@@ -3170,24 +3226,39 @@ do_add_domain() {
31703226 local existing_domains
31713227 existing_domains=$( dnstm tunnel list 2> /dev/null | grep -o ' domain=[^ ]*' | sed ' s/domain=//;s/^[a-z0-9]*\.//' | sort -u || true)
31723228
3173- # Ask for new domain
3174- while true ; do
3175- DOMAIN=$( prompt_input " Enter the new backup domain (e.g. backup.com) " )
3229+ # Use domain from argument if provided, otherwise prompt
3230+ if [[ -n " $ADD_DOMAIN_ARG " ]] ; then
3231+ DOMAIN=" $ADD_DOMAIN_ARG "
31763232 DOMAIN=$( echo " $DOMAIN " | sed ' s|^[[:space:]]*||;s|[[:space:]]*$||;s|^https\?://||;s|/.*$||' )
3177- if [[ -z " $DOMAIN " ]]; then
3178- print_fail " Domain cannot be empty. Please try again."
3179- elif [[ ! " $DOMAIN " =~ \. ]]; then
3180- print_fail " Invalid domain (must contain a dot). Please try again."
3181- elif [[ " $DOMAIN " =~ \.\. ]]; then
3182- print_fail " Invalid domain (consecutive dots not allowed). Please try again."
3183- elif [[ ! " $DOMAIN " =~ ^[a-zA-Z0-9]([a-zA-Z0-9.-]* [a-zA-Z0-9])? $ ]]; then
3184- print_fail " Invalid domain (use only letters, numbers, dots, hyphens). Please try again."
3185- elif echo " $existing_domains " | grep -qx " $DOMAIN " ; then
3186- print_fail " Domain '${DOMAIN} ' is already in use by an existing tunnel. Please enter a different domain."
3187- else
3188- break
3233+ if [[ -z " $DOMAIN " ]] || [[ ! " $DOMAIN " =~ \. ]]; then
3234+ print_fail " Invalid domain: ${ADD_DOMAIN_ARG} "
3235+ exit 1
31893236 fi
3190- done
3237+ if [[ -n " $existing_domains " ]] && echo " $existing_domains " | grep -qx " $DOMAIN " ; then
3238+ print_fail " Domain '${DOMAIN} ' is already in use by an existing tunnel."
3239+ exit 1
3240+ fi
3241+ else
3242+ # Interactive prompt — reopen /dev/tty in case stdin is a pipe
3243+ while true ; do
3244+ echo -ne " ${BOLD} Enter the new backup domain (e.g. backup.com)${NC} ${DIM} (h=help)${NC} : " >&2
3245+ read -r DOMAIN < /dev/tty || { print_fail " Cannot read input (stdin is a pipe). Pass domain as argument: --add-domain example.com" ; exit 1; }
3246+ DOMAIN=$( echo " $DOMAIN " | sed ' s|^[[:space:]]*||;s|[[:space:]]*$||;s|^https\?://||;s|/.*$||' )
3247+ if [[ -z " $DOMAIN " ]]; then
3248+ print_fail " Domain cannot be empty. Please try again."
3249+ elif [[ ! " $DOMAIN " =~ \. ]]; then
3250+ print_fail " Invalid domain (must contain a dot). Please try again."
3251+ elif [[ " $DOMAIN " =~ \.\. ]]; then
3252+ print_fail " Invalid domain (consecutive dots not allowed). Please try again."
3253+ elif [[ ! " $DOMAIN " =~ ^[a-zA-Z0-9]([a-zA-Z0-9.-]* [a-zA-Z0-9])? $ ]]; then
3254+ print_fail " Invalid domain (use only letters, numbers, dots, hyphens). Please try again."
3255+ elif [[ -n " $existing_domains " ]] && echo " $existing_domains " | grep -qx " $DOMAIN " ; then
3256+ print_fail " Domain '${DOMAIN} ' is already in use by an existing tunnel. Please enter a different domain."
3257+ else
3258+ break
3259+ fi
3260+ done
3261+ fi
31913262
31923263 echo " "
31933264 print_ok " Domain: ${DOMAIN} "
@@ -3421,12 +3492,31 @@ do_add_domain() {
34213492 s_pass=" $SOCKS_PASS "
34223493 fi
34233494 slipnet_url=$( generate_slipnet_url " ss" " t" " " " " " " " $s_user " " $s_pass " )
3424- echo -e " ${GREEN}${slip_tag} :${NC} ${slipnet_url} "
3495+ echo -e " ${GREEN}${slip_tag} :${NC} ${slipnet_url} "
34253496 if [[ -n " $DNSTT_PUBKEY " ]]; then
34263497 slipnet_url=$( generate_slipnet_url " dnstt" " d" " $DNSTT_PUBKEY " " " " " " $s_user " " $s_pass " )
3427- echo -e " ${GREEN}${dnstt_tag} :${NC} ${slipnet_url} "
3498+ echo -e " ${GREEN}${dnstt_tag} :${NC} ${slipnet_url} "
3499+ fi
3500+
3501+ # Ask user for SSH credentials to generate SSH tunnel URLs
3502+ echo " "
3503+ if prompt_yn " Generate SSH tunnel slipnet:// URLs?" " y" ; then
3504+ local ssh_tun_user ssh_tun_pass
3505+ ssh_tun_user=$( prompt_input " SSH tunnel username" )
3506+ ssh_tun_pass=$( prompt_input " SSH tunnel password" )
3507+ if [[ " $ssh_tun_user " == * " |" * || " $ssh_tun_pass " == * " |" * ]]; then
3508+ print_fail " Username/password cannot contain the | character"
3509+ elif [[ -n " $ssh_tun_user " && -n " $ssh_tun_pass " ]]; then
3510+ slipnet_url=$( generate_slipnet_url " slipstream_ssh" " s" " " " $ssh_tun_user " " $ssh_tun_pass " " $s_user " " $s_pass " )
3511+ echo -e " ${GREEN}${slip_ssh_tag} :${NC} ${slipnet_url} "
3512+ if [[ -n " $DNSTT_PUBKEY " ]]; then
3513+ slipnet_url=$( generate_slipnet_url " dnstt_ssh" " ds" " $DNSTT_PUBKEY " " $ssh_tun_user " " $ssh_tun_pass " " $s_user " " $s_pass " )
3514+ echo -e " ${GREEN}${dnstt_ssh_tag} :${NC} ${slipnet_url} "
3515+ fi
3516+ else
3517+ echo -e " ${DIM} Skipped — username or password was empty.${NC} "
3518+ fi
34283519 fi
3429- echo -e " ${DIM} SSH tunnel slipnet:// URLs require credentials. Use --manage → Manage SSH users first.${NC} "
34303520 echo " "
34313521
34323522 echo -e " ${DIM} To add more domains, run again: sudo bash $0 --add-domain${NC} "
@@ -3436,6 +3526,7 @@ do_add_domain() {
34363526# ─── Parse Arguments ────────────────────────────────────────────────────────────
34373527
34383528ADD_DOMAIN_MODE=false
3529+ ADD_DOMAIN_ARG=" "
34393530HARDEN_ONLY_MODE=false
34403531MANAGE_USERS_MODE=false
34413532DNSTT_MTU=1232
@@ -3464,7 +3555,7 @@ while [[ $# -gt 0 ]]; do
34643555 ;;
34653556 --remove-tunnel)
34663557 # If $2 looks like another flag (starts with --), treat as no tag given
3467- if [[ -n " ${2:- } " && " ${2: 0: 2} " != " --" ]]; then
3558+ if [[ -n " ${2:- } " ]] && [[ " ${2: 0: 2} " != " --" ]]; then
34683559 do_remove_tunnel " $2 "
34693560 else
34703561 do_remove_tunnel " "
@@ -3477,7 +3568,13 @@ while [[ $# -gt 0 ]]; do
34773568 ;;
34783569 --add-domain)
34793570 ADD_DOMAIN_MODE=true
3480- shift
3571+ # Accept optional domain argument: --add-domain example.com
3572+ if [[ -n " ${2:- } " ]] && [[ ! " $2 " =~ ^-- ]]; then
3573+ ADD_DOMAIN_ARG=" $2 "
3574+ shift 2
3575+ else
3576+ shift
3577+ fi
34813578 ;;
34823579 --users)
34833580 MANAGE_USERS_MODE=true
@@ -3507,9 +3604,9 @@ done
35073604# ─── Validate conflicting flags ──────────────────────────────────────────────────
35083605
35093606mode_count=0
3510- [[ " $ADD_DOMAIN_MODE " == true ]] && (( mode_count++ ))
3511- [[ " $HARDEN_ONLY_MODE " == true ]] && (( mode_count++ ))
3512- [[ " $MANAGE_USERS_MODE " == true ]] && (( mode_count++ ))
3607+ [[ " $ADD_DOMAIN_MODE " == true ]] && (( mode_count++ )) || true
3608+ [[ " $HARDEN_ONLY_MODE " == true ]] && (( mode_count++ )) || true
3609+ [[ " $MANAGE_USERS_MODE " == true ]] && (( mode_count++ )) || true
35133610if [[ $mode_count -gt 1 ]]; then
35143611 echo " Error: --add-domain, --harden, and --users cannot be combined."
35153612 exit 1
0 commit comments