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

HDDS-6419. Provide better error message for malformed auth header #3167

Merged
merged 6 commits into from Mar 9, 2022

Conversation

symious
Copy link
Contributor

@symious symious commented Mar 5, 2022

What changes were proposed in this pull request?

For many errors, S3g return a fuzzy error log which are not easy for users and maintainers to find the root cause of the error.

The current error is like follows:

An error occurred (500) when calling the ListBuckets operation (reached max retries: 4): Internal Server Error 

This ticket is to fix this issue and return more detailed messages.

What is the link to the Apache JIRA

https://issues.apache.org/jira/browse/HDDS-6419

How was this patch tested?

Test example

  1. List bucket with empty auth.
    • Clean the key and secret in ~/.aws/credentials
    • Run the command of aws s3api --endpoint=http://localhost:9878 list-buckets
    • Before fix the return error is
      An error occurred () when calling the ListBuckets operation:
    • After fix the return error is
      An error occurred (AuthorizationHeaderMalformed) when calling the ListBuckets operation: The authorization header you provided is invalid.

@symious
Copy link
Contributor Author

symious commented Mar 5, 2022

@adoroszlai @elek Could you help to review this PR?

Copy link
Contributor

@adoroszlai adoroszlai left a comment

Choose a reason for hiding this comment

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

Thanks @symious for improving S3 Gateway.

  1. List bucket with empty auth.
  2. List bucket with OM offline

I think we should only fix the first case. We should not expose any internal details (e.g. OM address or state). Such problem should be inspected by admin.

@adoroszlai adoroszlai marked this pull request as draft March 5, 2022 06:40
@symious
Copy link
Contributor Author

symious commented Mar 5, 2022

@adoroszlai Thanks for the review.

I agree we should keep some internal information away from end users. But we do have some cases to let users know the error themselves, I think we'll do some walkaround internally.

Updated the patch, could you have a check?

Copy link
Contributor

@adoroszlai adoroszlai left a comment

Choose a reason for hiding this comment

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

Thanks @symious for updating the patch. Regarding client-side behavior, I think it is fine.

However, I think malformed authorization header is a user input problem, we should handle it as "expected" case. Logging verbose stack trace on S3 Gateway is unnecessary. Please see HDDS-6206 and HDDS-6247 for similar changes in other request/response scenarios.

Also, please add test cases in S3 acceptance tests. This helps in verifying the changes (manual testing is not scalable) and also in avoiding any regression in the future.

s3g_1       | 2022-03-05 08:19:42,809 [qtp1267149311-20] ERROR signature.AuthorizationV4HeaderParser: AWS access id shouldn't be empty. credential:/20220305/us-east-1/s3/aws4_request
s3g_1       | Mar 05, 2022 8:19:42 AM org.glassfish.jersey.internal.Errors logErrors
s3g_1       | WARNING: The following warnings have been detected: WARNING: Unknown HK2 failure detected:
s3g_1       | MultiException stack 1 of 1
s3g_1       | javax.ws.rs.WebApplicationException: The authorization header you provided is invalid.
s3g_1       | 	at org.apache.hadoop.ozone.s3.OzoneClientProducer.wrapOS3Exception(OzoneClientProducer.java:145)
s3g_1       | 	at org.apache.hadoop.ozone.s3.OzoneClientProducer.getSignature(OzoneClientProducer.java:105)
s3g_1       | 	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
s3g_1       | 	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
s3g_1       | 	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
s3g_1       | 	at java.base/java.lang.reflect.Method.invoke(Method.java:566)
s3g_1       | 	at org.jboss.weld.injection.StaticMethodInjectionPoint.invoke(StaticMethodInjectionPoint.java:88)
s3g_1       | 	at org.jboss.weld.injection.StaticMethodInjectionPoint.invoke(StaticMethodInjectionPoint.java:78)
s3g_1       | 	at org.jboss.weld.injection.producer.ProducerMethodProducer.produce(ProducerMethodProducer.java:100)
s3g_1       | 	at org.jboss.weld.injection.producer.AbstractMemberProducer.produce(AbstractMemberProducer.java:161)
s3g_1       | 	at org.jboss.weld.bean.AbstractProducerBean.create(AbstractProducerBean.java:180)
s3g_1       | 	at org.jboss.weld.context.unbound.DependentContextImpl.get(DependentContextImpl.java:70)
s3g_1       | 	at org.jboss.weld.bean.ContextualInstanceStrategy$DefaultContextualInstanceStrategy.get(ContextualInstanceStrategy.java:100)
s3g_1       | 	at org.jboss.weld.bean.ContextualInstance.get(ContextualInstance.java:50)
s3g_1       | 	at org.jboss.weld.manager.BeanManagerImpl.getReference(BeanManagerImpl.java:785)
s3g_1       | 	at org.jboss.weld.manager.BeanManagerImpl.getInjectableReference(BeanManagerImpl.java:885)
s3g_1       | 	at org.jboss.weld.injection.FieldInjectionPoint.inject(FieldInjectionPoint.java:92)
s3g_1       | 	at org.jboss.weld.util.Beans.injectBoundFields(Beans.java:358)
s3g_1       | 	at org.jboss.weld.util.Beans.injectFieldsAndInitializers(Beans.java:369)
s3g_1       | 	at org.jboss.weld.injection.producer.ResourceInjector$1.proceed(ResourceInjector.java:70)
s3g_1       | 	at org.jboss.weld.injection.InjectionContextImpl.run(InjectionContextImpl.java:48)
s3g_1       | 	at org.jboss.weld.injection.producer.ResourceInjector.inject(ResourceInjector.java:72)
s3g_1       | 	at org.jboss.weld.injection.producer.BasicInjectionTarget.inject(BasicInjectionTarget.java:117)
s3g_1       | 	at org.glassfish.jersey.ext.cdi1x.internal.CdiComponentProvider$InjectionManagerInjectedCdiTarget.inject(CdiComponentProvider.java:874)
s3g_1       | 	at org.jboss.weld.bean.ManagedBean.create(ManagedBean.java:159)
s3g_1       | 	at org.jboss.weld.context.unbound.DependentContextImpl.get(DependentContextImpl.java:70)
s3g_1       | 	at org.jboss.weld.bean.ContextualInstanceStrategy$DefaultContextualInstanceStrategy.get(ContextualInstanceStrategy.java:100)
s3g_1       | 	at org.jboss.weld.bean.ContextualInstance.get(ContextualInstance.java:50)
s3g_1       | 	at org.jboss.weld.manager.BeanManagerImpl.getReference(BeanManagerImpl.java:785)
s3g_1       | 	at org.jboss.weld.manager.BeanManagerImpl.getReference(BeanManagerImpl.java:808)
s3g_1       | 	at org.jboss.weld.util.ForwardingBeanManager.getReference(ForwardingBeanManager.java:61)
s3g_1       | 	at org.jboss.weld.bean.builtin.BeanManagerProxy.getReference(BeanManagerProxy.java:85)
s3g_1       | 	at org.glassfish.jersey.ext.cdi1x.internal.CdiUtil.getBeanReference(CdiUtil.java:127)
s3g_1       | 	at org.glassfish.jersey.ext.cdi1x.internal.AbstractCdiBeanSupplier$1.getInstance(AbstractCdiBeanSupplier.java:69)
s3g_1       | 	at org.glassfish.jersey.ext.cdi1x.internal.AbstractCdiBeanSupplier._provide(AbstractCdiBeanSupplier.java:103)
s3g_1       | 	at org.glassfish.jersey.ext.cdi1x.internal.RequestScopedCdiBeanSupplier.get(RequestScopedCdiBeanSupplier.java:46)
s3g_1       | 	at org.glassfish.jersey.inject.hk2.InstanceSupplierFactoryBridge.provide(InstanceSupplierFactoryBridge.java:53)
s3g_1       | 	at org.jvnet.hk2.internal.FactoryCreator.create(FactoryCreator.java:129)
s3g_1       | 	at org.jvnet.hk2.internal.SystemDescriptor.create(SystemDescriptor.java:463)
s3g_1       | 	at org.jvnet.hk2.internal.PerLookupContext.findOrCreate(PerLookupContext.java:46)
s3g_1       | 	at org.jvnet.hk2.internal.Utilities.createService(Utilities.java:2102)
s3g_1       | 	at org.jvnet.hk2.internal.ServiceLocatorImpl.internalGetService(ServiceLocatorImpl.java:758)
s3g_1       | 	at org.jvnet.hk2.internal.ServiceLocatorImpl.internalGetService(ServiceLocatorImpl.java:721)
s3g_1       | 	at org.jvnet.hk2.internal.ServiceLocatorImpl.getService(ServiceLocatorImpl.java:691)
s3g_1       | 	at org.glassfish.jersey.inject.hk2.AbstractHk2InjectionManager.getInstance(AbstractHk2InjectionManager.java:160)
s3g_1       | 	at org.glassfish.jersey.inject.hk2.ImmediateHk2InjectionManager.getInstance(ImmediateHk2InjectionManager.java:30)
s3g_1       | 	at org.glassfish.jersey.internal.inject.Injections.getOrCreate(Injections.java:105)
s3g_1       | 	at org.glassfish.jersey.server.model.MethodHandler$ClassBasedMethodHandler.getInstance(MethodHandler.java:260)
s3g_1       | 	at org.glassfish.jersey.server.internal.routing.PushMethodHandlerRouter.apply(PushMethodHandlerRouter.java:51)
s3g_1       | 	at org.glassfish.jersey.server.internal.routing.RoutingStage._apply(RoutingStage.java:86)
s3g_1       | 	at org.glassfish.jersey.server.internal.routing.RoutingStage._apply(RoutingStage.java:89)
s3g_1       | 	at org.glassfish.jersey.server.internal.routing.RoutingStage._apply(RoutingStage.java:89)
s3g_1       | 	at org.glassfish.jersey.server.internal.routing.RoutingStage._apply(RoutingStage.java:89)
s3g_1       | 	at org.glassfish.jersey.server.internal.routing.RoutingStage.apply(RoutingStage.java:69)
s3g_1       | 	at org.glassfish.jersey.server.internal.routing.RoutingStage.apply(RoutingStage.java:38)
s3g_1       | 	at org.glassfish.jersey.process.internal.Stages.process(Stages.java:173)
s3g_1       | 	at org.glassfish.jersey.server.ServerRuntime$1.run(ServerRuntime.java:247)
s3g_1       | 	at org.glassfish.jersey.internal.Errors$1.call(Errors.java:248)
s3g_1       | 	at org.glassfish.jersey.internal.Errors$1.call(Errors.java:244)
s3g_1       | 	at org.glassfish.jersey.internal.Errors.process(Errors.java:292)
s3g_1       | 	at org.glassfish.jersey.internal.Errors.process(Errors.java:274)
s3g_1       | 	at org.glassfish.jersey.internal.Errors.process(Errors.java:244)
s3g_1       | 	at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:265)
s3g_1       | 	at org.glassfish.jersey.server.ServerRuntime.process(ServerRuntime.java:234)
s3g_1       | 	at org.glassfish.jersey.server.ApplicationHandler.handle(ApplicationHandler.java:680)
s3g_1       | 	at org.glassfish.jersey.servlet.WebComponent.serviceImpl(WebComponent.java:394)
s3g_1       | 	at org.glassfish.jersey.servlet.WebComponent.service(WebComponent.java:346)
s3g_1       | 	at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:366)
s3g_1       | 	at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:319)
s3g_1       | 	at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:205)
s3g_1       | 	at org.eclipse.jetty.servlet.ServletHolder$NotAsync.service(ServletHolder.java:1459)
s3g_1       | 	at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:799)
s3g_1       | 	at org.eclipse.jetty.servlet.ServletHandler$ChainEnd.doFilter(ServletHandler.java:1626)
s3g_1       | 	at org.apache.hadoop.ozone.s3.RootPageDisplayFilter.doFilter(RootPageDisplayFilter.java:53)
s3g_1       | 	at org.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:193)
s3g_1       | 	at org.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1601)
s3g_1       | 	at org.apache.hadoop.ozone.s3.EmptyContentTypeFilter.doFilter(EmptyContentTypeFilter.java:76)
s3g_1       | 	at org.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:201)
s3g_1       | 	at org.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1601)
s3g_1       | 	at org.apache.hadoop.hdds.server.http.HttpServer2$QuotingInputFilter.doFilter(HttpServer2.java:1678)
s3g_1       | 	at org.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:193)
s3g_1       | 	at org.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1601)
s3g_1       | 	at org.apache.hadoop.hdds.server.http.NoCacheFilter.doFilter(NoCacheFilter.java:48)
s3g_1       | 	at org.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:193)
s3g_1       | 	at org.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1601)
s3g_1       | 	at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:548)
s3g_1       | 	at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:143)
s3g_1       | 	at org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:602)
s3g_1       | 	at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:127)
s3g_1       | 	at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:235)
s3g_1       | 	at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:1624)
s3g_1       | 	at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:233)
s3g_1       | 	at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1434)
s3g_1       | 	at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:188)
s3g_1       | 	at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:501)
s3g_1       | 	at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:1594)
s3g_1       | 	at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:186)
s3g_1       | 	at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1349)
s3g_1       | 	at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:141)
s3g_1       | 	at org.eclipse.jetty.server.handler.HandlerCollection.handle(HandlerCollection.java:146)
s3g_1       | 	at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:127)
s3g_1       | 	at org.eclipse.jetty.server.Server.handle(Server.java:516)
s3g_1       | 	at org.eclipse.jetty.server.HttpChannel.lambda$handle$1(HttpChannel.java:388)
s3g_1       | 	at org.eclipse.jetty.server.HttpChannel.dispatch(HttpChannel.java:633)
s3g_1       | 	at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:380)
s3g_1       | 	at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:277)
s3g_1       | 	at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:311)
s3g_1       | 	at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:105)
s3g_1       | 	at org.eclipse.jetty.io.ChannelEndPoint$1.run(ChannelEndPoint.java:104)
s3g_1       | 	at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.runTask(EatWhatYouKill.java:338)
s3g_1       | 	at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.doProduce(EatWhatYouKill.java:315)
s3g_1       | 	at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.tryProduce(EatWhatYouKill.java:173)
s3g_1       | 	at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.run(EatWhatYouKill.java:131)
s3g_1       | 	at org.eclipse.jetty.util.thread.ReservedThreadExecutor$ReservedThread.run(ReservedThreadExecutor.java:386)
s3g_1       | 	at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:883)
s3g_1       | 	at org.eclipse.jetty.util.thread.QueuedThreadPool$Runner.run(QueuedThreadPool.java:1034)
s3g_1       | 	at java.base/java.lang.Thread.run(Thread.java:829)
s3g_1       | Caused by: org.apache.hadoop.ozone.s3.exception.OS3Exception
s3g_1       | 	at org.apache.hadoop.ozone.s3.exception.S3ErrorTable.newError(S3ErrorTable.java:139)
s3g_1       | 	at org.apache.hadoop.ozone.s3.exception.S3ErrorTable.newError(S3ErrorTable.java:126)
s3g_1       | 	at org.apache.hadoop.ozone.s3.signature.AuthorizationV4HeaderParser.parseCredentials(AuthorizationV4HeaderParser.java:171)
s3g_1       | 	at org.apache.hadoop.ozone.s3.signature.AuthorizationV4HeaderParser.parseSignature(AuthorizationV4HeaderParser.java:91)
s3g_1       | 	at org.apache.hadoop.ozone.s3.signature.AWSSignatureProcessor.parseSignature(AWSSignatureProcessor.java:70)
s3g_1       | 	at org.apache.hadoop.ozone.s3.signature.AWSSignatureProcessor$Proxy$_$$_WeldClientProxy.parseSignature(Unknown Source)
s3g_1       | 	at org.apache.hadoop.ozone.s3.OzoneClientProducer.getSignature(OzoneClientProducer.java:86)
s3g_1       | 	... 115 more

@@ -47,7 +47,13 @@ File upload and directory list
Should not contain ${result} dir1
Should contain ${result} file

List buckets with empty access id
Execute aws configure set aws_access_key_id ''
${result} = Execute AWSS3APICli and checkrc list-buckets 255
Copy link
Contributor

@adoroszlai adoroszlai Mar 5, 2022

Choose a reason for hiding this comment

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

S3 tests are grouped, mostly based on operation type. list-buckets belongs to bucketlist.robot.

(awss3.robot is for aws s3 command, while other tests are for aws s3api.)

Also, adding it as last test case would avoid the need to run Setup s3 tests explicitly in the next test case.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure, I'll move it to bucketlist.robot.
I didn't put it at last incase others adding new test would loss the accesskey.

@symious
Copy link
Contributor Author

symious commented Mar 5, 2022

For the verbose log, it's from jersey and it's print to ozone-s3g.out.
I haven't figured out how to ignore the warnings yet, could you give some adivce? @adoroszlai

Co-authored-by: Doroszlai, Attila <6454655+adoroszlai@users.noreply.github.com>
@adoroszlai adoroszlai marked this pull request as ready for review March 8, 2022 08:12
…3/OzoneClientProducer.java

Co-authored-by: Doroszlai, Attila <6454655+adoroszlai@users.noreply.github.com>
@symious
Copy link
Contributor Author

symious commented Mar 9, 2022

There may be AccessControlExceptions from OM, should we handle and send back to users?

@adoroszlai
Copy link
Contributor

adoroszlai commented Mar 9, 2022

There may be AccessControlExceptions from OM, should we handle and send back to users?

Which call results in AccessControlExceptions? Can you please give an example?

@symious
Copy link
Contributor Author

symious commented Mar 9, 2022

A example would be the impersonate feature, as the test case of https://github.com/apache/ozone/pull/3157/files.

@adoroszlai
Copy link
Contributor

A example would be the impersonate feature, as the test case of

Correct me if I'm wrong, but my understanding is that this would be a server-side configuration issue, to be fixed by admin, not relevant to S3 user, so should not be sent back to them.

@symious
Copy link
Contributor Author

symious commented Mar 9, 2022

Yes, the current usage won't trigger this issue.

Copy link
Contributor

@adoroszlai adoroszlai left a comment

Choose a reason for hiding this comment

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

Thanks @symious for iterating on this patch.

@symious
Copy link
Contributor Author

symious commented Mar 9, 2022

@adoroszlai Thank you for the detail review.

@adoroszlai adoroszlai merged commit ea06d26 into apache:master Mar 9, 2022
@adoroszlai adoroszlai changed the title HDDS-6419. Add detail error message instead of generic INTERNAL SERVER ERROR for S3g HDDS-6419. Provide better error message for malformed auth header Mar 9, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
2 participants