This article provides a short and simple introduction to Glacier2. The claim that you will be able to learn Glacier2 in ten minutes is something of an exaggeration: to learn Glacier2 thoroughly, you will have to invest more than ten minutes. However, you can learn the basics of Glacier2 in the time it takes to read this article, which is about ten minutes.
On this page:
What is Glacier2?
Glacier2, in a nutshell, is a network proxy service for Ice: it allows Ice servers to sit behind a corporate firewall, such that clients in the outside world can use these servers.
Glacier2 is a simple program; at its core, Glacier2 is an Ice server that receives incoming requests from clients and passes them on as blobs of bits to servers. This is quite similar to the functioning of an IP router that receives packets on one interface and forwards them via another interface. This simplicity not only makes Glacier2 easy to configure, but it also makes it much more likely that Glacier2 is secure. (Lower complexity means fewer bugs, not to mention better performance.) In particular, Glacier2 does not depend for its security on the integrity and correct configuration of other components, such as a web server. Glacier2 can also require clients to connect via SSL and reject insecure TCP connections. (Glacier2 does not support UDP.)
Glacier2 Feature Highlights
Here are some of the main distinguishing features of Glacier2:
- You need to open only a single port in the corporate firewall for any number of Ice servers behind the firewall.
- Glacier2 can be configured to only accept SSL connections.
- Glacier2 does not require any configuration that would need to change as applications change. In particular, Glacier2 does not require knowledge of the Slice definitions used by the back-end servers.
- Clients require only minimal source code changes in order to work with Glacier2.
- Servers do not require any source code changes in order to work with Glacier2.
- Callbacks from servers to clients do not require the client's firewall to permit incoming connections.
In addition to the above highlights, Glacier2 also offers a number of advanced features:
- Access control, which allows you to add additional security controls beyond those provided by SSL, such as authentication with passwords or SSL certificates.
- Integration hooks for custom authentication mechanisms.
- Filters for restricting the addresses and ports a client can access on the internal network. Filters can also be used to limit client requests to specific object adapters or objects.
- Session management for recovering resources associated with clients that do not disconnect in an orderly fashion. This includes hooks that you can use to integrate Glacier2's session management with application-specific functionality, for example, to establish and clean up per-client contextual information.
As befits an introductory article, we will focus on getting started with Glacier2 and leave you to check the Ice Manual for details on the advanced features.
Getting Started with Glacier2
Before your clients and servers can communicate via Glacier2, you must configure your firewall and configure and run Glacier2.
Configuring Your Firewall for Glacier2
Chances are that you will already have a firewall that is configured to disallow incoming connections (except for a number of selected services, such as web and e-mail traffic). To allow Glacier2 to work with your firewall, you must configure the firewall to open a single TCP port and forward all traffic for that port to the machine on which Glacier2 runs. Exactly how you achieve this depends on your firewall. However, most firewalls have an administrative interface that allows you to easily add a rule that essentially says "forward all incoming TCP traffic on port 4064 to port 4064 on machine
glacier2.zeroc.com." The figure below illustrates this situation:
Glacier2 Behind a Firewall
We suggest that you use port 4064 as the incoming SSL port for Glacier2 and, if you want to allow client access via TCP, that you use port 4063 as the incoming TCP port for Glacier2. These two ports are reserved for Glacier2 by IANA (Internet Assigned Numbers Authority), so you can be reasonably sure that they are not used by some other service. For this article, we will assume that the firewall (
firewall.zeroc.com) forwards incoming connections on ports 4063 and 4064 to the same ports on the internal machine
glacier2.zeroc.com, which runs Glacier2. (The internal machine need not run Glacier2 on these ports but, seeing that they are reserved for Glacier2, you might as well use them.)
Glacier2 is provided as the command
glacier2router in the Ice distribution. The simplest way to run Glacier2 is as follows:
Glacier2.Client.Endpoints configures the port at which Glacier2 listens for client TCP requests. In this case, it listens only on the interface bound to
glacier2.zeroc.com's IP address on port 4063. As mentioned earlier, Glacier2 can also be configured to require authentication from clients. The property
Glacier2.PermissionsVerifier determines the authentication mechanism. Glacier2 ships with a built-in null permissions verifier that allows anyone to connect — the object identity
Glacier2/NullPermissionsVerifier selects this "allow anyone" verifier. (We will discuss other authentication options shortly.)
Running a Server with Glacier2
On the server side, no configuration is required at all: to use a server with Glacier2, you simply start the server with the same configuration as you would without Glacier2.
Running a Client with Glacier2
For a client, we need to make minor source code changes to allow the client to use Glacier2. Specifically, clients must establish a session with Glacier2 to have their requests forwarded to servers. On start-up, the client needs to execute the following code:
The call to
createSession expects a user name and password. Because we are using the null permissions verifier (for the moment), any user name and password will do, so the code passes empty strings. This code establishes the session that allows the client to communicate with the server via Glacier2.
The client should destroy the session before terminating:
You can also configure Glacier2 to automatically destroy idle sessions by setting the property
Glacier2.SessionTimeout to the idle time in seconds. We strongly recommend setting this property to ensure correct cleanup in the event of a client crash. Even with this feature enabled, it is still a good idea for the client to explicitly destroy its session to ensure timely clean-up of resources inside Glacier2. (And, if you do not configure a session timeout, sessions last indefinitely.)
The above code is all that is necessary to make a client cooperate with Glacier2. You can bundle this code into utility functions and then reuse it in your clients so that they automatically use Glacier2. In fact, several Ice language mappings provide helper classes to simplify the task of creating reliable Glacier2 applications.
At a minimum, a Glacier2 client requires the following configuration settings:
Ice.Default.Router configures a default router for the client. Setting this property causes all client requests to be sent to the object specified by that property, instead of being sent to the endpoint that is inside the proxy that a client uses to make an invocation. In effect, the property says "send all invocations to the specified object, instead of sending them as you normally would." The host and port for this property must point at the firewall, which port forwards all traffic to Glacier2 on the host and port set by Glacier2's
Finally, retries do not make sense if a client communicates with a server via Glacier2 because Glacier2 will retry failed requests automatically on behalf of the client. To disable retries, we set
Ice.RetryIntervals to a negative value.
This is all that is needed to get off the ground, at least for this simple scenario: run Glacier2, add the preceding few lines of code to the client, run the server, and run the client with these additional configuration settings.
If you have problems getting things to work, it will almost certainly be due to incorrect endpoint configuration. In particular, the client's setting for
Ice.Default.Router must point at the firewall and the firewall must forward to the host and port defined by
Glacier2.Client.Endpoints. You can set
Ice.Trace.Network=1 for Glacier2 and the client to check whether connections are being made to the correct address and port.
Better Authentication with Passwords
You can force clients to authenticate themselves with a user name and password when they create a Glacier2 session by leaving
Glacier2.PermissionsVerifier undefined, and instead setting the property
Glacier2.CryptPasswords to the path name of a password file. Doing this activates a built-in permissions verifier to authenticate clients. The password file must contain pairs of user names and encrypted passwords, one pair per line. The client passes the user name and (plain-text) password to
createSession, and Glacier2 allows access only if the supplied password encrypts to the same string that is stored in the password file. Note that if you use this mechanism, you should restrict client access to SSL, otherwise the password will be sent in clear text over the wire.
Using SSL with Glacier2
For security-sensitive applications, you will probably want to use a SSL connection instead of TCP to ensure that no-one can eavesdrop on the traffic between clients and Glacier2. To run Glacier2 with SSL enabled and TCP disabled, you need to set a few additional properties:
Note that Glacier2 now uses a SSL endpoint. The remaining properties specify that the Ice run time should load the SSL plug-in (
Ice.Plugin.IceSSL) and configure the directory and files that provide the plug-in with the relevant certificate and key information. We are still using the null permissions verifier, so any client can connect, but only via SSL. Because the client is still authenticated via user name and password, it need not provide its own SSL credentials: Glacier2 sets
IceSSL.VerifyPeer to zero to accept such anonymous connections. (This example uses the certificates that accompany the Ice distribution. For a real-world deployment, you would generate your own CA certificate and a unique certificate for the Glacier2 router. See the Ice Manual for more details on configuring the SSL plug-in and generating certificates.)
As before, no changes are required for the server — the server can provide either TCP or SSL endpoints, and Glacier2 will forward client requests to the server as appropriate.
The client must be configured as follows:
IceSSL.TrustOnly rule tells the client to connect only to a server whose SSL certificate has the common name
Server. In a real-world deployment, you would use a unique common name for your Glacier2 router, and use that common name instead.
With this configuration, clients communicate with Glacier2 only via SSL, and Glacier2 forwards requests to back-end servers using whatever endpoints (TCP or SSL) are provided by these servers.
Using SSL Connection Credentials with Glacier2
You can use SSL in combination with user name and password authentication exactly as with TCP: leave
Glacier2.PermissionsVerifier undefined, and instead set
Glacier2.CryptPasswords to the path name of the password file. SSL's encryption means the client's password is no longer sent in plain text over the wire when the client calls
An alternative way to authenticate clients is to use the credentials that are established for the client's SSL connection. In that case, the client does not need to supply a password — instead, the client calls
createSessionFromSecureConnection, which requires no arguments:
For this to work, Glacier2 must be configured slightly differently: instead of setting
Glacier2.CryptPasswords, leave these properties undefined and set
Glacier2/NullSSLPermissionsVerifier allows any client to connect, provided that the SSL connection could be established. If you want to restrict access to specific clients, you need to install a custom permissions verifier.
Custom Permissions Verifiers
You can set
Glacier2.PermissionsVerifier to the proxy of an arbitrary Ice object that you implement in any server that is reachable by Glacier2. The target object must support the
Glacier2.PermissionsVerifier interface, which defines a
checkPermissions operation. For each call to
createSession, Glacier2 invokes your
checkPermissions operation to decide whether the client should be authorized.
Similarly, you can set
Glacier2.SSLPermissionsVerifier to the proxy of an Ice object that implements the
Glacier2::SSLPermissionsVerifier interface, which contains an
authorize operation that Glacier2 invokes when the client calls
This facility allows you to implement arbitrary authorization policies, typically by delegating the decision to an authorization mechanism that you already have in place.
Using Callbacks with Glacier2
With the setup we have seen so far, clients can reach servers through the firewall, but servers cannot necessarily reach clients. Doing this is necessary if a client passes a proxy for a callback object to a server. In that case, the client is both client and server and, when the server calls back into the client, they momentarily reverse roles: the server acts as the client, and the client acts as the server.
There is nothing wrong with this as such: if the client is not behind a firewall of its own, the server will simply open a new connection to the client and invoke the callback via that connection. However, chances are that the client will be behind its own firewall, with that firewall disallowing incoming connections.
The solution to this problem is for the server to send the callback request to Glacier2, which forwards it to the correct client via the already-existing connection that was established by the client. That way, the server can reach the client even if the client is behind a firewall that disallows incoming connections, as shown below:
Bidirectional Communication with Glacier2 for Callbacks
To make this setup work, no code or configuration changes are necessary in the server. However, we need to add one additional property setting to Glacier2's configuration:
Glacier2.Server.Endpoints enables an endpoint in Glacier2 that servers use when they invoke a callback on a client. (Note that you need not specify a port number for this property.) The endpoint you specify here must be accessible on the internal network so that back-end servers can connect to it. This endpoint should not be accessible from the external network to prevent malicious clients from flooding Glacier2's server endpoint with requests.
The million-dollar question is: how does it happen that servers connect to Glacier2's server endpoint when they invoke a callback, instead of attempting to open a separate connection directly to the client? The answer involves two separate components on the client side. The first is that the client must have an additional property setting:
Note the setting of
CallbackAdapter.Router. (We assume that the client's object adapter that hosts the callback object has the name
The property configures the client's object adapter with a router, and the specified proxy must point at the firewall. By setting this property, the client's object adapter now publishes the router's server endpoint (instead of the adapter's own endpoint) in the proxies that it creates. This explains how, when the server invokes a callback, it ends up connecting to Glacier2: the client-side run time notices the property setting, asks Glacier2 for the endpoint that Glacier2 provides to servers for callbacks, and puts that server endpoint (which is on the internal network) into the callback proxy. Therefore, the back-end server connects to Glacier2's server endpoint when it invokes the callback.
The second part of the answer deals with how Glacier2 can ensure that callbacks for different clients actually get delivered to the correct client: because each server has only a single connection to Glacier2, but may need to send callbacks to different clients, the identity of the target client is no longer implicit in the server's connection to Glacier2. Instead, the client must provide an identifier that Glacier2 can use to de-multiplex callbacks from back-end servers and forward them to the correct clients.
Glacier2 does this by assigning a unique identifier to each client. In turn, the client is expected to provide that identifier in the
category part of the object identity for its callback objects. For example, suppose the client provides callback objects of interface
Callback to a number of back-end servers. The client must contact Glacier2 once, to obtain the unique category Glacier2 has assigned to the client, and then use that category in the object identity of its callback objects:
The Glacier2 helper classes included with Ice make this process quite a bit simpler. For example, the C++ class
Glacier2::Application provides the
Although similar in purpose to the
ObjectAdapter operation of the same name, this method offers enhanced functionality for Glacier2 clients:
- It creates an object adapter specifically for hosting callback objects (if the adapter hasn't been created yet);
- It generates an object identity that uses the router-assigned category and a UUID for the name;
- It registers the servant with the callback object adapter and returns a proxy to the new callback object.
We encourage you to use these helper classes whenever possible, or modify them to suit your own needs.
Glacier2 makes it very easy to provide secure access to Ice servers that sit behind a firewall. Once you know Glacier2, you can make a new server available in just a few minutes. The coding effort required to make clients cooperate with Glacier2 is truly minimal: you only need to write a few lines of code once and then can re-use that code in all your clients. Moreover, Ice includes robust helper classes that provide the functionality common to most Glacier2 applications.
If you want to experiment with Glacier2, we suggest you start with the demo that is provided in the
Glacier2/callback directory in the Ice sample programs repository. The demo also illustrates how to configure Glacier2 with a custom permissions verifier, and how to use explicit session management. In addition, we suggest that you take a look at the Glacier2 chapter in the Ice Manual, which provides information on advanced features such as fine-grained access control, integration with IceGrid, and other topics.