Skip to content

Commit 350ba55

Browse files
authored
Merge pull request #174 from fardke/fardke/feature/disable_auth_header_for_downloads
Allow disabling Authorization for bundle downloads
2 parents f0928cd + 5227e50 commit 350ba55

7 files changed

Lines changed: 104 additions & 1 deletion

File tree

docs/reference.rst

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,17 @@ Optional options:
152152

153153
Defaults to ``message``.
154154

155+
``send_download_authentication=<boolean>``
156+
Whether to send authentication data (token or client certificate) for
157+
download requests.
158+
hawkBit can be configured to use external storage providers for artifact
159+
downloads.
160+
rauc-hawkbit-updater's default behavior is to send authentication data, same
161+
as for all other DDI API requests.
162+
Sending unexpected authentication data can lead to errors in such
163+
configuration (e.g. on Azure Blob Storage or AWS S3).
164+
Defaults to ``true``.
165+
155166
.. _keyring-section:
156167

157168
**[device] section**

include/config-file.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ typedef struct Config_ {
3333
int low_speed_rate; /**< low speed limit to abort transfer */
3434
GLogLevelFlags log_level; /**< log level */
3535
GHashTable* device; /**< Additional attributes sent to hawkBit */
36+
gboolean send_download_authentication; /**< Send security header in download requests */
3637
} Config;
3738

3839
/**

src/config-file.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ static const gboolean DEFAULT_SSL = TRUE;
1919
static const gboolean DEFAULT_SSL_VERIFY = TRUE;
2020
static const gboolean DEFAULT_REBOOT = FALSE;
2121
static const gchar* DEFAULT_LOG_LEVEL = "message";
22+
static const gboolean DEFAULT_SEND_DOWNLOAD_AUTHENTICATION = TRUE;
2223

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

344+
if (!get_key_bool(ini_file, "client", "send_download_authentication",
345+
&config->send_download_authentication,
346+
DEFAULT_SEND_DOWNLOAD_AUTHENTICATION, error))
347+
return NULL;
348+
343349
if (config->timeout > 0 && config->connect_timeout > 0 &&
344350
config->timeout < config->connect_timeout) {
345351
g_set_error(error,

src/hawkbit-client.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -405,7 +405,8 @@ static gboolean get_binary(const gchar *download_url, const gchar *file, curl_of
405405

406406
curl_easy_setopt(curl, CURLOPT_RESUME_FROM_LARGE, resume_from);
407407

408-
if (!set_auth_curl(curl, &headers, error))
408+
if (hawkbit_config->send_download_authentication &&
409+
!set_auth_curl(curl, &headers, error))
409410
return FALSE;
410411

411412
// set up request headers

test/conftest.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -349,3 +349,22 @@ def mtls_download_port(nginx_proxy, ssl_issuer_hash):
349349
"""
350350
location_options = {"proxy_set_header X-Ssl-Issuer-Hash-1": ssl_issuer_hash}
351351
return nginx_proxy(location_options, mtls=True)
352+
353+
@pytest.fixture
354+
def download_without_auth_headers_port(tmp_path_factory, rauc_bundle, nginx_proxy):
355+
"""
356+
Runs an nginx proxy which requires artifact requests without authentication headers. HTTP
357+
requests are forwarded to port 8080 (default port of the docker hawkBit instance). Returns the
358+
port the proxy is running on. This port can be set in the rauc-hawkbit-updater config to test
359+
downloads without auth headers.
360+
"""
361+
nginx_conf_dir = f'{os.path.dirname(os.path.abspath(__file__))}/nginx'
362+
363+
with open(f'{nginx_conf_dir}/download_without_auth_headers.inc.in') as f:
364+
dl_without_auth_include_template = Template(f.read())
365+
366+
dl_without_auth_include_str = dl_without_auth_include_template.substitute(rauc_bundle=rauc_bundle)
367+
dl_without_auth_config = tmp_path_factory.mktemp('nginx') / 'download_without_auth_headers.inc'
368+
dl_without_auth_config.write_text(dl_without_auth_include_str)
369+
location_options = {'include': dl_without_auth_config}
370+
return nginx_proxy(location_options)
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# serves artifacts only if no auth headers are provided
2+
location ~*/.*/controller/v1/test-target/softwaremodules/.*/artifacts/ {
3+
content_by_lua_block {
4+
local auth_header = ngx.req.get_headers()["authorization"]
5+
if auth_header then
6+
ngx.exit(ngx.HTTP_UNAUTHORIZED)
7+
else
8+
local file = io.open("${rauc_bundle}", "rb")
9+
ngx.print(file:read("*all"))
10+
file:close()
11+
end
12+
}
13+
}

test/test_download.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,3 +141,55 @@ def test_download_only(hawkbit, config, assign_bundle):
141141

142142
# check last status message
143143
assert 'File checksum OK.' in status[0]['messages']
144+
145+
def test_download_only_with_auth_header(hawkbit, adjust_config, assign_bundle,
146+
download_without_auth_headers_port):
147+
"""
148+
Test that rauc-hawkbit-updater fails when sending authentication header to external storage
149+
provider with send_download_authentication=true.
150+
"""
151+
config = adjust_config({'client': {
152+
'send_download_authentication': 'true',
153+
'hawkbit_server': f'{hawkbit.host}:{download_without_auth_headers_port}'
154+
}})
155+
assign_bundle(params={'type': 'downloadonly'})
156+
157+
out, err, exitcode = run(f'rauc-hawkbit-updater -c "{config}" -r')
158+
assert 'Start downloading' in out
159+
assert 'hawkBit requested to skip installation, not invoking RAUC yet.' in out
160+
assert 'Download failed: HTTP request failed: 401' in err
161+
assert exitcode == 1
162+
163+
status = hawkbit.get_action_status()
164+
165+
assert status[0]['type'] == 'error'
166+
# check last status message
167+
assert 'Download failed: HTTP request failed: 401' in status[0]['messages']
168+
169+
def test_download_only_without_auth_header(hawkbit, adjust_config, assign_bundle,
170+
download_without_auth_headers_port):
171+
"""
172+
Test that rauc-hawkbit-updater does not send authentication header with
173+
send_download_authentication=false.
174+
Test that rauc-hawkbit-updater succeeds when not sending authentication header to external
175+
storage provider with send_download_authentication=false.
176+
"""
177+
config = adjust_config({'client': {
178+
'send_download_authentication': 'false',
179+
'hawkbit_server': f'{hawkbit.host}:{download_without_auth_headers_port}'
180+
}})
181+
assign_bundle(params={'type': 'downloadonly'})
182+
183+
out, err, exitcode = run(f'rauc-hawkbit-updater -c "{config}" -r')
184+
assert 'Start downloading' in out
185+
assert 'hawkBit requested to skip installation, not invoking RAUC yet.' in out
186+
assert 'Download complete' in out
187+
assert 'File checksum OK' in out
188+
assert err == ''
189+
assert exitcode == 0
190+
191+
status = hawkbit.get_action_status()
192+
193+
assert status[0]['type'] == 'downloaded'
194+
# check last status message
195+
assert 'File checksum OK.' in status[0]['messages']

0 commit comments

Comments
 (0)