GoodMem
ReferenceSecurity

TLS Configuration

Overview of TLS modes, self-signed defaults, and certificate options for GoodMem deployments

TLS Configuration

This guide explains how GoodMem configures TLS for its REST and gRPC endpoints, how the GoodMem CLI connects to gRPC, and what operators should validate in production deployments.

Endpoints

GoodMem exposes two network endpoints:

  • REST API (HTTP/JSON) on --rest-port (default: 8080)
  • gRPC API (HTTP/2) on --grpc-port (default: 9090)

TLS enablement is configured per endpoint, but the certificate source (self-signed vs. user-supplied cert/key) is shared across any TLS-enabled endpoint. You cannot use different certificates for REST vs. gRPC today.

Defaults (important)

  • TLS is enabled for both endpoints by default.
  • If TLS is enabled and you do not provide --tls-cert-file/--tls-key-file, GoodMem (non-FIPS mode) generates an in-memory self-signed certificate at startup.
  • The self-signed certificate is ephemeral (regenerated on each restart). Do not rely on it for production.
  • In FIPS mode (--fips-mode-enabled), self-signed TLS is not permitted: you must provide a certificate/key pair or disable TLS.

Choose one of these approaches for production:

  • TLS at GoodMem: provide --tls-cert-file + --tls-key-file and leave TLS enabled on the endpoints you expose.
  • ACME (automatic certificates): let GoodMem obtain and renew certificates from Let's Encrypt or another ACME CA. See ACME (Automatic Certificate Issuance) below.
  • TLS upstream (load balancer / ingress / sidecar): run GoodMem on a private network with plaintext enabled (--tls-disabled or --rest-tls-enabled=false / --grpc-tls-enabled=false) and enforce TLS at the edge.

CLI connection behavior (gRPC)

When the CLI connects to a gRPC address with https:// (or with no scheme), it probes in order:

  • TLS with certificate verification.
  • TLS accepting self-signed (skips verification).
  • Plaintext (h2c) if TLS fails. The first successful mode is used.

If you explicitly set --server=http://..., the CLI uses plaintext only.

Warning: this fallback can hide misconfiguration (e.g., expired certificate, wrong DNS name) by silently connecting with weaker security. In production, validate the server TLS independently (see Validation).

Core TLS Enablement and Overrides

Controls that turn TLS on/off per endpoint or globally. Global disable wins; per-endpoint flags decide whether REST/gRPC use TLS. FIPS is included here because it constrains which TLS materials are allowed.

Server flagEnvironment variableDescription
--tls-disabledGOODMEM_TLS_DISABLEDGlobal off switch; forces REST/gRPC to plaintext.
--rest-tls-enabledGOODMEM_REST_TLS_ENABLEDToggle REST TLS (default: true).
--grpc-tls-enabledGOODMEM_GRPC_TLS_ENABLEDToggle gRPC TLS (default: true).
--fips-mode-enabledGOODMEM_FIPS_MODE_ENABLEDEnforce FIPS-only crypto; disallows self-signed when TLS is on.

Additional validation rules:

  • --tls-disabled forces both endpoints to plaintext.
  • If TLS is disabled for all endpoints, supplying any TLS material/options (certificate/key/trust/ACME flags) is treated as an error (to avoid silent misconfiguration).

Self-Signed Controls

Applies when TLS is enabled and no user-provided cert/key is supplied. Defaults allow in-memory self-signed unless FIPS mode is enabled.

Server flagEnvironment variableDescription
--tls-self-signed-devGOODMEM_TLS_SELF_SIGNED_DEVAllow self-signed when no cert/key provided (default: true unless FIPS).
--tls-self-signed-hostnamesGOODMEM_TLS_SELF_SIGNED_HOSTNAMESSANs for generated self-signed cert (default: localhost,127.0.0.1,::1).

If you override --tls-self-signed-hostnames, include every hostname/IP clients will use to reach the server, otherwise hostname verification will fail. Provide bare hostnames/IPs only (no scheme, no port).

Self-Signed Certificate Details

The server-generated self-signed identity is created in-memory with a FIPS-compliant JCE provider:

  • Key: RSA 2048-bit, generated with the FIPS provider.
  • Certificate: X.509 v3, SHA256withRSA, serial is a 128-bit positive random number.
  • Validity: 365 days, backdated 60 seconds to avoid clock-skew “not yet valid” errors.
  • SANs: controlled by --tls-self-signed-hostnames and may include DNS names and/or IP literals.
  • Extensions: BasicConstraints CA=false (critical), KeyUsage digitalSignature|keyEncipherment (critical), EKU serverAuth (non-critical).
  • Keystore: In-memory BCFKS with alias self, protected by an internal password; no files are written.

User-Supplied Certificates and Trust

Inputs for properly-signed TLS (certificate and key). The same cert/key are used for any TLS-enabled endpoint.

Server flagEnvironment variableDescription
--tls-cert-fileGOODMEM_TLS_CERT_FILEPEM certificate/chain for user-supplied TLS (requires --tls-key-file).
--tls-key-fileGOODMEM_TLS_KEY_FILEPEM private key for user-supplied TLS (requires --tls-cert-file).
--tls-trust-cert-fileGOODMEM_TLS_TRUST_CERT_FILEReserved for future client-auth support; currently unused for inbound TLS.

Notes:

  • The certificate file may contain a chain (leaf + intermediates).
  • The private key must be PEM-encoded PKCS#1 or PKCS#8 and is expected to be unencrypted (no passphrase prompting).
  • Updating TLS files requires a server restart (no live reload).
  • For local-docker installs that started with the default self-signed TLS, see Local TLS with mkcert for an in-place migration procedure.

GoodMem does not currently support mutual TLS / client certificate authentication on REST or gRPC.

ACME (Automatic Certificate Issuance)

GoodMem can obtain and automatically renew certificates from an ACME certificate authority such as Let's Encrypt. When ACME is enabled, GoodMem manages the certificate lifecycle for you and uses the issued certificate for any TLS-enabled endpoint, so you do not supply --tls-cert-file/--tls-key-file.

ACME is mutually exclusive with both manual cert/key files and self-signed TLS. Because self-signed dev mode is enabled by default (outside FIPS mode), you must explicitly turn it off with --tls-self-signed-dev=false when enabling ACME, or the server refuses to start.

How it works

  • Challenge type: Only the HTTP-01 challenge is supported. GoodMem runs a small HTTP challenge server (default port 80) that the CA contacts to validate each domain. This port must be reachable from the public internet (or from your ACME CA) and must not collide with the REST or gRPC ports.
  • Wildcards are not supported. Wildcard certificates require the DNS-01 challenge, which GoodMem does not implement. List each fully-qualified domain explicitly.
  • Renewal: After the first certificate is obtained, a background scheduler periodically checks for upcoming expiry and renews automatically. By default it renews when a certificate is within 30 days of expiry and checks every 24 hours.
  • Storage: Account keys and issued certificates are persisted under $HOME/.goodmem/etc/ssl/acme by default, so they survive restarts.

Required settings

When --tls-acme-enabled is set, GoodMem validates the configuration at startup and refuses to start if any of the following is missing or invalid:

  • TLS must be enabled on at least one endpoint (--rest-tls-enabled or --grpc-tls-enabled).
  • A directory URL, a contact email, and at least one domain must be provided.
  • Terms of Service must be explicitly accepted with --tls-acme-agree-tos (setting it acknowledges the CA's Terms of Service).
  • No conflicting certificate source may be configured: --tls-cert-file/--tls-key-file must not be set, and self-signed dev mode must be disabled with --tls-self-signed-dev=false (it is already disabled when --fips-mode-enabled is set).
  • If overridden, the renewal threshold and check interval must be positive.

Options

Server flagEnvironment variableDescription
--tls-acme-enabledGOODMEM_TLS_ACME_ENABLEDEnable ACME certificate management (default: false).
--tls-acme-directory-urlGOODMEM_TLS_ACME_DIRECTORY_URLACME directory URL; must be an absolute http/https URL (required when ACME enabled).
--tls-acme-emailGOODMEM_TLS_ACME_EMAILContact email for ACME account registration (required when ACME enabled).
--tls-acme-domainsGOODMEM_TLS_ACME_DOMAINSComma-separated list of domains to request; no wildcards (required when ACME enabled).
--tls-acme-agree-tosGOODMEM_TLS_ACME_AGREE_TOSAgree to the ACME CA's Terms of Service; must be true when ACME enabled (default: false).
--tls-acme-storage-dirGOODMEM_TLS_ACME_STORAGE_DIRDirectory for ACME account keys and certificates (default: $HOME/.goodmem/etc/ssl/acme).
--tls-acme-http01-bindGOODMEM_TLS_ACME_HTTP01_BINDBind address for the HTTP-01 challenge server (default: 0.0.0.0).
--tls-acme-http01-portGOODMEM_TLS_ACME_HTTP01_PORTPort for the HTTP-01 challenge server (default: 80); must not collide with the REST or gRPC port.
--tls-acme-renew-before-daysGOODMEM_TLS_ACME_RENEW_BEFORE_DAYSRenew when a certificate is within this many days of expiry (default: 30).
--tls-acme-renew-check-interval-hoursGOODMEM_TLS_ACME_RENEW_CHECK_INTERVAL_HOURSHow often the renewal scheduler checks for expiry, in hours (default: 24).

Example

Issue a certificate from Let's Encrypt for two domains. The HTTP-01 challenge port (80) must be published so the CA can reach it, and the storage directory should be mounted so issued certificates and account keys survive restarts:

docker run \
  -e GOODMEM_TLS_ACME_ENABLED=true \
  -e GOODMEM_TLS_ACME_DIRECTORY_URL=https://acme-v02.api.letsencrypt.org/directory \
  -e GOODMEM_TLS_ACME_EMAIL=ops@example.com \
  -e GOODMEM_TLS_ACME_DOMAINS=api.example.com,grpc.example.com \
  -e GOODMEM_TLS_ACME_AGREE_TOS=true \
  -e GOODMEM_TLS_SELF_SIGNED_DEV=false \
  -e GOODMEM_TLS_ACME_STORAGE_DIR=/etc/goodmem/acme \
  -v /host/path/to/acme:/etc/goodmem/acme \
  -p 80:80 \
  goodmem/server:latest \
  ...

While testing, use the Let's Encrypt staging directory (https://acme-staging-v02.api.letsencrypt.org/directory) to avoid hitting production rate limits, then switch to the production URL once issuance succeeds.

Validation & troubleshooting

REST (curl)

Good endpoints to validate connectivity:

  • GET /health (always 200)
  • GET /readyz (200 when ready, otherwise 503)
  • GET /livez, GET /startupz

Examples:

  • Verified TLS with a custom CA:
    • curl -v --cacert /path/to/ca.crt https://HOST:8080/readyz
  • Self-signed dev TLS (skip verification):
    • curl -vk https://localhost:8080/readyz

gRPC (grpcurl)

Examples:

  • Verified TLS with a custom CA:
    • grpcurl -cacert /path/to/ca.crt HOST:9090 list
  • Self-signed dev TLS:
    • grpcurl -insecure HOST:9090 list
  • Plaintext:
    • grpcurl -plaintext HOST:9090 list