Kong / kong-python-pdk

Write Kong plugins in Python (Experimental)
Apache License 2.0
40 stars 14 forks source link

Unable to identify anything during the `certificate` phase #107

Open michaelarnauts opened 1 year ago

michaelarnauts commented 1 year ago

I'm trying to conditionally request a client certificate with kong.client.tls.request_client_certificate() in the certificate phase, however, I don't find anything I can use to identify the request.

Almost none of the methods are available in the certificate phase, however according to the docs of the mtls plugin (https://docs.konghq.com/hub/kong-inc/mtls-auth/#client-certificate-request), the SNI should be available.

Is there a way to get the SNI in the certificate phase?

StarlightIbuki commented 1 year ago

I remember I've answered the same question before but cannot find the record... The plugin implemented the logic to interfere certificate with an internal interface. The internal interface is not accessible from external plugins (Go, Python, JS), and is very dangerous to implement yourself even with Lua plugins. So sadly we don't have a way to do it for Python.

michaelarnauts commented 1 year ago

Hmm, that makes the whole certificate phase pretty useless, since we can't figure out what request we are handling. You can only request a client certificate for all requests.

I understand that the mtls plugin isn't open source, but by denying access to this information from other plugins, there is no solution but to use the closed source plugin?

StarlightIbuki commented 1 year ago

@michaelarnauts Sorry. I confirmed with @fffonion offline and it's possible to do what you want (just not the way I expected when I wrote down the answer).

kong.nginx.get_var("ssl_server_name") can be used to get the Nginx variable "ssl_server_name".

Ref: https://kong.github.io/kong-python-pdk/kong.html#kong.kong.nginx.get_var (Note the document is not accurate. Will create a fix for that.) http://nginx.org/en/docs/http/ngx_http_ssl_module.html#:~:text=%24-,ssl_server_name,-returns%20the%20server

michaelarnauts commented 1 year ago

@StarlightIbuki thanks for the update. I'll try to find some time to test the kong.nginx.get_var() method.

michaelarnauts commented 1 year ago

@StarlightIbuki @fffonion

I've tried the suggested kong.nginx.get_var("ssl_server_name"), but I get back the following error.

It seems that this is thrown due to a patch that kong made itself: https://github.com/Kong/kong/blob/a9ff98a168924b8135eda010f3c79bc91c157908/build/openresty/patches/ngx_lua-0.10.21_02-dyn_upstream_keepalive.patch#L1030

Python code:

    def certificate(self, kong: kong.kong):
        kong.log.debug("---- Certificate execution -----")
        kong.log.debug("nginx var ssl_server_name = %s" % kong.nginx.get_var("ssl_server_name"))

Kong logs:

...
2023/05/31 14:19:15 [info] 1245035#0: *595 [mtls-auth-prod:1245061] DEBG - [22:19:15] rpc: #2 return: {'Data': {'Method': 'kong.log.debug', 'Args': ('---- Certificate execution -----',)}, 'EventId': 0}, context: ngx.timer
2023/05/31 14:19:15 [debug] 1245037#0: *606 [kong] mp_rpc.lua:161 [mtls-auth-prod] ---- Certificate execution -----
2023/05/31 14:19:15 [info] 1245035#0: *595 [mtls-auth-prod:1245061] DEBG - [22:19:15] rpc: #3 method: plugin.Step args: [{'EventId': 0}], context: ngx.timer
2023/05/31 14:19:15 [info] 1245035#0: *595 [mtls-auth-prod:1245061] DEBG - [22:19:15] rpc: #3 return: {'Data': {'Method': 'kong.nginx.get_var', 'Args': ('ssl_server_name',)}, 'EventId': 0}, context: ngx.timer
2023/05/31 14:19:15 [error] 1245037#0: *606 lua entry thread aborted: runtime error: ...local/share/lua/5.1/kong/runloop/plugin_servers/init.lua:41: API disabled in the current context
stack traceback:
coroutine 0:
        [C]: in function 'error'
        /usr/local/openresty/lualib/resty/kong/var.lua:111: in function '__index'
        ...local/share/lua/5.1/kong/runloop/plugin_servers/init.lua:41: in function 'method'
        ...cal/share/lua/5.1/kong/runloop/plugin_servers/mp_rpc.lua:161: in function 'call_pdk_method'
        ...cal/share/lua/5.1/kong/runloop/plugin_servers/mp_rpc.lua:317: in function 'bridge_loop'
        ...cal/share/lua/5.1/kong/runloop/plugin_servers/mp_rpc.lua:336: in function 'handle_event'
        ...local/share/lua/5.1/kong/runloop/plugin_servers/init.lua:274: in function <...local/share/lua/5.1/kong/runloop/plugin_servers/init.lua:273>
        /usr/local/share/lua/5.1/kong/init.lua:351: in function 'execute_plugins_iterator'
        /usr/local/share/lua/5.1/kong/init.lua:879: in function 'ssl_certificate'
        ssl_certificate_by_lua:2: in main chunk, context: ssl_certificate_by_lua*, client: xxx.xxx.xxx.xxx, server: 0.0.0.0:443
2023/05/31 14:19:15 [info] 1245037#0: *605 SSL_do_handshake() failed (SSL: error:1417A179:SSL routines:tls_post_process_client_hello:cert cb error) while SSL handshaking, client: xxx.xxx.xxx.xxx, server: 0.0.0.0:443
StarlightIbuki commented 1 year ago

Sorry for the confusion. I thought the API is available for certificate phase but it's actually not. Ping @fffonion for help on this issue. Do we have any workaround for this?

StarlightIbuki commented 1 year ago

Also sorry for replying late. Sometimes it got buried in a flood of notifications. Please feel free to ping me when you think it takes too long.

fffonion commented 1 year ago

yes, sorry this was an oversight, the approach doesn't work in certificate phase. we will need a new PDK API that calls ngx.ssl.server_name() to extract SNI, there's no such support exists in kong yet.

michaelarnauts commented 1 year ago

yes, sorry this was an oversight, the approach doesn't work in certificate phase. we will need a new PDK API that calls ngx.ssl.server_name() to extract SNI, there's no such support exists in kong yet.

That's unfortunate. Is this planned, or something where I could help?

fffonion commented 1 year ago

@michaelarnauts this is currently not on our roadmap. PRs are welcomed, https://github.com/Kong/kong/pull/9512 is a good example on how to add such function in PDK. Note you only need to implement at kong/lua side, no extra effort needed on python-pdk side.