Docker has an authentication problem. When you build a docker, you don't want to include authentication information to external resources in the docker itself, but you need to be able to provide that information easily at runtime. Some people advocate for using a vault api and retrieving credentials from an external service; that may work, but there is a bit of a chicken and egg problem there (how do you authenticate to the vault?) and not all applications support that. I'm thinking in particular of Tomcat, which requires authentication information in server.xml for realms that connect to external databases, and in each webapp's context configuration (eg, ROOT.xml) for the same situation. How to pass those in at runtime when they are large and complex files?
The best workable solution appears to be passing them in as environment variables on the docker command line, and running sed in your docker entrypoint to do a search and replace within your configuration files. You can do that using the username, password, and driver name easily. The database url will need some escape characters so you may choose to leave it intact or replace it in pieces. You'll have to write a small script to do this at startup time within the docker and remember to pass the environment variables into docker each time you run it (ie, another script).
This seems workable and secure so long as no one else can access your running docker container. There are potential flaws if your docker containers are running in the cloud, so there's room for improvement, but if you are using Tomcat to begin with, those configuration files are sitting on the docker filesystem already.
Another option would be to load the tomcat configuration entirely from an external disk, using a volume. That's certainly convenient and seems to work, when running on a local server within docker, even if not ideal for a cloud deployment.
Wed Oct 23 03:59:09 CDT 2019
First, a statement of the problem: A vaadin application that needs to authenticate users in a manner compatible with Tomcat's container-based authentication (8.5, preferably). Vaadin goes to some lengths to prevent me accessing the HttpServletRequest directly, where I could use login() or authenticate(). They have a login control that can post to a form url which I might be able to make work. Currently, I have a sort of working solution that checks passwords against the same database as Tomcat, but I need to access the credential handler from tomcat to match anything other than passwords stored in plaintext. The current solution works fine in dev but obviously is a non-starter in production. Also, dev is running on jetty and doesn't have the environment setup properly to match Tomcat. So whatever I do also has to work there, even if it doesn't quite work the same way.
I can get a RequestListener but it doesn't give me access to the methods I need. I'd rather not post to the form handler .. although maybe that will work. I'll at least try it.
I think I will try to configure an instance of the credential handler in the context. That way I can run credentials through exactly as tomcat would in production, and in dev, it just won't be configured so I can do something else.
Thu Jul 25 09:52:01 CDT 2019
Another recently encountered problem: how to make Docker container DNS work when the configured DNS server is blocked.
The answer is simple:
/etc/docker/daemon.json contains the docker daemon configuration, and you can specify additional defaults. In this case, I added my local DNS server first, followed by the Google public DNS server (which I prefer not to use generally, but am OK with a docker container using).
"dns": ["192.168.1.1", "22.214.171.124"]
I wonder if there's a market for a docker image with a basic caching resolver like dnscache already configured? It's small, efficient, and removes the need for overriding configurations like this.
Fri Jun 21 17:45:00 CDT 2019
So I ran into an interesting problem recently. I run a number of websites with custom software, this blog being one of them. I use Let's Encrypt to automatically obtain SSL certificates for them, since they are personal sites that don't do e-commerce and the free cert is fine. They are running on Tomcat, using JSP and servlets, with a database backend, and Apache HTTPD in front handling the SSL part. If it wasn't for the SSL part, I would be happy with Tomcat serving them directly. Load is not really a big concern, given the size. But to fit in with the automatic certificates, I need Apache HTTPD in front.
That broke for me recently, when some software got updated and was slow to percolate downwards. When the fixes finally did percolate, the renewals still didn't work. Why not? Because the renewal process needs to access urls with Apache that were being forwarded to Tomcat. Tomcat of course had no idea what to do with them.
Once I figured out the problem it was easy to solve; make sure the url needed by Let's Encrypt was not one of those forwarded. But that broke my (easy, default) configuration of just forwarding everything to Tomcat. And there's no way to specify an inverted forward (eg, forward everything EXCEPT the Let's Encrypt requests).
UPDATE: JkUnmount can unforward a specific path and can be specified precisely enough to cover certbot for this purpose. Documented here
. Other places I checked did not have it. JkAutoAlias appears to solve the autoconfiguration problem I was thinking about, too.
1) Forward only *.jsp and /servlet/*. This is pretty standard, and lets Apache HTTPD serve images and other static files which would be important if I was more worried about performance, but has a number of other issues. For example, suddenly my permissions have to allow for both tomcat and apache ... even if my update process is an automated deploy of a war file extracted by tomcat. And I can't use normal directory urls (like "/directory/") unless I either provide a DirectoryIndex of index.jsp (which gets forwarded, but isn't what I want the url to be) or prefix it with servlet.
2) Forward *.jsp, /servlet/*, and everything from each additional webapp (like "/weblog/*"). This mostly works (especially since my ROOT webapp is usually small) but needs to be updated each time I need a new webapp added and for each site.
So why hasn't someone solved this problem by writing an Apache HTTPD plugin or module that can actually ask Tomcat what urls it wants to handle and which are handled dynamically, so Apache HTTPD can avoid this configuration step and handle it automatically?
There is an option in Tomcat that dates back to Tomcat 3.3 that tries to do something like this by auto-generating Apache configuration files. I suspect this is not exactly what I am looking for. But it's an interesting problem.
Fri Jun 21 17:39:21 CDT 2019