-
Notifications
You must be signed in to change notification settings - Fork 1
/
pan_instcert
241 lines (217 loc) · 8.62 KB
/
pan_instcert
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
#!/bin/bash
## Logging
LOG="/var/log/pan_instcert.log"
wlog() {
printf "$*"
printf "[$(date --rfc-3339=seconds)]: $*" >> "$LOG"
}
trap 'wlog "ERROR - Certificate installation failed.\n"' TERM HUP
## Usage info
show_help() {
cat << EOF
Usage: ${0##*/} [-hv] -c CERT_CN [-n CERT_NAME] [OPTIONS] FQDN
This script uploads a certificate issued by ipa-getcert to a Palo Alto firewall
or Panorama and optionally adds it to up to two SSL/TLS Profiles.
FQDN Fully qualified name of the Palo Alto firewall or Panorama
interface. Must be reachable from this host on port TCP/443.
-c CERT_CN REQUIRED. Common Name (Subject) of the certificate, to find
the certificate and key files.
OPTIONS:
-n CERT_NAME Name of the certificate in PanOS configuration. Defaults to the
certificate Common Name.
-Y Append the current year '_YYYY' to the certificate name.
-k key(path|ext) API key file location or extension.
-p PROFILE_NAME Apply the certificate to a (primary) SSL/TLS Service Profile.
-s PROFILE_NAME Apply the certificate to a (secondary) SSL/TLS Service Profile.
-h Display this help and exit.
-v Verbose mode.
EOF
}
## Fixed variables
API_KEY="/etc/ipa/.panrc"
TEMP_PFX="/tmp/getcert_pkcs12.pfx"
TEMP_PWD=$(openssl rand -hex 15)
VERBOSE=0
OPTIND=1
## Read/interpret optional arguments
while getopts c:n:Yp:s:k:vh opt; do
case $opt in
c) CERT_CN=$OPTARG
;;
n) CERT_NAME=$OPTARG
;;
Y) YEAR="_$(date +'%Y')"
;;
k) API_KEY=$OPTARG
;;
p) if grep -q -P '(^[\w-]+$)' <<< "$OPTARG"; then
SSL_TLS_PROFILE_1=$OPTARG
else
printf 'ERROR: Invalid first SSL/TLS Profile name given: %s\n' "$OPTARG"
printf 'Ensure the name does not contain dots or spaces.\n'
exit 2
fi
;;
s) if grep -q -P '(^[\w-]+$)' <<< "$OPTARG"; then
SSL_TLS_PROFILE_2=$OPTARG
else
printf 'ERROR: Invalid second SSL/TLS Profile name given: %s\n' "$OPTARG"
printf 'Ensure the name does not contain dots or spaces.\n'
exit 2
fi
;;
v) VERBOSE=$((VERBOSE+1))
;;
h) show_help
exit 0
;;
*) show_help >&2
exit 1
;;
esac
done
shift "$((OPTIND-1))" # Discard the options and sentinel --
## This script must be run as root
if [ "$EUID" -ne 0 ]; then
echo "Please run as root"
exit
fi
## Start logging
wlog "START of pan_instcert.\n"
# Check that a Common Name name was given
if [[ -z $CERT_CN ]]; then
wlog "ERROR: Missing -c, a certificate requires a Common Name.\n\n"
show_help >&2
exit 1
elif grep -q -P '(?=^.{4,253}$)(^((?!-)[a-zA-Z0-9-]{1,63}(?<!-)\.)+[a-zA-Z]{2,63}$)' <<< "$CERT_CN"; then
CERT_CN=${CERT_CN,,}
wlog "Certificate Common Name: $CERT_CN\n"
fi
if [[ -z $CERT_NAME ]]; then
CERT_NAME=$CERT_CN
fi
# Append the current year to CERT_NAME if requested [-Y]
CERT_NAME="$CERT_NAME$YEAR"
# Check if an alternate API key file path or extension was given
if [[ "$API_KEY" != "/etc/ipa/.panrc" ]]; then
if [ -d "API_KEY" ]; then
# Parsed string is a directory
API_KEY="${API_KEY}/.panrc"
elif [[ "$API_KEY" != *\/* ]]; then
# Parsed string is a file extension
API_KEY="/etc/ipa/.panrc.$API_KEY"
fi
if [ -f "$API_KEY" ]; then
wlog "Parsed API key file exists: $API_KEY\n"
else
wlog "ERROR: Parsed API key file doesn't exist: $API_KEY\n"
show_help >&2
exit 1
fi
fi
(( $VERBOSE > 0 )) && wlog "Parsed certificate Name: $CERT_NAME\n"
# Check that a single valid FQDN was parsed for the Palo Alto API interface
if grep -q -P '(?=^.{4,253}$)(^((?!-)[a-zA-Z0-9-]{1,63}\.)+[a-zA-Z]{2,63}$)' <<< "$@"; then
PAN_MGMT="${@,,}"
(( $VERBOSE > 0 )) && wlog "PAN_FQDN: $PAN_MGMT\n"
if ! nc -z $PAN_MGMT 443 2>/dev/null; then
wlog "ERROR: Palo Alto device unreachable at: https://$PAN_MGMT/\n"
exit 4
fi
else
wlog "ERROR: A valid FQDN is required to issue a certificate.\n"
wlog "Parsed string: $@\n\n"
show_help >&2
exit 4
fi
# Verbosely log SSL/TLS profile names, only allow secondary if a primary is given
if [[ ! -z $SSL_TLS_PROFILE_1 ]]; then
(( $VERBOSE > 0 )) && wlog "Primary SSL/TLS Profile name: $SSL_TLS_PROFILE_1\n"
if [[ ! -z $SSL_TLS_PROFILE_2 ]]; then
(( $VERBOSE > 0 )) && wlog "Secondary SSL/TLS Profile name: $SSL_TLS_PROFILE_2\n"
fi
else
SSL_TLS_PROFILE_2=""
(( $VERBOSE > 0 )) && wlog "No SSL/TLS Profile names parsed.\n"
fi
## Requirements
# Installed packages: openssl, pan-python
# Verify the API key exists
if ! grep -q -P '^api_key=' "$API_KEY"; then
wlog "ERROR: File missing or no API key found in: $API_KEY\n"
exit 5
else
(( $VERBOSE > 0 )) && wlog "API key found in: $API_KEY\n"
# Commit the cardinal sin of changing the type of the variable from file-path to string.
API_KEY=$(grep -P '^api_key=' "$API_KEY")
API_KEY="${API_KEY#api_key=}"
fi
## Upload and install the certificate to Palo Alto firewall or Panorama.
# Verify state of the certificate is MONITORING or POST_SAVED_CERT
KEY_FILE="/etc/ssl/private/${CERT_CN}.key"
CRT_FILE="/etc/ssl/certs/${CERT_CN}.crt"
CERT_STATUS=$(ipa-getcert status -f "$CRT_FILE" -v)
# Verify both certificate and key files exist
if [[ ! -f "$KEY_FILE" ]] || [[ ! -f "$CRT_FILE" ]]; then
wlog "ERROR: Missing certificate or key files. Check if the following files exist:\n"
wlog "Key file: $KEY_FILE\nCert file: $CRT_FILE\n"
exit 10
elif ! [[ "$CERT_STATUS" =~ (MONITORING|POST_SAVED_CERT) ]]; then
wlog 'ERROR: Problem encountered with the requested certificate.\nInstallation aborted, verify that the certificate status is "MONITORING".\n'
exit 11
else
(( $VERBOSE > 0 )) && wlog "Certmonger certificate ${CERT_STATUS,}\n"
fi
printf '\n'
# Create a password protected PKCS12 file. If you need an intermediary certificate Manually load it on the Palo Alto firewall or Panorama.
openssl pkcs12 -export -out "$TEMP_PFX" -inkey "$KEY_FILE" -in "$CRT_FILE" -passout pass:$TEMP_PWD
# Upload it to the firewall
OUTPUT=$(curl -k --form file=@$TEMP_PFX "https://$PAN_MGMT/api/?type=import&category=certificate&certificate-name=$CERT_NAME&format=pkcs12&passphrase=$TEMP_PWD&key=$API_KEY" && echo " ")
CRT_STATUS=$?
wlog "XML API output for crt: $OUTPUT\n"
if [ $CRT_STATUS -eq 0 ]; then
OUTPUT=$(curl -k --form file=@$TEMP_PFX "https://$PAN_MGMT/api/?type=import&category=private-key&certificate-name=$CERT_NAME&format=pkcs12&passphrase=$TEMP_PWD&key=$API_KEY" && echo " ")
KEY_STATUS=$?
wlog "XML API output for key: $OUTPUT\n"
if [ $KEY_STATUS -eq 0 ]; then
wlog "Finished uploading certificate: $CERT_NAME\n"
elif [ $KEY_STATUS -ne 0 ]; then
wlog "ERROR: Upload of key failed.\n"
exit 12
fi
else
wlog "ERROR: Upload of certificate failed.\n"
exit 12
fi
rm "$TEMP_PFX"
# Install/update the certificate to given SSL/TLS Profile(s)
if [[ ! -z $SSL_TLS_PROFILE_1 ]]; then
RESULT=$(/usr/local/bin/panxapi.py -h $PAN_MGMT -K $API_KEY -S "<certificate>$CERT_NAME</certificate>" "/config/shared/ssl-tls-service-profile/entry[@name='$SSL_TLS_PROFILE_1']" 2>&1)
(( $VERBOSE > 0 )) && wlog "$RESULT\n"
if [[ "$RESULT" =~ (command succeeded) ]]; then
PROFILES=$SSL_TLS_PROFILE_1
wlog "Successfully updated SSL/TLS Profile $SSL_TLS_PROFILE_1\n"
else
wlog "ERROR: Update of SSL/TLS Profile $SSL_TLS_PROFILE_1 failed.\n"
fi
if [[ ! -z $SSL_TLS_PROFILE_2 ]]; then
RESULT=$(/usr/local/bin/panxapi.py -h $PAN_MGMT -K $API_KEY -S "<certificate>$CERT_NAME</certificate>" "/config/shared/ssl-tls-service-profile/entry[@name='$SSL_TLS_PROFILE_2']" 2>&1)
(( $VERBOSE > 0 )) && wlog "$RESULT\n"
if [[ "$RESULT" =~ (command succeeded) ]]; then
PROFILES="$PROFILES $SSL_TLS_PROFILE_2"
wlog "Successfully updated SSL/TLS Profile $SSL_TLS_PROFILE_2\n"
else
wlog "ERROR: Update of SSL/TLS Profile $SSL_TLS_PROFILE_2 failed.\n"
fi
fi
fi
if (( $VERBOSE > 0 )) && [[ ! -z $PROFILES ]]; then
wlog "$CERT_NAME has been set on SSL/TLS Profile(s): $PROFILES\n"
fi
## Save/commit the changes
wlog "Starting commit, please be patient.\n"
SECONDS=0
RESULT=$(/usr/local/bin/panxapi.py -h $PAN_MGMT -K $API_KEY -C '' --sync 2>&1)
wlog "$RESULT\n"
wlog "The commit took $SECONDS seconds to complete.\n"
wlog "END - Finished certificate installation to: $PAN_MGMT\n"