70.1.48. pepsi-helper-token-refresh¶
Refresh OAuth access tokens for the smarthost relay stage
- Manual section:
1
70.1.48.1.1. Name¶
pepsi-helper-token-refresh - keep the smarthost SASL OAuth (XOAUTH2 / OAUTHBEARER) access tokens current.
70.1.48.1.2. Synopsis¶
pepsi-helper-token-refresh [GLOBAL-OPTIONS]
pepsi-helper-token-refresh [GLOBAL-OPTIONS] –once
70.1.48.1.3. Description¶
When a smarthost MTA is configured with AUTH = oauth (see
pepsi-stage-relay-to-smarthost(1)), the relay stage authenticates with a
SASL bearer token it reads — fresh on every delivery — from that MTA’s
TOKEN_FILE. OAuth access tokens are short-lived (typically one hour), so
something must replace the file’s contents before each token expires.
pepsi-helper-token-refresh is that something. For every
[pepsi-stage-relay-to-smarthost-mta-<name>] section with AUTH = oauth it
looks for a matching secret section [pepsi-helper-token-refresh-<name>]
holding the OAuth client credentials, requests a fresh access token from the
provider’s token endpoint, and writes it atomically to that MTA’s TOKEN_FILE.
An OAuth MTA with no readable matching secret section is skipped with a warning —
its token is assumed to be supplied by other means.
It is one implementation of an external refresh contract; a site may
substitute its own equivalent (see the Extending the Pipeline chapter of the
Pepsi manual). It is deliberately not part of pepsi.target and must be
enabled explicitly.
70.1.48.1.4. Security model¶
The service runs as the dedicated unprivileged account
pepsi-helper-token-refresh and is the only reader of the OAuth client
secrets:
The per-target secret sections live in a separate file included into the main configuration with the taler
@inline-secret@directive (see pepsi.conf(5)). That file is readable only by this account, so theCLIENT_SECRETandREFRESH_TOKENare never exposed to the relay stage (which runs aspepsi) or to other users.@inline-secret@is silently skipped for a reader that cannot open the file, so the relay stage parses the samepepsi.confwithout error.The access-token files are written into a directory that is SGID the
pepsi-tokengroup (default/var/pepsi/tokens), so each token file inherits that group and is created mode0640. The smarthost relay stage binary is installed SGIDpepsi-token, giving the dispatcher’spepsiworker just enough group membership to read the token — and nothing else.Rotated refresh tokens (RFC 6749 §10.4; e.g. Microsoft) are persisted to a private state directory (default
/var/pepsi/token-refresh, mode0700, never group-readable) and take precedence over the configuredREFRESH_TOKENon the next run.
Started as root, the process first drops to the
pepsi-helper-token-refresh account; it refuses to run as root.
Credentials are sent only over HTTPS.
70.1.48.1.5. Modes¶
With no flag, pepsi-helper-token-refresh runs as a long-lived service.
Each token is refreshed independently and proactively, ahead of its stated
expiry by REFRESH_MARGIN. A target whose refresh fails is retried with
exponential back-off while its previous token file is left in place, so a
transient token-endpoint outage never blanks a working token. It runs until it
receives SIGINT or SIGTERM.
With –once, every configured target is refreshed a single time and the process exits — the form for a manual run, a systemd timer, or cron. The exit status is non-zero if any target failed (after all were attempted).
70.1.48.1.6. Configuration¶
The targets are derived from the smarthost relay configuration; each pairs an MTA section with a secret section. See pepsi.conf(5) for the full option reference. In outline:
# main pepsi.conf (world-readable):
[pepsi-stage-relay-to-smarthost-mta-gmail]
HOST = smtp.gmail.com
PORT = 587
MODE = starttls
AUTH = oauth
USERNAME = me@example.com
TOKEN_FILE = /var/pepsi/tokens/gmail
CATCH_ALL = YES
@inline-secret@ pepsi-helper-token-refresh-gmail /etc/pepsi/secrets/token-refresh.conf
# /etc/pepsi/secrets/token-refresh.conf (readable only by the refresher):
[pepsi-helper-token-refresh-gmail]
TOKEN_ENDPOINT = https://oauth2.googleapis.com/token
GRANT = refresh_token
CLIENT_ID = ...
CLIENT_SECRET = ...
REFRESH_TOKEN = ...
# REFRESH_MARGIN = 5 m
For an app-only Microsoft 365 target use GRANT = client_credentials with
SCOPE instead of a REFRESH_TOKEN.
70.1.48.1.7. Options¶
- –once
Refresh every configured target once and exit, instead of running as a service that refreshes ahead of expiry.
70.1.48.1.8. Global Options¶
These options may appear before or after the other flags.
- -c FILE, –config FILE
Read the configuration from FILE instead of searching the default locations.
- -L LOGLEVEL, –log LOGLEVEL
Set the logging verbosity. LOGLEVEL is one of
error,warn,info,debugortrace(default:info).- -v, –verbose
Show log messages from all sources, including third-party libraries.
- -h, –help
Print a usage summary and exit.
- -V, –version
Print the version and exit.
70.1.48.1.9. Exit Status¶
- 0
Successful completion (a
--oncerun refreshed every target, or the service shut down cleanly on a signal).- 1
An error occurred: a malformed configuration, a secret section missing a required option, at least one target failing in
--oncemode, or an attempt to run asrootwithout thepepsi-helper-token-refreshaccount.
70.1.48.1.10. Examples¶
Run as a service (the typical deployment):
systemctl enable --now pepsi-helper-token-refresh.service
Refresh every token once, by hand:
pepsi-helper-token-refresh -c /etc/pepsi/pepsi.conf --once
70.1.48.1.11. See Also¶
pepsi-stage-relay-to-smarthost(1), pepsi-setup(1), pepsi.conf(5)
70.1.48.1.12. Bugs¶
Report bugs to the Pepsi issue tracker.