Details
-
Bug
-
Status: Closed
-
Major
-
Resolution: Fixed
-
3.0.1-incubating
-
None
Description
Errors with HTTP REST basic auth. I was able to reproduce this on tp30 and master. I have a pull request coming.
1. Built the latest tp30 branch. Copied gremlin-server-secure.yaml to gremlin-server-secure-rest.yaml and updated the channelizer to org.apache.tinkerpop.gremlin.server.channel.HttpChannelizer.
Ran this command.
$ curl -k -X POST -v -H 'Content-Type: application/json' -u stephen:password https://127.0.0.1:8182 -d '{"gremlin":"100-3"}' * Rebuilt URL to: https://127.0.0.1:8182/ * Trying 127.0.0.1... * Connected to 127.0.0.1 (127.0.0.1) port 8182 (#0) * TLS 1.2 connection using TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA * Server certificate: example.com * Server auth using Basic with user 'stephen' > POST / HTTP/1.1 > Host: 127.0.0.1:8182 > Authorization: Basic c3RlcGhlbjpwYXNzd29yZA== > User-Agent: curl/7.43.0 > Accept: */* > Content-Type: application/json > Content-Length: 19 > * upload completely sent off: 19 out of 19 bytes
Got this output on the server.
[ERROR] HttpGremlinEndpointHandler - Error processing HTTP Request java.lang.IllegalArgumentException: Illegal base64 character 20 at java.util.Base64$Decoder.decode0(Base64.java:714) at java.util.Base64$Decoder.decode(Base64.java:526) at java.util.Base64$Decoder.decode(Base64.java:549) at org.apache.tinkerpop.gremlin.server.handler.HttpBasicAuthenticationHandler.channelRead(HttpBasicAuthenticationHandler.java:64) at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:308) at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:294) at io.netty.handler.codec.MessageToMessageDecoder.channelRead(MessageToMessageDecoder.java:103) at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:308) at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:294) at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:244) at io.netty.channel.CombinedChannelDuplexHandler.channelRead(CombinedChannelDuplexHandler.java:147) at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:308) at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:294) at io.netty.handler.ssl.SslHandler.unwrap(SslHandler.java:1069) at io.netty.handler.ssl.SslHandler.decode(SslHandler.java:944) at io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:327) at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:230) at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:308) at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:294) at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:846) at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:131) at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:511) at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:468) at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:382) at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:354) at io.netty.util.concurrent.SingleThreadEventExecutor$2.run(SingleThreadEventExecutor.java:111) at java.lang.Thread.run(Thread.java:745)
The fix is to chop off "Basic " from the Authorization header.
2. After that worked, I ran into this error when trying to run consecutive curls.
$ curl -k -X POST -v -H 'Content-Type: application/json' -u stephen:password https://127.0.0.1:8182 -d '{"gremlin":"100-3"}' * Rebuilt URL to: https://127.0.0.1:8182/ * Trying 127.0.0.1... * Connected to 127.0.0.1 (127.0.0.1) port 8182 (#0) * Server aborted the SSL handshake * Closing connection 0 curl: (35) Server aborted the SSL handshake
Got this output on the server.
io.netty.channel.ChannelPipelineException: org.apache.tinkerpop.gremlin.server.handler.HttpBasicAuthenticationHandler is not a @Sharable handler, so can't be added or removed multiple times. at io.netty.channel.DefaultChannelPipeline.checkMultiplicity(DefaultChannelPipeline.java:464) at io.netty.channel.DefaultChannelPipeline.addLast0(DefaultChannelPipeline.java:136) at io.netty.channel.DefaultChannelPipeline.addLast(DefaultChannelPipeline.java:129) at io.netty.channel.DefaultChannelPipeline.addLast(DefaultChannelPipeline.java:120) at org.apache.tinkerpop.gremlin.server.channel.HttpChannelizer.configure(HttpChannelizer.java:71) at org.apache.tinkerpop.gremlin.server.AbstractChannelizer.initChannel(AbstractChannelizer.java:134) at org.apache.tinkerpop.gremlin.server.AbstractChannelizer.initChannel(AbstractChannelizer.java:63) at io.netty.channel.ChannelInitializer.channelRegistered(ChannelInitializer.java:69) at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRegistered(AbstractChannelHandlerContext.java:133) at io.netty.channel.AbstractChannelHandlerContext.fireChannelRegistered(AbstractChannelHandlerContext.java:119) at io.netty.channel.DefaultChannelPipeline.fireChannelRegistered(DefaultChannelPipeline.java:733) at io.netty.channel.AbstractChannel$AbstractUnsafe.register0(AbstractChannel.java:450) at io.netty.channel.AbstractChannel$AbstractUnsafe.access$100(AbstractChannel.java:378) at io.netty.channel.AbstractChannel$AbstractUnsafe$1.run(AbstractChannel.java:424) at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:357) at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:357) at io.netty.util.concurrent.SingleThreadEventExecutor$2.run(SingleThreadEventExecutor.java:111) at java.lang.Thread.run(Thread.java:745)
I fixed this by moving the initialization of the authentication handler in HttpChannelizer from init() to configure(). It doesn't seem like a safe to assumption that the Authenticator interface implementation can be shared.
3. After that worked, the other thing I noticed is that if authorization fails, the curl doesn't close.
$ curl -k -X POST -v -H 'Content-Type: application/json' -u stephen:bogus https://127.0.0.1:8182 -d '{"gremlin":"100-3"}' * Rebuilt URL to: https://127.0.0.1:8182/ * Trying 127.0.0.1... * Connected to 127.0.0.1 (127.0.0.1) port 8182 (#0) * TLS 1.2 connection using TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA * Server certificate: example.com * Server auth using Basic with user 'stephen' > POST / HTTP/1.1 > Host: 127.0.0.1:8182 > Authorization: Basic c3RlcGhlbjpwYXNz > User-Agent: curl/7.43.0 > Accept: */* > Content-Type: application/json > Content-Length: 19 > * upload completely sent off: 19 out of 19 bytes < HTTP/1.1 401 Unauthorized * no chunk, no close, no size. Assume close to signal end <
I fixed this by adding a listener ChannelFutureListener.CLOSE during the write unautorized response.