Zack McCauley - WardsParadox

Hi, I’m a macadmin for a school district in Montana. Never stop learning.

Munki URL Redirector

2019-09-18

Due to recent requests by staff who are struggling to find software in the Managed Software Center, I tried to find a way to send links to the users via email when they have requested. We use GSuite for Education, and Gmail will strip any links that have custom URL Schemes (like what Munki uses munki://) for security reasons.

TL;DR

After doing some research I found Nginx has some nice rewrite rules that can use Regex. To use the appropriate regex engine, you'll want to make sure it works the PCRE engine. I struggled to figure out the regex pattern even using Regex101. Turns out there are some variables in rewrite/return rules that you can make use of. More on that later.

ProTip: Best Regex tool (Thank you, Ben Toms, for pointing this out in the MacAdmins Slack!)

Regex101

You'll first want to decide what the URL is going to be on your Nginx server. I wanted it shorter and easier to type. So I went with the location msc. This can be anything as long as you match it in the regex. The more important part is that you still must use the name key not the display_name key in the pkginfo file, just like with the Munki Links capabilities.

If you want to spin up a separate Nginx server, you can but because of how minimal impact this has, I decided to add this to our munki server.

This is done via a return + location directive/match regex. To understand location directives a bit more, I suggest reading this quote from Understanding Nginx Server and Location Block Selection Algorithms

The location_match in the above defines what Nginx should check the request URI against. The existence or nonexistence of the modifier in the above example affects the way that the Nginx attempts to match the location block. The modifiers below will cause the associated location block to be interpreted as follows:

  • (none): If no modifiers are present, the location is interpreted as a prefix match. This means that the location given will be matched against the beginning of the request URI to determine a match.
  • =: If an equal sign is used, this block will be considered a match if the request URI exactly matches the location given.
  • ~: If a tilde modifier is present, this location will be interpreted as a case-sensitive regular expression match.
  • ~*: If a tilde and asterisk modifier is used, the location block will be interpreted as a case-insensitive regular expression match.
  • ^~: If a carat and tilde modifier is present, and if this block is selected as the best non-regular expression match, regular expression matching will not take place.

I first used the ~ wildcard as that's what I had seen with a lot of the Nginx examples before. I was struggling to get this working. Managed Software Center was opening, but not to anything useful.

msc_nothinghelpful.png

Turns out the External URL event is logged to MSC, but you need to enable the MSC Log + Debug Log. You can also use the log command in macOS to narrow this down. (Thanks to Eric Holtam for the commands and Thanks to Elios for the enable log path! ) Either way works for narrowing this down.

log stream --debug --predicate='processImagePath contains[c] "Managed Software Center" AND eventMessage contains[c] "munki://"'

What I was seeing in the log was this:

MSC: debug  Got load request for
file:///Users/zackmccauley/Library/Caches/com.googlecode.munki.ManagedSoftwareCenter/html/
detail-msc.html
MSC: debug  Got URL scheme: file
MSC: debug  build_page for detail-msc.html
MSC: debug  buildDetailPage for msc
MSC: debug  No detail found for msc
MSC: debug  generate_page for detail-msc.html
MSC: debug  Requested pagename is detail-msc.htm

I was thoroughly stumped. Thankfully I found this: A Regular Expression Tester for NGINX and NGINX Plus

This is a tool provided by Nginx to help build regex expressions. It runs on docker and includes a docker-compose file to help bring it up. With that, I was able to narrow down the issue to the $1 I was specifying. It ended up specifying the wrong Regex group (Nginx uses $1 instead of $0 to specify the first element in the regex group)..

I was able to simplify my location code to this (This also simplified the regex which seemed to help speed).:

location ~* msc.(.*) {
            return 301 munki://detail-$1;
        }

If you want to use additional munki links, you can drop the detail part of the return, just make sure to specify that in the link you use like: https://mymunkiserver/msc/detail-foo

With this you can now specify a link like such: https://mymunkiserver/msc/foo which will redirect to munki://detail-Foo

Make sure you add this inside your server block.

I'll be sure to add a video demonstrating this soon. EDIT: Video :D