ipaghazi is a web service for over-the-air (OTA) distribution of iOS apps.

Use cases

What it does

What it doesn't do

Configuration

ipaghazi is configured through environment variables.

IPA retrieval methods

s3

Retrieves from an S3 bucket. Takes the following method-params:

{"bucket": "some-s3-bucket",
 "object": "some-object-key"}

To use this method with a non-public bucket, you must arrange to configure the AWS SDK credentials. In general, setting AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY should suffice. If you are running on an EC2 instance with an IAM role, the AWS SDK should automatically pick up credentials.

url

Retrieves from a URL. Takes the following method-params:

{"url": "http://server/some-app.ipa"}

file (do not use)

Retrieves from the local filesystem. Takes the following method-params:

{"path": "/path/to/app.ipa"}

This method is obviously dangerous, so don't enable it in production. One could easily submit a build whose path is /etc/shadow. Running curl http://server/api/build/98eccd9324cb9c674811e65c/ipa would then dump out all your password hashes. Oops.

But it's handy for testing.

Installation

From source:

npm install
bower install
env [...] ./ipaghazi

Background

Within organizations, one would often like to distribute internal iOS apps through the simple means of opening a link to an app file, as is possible with Android APKs. Of course, this is Apple technology, so the simple solution is out of the question. Instead we are forced to deal with the unnecessarily complicated mechanism of over-the-air (OTA) install manifests. The user opens a link of the form:

itms-services://?action=download-manifest&url=https://example.org/manifest.plist

Note the conspicuous lack of any IPA files in that link. Instead, one specifies a URL to a "manifest" which looks like this:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>items</key>
    <array>
        <dict>
            <key>assets</key>
            <array>
                <dict>
                    <key>kind</key>
                    <string>software-package</string>
                    <key>url</key>
                    <string>https://example.org/someapp.ipa</string>
                </dict>
            </array>
            <key>metadata</key>
            <dict>
                <key>bundle-identifier</key>
                <string>org.example.someapp</string>
                <key>bundle-version</key>
                <string>1.0</string>
                <key>kind</key>
                <string>software</string>
                <key>title</key>
                <string>SomeApp</string>
            </dict>
        </dict>
    </array>
</dict>
</plist>

The IPA URL is specified here in addition to the app bundle ID, version, and name. The manifest is completely redundant as all of this information is contained within the IPA itself. Moreover, arbitrary constraints have been applied to this process:

iOS performs installation through a service running in the background. When the user presses "OK" to allow the install, nothing else happens in the browser, and one must go to the home screen to observe progress.

Because the manifest hardcodes the URL, it will break if one decides to put the IPA somewhere else. One must also generate a manifest anew for every build so that the redundant metadata will match. It would therefore be preferable to autogenerate the manifest, and for this one needs a backend.

Authentication is also a problem. You can...

The options for authentication are otherwise limited. You can't use OAuth because that requires in-browser user interaction, and you can't specify HTTP headers. You also can't embed a password in the manifest and IPA URLs like https://user:password@example.org/..., because iOS will prefill the username but ignore the password. Putting credentials in the query string is an option.

ipaghazi approaches the problem by making it your problem. You could configure your HTTPS termination proxy to require Basic authentication for the web interface ($IPAGHAZI_BASEURL) but allow unauthenticated requests to the API ($IPAGHAZI_BASEURL/api). Or you could put the whole thing behind a firewall and forget about authentication.

"ipaghazi"?

It's an "IPA gateway". "-gate" is a common suffix for political scandals, as is now "-ghazi". Thus, "ipaghazi", an uninspired and unnecessarily provocative play on words. Suggestions for better names are welcome.

Hey! You got your politics butter on my software chocolate!

Oops.