]>
Commit | Line | Data |
---|---|---|
1 | #!/usr/bin/env sh | |
2 | ||
3 | # Here is a script to deploy cert to Synology DSM | |
4 | # | |
5 | # It requires following environment variables: | |
6 | # | |
7 | # SYNO_Username - Synology Username to login (must be an administrator) | |
8 | # SYNO_Password - Synology Password to login | |
9 | # SYNO_Certificate - Certificate description to target for replacement | |
10 | # | |
11 | # The following environmental variables may be set if you don't like their | |
12 | # default values: | |
13 | # | |
14 | # SYNO_Scheme - defaults to http | |
15 | # SYNO_Hostname - defaults to localhost | |
16 | # SYNO_Port - defaults to 5000 | |
17 | # SYNO_DID - device ID to skip OTP - defaults to empty | |
18 | # SYNO_TOTP_SECRET - TOTP secret to generate OTP - defaults to empty | |
19 | # | |
20 | # Dependencies: | |
21 | # ------------- | |
22 | # - jq and curl | |
23 | # - oathtool (When using 2 Factor Authentication and SYNO_TOTP_SECRET is set) | |
24 | # | |
25 | #returns 0 means success, otherwise error. | |
26 | ||
27 | ######## Public functions ##################### | |
28 | ||
29 | #domain keyfile certfile cafile fullchain | |
30 | synology_dsm_deploy() { | |
31 | ||
32 | _cdomain="$1" | |
33 | _ckey="$2" | |
34 | _ccert="$3" | |
35 | _cca="$4" | |
36 | ||
37 | _debug _cdomain "$_cdomain" | |
38 | ||
39 | # Get Username and Password, but don't save until we successfully authenticate | |
40 | _getdeployconf SYNO_Username | |
41 | _getdeployconf SYNO_Password | |
42 | _getdeployconf SYNO_Create | |
43 | _getdeployconf SYNO_DID | |
44 | _getdeployconf SYNO_TOTP_SECRET | |
45 | if [ -z "${SYNO_Username:-}" ] || [ -z "${SYNO_Password:-}" ]; then | |
46 | _err "SYNO_Username & SYNO_Password must be set" | |
47 | return 1 | |
48 | fi | |
49 | _debug2 SYNO_Username "$SYNO_Username" | |
50 | _secure_debug2 SYNO_Password "$SYNO_Password" | |
51 | ||
52 | # Optional scheme, hostname, and port for Synology DSM | |
53 | _getdeployconf SYNO_Scheme | |
54 | _getdeployconf SYNO_Hostname | |
55 | _getdeployconf SYNO_Port | |
56 | ||
57 | # default vaules for scheme, hostname, and port | |
58 | # defaulting to localhost and http because it's localhost... | |
59 | [ -n "${SYNO_Scheme}" ] || SYNO_Scheme="http" | |
60 | [ -n "${SYNO_Hostname}" ] || SYNO_Hostname="localhost" | |
61 | [ -n "${SYNO_Port}" ] || SYNO_Port="5000" | |
62 | ||
63 | _savedeployconf SYNO_Scheme "$SYNO_Scheme" | |
64 | _savedeployconf SYNO_Hostname "$SYNO_Hostname" | |
65 | _savedeployconf SYNO_Port "$SYNO_Port" | |
66 | ||
67 | _debug2 SYNO_Scheme "$SYNO_Scheme" | |
68 | _debug2 SYNO_Hostname "$SYNO_Hostname" | |
69 | _debug2 SYNO_Port "$SYNO_Port" | |
70 | ||
71 | # Get the certificate description, but don't save it until we verfiy it's real | |
72 | _getdeployconf SYNO_Certificate | |
73 | _debug SYNO_Certificate "${SYNO_Certificate:-}" | |
74 | ||
75 | # shellcheck disable=SC1003 # We are not trying to escape a single quote | |
76 | if printf "%s" "$SYNO_Certificate" | grep '\\'; then | |
77 | _err "Do not use a backslash (\) in your certificate description" | |
78 | return 1 | |
79 | fi | |
80 | ||
81 | _base_url="$SYNO_Scheme://$SYNO_Hostname:$SYNO_Port" | |
82 | _debug _base_url "$_base_url" | |
83 | ||
84 | _debug "Getting API version" | |
85 | response=$(_get "$_base_url/webapi/query.cgi?api=SYNO.API.Info&version=1&method=query&query=SYNO.API.Auth") | |
86 | api_version=$(echo "$response" | grep "SYNO.API.Auth" | sed -n 's/.*"maxVersion" *: *\([0-9]*\).*/\1/p') | |
87 | _debug3 response "$response" | |
88 | _debug3 api_version "$api_version" | |
89 | ||
90 | # Login, get the token from JSON and session id from cookie | |
91 | _info "Logging into $SYNO_Hostname:$SYNO_Port" | |
92 | encoded_username="$(printf "%s" "$SYNO_Username" | _url_encode)" | |
93 | encoded_password="$(printf "%s" "$SYNO_Password" | _url_encode)" | |
94 | ||
95 | otp_code="" | |
96 | if [ -n "$SYNO_TOTP_SECRET" ]; then | |
97 | otp_code="$(oathtool --base32 --totp "${SYNO_TOTP_SECRET}" 2>/dev/null)" | |
98 | fi | |
99 | ||
100 | if [ -n "$SYNO_DID" ]; then | |
101 | _H1="Cookie: did=$SYNO_DID" | |
102 | export _H1 | |
103 | _debug3 H1 "${_H1}" | |
104 | fi | |
105 | ||
106 | response=$(_post "method=login&account=$encoded_username&passwd=$encoded_password&api=SYNO.API.Auth&version=$api_version&enable_syno_token=yes&otp_code=$otp_code" "$_base_url/webapi/auth.cgi?enable_syno_token=yes") | |
107 | token=$(echo "$response" | grep "synotoken" | sed -n 's/.*"synotoken" *: *"\([^"]*\).*/\1/p') | |
108 | _debug3 response "$response" | |
109 | _debug token "$token" | |
110 | ||
111 | if [ -z "$token" ]; then | |
112 | _err "Unable to authenticate to $SYNO_Hostname:$SYNO_Port using $SYNO_Scheme." | |
113 | _err "Check your username and password." | |
114 | _err "If two-factor authentication is enabled for the user, set SYNO_TOTP_SECRET." | |
115 | return 1 | |
116 | fi | |
117 | sid=$(echo "$response" | grep "sid" | sed -n 's/.*"sid" *: *"\([^"]*\).*/\1/p') | |
118 | ||
119 | _H1="X-SYNO-TOKEN: $token" | |
120 | export _H1 | |
121 | _debug2 H1 "${_H1}" | |
122 | ||
123 | # Now that we know the username and password are good, save them | |
124 | _savedeployconf SYNO_Username "$SYNO_Username" | |
125 | _savedeployconf SYNO_Password "$SYNO_Password" | |
126 | _savedeployconf SYNO_DID "$SYNO_DID" | |
127 | _savedeployconf SYNO_TOTP_SECRET "$SYNO_TOTP_SECRET" | |
128 | ||
129 | _info "Getting certificates in Synology DSM" | |
130 | response=$(_post "api=SYNO.Core.Certificate.CRT&method=list&version=1&_sid=$sid" "$_base_url/webapi/entry.cgi") | |
131 | _debug3 response "$response" | |
132 | escaped_certificate="$(printf "%s" "$SYNO_Certificate" | sed 's/\([].*^$[]\)/\\\1/g;s/"/\\\\"/g')" | |
133 | _debug escaped_certificate "$escaped_certificate" | |
134 | id=$(echo "$response" | sed -n "s/.*\"desc\":\"$escaped_certificate\",\"id\":\"\([^\"]*\).*/\1/p") | |
135 | _debug2 id "$id" | |
136 | ||
137 | if [ -z "$id" ] && [ -z "${SYNO_Create:-}" ]; then | |
138 | _err "Unable to find certificate: $SYNO_Certificate and \$SYNO_Create is not set" | |
139 | return 1 | |
140 | fi | |
141 | ||
142 | # we've verified this certificate description is a thing, so save it | |
143 | _savedeployconf SYNO_Certificate "$SYNO_Certificate" "base64" | |
144 | ||
145 | _info "Generate form POST request" | |
146 | nl="\0015\0012" | |
147 | delim="--------------------------$(_utc_date | tr -d -- '-: ')" | |
148 | content="--$delim${nl}Content-Disposition: form-data; name=\"key\"; filename=\"$(basename "$_ckey")\"${nl}Content-Type: application/octet-stream${nl}${nl}$(cat "$_ckey")\0012" | |
149 | content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"cert\"; filename=\"$(basename "$_ccert")\"${nl}Content-Type: application/octet-stream${nl}${nl}$(cat "$_ccert")\0012" | |
150 | content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"inter_cert\"; filename=\"$(basename "$_cca")\"${nl}Content-Type: application/octet-stream${nl}${nl}$(cat "$_cca")\0012" | |
151 | content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"id\"${nl}${nl}$id" | |
152 | content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"desc\"${nl}${nl}${SYNO_Certificate}" | |
153 | if echo "$response" | sed -n "s/.*\"desc\":\"$escaped_certificate\",\([^{]*\).*/\1/p" | grep -- 'is_default":true' >/dev/null; then | |
154 | _debug2 default "this is the default certificate" | |
155 | content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"as_default\"${nl}${nl}true" | |
156 | else | |
157 | _debug2 default "this is NOT the default certificate" | |
158 | fi | |
159 | content="$content${nl}--$delim--${nl}" | |
160 | content="$(printf "%b_" "$content")" | |
161 | content="${content%_}" # protect trailing \n | |
162 | ||
163 | _info "Upload certificate to the Synology DSM" | |
164 | response=$(_post "$content" "$_base_url/webapi/entry.cgi?api=SYNO.Core.Certificate&method=import&version=1&SynoToken=$token&_sid=$sid" "" "POST" "multipart/form-data; boundary=${delim}") | |
165 | _debug3 response "$response" | |
166 | ||
167 | if ! echo "$response" | grep '"error":' >/dev/null; then | |
168 | if echo "$response" | grep '"restart_httpd":true' >/dev/null; then | |
169 | _info "http services were restarted" | |
170 | else | |
171 | _info "http services were NOT restarted" | |
172 | fi | |
173 | return 0 | |
174 | else | |
175 | _err "Unable to update certificate, error code $response" | |
176 | return 1 | |
177 | fi | |
178 | } |