@@ -3660,41 +3660,208 @@ step_ask_domain() {
36603660 print_ok " Using domain: ${BOLD}${DOMAIN}${NC} "
36613661}
36623662
3663+ # ─── Cloudflare API: Auto-create DNS records ────────────────────────────────────
3664+
3665+ # Create all required DNS records via Cloudflare API.
3666+ # Args: $1=API token, $2=domain, $3=server IP
3667+ cloudflare_create_dns_records () {
3668+ local api_token=" $1 "
3669+ local domain=" $2 "
3670+ local server_ip=" $3 "
3671+ local cf_api=" https://api.cloudflare.com/client/v4"
3672+
3673+ # Step 1: Get Zone ID
3674+ print_info " Looking up Cloudflare Zone ID for ${domain} ..."
3675+ local zone_resp
3676+ zone_resp=$( curl -s -X GET " ${cf_api} /zones?name=${domain} " \
3677+ -H " Authorization: Bearer ${api_token} " \
3678+ -H " Content-Type: application/json" --max-time 15 2> /dev/null || true)
3679+
3680+ if [[ -z " $zone_resp " ]]; then
3681+ print_fail " Could not connect to Cloudflare API"
3682+ return 1
3683+ fi
3684+
3685+ local zone_id
3686+ zone_id=$( echo " $zone_resp " | jq -r ' .result[0].id // empty' 2> /dev/null || true)
3687+ if [[ -z " $zone_id " ]]; then
3688+ local cf_err
3689+ cf_err=$( echo " $zone_resp " | jq -r ' .errors[0].message // "Unknown error"' 2> /dev/null || echo " Invalid response" )
3690+ print_fail " Could not find zone for ${domain} : ${cf_err} "
3691+ print_info " Make sure the domain is added to your Cloudflare account and the API token has Zone:Read permission"
3692+ return 1
3693+ fi
3694+ print_ok " Zone ID: ${zone_id} "
3695+
3696+ # Helper: create or skip a DNS record
3697+ local created=0 skipped=0 failed=0
3698+ _cf_create_record () {
3699+ local rtype=" $1 " rname=" $2 " rcontent=" $3 " proxied=" ${4:- false} "
3700+ local full_name=" ${rname} .${domain} "
3701+
3702+ # Check if record already exists
3703+ local existing
3704+ existing=$( curl -s -X GET " ${cf_api} /zones/${zone_id} /dns_records?name=${full_name} &type=${rtype} " \
3705+ -H " Authorization: Bearer ${api_token} " \
3706+ -H " Content-Type: application/json" --max-time 10 2> /dev/null || true)
3707+ local count
3708+ count=$( echo " $existing " | jq ' .result | length' 2> /dev/null || echo " 0" )
3709+
3710+ if [[ " $count " -gt 0 ]]; then
3711+ echo -e " ${DIM} [skip]${NC} ${rtype} ${rname} — already exists"
3712+ skipped=$(( skipped + 1 ))
3713+ return 0
3714+ fi
3715+
3716+ # Create the record
3717+ local payload
3718+ if [[ " $rtype " == " A" ]]; then
3719+ payload=$( jq -n --arg t " $rtype " --arg n " $full_name " --arg c " $rcontent " --argjson p " $proxied " \
3720+ ' {type: $t, name: $n, content: $c, ttl: 3600, proxied: $p}' )
3721+ else
3722+ payload=$( jq -n --arg t " $rtype " --arg n " $full_name " --arg c " $rcontent " \
3723+ ' {type: $t, name: $n, content: $c, ttl: 3600}' )
3724+ fi
3725+
3726+ local create_resp
3727+ create_resp=$( curl -s -X POST " ${cf_api} /zones/${zone_id} /dns_records" \
3728+ -H " Authorization: Bearer ${api_token} " \
3729+ -H " Content-Type: application/json" \
3730+ -d " $payload " --max-time 10 2> /dev/null || true)
3731+
3732+ local success
3733+ success=$( echo " $create_resp " | jq -r ' .success // false' 2> /dev/null || echo " false" )
3734+ if [[ " $success " == " true" ]]; then
3735+ echo -e " ${GREEN} [created]${NC} ${rtype} ${rname} → ${rcontent} "
3736+ created=$(( created + 1 ))
3737+ else
3738+ local err_msg
3739+ err_msg=$( echo " $create_resp " | jq -r ' .errors[0].message // "Unknown error"' 2> /dev/null || echo " API error" )
3740+ echo -e " ${RED} [failed]${NC} ${rtype} ${rname} : ${err_msg} "
3741+ failed=$(( failed + 1 ))
3742+ fi
3743+ }
3744+
3745+ # Step 2: Create A record
3746+ echo " "
3747+ print_info " Creating DNS records..."
3748+ echo " "
3749+ _cf_create_record " A" " ns" " $server_ip " " false"
3750+
3751+ # Step 3: Create NS records
3752+ local ns_target=" ns.${domain} "
3753+ local subdomains=(" t" " d" " n" " s" " ds" " z" )
3754+ for sub in " ${subdomains[@]} " ; do
3755+ _cf_create_record " NS" " $sub " " $ns_target "
3756+ done
3757+
3758+ echo " "
3759+ print_ok " Done: ${created} created, ${skipped} skipped, ${failed} failed"
3760+
3761+ if [[ $failed -gt 0 ]]; then
3762+ print_warn " Some records failed — check your Cloudflare dashboard"
3763+ return 1
3764+ fi
3765+ return 0
3766+ }
3767+
36633768# ─── STEP 3: Show DNS Records ──────────────────────────────────────────────────
36643769
36653770step_dns_records () {
36663771 print_step 3 " DNS Records (Cloudflare)"
36673772
3668- print_info " Create these DNS records in your Cloudflare dashboard:"
36693773 echo " "
3670- print_box \
3671- " Record 1: Type: A | Name: ns | Value: ${SERVER_IP} " \
3672- " Proxy: OFF (DNS Only - grey cloud)" \
3673- " " \
3674- " Record 2: Type: NS | Name: t | Value: ns.${DOMAIN} " \
3675- " Record 3: Type: NS | Name: d | Value: ns.${DOMAIN} " \
3676- " Record 4: Type: NS | Name: s | Value: ns.${DOMAIN} " \
3677- " Record 5: Type: NS | Name: ds | Value: ns.${DOMAIN} " \
3678- " Record 6: Type: NS | Name: n | Value: ns.${DOMAIN} " \
3679- " Record 7: Type: NS | Name: z | Value: ns.${DOMAIN} "
3680-
3774+ echo -e " ${BOLD} How do you want to set up DNS records?${NC} "
36813775 echo " "
3682- print_warn " IMPORTANT: The A record MUST be DNS Only (grey cloud, NOT orange)"
3683- print_warn " IMPORTANT: The A record name must be \" ns\" (not \" tns\" )"
3684- echo " "
3685- echo " Subdomain purposes:"
3686- echo " t = Slipstream + SOCKS tunnel"
3687- echo " d = DNSTT + SOCKS tunnel"
3688- echo " n = NoizDNS + SOCKS tunnel (DPI-resistant)"
3689- echo " s = Slipstream + SSH tunnel"
3690- echo " ds = DNSTT + SSH tunnel"
3691- echo " z = NoizDNS + SSH tunnel (DPI-resistant)"
3776+ echo -e " ${BOLD} 1)${NC} Automatic (Cloudflare API) ${DIM} — enter API token, records created for you${NC} "
3777+ echo -e " ${BOLD} 2)${NC} Manual ${DIM} — create records yourself in Cloudflare dashboard${NC} "
36923778 echo " "
3779+ local dns_choice
3780+ dns_choice=$( prompt_input " Select (1-2)" " 1" )
36933781
3694- if ! prompt_yn " Have you created these DNS records in Cloudflare?" " n" ; then
3782+ if [[ " $dns_choice " == " 1" ]]; then
3783+ # Automatic via Cloudflare API
36953784 echo " "
3696- print_info " Please create the DNS records and re-run this script."
3697- exit 0
3785+ echo -e " ${BOLD}${YELLOW} How to get a Cloudflare API Token:${NC} "
3786+ echo " "
3787+ echo -e " ${BOLD} 1.${NC} Go to: ${GREEN} https://dash.cloudflare.com/profile/api-tokens${NC} "
3788+ echo -e " ${BOLD} 2.${NC} Click ${BOLD} Create Token${NC} "
3789+ echo -e " ${BOLD} 3.${NC} Select the ${BOLD} Edit zone DNS${NC} template"
3790+ echo -e " ${BOLD} 4.${NC} Under ${BOLD} Zone Resources${NC} , select your domain (or All Zones)"
3791+ echo -e " ${BOLD} 5.${NC} Click ${BOLD} Continue to summary${NC} → ${BOLD} Create Token${NC} "
3792+ echo -e " ${BOLD} 6.${NC} Copy the token (you'll only see it once!)"
3793+ echo " "
3794+ echo -e " ${DIM} Required permissions: Zone:DNS:Edit + Zone:Zone:Read${NC} "
3795+ echo -e " ${DIM} The 'Edit zone DNS' template includes both automatically${NC} "
3796+ echo " "
3797+
3798+ local cf_token
3799+ cf_token=$( prompt_input " Paste your Cloudflare API Token here" )
3800+ cf_token=$( echo " $cf_token " | sed ' s/^[[:space:]]*//;s/[[:space:]]*$//' )
3801+
3802+ if [[ -z " $cf_token " ]]; then
3803+ print_fail " API token cannot be empty"
3804+ exit 1
3805+ fi
3806+
3807+ # Validate token
3808+ print_info " Validating API token..."
3809+ local verify_resp
3810+ verify_resp=$( curl -s -X GET " https://api.cloudflare.com/client/v4/user/tokens/verify" \
3811+ -H " Authorization: Bearer ${cf_token} " \
3812+ -H " Content-Type: application/json" --max-time 10 2> /dev/null || true)
3813+ local token_status
3814+ token_status=$( echo " $verify_resp " | jq -r ' .result.status // empty' 2> /dev/null || true)
3815+
3816+ if [[ " $token_status " != " active" ]]; then
3817+ print_fail " API token is invalid or expired"
3818+ print_info " Check your token at: https://dash.cloudflare.com/profile/api-tokens"
3819+ exit 1
3820+ fi
3821+ print_ok " API token is valid"
3822+
3823+ # Create DNS records
3824+ if cloudflare_create_dns_records " $cf_token " " $DOMAIN " " $SERVER_IP " ; then
3825+ print_ok " All DNS records created successfully"
3826+ else
3827+ echo " "
3828+ if ! prompt_yn " Some records failed. Continue anyway?" " n" ; then
3829+ exit 1
3830+ fi
3831+ fi
3832+ else
3833+ # Manual setup
3834+ print_info " Create these DNS records in your Cloudflare dashboard:"
3835+ echo " "
3836+ print_box \
3837+ " Record 1: Type: A | Name: ns | Value: ${SERVER_IP} " \
3838+ " Proxy: OFF (DNS Only - grey cloud)" \
3839+ " " \
3840+ " Record 2: Type: NS | Name: t | Value: ns.${DOMAIN} " \
3841+ " Record 3: Type: NS | Name: d | Value: ns.${DOMAIN} " \
3842+ " Record 4: Type: NS | Name: s | Value: ns.${DOMAIN} " \
3843+ " Record 5: Type: NS | Name: ds | Value: ns.${DOMAIN} " \
3844+ " Record 6: Type: NS | Name: n | Value: ns.${DOMAIN} " \
3845+ " Record 7: Type: NS | Name: z | Value: ns.${DOMAIN} "
3846+
3847+ echo " "
3848+ print_warn " IMPORTANT: The A record MUST be DNS Only (grey cloud, NOT orange)"
3849+ print_warn " IMPORTANT: The A record name must be \" ns\" (not \" tns\" )"
3850+ echo " "
3851+ echo " Subdomain purposes:"
3852+ echo " t = Slipstream + SOCKS tunnel"
3853+ echo " d = DNSTT + SOCKS tunnel"
3854+ echo " n = NoizDNS + SOCKS tunnel (DPI-resistant)"
3855+ echo " s = Slipstream + SSH tunnel"
3856+ echo " ds = DNSTT + SSH tunnel"
3857+ echo " z = NoizDNS + SSH tunnel (DPI-resistant)"
3858+ echo " "
3859+
3860+ if ! prompt_yn " Have you created these DNS records in Cloudflare?" " n" ; then
3861+ echo " "
3862+ print_info " Please create the DNS records and re-run this script."
3863+ exit 0
3864+ fi
36983865 fi
36993866
37003867 echo " "
@@ -4949,30 +5116,51 @@ do_add_domain() {
49495116 print_ok " Domain: ${DOMAIN} "
49505117 echo " "
49515118
4952- # DNS record instructions
5119+ # DNS record setup
49535120 print_header " DNS Records for ${DOMAIN} "
49545121
4955- print_info " Create these records in Cloudflare for ${BOLD}${DOMAIN}${NC} :"
49565122 echo " "
4957- print_box \
4958- " Record 1: Type: A | Name: ns | Value: ${SERVER_IP} " \
4959- " Proxy: OFF (DNS Only - grey cloud)" \
4960- " " \
4961- " Record 2: Type: NS | Name: t | Value: ns.${DOMAIN} " \
4962- " Record 3: Type: NS | Name: d | Value: ns.${DOMAIN} " \
4963- " Record 4: Type: NS | Name: s | Value: ns.${DOMAIN} " \
4964- " Record 5: Type: NS | Name: ds | Value: ns.${DOMAIN} " \
4965- " Record 6: Type: NS | Name: n | Value: ns.${DOMAIN} " \
4966- " Record 7: Type: NS | Name: z | Value: ns.${DOMAIN} "
4967-
5123+ echo -e " ${BOLD} How do you want to set up DNS records?${NC} "
49685124 echo " "
4969- print_warn " IMPORTANT: The A record MUST be DNS Only (grey cloud, NOT orange)"
5125+ echo -e " ${BOLD} 1)${NC} Automatic (Cloudflare API)"
5126+ echo -e " ${BOLD} 2)${NC} Manual (create in dashboard)"
49705127 echo " "
5128+ local dns_choice
5129+ dns_choice=$( prompt_input " Select (1-2)" " 2" )
49715130
4972- if ! prompt_yn " Have you created these DNS records in Cloudflare?" " n" ; then
5131+ if [[ " $dns_choice " == " 1" ]]; then
5132+ local cf_token
5133+ cf_token=$( prompt_input " Cloudflare API Token" )
5134+ cf_token=$( echo " $cf_token " | sed ' s/^[[:space:]]*//;s/[[:space:]]*$//' )
5135+ if [[ -n " $cf_token " ]]; then
5136+ cloudflare_create_dns_records " $cf_token " " $DOMAIN " " $SERVER_IP " || true
5137+ else
5138+ print_fail " API token cannot be empty"
5139+ exit 1
5140+ fi
5141+ else
5142+ print_info " Create these records in Cloudflare for ${BOLD}${DOMAIN}${NC} :"
49735143 echo " "
4974- print_info " Please create the DNS records and re-run: sudo bash $0 --add-domain"
4975- exit 0
5144+ print_box \
5145+ " Record 1: Type: A | Name: ns | Value: ${SERVER_IP} " \
5146+ " Proxy: OFF (DNS Only - grey cloud)" \
5147+ " " \
5148+ " Record 2: Type: NS | Name: t | Value: ns.${DOMAIN} " \
5149+ " Record 3: Type: NS | Name: d | Value: ns.${DOMAIN} " \
5150+ " Record 4: Type: NS | Name: s | Value: ns.${DOMAIN} " \
5151+ " Record 5: Type: NS | Name: ds | Value: ns.${DOMAIN} " \
5152+ " Record 6: Type: NS | Name: n | Value: ns.${DOMAIN} " \
5153+ " Record 7: Type: NS | Name: z | Value: ns.${DOMAIN} "
5154+
5155+ echo " "
5156+ print_warn " IMPORTANT: The A record MUST be DNS Only (grey cloud, NOT orange)"
5157+ echo " "
5158+
5159+ if ! prompt_yn " Have you created these DNS records in Cloudflare?" " n" ; then
5160+ echo " "
5161+ print_info " Please create the DNS records and re-run: sudo bash $0 --add-domain"
5162+ exit 0
5163+ fi
49765164 fi
49775165
49785166 echo " "
0 commit comments