-
Notifications
You must be signed in to change notification settings - Fork 0
/
scow_hook.sh
199 lines (174 loc) · 6.57 KB
/
scow_hook.sh
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
#!/usr/bin/env bash
set -e
set -o pipefail
source ./scow-hook.auth # contains user and pass variables
SCOW_User=${user}
SCOW_Pass=${pass}
WAIT=15 # time in seconds to wait after each request
function deploy_challenge() {
# Parameters:
# - SIGN_DOMAIN
# The domain name (CN or subject alternative name) being
# validated.
# - TOKEN_FILENAME
# The name of the file is irrelevant for the DNS challenge, yet still provided
# - TOKEN_VALUE
# The token value that needs to be served for validation. For DNS
# validation, this is what you want to put in the _acme-challenge
# TXT record. For HTTP validation it is the value that is expected
# be found in the $TOKEN_FILENAME file.
if ! which dig > /dev/null; then
echo -e "\n\t ERROR: Cannot find dig !!!\n\t(Debian and derivates: dnsutils; CentOS, Arch, Alpine: bind-tools) \n"
return 1
fi
# Check for credentials
if [[ -z "$SCOW_Pass" ]] || [[ -z "$SCOW_User" ]]; then
SCOW_Pass=""
SCOW_User=""
echo -e "\n\t ERROR: No Servercow login data provided. \n"
echo -e "\n\t ERROR: Please create a new user with access to the DNS API. \n"
return 1
fi
local SIGN_DOMAIN="${1}"
local TOKEN_FILENAME="${2}"
local TOKEN_VALUE="${3}"
[[ ! -z ${4} ]] && SLEEP=n || SLEEP=y
# Start with SLD=SIGN_DOMAIN and break down SLD until it equals to the second level domain
local SLD=${SIGN_DOMAIN}
#until [[ -z $(dig ns ${SLD} +short | grep -viE 'ns.+.servercow.de') ]]; do
until [[ ! -z $(dig ns ${SLD} +short | grep -iE 'ns.+\.servercow\.de') ]]; do
SLD=${SLD#*.}
if [ $(echo ${SLD} | awk -F. '{print NF-1}') -lt 1 ]; then
echo -e "\n\t ERROR: Cannot determine root domain with a Servercow NS record for ${SIGN_DOMAIN} \n"
return 1
fi
done
echo -e "\t Creating challenge record for ${SIGN_DOMAIN} using root domain ${SLD}"
api_return=$(curl -sX POST "https://api.servercow.de/dns/v1/domains/${SLD}" \
-H "X-Auth-Username: ${SCOW_User}" \
-H "X-Auth-Password: ${SCOW_Pass}" \
-H "Content-Type: application/json" \
--data "{\"type\":\"TXT\",\"name\":\"_acme-challenge.${SIGN_DOMAIN}\",\"content\":\"${TOKEN_VALUE}\",\"ttl\":0}")
echo ${api_return} | grep -qi '{"message":"ok"}'
if [[ $? != 0 ]]; then
echo -e "\n\t ERROR: Post to API failed: ${api_return} \n"
return 1
else
echo -e "\t API call succeeded: ${api_return}"
if [[ ${SLEEP} == "y" ]]; then
echo -e "\t Sleeping ${WAIT} seconds ..."
sleep ${WAIT}
fi
fi
}
function clean_challenge() {
local SIGN_DOMAIN="${1}" TOKEN_FILENAME="${2}" TOKEN_VALUE="${3}"
# Start with SLD=SIGN_DOMAIN and break down SLD until it equals to the second level domain
local SLD=${SIGN_DOMAIN}
#until [[ -z $(dig ns ${SLD} +short | grep -viE 'ns.+.servercow.de') ]]; do
until [[ ! -z $(dig ns ${SLD} +short | grep -iE 'ns.+\.servercow\.de') ]]; do
SLD=${SLD#*.}
if [ $(echo ${SLD} | awk -F. '{print NF-1}') -lt 1 ]; then
echo -e "\n\t ERROR: Cannot determine root domain with a Servercow NS record for ${SIGN_DOMAIN} \n"
return 1
fi
done
echo -e "\t Deleting challenge record for ${SIGN_DOMAIN} using root domain ${SLD}"
api_return=$(curl -sX DELETE "https://api.servercow.de/dns/v1/domains/${SLD}" \
-H "X-Auth-Username: ${SCOW_User}" \
-H "X-Auth-Password: ${SCOW_Pass}" \
-H "Content-Type: application/json" \
--data "{\"type\":\"TXT\",\"name\":\"_acme-challenge.${SIGN_DOMAIN}\"}")
echo ${api_return} | grep -qi '{"message":"ok"}'
if [[ $? != 0 ]]; then
echo -e "\n\t ERROR: Post to API failed: ${api_return} \n"
return 1
else
echo -e "\t API call succeeded: ${api_return}"
fi
}
deploy_cert() {
local DOMAIN="${1}" KEYFILE="${2}" CERTFILE="${3}" FULLCHAINFILE="${4}" CHAINFILE="${5}" TIMESTAMP="${6}"
# This hook is called once for each certificate that has been
# produced. Here you might, for instance, copy your new certificates
# to service-specific locations and reload the service.
#
# Parameters:
# - DOMAIN
# The primary domain name, i.e. the certificate common
# name (CN).
# - KEYFILE
# The path of the file containing the private key.
# - CERTFILE
# The path of the file containing the signed certificate.
# - FULLCHAINFILE
# The path of the file containing the full certificate chain.
# - CHAINFILE
# The path of the file containing the intermediate certificate(s).
# - TIMESTAMP
# Timestamp when the specified certificate was created.
}
unchanged_cert() {
local DOMAIN="${1}" KEYFILE="${2}" CERTFILE="${3}" FULLCHAINFILE="${4}" CHAINFILE="${5}"
# This hook is called once for each certificate that is still
# valid and therefore wasn't reissued.
#
# Parameters:
# - DOMAIN
# The primary domain name, i.e. the certificate common
# name (CN).
# - KEYFILE
# The path of the file containing the private key.
# - CERTFILE
# The path of the file containing the signed certificate.
# - FULLCHAINFILE
# The path of the file containing the full certificate chain.
# - CHAINFILE
# The path of the file containing the intermediate certificate(s).
}
invalid_challenge() {
local DOMAIN="${1}" RESPONSE="${2}"
# This hook is called if the challenge response has failed, so domain
# owners can be aware and act accordingly.
#
# Parameters:
# - DOMAIN
# The primary domain name, i.e. the certificate common
# name (CN).
# - RESPONSE
# The response that the verification server returned
}
request_failure() {
local STATUSCODE="${1}" REASON="${2}" REQTYPE="${3}"
# This hook is called when an HTTP request fails (e.g., when the ACME
# server is busy, returns an error, etc). It will be called upon any
# response code that does not start with '2'. Useful to alert admins
# about problems with requests.
#
# Parameters:
# - STATUSCODE
# The HTML status code that originated the error.
# - REASON
# The specified reason for the error.
# - REQTYPE
# The kind of request that was made (GET, POST...)
}
startup_hook() {
echo -e "\n\t\t $(date +%F' '%X): Start checking certs ...\n"
}
exit_hook() {
echo -e "\n\t\t $(date +%F' '%X): Finished checking certs\n"
}
HANDLER="$1"; shift
if [[ "${HANDLER}" =~ ^(deploy_challenge|clean_challenge|deploy_cert|unchanged_cert|startup_hook|exit_hook)$ ]]; then
LOOPS=$(($(echo "$@" | wc -w) / 3))
LOOP=0
echo "$@" | xargs -n3 | while read line; do
let LOOP=LOOP+1
if [ ${LOOP} -lt ${LOOPS} ]; then
$HANDLER ${line} no_sleep
else
$HANDLER ${line}
fi
done
fi