Running amanda behind an apache reverse proxy

This post started off as a plea for help, but I’ve pretty much got everything working! I thought I’d pass this along in case anyone else is trying to do the same thing.

The problem is to run https://github.com/sivel/amanda/ behind an Apache httpd reverse proxy. I’ve set it up first on my local laptop which is running both Apache httpd and amanda in order to find the sharp corners before moving it onto a “for realz” httpd that’s already doing a few other internal tasks.

BTW, hitting it locally is working fantastically! (Thanks, @sivel !) I’m using the -ui option to get a web GUI, a custom -port (5001 instead of the default 5000, for reasons), and a custom -artifacts path.

With all that in place and http://localhost:5001/ throwing no unpleasant surprises, the remaining task is to configure the Apache httpd server to reverse proxy the service as http://laptop.madeup.org/amanda. I can’t expose the custom port 5001 in the VLAN this is ultimately supposed to run in, hence the httpd proxy. And while I’m using http on the laptop, the “real” server has certificates, so that will be https.

Through trial and error, I’ve found three paths into amanda that need to be proxied: /, /api, and /_ui. Here’s how I’m handling that so far:

ServerName laptop.madeup.org

ProxyPassMatch  "^/amanda(.*)" "http://localhost:5001$1"
ProxyPassReverse "/amanda"     "http://localhost:5001"

ProxyPassMatch  "^/api(.*)"    "http://localhost:5001/api$1"
ProxyPassReverse "/api"        "http://localhost:5001/api"

ProxyPassMatch  "^/_ui(.*)"    "http://localhost:5001/_ui$1"
ProxyPassReverse "/_ui"        "http://localhost:5001/_ui"

AddOutputFilterByType SUBSTITUTE text/html
AddOutputFilterByType SUBSTITUTE application/json
Substitute "s|http://localhost:5001|http://laptop.madeup.org/amanda|q"

The ProxyPass* directives only alter specific headers. It’s the Substitute directive that “fixes” the urls contained in the text/html and application/json bodies that amanda returns.

The only part it doesn’t handle correctly is the “Copy” button to copy the install string. Also, that install string is, for example,

$ ansible-galaxy collection install -s http://laptop.madeup.org/ utoddl.logical==1.0.0

which doesn’t include the /amanda bit on the server parameter. Fortunately the ansible-galaxy command does the Right Thing™ when that part is included, like this:

$ ansible-galaxy collection install -s http://laptop.madeup.org/amanda utoddl.logical==1.0.0

I don’t do this sort of thing very often, so it took several hours longer than it should have. Writing this up wasn’t so quick either. The hope is it’ll save somebody else the time it cost me. And as always, if you have suggestions for how this could be done better, I’d love to hear them.


As a bonus, here’s the (trivial) systemd amanda.service file I created in the process. Cheers!

# /etc/systemd/system/amanda.service
# See https://github.com/sivel/amanda/

[Unit]
Description=Ansible Galaxy Collections v3 API endpoints
After=network.target

[Service]
Type=exec
Environment=LANG=C
User=httpd
ExecStart=/opt/local/amanda/bin/amanda -ui -artifacts=/opt/local/amanda/artifacts -port 5001

[Install]
WantedBy=multi-user.target
3 Likes

It could also be a bug in the UI, that is not outside the realm of possibility. I don’t have the UI running behind a proxy currently, only the API. I can attempt to take a look later.

1 Like

Ok, there were a couple of issues that could be addressed, and I’ve pushed them and new binaries are built:

Here is an apache config that should work (although I only tested using caddy)

ProxyPreserveHost On
RequestHeader set X-Forwarded-Prefix "/amanda"
ProxyPass "/amanda/" "http://localhost:5000/"
ProxyPassReverse "/amanda/" "http://localhost:5000/"

I’ve added this info to the README as well.

1 Like

I’ve been trying variations on that apache httpd config with the new binary since you posted earlier today. I can get almost everything to work, but never quite all the things at the same time.

The “things” are:

  • the “←Back” button at the top of the 2nd page (always works)
  • the pull-down list of versions (fragile)
  • the repository link under “Links” (always works)
  • the “download tarball” link under “Download” (suffers from hostname identity crisis, see below)
  • the “ansible-galaxy …” string under “Install” for copy-n-paste (okay if the pull-down versions are okay)
  • the “Copy” button next to said string (throwing javascript errors)
  • the host in the various user-facing URLs

That last one is the worst. It can be the hostname, the ipv4 address, or localhost in user facing URLs or background requests. I’ve thrown every trick I know at it. The result is the config below — the most stable I’ve found all day. Everything works with this config except the “Copy” button. (Wo0T!) It’s throwing this:

Uncaught TypeError: can't access property "writeText", navigator.clipboard is undefined
    copyInstallCmd http://tango.trailsong.org/amanda:399

BTW, that “Copy” button worked earlier this afternoon. There seems to be network issues with a certain cdn from which it pulls in some javascript. (Which is kind of scary if the point is to reduce our dependency on external network resources. Hmm.) Maybe that’s okay though if it only affects the UI. We’d mostly be hitting it through scripts, not browsers.

Here’s the config. For context, 192.168.254.250 is my local static ipv4 address for tango.trailsong.org, and I’m running amanda on port 5001 (again, because reasons).

ServerName tango.trailsong.org
<LocationMatch         "^/(amanda|api|_ui)(.*)">
  SetEnvIf Request_URI "^/(amanda|api|_ui)(.*)" x_forwarded_prefix=$1
  RequestHeader set X-Forwarded-Prefix /%{x_forwarded_prefix}e
  RequestHeader set Host tango.trailsong.org
  Header        set Access-Control-Allow-Origin: *
  ProxyPreserveHost On
  ProxyPassMatch   "http://localhost:5001$2"
  ProxyPassReverse "http://localhost:5001"
  AddOutputFilterByType SUBSTITUTE text/html
  AddOutputFilterByType SUBSTITUTE application/json
  Substitute "s|http://localhost:5001|http://tango.trailsong.org/%{x_forwarded_prefix}e|iq"
  Substitute "s|http://192.168.254.250|http://tango.trailsong.org|iq"
</LocationMatch>

Leaving out any of those things breaks something in the above “things” list. Or at least it seems that way to me. I’ve reloaded variations on the above 62 times this afternoon and my brain is fried. I would have thought ProxyPreserveHost On would fix a lot, but the Host request header was often the ipv4 address rather than tango.trailsong.org. I’m going to quit banging my head to let the wall recover. It’s good enough to use as is; it’s definitely better after your changes earlier today, so thanks for that. If you’d like me to test something specific with this setup, ask away. But I’m taking a break for a while.

Thanks again for making this available.

The copy button only works on localhost or HTTPS, so instead of complicating the code, it’ll only be shown in those scenarios. Otherwise, manual selection and copying is required.

Your config also still seems a bit complicated, I’ve tried to update the example in the readme to be a bit more simple as well. I’m no longer the apache expert I was 20 years ago, so that’s about the best I can do for now.

1 Like