Skip to content

VIP 17: Enable Unix domain sockets for listen and backend addresses

Poul-Henning Kamp edited this page May 15, 2017 · 19 revisions

Synopsis

Allow Unix Domain Sockets (UDS) as listen addresses for Varnish (-a, -T and -M options) and as addresses for backends. Ideally also obtain credentials of the peer process connected on a UDS, such as uid and gid, for use in VCL.

Named listen addresses

This is not directly related to UDS, but this change would solve some of the problems and mitigate some the complexity raised by the original draft. Because this change has already been accepted, there is no VIP to link to, and no documentation to refer to until it is implemented. For convenience it is described here.

Influence

This feature is similar to how storage backends are exposed in VCL, they have a name that can then be used in VCL, and when a name is omitted, generic names are attributed (s0, s1, sN etc).

Example: varnishd -s malloc,10G -s video=malloc,100G [...]

You end up with 3 storage backends called s0, video and Transient, and as such have access in VCL to the following symbols and their respective fields:

  • storage.s0
  • storage.video
  • storage.Transient
  • (and storage.<name>.*, see man vcl)

You can then have this kind of logic in VCL:

sub vcl_backend_response {
    if (beresp.http.content-type ~ "video") {
        set beresp.storage = storage.video;
    } else {
        set beresp.storage = storage.s0;
    }
}

The advantage of beresp.storage over beresp.storage_hint is the strong typing guaranteeing that VCL won't compile if there is a typo in the storage name.

Implementation

Named listen addresses will work like storage backends in that regard (generic names being a0, a1, aN etc).

Example: varnishd -a public_http=:80 -a public_https=:8443,PROXY admin=:1234 [...]

You can then use the logical names in your VCL:

sub vcl_recv {
    if (local.address == listen_address.public_http) {
        # do an https redirect for example
    }
    if (req.method == "PURGE") {
        if (local.address != listen_address.admin) {
            return (synth(405));
        }
        return (purge);
    }
}

Actual names of the variables used to access this information in VCL hasn't been decided yet.

The benefits are the ability to reuse the same VCL when all varnishd instances in a cluster may not be able to provide consistent listen interfaces or port numbers.

Security concerns

This is not a security feature despite what the example above may suggest. Using this as a security measures implies the assumption that the network is actually secured before traffic hits Varnish on the admin listen address for example (firewalls and all that jazz).

I don't agree entirely, the root@ may want to restrict the paths to backends.

Testing

We can expose additional macros for listen addresses. For example with a v1 varnish instance:

  • v1_addr: the first listen address
  • v1_port: the first listen port
  • v1_sock: the first listen address+port
  • v1_addr_a0: a0's listen address
  • v1_port_a0: a0's listen port
  • v1_sock_a0: a0's listen address+port

Benefits

Once again strong typing, because port numbers in VCL and in the varnishd command line may get out of sync without being noticed. Here a typo in the name prevents the VCL from compiling. It's also a transport-independent alternative to ACLs, as shown in the purge example above.

Being transport-independent, it also means that it can accommodate future transports, like for example unix domain sockets described below.

Why?

The main reason to use a UDS is that it works like TCP sockets (reliable bidirectional byte stream behind a file descriptor) and would likely not be too intrusive in the existing code base.

Other noteworthy reasons:

  • Eliminate the overhead of TCP/loopback for connections with peers that are co-located on a host with Varnish
  • The possibility to query the peer process credentials and restrict access using regular filesystem permissions

A common case for co-locating Varnish with a peer is the need of a TLS proxy for HTTPS. On both client and backend sides, a UDS should work seamlessly with the PROXY protocol.

How?

Listen address notation

On the listen side, expecting an absolute path would prevent ambiguity with IP addresses or ports:

varnishd -a /path/to/http.sock -T /path/to/cli.sock [...]

As it is common with other varnishd options, we can pass additional parameters:

varnishd -T /path/to/cli.sock,uid=varnish,gid=varnish

However this introduces an ambiguity for PROXY protocol in the -a option. The syntax can be changed to:

varnishd -a /path/to/http.sock,proto=<proto>,uid=varnish,mode=0600 [...]

The -M option being of the connect persuasion, it wouldn't take additional parameters to the absolute path.

Backend address notation

On the backend side we can avoid ambiguity by introducing a new .path field:

backend local {
    .path = "/path/to/backend.sock";
    # or maybe .unix or .uds instead?
}

The .path field would be enough in itself to declare a backend (like .host) and would be mutual exclusive with .host and .port.

By adding a parameter (for example uds_path) akin to vcl_path and vmod_path to maintain a PATH where to look sockets up we could allow relative paths on the backend side.

Peer credentials

Getting the peer credentials is not portable, and the least common denominator seems to be the euid and egid. We probably want to extract them both as names and numbers. See Geoff's draft for the technical details.

VCL/VRT

TODO

Needs further discussions

  1. What happens to struct suckaddr ? We added that to avoid lugging around sockaddr_storage all over the place and it shaves something like 4x96 bytes off the size of a session ?

  2. On the VCL side, what happens if in the future a jail performs a chroot? Users would have similar problems with today's std.fileread.

  3. During the first planning session for Varnish 6 we agreed that UDS addresses would be kept separate from suckaddr. (How?) Is the question of naming from the original draft still relevant?

  4. What happens if the VCL asks for remote.ip.port() ?

  5. What happens if the VCL asks for remote.ip.uid() on a IPv4/6 socket ?

Clone this wiki locally