Sample configurations for Juniper ZTP with ISC Kea DHCP
- Install the latest Kea DHCP-server and open source hooks from the official repository.
- The example configuration has been tested with Ubuntu 22.04 LTS and with Kea 2.4.0
- The sample configuration takes the client's MAC-address and flexibly creates option 43 encapsulated options
- Flex option is used to dynamically create the name of the configuration file
- Other options are generated in client classification block such as image file name. This can not be dynamically set on Juniper devices because some models expect the name of the image file to follow certain naming conventions. Best bet is to use the official name eg. the exact same name it has on the Juniper download page, otherwise the installation process fails with varying error codes such as disk space warnings.
- To use the flexible options with the provided HTTP-download, an additional software is needed which processes the HTTP-request
- The device attempts to request config and image file with it's vendor class identifier + MAC-address from the HTTP-server
- Correct image and config files can be served by saving the files with corresponding names to the HTTP-server
Dynamic serving can be achieved by passing the request to some backend application such as Django, which parses the fields from the URL. Combine Django with nginx and ¨X-Accel-Redirect¨-header to handle the logic within Python and just serve the static files with nginx. This example uses nginx to catch the actual request, pass it to Django backend which can then figure out what to do with the device with given vendor class identifier and MAC. If Django responds to nginx's internal request with a valid ¨X-Accel-Redirect¨-header, nginx will internally fetch that URI and serve it's contents transparently to the client. Using just nginx is also fine.
# Just normal proxy pass to Django
location /ztp_fetch_config {
# Include an ACL here if needed
# include /etc/nginx/snippets/ztp_acl.conf;
proxy_pass http://127.0.0.1:8000/device/ztp_fetch_config;
}
# X-Accel-Redirect magic :)
# See: https://www.nginx.com/resources/wiki/start/topics/examples/x-accel/
location /protected_ztp_files {
internal;
alias /opt/ztp/ztp;
}
path('ztp_fetch_config/<mode>/<vci>/<mac>', views.ztp_fetch_config, name='ztp_fetch_config')
# Nginx X-Accel-Redirect for device configs
def ztp_fetch_config(request, vci, mac):
logger.info(f"ZTP configuration download requested with parameters: {request.GET}")
response = HttpResponse()
# Validate MAC-address
if not re.match(r'^([0-9a-f]{2}[:-]){5}([0-9a-f]){2}$', mac, re.IGNORECASE):
logger.warning(f"Requested ZTP configuration file {mac} failed name validation.")
return HttpResponseNotFound()
# Check that the file exists
if not os.path.exists(f"/opt/ztp/ztp/configs/{mac}"):
logger.warning(f"Requested ZTP configuration file {mac} does not exist.")
return HttpResponseNotFound()
logger.info(f"Serving requested ZTP configuration file for {vci} -> {mac}")
response['Content-Type'] = ''
response["X-Accel-Buffering"] = "no"
response['Content-Disposition'] = f'attachment; filename={mac}'
response["X-Accel-Redirect"] = f"/protected_ztp_files/configs/{mac}"
return response