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:
http://server-external-ip/prn1
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… 🙂
Best Answer
Why not do the following: Define two DNS names for your external printer, eg.
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:
That way your IPP request for http://printer-2-external.mydomain.com/printer will have the correct service name in its post parameters.