Caching iOS updates on a Squid proxy server

Update (22 December 2014): The following instructions have been updated and tested with iOS 8.

Right now, my challenge is upgrading almost 200 iPads to iOS 7 with minimal pain (read: zero device handling). Factor in less-than-ideal Internet bandwidth and Apple’s disinterest in allowing proxies to cache iOS updates, and it’s been a bit of a headache.

First, a word of advice: ask your users not to upgrade when prompted. Do this before Apple release a major update, to buy yourself some time to test it on your network and to check that the update is being cached properly.

Hopefully your iPad fleet is already using your Squid proxy. Ours is configured (via Apple’s Profile Manager) to use a PAC file when it’s on our WiFi network. The PAC file directs all but onsite requests to Squid.

Unfortunately, iOS doesn’t use the proxy for everything; system update authorizations, in particular, don’t get out unless permitted on your firewall. Here’s the relevant rule on our iptables firewall (no_proxy_ok is one of our custom chains, as is tcp_allowed):

-A no_proxy_ok -p tcp -m comment -m tcp -m multiport -d -j tcp_allowed --dports 80,443,5223,2195,2196 --comment "allow Apple services (e.g. APNs, updates)"

Mercifully, the update itself is requested via the proxy, but getting it to cache is non-trivial. Obviously max_object_size needs to be big enough to accommodate a 1GB+ file. I went with 2GB:

maximum_object_size 2048000000 bytes

But this wasn’t enough to get the update to cache. A bit of sleuthing led to the first problem: Apple adds HTTP headers like these to its updates, so Squid discards them:

Cache-Control: max-age=0, no-cache, no-store
Pragma: no-cache

The workaround is to break HTTP a little by adding this line above any other refresh_pattern entries in your squid.conf:

refresh_pattern -i appldnld\.apple\.com 129600 100% 129600 ignore-reload ignore-no-store override-expire override-lastmod ignore-must-revalidate

refresh_pattern -i phobos\.apple\.com 129600 100% 129600 ignore-reload ignore-no-store override-expire override-lastmod ignore-must-revalidate

This forces Squid to treat objects from * and * as “fresh” (i.e. cacheable) for 90 days (129600 minutes), no matter what and say.

Finally, I made sure requests were excluded from Squid’s delay pools and filtering ACLs; you may need to make similar tweaks. I also found that maximum_object_size wasn’t being applied correctly to cache_dir, so I defined it explicitly, i.e.:

cache_dir aufs /var/spool/squid3 256000 128 256 max-size=2048000000

iOS 7 is rolling out smoothly as I type.

Embedding fonts in a PDF (after creating it)

I don’t do a lot of desktop publishing, but when I do, my tool of choice is Serif PagePlus. It’s affordable, fast, stable, intuitive, EPS-friendly and capable of producing press-ready output (i.e. CMYK PDFs with bleed). The usual suspects (Adobe InDesign, QuarkXPress, Microsoft Publisher) all fail to meet 2 or more of those requirements.

But PagePlus only runs on Windows, and I’m not spending much time on Windows these days. Pixelmator and iDraw are excellent for bitmap and vector editing respectively, but iStudio Publisher is still missing a few features it needs to cut the mustard as a serious DTP app.

So I’ve been experimenting with Scribus, which is open-source, cross-platform and (surprisingly) well-documented. Being Qt-based, it’s not as snappy as PagePlus, but it’s usable on slower machines and meets all of my other requirements.

I’ve only found one catch so far: when exporting to PDF, its font outlining is slightly inaccurate (text looks heavier than it should, at least when using Avenir LT’s OpenType fonts). And it can’t embed OpenType fonts. There’s a workaround, though: export from Scribus without outlining or embedding any fonts, then use Ghostscript to do the embedding afterwards. Here’s a little BASH script that looks after it on OS X:


if [ $# -ne 1 ]; then

    echo "Usage: MyPdf.pdf"
    exit 1


if [ ! -f "$1" ]; then

    echo "Not a file: $1"
    exit 1


command -v gs >/dev/null 2>&1 || { echo "Ghostscript not found."; exit 2; }

TEMPFILE="$(dirname "$1")/nofonts.$(basename "$1")"

mv -vf "$1" "$TEMPFILE"


gs -sDEVICE=pdfwrite -sFONTPATH=$FONTPATH -dPDFSETTINGS=/prepress -dAutoFilterColorImages=false -dAutoFilterGrayImages=false -dColorImageFilter=/FlateEncode -dDownsampleColorImages=false -dDownsampleGrayImages=false -dDownsampleMonoImages=false -dGrayImageFilter=/FlateEncode -o "$1" "$TEMPFILE"