Files
tilde.club/docs/shellserver.md
T

7.0 KiB
Raw Blame History

Running the shell host

Use the setup guide for a new machine. This chapter covers the work after people start using it.

Accounts and groups

Give each person a private primary group. Use supplementary groups for specific shared resources. The production makeuser also adds a common group by numeric ID; resolve that ID locally before copying the policy to another host.

Keep the list of privileged groups short. Membership in wheel, Docker's administration group, or a service configuration group can be equivalent to root access. Shared web backends need their own identities and narrowly scoped permissions; see web publishing.

To suspend a compromised account, disable its actual login paths. Locking the Unix password alone does not necessarily prevent public-key SSH or existing sessions. Check keys, SSH account rules, active sessions, cron, user services, ZNC, web applications, and application-specific credentials. Preserve the data needed to understand the incident before deleting anything.

Use account provisioning as a checklist. Provisioning a Unix account and provisioning its external services are separate operations.

Disk quotas

The collected host uses XFS with usrquota,grpquota on the root filesystem. /home is a directory on that filesystem. The collector's attempt to query xfs_quota with /home as a mount path failed even though the command returned zero, so inspect its output as well as its exit status.

First discover the mount:

findmnt --target /home --output TARGET,FSTYPE,OPTIONS

On the recorded layout, query and set limits using /:

xfs_quota -x -c 'state' /
xfs_quota -x -c 'report -h -u' /
xfs_quota -x -c 'limit -u bsoft=1G bhard=3G member' /
xfs_quota -x -c 'quota -h -u member' /

These match the block limits requested by the current provisioning script. The soft limit can be exceeded during a grace period; the hard limit cannot. Check the grace policy and add inode limits if small-file counts are a concern. If /home is a separate XFS mount on your host, use that mount instead.

Enabling XFS quotas can require mount or boot configuration work. Follow the filesystem's documentation, take a backup, and arrange a maintenance window. Do not remount a busy filesystem using an unreviewed command copied from here. For ext4 or another filesystem, use its own quota tools and activation procedure.

Mail and user applications can live in the same quota as the home. Decide what happens when someone reaches the hard limit: mail delivery and saving an editor buffer are both affected.

Process, memory, and CPU limits

The system record did not establish per-user limits. It reported an unlimited aggregate user.slice, which alone says nothing about individual user slices or PAM limits.

For a new host using systemd user slices, a starting example is /etc/systemd/system/user-.slice.d/50-tilde-limits.conf:

[Slice]
TasksMax=512
MemoryHigh=1G
MemoryMax=2G
CPUQuota=200%

Those values are examples to test, not the existing server's measured policy. The drop-in applies to user-UID.slice units. Apply configuration changes with systemctl daemon-reload, then check a fresh test session:

id -u member
systemctl show user-UID.slice -p TasksMax -p MemoryHigh -p MemoryMax -p CPUQuotaPerSecUSec
loginctl user-status member

Replace UID with the number printed by id. Verify SSH sessions are actually placed in that slice. A hard memory limit can terminate a user's work. Tell people what the limits are and watch for repeated limit hits before tightening them. PAM limits.d settings are useful for session limits but are not a replacement for aggregate cgroup limits.

Run long-lived community daemons as separate service users with their own limits. Enable user lingering only where persistent user services are intended. Do not quietly turn every interactive account into an unrestricted boot-time service account.

Ports and firewall policy

The existing host uses CSF, with IPv4 and IPv6 rules and a number of historical exceptions. Its broad port ranges are not a minimum firewall template. A new host should allow the services it actually serves.

Service Typical inbound port When it is needed
SSH TCP 22 Member and admin shell access.
HTTP and HTTPS TCP 80, 443 Publishing and certificate validation.
SMTP delivery TCP 25 Receiving Internet mail.
Authenticated submission TCP 587 or 465 Sending mail from remote clients with TLS.
IMAPS TCP 993 Remote mailbox access.
ManageSieve TCP 4190 Remote filter editing, if enabled with appropriate TLS/auth.
Gopher TCP 70 Public Gopher service.
Gemini TCP 1965 Public Gemini service.
Finger TCP 79 A deliberately enabled finger service.
Ident TCP 113 Trusted IRC servers querying user identity.
NNTP/NNTP over TLS TCP 119, 563 Remote newsreaders, if intentionally allowed.
Mosh UDP chosen from 60000–61000 An explicit Mosh port allocation; the range is not a universal default.

IRC client connections are outbound. You do not need an inbound IRC daemon port just because members run an IRC client. Database, cache, PHP, CGI, and mail filter sockets should remain local unless there is a separately designed reason for remote access.

Check ss -lntup, the configured bind addresses, host rules, provider rules, and an outside connection test. A listening socket is not proof of public reachability, and a firewall listing is not proof of a working service.

Updates and local programs

Install supported OS updates, read service migration notes, and schedule reboots. For a shared host, announce downtime through a public status/contact path and inside the shell. People may have a compiler or editor running when you reboot.

Keep an inventory of programs in /usr/local, services built from source, Python virtual environments, Ruby gems, and application dependency lockfiles. An RPM update will not update a different executable earlier in PATH. The recorded borgmatic installation is a concrete example: cron uses /usr/bin/borgmatic 2.1.9 while /usr/local/bin/borgmatic reports 1.5.21.

Do not update every language ecosystem globally as root on behalf of all users. Separate OS packages, operator applications, and user-managed dependencies. Keep root-owned service code outside user-writable directories.

Routine checks

systemctl --failed
systemctl list-timers --all
ss -lntup
df -h
df -i
journalctl -p err --since today

Review mail queues, backup completion, certificate expiry, disk and inode use, resource-limit events, and pending account requests. Keep logs rotated and retain enough history to investigate failures without logging passwords or private user content unnecessarily. See accounting for what session and process accounting can actually tell you.

Document how to recover from a failed update, restore an account, rotate a credential, handle an abuse report, and reach someone when the host is down. See backup and recovery for the restore procedure.