6

I have the following setup:

  • Printer #1 on LAN
  • Printer #2 on LAN
  • Internet facing Debian Apache 2.2 web server at server-external-ip that I want to use as an IPP gateway to the two printers

The two printers are reachable (from LAN and from the Apache server) at the following IPP URLs:

  • http://printer-1-local-ip/printer
  • http://printer-2-local-ip/printer

(The printers are not physically attached to the web server.)

I want them to be reachable from the Internet at the following URLs:

  1. http://server-external-ip/prn1
  2. http://server-external-ip/prn2

IPP works exclusively through HTTP requests to the printer address (i.e., the whole printing process happens through POST requests at the http://printer-X-local-ip/printer URLs), so I only need to redirect (i.e., reverse proxy with Apache) URLs 1 and 2 above.

Apache is serving other content, so I cannot replace it with a custom program (e.g., netcat or netsed). Also, I cannot run a custom program on a different port since the printer clients will only be able to reach the server at port 80.

Then I tried the following Apache configuration:

RewriteRule ^/prn1$ http://printer-1-local-ip:80/printer [P]
ProxyPassReverse /prn1 http://printer-1-local-ip

Connecting a Windows client to the http://server-external-ip/prn1 URL, the reverse proxy works. But the IPP protocol also sends to the printer (inside the POST-ed data) the full device URL.

This means that the printer receives an explicit IPP request for a http://server-external-ip/prn1 printer, and not for its correct address (http://printer-1-local-ip/printer). So it refuses the connection.

I added this entry into the HOST file at the Windows client:

server-external-ip    printer-dns-name

But it still doesn't work since the printer receives an IPP request for http://printer-dns-name/prn1 which still has the wrong service name (i.e., prn1 instead of printer).

I cannot change the reverse proxy url from http://server-external-ip/prn1 to http://server‑external‑ip/printer since I have to provide access to both printers (and I can't change the printer service name in the printer configuration).

What I want to do is to mangle the IPP data HTTP POST-ed to the printer to substitute http://server‑external‑ip/prnX with http://printer-X-local-ip/printer (there are no checksums in the IPP protocol and from the packets I captured this should work).

The problem is that all the Apache modules I can google for won't help you in mangling HTTP request bodies sent to the reverse-proxied printer. mod_rewrite works only on headers, mod_substitute works on response bodies, mod_headers works on request and response headers, mod_replace works on everything but request bodies, etc.

With mod_substitute I tried with the following:

<Location /> 
    AddOutputFilterByType SUBSTITUTE application/ipp
    Substitute "s|server-external-ip/prn1|printer-1-local-ip/printer|"
</Location>

But, as expected, it works perfectly on response bodies but not on proxied requests (I checked proxying to another server). Also note that IPP requests are of the application/ipp MIME type, so the filtering won't (significantly) impact normal traffic.

Any idea on how to solve this mess? I feel like there should be an easy solution and I'm not looking at things the right way. That's why I'm asking this always-awesome community (I have no posts here yet, but I'm a longtime fan).

I'd like to stay on this "redirection approach", so workarounds will be useful only if no direct solution exists. And yes, I could modify an Apache module for the purpose, but I don't really feel like it... :-)

In the meantime I'll try some netsed magic... :-)

7
  • Not sure that using Apache as a proxy is the Right Thing here (though it may give you the option of security you wouldn't have if you just mapped the port externally). Why do you want to make your printers available on the internet? The usual result is someone printing out War and Peace (or the latest and greatest porn) when they discover an IPP-enabled printer exposed to the public...
    – voretaq7
    Oct 22, 2012 at 16:14
  • For sure I could later add IP whitelisting or authentication and even SSL for confidentiality. The point of the whole thing is to have the printers exposed on port 80 together with the usual web server. This is for ease of use of the clients that will have to print through a pretty restricted firewall, and other solutions (VPN, LAN connection, etc.) initially looked (and still look) harder than a "simple" reverse proxy. Oct 22, 2012 at 18:29
  • You could run a custom proxy on a different port, and have apache forward the printer urls to this proxy, which then forwards them onwards to the printer. There are a few application level proxies out there that can modify POST content. Apr 2, 2013 at 4:04
  • Could you just install CUPS somewhere and define printers with whatever names you want, using the ipp backend to connect it to real printers, then configure Apache to proxy requests to that CUPS server? At least basic printing should work this way (not sure about print job control if other clients will use the printer IPP URLs directly without going through your CUPS server). Apr 7, 2013 at 9:11
  • 1
    @dan: You need a "application level" proxy here. And I have some experience with the Zeus Traffic Manager. That is however a commercial product, that can do a lot more than what you need. I did use it to modify request and response bodies though. Maybe you could use mod_security. However, why not do the following: Define two external DNS names for your external IP, one for each printer, and define two virtual hosts, each proxying one single printer? Apr 9, 2013 at 6:43

2 Answers 2

3
+50

Why not do the following: Define two DNS names for your external printer, eg.

printer-1-external.mydomain.com CNAME external-server.mydomain.com
printer-2-external.mydomain.com CNAME external-server.mydomain.com

You could even use the same names as you use internally. Have the internal DNS resolve printer names directly to the printer, and the external DNS to your external IP...

Add two name based virtual hosts on your server:

<VirtualHost *:80>
    ServerName printer-1-external.mydomain.com
    ProxyPass / http://printer-1-local-ip/
</VirtualHost>
<VirtualHost *:80>
    ServerName printer-2-external.mydomain.com
    ProxyPass / http://printer-2-local-ip/
</VirtualHost>

That way your IPP request for http://printer-2-external.mydomain.com/printer will have the correct service name in its post parameters.

2
  • This just might work for what I need at least.. I'll award the bounty because I am running out of time. (This may not solve the original question, though.) OP can still accept if it works for him. Apr 9, 2013 at 14:41
  • While I still consider this as a workaround (since it will force me to poke the DNS overlords upstairs), it's a really good twist at how to tackle the problem. I'm accepting your answer also for the suggestion about mod_security in the comments above. From a quick look at the documentation, it may lead to a proper solution. Thanks for your kind advice! Apr 9, 2013 at 14:52
2

Sorry I can't add comments, however, if you have not already, check to to see if:

ProxyPreserveHost On

is setup in your conf file.

The approach with the rewrite is good for the context root, but for the hostname issue it sounds like you would want to check the ProxyPreserveHost.

http://httpd.apache.org/docs/2.2/vhosts/examples.html

1
  • I think I already tried this in the past without success and, reading the documentation, "When enabled, this option will pass the Host: line from the incoming request to the proxied host, instead of the hostname specified in the ProxyPass line." So this is doing what I need, but only on the Host: field. I need to do the same thing, but on the POSTed data. Thanks tho for your suggestion! Apr 8, 2013 at 15:55

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .