Pre-1.0 hoardDB is pre-1.0. Expect breaking changes.

Security

What hoardDB protects out of the box, what you have to turn on, what it does not protect, and how to tell us about a vulnerability. Every feature on this page is free and unlocked in every build; there is no enterprise tier for TLS, users or encryption.

What a fresh install does

With no configuration, hoardDB-server:

  • serves every connection over TLS 1.3, with no way to downgrade;
  • requires a login on every connection before it will answer anything;
  • generates a root credential the first time it starts, prints it once and saves it to <data dir>/root.password at mode 0600 (set HOARDDB_ROOT_USER and HOARDDB_ROOT_PASSWORD to choose your own instead);
  • creates its own private keys with mode 0600, in the data directory;
  • stores passwords only as hashes, and leaves the audit log, the metrics endpoint and the profiling endpoint off.

It does not encrypt the data on disk until you ask it to, and it listens on every network interface. Both are covered below.

TLS and self-signed certificates

A node generates an Ed25519 key and a self-signed certificate on its first start and serves them from <data dir>/keys/server.key and server.crt. No certificate authority is involved, so there is nothing to renew and nothing for a client to download — and nothing that proves to a client that it reached the right machine, other than the certificate’s fingerprint.

The server prints its fingerprint every time it starts, in the line that says it is listening:

{"level":"INFO","msg":"TCP driver listener listening","address":"[::]:7433","fingerprint":"SHA256:d8aa96c0…","alpn":"hoarddb/1"}

Trusting a server from the CLI

The CLI treats a server it has never seen the way ssh treats a new host. It shows the fingerprint and asks, and it remembers the answer, for that address, in ~/.hoarddb/known_servers. A later connection to the same address whose key is different fails with WARNING: server key for <address> has changed! and shows both fingerprints, instead of asking again. If you did replace the server, remove the old entry from that file; if you did not, treat the warning as an attack until you know otherwise.

A script has nobody to ask, so a non-interactive session refuses:

The authenticity of server '127.0.0.1:7433' cannot be established.
ED25519 key fingerprint is SHA256:5612612c….
Refusing to trust it non-interactively: pre-populate ~/.hoarddb/known_servers or pass --insecure to bypass.

Compare the fingerprint with the one in the server’s start-up log, then answer y. For unattended use, put the server’s entry in ~/.hoarddb/known_servers ahead of time.

-insecure skips the check altogether. It prints a warning that says exactly what it gives up, and it is for a server you started yourself on the same machine. It is a setting of the client and never makes a server less strict. Do not put it in a script that talks to a server across a network.

Nodes trusting each other

Nodes use the same certificates between themselves. A node that is told to join a cluster must be given the fingerprints it should expect (HOARDDB_SEED_FINGERPRINTS) and refuses to start without them; a certificate that changes for a node it already knows is a hard failure, not a quiet re-trust. Configuration has the details.

Your own certificate

You cannot substitute your own certificate in this version. A node started with HOARDDB_TLS_MODE=custom, or with HOARDDB_TLS_CERT, HOARDDB_TLS_KEY or HOARDDB_TLS_CA set, refuses to start: it exits with an error that names the setting, rather than quietly serving the certificate it generated. Unset the setting and use the generated certificate, which clients trust by its fingerprint. Operator-supplied certificates come after 1.0. There is no certificate authority check to configure; the fingerprint is the identity.

Users and authentication

Every connection logs in once, and what the connection may do is decided by the roles the user held at that moment.

  • Passwords are hashed with Argon2id, with a server-side secret added, and the plain text is never stored. A user’s password must be at least 12 characters (HOARDDB_PASSWORD_MIN_LENGTH).
  • A password is never a command-line argument. The CLI prompts, or reads a 0600 file or standard input, so it does not end up in ps or shell history.
  • Repeated failures slow an address down. After five failed logins in a row from one address, each further attempt is delayed, doubling from one second up to thirty. At HOARDDB_TCP_AUTH_MAX_FAILS failures in a row (default 20) the address is refused outright for sixty seconds, before any password is checked. A successful login clears the count.
  • Roles are per database, from read up to root. A user with no grant on a database is refused there, and the refusal names the roles the user has and the command that would fix it.
  • Disabling or dropping a user ends that user’s sessions at once. Granting or revoking a role takes effect at the user’s next login; an open connection keeps the roles it logged in with. If you need a change to bite immediately, change the password or disable the user.

Creating users, roles and the full list of what each can do: User management. The honest list of what this model does not do: Limitations.

Rotating the root password

Stop the server and run:

hoardDB-server -reset-password admin

It asks for the new password twice, without echo, and deletes <data dir>/root.password once the new one is written, so the bootstrap secret does not linger on disk.

The cluster token

A cluster has a token that every node holds (<data dir>/cluster.token, mode 0600). On a clustered node the keys that sign login tokens are derived from it, so whoever holds it can forge a login and can join a node to the cluster. Treat it like a signing key: keep it out of backups that other people can read, and delete it, and the node’s users.json, when you decommission a node.

Encryption at rest

Encryption at rest is built for a whole node, and it is off by default.

When it is off, the data directory is plaintext on disk, and only the users.json file is sealed, which is not encryption at rest for your data. When you turn it on, every bucket on that node is encrypted under a key that you provide and that is not kept in the data directory:

encryption:
  keystore: keyfile
  key_file: /etc/hoarddb/kek
  mode: require

What to know before you rely on it:

  • It belongs to the node. One key opens every bucket on the node; there is no per-bucket key. Per-database and per-bucket keys are planned and are not built.
  • It is decided when the data directory is first started. You cannot switch a node between encrypted and plaintext afterwards; you dump, and restore into a fresh directory started the way you want.
  • An encrypted node seals blob payloads too. Blob content of 64 KiB and over is stored in files that are encrypted and authenticated in pieces, under keys kept in keys/blobs.wrapped. Back that file up with the data directory: the node-master escrow does not cover it, and without it sealed blob files cannot be read.
  • A dump is plaintext whatever the node’s mode. Protect the file the way you protect the data.
  • Losing the key is unrecoverable data loss. Back the key up apart from the data directory.
  • It protects a copied disk or data directory. It does not protect against someone who can run code as the server’s user, who can ask the same keystore for the key. It does not replace disk encryption either, which also covers swap and freed blocks.

How to set it up, the three places the key can come from, export and import of the node key, and what a cluster does with an unencrypted peer: Configuration.

Default network exposure

A node you start with no arguments listens on this machine only. Nothing on the network can reach it until you say so. The Docker image is the exception: it listens on every interface inside the container, because loopback inside a container cannot be reached through docker run -p, and what the outside world can reach is then decided by the ports you publish.

WhatDefault (binary)Docker imageSetting
Client port (TCP+TLS)127.0.0.1:74330.0.0.0:7433HOARDDB_TCP_LISTEN
Node-to-node port127.0.0.1:44340.0.0.0:4434HOARDDB_LISTEN (the node address; the port is its port plus one), or HOARDDB_TCP_INTERNODE_LISTEN to set it directly
Metricsoff; when on, 127.0.0.1:90900.0.0.0:9090HOARDDB_METRICS_ENABLED, HOARDDB_METRICS_LISTEN
Profilingoff; when on, 127.0.0.1:6060127.0.0.1:6060HOARDDB_PPROF_ENABLED
Audit logoffoffHOARDDB_AUDIT_ENABLED, HOARDDB_AUDIT_OUTPUT

The start-up log says where the node listens, and when it is on loopback it names the settings that open it up.

The start-up log shows the node’s address in the ring, which is HOARDDB_LISTEN. Its default, 127.0.0.1:4433 for HOARDDB_LISTEN, is the name other nodes use for it, not a port the node opens.

Opening a node to other machines

  • Applications on other machines. Set the client address. 0.0.0.0 means every interface; use one interface’s address to be narrower:

    HOARDDB_TCP_LISTEN=0.0.0.0:7433 hoardDB-server
    
  • A cluster across machines. Set HOARDDB_LISTEN to the address the other nodes reach this node on, and list that same address in HOARDDB_SEED_NODES on every node:

    HOARDDB_LISTEN=10.0.0.1:4433 HOARDDB_SEED_NODES=10.0.0.1:4433,10.0.0.2:4433,10.0.0.3:4433 ...
    

    A node whose seeds are on another machine but which is still on the loopback default is refused at start, with the setting to change in the message: it would tell its peers to reach it on 127.0.0.1, which only it can do. A cluster whose nodes all run on one machine (every seed on 127.0.0.1) needs no change.

Two things follow:

  • On a host that is reachable from outside your network, bind to the interface your applications use rather than 0.0.0.0, or put a firewall in front of the client port and the node-to-node port. Peers must hold the cluster token to use the node-to-node port, but the port accepts a connection from anywhere it is reachable, so keep it to the nodes of your cluster.
  • The client port is protected by login and TLS, not by obscurity. Anyone who can reach it can try passwords, which the failure delay slows down but does not stop. Choose long root and user passwords.

Metrics exposure

When you enable metrics, /metrics is served over plain HTTP with no authentication. It reports counts, durations and capacity readings for the node and carries no document content, and it keeps no series per bucket. It is still information about your system. It listens on the loopback interface by default, so a scraper on the same machine needs nothing more; to let a monitoring host reach it, bind to the address of your monitoring network (not 0.0.0.0 on a host that faces the internet):

HOARDDB_METRICS_ENABLED=true HOARDDB_METRICS_LISTEN=10.0.0.5:9090 hoardDB-server

With metrics off, the node’s capacity warnings still go to its log and to hoardDB status; see Is this node full?. The profiling endpoint is also unauthenticated and plain; leave it off on a server.

What is not protected

A short list, so that you do not assume otherwise:

  • A connection is checked when it logs in, not on every request. A role change reaches an open connection only at its next login.
  • There is no way to revoke one login token without affecting that user’s others. Revocation is per user.
  • Anyone who holds the cluster token can act as a cluster member and can forge logins (above).
  • Anyone who can read the data directory can read a plaintext node’s data. The only file sealed on a node without encryption at rest is users.json, and its key sits in the same directory.
  • A dump file, and the key file you export for escrow, are not encrypted.
  • The CLI’s -insecure flag removes the server-identity check for that session.

Reporting a vulnerability

Please report a security problem privately, not in a public issue or a discussion.

Send your report to the security contact named in SECURITY.md at the root of the repository (TODO(owner): contact address). Include:

  • what you found and which version or commit it affects (hoardDB-server -v);
  • how to reproduce it, as short as you can make it;
  • what an attacker gains, if you know.

We acknowledge a report, agree a disclosure date with you, and fix the problem before it is described in public. A security fix is announced in the changelog after it ships, and the people who reported it are credited if they want to be. Please do not test against systems you do not own.

Source: docs/user/security.md in the repository.