bud_error_t bud_config_load_backend(bud_config_t* config, JSON_Object* obj, bud_config_backend_t* backend, bud_hashmap_t* map, unsigned int* ext_count) { bud_error_t err; JSON_Value* val; const char* external; int r; bud_config_load_addr(obj, (bud_config_addr_t*) backend); backend->config = config; backend->xforward = -1; val = json_object_get_value(obj, "proxyline"); if (json_value_get_type(val) == JSONString) { const char* pline; pline = json_value_get_string(val); if (strcmp(pline, "haproxy") == 0) backend->proxyline = kBudProxylineHAProxy; else if (strcmp(pline, "json") == 0) backend->proxyline = kBudProxylineJSON; else return bud_error_dstr(kBudErrProxyline, pline); } else { backend->proxyline = val != NULL && json_value_get_boolean(val) ? kBudProxylineHAProxy : kBudProxylineNone; } val = json_object_get_value(obj, "x-forward"); if (val != NULL) backend->xforward = json_value_get_boolean(val); /* Set defaults here to use them in sni.c */ bud_config_set_backend_defaults(backend); r = bud_config_str_to_addr(backend->host, backend->port, &backend->addr); if (r != 0) return bud_error_num(kBudErrPton, r); external = json_object_get_string(obj, "external"); if (external == NULL) return bud_ok(); /* Insert backend into a hashmap */ err = bud_hashmap_insert(map, external, strlen(external), backend); if (!bud_is_ok(err)) return err; (*ext_count)++; return bud_ok(); }
bud_error_t bud_config_init(bud_config_t* config) { bud_error_t err; int i; int r; /* Get addresses of frontend and backend */ r = bud_config_str_to_addr(config->frontend.host, config->frontend.port, &config->frontend.addr); if (r != 0) return bud_error_num(kBudErrPton, r); for (i = 0; i < config->frontend.interface.count; i++) { bud_config_addr_t* addr; addr = &config->frontend.interface.list[i]; r = bud_config_str_to_addr(addr->host, addr->port, &addr->addr); if (r != 0) return bud_error_num(kBudErrPton, r); } err = bud_config_format_proxyline(config); if (!bud_is_ok(err)) return err; i = 0; config->balance_e = bud_config_balance_to_enum(config->balance); /* At least one backend should be present for non-SNI balancing */ if (config->contexts[0].backend.count == 0 && config->balance_e != kBudBalanceSNI) { err = bud_error(kBudErrNoBackend); goto fatal; } /* Get indexes for SSL_set_ex_data()/SSL_get_ex_data() */ if (kBudSSLClientIndex == -1) { kBudSSLConfigIndex = SSL_CTX_get_ex_new_index(0, NULL, NULL, NULL, NULL); kBudSSLClientIndex = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL); kBudSSLSNIIndex = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL); kBudSSLTicketKeyIndex = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL); if (kBudSSLConfigIndex == -1 || kBudSSLClientIndex == -1 || kBudSSLSNIIndex == -1 || kBudSSLTicketKeyIndex == -1) { err = bud_error(kBudErrNoSSLIndex); goto fatal; } } #ifndef SSL_CTRL_SET_TLSEXT_SERVERNAME_CB if (config->context_count != 0) { err = bud_error(kBudErrSNINotSupported); goto fatal; } #endif /* !SSL_CTRL_SET_TLSEXT_SERVERNAME_CB */ /* Allocate workers */ if (!config->is_worker && config->worker_count != 0) { config->workers = calloc(config->worker_count, sizeof(*config->workers)); if (config->workers == NULL) { err = bud_error_str(kBudErrNoMem, "workers"); goto fatal; } } /* Initialize logger */ config->logger = bud_logger_new(config, &err); if (!bud_is_ok(err)) goto fatal; err = bud_config_init_tracing(&config->trace); if (!bud_is_ok(err)) goto fatal; if (config->is_worker || config->worker_count == 0) { /* Connect to SNI server */ if (config->sni.enabled) { config->sni.pool = bud_http_pool_new(config, config->sni.host, config->sni.port, &err); if (config->sni.pool == NULL) goto fatal; } /* Connect to OCSP Stapling server */ if (config->stapling.enabled) { config->stapling.pool = bud_http_pool_new(config, config->stapling.host, config->stapling.port, &err); if (config->stapling.pool == NULL) goto fatal; } } /* Init all contexts */ for (i = 0; i < config->context_count + 1; i++) { err = bud_context_init(config, &config->contexts[i]); if (!bud_is_ok(err)) goto fatal; } return bud_ok(); fatal: /* Free all allocated contexts */ do bud_context_free(&config->contexts[i--]); while (i >= 0); return err; }
bud_error_t bud_config_init(bud_config_t* config) { int i; int r; bud_context_t* ctx; bud_error_t err; const char* cert_file; const char* key_file; BIO* cert_bio; i = 0; /* Get addresses of frontend and backend */ r = bud_config_str_to_addr(config->frontend.host, config->frontend.port, &config->frontend.addr); if (r != 0) { err = bud_error_num(kBudErrPton, r); goto fatal; } r = bud_config_str_to_addr(config->backend.host, config->backend.port, &config->backend.addr); if (r != 0) { err = bud_error_num(kBudErrPton, r); goto fatal; } /* Get indexes for SSL_set_ex_data()/SSL_get_ex_data() */ if (kBudSSLClientIndex == -1) { kBudSSLClientIndex = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL); kBudSSLSNIIndex = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL); if (kBudSSLClientIndex == -1 || kBudSSLSNIIndex == -1) goto fatal; } #ifndef SSL_CTRL_SET_TLSEXT_SERVERNAME_CB if (config->context_count != 0) { err = bud_error(kBudErrSNINotSupported); goto fatal; } #endif /* !SSL_CTRL_SET_TLSEXT_SERVERNAME_CB */ /* Allocate workers */ if (!config->is_worker) { config->workers = calloc(config->worker_count, sizeof(*config->workers)); if (config->workers == NULL) { err = bud_error_str(kBudErrNoMem, "workers"); goto fatal; } } /* Initialize logger */ err = bud_logger_new(config); if (!bud_is_ok(err)) goto fatal; if (config->is_worker || config->worker_count == 0) { /* Connect to SNI server */ if (config->sni.enabled) { config->sni.pool = bud_http_pool_new(config, config->sni.host, config->sni.port, &err); if (config->sni.pool == NULL) goto fatal; } /* Connect to OCSP Stapling server */ if (config->stapling.enabled) { config->stapling.pool = bud_http_pool_new(config, config->stapling.host, config->stapling.port, &err); if (config->stapling.pool == NULL) goto fatal; } } /* Load all contexts */ for (i = 0; i < config->context_count + 1; i++) { ctx = &config->contexts[i]; err = bud_config_new_ssl_ctx(config, ctx); if (!bud_is_ok(err)) goto fatal; /* Default context */ if (i == 0) { cert_file = config->frontend.cert_file; key_file = config->frontend.key_file; } else { cert_file = ctx->cert_file; key_file = ctx->key_file; } cert_bio = BIO_new_file(cert_file, "r"); if (cert_bio == NULL) { err = bud_error_str(kBudErrLoadCert, cert_file); goto fatal; } r = bud_context_use_certificate_chain(ctx, cert_bio); BIO_free_all(cert_bio); if (!r) { err = bud_error_str(kBudErrParseCert, cert_file); goto fatal; } if (!SSL_CTX_use_PrivateKey_file(ctx->ctx, key_file, SSL_FILETYPE_PEM)) { err = bud_error_str(kBudErrParseKey, key_file); goto fatal; } } return bud_ok(); fatal: /* Free all allocated contexts */ do bud_context_free(&config->contexts[i--]); while (i >= 0); return err; }
bud_error_t bud_config_new_ssl_ctx(bud_config_t* config, bud_context_t* context) { SSL_CTX* ctx; int ecdh_nid; EC_KEY* ecdh; bud_error_t err; int options; int r; const char* ticket_key; size_t max_len; if (context->backend != NULL) { if (context->backend->keepalive == -1) context->backend->keepalive = kBudDefaultKeepalive; r = bud_config_str_to_addr(context->backend->host, context->backend->port, &context->backend->addr); if (r != 0) return bud_error_num(kBudErrPton, r); } /* Decode ticket_key */ ticket_key = context->ticket_key == NULL ? config->frontend.ticket_key : context->ticket_key; if (ticket_key != NULL) { max_len = sizeof(context->ticket_key_storage); if (bud_base64_decode(context->ticket_key_storage, max_len, ticket_key, strlen(ticket_key)) < max_len) { return bud_error(kBudErrSmallTicketKey); } } /* Choose method, tlsv1_2 by default */ if (config->frontend.method == NULL) { if (strcmp(config->frontend.security, "tls1.1") == 0) config->frontend.method = TLSv1_1_server_method(); else if (strcmp(config->frontend.security, "tls1.0") == 0) config->frontend.method = TLSv1_server_method(); else if (strcmp(config->frontend.security, "tls1.2") == 0) config->frontend.method = TLSv1_2_server_method(); else if (strcmp(config->frontend.security, "ssl3") == 0) config->frontend.method = SSLv3_server_method(); else config->frontend.method = SSLv23_server_method(); } ctx = SSL_CTX_new(config->frontend.method); if (ctx == NULL) return bud_error_str(kBudErrNoMem, "SSL_CTX"); /* Disable sessions, they won't work with cluster anyway */ SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF); if (config->frontend.max_send_fragment) SSL_CTX_set_max_send_fragment(ctx, config->frontend.max_send_fragment); if (ticket_key != NULL) { SSL_CTX_set_tlsext_ticket_keys(ctx, context->ticket_key_storage, sizeof(context->ticket_key_storage)); } /* ECDH curve selection */ if (context->ecdh != NULL || config->frontend.ecdh != NULL) { if (context->ecdh != NULL) ecdh_nid = OBJ_sn2nid(context->ecdh); else ecdh_nid = OBJ_sn2nid(config->frontend.ecdh); if (ecdh_nid == NID_undef) { ecdh = NULL; err = bud_error_str(kBudErrECDHNotFound, context->ecdh == NULL ? config->frontend.ecdh : context->ecdh); goto fatal; } ecdh = EC_KEY_new_by_curve_name(ecdh_nid); if (ecdh == NULL) { err = bud_error_str(kBudErrNoMem, "EC_KEY"); goto fatal; } SSL_CTX_set_options(ctx, SSL_OP_SINGLE_ECDH_USE); SSL_CTX_set_tmp_ecdh(ctx, ecdh); EC_KEY_free(ecdh); } ecdh = NULL; /* Cipher suites */ if (context->ciphers != NULL) SSL_CTX_set_cipher_list(ctx, context->ciphers); else if (config->frontend.ciphers != NULL) SSL_CTX_set_cipher_list(ctx, config->frontend.ciphers); /* Disable SSL2 */ options = SSL_OP_NO_SSLv2 | SSL_OP_ALL; if (!config->frontend.ssl3) options |= SSL_OP_NO_SSLv3; if (config->frontend.server_preference) options |= SSL_OP_CIPHER_SERVER_PREFERENCE; SSL_CTX_set_options(ctx, options); #ifdef SSL_CTRL_SET_TLSEXT_SERVERNAME_CB if (config->context_count != 0) { SSL_CTX_set_tlsext_servername_callback(ctx, bud_config_select_sni_context); SSL_CTX_set_tlsext_servername_arg(ctx, config); } #endif /* SSL_CTRL_SET_TLSEXT_SERVERNAME_CB */ #ifdef OPENSSL_NPN_NEGOTIATED context->npn_line = bud_config_encode_npn(config, context->npn, &context->npn_line_len, &err); if (!bud_is_ok(err)) goto fatal; if (context->npn_line != NULL) { SSL_CTX_set_next_protos_advertised_cb(ctx, bud_config_advertise_next_proto, context); } #else /* !OPENSSL_NPN_NEGOTIATED */ err = bud_error(kBudErrNPNNotSupported); goto fatal; #endif /* OPENSSL_NPN_NEGOTIATED */ SSL_CTX_set_tlsext_status_cb(ctx, bud_client_stapling_cb); context->ctx = ctx; return bud_ok(); fatal: if (ecdh != NULL) EC_KEY_free(ecdh); SSL_CTX_free(ctx); return err; }