Steve Borba

My notes, I hope they help you, feel free to comment/add to them

Global Protect Portal – Let’s Encrypt

Portals work a little better when using a publicly signed certificate, but getting the funds for one isn’t always possible, especially not in the initial phase or for a home lab. So, I decided to use Let’s Encrypt, the signing is only for 90 days. So either you have to manual update, or do some scripting to automate it for the Palo Alto Networks firewall, but it is possible.

What I used:
1) RaspberryPi
2) DNS Domain/Name steveborba.com
3) Script to Keep Domain Name up to date
4) pip + pan-python
5) PA220

Setup the RaspberryPi – there is plenty of info about that out there, I’m not going to cover it.

DNS and Go Daddy (it’s inexpensive and works), I have this domain name, but my home is on DHCP, so I need to keep the address up to date. (I don’t want to use a dynamic dns service – PAN-DB would likely know it and I would not be able to access my house from anywhere I setup, I block them.) So I found this:

#!/bin/bash
# This script is used to check and update your GoDaddy DNS server to the IP address of your current internet connection.
# Thanks to mfox for his ps script
# https://github.com/markafox/GoDaddy_Powershell_DDNS
#
# First go to GoDaddy developer site to create a developer account and get your key and secret
#
# https://developer.godaddy.com/getstarted
# Be aware that there are 2 types of key and secret - one for the test server and one for the production server
# Get a key and secret for the production server
#
#Update the first 4 variables with your information
domain="steveborba.com"   # your domain
name="portal"     # name of A record to update
key=""      # key for godaddy developer API
secret=""   # secret for godaddy developer API
echo $(date)
headers="Authorization: sso-key $key:$secret"
# echo $headers
result=$(curl -k -s -X GET -H "$headers" "https://api.godaddy.com/v1/domains/$domain/records/A/$name")
# echo $result
dnsIp=$(echo $result | grep -oE "\b([0-9]{1,3}\.){3}[0-9]{1,3}\b")
# echo "dnsIp:" $dnsIp
# Get public ip address
ret=$(curl -s GET "http://ipinfo.io/json")
currentIp=$(echo $ret | grep -oE "\b([0-9]{1,3}\.){3}[0-9]{1,3}\b")
# echo "currentIp:" $currentIp
if [ "$dnsIp" != "$currentIp" ];
 then
        echo "IPs are not equal"
        curl -k -s -X PUT -H 'Content-Type: application/json' -H 'Accept: application/json' -H "Authorization: sso-key $key:$secret" "https://api.godaddy.com/v1/domains/$domain/records/A/$name" -d "[{\"data\": \"$currentIp\"}]"
fi
Put that in a cron job, and there you go, dynamic dns with go daddy!

Now we need something to interact with the firewall, in this case I used pan-python

apt install python-pip
pip install pan-python
panxapi.py -t '' -h <PAN-MGMT-IP> -l <USERNAME> -k >> ~/.panrc

You also need a PAN firewall – I have a PA220, sometimes I wish I had a VM (when I commit or reboot especially!). I can’t cover everything in this one post about setting it up, hopefully my other posts have enough to get you there though

First we need to forward port 80 on the outside to the pi, here is a snippet of the rules I created using cli commands:

set address <FQDN-OBJECT> fqdn <FQDN>
set address rpi2 ip-netmask <RPI-ADDRESS>
set rulebase nat rules "Certbot to RPi" from Outside to Outside source any destination <FQDN-OBJECT> nat-type ipv4 service tcp-80 group-tag Inbound tag Inbound description "Allow Certbot to generate a certificate" destination-translation translated-address rpi2
set rulebase security rules "Certbot to RPi" from Outside to Inside source any destination <FQDN-OBJECT> application web-browsing service application-default tag Inbound description "Temporary allow for Certbot to generate a certificate" group-tag Inbound action allow log-end yes log-setting default rule-type interzone profile-setting group Inbound
now we get cert bot to generate the certificate, package it in a pfx, and upload to pan, then set the service profile and commit
apt install certbot
certbot certonly -d <FQDN> -m steve@steveborba.com --standalone -n --agree-tos
cd /etc/letsencrypt/live/<FQDN>/
#This may need to be updated if certbot give you only two files
openssl pkcs12 -export -out full.pfx -inkey privkey.pem -in cert.pem -certfile chain.pem -passout pass:<RANDOM-KEY>
key=$(grep api_key ~/.panrc | head -n 1 | sed -r 's/=/\t/' | awk '{print $2}')
curl -k --form file=@full.pfx "https://<PAN-MGMT-IP>/api/?type=import&category=certificate&certificate-name=LetsEncryptPortalYYYYMM&format=pkcs12&passphrase=<RANDOM-KEY>&key=$key"
curl -k --form file=@full.pfx "https://<PAN-MGMT-IP>/api/?type=import&category=private-key&certificate-name=LetsEncryptPortalYYYYMM&format=pkcs12&passphrase=<RANDOM-KEY>&key=$key"
panxapi.py -h <PAN-MGMT-IP> -S '<certificate>LetsEncryptPortalYYYYMM</certificate>' "/config/shared/ssl-tls-service-profile/entry[@name='portal']"
panxapi.py -h <PAN-MGMT-IP> -C 'update cert' --sync

And here is the script to pu in a cron job:

#!/bin/bash
PANMGMTIP=IP_GOES_HERE
FQDN=FQDN
EMAIL=EMAIL
#Grab the first line with api_key and take everything after the first "="
key=$(grep api_key ~/.panrc | head -n 1 | sed -r 's/=/\t/' | awk '{print $2}')
panxapi.py -h $PANMGMTIP -K $key -S 'no' "/config/devices/entry/vsys/entry/rulebase/security/rules/entry[@name='Certbot to RPi']"
panxapi.py -h $PANMGMTIP -K $key -S 'no' "/config/devices/entry/vsys/entry/rulebase/nat/rules/entry[@name='Certbot to RPi']"
panxapi.py -h $PANMGMTIP -K $key -C '' --sync
certbot certonly -d $FQDN -m $EMAIL --standalone -n --agree-tos --force-renew
#You may need to update here, certbot may not give you a separate files for the cert and chain
openssl pkcs12 -export -out full.pfx -inkey /etc/letsencrypt/live/$FQDN/privkey.pem -in /etc/letsencrypt/live/$FQDN/cert.pem -certfile /etc/letsencrypt/live/$FQDN/chain.pem -passout pass:1wDbnY5DsH8CwKfOa
curl -k --form file=@full.pfx "https://$PANMGMTIP/api/?type=import&category=certificate&certificate-name=LetsEncryptPortal$(date +'%Y%m')&format=pkcs12&passphrase=1wDbnY5DsH8CwKfOa&key=$key"
curl -k --form file=@full.pfx "https://$PANMGMTIP/api/?type=import&category=private-key&certificate-name=LetsEncryptPortal$(date +'%Y%m')&format=pkcs12&passphrase=1wDbnY5DsH8CwKfOa&key=$key"
rm full.pfx
panxapi.py -h $PANMGMTIP -K $key -S 'yes' "/config/devices/entry/vsys/entry/rulebase/security/rules/entry[@name='Certbot to RPi']"
panxapi.py -h $PANMGMTIP -K $key -S 'yes' "/config/devices/entry/vsys/entry/rulebase/nat/rules/entry[@name='Certbot to RPi']"
panxapi.py -h $PANMGMTIP -K $key -S "LetsEncryptPortal$(date +'%Y%m')" "/config/shared/ssl-tls-service-profile/entry[@name='portal']"
panxapi.py -h $PANMGMTIP -K $key -C '' --sync




Thank you for the feed back Andrey! (thank you for letting me know I broke the comments function too 🙂 )

#!/bin/bash
PANMGMTIP=10.20.22.99
key=$(grep api_key ~/.panrc | head -n 1 | sed -r 's/=/\t/' | awk '{print $2}')
EMAIL=steve@steveborba.com

/usr/local/bin/panxapi.py -h $PANMGMTIP -K $key -S '<disabled>no</disabled>' "/config/devices/entry/vsys/entry/rulebase/security/rules/entry[@name='Certbot to Util']"
/usr/local/bin/panxapi.py -h $PANMGMTIP -K $key -S '<disabled>no</disabled>' "/config/devices/entry/vsys/entry/rulebase/nat/rules/entry[@name='Certbot to Util']"
/usr/local/bin/panxapi.py -h $PANMGMTIP -K $key -S '<disabled>no</disabled>' "/config/devices/entry/vsys/entry/rulebase/nat/rules/entry[@name='Certbot to Util 2']"
/usr/local/bin/panxapi.py -h $PANMGMTIP -K $key -S '<disabled>no</disabled>' "/config/devices/entry/vsys/entry/rulebase/pbf/rules/entry[@name='Track Certbot']"
/usr/local/bin/panxapi.py -h $PANMGMTIP -K $key -C '' --sync

##########################################################
#######  vpn.steveborba.com
###########################################################
#*************  Get Cert
certbot certonly -d vpn.steveborba.com,gateway1.steveborba.com,gateway2.steveborba.com -m $EMAIL --reuse-key --standalone -n --agree-tos --force-renew

#*************  Bundle and Copy Cert to PAN
/usr/bin/openssl pkcs12 -export -out /root/full.pfx -inkey /etc/letsencrypt/live/vpn.steveborba.com/privkey.pem -in /etc/letsencrypt/live/vpn.steveborba.com/cert.pem -certfile /etc/letsencrypt/live/vpn.steveborba.com/chain.pem -passout pass:1wDbnY5DsH8CwKfOa
/usr/bin/curl -k --form file=@/root/full.pfx "https://$PANMGMTIP/api/?type=import&category=certificate&certificate-name=vpn.steveborba.com&format=pkcs12&passphrase=1wDbnY5DsH8CwKfOa&key=$key"
/usr/bin/curl -k --form file=@/root/full.pfx "https://$PANMGMTIP/api/?type=import&category=private-key&certificate-name=vpn.steveborba.com&format=pkcs12&passphrase=1wDbnY5DsH8CwKfOa&key=$key"
rm /root/full.pfx

##########################################################
#######  Clean up/Activate
###########################################################
/usr/local/bin/panxapi.py -h $PANMGMTIP -K $key -S '<disabled>yes</disabled>' "/config/devices/entry/vsys/entry/rulebase/security/rules/entry[@name='Certbot to Util']"
/usr/local/bin/panxapi.py -h $PANMGMTIP -K $key -S '<disabled>yes</disabled>' "/config/devices/entry/vsys/entry/rulebase/nat/rules/entry[@name='Certbot to Util']"
/usr/local/bin/panxapi.py -h $PANMGMTIP -K $key -S '<disabled>yes</disabled>' "/config/devices/entry/vsys/entry/rulebase/nat/rules/entry[@name='Certbot to Util 2']"
/usr/local/bin/panxapi.py -h $PANMGMTIP -K $key -S '<disabled>yes</disabled>' "/config/devices/entry/vsys/entry/rulebase/pbf/rules/entry[@name='Track Certbot']"
/usr/local/bin/panxapi.py -h $PANMGMTIP -K $key -C '' --sync

2 Responses to “Global Protect Portal – Let’s Encrypt”

  1. Ryan Treser says:

    Awesome write up! Helped me a ton, one thing I noticed in your cron job script, your API call to add the cert to the TLS profile has incorrect syntax, need to add the “…” around the name, otherwise golden!!

    1. sjborbajr says:

      I am glad it helps! Thank you for the feedback.

Leave a Reply

Your email address will not be published.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>