8000 send headers for InspectBundle and add support for a system-version HTTP header by jluebbe · Pull Request #1654 · rauc/rauc · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

send headers for InspectBundle and add support for a system-version HTTP header #1654

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Apr 3, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions docs/reference.rst
Original file line number Diff line number Diff line change
Expand Up @@ -363,11 +363,24 @@ For more information about using the streaming support of RAUC, refer to

.. note:: The machine ID should be considered "confidential" and thus not
be used over unauthenticated connections or with untrusted servers!
* ``system-version``: Enables sending the *system version* as ``RAUC-System-Version`` header field.
* ``serial``: Enables sending the *system serial* as ``RAUC-Serial`` header field.
* ``variant``: Enables sending the *variant* as ``RAUC-Variant`` header field.
* ``transaction-id``: Enables sending the *transaction UUID* as ``RAUC-Transaction-ID`` header field.
* ``uptime``: Enables sending the system's current uptime as ``RAUC-Uptime`` header field.

These headers are sent on the initial HTTP request when streaming a bundle,
e.g. when using `rauc install` or `rauc info` (or their corresponding D-Bus
calls).
They are *not* sent for the subsequent requests which fetch the actual bundle
data.

.. note::
Currently, when using ``rauc info`` without the service or without an
explicit config file, most headers are not sent, because the
``system-info`` handler is not used and the status information is not
loaded.

``[encryption]`` Section
~~~~~~~~~~~~~~~~~~~~~~~~

Expand Down
14 changes: 13 additions & 1 deletion include/bundle.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ typedef struct {
gchar *tls_ca;
gboolean tls_no_verify;
GStrv http_headers;
GStrv http_info_headers;
GPtrArray *http_info_headers;
} RaucBundleAccessArgs;

typedef struct {
Expand Down Expand Up @@ -276,6 +276,18 @@ void free_bundle(RaucBundle *bundle);

G_DEFINE_AUTOPTR_CLEANUP_FUNC(RaucBundle, free_bundle);

/**
* Assembles HTTP Headers for use in the initial streaming request, based on the
* selection in the system config. Additional headers can be added to the
* GPtrArray later.
*
* @param transaction currently running installation transaction or NULL
*
* @return newly allocated GPtrArray with HTTP Header strings
*/
GPtrArray *assemble_info_headers(const gchar *transaction)
G_GNUC_WARN_UNUSED_RESULT;

/**
* Frees the memory pointed to by the RaucBundleAccessArgs, but not the
* structure itself.
Expand Down
2 changes: 1 addition & 1 deletion include/nbd.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ typedef struct {
gchar *tls_ca; /* local file */
gboolean tls_no_verify;
GStrv headers; /* array of strings such as 'Foo: bar' */
GStrv info_headers; /* array of strings such as 'Foo: bar' */
GPtrArray *info_headers; /* array of strings such as 'Foo: bar' */

/* discovered information */
guint64 data_size; /* bundle size */
Expand Down
2 changes: 1 addition & 1 deletion qemu-test-init
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ if type nginx; then
export RAUC_TEST_HTTP_SERVER=1
fi

if python3 test/nginx_backend.py; then
if python3 test/nginx_backend.py -s /tmp/backend.sock -d; then
export RAUC_TEST_HTTP_BACKEND=1
fi

Expand Down
91 changes: 89 additions & 2 deletions src/bundle.c
Original file line number Diff line number Diff line change
Expand Up @@ -2343,7 +2343,8 @@ gboolean check_bundle(const gchar *bundlename, RaucBundle **bundle, CheckBundleP
ibundle->nbd_srv->tls_ca = g_strdup(access_args->tls_ca);
ibundle->nbd_srv->tls_no_verify = access_args->tls_no_verify;
ibundle->nbd_srv->headers = g_strdupv(access_args->http_headers);
ibundle->nbd_srv->info_headers = g_strdupv(access_args->http_info_headers);
if (access_args->http_info_headers)
ibundle->nbd_srv->info_headers = g_ptr_array_ref(access_args->http_info_headers);
}
if (!ibundle->nbd_srv->tls_cert)
ibundle->nbd_srv->tls_cert = g_strdup(r_context()->config->streaming_tls_cert);
Expand Down Expand Up @@ -3276,14 +3277,100 @@ void free_bundle(RaucBundle *bundle)
g_free(bundle);
}

static gchar* get_uptime(void)
{
g_autofree gchar *contents = NULL;
g_autoptr(GError) ierror = NULL;
g_auto(GStrv) uptime = NULL;

if (!g_file_get_contents("/proc/uptime", &contents, NULL, &ierror)) {
g_warning("Failed to get uptime: %s", ierror->message);
return NULL;
}

/* file contains two values and a newline, 'chomp' in-place and split then */
uptime = g_strsplit(g_strchomp(contents), " ", 2);

return g_strdup(uptime[0]);
}

/* If the input key starts with RAUC_HTTP_, it returns a valid HTTP header
* string with 'RAUC_HTTP_' replaced by 'RAUC-'.
* If the input string does not start with RAUC_HTTP_, NULL is returned.
*/
static gchar *system_info_to_header(const gchar *key, const gchar *value)
{
g_autofree gchar *header_key = NULL;

g_return_val_if_fail(key, NULL);
g_return_val_if_fail(value, NULL);

if (!g_str_has_prefix(key, "RAUC_HTTP_"))
return NULL;

header_key = g_strdup(key + strlen("RAUC_HTTP_"));
for (size_t i = 0; i < strlen(header_key); i++) {
if (header_key[i] == '_')
header_key[i] = '-';
}

return g_strdup_printf("RAUC-%s: %s", header_key, value);
}

GPtrArray *assemble_info_headers(const gchar *transaction)
{
g_autoptr(GPtrArray) headers = g_ptr_array_new_with_free_func(g_free);

if (!r_context()->config->enabled_headers)
goto no_std_headers;

for (gchar **header = r_context()->config->enabled_headers; *header; header++) {
/* Add static system information */
if (g_strcmp0(*header, "boot-id") == 0 && r_context()->boot_id)
g_ptr_array_add(headers, g_strdup_printf("RAUC-Boot-ID: %s", r_context()->boot_id));
if (g_strcmp0(*header, "machine-id") == 0 && r_context()->machine_id)
g_ptr_array_add(headers, g_strdup_printf("RAUC-Machine-ID: %s", r_context()->machine_id));
if (g_strcmp0(*header, "system-version") == 0 && r_context()->system_version)
g_ptr_array_add(headers, g_strdup_printf("RAUC-System-Version: %s", r_context()->system_version));
if (g_strcmp0(*header, "serial") == 0 && r_context()->system_serial)
g_ptr_array_add(headers, g_strdup_printf("RAUC-Serial: %s", r_context()->system_serial));
if (g_strcmp0(*header, "variant") == 0 && r_context()->config->system_variant)
g_ptr_array_add(headers, g_strdup_printf("RAUC-Variant: %s", r_context()->config->system_variant));
/* Add per-installation information */
if (g_strcmp0(*header, "transaction-id") == 0 && transaction)
g_ptr_array_add(headers, g_strdup_printf("RAUC-Transaction-ID: %s", transaction));
/* Add live information */
if (g_strcmp0(*header, "uptime") == 0) {
g_autofree gchar *uptime = get_uptime();
g_ptr_array_add(headers, g_strdup_printf("RAUC-Uptime: %s", uptime));
}
}

no_std_headers:

if (r_context()->system_info) {
GHashTableIter iter;
gchar *key = NULL;
gchar *value = NULL;

g_hash_table_iter_init(&iter, r_context()->system_info);
while (g_hash_table_iter_next(&iter, (gpointer*) &key, (gpointer*) &value)) {
gchar *header = system_info_to_header(key, value);
if (header)
g_ptr_array_add(headers, header);
}
}

return g_steal_pointer(&headers);
}
void clear_bundle_access_args(RaucBundleAccessArgs *access_args)
{
if (ENABLE_STREAMING) {
g_free(access_args->tls_cert);
g_free(access_args->tls_key);
g_free(access_args->tls_ca);
g_strfreev(access_args->http_headers);
g_strfreev(access_args->http_info_headers);
g_clear_pointer(&access_args->http_info_headers, g_ptr_array_unref);
}

memset(access_args, 0, sizeof(*access_args));
Expand Down
101 changes: 11 additions & 90 deletions src/install.c
Original file line number Diff line number Diff line change
Expand Up @@ -1486,94 +1486,6 @@ static gboolean launch_and_wait_default_handler(RaucInstallArgs *args, gchar* bu
return TRUE;
}

static gchar* get_uptime(void)
{
g_autofree gchar *contents = NULL;
g_autoptr(GError) ierror = NULL;
g_auto(GStrv) uptime = NULL;

if (!g_file_get_contents("/proc/uptime", &contents, NULL, &ierror)) {
g_warning("Failed to get uptime: %s", ierror->message);
return NULL;
}

/* file contains two values and a newline, 'chomp' in-place and split then */
uptime = g_strsplit(g_strchomp(contents), " ", 2);

return g_strdup(uptime[0]);
}

/* If the input key starts with RAUC_HTTP_, it returns a valid HTTP header
* string with 'RAUC_HTTP_' replaced by 'RAUC-'.
* If the input string does not start with RAUC_HTTP_, NULL is returned.
*/
static gchar *system_info_to_header(const gchar *key, const gchar *value)
{
g_autofree gchar *header_key = NULL;

g_return_val_if_fail(key, NULL);
g_return_val_if_fail(value, NULL);

if (!g_str_has_prefix(key, "RAUC_HTTP_"))
return NULL;

header_key = g_strdup(key + strlen("RAUC_HTTP_"));
for (size_t i = 0; i < strlen(header_key); i++) {
if (header_key[i] == '_')
header_key[i] = '-';
}

return g_strdup_printf("RAUC-%s: %s", header_key, value);
}

static gchar **assemble_info_headers(RaucInstallArgs *args)
{
GPtrArray *headers = g_ptr_array_new_with_free_func(g_free);

g_return_val_if_fail(args, NULL);

if (!r_context()->config->enabled_headers)
goto no_std_headers;

for (gchar **header = r_context()->config->enabled_headers; *header; header++) {
/* Add static system information */
if (g_strcmp0(*header, "boot-id") == 0)
g_ptr_array_add(headers, g_strdup_printf("RAUC-Boot-ID: %s", r_context()->boot_id));
if (g_strcmp0(*header, "machine-id") == 0)
g_ptr_array_add(headers, g_strdup_printf("RAUC-Machine-ID: %s", r_context()->machine_id));
if (g_strcmp0(*header, "serial") == 0)
g_ptr_array_add(headers, g_strdup_printf("RAUC-Serial: %s", r_context()->system_serial));
if (g_strcmp0(*header, "variant") == 0)
g_ptr_array_add(headers, g_strdup_printf("RAUC-Variant: %s", r_context()->config->system_variant));
/* Add per-installation information */
if (g_strcmp0(*header, "transaction-id") == 0)
g_ptr_array_add(headers, g_strdup_printf("RAUC-Transaction-ID: %s", args->transaction));
/* Add live information */
if (g_strcmp0(*header, "uptime") == 0) {
g_autofree gchar *uptime = get_uptime();
g_ptr_array_add(headers, g_strdup_printf("RAUC-Uptime: %s", uptime));
}
}

no_std_headers:

if (r_context()->system_info) {
GHashTableIter iter;
gchar *key = NULL;
gchar *value = NULL;

g_hash_table_iter_init(&iter, r_context()->system_info);
while (g_hash_table_iter_next(&iter, (gpointer*) &key, (gpointer*) &value)) {
gchar *header = system_info_to_header(key, value);
if (header)
g_ptr_array_add(headers, header);
}
}
g_ptr_array_add(headers, NULL);

return (gchar**) g_ptr_array_free(headers, FALSE);
}

gboolean do_install_bundle(RaucInstallArgs *args, GError **error)
{
const gchar* bundlefile = args->name;
Expand Down Expand Up @@ -1605,7 +1517,7 @@ gboolean do_install_bundle(RaucInstallArgs *args, GError **error)
// TODO: mount info in context ?
install_args_update(args, "Checking and mounting bundle...");

args->access_args.http_info_headers = assemble_info_headers(args);
args->access_args.http_info_headers = assemble_info_headers(args->transaction);

res = check_bundle(bundlefile, &bundle, CHECK_BUNDLE_DEFAULT, &args->access_args, &ierror);
if (!res) {
Expand Down Expand Up @@ -1810,7 +1722,16 @@ gboolean install_run(RaucInstallArgs *args)
return TRUE;
}

static const gchar *supported_http_headers[] = {"boot-id", "transaction-id", "machine-id", "serial", "variant", "uptime", NULL};
static const gchar *supported_http_headers[] = {
"boot-id",
"transaction-id",
"machine-id",
"system-version",
"serial",
"variant",
"uptime",
NULL
};

gboolean r_install_is_supported_http_header(const gchar *header)
{
Expand Down
3 changes: 3 additions & 0 deletions src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -1307,6 +1307,9 @@ static gboolean info_start(int argc, char **argv)
if (no_check_time)
check_bundle_params |= CHECK_BUNDLE_NO_CHECK_TIME;

g_assert(access_args.http_info_headers == NULL);
access_args.http_info_headers = assemble_info_headers(NULL);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the scenario this would be useful for? When using the CLI, the context information will not be ready. When using RAUC without the service, this might not be relevant anymore (since e.g. polling will not work).

Copy link
Member Author
@jluebbe jluebbe Mar 31, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For rauc install, r_context_configure_target() is called even without the service, as it uses R_CONTEXT_CONFIG_MODE_REQUIRED. For rauc info (which uses _AUTO), the target information (mainly the system-info handler) is not loaded unless an explicit config file is given. So we probably only get the uptime.

We could change that by calling r_context_configure_target() in more cases, but that can be left for later. For now, I've documented it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Documenting this clearly might be sufficient for now.


res = check_bundle(bundlelocation, &bundle, check_bundle_params, &access_args, &error);
if (!res) {
g_printerr("%s\n", error->message);
Expand Down
5 changes: 3 additions & 2 deletions src/nbd.c
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ void r_nbd_free_server(RaucNBDServer *nbd_srv)
g_free(nbd_srv->tls_key);
g_free(nbd_srv->tls_ca);
g_strfreev(nbd_srv->headers);
g_strfreev(nbd_srv->info_headers);
g_clear_pointer(&nbd_srv->info_headers, g_ptr_array_unref);
g_free(nbd_srv->effective_url);
g_free(nbd_srv);
}
Expand Down Expand Up @@ -1025,7 +1025,8 @@ static gboolean nbd_configure(RaucNBDServer *nbd_srv, GError **error)
if (nbd_srv->headers)
g_variant_dict_insert(&dict, "headers", "^as", nbd_srv->headers);
if (nbd_srv->info_headers)
g_variant_dict_insert(&dict, "info-headers", "^as", nbd_srv->info_headers);
g_variant_dict_insert(&dict, "info-headers", "@as",
g_variant_new_strv((const gchar **)nbd_srv->info_headers->pdata, nbd_srv->info_headers->len));
v = g_variant_dict_end(&dict);
{
g_autofree gchar *tmp = g_variant_print(v, TRUE);
Expand Down
3 changes: 3 additions & 0 deletions src/service.c
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,9 @@ static gboolean r_on_handle_inspect_bundle(RInstaller *interface,
goto out;
}

g_assert(access_args.http_info_headers == NULL);
access_args.http_info_headers = assemble_info_headers(NULL);

res = check_bundle(arg_bundle, &bundle, CHECK_BUNDLE_DEFAULT, &access_args, &error);
if (!res) {
message = g_strdup(error->message);
Expand Down
Loading
Loading
0