Apple Push Notification Service Proxy

Problem this snippet solves:

This solution provides a way for isolated networks (no direct access to the internet) to proxy connectivity to the Apple Push Notification Service (APNS) which is necessary for providing Mobile Device Management of Apple devices connected to isolated networks and don't have cellular network access. It also enables these devices access to Apple's iMessage service.

Examples of these devices include iPod's, WiFi only iPad's and Mac's.

/blocking image here/

To specifically allow access to the APNS service using traditional firewall rules would be administratively prohibitive as Apple currently maintains 200 namespace endpoints (1-courier.push.apple.com to 200-courier.push.apple.com) each of which hands back 8 IP addresses across the 17.0.0.0/8 address space. Hence this solution becomes advantageous for enterprises wanting to securely allow access to APNS.

/proxy image here/

How to use this snippet:

The setup of this APNS proxy solution requires the creation of two iRule and two virtual servers: The first to handle DNS requests destined for the push.apple.com domain. The second to provide the actual APNS connection proxy.

APNS DNS Handler

As the APNS protocol doesn't understand the concept of a 'proxy' we need to trick Apple devices within the isolated network to direct APNS traffic to the APNS TCP proxy virtual IP's; the easiest way of doing this is to manipulate DNS.

The APNS DNS handler achieves this by implementing the BIG-IP's DNS Services capabilities to intercept DNS requests destined for the courier.push.apple.com domain and responding directly with IP addresses corresponding to the APNS TCP Proxy virtual server.

You will need to customise the APNS_PROXY_VIP_PREFIX variable to provide an IP address prefix suitable for use within your isolated network. The IP address prefix should correspond with a /24 address space; in the example iRule provided we are using 10.100.100.0/24

APNS TCP Proxy

The APNS TCP proxy iRule is used to create a mapping between the IP address on which TCP connections arrive and the Apple Courier server that the client had meant to connect to.

To do this it looks at the local_addr variable and then maps that to the courier server by performing a DNS lookup of the courier server name. For example, based on the APNS_PROXY_VIP_PREFIX of "10.100.100." requests to the APNS TCP proxy virtual server 10.100.100.5 would create a lookup of 5-courier.push.apple.com (which in most cases is a CNAME to an AKAMAI DNS service that eventually gives us an A record). The iRule then proxy's the connection to the first courier server selected by the AKADNS.

Code :

rule APNS_Proxy_DNS {
  when RULE_INIT {
    set static::APNS_PROXY_VIP_PREFIX "10.100.100."
    set static::APNS_DNS_DEBUG 1
  }

  when DNS_REQUEST {
    if { [DNS::question name] contains "courier.push.apple.com" } {
      # Extract Courier server number
      set courier [string range [DNS::question name] 0 [expr [string first "-" [DNS::question name] ] - 1 ] ]
      set APNS_VIP "$static::APNS_PROXY_VIP_PREFIX$courier"
      if { $static::APNS_DNS_DEBUG > 0 } { log local0. "APNS DNS Query for Courier Server $courier from [IP::client_addr]- Returning $APNS_VIP" }
      DNS::answer insert "[DNS::question name]. 30 [DNS::question class] [DNS::question type] $APNS_VIP"
      DNS::return 
    }
  }
}

### APNS TCP Proxy Virtual Server configuration ###
ltm virtual apns_tcp_proxy {
    destination 10.100.100.0:5223
    mask 255.255.255.0
    ip-protocol tcp
    profiles {
        tcp { }
    }
    rules {
        APNS_Proxy_TCP
    }
}

rule APNS_Proxy_TCP {
  when RULE_INIT {
    set static::dns_ip "8.8.8.8"
    set static::APNS_TCP_DEBUG 1
  }

  when CLIENT_ACCEPTED {
    # Select appropriate APNS server
    set courier [ getfield [IP::local_addr] "." 4 ]
    if { $static::APNS_TCP_DEBUG > 0 } { log local0. "Recieved TCP connection for Courier $courier" }
    # Lookup IP for server
    set courier_ips [RESOLV::lookup @$static::dns_ip "$courier-courier.push.apple.com"]
    node [lindex $courier_ips 0]
    if { $static::APNS_TCP_DEBUG > 0 } { log local0. "Conencting to Courier server on [lindex $courier_ips 0]" }
  }
}
Published Mar 13, 2015
Version 1.0
  • Hi, currently, I can only resolve up to 50-courier.push.apple.com.

     

    $ nslookup 50-courier.push.apple.com

    Server: 192.168.213.180

    Address: 192.168.213.180#53

     

    Non-authoritative answer:

    50-courier.push.apple.com canonical name = 50.courier-push-apple.com.akadns.net.

    50.courier-push-apple.com.akadns.net canonical name = apac-au-courier-4.push-apple.com.akadns.net.

    Name: apac-au-courier-4.push-apple.com.akadns.net

    Address: 17.57.145.37

    Name: apac-au-courier-4.push-apple.com.akadns.net

    Address: 17.57.145.36

     

    $ nslookup 51-courier.push.apple.com

    Server: 192.168.213.180

    Address: 192.168.213.180#53

     

    ** server can't find 51-courier.push.apple.com: NXDOMAIN

  • Adrian,

     

    How does your internal DNS handle "sandbox.push.apple.com" which is the DNS name JAMF would hit to publish to APNs? I would think you need to put a forwarder in DNS for "push.apple.com" to resolve all the courier names, but that would then include sandbox.

     

    Penny for your thoughts...

     

  • How does this scale up when the corporate network is bigger than a single /24 subnet? Do you add more prefix lines to accommodate the additional subnets? Do you have to create additional virtual servers to handle each one?
  • I'd like to give this a try, can you clarify a couple of things though: - What is the reason that the virtual server IP needs to change with every request? Can it not be a single IP? - Is there any risk of virtual servers being duplicated or does nothing happen if one already exist? - In order to make this work, is DNS the only traffic that needs to be sent to the F5?