Skip to content

Commit d40c768

Browse files
committed
Set default value for "state_path".
Set default state_path to "acme_{issuer.name}" and introduce a special value "off" to revert to a stateless behavior. As usual, relative paths in configuration are resolved against NGX_PREFIX. The NGX_ACME_STATE_PREFIX environment variable can be set during build to override this behavior. The way we take the option may look a bit unusual, but unfortunately there's no infra for adding module-specific options in auto/configure. Fixes #32.
1 parent 28ae1b3 commit d40c768

File tree

3 files changed

+89
-4
lines changed

3 files changed

+89
-4
lines changed

README.md

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,28 @@ The result will be located at `objs/ngx_http_acme_module.so`.
5757
Currently this method produces a slightly larger library, as we don't instruct
5858
the linker to perform LTO and remove unused code.
5959

60+
#### Build options
61+
62+
As there is no mechanism to add third-party module configuration options to
63+
auto/configure, all the module build-time options are set via environment
64+
variables. Currently there's one option:
65+
66+
- `NGX_ACME_STATE_PREFIX`: sets a default prefix for per-issuer state paths.
67+
If unset, state paths are created relative to the NGINX prefix directory.
68+
The prefix directory should exist and be readable to the worker processes.
69+
70+
Example:
71+
72+
```sh
73+
export NGX_ACME_STATE_PREFIX=/var/cache/nginx
74+
auto/configure \
75+
... \
76+
--with-compat \
77+
--with-http_ssl_module \
78+
--add-dynamic-module=/path/to/nginx-acme
79+
make
80+
```
81+
6082
### Testing
6183

6284
The repository contains an integration test suite based on the [nginx-tests].
@@ -218,9 +240,9 @@ Enables or disables verification of the ACME server certificate.
218240

219241
### state_path
220242

221-
**Syntax:** state_path `path`
243+
**Syntax:** state_path `path` | `off`
222244

223-
**Default:** -
245+
**Default:** acme_`name` or `$NGX_ACME_STATE_PREFIX`/acme_`name`
224246

225247
**Context:** acme_issuer
226248

src/conf.rs

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ pub mod ssl;
3434

3535
const NGX_CONF_DUPLICATE: *mut c_char = c"is duplicate".as_ptr().cast_mut();
3636
const NGX_CONF_INVALID_VALUE: *mut c_char = c"invalid value".as_ptr().cast_mut();
37+
pub const NGX_CONF_UNSET_PTR: *mut core::ffi::c_void = nginx_sys::NGX_CONF_UNSET as _;
3738

3839
/// Main (http block) level configuration.
3940
#[derive(Debug, Default)]
@@ -132,7 +133,7 @@ static mut NGX_HTTP_ACME_ISSUER_COMMANDS: [ngx_command_t; 9] = [
132133
ngx_command_t {
133134
name: ngx_string!("state_path"),
134135
type_: NGX_CONF_TAKE1 as ngx_uint_t,
135-
set: Some(nginx_sys::ngx_conf_set_path_slot),
136+
set: Some(cmd_issuer_set_state_path),
136137
conf: 0,
137138
offset: mem::offset_of!(Issuer, state_path),
138139
post: ptr::null_mut(),
@@ -479,6 +480,28 @@ extern "C" fn cmd_issuer_set_uri(
479480
NGX_CONF_OK
480481
}
481482

483+
/// A wrapper over the `ngx_conf_set_path_slot` that takes the "off" value to disable persistency.
484+
extern "C" fn cmd_issuer_set_state_path(
485+
cf: *mut ngx_conf_t,
486+
cmd: *mut ngx_command_t,
487+
conf: *mut c_void,
488+
) -> *mut c_char {
489+
let cf = unsafe { cf.as_mut().expect("cf ptr is always valid") };
490+
let issuer = unsafe { conf.cast::<Issuer>().as_mut().expect("issuer conf") };
491+
492+
if issuer.state_path != NGX_CONF_UNSET_PTR.cast() {
493+
return NGX_CONF_DUPLICATE;
494+
}
495+
496+
issuer.state_path = ptr::null_mut();
497+
498+
if cf.args().get(1).map(ngx_str_t::as_bytes) == Some(b"off") {
499+
return NGX_CONF_OK;
500+
}
501+
502+
unsafe { nginx_sys::ngx_conf_set_path_slot(cf, cmd, ptr::from_mut(issuer).cast()) }
503+
}
504+
482505
extern "C" fn cmd_issuer_set_accept_tos(
483506
_cf: *mut ngx_conf_t,
484507
_cmd: *mut ngx_command_t,

src/conf/issuer.rs

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
// LICENSE file in the root directory of this source tree.
55

66
use core::error::Error as StdError;
7+
use core::fmt::Write;
78
use core::ptr::{self, NonNull};
89
use core::str;
910
use std::ffi::OsStr;
@@ -100,7 +101,7 @@ impl Issuer {
100101
resolver_timeout: NGX_CONF_UNSET_MSEC,
101102
ssl_trusted_certificate: ngx_str_t::empty(),
102103
ssl_verify: NGX_CONF_UNSET_FLAG,
103-
state_path: ptr::null_mut(),
104+
state_path: super::NGX_CONF_UNSET_PTR.cast(),
104105
accept_tos: None,
105106
ssl,
106107
pkey: None,
@@ -128,6 +129,22 @@ impl Issuer {
128129
return Err(IssuerError::Uri);
129130
}
130131

132+
if self.state_path == super::NGX_CONF_UNSET_PTR.cast() {
133+
let mut init: nginx_sys::ngx_path_init_t = unsafe { core::mem::zeroed() };
134+
init.name = default_state_path(cf, &self.name)?;
135+
136+
self.state_path = ptr::null_mut();
137+
138+
unsafe {
139+
nginx_sys::ngx_conf_merge_path_value(
140+
cf,
141+
&mut self.state_path,
142+
ptr::null_mut(),
143+
&mut init,
144+
)
145+
};
146+
}
147+
131148
if matches!(self.account_key, PrivateKey::Unset) {
132149
self.account_key = PrivateKey::default();
133150
}
@@ -306,6 +323,29 @@ impl Issuer {
306323
}
307324
}
308325

326+
fn default_state_path(cf: &mut ngx_conf_t, name: &ngx_str_t) -> Result<ngx_str_t, AllocError> {
327+
let mut path = ngx::core::NgxString::new_in(cf.pool());
328+
329+
match core::option_env!("NGX_ACME_STATE_PREFIX") {
330+
Some(p) => {
331+
let p = p.trim_end_matches('/');
332+
path.try_reserve_exact(name.len + p.len() + 6)
333+
.map_err(|_| AllocError)?;
334+
// trivial formatters writing into preallocated buffer
335+
let _ = write!(&mut path, "{p}/acme_{name}");
336+
}
337+
None => {
338+
path.try_reserve_exact(name.len + 5)
339+
.map_err(|_| AllocError)?;
340+
// trivial formatters writing into preallocated buffer
341+
let _ = write!(&mut path, "acme_{name}");
342+
}
343+
};
344+
345+
let (data, len, _, _) = path.into_raw_parts();
346+
Ok(ngx_str_t { data, len })
347+
}
348+
309349
#[derive(Debug, thiserror::Error)]
310350
enum CachedCertificateError {
311351
#[error(transparent)]

0 commit comments

Comments
 (0)