Skip to content
This repository was archived by the owner on Jun 9, 2026. It is now read-only.

Commit e05c36d

Browse files
authored
Merge pull request #285 from icing/tailscale
Tailscale Support
2 parents 6432472 + 50fe67f commit e05c36d

21 files changed

Lines changed: 754 additions & 13 deletions

configure.ac

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,9 @@ AC_SUBST(MODULE_SRC)
260260
ACME_DEF_URL=https://acme-v02.api.letsencrypt.org/directory
261261
AC_SUBST(ACME_DEF_URL)
262262

263+
TAILSCALE_DEF_URL="file://localhost/var/run/tailscale/tailscaled.sock"
264+
AC_SUBST(TAILSCALE_DEF_URL)
265+
263266
# it must be configurable without an ACME test server installed
264267
ACME_TEST_TYPE="none"
265268
ACME_TEST_URL="none"

src/Makefile.am

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ A2LIB_OBJECTS = \
4747
md_status.c \
4848
md_store.c \
4949
md_store_fs.c \
50+
md_tailscale.c \
5051
md_time.c \
5152
md_util.c
5253

@@ -70,6 +71,7 @@ A2LIB_HFILES = \
7071
md_status.h \
7172
md_store.h \
7273
md_store_fs.h \
74+
md_tailscale.h \
7375
md_time.h \
7476
md_util.h \
7577
md.h

src/md_acme_drive.c

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1036,9 +1036,19 @@ static apr_status_t acme_driver_preload(md_proto_driver_t *d,
10361036
return rv;
10371037
}
10381038

1039+
static apr_status_t acme_complete_md(md_t *md, apr_pool_t *p)
1040+
{
1041+
(void)p;
1042+
if (!md->ca_url) {
1043+
md->ca_url = MD_ACME_DEF_URL;
1044+
}
1045+
return APR_SUCCESS;
1046+
}
1047+
10391048
static md_proto_t ACME_PROTO = {
10401049
MD_PROTO_ACME, acme_driver_init, acme_driver_renew,
1041-
acme_driver_preload_init, acme_driver_preload
1050+
acme_driver_preload_init, acme_driver_preload,
1051+
acme_complete_md,
10421052
};
10431053

10441054
apr_status_t md_acme_protos_add(apr_hash_t *protos, apr_pool_t *p)

src/md_core.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -427,7 +427,7 @@ apr_status_t md_get_ca_url_from_name(const char **purl, apr_pool_t *p, const cha
427427
}
428428
}
429429
*purl = name;
430-
rv = md_util_abs_http_uri_check(p, name, &err);
430+
rv = md_util_abs_uri_check(p, name, &err);
431431
if (APR_SUCCESS != rv) {
432432
apr_array_header_t *names;
433433

src/md_crypt.c

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -709,6 +709,53 @@ apr_status_t md_pkey_fsave(md_pkey_t *pkey, apr_pool_t *p,
709709
return rv;
710710
}
711711

712+
apr_status_t md_pkey_read_http(md_pkey_t **ppkey, apr_pool_t *pool,
713+
const struct md_http_response_t *res)
714+
{
715+
apr_status_t rv;
716+
apr_off_t data_len;
717+
char *pem_data;
718+
apr_size_t pem_len;
719+
md_pkey_t *pkey;
720+
BIO *bf;
721+
passwd_ctx ctx;
722+
723+
rv = apr_brigade_length(res->body, 1, &data_len);
724+
if (APR_SUCCESS != rv) goto leave;
725+
if (data_len > 1024*1024) { /* certs usually are <2k each */
726+
rv = APR_EINVAL;
727+
goto leave;
728+
}
729+
rv = apr_brigade_pflatten(res->body, &pem_data, &pem_len, res->req->pool);
730+
if (APR_SUCCESS != rv) goto leave;
731+
732+
if (NULL == (bf = BIO_new_mem_buf(pem_data, (int)pem_len))) {
733+
rv = APR_ENOMEM;
734+
goto leave;
735+
}
736+
pkey = make_pkey(pool);
737+
ctx.pass_phrase = NULL;
738+
ctx.pass_len = 0;
739+
ERR_clear_error();
740+
pkey->pkey = PEM_read_bio_PrivateKey(bf, NULL, NULL, &ctx);
741+
BIO_free(bf);
742+
743+
if (pkey->pkey == NULL) {
744+
unsigned long err = ERR_get_error();
745+
rv = APR_EINVAL;
746+
md_log_perror(MD_LOG_MARK, MD_LOG_WARNING, rv, pool,
747+
"error loading pkey from http response: %s",
748+
ERR_error_string(err, NULL));
749+
goto leave;
750+
}
751+
rv = APR_SUCCESS;
752+
apr_pool_cleanup_register(pool, pkey, pkey_cleanup, apr_pool_cleanup_null);
753+
754+
leave:
755+
*ppkey = (APR_SUCCESS == rv)? pkey : NULL;
756+
return rv;
757+
}
758+
712759
/* Determine the message digest used for signing with the given private key.
713760
*/
714761
static const EVP_MD *pkey_get_MD(md_pkey_t *pkey)
@@ -1137,6 +1184,11 @@ const char *md_cert_get_serial_number(const md_cert_t *cert, apr_pool_t *p)
11371184
return s;
11381185
}
11391186

1187+
int md_certs_are_equal(const md_cert_t *a, const md_cert_t *b)
1188+
{
1189+
return X509_cmp(a->x509, b->x509) == 0;
1190+
}
1191+
11401192
int md_cert_is_valid_now(const md_cert_t *cert)
11411193
{
11421194
return ((X509_cmp_current_time(X509_get_notBefore(cert->x509)) < 0)

src/md_crypt.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,12 @@ void *md_pkey_get_EVP_PKEY(struct md_pkey_t *pkey);
117117
apr_status_t md_crypt_hmac64(const char **pmac64, const struct md_data_t *hmac_key,
118118
apr_pool_t *p, const char *d, size_t dlen);
119119

120+
/**
121+
* Read a private key from a http response.
122+
*/
123+
apr_status_t md_pkey_read_http(md_pkey_t **ppkey, apr_pool_t *pool,
124+
const struct md_http_response_t *res);
125+
120126
/**************************************************************************************************/
121127
/* X509 certificates */
122128

@@ -179,6 +185,11 @@ apr_time_t md_cert_get_not_after(const md_cert_t *cert);
179185
apr_time_t md_cert_get_not_before(const md_cert_t *cert);
180186
struct md_timeperiod_t md_cert_get_valid(const md_cert_t *cert);
181187

188+
/**
189+
* Return != 0 iff the hash values of the certificates are equal.
190+
*/
191+
int md_certs_are_equal(const md_cert_t *a, const md_cert_t *b);
192+
182193
apr_status_t md_cert_get_issuers_uri(const char **puri, const md_cert_t *cert, apr_pool_t *p);
183194
apr_status_t md_cert_get_alt_names(apr_array_header_t **pnames, const md_cert_t *cert, apr_pool_t *p);
184195

src/md_curl.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,9 @@ static apr_status_t internals_setup(md_http_request_t *req)
301301
if (req->ca_file) {
302302
curl_easy_setopt(curl, CURLOPT_CAINFO, req->ca_file);
303303
}
304+
if (req->unix_socket_path) {
305+
curl_easy_setopt(curl, CURLOPT_UNIX_SOCKET_PATH, req->unix_socket_path);
306+
}
304307

305308
if (req->body_len >= 0) {
306309
/* set the Content-Length */

src/md_http.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ struct md_http_t {
3333
void *impl_data; /* to be used by the implementation */
3434
const char *user_agent;
3535
const char *proxy_url;
36+
const char *unix_socket_path;
3637
md_http_timeouts_t timeout;
3738
const char *ca_file;
3839
};
@@ -143,6 +144,11 @@ void md_http_set_ca_file(md_http_t *http, const char *ca_file)
143144
http->ca_file = ca_file;
144145
}
145146

147+
void md_http_set_unix_socket_path(md_http_t *http, const char *path)
148+
{
149+
http->unix_socket_path = path;
150+
}
151+
146152
static apr_status_t req_set_body(md_http_request_t *req, const char *content_type,
147153
apr_bucket_brigade *body, apr_off_t body_len,
148154
int detect_len)
@@ -211,6 +217,7 @@ static apr_status_t req_create(md_http_request_t **preq, md_http_t *http,
211217
req->proxy_url = http->proxy_url;
212218
req->timeout = http->timeout;
213219
req->ca_file = http->ca_file;
220+
req->unix_socket_path = http->unix_socket_path;
214221
*preq = req;
215222
return rv;
216223
}

src/md_http.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ struct md_http_request_t {
6565
const char *user_agent;
6666
const char *proxy_url;
6767
const char *ca_file;
68+
const char *unix_socket_path;
6869
apr_table_t *headers;
6970
struct apr_bucket_brigade *body;
7071
apr_off_t body_len;
@@ -117,6 +118,12 @@ void md_http_set_stalling(md_http_request_t *req, long bytes_per_sec, apr_time_t
117118
*/
118119
void md_http_set_ca_file(md_http_t *http, const char *ca_file);
119120

121+
/**
122+
* Set the path of a unix domain socket for use instead of TCP
123+
* in a connection. Disable by providing NULL as path.
124+
*/
125+
void md_http_set_unix_socket_path(md_http_t *http, const char *path);
126+
120127
/**
121128
* Perform the request. Then this function returns, the request and
122129
* all its memory has been freed and must no longer be used.

src/md_reg.c

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
#include "md_reg.h"
3434
#include "md_store.h"
3535
#include "md_status.h"
36+
#include "md_tailscale.h"
3637
#include "md_util.h"
3738

3839
#include "md_acme.h"
@@ -98,7 +99,8 @@ apr_status_t md_reg_create(md_reg_t **preg, apr_pool_t *p, struct md_store_t *st
9899
md_timeslice_create(&reg->renew_window, p, MD_TIME_LIFE_NORM, MD_TIME_RENEW_WINDOW_DEF);
99100
md_timeslice_create(&reg->warn_window, p, MD_TIME_LIFE_NORM, MD_TIME_WARN_WINDOW_DEF);
100101

101-
if (APR_SUCCESS == (rv = md_acme_protos_add(reg->protos, p))) {
102+
if (APR_SUCCESS == (rv = md_acme_protos_add(reg->protos, p))
103+
&& APR_SUCCESS == (rv = md_tailscale_protos_add(reg->protos, p))) {
102104
rv = load_props(reg, p);
103105
}
104106

@@ -901,12 +903,22 @@ apr_status_t md_reg_sync_finish(md_reg_t *reg, md_t *md, apr_pool_t *p, apr_pool
901903
md_t *old;
902904
apr_status_t rv;
903905
int changed = 1;
906+
md_proto_t *proto;
904907

905-
if (!md->ca_url) {
906-
md->ca_url = MD_ACME_DEF_URL;
907-
md->ca_proto = MD_PROTO_ACME;
908+
if (!md->ca_proto) {
909+
md->ca_proto = MD_PROTO_ACME;
910+
}
911+
proto = apr_hash_get(reg->protos, md->ca_proto, (apr_ssize_t)strlen(md->ca_proto));
912+
if (!proto) {
913+
rv = APR_ENOTIMPL;
914+
md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, ptemp,
915+
"[%s] uses unknown CA protocol '%s'",
916+
md->name, md->ca_proto);
917+
goto leave;
908918
}
909-
919+
rv = proto->complete_md(md, p);
920+
if (APR_SUCCESS != rv) goto leave;
921+
910922
rv = state_init(reg, p, md);
911923
if (APR_SUCCESS != rv) goto leave;
912924

0 commit comments

Comments
 (0)