Rework worker startup/privsep config.

Starting with the privsep config, this commit changes the following:

- Removes the root, runas, keymgr_root, keymgr_runas, acme_root and
  acme_runas configuration options.

  Instead these are now configured via a privsep configuration context:

  privsep worker {
      root /tmp
      runas nobody
  }

  This is also configurable via Python using the new kore.privsep() method:

      kore.privsep("worker", root="/tmp", runas="nobody", skip=["chroot"])

Tied into this we also better handle worker startup:

- Per worker process, wait until it signalled it is ready.
- If a worker fails at startup, display its last log lines more clearly.
- Don't start acme process if no domain requires acme.
- Remove each process its individual startup log message in favour
  of a generalized one that displays its PID, root and user.
- At startup, log the kore version and built-ins in a nicer way.
- The worker processes now check things they need to start running
  before signaling they are ready (such as access to CA certs for
  TLS client authentication).
This commit is contained in:
Joris Vink 2021-09-07 21:59:22 +02:00
parent 1c33ce01d0
commit 3b20cda11c
13 changed files with 485 additions and 266 deletions

View File

@ -20,16 +20,57 @@ server tls {
# tls no
#}
# The worker process root directory. If chrooting was not disabled
# at startup the worker processes will chroot into this directory.
# Kore can have multiple settings for each processes that run under it.
# There are 3 different type of processes:
#
# If this configuration option is not set, Kore will take the current
# working directory as the root.
root /home/joris/src/kore
# 1) Worker processes, these handle the HTTP requests and your code
# runs inside of these.
# 2) The keymgr process, this handles your domain private keys
# and signing during the TLS handshakes. It also holds your
# ACME account-key and will sign ACME requests.
# 3) The acme process, this talks to the ACME servers.
#
# You can individually turn on/off chrooting and dropping user
# privileges per process. The -n and -r command-line options
# are a global override for skipping chroot or dropping user
# permissions on all processes.
#
# If no root/runas options are set in a process, it will inherit the
# default values from the worker processes.
#
# The worker processes will get the current working directory or
# current user if no options where specified for it.
#
# Configures the worker processes.
privsep worker {
# The user the workers will run as.
runas kore
# Worker processes will run as the specified user. If this option is
# missing Kore will run as the current user.
runas joris
# The root directory for the worker processes, if chroot isn't
# skipped, this is the directory it will chroot into.
#
# If not set, Kore will take the current working directory.
root /var/chroot/kore
# We could configure this process to not chroot and only
# chdir into its root directory.
#skip chroot
}
# Configures the keymgr process.
# If TLS is enabled you will need to specify paths to the domain
# certificate and key that Kore will load. This loading is done
# from the keymgr (separate process) and all paths must be relative
# to the keymgr process its root configuration option.
privsep keymgr {
# The user the keymgr will run as.
runas keymgr
# The root directory for the keymgr process. In this example
# we do not turn off chroot for this process so the keymgr
# will chroot into this directory.
root /etc/keymgr
}
# How many worker processes Kore will spawn. If the directive
# worker_set_affinity is set to 1 (the default) Kore will automatically
@ -88,25 +129,6 @@ workers 4
# NOTE: This file location must be inside your chrooted environment.
#rand_file random.data
# Key manager specific options.
# If TLS is enabled you will need to specify paths to the domain
# certificate and key that Kore will load. This loading is done
# from the keymgr (separate process) and all paths must be relative
# to the keymgr_root configuration option.
#
# keymgr_root The root path the keymgr will chdir into.
# If chroot was not disable at startup time
# the keymgr process will chroot into here.
#
# keymgr_runas The user to run the keymgr as.
#
# If privsep and chrooting is enabled at startup time but these
# configuration options were not set, they will take over the
# values from the 'root' and 'runas' configuration options.
#
#keymgr_root
#keymgr_runas
# Filemap settings
# filemap_index Name of the file to be used as the directory
# index for a filemap.

View File

@ -54,6 +54,7 @@ int kore_acme_tls_alpn(SSL *, const unsigned char **, unsigned char *,
const unsigned char *, unsigned int, void *);
extern char *acme_email;
extern int acme_domains;
extern char *acme_provider;
#if defined(__cplusplus)

View File

@ -450,9 +450,17 @@ struct kore_alog_header {
u_int16_t loglen;
} __attribute__((packed));
struct kore_privsep {
char *root;
char *runas;
int skip_runas;
int skip_chroot;
};
struct kore_worker {
u_int16_t id;
u_int16_t cpu;
int ready;
int running;
#if defined(__linux__)
int tracing;
@ -464,6 +472,7 @@ struct kore_worker {
int restarted;
u_int64_t time_locked;
struct kore_module_handle *active_hdlr;
struct kore_privsep *ps;
/* Used by the workers to store accesslogs. */
struct {
@ -701,8 +710,6 @@ extern int skip_runas;
extern int kore_foreground;
extern char *kore_pidfile;
extern char *kore_root_path;
extern char *kore_runas_user;
extern char *kore_tls_cipher_list;
extern volatile sig_atomic_t sig_recv;
@ -711,15 +718,16 @@ extern int tls_version;
extern DH *tls_dhparam;
extern char *rand_file;
extern int keymgr_active;
extern char *keymgr_runas_user;
extern char *keymgr_root_path;
extern char *acme_runas_user;
extern char *acme_root_path;
extern struct kore_privsep worker_privsep;
extern struct kore_privsep keymgr_privsep;
extern struct kore_privsep acme_privsep;
extern u_int8_t nlisteners;
extern u_int16_t cpu_count;
extern u_int8_t worker_count;
extern const char *kore_version;
extern const char *kore_build_date;
extern int worker_policy;
extern u_int8_t worker_set_affinity;
extern u_int32_t worker_rlimit_nofiles;
@ -742,12 +750,13 @@ void kore_proctitle(const char *);
void kore_default_getopt(int, char **);
void kore_worker_reap(void);
void kore_worker_init(void);
int kore_worker_init(void);
void kore_worker_privsep(void);
void kore_worker_started(void);
void kore_worker_make_busy(void);
void kore_worker_shutdown(void);
void kore_worker_dispatch_signal(int);
void kore_worker_privdrop(const char *, const char *);
void kore_worker_spawn(u_int16_t, u_int16_t, u_int16_t);
int kore_worker_spawn(u_int16_t, u_int16_t, u_int16_t);
int kore_worker_keymgr_response_verify(struct kore_msg *,
const void *, struct kore_domain **);

View File

@ -62,6 +62,8 @@ static PyObject *python_kore_sendobj(PyObject *, PyObject *,
PyObject *);
static PyObject *python_kore_server(PyObject *, PyObject *,
PyObject *);
static PyObject *python_kore_privsep(PyObject *, PyObject *,
PyObject *);
#if defined(KORE_USE_PGSQL)
@ -108,6 +110,7 @@ static struct PyMethodDef pykore_methods[] = {
METHOD("domain", python_kore_domain, METH_VARARGS | METH_KEYWORDS),
METHOD("server", python_kore_server, METH_VARARGS | METH_KEYWORDS),
METHOD("gather", python_kore_gather, METH_VARARGS | METH_KEYWORDS),
METHOD("privsep", python_kore_privsep, METH_VARARGS | METH_KEYWORDS),
METHOD("sendobj", python_kore_sendobj, METH_VARARGS | METH_KEYWORDS),
METHOD("websocket_broadcast", python_websocket_broadcast, METH_VARARGS),
#if defined(KORE_USE_PGSQL)

View File

@ -265,10 +265,10 @@ static char *account_url = NULL;
static u_int8_t acme_alpn_name[] =
{ 0xa, 'a', 'c', 'm', 'e', '-', 't', 'l', 's', '/', '1' };
struct kore_privsep acme_privsep;
int acme_domains = 0;
char *acme_email = NULL;
char *acme_provider = NULL;
char *acme_root_path = NULL;
char *acme_runas_user = NULL;
u_int32_t acme_request_timeout = 8;
void
@ -310,7 +310,7 @@ kore_acme_run(void)
#if defined(KORE_USE_PYTHON)
kore_msg_unregister(KORE_PYTHON_SEND_OBJ);
#endif
kore_worker_privdrop(acme_runas_user, acme_root_path);
kore_worker_privsep();
#if defined(__OpenBSD__)
if (unveil("/etc/ssl/", "r") == -1)
@ -321,14 +321,10 @@ kore_acme_run(void)
http_init();
if (!kore_quiet) {
kore_log(LOG_NOTICE,
"acme worker started (pid#%d)", worker->pid);
}
LIST_INIT(&orders);
LIST_INIT(&signops);
kore_worker_started();
acme_parse_directory();
while (quit != 1) {
@ -434,7 +430,7 @@ kore_acme_tls_challenge_use_cert(SSL *ssl, struct kore_domain *dom)
return;
}
kore_log(LOG_NOTICE, "[%s] acme-tls/1 challenge requested",
kore_log(LOG_INFO, "[%s] acme-tls/1 challenge requested",
dom->domain);
if ((c = SSL_get_ex_data(ssl, 0)) == NULL)
@ -989,7 +985,7 @@ acme_order_remove(struct acme_order *order, const char *reason)
kore_free(auth);
}
kore_log(LOG_NOTICE, "[%s] order removed (%s)", order->domain, reason);
kore_log(LOG_INFO, "[%s] order removed (%s)", order->domain, reason);
if (strcmp(reason, "completed"))
acme_order_retry(order->domain);
@ -1366,7 +1362,7 @@ cleanup:
} else {
order->auths--;
if (order->auths == 0) {
kore_log(LOG_NOTICE,
kore_log(LOG_INFO,
"[%s:auth] authentications done", order->domain);
order->state = ACME_ORDER_STATE_RUNNING;
}
@ -1389,12 +1385,12 @@ acme_challenge_tls_alpn_01(struct acme_order *order,
acme_challenge_tls_alpn_01_create(order, challenge);
break;
case ACME_STATUS_PROCESSING:
kore_log(LOG_NOTICE,
kore_log(LOG_INFO,
"[%s:auth:challenge:tls-alpn-01] processing",
order->domain);
break;
case ACME_STATUS_VALID:
kore_log(LOG_NOTICE,
kore_log(LOG_INFO,
"[%s:auth:challenge:tls-alpn-01] valid",
order->domain);
ret = KORE_RESULT_OK;
@ -1419,7 +1415,7 @@ acme_challenge_tls_alpn_01_create(struct acme_order *order,
u_int8_t digest[SHA256_DIGEST_LENGTH];
if (challenge->flags & ACME_FLAG_CHALLENGE_CREATED) {
kore_log(LOG_NOTICE,
kore_log(LOG_INFO,
"[%s:auth:challenge:tls-alpn-01] pending keymgr",
order->domain);
return;
@ -1427,7 +1423,7 @@ acme_challenge_tls_alpn_01_create(struct acme_order *order,
challenge->flags |= ACME_FLAG_CHALLENGE_CREATED;
kore_log(LOG_NOTICE,
kore_log(LOG_INFO,
"[%s:auth:challenge:tls-alpn-01] requested from keymgr",
order->domain);

View File

@ -69,8 +69,6 @@ static int configure_file(char *);
#if defined(KORE_USE_ACME)
static int configure_acme(char *);
static int configure_acme_root(char *);
static int configure_acme_runas(char *);
static int configure_acme_email(char *);
static int configure_acme_provider(char *);
#endif
@ -82,8 +80,7 @@ static int configure_bind(char *);
static int configure_bind_unix(char *);
static int configure_attach(char *);
static int configure_domain(char *);
static int configure_root(char *);
static int configure_runas(char *);
static int configure_privsep(char *);
static int configure_workers(char *);
static int configure_pidfile(char *);
static int configure_rlimit_nofiles(char *);
@ -92,6 +89,9 @@ static int configure_accept_threshold(char *);
static int configure_death_policy(char *);
static int configure_set_affinity(char *);
static int configure_socket_backlog(char *);
static int configure_privsep_skip(char *);
static int configure_privsep_root(char *);
static int configure_privsep_runas(char *);
#if defined(KORE_USE_PLATFORM_PLEDGE)
static int configure_add_pledge(char *);
@ -103,8 +103,6 @@ static int configure_certkey(char *);
static int configure_tls_version(char *);
static int configure_tls_cipher(char *);
static int configure_tls_dhparam(char *);
static int configure_keymgr_root(char *);
static int configure_keymgr_runas(char *);
static int configure_client_verify(char *);
static int configure_client_verify_depth(char *);
@ -155,9 +153,6 @@ static int configure_task_threads(char *);
#if defined(KORE_USE_PYTHON)
static int configure_deployment(char *);
static int configure_skip_chroot(char *);
static int configure_python_path(char *);
static int configure_python_import(char *);
#endif
#if defined(KORE_USE_CURL)
@ -180,18 +175,18 @@ static struct {
{ "bind", configure_bind },
{ "load", configure_load },
{ "domain", configure_domain },
{ "privsep", configure_privsep },
{ "server", configure_server },
{ "attach", configure_attach },
{ "certkey", configure_certkey },
{ "certfile", configure_certfile },
{ "include", configure_include },
{ "unix", configure_bind_unix },
{ "skip", configure_privsep_skip },
{ "root", configure_privsep_root },
{ "runas", configure_privsep_runas },
{ "client_verify", configure_client_verify },
{ "client_verify_depth", configure_client_verify_depth },
#if defined(KORE_USE_PYTHON)
{ "python_path", configure_python_path },
{ "python_import", configure_python_import },
#endif
#if !defined(KORE_NO_HTTP)
{ "route", configure_route},
{ "filemap", configure_filemap },
@ -217,9 +212,6 @@ static struct {
const char *name;
int (*configure)(char *);
} config_settings[] = {
{ "root", configure_root },
{ "chroot", configure_root },
{ "runas", configure_runas },
{ "workers", configure_workers },
{ "worker_max_connections", configure_max_connections },
{ "worker_rlimit_nofiles", configure_rlimit_nofiles },
@ -232,11 +224,7 @@ static struct {
{ "tls_cipher", configure_tls_cipher },
{ "tls_dhparam", configure_tls_dhparam },
{ "rand_file", configure_rand_file },
{ "keymgr_runas", configure_keymgr_runas },
{ "keymgr_root", configure_keymgr_root },
#if defined(KORE_USE_ACME)
{ "acme_runas", configure_acme_runas },
{ "acme_root", configure_acme_root },
{ "acme_email", configure_acme_email },
{ "acme_provider", configure_acme_provider },
#endif
@ -267,7 +255,6 @@ static struct {
#endif
#if defined(KORE_USE_PYTHON)
{ "deployment", configure_deployment },
{ "skipchroot", configure_skip_chroot },
#endif
#if defined(KORE_USE_PGSQL)
{ "pgsql_conn_max", configure_pgsql_conn_max },
@ -302,12 +289,14 @@ static struct kore_module_handle *current_handler = NULL;
extern const char *__progname;
static struct kore_domain *current_domain = NULL;
static struct kore_server *current_server = NULL;
static struct kore_privsep *current_privsep = NULL;
void
kore_parse_config(void)
{
FILE *fp;
BIO *bio;
struct passwd *pwd;
char path[PATH_MAX];
if (finalized)
@ -345,10 +334,10 @@ kore_parse_config(void)
if (!kore_module_loaded())
fatal("no application module was loaded");
if (kore_root_path == NULL) {
if (worker_privsep.root == NULL) {
if (getcwd(path, sizeof(path)) == NULL)
fatal("getcwd: %s", errno_s);
kore_root_path = kore_strdup(path);
worker_privsep.root = kore_strdup(path);
if (!kore_quiet) {
kore_log(LOG_NOTICE, "privsep: no root path set, "
@ -356,37 +345,54 @@ kore_parse_config(void)
}
}
if (getuid() != 0 && skip_chroot == 0)
fatal("cannot chroot, use -n to skip it");
if (worker_privsep.runas == NULL) {
if ((pwd = getpwuid(getuid())) == NULL)
fatal("getpwuid: %s", errno_s);
if (skip_runas != 1 && kore_runas_user == NULL)
fatal("missing runas user, use -r to skip it");
worker_privsep.runas = kore_strdup(pwd->pw_name);
if (!kore_quiet) {
kore_log(LOG_NOTICE, "privsep: no runas user set, "
"using current user %s", worker_privsep.runas);
}
if (getuid() != 0 && skip_runas == 0)
fatal("cannot drop privileges, use -r to skip it");
endpwent();
}
if (skip_runas) {
if (!kore_quiet)
kore_log(LOG_WARNING, "privsep: will not change user");
} else {
configure_check_var(&keymgr_runas_user, kore_runas_user,
"privsep: no keymgr_runas set, using 'runas` user");
configure_check_var(&keymgr_privsep.runas, worker_privsep.runas,
"privsep: no keymgr runas set, using 'privsep.worker.runas`");
#if defined(KORE_USE_ACME)
configure_check_var(&acme_runas_user, kore_runas_user,
"privsep: no acme_runas set, using 'runas` user");
configure_check_var(&acme_privsep.runas, worker_privsep.runas,
"privsep: no acme runas set, using 'privsep.worker.runas`");
#endif
configure_check_var(&keymgr_privsep.root, worker_privsep.root,
"privsep: no keymgr root set, using 'privsep.worker.root`");
#if defined(KORE_USE_ACME)
configure_check_var(&acme_privsep.root, worker_privsep.root,
"privsep: no acme root set, using 'privsep.worker.root`");
#endif
if (skip_chroot) {
worker_privsep.skip_chroot = 1;
keymgr_privsep.skip_chroot = 1;
#if defined(KORE_USE_ACME)
acme_privsep.skip_chroot = 1;
#endif
}
configure_check_var(&keymgr_root_path, kore_root_path,
"privsep: no keymgr_root set, using 'root` directory");
if (skip_runas) {
worker_privsep.skip_runas = 1;
keymgr_privsep.skip_runas = 1;
#if defined(KORE_USE_ACME)
configure_check_var(&acme_root_path, kore_root_path,
"privsep: no acme_root set, using 'root` directory");
acme_privsep.skip_runas = 1;
#endif
}
if (skip_runas && !kore_quiet)
kore_log(LOG_NOTICE, "privsep: skipping all runas options");
if (skip_chroot && !kore_quiet)
kore_log(LOG_WARNING, "privsep: will not chroot");
kore_log(LOG_NOTICE, "privsep: skipping all chroot options");
finalized = 1;
}
@ -404,6 +410,12 @@ kore_parse_config_file(FILE *fp)
continue;
}
if (!strcmp(p, "}") && current_privsep != NULL) {
lineno++;
current_privsep = NULL;
continue;
}
if (!strcmp(p, "}") && current_server != NULL) {
lineno++;
kore_server_finalize(current_server);
@ -649,7 +661,7 @@ configure_acme(char *yesno)
kore_acme_get_paths(current_domain->domain,
&current_domain->certkey, &current_domain->certfile);
acme_domains++;
} else {
printf("invalid '%s' for yes|no acme option\n", yesno);
return (KORE_RESULT_ERROR);
@ -658,24 +670,6 @@ configure_acme(char *yesno)
return (KORE_RESULT_OK);
}
static int
configure_acme_runas(char *user)
{
kore_free(acme_runas_user);
acme_runas_user = kore_strdup(user);
return (KORE_RESULT_OK);
}
static int
configure_acme_root(char *root)
{
kore_free(acme_root_path);
acme_root_path = kore_strdup(root);
return (KORE_RESULT_OK);
}
static int
configure_acme_email(char *email)
{
@ -937,21 +931,89 @@ configure_certkey(char *path)
}
static int
configure_keymgr_runas(char *user)
configure_privsep(char *options)
{
if (keymgr_runas_user != NULL)
kore_free(keymgr_runas_user);
keymgr_runas_user = kore_strdup(user);
char *argv[3];
if (current_privsep != NULL) {
printf("nested privsep contexts are not allowed\n");
return (KORE_RESULT_ERROR);
}
kore_split_string(options, " ", argv, 3);
if (argv[0] == NULL || argv[1] == NULL) {
printf("invalid privsep context\n");
return (KORE_RESULT_ERROR);
}
if (strcmp(argv[1], "{")) {
printf("privsep context not opened correctly\n");
return (KORE_RESULT_ERROR);
}
if (!strcmp(argv[0], "worker")) {
current_privsep = &worker_privsep;
} else if (!strcmp(argv[0], "keymgr")) {
current_privsep = &keymgr_privsep;
#if defined(KORE_USE_ACME)
} else if (!strcmp(argv[0], "keymgr")) {
current_privsep = &acme_privsep;
#endif
} else {
printf("unknown privsep context: %s\n", argv[0]);
return (KORE_RESULT_ERROR);
}
return (KORE_RESULT_OK);
}
static int
configure_keymgr_root(char *root)
configure_privsep_runas(char *user)
{
if (keymgr_root_path != NULL)
kore_free(keymgr_root_path);
keymgr_root_path = kore_strdup(root);
if (current_privsep == NULL) {
printf("runas not specified in privsep context\n");
return (KORE_RESULT_ERROR);
}
if (current_privsep->runas != NULL)
kore_free(current_privsep->runas);
current_privsep->runas = kore_strdup(user);
return (KORE_RESULT_OK);
}
static int
configure_privsep_root(char *root)
{
if (current_privsep == NULL) {
printf("root not specified in privsep context\n");
return (KORE_RESULT_ERROR);
}
if (current_privsep->root != NULL)
kore_free(current_privsep->root);
current_privsep->root = kore_strdup(root);
return (KORE_RESULT_OK);
}
static int
configure_privsep_skip(char *option)
{
if (current_privsep == NULL) {
printf("skip not specified in privsep context\n");
return (KORE_RESULT_ERROR);
}
if (!strcmp(option, "chroot")) {
current_privsep->skip_chroot = 1;
} else {
printf("unknown skip option '%s'\n", option);
return (KORE_RESULT_ERROR);
}
return (KORE_RESULT_OK);
}
@ -1709,26 +1771,6 @@ configure_websocket_timeout(char *option)
#endif /* !KORE_NO_HTTP */
static int
configure_root(char *path)
{
if (kore_root_path != NULL)
kore_free(kore_root_path);
kore_root_path = kore_strdup(path);
return (KORE_RESULT_OK);
}
static int
configure_runas(char *user)
{
if (kore_runas_user != NULL)
kore_free(kore_runas_user);
kore_runas_user = kore_strdup(user);
return (KORE_RESULT_OK);
}
static int
configure_workers(char *option)
{
@ -1885,22 +1927,6 @@ configure_task_threads(char *option)
#endif
#if defined(KORE_USE_PYTHON)
static int
configure_skip_chroot(char *value)
{
if (!strcmp(value, "True")) {
skip_chroot = 1;
} else if (!strcmp(value, "False")) {
skip_chroot = 0;
} else {
kore_log(LOG_NOTICE,
"kore.config.skipchroot: bad value '%s'", value);
return (KORE_RESULT_ERROR);
}
return (KORE_RESULT_OK);
}
static int
configure_deployment(char *value)
{
@ -1925,26 +1951,6 @@ configure_deployment(char *value)
return (KORE_RESULT_OK);
}
static int
configure_python_path(char *path)
{
kore_python_path(path);
return (KORE_RESULT_OK);
}
static int
configure_python_import(char *module)
{
char *argv[3];
kore_split_string(module, " ", argv, 3);
if (argv[0] == NULL)
return (KORE_RESULT_ERROR);
kore_module_load(argv[0], argv[1], KORE_MODULE_PYTHON);
return (KORE_RESULT_OK);
}
#endif
#if defined(KORE_USE_PLATFORM_PLEDGE)

View File

@ -69,9 +69,9 @@ kore_filemap_create(struct kore_domain *dom, const char *path, const char *root)
if (root[0] != '/' || root[sz - 1] != '/')
return (KORE_RESULT_ERROR);
if (kore_root_path != NULL) {
if (worker_privsep.root != NULL) {
len = snprintf(fpath, sizeof(fpath), "%s/%s",
kore_root_path, path);
worker_privsep.root, path);
if (len == -1 || (size_t)len >= sizeof(fpath))
fatal("kore_filemap_create: failed to concat paths");
} else {

View File

@ -248,9 +248,8 @@ static void keymgr_rsa_encrypt(struct kore_msg *, const void *,
static void keymgr_ecdsa_sign(struct kore_msg *, const void *,
struct key *);
int keymgr_active = 0;
char *keymgr_root_path = NULL;
char *keymgr_runas_user = NULL;
struct kore_privsep keymgr_privsep;
int keymgr_active = 0;
#if defined(__OpenBSD__)
#if defined(KORE_USE_ACME)
@ -293,10 +292,7 @@ kore_keymgr_run(void)
#if defined(KORE_USE_PYTHON)
kore_msg_unregister(KORE_PYTHON_SEND_OBJ);
#endif
kore_worker_privdrop(keymgr_runas_user, keymgr_root_path);
if (!kore_quiet)
kore_log(LOG_NOTICE, "key manager started (pid#%d)", getpid());
kore_worker_privsep();
if (rand_file != NULL) {
keymgr_load_randfile();
@ -321,6 +317,8 @@ kore_keymgr_run(void)
X509V3_EXT_add_alias(acme_oid, NID_subject_key_identifier);
#endif
kore_worker_started();
while (quit != 1) {
now = kore_time_ms();
if ((now - last_seed) > RAND_POLL_INTERVAL) {
@ -367,7 +365,7 @@ kore_keymgr_cleanup(int final)
struct key *key, *next;
if (final && !kore_quiet)
kore_log(LOG_NOTICE, "cleaning up keys");
kore_log(LOG_INFO, "cleaning up keys");
if (initialized == 0)
return;
@ -788,7 +786,7 @@ keymgr_acme_init(void)
ACME_RENEWAL_TIMER, NULL, 0);
if (key->pkey == NULL) {
kore_log(LOG_NOTICE, "generating new ACME account key");
kore_log(LOG_INFO, "generating new ACME account key");
key->pkey = kore_rsakey_generate(KORE_ACME_ACCOUNT_KEY);
needsreg = 1;
} else {
@ -828,7 +826,7 @@ keymgr_acme_domainkey(struct kore_domain *dom, struct key *key)
{
char *p;
kore_log(LOG_NOTICE, "generated new domain key for %s", dom->domain);
kore_log(LOG_INFO, "generated new domain key for %s", dom->domain);
if ((p = strrchr(dom->certkey, '/')) == NULL)
fatalx("invalid certkey path '%s'", dom->certkey);
@ -1029,7 +1027,7 @@ keymgr_acme_install_cert(const void *data, size_t len, struct key *key)
fd = open(key->dom->certfile, O_CREAT | O_TRUNC | O_WRONLY, 0700);
if (fd == -1)
fatal("open(%s): %s", key->dom->certfile, errno_s);
fatalx("open(%s): %s", key->dom->certfile, errno_s);
kore_log(LOG_INFO, "writing %zu bytes of data", len);
@ -1038,14 +1036,14 @@ keymgr_acme_install_cert(const void *data, size_t len, struct key *key)
if (ret == -1) {
if (errno == EINTR)
continue;
fatal("write(%s): %s", key->dom->certfile, errno_s);
fatalx("write(%s): %s", key->dom->certfile, errno_s);
}
break;
}
if ((size_t)ret != len) {
fatal("incorrect write on %s (%zd/%zu)",
fatalx("incorrect write on %s (%zd/%zu)",
key->dom->certfile, ret, len);
}
@ -1102,7 +1100,7 @@ keymgr_acme_challenge_cert(const void *data, size_t len, struct key *key)
slen = snprintf(hex + (idx * 2), sizeof(hex) - (idx * 2),
"%02x", digest[idx]);
if (slen == -1 || (size_t)slen >= sizeof(hex))
fatal("failed to convert digest to hex");
fatalx("failed to convert digest to hex");
}
if ((x509 = X509_new()) == NULL)
@ -1176,7 +1174,7 @@ keymgr_acme_csr(const struct kore_keyreq *req, struct key *key)
kore_log(LOG_INFO, "[%s] creating CSR", req->domain);
if ((csr = X509_REQ_new()) == NULL)
fatal("X509_REQ_new: %s", ssl_errno_s);
fatalx("X509_REQ_new: %s", ssl_errno_s);
if (!X509_REQ_set_version(csr, 3))
fatalx("X509_REQ_set_version(): %s", ssl_errno_s);

View File

@ -64,12 +64,12 @@ u_int8_t worker_count = 0;
char **kore_argv = NULL;
int kore_foreground = 0;
char *kore_progname = NULL;
char *kore_root_path = NULL;
char *kore_runas_user = NULL;
u_int32_t kore_socket_backlog = 5000;
char *kore_pidfile = KORE_PIDFILE_DEFAULT;
char *kore_tls_cipher_list = KORE_DEFAULT_CIPHER_LIST;
struct kore_privsep worker_privsep;
extern char **environ;
extern char *__progname;
static size_t proctitle_maxlen = 0;
@ -113,9 +113,9 @@ usage(void)
#endif
printf("\t-f\tstart in foreground\n");
printf("\t-h\tthis help text\n");
printf("\t-n\tdo not chroot\n");
printf("\t-n\tdo not chroot on any worker\n");
printf("\t-q\tonly log errors\n");
printf("\t-r\tdo not drop privileges\n");
printf("\t-r\tdo not change user on any worker\n");
printf("\t-v\tdisplay %s build information\n", __progname);
printf("\nFind more information on https://kore.io\n");
@ -226,6 +226,34 @@ main(int argc, char *argv[])
kore_module_init();
kore_server_sslstart();
if (!kore_quiet) {
kore_log(LOG_INFO, "%s %s starting, built=%s",
__progname, kore_version, kore_build_date);
kore_log(LOG_INFO, "built-ins: "
#if defined(__linux__)
"seccomp "
#endif
#if defined(KORE_USE_PGSQL)
"pgsql "
#endif
#if defined(KORE_USE_TASKS)
"tasks "
#endif
#if defined(KORE_USE_JSONRPC)
"jsonrpc "
#endif
#if defined(KORE_USE_PYTHON)
"python "
#endif
#if defined(KORE_USE_ACME)
"acme "
#endif
#if defined(KORE_USE_CURL)
"curl "
#endif
);
}
#if !defined(KORE_SINGLE_BINARY) && !defined(KORE_USE_PYTHON)
if (config_file == NULL)
usage();
@ -276,7 +304,7 @@ main(int argc, char *argv[])
kore_server_start(argc, argv);
if (!kore_quiet)
kore_log(LOG_NOTICE, "server shutting down");
kore_log(LOG_INFO, "server shutting down");
kore_worker_shutdown();
@ -292,7 +320,7 @@ main(int argc, char *argv[])
kore_server_cleanup();
if (!kore_quiet)
kore_log(LOG_NOTICE, "goodbye");
kore_log(LOG_INFO, "goodbye");
#if defined(KORE_USE_PYTHON)
kore_python_cleanup();
@ -554,10 +582,10 @@ kore_server_finalize(struct kore_server *srv)
proto = "http";
if (l->family == AF_UNIX) {
kore_log(LOG_NOTICE, "%s serving %s on %s",
kore_log(LOG_INFO, "%s serving %s on %s",
srv->name, proto, l->host);
} else {
kore_log(LOG_NOTICE, "%s serving %s on %s:%s",
kore_log(LOG_INFO, "%s serving %s on %s:%s",
srv->name, proto, l->host, l->port);
}
}
@ -881,25 +909,6 @@ kore_server_start(int argc, char *argv[])
kore_pid = getpid();
kore_write_kore_pid();
if (!kore_quiet) {
kore_log(LOG_NOTICE, "%s is starting up", __progname);
#if defined(__linux__)
kore_log(LOG_NOTICE, "seccomp sandbox enabled");
#endif
#if defined(KORE_USE_PGSQL)
kore_log(LOG_NOTICE, "pgsql built-in enabled");
#endif
#if defined(KORE_USE_TASKS)
kore_log(LOG_NOTICE, "tasks built-in enabled");
#endif
#if defined(KORE_USE_JSONRPC)
kore_log(LOG_NOTICE, "jsonrpc built-in enabled");
#endif
#if defined(KORE_USE_PYTHON)
kore_log(LOG_NOTICE, "python built-in enabled");
#endif
}
#if !defined(KORE_SINGLE_BINARY) && !defined(KORE_USE_PYTHON)
kore_call_parent_configure(argc, argv);
#endif
@ -922,7 +931,19 @@ kore_server_start(int argc, char *argv[])
}
kore_platform_proctitle("[parent]");
kore_worker_init();
if (!kore_worker_init()) {
kore_log(LOG_ERR, "last worker log lines:");
kore_log(LOG_ERR, "=====================================");
net_init();
kore_connection_init();
kore_platform_event_init();
kore_msg_parent_init();
kore_platform_event_wait(10);
kore_worker_dispatch_signal(SIGQUIT);
kore_log(LOG_ERR, "=====================================");
return;
}
/* Set worker_max_connections for kore_connection_init(). */
tmp = worker_max_connections;

View File

@ -27,6 +27,7 @@ struct kore_wlog {
char logmsg[];
};
static void log_print(int, const char *, ...);
static void log_from_worker(struct kore_msg *, const void *);
void
@ -79,7 +80,7 @@ kore_log(int prio, const char *fmt, ...)
str = kore_buf_stringify(&buf, NULL);
if (kore_foreground)
printf("[parent]: %s\n", str);
log_print(prio, "[parent]: %s\n", str);
else
syslog(prio, "[parent]: %s", str);
}
@ -104,11 +105,32 @@ log_from_worker(struct kore_msg *msg, const void *data)
name = kore_worker_name(wlog->wid);
if (kore_foreground) {
printf("%s: %.*s\n",
log_print(wlog->prio, "%s: %.*s\n",
name, (int)wlog->loglen, wlog->logmsg);
fflush(stdout);
} else {
syslog(wlog->prio, "%s: %.*s",
name, (int)wlog->loglen, wlog->logmsg);
}
}
static void
log_print(int prio, const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
switch (prio) {
case LOG_ERR:
case LOG_WARNING:
case LOG_NOTICE:
case LOG_INFO:
case LOG_DEBUG:
break;
}
vprintf(fmt, args);
fflush(stdout);
va_end(args);
}

View File

@ -1847,6 +1847,73 @@ python_kore_server(PyObject *self, PyObject *args, PyObject *kwargs)
Py_RETURN_NONE;
}
static PyObject *
python_kore_privsep(PyObject *self, PyObject *args, PyObject *kwargs)
{
struct kore_privsep *ps;
const char *val;
PyObject *skip, *obj;
Py_ssize_t list_len, idx;
if (!PyArg_ParseTuple(args, "s", &val))
return (NULL);
if (!strcmp(val, "worker")) {
ps = &worker_privsep;
} else if (!strcmp(val, "keymgr")) {
ps = &keymgr_privsep;
#if defined(KORE_USE_ACME)
} else if (!strcmp(val, "keymgr")) {
ps = &acme_privsep;
#endif
} else {
PyErr_Format(PyExc_RuntimeError,
"unknown privsep process '%s'", val);
return (NULL);
}
if ((val = python_string_from_dict(kwargs, "root")) != NULL) {
kore_free(ps->root);
ps->root = kore_strdup(val);
}
if ((val = python_string_from_dict(kwargs, "runas")) != NULL) {
kore_free(ps->runas);
ps->runas = kore_strdup(val);
}
if ((skip = PyDict_GetItemString(kwargs, "skip")) != NULL) {
if (!PyList_CheckExact(skip)) {
PyErr_Format(PyExc_RuntimeError,
"privsep skip keyword needs to be a list");
return (NULL);
}
list_len = PyList_Size(skip);
for (idx = 0; idx < list_len; idx++) {
if ((obj = PyList_GetItem(skip, idx)) == NULL)
return (NULL);
if (!PyUnicode_Check(obj))
return (NULL);
if ((val = PyUnicode_AsUTF8AndSize(obj, NULL)) == NULL)
return (NULL);
if (!strcmp(val, "chroot")) {
ps->skip_chroot = 1;
} else {
PyErr_Format(PyExc_RuntimeError,
"unknown skip keyword '%s'", val);
return (NULL);
}
}
}
Py_RETURN_NONE;
}
static PyObject *
python_kore_prerequest(PyObject *self, PyObject *args)
{

View File

@ -652,14 +652,10 @@ fatal_log(const char *fmt, va_list args)
extern const char *kore_progname;
(void)vsnprintf(buf, sizeof(buf), fmt, args);
if (!kore_foreground)
kore_log(LOG_ERR, "%s", buf);
kore_log(LOG_ERR, "%s", buf);
if (worker != NULL && worker->id == KORE_WORKER_KEYMGR)
kore_keymgr_cleanup(1);
printf("%s: %s\n", kore_progname, buf);
}
static int

View File

@ -16,6 +16,7 @@
#include <sys/param.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/shm.h>
#include <sys/wait.h>
#include <sys/time.h>
@ -28,6 +29,7 @@
#include <grp.h>
#include <pwd.h>
#include <signal.h>
#include <unistd.h>
#include "kore.h"
@ -77,6 +79,7 @@ struct wlock {
static int worker_trylock(void);
static void worker_unlock(void);
static void worker_reaper(pid_t, int);
static void worker_domain_check(struct kore_domain *);
static inline int worker_acceptlock_obtain(void);
static inline void worker_acceptlock_release(void);
@ -99,7 +102,7 @@ u_int32_t worker_max_connections = 512;
u_int32_t worker_active_connections = 0;
int worker_policy = KORE_WORKER_POLICY_RESTART;
void
int
kore_worker_init(void)
{
size_t len;
@ -149,27 +152,33 @@ kore_worker_init(void)
for (idx = KORE_WORKER_BASE; idx < worker_count; idx++) {
if (cpu >= cpu_count)
cpu = 0;
kore_worker_spawn(idx, id++, cpu++);
if (!kore_worker_spawn(idx, id++, cpu++))
return (KORE_RESULT_ERROR);
}
if (keymgr_active) {
#if defined(KORE_USE_ACME)
/* The ACME process is only started if we need it. */
if (acme_provider) {
kore_worker_spawn(KORE_WORKER_ACME_IDX,
KORE_WORKER_ACME, 0);
if (acme_domains) {
if (!kore_worker_spawn(KORE_WORKER_ACME_IDX,
KORE_WORKER_ACME, 0))
return (KORE_RESULT_ERROR);
}
#endif
/* Now we can start the keymgr. */
kore_worker_spawn(KORE_WORKER_KEYMGR_IDX,
KORE_WORKER_KEYMGR, 0);
if (!kore_worker_spawn(KORE_WORKER_KEYMGR_IDX,
KORE_WORKER_KEYMGR, 0))
return (KORE_RESULT_ERROR);
}
return (KORE_RESULT_OK);
}
void
int
kore_worker_spawn(u_int16_t idx, u_int16_t id, u_int16_t cpu)
{
int cnt;
struct kore_worker *kw;
kw = WORKER(idx);
@ -178,6 +187,7 @@ kore_worker_spawn(u_int16_t idx, u_int16_t id, u_int16_t cpu)
kw->has_lock = 0;
kw->active_hdlr = NULL;
kw->running = 1;
kw->ready = 0;
if (socketpair(AF_UNIX, SOCK_STREAM, 0, kw->pipe) == -1)
fatal("socketpair(): %s", errno_s);
@ -186,6 +196,20 @@ kore_worker_spawn(u_int16_t idx, u_int16_t id, u_int16_t cpu)
!kore_connection_nonblock(kw->pipe[1], 0))
fatal("could not set pipe fds to nonblocking: %s", errno_s);
switch (id) {
case KORE_WORKER_KEYMGR:
kw->ps = &keymgr_privsep;
break;
#if defined(KORE_USE_ACME)
case KORE_WORKER_ACME:
kw->ps = &acme_privsep;
break;
#endif
default:
kw->ps = &worker_privsep;
break;
}
kw->pid = fork();
if (kw->pid == -1)
fatal("could not spawn worker child: %s", errno_s);
@ -193,8 +217,24 @@ kore_worker_spawn(u_int16_t idx, u_int16_t id, u_int16_t cpu)
if (kw->pid == 0) {
kw->pid = getpid();
kore_worker_entry(kw);
/* NOTREACHED */
exit(1);
} else {
for (cnt = 0; cnt < 25; cnt++) {
if (kw->ready == 1)
break;
usleep(10000);
}
if (kw->ready == 0) {
kore_log(LOG_NOTICE,
"worker %d failed to start, shutting down",
kw->id);
return (KORE_RESULT_ERROR);
}
}
return (KORE_RESULT_OK);
}
struct kore_worker *
@ -282,37 +322,43 @@ kore_worker_dispatch_signal(int sig)
}
void
kore_worker_privdrop(const char *runas, const char *root)
kore_worker_privsep(void)
{
rlim_t fd;
struct rlimit rl;
struct passwd *pw = NULL;
struct passwd *pw;
if (root == NULL)
fatalx("no root directory for kore_worker_privdrop");
if (worker == NULL)
fatalx("%s called with no worker", __func__);
pw = NULL;
/* Must happen before chroot. */
if (skip_runas == 0) {
if (runas == NULL)
fatalx("no runas user given and -r not specified");
pw = getpwnam(runas);
if (pw == NULL) {
if (worker->ps->skip_runas == 0) {
if (worker->ps->runas == NULL) {
fatalx("no runas user given for %s",
kore_worker_name(worker->id));
}
if ((pw = getpwnam(worker->ps->runas)) == NULL) {
fatalx("cannot getpwnam(\"%s\") for user: %s",
runas, errno_s);
worker->ps->runas, errno_s);
}
}
if (skip_chroot == 0) {
if (chroot(root) == -1) {
if (worker->ps->skip_chroot == 0) {
if (chroot(worker->ps->root) == -1) {
fatalx("cannot chroot(\"%s\"): %s",
root, errno_s);
worker->ps->root, errno_s);
}
if (chdir("/") == -1)
fatalx("cannot chdir(\"/\"): %s", errno_s);
} else {
if (chdir(root) == -1)
fatalx("cannot chdir(\"%s\"): %s", root, errno_s);
if (chdir(worker->ps->root) == -1) {
fatalx("cannot chdir(\"%s\"): %s",
worker->ps->root, errno_s);
}
}
if (getrlimit(RLIMIT_NOFILE, &rl) == -1) {
@ -332,7 +378,7 @@ kore_worker_privdrop(const char *runas, const char *root)
worker_rlimit_nofiles, errno_s);
}
if (skip_runas == 0) {
if (worker->ps->skip_runas == 0) {
if (setgroups(1, &pw->pw_gid) ||
#if defined(__MACH__) || defined(NetBSD)
setgid(pw->pw_gid) || setegid(pw->pw_gid) ||
@ -341,7 +387,7 @@ kore_worker_privdrop(const char *runas, const char *root)
setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
#endif
fatalx("cannot drop privileges");
fatalx("cannot drop privileges (%s)", errno_s);
}
kore_platform_sandbox();
@ -370,7 +416,6 @@ kore_worker_entry(struct kore_worker *kw)
kore_platform_worker_setcpu(kw);
kore_pid = kw->pid;
kore_signal_setup();
if (kw->id == KORE_WORKER_KEYMGR) {
@ -394,7 +439,7 @@ kore_worker_entry(struct kore_worker *kw)
kore_task_init();
#endif
kore_worker_privdrop(kore_runas_user, kore_root_path);
kore_worker_privsep();
#if !defined(KORE_NO_HTTP)
http_init();
@ -435,12 +480,6 @@ kore_worker_entry(struct kore_worker *kw)
if (nlisteners == 0)
worker_no_lock = 1;
if (!kore_quiet) {
kore_log(LOG_NOTICE,
"worker %d started (cpu#%d, pid#%d)",
kw->id, kw->cpu, kw->pid);
}
rcall = kore_runtime_getcall("kore_worker_configure");
if (rcall != NULL) {
kore_runtime_execute(rcall);
@ -448,6 +487,9 @@ kore_worker_entry(struct kore_worker *kw)
}
kore_module_onload();
kore_domain_callback(worker_domain_check);
kore_worker_started();
worker->restarted = 0;
for (;;) {
@ -672,6 +714,40 @@ kore_worker_keymgr_response_verify(struct kore_msg *msg, const void *data,
return (KORE_RESULT_OK);
}
void
kore_worker_started(void)
{
const char *chroot;
if (worker->ps->skip_chroot)
chroot = "root";
else
chroot = "chroot";
if (!kore_quiet) {
kore_log(LOG_NOTICE,
"process started (#%d %s=%s%s%s)",
getpid(), chroot, worker->ps->root,
worker->ps->skip_runas ? "" : " user=",
worker->ps->skip_runas ? "" : worker->ps->runas);
}
worker->ready = 1;
}
static void
worker_domain_check(struct kore_domain *dom)
{
struct stat st;
if (dom->cafile != NULL) {
if (stat(dom->cafile, &st) == -1)
fatalx("'%s': %s", dom->cafile, errno_s);
if (access(dom->cafile, R_OK) == -1)
fatalx("'%s': not readable", dom->cafile, errno_s);
}
}
static void
worker_reaper(pid_t pid, int status)
{
@ -758,9 +834,11 @@ worker_reaper(pid_t pid, int status)
kore_log(LOG_NOTICE, "restarting worker %d", kw->id);
kw->restarted = 1;
kore_msg_parent_remove(kw);
kore_worker_spawn(idx, kw->id, kw->cpu);
kore_msg_parent_add(kw);
if (!kore_worker_spawn(idx, kw->id, kw->cpu))
(void)raise(SIGQUIT);
kore_msg_parent_add(kw);
break;
}
}