Skip to content

out_azure_blob: append is sent without the SAS signature after the blob is created (regression since 3.0.7) #12636

Open

Description

@mcampbel-ext

Bug Report

Describe the bug

With auth_type sas and the default append blob type, the first append to every new blob goes out without the sig parameter of the SAS token, so the service rejects it (403 on Azurite, 401 NoAuthenticationInformation on Azure Storage). The chunk is then retried and the retry succeeds, so data normally arrives, but every new blob costs one failed request, an [error] line and a delay of one retry interval.

It is a regression: 3.0.7 is not affected, 5.1.4 is, and the code involved is unchanged on master.

To Reproduce

  1. Start Azurite and create an account SAS signed with its well-known development key:
docker network create azt
docker run -d --name azurite --network azt mcr.microsoft.com/azure-storage/azurite \
  azurite-blob --blobHost 0.0.0.0 --skipApiVersionCheck --loose
# sas.py: prints the SAS token
import base64, hmac, hashlib, urllib.parse
KEY = "Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw=="
acct, sv, sp, ss, srt, se, spr = "devstoreaccount1", "2021-08-06", "racwdl", "b", "sco", "2031-01-01T00:00:00Z", "https,http"
sts = "\n".join([acct, sp, ss, srt, "", se, "", spr, sv, ""]) + "\n"
sig = base64.b64encode(hmac.new(base64.b64decode(KEY), sts.encode(), hashlib.sha256).digest()).decode()
print(urllib.parse.urlencode({"sv": sv, "ss": ss, "srt": srt, "sp": sp, "se": se, "spr": spr, "sig": sig}, quote_via=urllib.parse.quote))
  1. Run Fluent Bit with this configuration (sas_token is the output of sas.py):
[SERVICE]
    Flush 1
    Log_Level info

[INPUT]
    Name  dummy
    Dummy {"message":"hello"}
    Rate  1

[OUTPUT]
    Name                  azure_blob
    Match                 *
    account_name          devstoreaccount1
    auth_type             sas
    sas_token             <output of sas.py>
    container_name        test
    path                  logs
    auto_create_container on
    emulator_mode         on
    endpoint              http://azurite:10000
    tls                   off
docker run --network azt -v $PWD/fluent-bit.conf:/fluent-bit/etc/fluent-bit.conf:ro fluent/fluent-bit:5.1.4

Expected behavior

The append that follows "blob created successfully" carries the full SAS token and succeeds.

Actual behavior

[ info] [output:azure_blob:azure_blob.0] container 'test' created sucessfully
[ info] [output:azure_blob:azure_blob.0] blob not found: /devstoreaccount1/test/logs/dummy.0?comp=appendblock&sv=2021-08-06&...
[ info] [output:azure_blob:azure_blob.0] blob created successfully: /devstoreaccount1/test/logs/dummy.0?sv=2021-08-06&...
[error] [output:azure_blob:azure_blob.0] http_status=403 cannot append content to blob
[ warn] [engine] failed to flush chunk '1-1792673448.64011297.flb', retry in 7 seconds: ...

The requests Azurite received for the first blob, showing only the parameter names:

PUT dummy.0  comp&sv&ss&srt&sp&se&spr&sig   -> 404
PUT dummy.0  sv&ss&srt&sp&se&spr&sig        -> 201   (create blob)
PUT dummy.0  comp&sv&ss&srt&sp&se&spr       -> 403   (append: no sig)
PUT dummy.0  comp&sv&ss&srt&sp&se&spr&sig   -> 201   (the retry, a fresh URI)

With fluent/fluent-bit:3.0.7 the same configuration produces no error, and the append after the create is signed. With auth_type key there is nothing to truncate and it does not happen.

Cause

http_send_blob() hides the signature from the log line by writing a NUL into the request URI (plugins/out_azure_blob/azure_blob.c, v5.1.4 line 1030, in the 404 branch):

/* delete "&sig=..." in the c->uri for security */
char *p = strstr(c->uri, "&sig=");
if (p) {
    *p = '\0';
}
flb_plg_info(ctx->ins, "blob not found: %s", c->uri);

flb_http_client() does not copy the URI (src/flb_http_client.c: c->uri = uri;), so c->uri is the buffer send_blob() allocated, and that string is now cut before &sig=. send_blob() then creates the blob and calls http_send_blob() again with the same uri (line 1177 onwards):

else if (ret == CREATE_BLOB) {
    ret = create_blob(ctx, path_prefix, name);
    if (ret == FLB_OK) {
        ret = http_send_blob(config, ctx, ref_name, uri, block_id, event_type, data, bytes);

In 3.0.7 the equivalent code called send_blob() again, which rebuilt the URI, so the redaction was harmless there.

Suggested fix

Redact a copy instead of the live request, e.g. log with a length: flb_plg_info(ctx->ins, "blob not found: %.*s", p ? (int) (p - c->uri) : (int) strlen(c->uri), c->uri);, or duplicate the string first. The same strstr(c->uri, "&sig=") write appears in the 201 and 202 branches (lines 755 and 834); they do not reuse the buffer afterwards today, but they would break the same way if they did, so they are worth changing too. I am happy to send a PR.

Your Environment

  • Version used: 5.1.4 (official image); the code is the same on master (checked 2026-10-22)
  • Configuration: above
  • Environment name and version: Docker; Azurite for the reproduction, and the same 401 NoAuthenticationInformation seen against Azure Storage
  • Filters and plugins: dummy input, azure_blob output

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions