-
Notifications
You must be signed in to change notification settings - Fork 339
Middleware
This feature requires Munki version 2.7.0 or greater.
This optional feature allows a Munki admin to use third party code, or create their own code to manipulate Munki's HTTP requests. The primary use case for this feature is to provide interaction with APIs, although it is not limited to that. Middleware is activated by the presence of a Python file. If it's there it gets called, if it's not it won't, it's as simple as that.
Middleware gets imported at runtime as a Python module so the middleware file must be written in Python. Nothing is stopping you from then calling another executable from Python, so in that sense, you're not limited to Python. At the beginning of the Munki run, Munki searches for "middleware*.py", it then tries to import it then looks for the function, "process_request_options". The options are then sent through, examined, changed, or not, before heading to the server.
You may only use one middleware file. If you have more then one of middleware file you will have unpredictable results.
For the middleware to work, you will need the following:
Munki is looking for files the start with "middleware" and end with ".py". Examples of good and bad middleware filenames:
- middleware.py👍
-
middleware👎 -
my_middleware.py👎 - middleware_cloudfoo.py👍
The middleware file must live in the root of the munkitools folder (/usr/local/munki).
Root must be the owner of the file and be able to read it. Since its imported by python, the executable part isn't necessary. It's important to note that if you plan on storing sensitive information inside you should restrict access.
sudo chown root /usr/local/munki/middleware*.py
sudo chmod 600 /usr/local/munki/middleware*.py
This is the function that Munki is looking for. Think of it as the "main" fuction for the middleware. If Munki doesn't find this function it will abandon the middleware, and continue on as if it wasn't there.
Example function
def process_request_options(options):
print('***Requesting: %s' % options.get('url'))
return options
This is a basic example: Munki sends the request options, we print the requested url and then return the options unchanged.
process_request_options
has a single input parameter: the options dictionary (described in detail below). It must return the options dictionary (possibly modified).
Query Params Example
from urllib import urlencode
QUERY_PARAMS = {"username": "apiuser", "password": "secret password"}
def encode_params(data):
"""Encode parameters in a piece of data.
Will successfully encode parameters when passed as a dict or a list of
2-tuples. Order is retained if data is a list of 2-tuples but arbitrary
if parameters are supplied as a dict.
"""
result = []
for k, vs in QUERY_PARAMS.items():
if isinstance(vs, basestring) or not hasattr(vs, "__iter__"):
vs = [vs]
for v in vs:
if v is not None:
result.append(
(
k.encode("utf-8") if isinstance(k, str) else k,
v.encode("utf-8") if isinstance(v, str) else v,
)
)
return urlencode(result, doseq=True)
def process_request_options(options):
url = options.get("url")
if "swscan.apple.com" not in url:
print("URL Before: %s" % options.get("url"))
# concat url and query string
options["url"] = options["url"] + "?" + encode_params(QUERY_PARAMS)
print("URL After: %s" % options.get("url"))
return options
In the example above we are joining the URL from Munki and query string together.
http://munki.example.com/catalogs/production
http://munki.example.com/catalogs/production?username=apiuser&password=secret+password
Headers Example
HEADERS = {"api_key": "123_IAM_A_KEY", "api_secret": "SECRETKEY"}
def process_request_options(options):
url = options.get("url")
if "swscan.apple.com" not in url:
print("Headers before: %s" % options["additional_headers"])
# Merge headers with Munki
options["additional_headers"].update(HEADERS)
print("Headers after: %s" % options["additional_headers"])
return options
Since the 'additional_headers' key is a dictionary it is easy for us to update retaining the User-Agent
and Authorization
headers.
{u'Authorization': u'Basic bXVua2k6WUhuUXhWWFFh==', 'User-Agent': u'managedsoftwareupdate/2.6.1 Darwin/15.4.0'}
{'api_secret': 'SECRETKEY', 'api_key': '123_IAM_A_KEY', u'Authorization': u'Basic bXVua2k6WUhuUXhWWFFh==', 'User-Agent': u'managedsoftwareupdate/2.6.1 Darwin/15.4.0'}
These are all the options that can be changed with the middleware. Most of the time you'll only be interested in the url and the additional_headers
Key | Type | Description | Default Value |
---|---|---|---|
url | String | The 'url' you are requesting. | |
additional_headers | Dictionary | These are the HTTP headers that are going in the get request. List of HTTP header fields | {'User-Agent': u'managedsoftwareupdate/%MUNKI_VER% Darwin/%OS_VER%'} |
follow_redirects | String | Explained here | None |
resume | Boolean | If True , Gurl will attempt to resume an interrupted download. **You should probably leave this alone.
|
False |
download_only_if_changed | Boolean | If destinationpath already exists, you can set 'onlyifnewer' to true to indicate you only want to download the file only if it's newer on the server. **You should probably leave this alone. | False |
cache_data | NSObject | **Don't touch this. | |
logging_function | Function | The logging function in use. **You should probably leave this alone. |
Here are some middleware modules to try:
Provider | URL |
---|---|
Amazon CloudFront | https://github.com/AaronBurchfield/CloudFront-Middleware |
Amazon s3 | https://github.com/waderobson/s3-auth |
Backblaze B2 | https://github.com/sphen13/B2-Middleware |
Google Cloud storage | https://github.com/waderobson/gcs-auth |
Azure Blob Storage | https://github.com/okieselbach/Munki-Middleware-Azure-Storage |
- Getting Started
- Overview
- Discussion Group
- Demonstration Setup
- Glossary
- Frequently Asked Questions
- Contributing to Munki
- Release Notes
- Introduction
- Managed Software Center in Munki 5.2
- Manual Apple Updates
- force_install_after_date for Apple Updates
- Additional update encouragement
- Aggressive update notifications
- AggressiveUpdateNotificationDays preference
- Additional Munki 5 changes
- Configuration profile notes
- Major macOS upgrade notes
- Upgrading to Munki 5
- Introduction
- Munki Links
- Product Icons
- Screenshots In Product Descriptions
- Client Customization
- Custom Help Content
- Featured Items
- Update Notifications:
- Introduction
- iconimporter
- makepkginfo
- munkiimport
- managedsoftwareupdate
- makecatalogs
- manifestutil
- repoclean
- Preferences
- Default Repo Detection
- Default Manifest Resolution
- Managed Preferences Support In Munki
- Apple Software Updates With Munki
- Pkginfo Files
- Supported Pkginfo Keys
- Pre And Postinstall Scripts
- Munki And AutoRemove
- Blocking Applications
- ChoiceChangesXML
- CopyFromDMG
- nopkg items
- How Munki Decides What Needs To Be Installed
- Default Installs
- Removal of Unused Software
- Upgrading macOS:
- Apple Updates:
- Securing the Munki repo
- Preflight And Postflight Scripts
- Report Broken Client
- MSC Logging
- Munki With Git
- Bootstrapping With Munki
- License Seat Tracking
- LaunchD Jobs and Changing When Munki Runs
- Web Request Middleware
- Repo Plugins
- Downgrading Software
- Downgrading Munki tools
- Authorized Restarts
- Allowing Untrusted Packages
- About Munki's Embedded Python
- Customizing Python for Munki
- Configuration Profile Emulation
- PPPC Privacy permissions
- AutoPkg
- Repackaging
- Creating Disk Images
- Stupid Munki Tricks
- Troubleshooting
- Professional Support
- Known Issues and Workarounds
- Building Munki packages
- Munki packages and restarts
- Signing Munki
- Removing Munki
- More Links And Tools
- Munki Configuration Script
- Who's Using Munki
- Munki 3 Information
- Munki 4 Information
- macOS Monterey Info
- Pkginfo For Apple Software Updates
- Managing Configuration Profiles
- Microsoft Office
- Adobe Products
- Upgrading macOS: