[go: up one dir, main page]

File: dns_cf.sh

package info (click to toggle)
acme.sh 3.1.1-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 2,704 kB
  • sloc: sh: 36,037; makefile: 12
file content (252 lines) | stat: -rwxr-xr-x 7,216 bytes parent folder | download
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
#!/usr/bin/env sh
# shellcheck disable=SC2034
dns_cf_info='CloudFlare
Site: CloudFlare.com
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_cf
Options:
 CF_Key API Key
 CF_Email Your account email
OptionsAlt:
 CF_Token API Token
 CF_Account_ID Account ID
 CF_Zone_ID Zone ID. Optional.
'

CF_Api="https://api.cloudflare.com/client/v4"

########  Public functions #####################

#Usage: add  _acme-challenge.www.domain.com   "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_cf_add() {
  fulldomain=$1
  txtvalue=$2

  CF_Token="${CF_Token:-$(_readaccountconf_mutable CF_Token)}"
  CF_Account_ID="${CF_Account_ID:-$(_readaccountconf_mutable CF_Account_ID)}"
  CF_Zone_ID="${CF_Zone_ID:-$(_readaccountconf_mutable CF_Zone_ID)}"
  CF_Key="${CF_Key:-$(_readaccountconf_mutable CF_Key)}"
  CF_Email="${CF_Email:-$(_readaccountconf_mutable CF_Email)}"

  if [ "$CF_Token" ]; then
    if [ "$CF_Zone_ID" ]; then
      _savedomainconf CF_Token "$CF_Token"
      _savedomainconf CF_Account_ID "$CF_Account_ID"
      _savedomainconf CF_Zone_ID "$CF_Zone_ID"
    else
      _saveaccountconf_mutable CF_Token "$CF_Token"
      _saveaccountconf_mutable CF_Account_ID "$CF_Account_ID"
      _clearaccountconf_mutable CF_Zone_ID
      _clearaccountconf CF_Zone_ID
    fi
  else
    if [ -z "$CF_Key" ] || [ -z "$CF_Email" ]; then
      CF_Key=""
      CF_Email=""
      _err "You didn't specify a Cloudflare api key and email yet."
      _err "You can get yours from here https://dash.cloudflare.com/profile."
      return 1
    fi

    if ! _contains "$CF_Email" "@"; then
      _err "It seems that the CF_Email=$CF_Email is not a valid email address."
      _err "Please check and retry."
      return 1
    fi
    #save the api key and email to the account conf file.
    _saveaccountconf_mutable CF_Key "$CF_Key"
    _saveaccountconf_mutable CF_Email "$CF_Email"

    _clearaccountconf_mutable CF_Token
    _clearaccountconf_mutable CF_Account_ID
    _clearaccountconf_mutable CF_Zone_ID
    _clearaccountconf CF_Token
    _clearaccountconf CF_Account_ID
    _clearaccountconf CF_Zone_ID

  fi

  _debug "First detect the root zone"
  if ! _get_root "$fulldomain"; then
    _err "invalid domain"
    return 1
  fi
  _debug _domain_id "$_domain_id"
  _debug _sub_domain "$_sub_domain"
  _debug _domain "$_domain"

  _debug "Getting txt records"
  _cf_rest GET "zones/${_domain_id}/dns_records?type=TXT&name=$fulldomain"

  if ! echo "$response" | tr -d " " | grep \"success\":true >/dev/null; then
    _err "Error"
    return 1
  fi

  # For wildcard cert, the main root domain and the wildcard domain have the same txt subdomain name, so
  # we can not use updating anymore.
  #  count=$(printf "%s\n" "$response" | _egrep_o "\"count\":[^,]*" | cut -d : -f 2)
  #  _debug count "$count"
  #  if [ "$count" = "0" ]; then
  _info "Adding record"
  if _cf_rest POST "zones/$_domain_id/dns_records" "{\"type\":\"TXT\",\"name\":\"$fulldomain\",\"content\":\"$txtvalue\",\"ttl\":120}"; then
    if _contains "$response" "$txtvalue"; then
      _info "Added, OK"
      return 0
    elif _contains "$response" "The record already exists"; then
      _info "Already exists, OK"
      return 0
    else
      _err "Add txt record error."
      return 1
    fi
  fi
  _err "Add txt record error."
  return 1

}

#fulldomain txtvalue
dns_cf_rm() {
  fulldomain=$1
  txtvalue=$2

  CF_Token="${CF_Token:-$(_readaccountconf_mutable CF_Token)}"
  CF_Account_ID="${CF_Account_ID:-$(_readaccountconf_mutable CF_Account_ID)}"
  CF_Zone_ID="${CF_Zone_ID:-$(_readaccountconf_mutable CF_Zone_ID)}"
  CF_Key="${CF_Key:-$(_readaccountconf_mutable CF_Key)}"
  CF_Email="${CF_Email:-$(_readaccountconf_mutable CF_Email)}"

  _debug "First detect the root zone"
  if ! _get_root "$fulldomain"; then
    _err "invalid domain"
    return 1
  fi
  _debug _domain_id "$_domain_id"
  _debug _sub_domain "$_sub_domain"
  _debug _domain "$_domain"

  _debug "Getting txt records"
  _cf_rest GET "zones/${_domain_id}/dns_records?type=TXT&name=$fulldomain&content=$txtvalue"

  if ! echo "$response" | tr -d " " | grep \"success\":true >/dev/null; then
    _err "Error: $response"
    return 1
  fi

  count=$(echo "$response" | _egrep_o "\"count\": *[^,]*" | cut -d : -f 2 | tr -d " ")
  _debug count "$count"
  if [ "$count" = "0" ]; then
    _info "Don't need to remove."
  else
    record_id=$(echo "$response" | _egrep_o "\"id\": *\"[^\"]*\"" | cut -d : -f 2 | tr -d \" | _head_n 1 | tr -d " ")
    _debug "record_id" "$record_id"
    if [ -z "$record_id" ]; then
      _err "Can not get record id to remove."
      return 1
    fi
    if ! _cf_rest DELETE "zones/$_domain_id/dns_records/$record_id"; then
      _err "Delete record error."
      return 1
    fi
    echo "$response" | tr -d " " | grep \"success\":true >/dev/null
  fi

}

####################  Private functions below ##################################
#_acme-challenge.www.domain.com
#returns
# _sub_domain=_acme-challenge.www
# _domain=domain.com
# _domain_id=sdjkglgdfewsdfg
_get_root() {
  domain=$1
  i=1
  p=1

  # Use Zone ID directly if provided
  if [ "$CF_Zone_ID" ]; then
    if ! _cf_rest GET "zones/$CF_Zone_ID"; then
      return 1
    else
      if echo "$response" | tr -d " " | grep \"success\":true >/dev/null; then
        _domain=$(echo "$response" | _egrep_o "\"name\": *\"[^\"]*\"" | cut -d : -f 2 | tr -d \" | _head_n 1 | tr -d " ")
        if [ "$_domain" ]; then
          _cutlength=$((${#domain} - ${#_domain} - 1))
          _sub_domain=$(printf "%s" "$domain" | cut -c "1-$_cutlength")
          _domain_id=$CF_Zone_ID
          return 0
        else
          return 1
        fi
      else
        return 1
      fi
    fi
  fi

  while true; do
    h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
    _debug h "$h"
    if [ -z "$h" ]; then
      #not valid
      return 1
    fi

    if [ "$CF_Account_ID" ]; then
      if ! _cf_rest GET "zones?name=$h&account.id=$CF_Account_ID"; then
        return 1
      fi
    else
      if ! _cf_rest GET "zones?name=$h"; then
        return 1
      fi
    fi

    if _contains "$response" "\"name\":\"$h\"" || _contains "$response" '"total_count":1'; then
      _domain_id=$(echo "$response" | _egrep_o "\[.\"id\": *\"[^\"]*\"" | _head_n 1 | cut -d : -f 2 | tr -d \" | tr -d " ")
      if [ "$_domain_id" ]; then
        _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
        _domain=$h
        return 0
      fi
      return 1
    fi
    p=$i
    i=$(_math "$i" + 1)
  done
  return 1
}

_cf_rest() {
  m=$1
  ep="$2"
  data="$3"
  _debug "$ep"

  email_trimmed=$(echo "$CF_Email" | tr -d '"')
  key_trimmed=$(echo "$CF_Key" | tr -d '"')
  token_trimmed=$(echo "$CF_Token" | tr -d '"')

  export _H1="Content-Type: application/json"
  if [ "$token_trimmed" ]; then
    export _H2="Authorization: Bearer $token_trimmed"
  else
    export _H2="X-Auth-Email: $email_trimmed"
    export _H3="X-Auth-Key: $key_trimmed"
  fi

  if [ "$m" != "GET" ]; then
    _debug data "$data"
    response="$(_post "$data" "$CF_Api/$ep" "" "$m")"
  else
    response="$(_get "$CF_Api/$ep")"
  fi

  if [ "$?" != "0" ]; then
    _err "error $ep"
    return 1
  fi
  _debug2 response "$response"
  return 0
}