Skip to content
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
11 changes: 11 additions & 0 deletions docs/reference.rst
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,17 @@ Optional options:

Defaults to ``message``.

``send_download_authentication=<boolean>``
Whether to send authentication data (token or client certificate) for
download requests.
hawkBit can be configured to use external storage providers for artifact
downloads.
rauc-hawkbit-updater's default behavior is to send authentication data, same
as for all other DDI API requests.
Sending unexpected authentication data can lead to errors in such
configuration (e.g. on Azure Blob Storage or AWS S3).
Defaults to ``true``.

.. _keyring-section:

**[device] section**
Expand Down
1 change: 1 addition & 0 deletions include/config-file.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ typedef struct Config_ {
int low_speed_rate; /**< low speed limit to abort transfer */
GLogLevelFlags log_level; /**< log level */
GHashTable* device; /**< Additional attributes sent to hawkBit */
gboolean send_download_authentication; /**< Send security header in download requests */
} Config;

/**
Expand Down
6 changes: 6 additions & 0 deletions src/config-file.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ static const gboolean DEFAULT_SSL = TRUE;
static const gboolean DEFAULT_SSL_VERIFY = TRUE;
static const gboolean DEFAULT_REBOOT = FALSE;
static const gchar* DEFAULT_LOG_LEVEL = "message";
static const gboolean DEFAULT_SEND_DOWNLOAD_AUTHENTICATION = TRUE;

/**
* @brief Get string value from key_file for key in group, optional default_value can be specified
Expand Down Expand Up @@ -340,6 +341,11 @@ Config* load_config_file(const gchar *config_file, GError **error)
if (!get_key_bool(ini_file, "client", "post_update_reboot", &config->post_update_reboot, DEFAULT_REBOOT, error))
return NULL;

if (!get_key_bool(ini_file, "client", "send_download_authentication",
&config->send_download_authentication,
DEFAULT_SEND_DOWNLOAD_AUTHENTICATION, error))
return NULL;

if (config->timeout > 0 && config->connect_timeout > 0 &&
config->timeout < config->connect_timeout) {
g_set_error(error,
Expand Down
3 changes: 2 additions & 1 deletion src/hawkbit-client.c
Original file line number Diff line number Diff line change
Expand Up @@ -405,7 +405,8 @@ static gboolean get_binary(const gchar *download_url, const gchar *file, curl_of

curl_easy_setopt(curl, CURLOPT_RESUME_FROM_LARGE, resume_from);

if (!set_auth_curl(curl, &headers, error))
if (hawkbit_config->send_download_authentication &&
!set_auth_curl(curl, &headers, error))
return FALSE;

// set up request headers
Expand Down
19 changes: 19 additions & 0 deletions test/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -349,3 +349,22 @@ def mtls_download_port(nginx_proxy, ssl_issuer_hash):
"""
location_options = {"proxy_set_header X-Ssl-Issuer-Hash-1": ssl_issuer_hash}
return nginx_proxy(location_options, mtls=True)

@pytest.fixture
def download_without_auth_headers_port(tmp_path_factory, rauc_bundle, nginx_proxy):
"""
Runs an nginx proxy which requires artifact requests without authentication headers. HTTP
requests are forwarded to port 8080 (default port of the docker hawkBit instance). Returns the
port the proxy is running on. This port can be set in the rauc-hawkbit-updater config to test
downloads without auth headers.
"""
nginx_conf_dir = f'{os.path.dirname(os.path.abspath(__file__))}/nginx'

with open(f'{nginx_conf_dir}/download_without_auth_headers.inc.in') as f:
dl_without_auth_include_template = Template(f.read())

dl_without_auth_include_str = dl_without_auth_include_template.substitute(rauc_bundle=rauc_bundle)
dl_without_auth_config = tmp_path_factory.mktemp('nginx') / 'download_without_auth_headers.inc'
dl_without_auth_config.write_text(dl_without_auth_include_str)
location_options = {'include': dl_without_auth_config}
return nginx_proxy(location_options)
13 changes: 13 additions & 0 deletions test/nginx/download_without_auth_headers.inc.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# serves artifacts only if no auth headers are provided
location ~*/.*/controller/v1/test-target/softwaremodules/.*/artifacts/ {
content_by_lua_block {
local auth_header = ngx.req.get_headers()["authorization"]
if auth_header then
ngx.exit(ngx.HTTP_UNAUTHORIZED)
else
local file = io.open("${rauc_bundle}", "rb")
ngx.print(file:read("*all"))
file:close()
end
}
}
52 changes: 52 additions & 0 deletions test/test_download.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,3 +141,55 @@ def test_download_only(hawkbit, config, assign_bundle):

# check last status message
assert 'File checksum OK.' in status[0]['messages']

def test_download_only_with_auth_header(hawkbit, adjust_config, assign_bundle,
download_without_auth_headers_port):
"""
Test that rauc-hawkbit-updater fails when sending authentication header to external storage
provider with send_download_authentication=true.
"""
config = adjust_config({'client': {
'send_download_authentication': 'true',
'hawkbit_server': f'{hawkbit.host}:{download_without_auth_headers_port}'
}})
assign_bundle(params={'type': 'downloadonly'})

out, err, exitcode = run(f'rauc-hawkbit-updater -c "{config}" -r')
assert 'Start downloading' in out
assert 'hawkBit requested to skip installation, not invoking RAUC yet.' in out
assert 'Download failed: HTTP request failed: 401' in err
assert exitcode == 1

status = hawkbit.get_action_status()

assert status[0]['type'] == 'error'
# check last status message
assert 'Download failed: HTTP request failed: 401' in status[0]['messages']

def test_download_only_without_auth_header(hawkbit, adjust_config, assign_bundle,
download_without_auth_headers_port):
"""
Test that rauc-hawkbit-updater does not send authentication header with
send_download_authentication=false.
Test that rauc-hawkbit-updater succeeds when not sending authentication header to external
storage provider with send_download_authentication=false.
"""
config = adjust_config({'client': {
'send_download_authentication': 'false',
'hawkbit_server': f'{hawkbit.host}:{download_without_auth_headers_port}'
}})
assign_bundle(params={'type': 'downloadonly'})

out, err, exitcode = run(f'rauc-hawkbit-updater -c "{config}" -r')
assert 'Start downloading' in out
assert 'hawkBit requested to skip installation, not invoking RAUC yet.' in out
assert 'Download complete' in out
assert 'File checksum OK' in out
assert err == ''
assert exitcode == 0

status = hawkbit.get_action_status()

assert status[0]['type'] == 'downloaded'
# check last status message
assert 'File checksum OK.' in status[0]['messages']