Using Fedora's Cockpit behind Swag's Nginx as a subfolder proxy

I’m running Fedora 35 which uses Cockpit as a server management system by default. I’m also running Docker with about 30 containers on the same system, one of them is swag for which I rely on heavily as a reverse proxy for just about every web service.

Off and on I’ve done battle with those two trying to get cockpit working behind swag as a subfolder. There are several articles out there on how to get cockpit working behind nginx using a subdomain. But I didn’t want to do that, I wanted to do a subfolder.

Finally, I got it working and thought I would share the solution that ended up working for me.

I started by reviewing a few articles on the subject, and as I mentioned these were geared towards setting it up as a subdomain, two articles which I felt were very helpful were:

https://github.com/cockpit-project/cockpit/wiki/Proxying-Cockpit-over-nginx

and

https://medium.com/@r.szulist/how-to-install-and-configure-cockpit-on-cenos8-3615d503092a

Banging at various configuration combinations based on the above and my prior nginx experience, this is what I’ve come up with for swag running on a Fedora system (I’m sure this will work with any distro using Cockpit)…

First, configure a websocket service file for cockpit /config/nginx/site-confs/cockpit.conf. The IP address you may want to use here is the docker host’s IP address on the bridge network (or any docker network that’s appropriate for your implementation). Port 9090 is the default cockpit port under fedora. Here’s mine as an example:

upstream cockpit-ws {
  server 172.17.0.1:9090;
}

Next, create a new proxy configuration file /config/nginx/proxy-confs/cockpit.subfolder.conf, this is what I ended up going with:

location /mgmt {
    return 301 $scheme://$host/mgmt/;
}

location ^~ /mgmt/ {
    # enable the next two lines for http auth
    #auth_basic "Restricted";
    #auth_basic_user_file /config/nginx/.htpasswd;

    # enable the next two lines for ldap auth, also customize and enable ldap.conf in the default conf
    #auth_request /auth;
    #error_page 401 =200 /ldaplogin;

    # enable for Authelia, also enable authelia-server.conf in the default site config
    #include /config/nginx/authelia-location.conf;

    # To use the Organizr-auth API
    include /config/nginx/proxy-confs/organizr-auth.subfolder.conf;
    auth_request /auth-0;

    include /config/nginx/proxy.conf;
    include /config/nginx/resolver.conf;

    proxy_buffering off;
    proxy_pass https://cockpit-ws;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection $connection_upgrade;

    proxy_set_header Origin https://$host;
}

What these proxy options essentially do is “upgrade” a normal HTTP proxy into a WebService proxy. A WebService proxy is handled a bit differently by nginx, essentially, it allows the connection to remain connected for a longer time than normal for HTTP, and be more asynchronous-friendly.

A couple of notes to the above config:

1st. I threw in the auth configurations to be consistent with the other swag proxy-confs files. I run Organizr, and use the organizr-auth API for several of my container-based web services. In this case it works okay, I have Cockpit set up in organizr as one of the menu items. So when I hit my server I end up at Organizr first, authenticate there, then when I select Cockpit, it opens in a new window and I’m presented with Cockpit’s standard authentication page. I do not use organizr-auth to authenticate with Cockpit.

2nd. You’ll notice my location is “mgmt” and not “cockpit” as you’d normally intuitively use. This is because “cockpit” is a reserved word in the URL as per here (see UrlRoot):

https://cockpit-project.org/guide/latest/cockpit.conf.5

(sorry that’s not a link, apparently new users are limited to two links)

Once you’ve created the above two files you can restart your swag container.

Now the last bit of this is to create a /etc/cockpit/cockpit.conf file, on Fedora this is not normally there. Here’s what I ended up going with:

[WebService]
Origins = https://myserv.mydomain.com https://192.168.0.20:9090
ProtocolHeader = X-Forwarded-Proto
UrlRoot=/mgmt/

[Log]
Fatal = /var/log/cockpit.log

[Session]
IdleTimeout=15

A few notes on the above config, these are the essentials:

Origins: This is a space-separated list of locations your WebSocket service will be connecting from. The first one in my example is the $host value of my nginx server in swag. The 2nd one is when I connect locally from my LAN bypassing nginx proxy (for example, if my docker system and/or swag is down).

ProtocolHeader: This is used by cockpit to let it know which HTTP header to use to determine the protocol, Cockpit does both http and https on the same port (9090), so it needs to know if it’s going to do a TLS handshake.

UrlRoot: This bit is only needed because we’re doing a subfolder-type proxy, this wouldn’t be used when doing a subdomain-type proxy. This must match the location you used in your proxy conf.

The other options are my own personal preference and are not required. IdleTimeout is in minutes.

Once that’s set up, restart cockpit, I use:

systemctl stop cockpit
systemctl restart cockpit.socket

It could also be handy to follow systemd’s output when testing your implementation:

journalctl -f

Then hit the proxy URL with your web-browser.

Note, that one of the troubles I had trying to figure out this config was a core dump happening in cockpit that I did not notice early on until I started following it with journalctl -f

The core dump happens when I hit cockpit directly (not via proxy) using my local LAN address (https://192.168.0.20:9090/mgmt/ in my setup). If I leave the trailing / off, it core dumps. This sounds buggy to me so I’m going to report it on cockpit’s github page later today.

Anyway, that’s it.

Cheers!
-Mike

I didn’t read all of this, but I will point out (and hopefully you didn’t mention it already) subfolder must be supported by the app itself. Perhaps everything you see is subdomain because cockpit itself is not designed to support subfolder. It’s also why you will see more subdomain confs in swag than subfolder. subdomain will just work, subfolder requires extra effort from the app dev.

Cockpit supports subfolder, see the UrlRoot secret sauce.

Curious if anyone has gotten cockpit to work with Authelia? I previously had an older swag config (Version 2020/05/31) and this worked as expected. I recently upgraded to a fresh swag instance (Version 2023/02/05) and enabling Authelia seems to break it.