Need a better way to update certificate.
Assume that we have listen socket bind certificate.
curl -X PUT --data-binary @bundle.pem http://.../certificates/bundle
{
"listeners": {
"127.0.0.1:8080": {
"application": "wsgi-app",
"tls": {
"certificate": "bundle"
}
}
}
}
If we need to change the certificate content, we have to do by the following steps.
- Delete the association of listen and certificate.
- Delete certificate.
- Put certificate.
- Set the certificate to the listen again.
Is it right?
If that, we need a more convinient way to finish it only by one step.
curl -X PUT --data-binary @bundle.pem http://.../certificates/bundle
Thanks.
Currently, you can do the following steps:
- Put a new certificate under the new name "bundle2";
- Switch listening socket to the new certificate (PUT "bundle2" /config/listeners/127.0.0.1:8080/tls/certificate);
- (Optionally) Delete the old one.
That was just easier to implement the way it works now. In the future we should make it more convenient, just in one step as you mentioned. It needs additional work.
@VBart take a look, please.
- Support pretty json in configure. Since we often look over conf.json file, it would be better to support a pretty format.
- Auto sends the current conf to router process if the exists cert file is updated.
diff -r aae6699f4eee src/nxt_controller.c
--- a/src/nxt_controller.c Tue Oct 29 16:07:21 2019 +0300
+++ b/src/nxt_controller.c Mon Nov 04 22:01:42 2019 +0800
@@ -80,6 +80,10 @@
nxt_controller_request_t *req, nxt_str_t *path);
static void nxt_controller_process_cert_save(nxt_task_t *task,
nxt_port_recv_msg_t *msg, void *data);
+static void nxt_controller_process_cert_update(nxt_task_t *task,
+ nxt_port_recv_msg_t *msg, void *data);
+static void nxt_controller_process_cert_handler(nxt_task_t *task,
+ nxt_port_recv_msg_t *msg, void *data);
static nxt_bool_t nxt_controller_cert_in_use(nxt_str_t *name);
#endif
static void nxt_controller_conf_handler(nxt_task_t *task,
@@ -353,12 +357,13 @@
nxt_controller_conf_send(nxt_task_t *task, nxt_conf_value_t *conf,
nxt_port_rpc_handler_t handler, void *data)
{
- size_t size;
- uint32_t stream;
- nxt_int_t rc;
- nxt_buf_t *b;
- nxt_port_t *router_port, *controller_port;
- nxt_runtime_t *rt;
+ size_t size;
+ uint32_t stream;
+ nxt_int_t rc;
+ nxt_buf_t *b;
+ nxt_port_t *router_port, *controller_port;
+ nxt_runtime_t *rt;
+ nxt_conf_json_pretty_t pretty;
rt = task->thread->runtime;
@@ -370,14 +375,18 @@
controller_port = rt->port_by_type[NXT_PROCESS_CONTROLLER];
- size = nxt_conf_json_length(conf, NULL);
+ nxt_memzero(&pretty, sizeof(nxt_conf_json_pretty_t));
+
+ size = nxt_conf_json_length(conf, &pretty);
b = nxt_port_mmap_get_buf(task, router_port, size);
if (nxt_slow_path(b == NULL)) {
return NXT_ERROR;
}
- b->mem.free = nxt_conf_json_print(b->mem.free, conf, NULL);
+ nxt_memzero(&pretty, sizeof(nxt_conf_json_pretty_t));
+
+ b->mem.free = nxt_conf_json_print(b->mem.free, conf, &pretty);
stream = nxt_port_rpc_register_handler(task, controller_port,
handler, handler,
@@ -1281,7 +1290,9 @@
if (nxt_str_eq(&req->parser.method, "PUT", 3)) {
value = nxt_cert_info_get(&name);
if (value != NULL) {
- goto exists_cert;
+ nxt_cert_store_get(task, &name, c->mem_pool,
+ nxt_controller_process_cert_update, req);
+ return;
}
cert = nxt_cert_mem(task, &c->read->mem);
@@ -1346,15 +1357,6 @@
nxt_controller_response(task, req, &resp);
return;
-exists_cert:
-
- resp.status = 400;
- resp.title = (u_char *) "Certificate already exists.";
- resp.offset = -1;
-
- nxt_controller_response(task, req, &resp);
- return;
-
cert_in_use:
resp.status = 400;
@@ -1394,6 +1396,72 @@
static void
+nxt_controller_process_cert_update(nxt_task_t *task, nxt_port_recv_msg_t *msg,
+ void *data)
+{
+ nxt_int_t ret;
+ nxt_conn_t *c;
+ nxt_buf_mem_t *mbuf;
+ nxt_controller_request_t *req;
+ nxt_controller_response_t resp;
+
+ req = data;
+
+ if (msg == NULL || msg->port_msg.type == _NXT_PORT_MSG_RPC_ERROR) {
+ goto fail;
+ }
+
+ c = req->conn;
+
+ mbuf = &c->read->mem;
+
+ nxt_fd_write(msg->fd, mbuf->pos, nxt_buf_mem_used_size(mbuf));
+
+ nxt_fd_close(msg->fd);
+
+ ret = nxt_controller_conf_send(task, nxt_controller_conf.root,
+ nxt_controller_process_cert_handler, req);
+
+ if (nxt_fast_path(ret == NXT_OK)) {
+ return;
+ }
+
+fail:
+
+ nxt_memzero(&resp, sizeof(nxt_controller_response_t));
+
+ resp.status = 500;
+ resp.title = (u_char *) "Failed to store certificate.";
+
+ nxt_controller_response(task, req, &resp);
+}
+
+
+static void
+nxt_controller_process_cert_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg,
+ void *data)
+{
+ nxt_controller_request_t *req;
+ nxt_controller_response_t resp;
+
+ req = data;
+
+ nxt_memzero(&resp, sizeof(nxt_controller_response_t));
+
+ if (msg->port_msg.type != NXT_PORT_MSG_RPC_READY) {
+ resp.status = 500;
+ resp.title = (u_char *) "Failed to store certificate.";
+
+ } else {
+ resp.status = 200;
+ resp.title = (u_char *) "Certificate chain uploaded.";
+ }
+
+ nxt_controller_response(task, req, &resp);
+}
+
+
+static void
nxt_controller_process_cert_save(nxt_task_t *task, nxt_port_recv_msg_t *msg,
void *data)
{
@hongzhidao
- The
statedirectory is a blackbox. Nobody should look or touch anything inside it. All the changes must be done only using API (otherwise we will face with endless consistency problems). The content of the directory will be changed over time and may turn into binary some day. Making it readable is against the concept. - One of major concepts of API configuration is that all the modifications must be atomic. From your patch it's not clear how do you revert to the previous certificate if anything goes wrong.
For those coming here looking for a way to use certbot with nginx-unit, here's what everyone is talking about above in code.
function check_or_die()
{
if [ "$1" != "200" ]; then
kill "$(cat /opt/unit/unit.pid)"
return 1
fi
}
function update_certificate()
{
bundle="/tmp/bundle-new"
( cat "$UNIT_PEM"; echo; cat "$UNIT_KEY_PEM" ) > "$bundle"
NAME="bundle"
RESP_CODE="$(curl \
--output /tmp/bundle \
--write-out '%{http_code}' \
--silent \
--request GET \
--data-binary "@${bundle}" \
--unix-socket $UNIT_SOCKET \
http://localhost/certificates/${NAME})"
if [ "$RESP_CODE" == "404" ]; then
RESP_CODE="$(
curl \
--output /dev/null \
--write-out '%{http_code}' \
--silent \
--request PUT \
--data-binary "@${bundle}" \
--unix-socket $UNIT_SOCKET \
http://localhost/certificates/${NAME}
)"
check_or_die "$RESP_CODE"
else
if diff -q /tmp/bundle /tmp/bundle-new; then
RESP_CODE="$(
curl \
--output /dev/null \
--write-out '%{http_code}' \
--silent \
--request PUT \
--data-binary "@${bundle}" \
--unix-socket $UNIT_SOCKET \
http://localhost/certificates/${NAME}-new
)"
check_or_die "$RESP_CODE"
RESP_CODE="$(
curl \
--output /dev/null \
--write-out '%{http_code}' \
--silent \
--request PUT \
--data "${NAME}-new" \
--unix-socket $UNIT_SOCKET \
http://localhost/config/listeners/0.0.0.0:8443/tls/certificates
)"
check_or_die "$RESP_CODE"
RESP_CODE="$(
curl \
--output /dev/null \
--write-out '%{http_code}' \
--silent \
--request DELETE \
--unix-socket $UNIT_SOCKET \
http://localhost/certificates/${NAME}
)"
check_or_die "$RESP_CODE"
RESP_CODE="$(
curl \
--output /dev/null \
--write-out '%{http_code}' \
--silent \
--request PUT \
--data-binary "@${bundle}" \
--unix-socket $UNIT_SOCKET \
http://localhost/certificates/${NAME}
)"
check_or_die "$RESP_CODE"
RESP_CODE="$(
curl \
--output /dev/null \
--write-out '%{http_code}' \
--silent \
--request PUT \
--data "${NAME}" \
--unix-socket $UNIT_SOCKET \
http://localhost/config/listeners/0.0.0.0:8443/tls/certificates
)"
check_or_die "$RESP_CODE"
RESP_CODE="$(
curl \
--output /dev/null \
--write-out '%{http_code}' \
--silent \
--request DELETE \
--unix-socket $UNIT_SOCKET \
http://localhost/certificates/${NAME}-new
)"
check_or_die "$RESP_CODE"
fi
fi
}