Skip to content
This repository was archived by the owner on Jun 9, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 18 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1755,6 +1755,7 @@ checks by mod_md in v1.1.x which are now eliminated. If you have many domains, t
* [MDProfile](#mdprofile)
* [MDProfileMandatory](#mdprofilemandatory)
* [MDHttpProxy](#mdhttpproxy)
* [MDHttpProxyCACertificateFile](#mdhttpproxycacertificatefile)
* [MDRenewViaARI](#mdrenewviaari)
* [MDRenewMode](#mdrenewmode--renew-mode)
* [MDRenewWindow](#mdrenewwindow--when-to-renew)
Expand Down Expand Up @@ -2101,10 +2102,24 @@ best matching your client's capabilities.
***The URL of the http-proxy to use***<BR/>
`MDHttpProxy url`

Use a proxy (on `url`) to connect to the MDCertificateAuthority url. Use if your webserver has no outbound connectivity in combination with your forward proxy.
Use a proxy (on `url`) to connect to the `MDCertificateAuthority` url. Use if your webserver has no outbound connectivity in combination with your forward proxy.

Since version 2.6.11, a proxy can be configured separately for each `MDomain`.

## MDHttpProxyCACertificateFile

***Sets the root (CA) certificates to use for TLS connections to the http-proxy***<BR/>
`MDHttpProxyCACertificateFile path-to-pem-file`<BR/>
Default: none

This is used for connections to the HTTPS proxy (`MDHttpProxy`). It is needed if the certificate of the HTTPS proxy cannot be verified using the general CA root store.
This is sometimes the case in test setups or enterprise environments.
The certificate of the ACME server is verified with the root certificates set by `MDCACertificateFile`, so you might need to use both settings.

Use "none" as path to disable explicitly.

This can be configured separately for each `MDomain`.

## MDRenewMode / Renew Mode

***Controls when `mod_md` will try to obtain/renew certificates***<BR/>
Expand Down Expand Up @@ -2340,6 +2355,8 @@ store already and there is no special need.

Use "none" as path to disable explicitly.

Since version 2.6.11, this can be configured separately for each `MDomain`.

## MDExternalAccountBinding
***Sets the external account binding (EAB) information to use***<BR/>
`MDExternalAccountBinding key-id hmac-value | json-file`
Expand Down
3 changes: 3 additions & 0 deletions src/md.h
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,8 @@ struct md_t {
const char *proxy_url; /* Proxy URL, override global command */
const char *ca_certs; /* root certificates to use for connections,
override global command */
const char *proxy_ca_certs; /* root certificates to use for proxy connections,
override global command */
const struct md_srv_conf_t *sc; /* server config where it was defined or NULL */
const char *defn_name; /* config file this MD was defined */
unsigned defn_line_number; /* line number of definition */
Expand Down Expand Up @@ -188,6 +190,7 @@ struct md_t {
#define MD_KEY_PROFILE "profile"
#define MD_KEY_PROFILE_MANDATORY "profile-mandatory"
#define MD_KEY_PROTO "proto"
#define MD_KEY_PROXY_CA_CERTS "proxy-ca-certs"
#define MD_KEY_PROXY_URL "proxy-url"
#define MD_KEY_READY "ready"
#define MD_KEY_REGISTRATION "registration"
Expand Down
5 changes: 4 additions & 1 deletion src/md_acme.c
Original file line number Diff line number Diff line change
Expand Up @@ -622,7 +622,8 @@ apr_status_t md_acme_POST_new_account(md_acme_t *acme,
/* ACME setup */

apr_status_t md_acme_create(md_acme_t **pacme, apr_pool_t *p, const char *url,
const char *proxy_url, const char *ca_file)
const char *proxy_url, const char *ca_file,
const char *proxy_ca_file)
{
md_acme_t *acme;
const char *err = NULL;
Expand All @@ -648,6 +649,7 @@ apr_status_t md_acme_create(md_acme_t **pacme, apr_pool_t *p, const char *url,
acme->proxy_url = apr_pstrdup(p, proxy_url);
acme->max_retries = 9;
acme->ca_file = ca_file;
acme->proxy_ca_file = proxy_ca_file;

if (APR_SUCCESS != (rv = apr_uri_parse(p, url, &uri_parsed))) {
md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, p, "parsing ACME uri: %s", url);
Expand Down Expand Up @@ -800,6 +802,7 @@ apr_status_t md_acme_setup(md_acme_t *acme, md_result_t *result)
md_http_set_connect_timeout_default(acme->http, apr_time_from_sec(30));
md_http_set_stalling_default(acme->http, 10, apr_time_from_sec(30));
md_http_set_ca_file(acme->http, acme->ca_file);
md_http_set_proxy_ca_file(acme->http, acme->proxy_ca_file);

md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, 0, acme->p, "get directory from %s", acme->url);

Expand Down
5 changes: 4 additions & 1 deletion src/md_acme.h
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ struct md_acme_t {
const char *user_agent;
const char *proxy_url;
const char *ca_file;
const char *proxy_ca_file;

const char *acct_id; /* local storage id account was loaded from or NULL */
struct md_acme_acct_t *acct; /* account at ACME server to use for requests */
Expand Down Expand Up @@ -152,9 +153,11 @@ apr_status_t md_acme_init(apr_pool_t *pool, const char *base_version, int init_s
* @param url url of the server, optional if known at path
* @param proxy_url optional url of a HTTP(S) proxy to use
* @param ca_file optional CA trust anchor file to use
* @param proxy_ca_file optional CA trust anchor file to use for the HTTP proxy
*/
apr_status_t md_acme_create(md_acme_t **pacme, apr_pool_t *p, const char *url,
const char *proxy_url, const char *ca_file);
const char *proxy_url, const char *ca_file,
const char *proxy_ca_file);

/**
* Contact the ACME server and retrieve its directory information.
Expand Down
9 changes: 6 additions & 3 deletions src/md_acme_drive.c
Original file line number Diff line number Diff line change
Expand Up @@ -772,7 +772,8 @@ static apr_status_t acme_renew(md_proto_driver_t *d, md_result_t *result)
d->md->name, ca_effective);
if (APR_SUCCESS != (rv = md_acme_create(&ad->acme, d->p, ca_effective,
ad->md->proxy_url ? ad->md->proxy_url : d->proxy_url,
ad->md->ca_certs ? ad->md->ca_certs : d->ca_certs))) {
ad->md->ca_certs ? ad->md->ca_certs : d->ca_certs,
ad->md->proxy_ca_certs ? ad->md->proxy_ca_certs : d->proxy_ca_certs))) {
md_result_printf(result, rv, "setup ACME communications");
md_result_log(result, MD_LOG_ERR);
goto out;
Expand Down Expand Up @@ -1035,7 +1036,8 @@ static apr_status_t acme_preload(md_proto_driver_t *d, md_store_group_t load_gro

if (APR_SUCCESS != (rv = md_acme_create(&acme, d->p, md->ca_effective,
d->md->proxy_url ? d->md->proxy_url : d->proxy_url,
d->md->ca_certs ? d->md->ca_certs : d->ca_certs))) {
d->md->ca_certs ? d->md->ca_certs : d->ca_certs,
d->md->proxy_ca_certs ? d->md->proxy_ca_certs : d->proxy_ca_certs))) {
md_result_set(result, rv, "error setting up acme");
goto leave;
}
Expand Down Expand Up @@ -1145,7 +1147,8 @@ static apr_status_t acme_get_ari(md_proto_driver_t *d,

if (APR_SUCCESS != (rv = md_acme_create(&ad->acme, d->p, ca_effective,
d->md->proxy_url ? d->md->proxy_url : d->proxy_url,
d->md->ca_certs ? d->md->ca_certs : d->ca_certs))) {
d->md->ca_certs ? d->md->ca_certs : d->ca_certs,
d->md->proxy_ca_certs ? d->md->proxy_ca_certs : d->proxy_ca_certs))) {
md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, d->p,
"create ACME communications");
goto out;
Expand Down
4 changes: 2 additions & 2 deletions src/md_cmd_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ static apr_status_t cmd_process(md_cmd_ctx *ctx, const md_cmd_t *cmd)
}
if (APR_SUCCESS != (rv = md_reg_create(&ctx->reg, ctx->p, ctx->store,
md_cmd_ctx_get_option(ctx, MD_CMD_OPT_PROXY_URL),
ctx->ca_file, apr_time_from_sec(15), 10,
ctx->ca_file, NULL, apr_time_from_sec(15), 10,
0, apr_time_from_sec(5)))) {
fprintf(stderr, "error %d creating registry from store: %s\n", rv, ctx->base_dir);
return APR_EINVAL;
Expand All @@ -199,7 +199,7 @@ static apr_status_t cmd_process(md_cmd_ctx *ctx, const md_cmd_t *cmd)
}
rv = md_acme_create(&ctx->acme, ctx->p, ctx->ca_url,
md_cmd_ctx_get_option(ctx, MD_CMD_OPT_PROXY_URL),
ctx->ca_file);
ctx->ca_file, NULL);
if (APR_SUCCESS != rv) {
fprintf(stderr, "error creating acme instance %s (%s)\n",
ctx->ca_url, ctx->base_dir);
Expand Down
3 changes: 3 additions & 0 deletions src/md_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,7 @@ md_t *md_clone(apr_pool_t *p, const md_t *src)
if (src->dns01_cmd) md->dns01_cmd = apr_pstrdup(p, src->dns01_cmd);
if (src->proxy_url) md->proxy_url = apr_pstrdup(p, src->proxy_url);
if (src->ca_certs) md->ca_certs = apr_pstrdup(p, src->ca_certs);
if (src->proxy_ca_certs) md->proxy_ca_certs = apr_pstrdup(p, src->proxy_ca_certs);
if (src->cert_files) md->cert_files = md_array_str_clone(p, src->cert_files);
if (src->pkey_files) md->pkey_files = md_array_str_clone(p, src->pkey_files);
}
Expand Down Expand Up @@ -319,6 +320,7 @@ md_json_t *md_to_json(const md_t *md, apr_pool_t *p)
if (md->dns01_cmd) md_json_sets(md->dns01_cmd, json, MD_KEY_CMD_DNS01, NULL);
if (md->proxy_url) md_json_sets(md->proxy_url, json, MD_KEY_PROXY_URL, NULL);
if (md->ca_certs) md_json_sets(md->ca_certs, json, MD_KEY_CA_CERTS, NULL);
if (md->proxy_ca_certs) md_json_sets(md->proxy_ca_certs, json, MD_KEY_PROXY_CA_CERTS, NULL);
if (md->ca_eab_kid && strcmp("none", md->ca_eab_kid)) {
md_json_sets(md->ca_eab_kid, json, MD_KEY_EAB, MD_KEY_KID, NULL);
if (md->ca_eab_hmac) md_json_sets(md->ca_eab_hmac, json, MD_KEY_EAB, MD_KEY_HMAC, NULL);
Expand Down Expand Up @@ -390,6 +392,7 @@ md_t *md_from_json(md_json_t *json, apr_pool_t *p)
md->dns01_cmd = md_json_dups(p, json, MD_KEY_CMD_DNS01, NULL);
md->proxy_url = md_json_dups(p, json, MD_KEY_PROXY_URL, NULL);
md->ca_certs = md_json_dups(p, json, MD_KEY_CA_CERTS, NULL);
md->proxy_ca_certs = md_json_dups(p, json, MD_KEY_PROXY_CA_CERTS, NULL);
if (md_json_has_key(json, MD_KEY_EAB, NULL)) {
md->ca_eab_kid = md_json_dups(p, json, MD_KEY_EAB, MD_KEY_KID, NULL);
md->ca_eab_hmac = md_json_dups(p, json, MD_KEY_EAB, MD_KEY_HMAC, NULL);
Expand Down
14 changes: 14 additions & 0 deletions src/md_curl.c
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,7 @@ static apr_status_t internals_setup(md_http_request_t *req)
CURL *curl;
apr_status_t rv = APR_SUCCESS;
long ssl_options = 0;
long proxy_ssl_options = 0;

curl = md_http_get_impl_data(req->http);
if (!curl) {
Expand Down Expand Up @@ -313,6 +314,16 @@ static apr_status_t internals_setup(md_http_request_t *req)
ssl_options |= CURLSSLOPT_NO_REVOKE;
#endif
}
if (req->proxy_ca_file) {
curl_easy_setopt(curl, CURLOPT_PROXY_CAINFO, req->proxy_ca_file);
/* for a custom CA, allow certificates checking to ignore the
* Schannel error CRYPT_E_NO_REVOCATION_CHECK (could be a missing OCSP
* responder URL in the certs???). See issue #361 */
#ifdef CURLSSLOPT_NO_REVOKE
proxy_ssl_options |= CURLSSLOPT_NO_REVOKE;
#endif
}

if (req->unix_socket_path) {
curl_easy_setopt(curl, CURLOPT_UNIX_SOCKET_PATH, req->unix_socket_path);
}
Expand Down Expand Up @@ -354,6 +365,9 @@ static apr_status_t internals_setup(md_http_request_t *req)
if (ssl_options)
curl_easy_setopt(curl, CURLOPT_SSL_OPTIONS, ssl_options);

if (proxy_ssl_options)
curl_easy_setopt(curl, CURLOPT_PROXY_SSL_OPTIONS, proxy_ssl_options);

leave:
req->internals = (APR_SUCCESS == rv)? internals : NULL;
return rv;
Expand Down
10 changes: 10 additions & 0 deletions src/md_http.c
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ struct md_http_t {
const char *unix_socket_path;
md_http_timeouts_t timeout;
const char *ca_file;
const char *proxy_ca_file;
};

static md_http_impl_t *cur_impl;
Expand Down Expand Up @@ -107,6 +108,9 @@ apr_status_t md_http_clone(md_http_t **phttp,
if (source_http->ca_file) {
(*phttp)->ca_file = apr_pstrdup(p, source_http->ca_file);
}
if (source_http->proxy_ca_file) {
(*phttp)->proxy_ca_file = apr_pstrdup(p, source_http->proxy_ca_file);
}
}
return rv;
}
Expand Down Expand Up @@ -163,6 +167,11 @@ void md_http_set_ca_file(md_http_t *http, const char *ca_file)
http->ca_file = ca_file;
}

void md_http_set_proxy_ca_file(md_http_t *http, const char *ca_file)
{
http->proxy_ca_file = ca_file;
}

void md_http_set_unix_socket_path(md_http_t *http, const char *path)
{
http->unix_socket_path = path;
Expand Down Expand Up @@ -235,6 +244,7 @@ static apr_status_t req_create(md_http_request_t **preq, md_http_t *http,
req->proxy_url = http->proxy_url;
req->timeout = http->timeout;
req->ca_file = http->ca_file;
req->proxy_ca_file = http->proxy_ca_file;
req->unix_socket_path = http->unix_socket_path;
*preq = req;
return rv;
Expand Down
10 changes: 9 additions & 1 deletion src/md_http.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ struct md_http_request_t {
const char *user_agent;
const char *proxy_url;
const char *ca_file;
const char *proxy_ca_file;
const char *unix_socket_path;
apr_table_t *headers;
struct apr_bucket_brigade *body;
Expand Down Expand Up @@ -119,12 +120,19 @@ void md_http_set_stalling_default(md_http_t *http, long bytes_per_sec, apr_time_
void md_http_set_stalling(md_http_request_t *req, long bytes_per_sec, apr_time_t timeout);

/**
* Set a CA file (in PERM format) to use for root certificates when
* Set a CA file (in PEM format) to use for root certificates when
* verifying SSL connections. If not set (or set to NULL), the systems
* certificate store will be used.
*/
void md_http_set_ca_file(md_http_t *http, const char *ca_file);

/**
* Set a CA file (in PEM format) to use for root certificates when
* verifying SSL connections to the HTTP proxy. If not set (or set to NULL),
* the systems certificate store will be used.
*/
void md_http_set_proxy_ca_file(md_http_t *http, const char *ca_file);

/**
* Set the path of a unix domain socket for use instead of TCP
* in a connection. Disable by providing NULL as path.
Expand Down
9 changes: 7 additions & 2 deletions src/md_reg.c
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ struct md_reg_t {
int can_https;
const char *proxy_url;
const char *ca_certs;
const char *proxy_ca_certs;
int domains_frozen;
md_timeslice_t *renew_window;
md_timeslice_t *warn_window;
Expand Down Expand Up @@ -97,8 +98,9 @@ static apr_status_t load_props(md_reg_t *reg, apr_pool_t *p)

apr_status_t md_reg_create(md_reg_t **preg, apr_pool_t *p, struct md_store_t *store,
const char *proxy_url, const char *ca_certs,
apr_time_t min_delay, int retry_failover,
int use_store_locks, apr_time_t lock_wait_timeout)
const char *proxy_ca_certs, apr_time_t min_delay,
int retry_failover, int use_store_locks,
apr_time_t lock_wait_timeout)
{
md_reg_t *reg;
apr_status_t rv;
Expand All @@ -113,6 +115,8 @@ apr_status_t md_reg_create(md_reg_t **preg, apr_pool_t *p, struct md_store_t *st
reg->proxy_url = apr_pstrdup(p, proxy_url);
reg->ca_certs = (ca_certs && apr_cstr_casecmp("none", ca_certs))?
apr_pstrdup(p, ca_certs) : NULL;
reg->proxy_ca_certs = (proxy_ca_certs && apr_cstr_casecmp("none", proxy_ca_certs))?
apr_pstrdup(p, proxy_ca_certs) : NULL;
reg->min_delay = min_delay;
reg->retry_failover = retry_failover;
reg->use_store_locks = use_store_locks;
Expand Down Expand Up @@ -1110,6 +1114,7 @@ static apr_status_t run_init(void *baton, apr_pool_t *p, ...)
driver->store = md_reg_store_get(reg);
driver->proxy_url = reg->proxy_url;
driver->ca_certs = reg->ca_certs;
driver->proxy_ca_certs = reg->proxy_ca_certs;
driver->md = md;
driver->can_http = reg->can_http;
driver->can_https = reg->can_https;
Expand Down
7 changes: 5 additions & 2 deletions src/md_reg.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,15 @@ typedef struct md_reg_t md_reg_t;
* @param store the store to base on
* @param proxy_url optional URL of a proxy to use for requests
* @param ca_certs optional CA trust anchor file to use
* @param proxy_ca_certs optional CA trust anchor file to use for the HTTP proxy
* @param min_delay minimum delay between renewal attempts for a domain
* @param retry_failover number of failed renewals attempt to fail over to alternate ACME ca
*/
apr_status_t md_reg_create(md_reg_t **preg, apr_pool_t *pm, md_store_t *store,
const char *proxy_url, const char *ca_certs,
apr_time_t min_delay, int retry_failover,
int use_store_locks, apr_time_t lock_wait_timeout);
const char *proxy_ca_certs, apr_time_t min_delay,
int retry_failover, int use_store_locks,
apr_time_t lock_wait_timeout);

md_store_t *md_reg_store_get(md_reg_t *reg);

Expand Down Expand Up @@ -225,6 +227,7 @@ struct md_proto_driver_t {
md_store_t *store;
const char *proxy_url;
const char *ca_certs;
const char *proxy_ca_certs;
const md_t *md;

int can_http;
Expand Down
4 changes: 3 additions & 1 deletion src/mod_md.c
Original file line number Diff line number Diff line change
Expand Up @@ -857,6 +857,7 @@ static apr_status_t md_post_config_before_ssl(apr_pool_t *p, apr_pool_t *plog,
md_store_t *store;
const char *proxy_url;
const char *ca_certs;
const char *proxy_ca_certs;

apr_pool_userdata_get(&data, mod_md_init_key, s->process->pool);
if (data == NULL) {
Expand Down Expand Up @@ -897,8 +898,9 @@ static apr_status_t md_post_config_before_ssl(apr_pool_t *p, apr_pool_t *plog,

proxy_url = apr_table_get(mc->env, MD_KEY_PROXY_URL);
ca_certs = apr_table_get(mc->env, MD_KEY_CA_CERTS);
proxy_ca_certs = apr_table_get(mc->env, MD_KEY_PROXY_CA_CERTS);

rv = md_reg_create(&mc->reg, p, store, proxy_url, ca_certs,
rv = md_reg_create(&mc->reg, p, store, proxy_url, ca_certs, proxy_ca_certs,
mc->min_delay, mc->retry_failover,
mc->use_store_locks, mc->lock_wait_timeout);
if (APR_SUCCESS != rv) {
Expand Down
Loading
Loading