Sign in

Search issue titles and bodies, change requests and comments. Private repositories you cannot read are not included.

vole
Clone

Read only, no credentials:

https://rodent.software/git/vole.git

Write over SSH with a registered key:

git clone ssh://git@0.0.0.0:2222/vole.git

A free and open-source forge that lives in your Git repo.

Validate the configuration and harden what a deployment creates

ed1812123cd9e9df0f34fdaf0d2511a340c2aaa6 · Keenan Nemetz · 13d ago · parent 13cff33f

Validate the configuration and harden what a deployment creates

Two ways an instance used to run in a state it should have refused.

The configuration was never checked. `dev_login` was guarded only by a loopback
listener, so a proxy that rewrites Host to 127.0.0.1 inherited a credential-free
session; open registration could be enabled on a plain-HTTP origin WebAuthn would
never accept; a public_url could carry a path or credentials; a size could be
negative. Config.Validate now runs before anything is opened and reports every
problem at once, and dev_login requires an explicit dev_only opt-in next to a
loopback listener with no public address. The per-request check no longer trusts
Host at all: it must name the configured listener, and a request carrying a
forwarding header gets no session.

The files were never checked either. directories were created 0755 and the audit
log 0644, and a file keeps the mode it was created with, so an upgraded instance
stayed exposed. internal/secure creates secrets at 0600 in directories at 0700,
tightens what an existing install left, keeps group access for a shared backup
group, and refuses to start when a mode cannot be changed. Every audit writer and
the account backup go through it. The service unit drops all capabilities,
sets UMask=0027, disables core dumps, hides other processes and devices, and
restricts address families; docs/operations.md now states the production
baseline.

server/server.go +15

@@ -52,10 +52,25 @@ func New(cfg *config.Config, binPath string, log *slog.Logger) (*Server, error)
52 52 if log == nil {
53 53 log = slog.Default()
54 54 }
55 // Refuse an unsafe configuration before anything is opened: a listener that
56 // must not be reachable, a development login next to a public URL, a size that
57 // cannot be satisfied. A warning here would be read by nobody.
58 if err := cfg.Validate(); err != nil {
59 return nil, err
60 }
55 61 inst, err := forge.Open(cfg, binPath)
56 62 if err != nil {
57 63 return nil, err
58 64 }
65 // A secret that is group- or world-readable is a secret already disclosed.
66 // Tighten what an older install left, say so, and refuse to run when the mode
67 // cannot be changed: an instance that cannot protect its keys must not serve.
68 if tightened, err := forge.TightenSecrets(cfg); err != nil {
69 inst.Close()
70 return nil, err
71 } else if len(tightened) > 0 {
72 log.Warn("tightened permissions on secret files", "paths", strings.Join(tightened, ", "))
73 }
59 74 if inst.ImportedACLs > 0 {
60 75 log.Info("access rules imported", "repos", inst.ImportedACLs)
61 76 }