Cockpit
A Conn3x circuit that installs Cockpit, the web console for Linux servers, and sets how it listens, which certificate it serves and who may log in. Cockpit gives you a server’s logs, services, storage, network, updates and a terminal in the browser, logged in with the server’s own accounts. It’s the thing you open when SSH would do but a graph of the last hour would do better.
You pick the port, the address, the extra pages and the certificate once, and the same settings land on Debian, Ubuntu, Fedora and Rocky Linux. The circuit handles the parts that differ: backports on Debian and Ubuntu, as Cockpit’s own install guide recommends, SELinux’s port list on the Red Hat family, and the extra pages each distribution packages. Root can’t log in unless you say so.
module "cockpit" { source = "github.com/conn3xorg/circuit-cockpit?ref=v0.1.0"
hosts = { web1 = "203.0.113.10", web2 = "203.0.113.11" } ssh_private_key_file = "~/.ssh/id_ed25519"
packages = ["cockpit-podman", "cockpit-files"] login_title = "web servers"}Then run conn3x init and conn3x apply. A second conn3x plan says No changes. Browse to https://203.0.113.10:9090/ and log in with an account on that server. Cockpit’s self-signed certificate means a browser warning until you give it a real one.
Cockpit logs in with the server’s passwords, so put it behind a firewall or a VPN rather than on the open internet, and give the accounts that use it strong passwords. circuit-firewall can keep the port to your own network, and circuit-fail2ban can ban addresses that keep failing to log in.
For hosts created in the same plan, pass host objects with the host key you generated, so Conn3x checks it without a known_hosts entry. Give each one’s distribution in os too: the circuit picks its packages by distribution, and Conn3x can’t ask a host that doesn’t exist yet. If os turns out not to match the host, Conn3x stops before configuring it. Set os as well when you put depends_on on this module: depends_on holds back reading the hosts until the apply, so without os the plan fails with Invalid for_each argument.
hosts = { for name, d in digitalocean_droplet.web : name => { address = d.ipv4_address host_key = tls_private_key.host[name].public_key_openssh os = "ubuntu" }}To try it on a throwaway container first, see examples/local.
A real certificate
Section titled “A real certificate”Point tls_certificate and tls_key at a certificate and its key on the host. The circuit links them into /etc/cockpit/ws-certs.d as zz-conn3x.crt and zz-conn3x.key, which Cockpit picks over its own self-signed one. Cockpit reads them as root, so the key can stay mode 0600.
With circuit-certbot, use the paths under live, and have certbot restart Cockpit after each renewal so it serves the new certificate:
module "certbot" { source = "github.com/conn3xorg/circuit-certbot?ref=v0.1.0" # ... deploy_hook = "systemctl try-restart cockpit"}
module "cockpit" { source = "github.com/conn3xorg/circuit-cockpit?ref=v0.1.0" # ... tls_certificate = "/etc/letsencrypt/live/cockpit.example.com/fullchain.pem" tls_key = "/etc/letsencrypt/live/cockpit.example.com/privkey.pem"}The same goes for any certificate you already have for circuit-apache or another web server: Cockpit can use the same files.
Behind a reverse proxy
Section titled “Behind a reverse proxy”To serve Cockpit at https://cockpit.example.com through Apache, nginx or Caddy, keep Cockpit on loopback and tell it about the proxy:
address = "127.0.0.1" origins = ["https://cockpit.example.com"] behind_proxy = true allow_unencrypted = trueorigins lets the browser’s WebSocket in from the proxy’s address. behind_proxy makes Cockpit read X-Forwarded-Proto and X-Forwarded-For, which only the proxy should be able to set; that’s why Cockpit listens on loopback. allow_unencrypted is for a proxy that talks plain HTTP to Cockpit; leave it out if the proxy connects over HTTPS. The proxy has to pass WebSocket upgrades through, as Cockpit’s proxy guide shows for Apache.
What it does
Section titled “What it does”On each host, in order:
- On Debian and Ubuntu, with
backportson: adds<codename>-backportsto Debian’s apt sources unless it’s there already (Ubuntu has it out of the box), then runsapt-get install -t <codename>-backports cockpitwith your extra pages, the way Cockpit’s install guide does. Withbackportsoff, the same packages come from the release. - On the Red Hat family: installs
cockpitand your extra pages with dnf. Whenportisn’t 9090, adds it to SELinux’swebsm_port_t, the type Cockpit may listen on, withsemanage. - Writes
/etc/cockpit/cockpit.confwithAllowUnencrypted, andOrigins,LoginTitleand the proxy headers when you set them. - Adds
rootto/etc/cockpit/disallowed-users, which Cockpit’s PAM stack checks, or takes it out withroot_login = true. - Links your certificate and key into
/etc/cockpit/ws-certs.d, when you set them. - Writes
/etc/systemd/system/cockpit.socket.d/listen.confwith the port and address, reloads systemd when it changes, and startscockpit.socketnow and at boot. systemd listens on the port and starts Cockpit on the first connection. - Restarts
cockpit.socket, which restarts Cockpit with it, when the config, the certificate links or the port change. That logs out anyone using Cockpit at the time.
cockpit pulls in the pages the distribution recommends with it: on Debian and Ubuntu that includes cockpit-networkmanager, which installs NetworkManager. On the Ubuntu 24.04 droplet it was tested on, NetworkManager left netplan’s interfaces unmanaged, and the network came back as before after a reboot.
Rocky Linux and RHEL cloud images often have cockpit.socket on 9090 already. The circuit takes it over: the drop-in sets the port, and the restart moves it.
Before the circuit runs, the conn3x_host data source reads each host’s distribution over SSH. If you ask for a page the distribution doesn’t package, such as cockpit-pcp on Fedora, the plan stops with an error naming the host and the page.
| Page | Debian 13 | Ubuntu 24.04 | Fedora 45 | Rocky Linux 10 |
|---|---|---|---|---|
cockpit-files, -machines, -networkmanager, -packagekit, -podman, -sosreport, -storaged |
Yes | Yes | Yes | Yes |
cockpit-kdump, -ostree, -selinux |
No | No | Yes | Yes |
cockpit-pcp |
No | Yes | No | No |
On Rocky Linux and RHEL 10, cockpit-networkmanager, -selinux, -kdump and -sosreport are part of cockpit-system, which cockpit installs; asking for them is fine.
What it doesn’t do
Section titled “What it doesn’t do”- It doesn’t open the port in a firewall. On a host with firewalld or ufw on, open it with circuit-firewall or by hand.
- It doesn’t create users or set passwords. Log in with an account that’s in the
sudoorwheelgroup, then turn on administrative access in Cockpit’s top bar. - It doesn’t set up the reverse proxy itself, or get a certificate.
- It doesn’t upgrade Cockpit after the first install. On Debian and Ubuntu, upgrade with
apt-get install -t <codename>-backports cockpitto stay on backports. - Deleting the module leaves Cockpit installed and listening, with the last config.
Inputs
Section titled “Inputs”| Name | Type | Default | Description |
|---|---|---|---|
hosts |
any | Required | Hosts to configure, by name. Each value is an address, such as "203.0.113.10", or a host object such as { address = "203.0.113.10", host_key = "ssh-ed25519 AAAA..." }. A host object can also set user, port, private_key_file, jump_hosts and os, the distribution’s ID, such as ubuntu or rocky, which a host created in the same plan needs. The two forms can be mixed. |
ssh_user |
string | "root" |
User to log in as. |
ssh_port |
number | 22 |
SSH port. |
ssh_private_key_file |
string | null |
Path to a private key. When null, Conn3x uses the SSH agent. |
ssh_known_hosts_file |
string | null |
known_hosts file to check host keys against. When null, ~/.ssh/known_hosts. Not used for a host object with host_key. |
ssh_become |
bool | false |
Run commands as root through sudo. Set it when ssh_user isn’t root. |
packages |
list(string) | [] |
Extra Cockpit pages, such as ["cockpit-podman", "cockpit-machines"]. See the table above for which distribution has which. |
backports |
bool | true |
On Debian and Ubuntu, install from <codename>-backports, which carries a newer Cockpit than the release. |
port |
number | 9090 |
TCP port Cockpit listens on. |
address |
string | null |
IP address Cockpit listens on, such as "127.0.0.1". When null, every address. |
origins |
list(string) | [] |
Other origins the browser may reach Cockpit from, such as ["https://cockpit.example.com"]. Cockpit always accepts its own address. |
login_title |
string | null |
Browser title for the login page. When null, Cockpit’s default. |
allow_unencrypted |
bool | false |
Accept plain HTTP from other hosts instead of redirecting it to HTTPS. Only for a reverse proxy that talks HTTP to Cockpit. |
behind_proxy |
bool | false |
Trust X-Forwarded-Proto and X-Forwarded-For from a reverse proxy. Set it only when nothing but the proxy can reach Cockpit’s port. |
tls_certificate |
string | null |
Path on the host of a PEM certificate with its chain, such as /etc/letsencrypt/live/cockpit.example.com/fullchain.pem. When null, Cockpit’s self-signed certificate. |
tls_key |
string | null |
Path on the host of the certificate’s unencrypted private key. Set it with tls_certificate. |
root_login |
bool | false |
Let root log in to Cockpit with its password. |
Outputs
Section titled “Outputs”| Name | Description |
|---|---|
urls |
Cockpit’s address on each host, such as { web1 = "https://203.0.113.10:9090/" }. |
Requirements
Section titled “Requirements”Requires Conn3x v0.6.9 or later and systemd on each host, with a package mirror the host can reach. On Debian, the host also needs to reach deb.debian.org for backports, unless backports is off.
Tested on
Section titled “Tested on”Rocky Linux 10, Fedora 45, Ubuntu 24.04 and Debian 13. CI applies examples/local to each on amd64, checks that the second plan has no changes, that cockpit.socket is on and the login page answers on 9090 with the example’s title, then creates a test user in the container and checks it logs in, a wrong password doesn’t, and root is turned away.
The CI containers run with SELinux off. On a Rocky Linux 10 droplet with SELinux enforcing, which came with cockpit.socket already on 9090, the circuit moved Cockpit to port 9443 with a certificate under /etc/letsencrypt/live, cockpit-podman and cockpit-selinux. A test user logged in, Cockpit served the certificate, and after replacing it, systemctl try-restart cockpit served the new one. On an Ubuntu 24.04 droplet, Cockpit and cockpit-podman came from noble-backports and a test user logged in. Both came back after a reboot with no changes in the next plan, and the Rocky Linux audit log had no denials.
A loopback address with behind_proxy, allow_unencrypted and root_login was tried in a Fedora 45 container: Cockpit listened on 127.0.0.1 only, answered plain HTTP, and let root log in. Setting them back put Cockpit on 9090 with its self-signed certificate and turned root away again.
License
Section titled “License”MPL-2.0. See LICENSE. Cockpit itself is LGPL-2.1-or-later.
Source
Section titled “Source”- Repository: conn3xorg/circuit-cockpit. This page covers v0.1.0; the repository’s tags list every release.
- Examples: examples/local.
- Related circuits: Firewall, fail2ban, certbot, Apache httpd, Proxmox VE, Forgejo, GitLab CE, n8n, Fleet.