Skip to content

Commit

Permalink
Merge /httpd/httpd/trunk:r1910996,1911721
Browse files Browse the repository at this point in the history
 * mod_md:
   - New directive `MDMatchNames all|servernames` to allow more control over how
     MDomains are matched to VirtualHosts.
   - New directive `MDChallengeDns01Version`. Setting this to `2` will provide
     the command also with the challenge value on `teardown` invocation. In version
     1, the default, only the `setup` invocation gets this parameter.
     Refs #312. Thanks to @domrim for the idea.
   - For Managed Domain in "manual" mode, the checks if all used ServerName and
     ServerAlias are part of the MDomain now reports a warning instead of an error
     (AH10040) when not all names are present.
   - MDChallengeDns01 can now be configured for individual domains.
     Using PR from Jérôme Billiras (@bilhackmac) and adding test case and fixing proper working
   - Fixed a bug found by Jérôme Billiras (@bilhackmac) that caused the challenge
     teardown not being invoked as it should.

 * mod_md: fixed passing of the server environment variables to programs
   started via MDMessageCmd and MDChallengeDns01 on *nix system.
   See <icing/mod_md#319>.



git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1911722 13f79535-47bb-0310-9956-ffa450edef68
  • Loading branch information
icing committed Aug 16, 2023
1 parent 225b936 commit 44fd2ff
Show file tree
Hide file tree
Showing 11 changed files with 222 additions and 61 deletions.
14 changes: 14 additions & 0 deletions changes-entries/md_v2.4.23.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
* mod_md:
- New directive `MDMatchNames all|servernames` to allow more control over how
MDomains are matched to VirtualHosts.
- New directive `MDChallengeDns01Version`. Setting this to `2` will provide
the command also with the challenge value on `teardown` invocation. In version
1, the default, only the `setup` invocation gets this parameter.
Refs #312. Thanks to @domrim for the idea.
- For Managed Domain in "manual" mode, the checks if all used ServerName and
ServerAlias are part of the MDomain now reports a warning instead of an error
(AH10040) when not all names are present.
- MDChallengeDns01 can now be configured for individual domains.
Using PR from Jérôme Billiras (@bilhackmac) and adding test case and fixing proper working
- Fixed a bug found by Jérôme Billiras (@bilhackmac) that caused the challenge
teardown not being invoked as it should.
4 changes: 4 additions & 0 deletions changes-entries/md_v2.4.24.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
* mod_md: fixed passing of the server environment variables to programs
started via MDMessageCmd and MDChallengeDns01 on *nix system.
See <https://github.com/icing/mod_md/issues/319>.
[Stefan Eissing]
52 changes: 51 additions & 1 deletion docs/manual/mod/mod_md.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1033,7 +1033,9 @@ MDRequireHttps permanent
<p>
Define a program to be called when the `dns-01` challenge needs to be setup/torn down.
The program is given the argument `setup` or `teardown` followed by the domain name.
For `setup` the challenge content is additionally given.
For `setup` the challenge content is additionally given. When
<directive module="mod_md">MDChallengeDns01Version</directive> is set to 2,
the `teardown` also gets the challenge content as argument.
</p><p>
You do not need to specify this, as long as a 'http:' or 'https:' challenge
method is possible. However, Let's Encrypt makes 'dns-01' the only
Expand Down Expand Up @@ -1462,4 +1464,52 @@ MDMessageCmd /etc/apache/md-message
</usage>
</directivesynopsis>

<directivesynopsis>
<name>MDChallengeDns01Version</name>
<description></description>
<syntax>MDChallengeDns01Version 1|2</syntax>
<default>MDChallengeDns01Version 1</default>
<contextlist>
<context>server config</context>
</contextlist>
<compatibility>Available in version 2.4.58 and later</compatibility>
<usage>
<p>
Set the way MDChallengeDns01 command is invoked, e.g the number and
types of arguments. See <directive module="mod_md">MDChallengeDns01</directive>
for the differences.
This setting is global and cannot be varied per domain.
</p>
</usage>
</directivesynopsis>

<directivesynopsis>
<name>MDMatchNames</name>
<description></description>
<syntax>MDMatchNames all|servernames</syntax>
<default>MDMatchNames all</default>
<contextlist>
<context>server config</context>
</contextlist>
<compatibility>Available in version 2.4.58 and later</compatibility>
<usage>
<p>
The mode `all` is the behaviour as in all previous versions. Both ServerName
and ServerAlias are inspected to find the MDomain matching a VirtualHost.
This automatically detects coverage, even when you only have added
one of the names to an MDomain.
</p><p>
However, this auto-magic has drawbacks in more complex setups. If you set
this directive to `servernames`, only the ServerName of a virtual host is
used for matching. ServerAliases are disregarded then, for matching.
Aliases will still be added to the certificate obtained, unless you also
run `MDMembers manual`.
</p><p>
Another advantage of `servernames` is that it gives you more flexibility
with sub-domains and wildcards. You can define one MDomain with a wildcard
and have other MDomains for specific sub-domain names.
</p>
</usage>
</directivesynopsis>

</modulesynopsis>
18 changes: 9 additions & 9 deletions modules/md/md.h
Original file line number Diff line number Diff line change
Expand Up @@ -78,12 +78,7 @@ struct md_t {
struct apr_array_header_t *domains; /* all DNS names this MD includes */
struct apr_array_header_t *contacts; /* list of contact uris, e.g. mailto:xxx */

int transitive; /* != 0 iff VirtualHost names/aliases are auto-added */
md_require_t require_https; /* Iff https: is required for this MD */

int renew_mode; /* mode of obtaining credentials */
struct md_pkeys_spec_t *pks; /* specification for generating private keys */
int must_staple; /* certificates should set the OCSP Must Staple extension */
md_timeslice_t *renew_window; /* time before expiration that starts renewal */
md_timeslice_t *warn_window; /* time before expiration that warnings are sent out */

Expand All @@ -98,19 +93,23 @@ struct md_t {
const char *ca_eab_kid; /* optional KEYID for external account binding */
const char *ca_eab_hmac; /* optional HMAC for external account binding */

md_state_t state; /* state of this MD */
const char *state_descr; /* description of state of NULL */

struct apr_array_header_t *acme_tls_1_domains; /* domains supporting "acme-tls/1" protocol */
int stapling; /* if OCSP stapling is enabled */
const char *dns01_cmd; /* DNS challenge command, override global command */

int watched; /* if certificate is supervised (renew or expiration warning) */
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 */

const char *configured_name; /* name this MD was configured with, if different */

int renew_mode; /* mode of obtaining credentials */
md_require_t require_https; /* Iff https: is required for this MD */
md_state_t state; /* state of this MD */
int transitive; /* != 0 iff VirtualHost names/aliases are auto-added */
int must_staple; /* certificates should set the OCSP Must Staple extension */
int stapling; /* if OCSP stapling is enabled */
int watched; /* if certificate is supervised (renew or expiration warning) */
};

#define MD_KEY_ACCOUNT "account"
Expand All @@ -128,6 +127,7 @@ struct md_t {
#define MD_KEY_CHALLENGE "challenge"
#define MD_KEY_CHALLENGES "challenges"
#define MD_KEY_CMD_DNS01 "cmd-dns-01"
#define MD_KEY_DNS01_VERSION "cmd-dns-01-version"
#define MD_KEY_COMPLETE "complete"
#define MD_KEY_CONTACT "contact"
#define MD_KEY_CONTACTS "contacts"
Expand Down
47 changes: 33 additions & 14 deletions modules/md/md_acme_authz.c
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,8 @@ static apr_status_t cha_http_01_setup(md_acme_authz_cha_t *cha, md_acme_authz_t
md_acme_t *acme, md_store_t *store,
md_pkeys_spec_t *key_specs,
apr_array_header_t *acme_tls_1_domains, const md_t *md,
apr_table_t *env, md_result_t *result, apr_pool_t *p)
apr_table_t *env, md_result_t *result,
const char **psetup_token, apr_pool_t *p)
{
const char *data;
apr_status_t rv;
Expand Down Expand Up @@ -289,6 +290,8 @@ static apr_status_t cha_http_01_setup(md_acme_authz_cha_t *cha, md_acme_authz_t
rv = md_acme_POST(acme, cha->uri, on_init_authz_resp, authz_http_set, NULL, NULL, &ctx);
}
out:
*psetup_token = (APR_SUCCESS == rv)?
apr_psprintf(p, "%s:%s", MD_AUTHZ_TYPE_HTTP01, authz->domain) : NULL;
return rv;
}

Expand All @@ -302,7 +305,8 @@ static apr_status_t cha_tls_alpn_01_setup(md_acme_authz_cha_t *cha, md_acme_auth
md_acme_t *acme, md_store_t *store,
md_pkeys_spec_t *key_specs,
apr_array_header_t *acme_tls_1_domains, const md_t *md,
apr_table_t *env, md_result_t *result, apr_pool_t *p)
apr_table_t *env, md_result_t *result,
const char **psetup_token, apr_pool_t *p)
{
const char *acme_id, *token;
apr_status_t rv;
Expand Down Expand Up @@ -407,14 +411,17 @@ static apr_status_t cha_tls_alpn_01_setup(md_acme_authz_cha_t *cha, md_acme_auth
rv = md_acme_POST(acme, cha->uri, on_init_authz_resp, authz_http_set, NULL, NULL, &ctx);
}
out:
*psetup_token = (APR_SUCCESS == rv)?
apr_psprintf(p, "%s:%s", MD_AUTHZ_TYPE_TLSALPN01, authz->domain) : NULL;
return rv;
}

static apr_status_t cha_dns_01_setup(md_acme_authz_cha_t *cha, md_acme_authz_t *authz,
md_acme_t *acme, md_store_t *store,
md_pkeys_spec_t *key_specs,
apr_array_header_t *acme_tls_1_domains, const md_t *md,
apr_table_t *env, md_result_t *result, apr_pool_t *p)
apr_table_t *env, md_result_t *result,
const char **psetup_token, apr_pool_t *p)
{
const char *token;
const char * const *argv;
Expand Down Expand Up @@ -456,7 +463,7 @@ static apr_status_t cha_dns_01_setup(md_acme_authz_cha_t *cha, md_acme_authz_t *
"%s: dns-01 setup command: %s", authz->domain, cmdline);

apr_tokenize_to_argv(cmdline, (char***)&argv, p);
if (APR_SUCCESS != (rv = md_util_exec(p, argv[0], argv, NULL, &exit_code))) {
if (APR_SUCCESS != (rv = md_util_exec(p, argv[0], argv, &exit_code))) {
md_log_perror(MD_LOG_MARK, MD_LOG_WARNING, rv, p,
"%s: dns-01 setup command failed to execute for %s", md->name, authz->domain);
goto out;
Expand Down Expand Up @@ -486,14 +493,17 @@ static apr_status_t cha_dns_01_setup(md_acme_authz_cha_t *cha, md_acme_authz_t *
rv = md_acme_POST(acme, cha->uri, on_init_authz_resp, authz_http_set, NULL, NULL, &ctx);

out:
*psetup_token = (APR_SUCCESS == rv)?
apr_psprintf(p, "%s:%s %s", MD_AUTHZ_TYPE_DNS01, authz->domain, token) : NULL;
return rv;
}

static apr_status_t cha_dns_01_teardown(md_store_t *store, const char *domain, const md_t *md,
apr_table_t *env, apr_pool_t *p)
{
const char * const *argv;
const char *cmdline, *dns01_cmd;
const char *cmdline, *dns01_cmd, *dns01v;
char *tmp, *s;
apr_status_t rv;
int exit_code;

Expand All @@ -508,10 +518,20 @@ static apr_status_t cha_dns_01_teardown(md_store_t *store, const char *domain, c
md->name, domain);
goto out;
}

dns01v = apr_table_get(env, MD_KEY_DNS01_VERSION);
if (!dns01v || strcmp(dns01v, "2")) {
/* use older version of teardown args with only domain, remove token */
tmp = apr_pstrdup(p, domain);
s = strchr(tmp, ' ');
if (s) {
*s = '\0';
domain = tmp;
}
}

cmdline = apr_psprintf(p, "%s teardown %s", dns01_cmd, domain);
apr_tokenize_to_argv(cmdline, (char***)&argv, p);
if (APR_SUCCESS != (rv = md_util_exec(p, argv[0], argv, NULL, &exit_code)) || exit_code) {
if (APR_SUCCESS != (rv = md_util_exec(p, argv[0], argv, &exit_code)) || exit_code) {
md_log_perror(MD_LOG_MARK, MD_LOG_WARNING, rv, p,
"%s: dns-01 teardown command failed (exit code=%d) for %s",
md->name, exit_code, domain);
Expand All @@ -532,7 +552,8 @@ typedef apr_status_t cha_setup(md_acme_authz_cha_t *cha, md_acme_authz_t *authz,
md_acme_t *acme, md_store_t *store,
md_pkeys_spec_t *key_specs,
apr_array_header_t *acme_tls_1_domains, const md_t *md,
apr_table_t *env, md_result_t *result, apr_pool_t *p);
apr_table_t *env, md_result_t *result,
const char **psetup_token, apr_pool_t *p);

typedef apr_status_t cha_teardown(md_store_t *store, const char *domain, const md_t *md,
apr_table_t *env, apr_pool_t *p);
Expand Down Expand Up @@ -590,8 +611,7 @@ apr_status_t md_acme_authz_respond(md_acme_authz_t *authz, md_acme_t *acme, md_s
apr_status_t rv;
int i, j;
cha_find_ctx fctx;
const char *challenge_setup;


assert(acme);
assert(authz);
assert(authz->resource);
Expand All @@ -613,7 +633,7 @@ apr_status_t md_acme_authz_respond(md_acme_authz_t *authz, md_acme_t *acme, md_s
"type, this domain supports %s",
authz->domain, apr_array_pstrcat(p, challenges, ' '));
rv = APR_ENOTIMPL;
challenge_setup = NULL;
*psetup_token = NULL;
for (i = 0; i < challenges->nelts; ++i) {
fctx.type = APR_ARRAY_IDX(challenges, i, const char *);
fctx.accepted = NULL;
Expand All @@ -629,12 +649,12 @@ apr_status_t md_acme_authz_respond(md_acme_authz_t *authz, md_acme_t *acme, md_s
md_result_activity_printf(result, "Setting up challenge '%s' for domain %s",
fctx.accepted->type, authz->domain);
rv = CHA_TYPES[j].setup(fctx.accepted, authz, acme, store, key_specs,
acme_tls_1_domains, md, env, result, p);
acme_tls_1_domains, md, env, result,
psetup_token, p);
if (APR_SUCCESS == rv) {
md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, p,
"%s: set up challenge '%s' for %s",
authz->domain, fctx.accepted->type, md->name);
challenge_setup = CHA_TYPES[j].name;
goto out;
}
md_result_printf(result, rv, "error setting up challenge '%s' for %s, "
Expand All @@ -647,7 +667,6 @@ apr_status_t md_acme_authz_respond(md_acme_authz_t *authz, md_acme_t *acme, md_s
}

out:
*psetup_token = (APR_SUCCESS == rv)? apr_psprintf(p, "%s:%s", challenge_setup, authz->domain) : NULL;
if (!fctx.accepted || APR_ENOTIMPL == rv) {
rv = APR_EINVAL;
fctx.offered = apr_array_make(p, 5, sizeof(const char*));
Expand Down
29 changes: 17 additions & 12 deletions modules/md/md_util.c
Original file line number Diff line number Diff line change
Expand Up @@ -916,6 +916,19 @@ int md_dns_domains_match(const apr_array_header_t *domains, const char *name)
return 0;
}

int md_is_wild_match(const apr_array_header_t *domains, const char *name)
{
const char *domain;
int i;

for (i = 0; i < domains->nelts; ++i) {
domain = APR_ARRAY_IDX(domains, i, const char*);
if (md_dns_matches(domain, name))
return (domain[0] == '*' && domain[1] == '.');
}
return 0;
}

const char *md_util_schemify(apr_pool_t *p, const char *s, const char *def_scheme)
{
const char *cp = s;
Expand Down Expand Up @@ -1068,32 +1081,24 @@ apr_status_t md_util_try(md_util_try_fn *fn, void *baton, int ignore_errs,

/* execute process ********************************************************************************/

apr_status_t md_util_exec(apr_pool_t *p, const char *cmd, const char * const *argv,
apr_array_header_t *env, int *exit_code)
apr_status_t md_util_exec(apr_pool_t *p, const char *cmd,
const char * const *argv, int *exit_code)
{
apr_status_t rv;
apr_procattr_t *procattr;
apr_proc_t *proc;
apr_exit_why_e ewhy;
const char * const *envp = NULL;
char buffer[1024];

*exit_code = 0;
if (!(proc = apr_pcalloc(p, sizeof(*proc)))) {
return APR_ENOMEM;
}
if (env && env->nelts > 0) {
apr_array_header_t *nenv;

nenv = apr_array_copy(p, env);
APR_ARRAY_PUSH(nenv, const char *) = NULL;
envp = (const char * const *)nenv->elts;
}
if ( APR_SUCCESS == (rv = apr_procattr_create(&procattr, p))
&& APR_SUCCESS == (rv = apr_procattr_io_set(procattr, APR_NO_FILE,
APR_NO_PIPE, APR_FULL_BLOCK))
&& APR_SUCCESS == (rv = apr_procattr_cmdtype_set(procattr, APR_PROGRAM))
&& APR_SUCCESS == (rv = apr_proc_create(proc, cmd, argv, envp, procattr, p))) {
&& APR_SUCCESS == (rv = apr_procattr_cmdtype_set(procattr, APR_PROGRAM_ENV))
&& APR_SUCCESS == (rv = apr_proc_create(proc, cmd, argv, NULL, procattr, p))) {

/* read stderr and log on INFO for possible fault analysis. */
while(APR_SUCCESS == (rv = apr_file_gets(buffer, sizeof(buffer)-1, proc->err))) {
Expand Down
7 changes: 6 additions & 1 deletion modules/md/md_util.h
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ int md_array_str_add_missing(struct apr_array_header_t *dest,
/* process execution */

apr_status_t md_util_exec(apr_pool_t *p, const char *cmd, const char * const *argv,
struct apr_array_header_t *env, int *exit_code);
int *exit_code);

/**************************************************************************************************/
/* dns name check */
Expand Down Expand Up @@ -174,6 +174,11 @@ struct apr_array_header_t *md_dns_make_minimal(apr_pool_t *p,
*/
int md_dns_domains_match(const apr_array_header_t *domains, const char *name);

/**
* @return != 0 iff `name` is matched by a wildcard pattern in `domains`
*/
int md_is_wild_match(const apr_array_header_t *domains, const char *name);

/**************************************************************************************************/
/* file system related */

Expand Down
4 changes: 2 additions & 2 deletions modules/md/md_version.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,15 @@
* @macro
* Version number of the md module as c string
*/
#define MOD_MD_VERSION "2.4.21"
#define MOD_MD_VERSION "2.4.24"

/**
* @macro
* Numerical representation of the version number of the md module
* release. This is a 24 bit number with 8 bits for major number, 8 bits
* for minor and 8 bits for patch. Version 1.2.3 becomes 0x010203.
*/
#define MOD_MD_VERSION_NUM 0x020415
#define MOD_MD_VERSION_NUM 0x020418

#define MD_ACME_DEF_URL "https://acme-v02.api.letsencrypt.org/directory"
#define MD_TAILSCALE_DEF_URL "file://localhost/var/run/tailscale/tailscaled.sock"
Expand Down
Loading

0 comments on commit 44fd2ff

Please sign in to comment.