Skip to content
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

UserDelegationKey fails with 500 #2420

Open
NaridaL opened this issue Jul 2, 2024 · 7 comments
Open

UserDelegationKey fails with 500 #2420

NaridaL opened this issue Jul 2, 2024 · 7 comments
Assignees

Comments

@NaridaL
Copy link

NaridaL commented Jul 2, 2024

Which service(blob, file, queue, table) does this issue concern?

blob

Which version of the Azurite was used?

$ azurite --version
3.31.0

Where do you get Azurite? (npm, DockerHub, NuGet, Visual Studio Code Extension)

npm

What's the Node.js version?

node --version
v20.12.1

What problem was encountered?

Try to generate user delegated key, see log below.

Steps to reproduce the issue?

If possible, please provide the debug log using the -d parameter, replacing <pathtodebuglog> with an appropriate path for your OS, or review the instructions for docker containers:

2024-07-02T15:12:11.124Z 	 info: Azurite Blob service is starting on 127.0.0.1:10000
2024-07-02T15:12:11.127Z 	 info: AccountDataStore:init() Refresh accounts from environment variable AZURITE_ACCOUNTS with value *****
2024-07-02T15:12:11.191Z 	 info: BlobGCManager:start() Starting BlobGCManager. Set status to Initializing.
2024-07-02T15:12:11.192Z 	 info: BlobGCManager:start() Trigger mark and sweep loop. Set status to Running.
2024-07-02T15:12:11.192Z 	 info: BlobGCManager:markSweepLoop() Start next mark and sweep.
2024-07-02T15:12:11.192Z 	 info: BlobGCManager:markSweep() Get all extents.
2024-07-02T15:12:11.195Z 	 info: BlobGCManager:start() BlobGCManager successfully started.
2024-07-02T15:12:11.216Z 	 info: BlobGCManager:markSweep() Got 36 extents.
2024-07-02T15:12:11.216Z 	 info: BlobGCManager:markSweep() Get referred extents.
2024-07-02T15:12:11.230Z 	 info: BlobGCManager:markSweep() Got referred extents, unreferenced extents count is 0.
2024-07-02T15:12:11.230Z 	 info: BlobGCManager:markSweepLoop() Mark and sweep finished, taken 38ms.
2024-07-02T15:12:11.231Z 	 info: BlobGCManager:markSweepLoop() Sleep for 600000ms.
2024-07-02T15:12:11.235Z 	 info: Azurite Blob service successfully listens on https://127.0.0.1:10000
2024-07-02T15:12:42.541Z 9b77d658-d254-40ae-8d39-8a3b85a4e35c info: BlobStorageContextMiddleware: RequestMethod=POST RequestURL=https://localhost/eastus1/?restype=service&comp=userdelegationkey RequestHeaders:{"host":"localhost:10000","x-ms-version":"2024-05-04","accept":"application/xml","x-ms-client-request-id":"b976eac5-a468-40e4-ae8a-4a666a741fe4","x-ms-return-client-request-id":"true","user-agent":"azsdk-net-Storage.Blobs/12.20.0 (.NET 6.0.31; Microsoft Windows 10.0.22631)","authorization":"Bearer <fake token but removed anyway>","content-type":"application/xml","content-length":"94"} ClientIP=127.0.0.1 Protocol=https HTTPVersion=1.1
2024-07-02T15:12:42.541Z 9b77d658-d254-40ae-8d39-8a3b85a4e35c info: BlobStorageContextMiddleware: Account=eastus1 Container= Blob=
2024-07-02T15:12:42.542Z 9b77d658-d254-40ae-8d39-8a3b85a4e35c verbose: DispatchMiddleware: Dispatching request...
2024-07-02T15:12:42.543Z 9b77d658-d254-40ae-8d39-8a3b85a4e35c info: DispatchMiddleware: Operation=Service_GetUserDelegationKey
2024-07-02T15:12:42.544Z 9b77d658-d254-40ae-8d39-8a3b85a4e35c verbose: AuthenticationMiddlewareFactory:createAuthenticationMiddleware() Validating authentications.
2024-07-02T15:12:42.545Z 9b77d658-d254-40ae-8d39-8a3b85a4e35c info: PublicAccessAuthenticator:validate() Start validation against public access.
2024-07-02T15:12:42.545Z 9b77d658-d254-40ae-8d39-8a3b85a4e35c debug: PublicAccessAuthenticator:validate() Getting account properties...
2024-07-02T15:12:42.545Z 9b77d658-d254-40ae-8d39-8a3b85a4e35c debug: PublicAccessAuthenticator:validate() Retrieved account name from context: eastus1, container: , blob: 
2024-07-02T15:12:42.551Z 9b77d658-d254-40ae-8d39-8a3b85a4e35c debug: PublicAccessAuthenticator:validate() Skip public access authentication. Cannot get public access type for container 
2024-07-02T15:12:42.551Z 9b77d658-d254-40ae-8d39-8a3b85a4e35c info: BlobSharedKeyAuthenticator:validate() Start validation against account shared key authentication.
2024-07-02T15:12:42.551Z 9b77d658-d254-40ae-8d39-8a3b85a4e35c info: BlobSharedKeyAuthenticator:validate() Request doesn't include shared key authentication.
2024-07-02T15:12:42.551Z 9b77d658-d254-40ae-8d39-8a3b85a4e35c info: AccountSASAuthenticator:validate() Start validation against account Shared Access Signature pattern.
2024-07-02T15:12:42.551Z 9b77d658-d254-40ae-8d39-8a3b85a4e35c debug: AccountSASAuthenticator:validate() Getting account properties...
2024-07-02T15:12:42.551Z 9b77d658-d254-40ae-8d39-8a3b85a4e35c debug: AccountSASAuthenticator:validate() Retrieved account name from context: eastus1, container: , blob: 
2024-07-02T15:12:42.552Z 9b77d658-d254-40ae-8d39-8a3b85a4e35c debug: AccountSASAuthenticator:validate() Got account properties successfully.
2024-07-02T15:12:42.552Z 9b77d658-d254-40ae-8d39-8a3b85a4e35c debug: AccountSASAuthenticator:validate() Retrieved signature from URL parameter sig: undefined
2024-07-02T15:12:42.552Z 9b77d658-d254-40ae-8d39-8a3b85a4e35c info: AccountSASAuthenticator:validate() Failed to get valid account SAS values from request.
2024-07-02T15:12:42.552Z 9b77d658-d254-40ae-8d39-8a3b85a4e35c info: BlobSASAuthenticator:validate() Start validation against blob service Shared Access Signature pattern.
2024-07-02T15:12:42.552Z 9b77d658-d254-40ae-8d39-8a3b85a4e35c debug: BlobSASAuthenticator:validate() Getting account properties...
2024-07-02T15:12:42.552Z 9b77d658-d254-40ae-8d39-8a3b85a4e35c debug: BlobSASAuthenticator:validate() Retrieved account name from context: eastus1, container: , blob: 
2024-07-02T15:12:42.552Z 9b77d658-d254-40ae-8d39-8a3b85a4e35c debug: BlobSASAuthenticator:validate() Got account properties successfully.
2024-07-02T15:12:42.552Z 9b77d658-d254-40ae-8d39-8a3b85a4e35c debug: BlobSASAuthenticator:validate() Retrieved signature from URL parameter sig: undefined
2024-07-02T15:12:42.552Z 9b77d658-d254-40ae-8d39-8a3b85a4e35c debug: BlobSASAuthenticator:validate() No signature found in request. Skip blob service SAS validation.
2024-07-02T15:12:42.552Z 9b77d658-d254-40ae-8d39-8a3b85a4e35c info: BlobTokenAuthenticator:validate() Start validation against token authentication.
2024-07-02T15:12:42.553Z 9b77d658-d254-40ae-8d39-8a3b85a4e35c info: BlobTokenAuthenticator:authenticateBasic() Validation against token authentication successfully.
2024-07-02T15:12:42.553Z 9b77d658-d254-40ae-8d39-8a3b85a4e35c verbose: DeserializerMiddleware: Start deserializing...
2024-07-02T15:12:42.556Z 9b77d658-d254-40ae-8d39-8a3b85a4e35c debug: deserialize(): Raw request body string is (removed all empty characters) <KeyInfo><Start>2024-07-02T15:07:40Z</Start><Expiry>2024-07-09T15:12:40Z</Expiry></KeyInfo>
2024-07-02T15:12:42.560Z 9b77d658-d254-40ae-8d39-8a3b85a4e35c info: HandlerMiddleware: DeserializedParameters={"options":{"requestId":"b976eac5-a468-40e4-ae8a-4a666a741fe4"},"restype":"service","comp":"userdelegationkey","version":"2024-05-04","keyInfo":{"start":"2024-07-02T15:07:40Z","expiry":"2024-07-09T15:12:40Z"},"body":"ReadableStream"}
2024-07-02T15:12:42.561Z 9b77d658-d254-40ae-8d39-8a3b85a4e35c verbose: SerializerMiddleware: Start serializing...
2024-07-02T15:12:42.562Z 9b77d658-d254-40ae-8d39-8a3b85a4e35c error: ErrorMiddleware: Received an error, fill error information to HTTP response
2024-07-02T15:12:42.562Z 9b77d658-d254-40ae-8d39-8a3b85a4e35c error: ErrorMiddleware: ErrorName=Error ErrorMessage=UserDelegationKey.SignedOid cannot be null or undefined. ErrorStack="Error: UserDelegationKey.SignedOid cannot be null or undefined.\n    at Serializer.serialize (C:\\Users\\aleonhard\\AppData\\Roaming\\npm\\node_modules\\azurite\\node_modules\\@azure\\ms-rest-js\\dist\\msRest.node.js:510:19)\n    at serializeCompositeType (C:\\Users\\aleonhard\\AppData\\Roaming\\npm\\node_modules\\azurite\\node_modules\\@azure\\ms-rest-js\\dist\\msRest.node.js:911:50)\n    at Serializer.serialize (C:\\Users\\aleonhard\\AppData\\Roaming\\npm\\node_modules\\azurite\\node_modules\\@azure\\ms-rest-js\\dist\\msRest.node.js:547:27)\n    at serialize (C:\\Users\\aleonhard\\AppData\\Roaming\\npm\\node_modules\\azurite\\dist\\src\\blob\\generated\\utils\\serializer.js:185:36)\n    at serializerMiddleware (C:\\Users\\aleonhard\\AppData\\Roaming\\npm\\node_modules\\azurite\\dist\\src\\blob\\generated\\middleware\\serializer.middleware.js:27:32)\n    at C:\\Users\\aleonhard\\AppData\\Roaming\\npm\\node_modules\\azurite\\dist\\src\\blob\\generated\\ExpressMiddlewareFactory.js:91:49\n    at Layer.handle [as handle_request] (C:\\Users\\aleonhard\\AppData\\Roaming\\npm\\node_modules\\azurite\\node_modules\\express\\lib\\router\\layer.js:95:5)\n    at trim_prefix (C:\\Users\\aleonhard\\AppData\\Roaming\\npm\\node_modules\\azurite\\node_modules\\express\\lib\\router\\index.js:328:13)\n    at C:\\Users\\aleonhard\\AppData\\Roaming\\npm\\node_modules\\azurite\\node_modules\\express\\lib\\router\\index.js:286:9\n    at Function.process_params (C:\\Users\\aleonhard\\AppData\\Roaming\\npm\\node_modules\\azurite\\node_modules\\express\\lib\\router\\index.js:346:12)"
2024-07-02T15:12:42.562Z 9b77d658-d254-40ae-8d39-8a3b85a4e35c error: ErrorMiddleware: Set HTTP code: 500
2024-07-02T15:12:42.563Z 9b77d658-d254-40ae-8d39-8a3b85a4e35c info: EndMiddleware: End response. TotalTimeInMS=21 StatusCode=500 StatusMessage=undefined Headers={"server":"Azurite-Blob/3.31.0"}

Please be sure to remove any PII or sensitive information before sharing!
The debug log will log raw request headers and bodies, so that we can replay these against Azurite using REST and create tests to validate resolution.

Have you found a mitigation/solution?

not yet

@NaridaL
Copy link
Author

NaridaL commented Jul 2, 2024

OK, issue seems to be that the claims "tid" and "oid" were missing from by dummy auth token. These should probably be validated as part of the token validation.

For reference here is a TokenCredential which works:

namespace My.Test.Framework;

using System;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Security.Cryptography;
using System.Threading;
using System.Threading.Tasks;
using Azure.Core;
using Microsoft.IdentityModel.Tokens;

public class SelfSignedTokenCredential : TokenCredential
{
    private readonly TimeSpan _expiration = TimeSpan.FromHours(1);
    private readonly string _tenant = "aaaaaaaa-aaaa-aaaa-0000-aaaaaaaaaaaa"; 
    private readonly string _issuer = $"https://sts.windows.net/aaaaaaaa-aaaa-aaaa-0000-aaaaaaaaaaaa/";
    private readonly byte[] _secret = RandomNumberGenerator.GetBytes(32);

    public override async ValueTask<AccessToken> GetTokenAsync(
        TokenRequestContext requestContext,
        CancellationToken cancellationToken)
    {
        return GetToken(requestContext, cancellationToken);
    }

    public override AccessToken GetToken(TokenRequestContext requestContext, CancellationToken cancellationToken)
    {
        var audience = requestContext.Scopes.Length > 0 ? requestContext.Scopes[0] : "default-audience";
        audience = audience.Replace("//.default", "/");
        var expires = DateTimeOffset.UtcNow + _expiration;
        var token = GenerateJwtToken(audience, DateTime.UtcNow - TimeSpan.FromMinutes(5), expires.UtcDateTime);
        return new AccessToken(token, expires);
    }

    private string GenerateJwtToken(string audience, DateTime notBefore, DateTime? expires)
    {
        var securityKey = new SymmetricSecurityKey(_secret);
        var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);

        return new JwtSecurityTokenHandler().CreateEncodedJwt(
            issuer: _issuer, 
            audience: audience,
            subject: new ClaimsIdentity(new []
            {
                new Claim("oid", "c0ffee00-c0ff-eeee-0000-c0ffee000000"),
                new Claim("tid", _tenant),
            }),
            notBefore: notBefore,
            expires: expires,
            issuedAt: null,
            signingCredentials: credentials);
    }
}

@blueww
Copy link
Member

blueww commented Jul 3, 2024

@EmmaZhu

Would you please help to look at this issue?

@EmmaZhu
Copy link
Member

EmmaZhu commented Sep 12, 2024

Object ID and tenant ID are required to generate a user delegation key, they should always be included in the token credentials to access Azure Storage Service. Azurite's behavior is expected.

@NaridaL
Copy link
Author

NaridaL commented Sep 12, 2024 via email

@EmmaZhu
Copy link
Member

EmmaZhu commented Sep 13, 2024

With a token with invalid tenant id or object id, Azure would return 401 error like following:

HTTP/1.1 401 Server failed to authenticate the request. Please refer to the information in the www-authenticate header.
Content-Length: 414
Content-Type: application/xml
Server: Microsoft-HTTPAPI/2.0
x-ms-request-id: 2932de7b-701e-0051-45e8-05d02c000000
x-ms-error-code: InvalidAuthenticationInfo
WWW-Authenticate: Bearer authorization_uri=https://eastus2euap.login.microsoft.com/72f988bf-86f1-41af-91ab-2d7cd011db47/oauth2/authorize resource_id=https://storage.azure.com
Date: Fri, 13 Sep 2024 14:26:14 GMT
Connection: close

<?xml version="1.0" encoding="utf-8"?><Error><Code>InvalidAuthenticationInfo</Code><Message>Server failed to authenticate the request. Please refer to the information in the www-authenticate header.
RequestId:2932de7b-701e-0051-45e8-05d02c000000
Time:2024-09-13T14:26:14.5704756Z</Message><AuthenticationErrorDetail>Signature validation failed. Signature verification failed.</AuthenticationErrorDetail></Error>

401 error is for bearer token challenge logic, which Azurite cannot support. We'd need to discuss on Azurite's behavior when tid or oid is missing.

@NaridaL
Copy link
Author

NaridaL commented Sep 13, 2024

I don't follow your last point. The following file already includes various verifications on the bearer token claims:

It seems to me all that is missing are some checks there to ensure tid and oid are set.

https://github.com/Azure/Azurite/blob/76f626284e4b4b58b95065bb3c92351f30af7f3d/src/blob/authentication/BlobTokenAuthenticator.ts#L96C2-L96C34

@EmmaZhu
Copy link
Member

EmmaZhu commented Sep 14, 2024

We definitely should check whether tid and oid is set. The above message is just about what kind of message we should report. We'll discuss internally about it, and will update in this issue with any progress.

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

No branches or pull requests

3 participants