Line data Source code
1 : //! Local Proxy is a feature of our BaaS Neon Authorize project.
2 : //!
3 : //! Local Proxy validates JWTs and manages the pg_session_jwt extension.
4 : //! It also maintains a connection pool to postgres.
5 :
6 : use anyhow::{Context, Result};
7 : use camino::Utf8Path;
8 : use compute_api::spec::LocalProxySpec;
9 : use nix::sys::signal::Signal;
10 : use utils::pid_file::{self, PidFileRead};
11 :
12 0 : pub fn configure(local_proxy: &LocalProxySpec) -> Result<()> {
13 0 : write_local_proxy_conf("/etc/local_proxy/config.json".as_ref(), local_proxy)?;
14 0 : notify_local_proxy("/etc/local_proxy/pid".as_ref())?;
15 :
16 0 : Ok(())
17 0 : }
18 :
19 : /// Create or completely rewrite configuration file specified by `path`
20 0 : fn write_local_proxy_conf(path: &Utf8Path, local_proxy: &LocalProxySpec) -> Result<()> {
21 0 : let config =
22 0 : serde_json::to_string_pretty(local_proxy).context("serializing LocalProxySpec to json")?;
23 0 : std::fs::write(path, config).with_context(|| format!("writing {path}"))?;
24 :
25 0 : Ok(())
26 0 : }
27 :
28 : /// Notify local proxy about a new config file.
29 0 : fn notify_local_proxy(path: &Utf8Path) -> Result<()> {
30 0 : match pid_file::read(path)? {
31 : // if the file doesn't exist, or isn't locked, local_proxy isn't running
32 : // and will naturally pick up our config later
33 0 : PidFileRead::NotExist | PidFileRead::NotHeldByAnyProcess(_) => {}
34 0 : PidFileRead::LockedByOtherProcess(pid) => {
35 0 : // From the pid_file docs:
36 0 : //
37 0 : // > 1. The other process might exit at any time, turning the given PID stale.
38 0 : // > 2. There is a small window in which `claim_for_current_process` has already
39 0 : // > locked the file but not yet updates its contents. [`read`] will return
40 0 : // > this variant here, but with the old file contents, i.e., a stale PID.
41 0 : // >
42 0 : // > The kernel is free to recycle PID once it has been `wait(2)`ed upon by
43 0 : // > its creator. Thus, acting upon a stale PID, e.g., by issuing a `kill`
44 0 : // > system call on it, bears the risk of killing an unrelated process.
45 0 : // > This is an inherent limitation of using pidfiles.
46 0 : // > The only race-free solution is to have a supervisor-process with a lifetime
47 0 : // > that exceeds that of all of its child-processes (e.g., `runit`, `supervisord`).
48 0 : //
49 0 : // This is an ok risk as we only send a SIGHUP which likely won't actually
50 0 : // kill the process, only reload config.
51 0 : nix::sys::signal::kill(pid, Signal::SIGHUP).context("sending signal to local_proxy")?;
52 : }
53 : }
54 :
55 0 : Ok(())
56 0 : }
|