The magic of 2 lines in Lua, or how to convey the original HTTP Authorization header-authorization headers to the web service

The article will be useful to those:



  • who needs to use several types of authorization in one request to the server;
  • who wants to open the services of the Kubernetes / Docker world to the general Internet, without thinking about how to protect a particular service;
  • thinks that everything has already been done by someone, and would like to make the world a little more convenient and safer.


Foreword



Services made available through Kubernetes have a rich set of authorization methods. One of the more fashionable is the Authorization: Bearer header - for example: JWT authorization ( JSON Web Token ) with the transfer of many keys, and therefore values, in one header. There are also Basic authorizations, for example for the Registry (Docker image repository). This authorization does not use cookies and is automatically added by the browser (except for Safari - there are nuances that we have not yet decided) to all requests to the server.







Problem 1:



I can't log in through Firefox & Safari in the Storage OS interface, the Loader is shown, and that's it.



Mini-hypothesis:



Proxying problem. A quick check showed the result: if authorization without using proxying (universal secure access using a certificate), then everything works. So what's the deal?



After analyzing the network stack, we realized that the Authorization header was being used, however, earlier, during the configuration of the proxying of Rancher services, it was found out that this header is passed to the proxied service and contains the authorization data by the certificate, so it was decided to simply delete it after the authorization process was completed (FakeBasicAuth ).



Problem 2:



In many web servers, personal certificate authorization is implemented through emulation of Basic authorization (in fact, interfering with a user's request), probably with the aim of reducing changes in the main code of the web server. This method is called FakeBasicAuth. After setting such a header, the web server overwrites the Authorization header that comes from the user.



Hypotheses:



  1. The scope of the FakeBasicAuth header lends itself to even more restriction, so that the original header is restored for transmission to the proxied resource so that only the original header, if any, is transmitted.
  2. The scope of the Authorization header can be designed so that the header will be saved before activating the FakeBasicAuth mechanism and restored after.


Visible State - Purpose:



Storage OS authorizes, you can configure this service while maintaining a unified approach to making services available to the external Internet.



Additional goal:



Unified, fast and secure http access while preserving the functionality of all possible http services (for example, REST API for a mobile application or Registry Docker).



How to check?



  • docker login registry-rancher.xxx.ru - using keys and login / password.
  • storageos-rancher.xxx.ru/#/login - using login and password from configs secret rancher.xxx.ru/p/c-84bnv : p-qj9qm / secrets / kube-system: init-secr ... (does not work in Safari ).
  • registry-ui-rancher.xxx.ru - using a browser and login / password from Registry. For those who carefully read the trick: you can use this interface instead of the standard docker login registry-rancher.xxx.ru - there is built-in proxying to the Registry.


Testing hypotheses:



1. Based on previous experience, let's try to find a way on the Internet for such requests: apache authentification external basic via cert.



There was a more or less adequate article about ldap . In this way:



RewriteEngine on
RewriteCond %{IS_SUBREQ} ^false$
RewriteCond %{LA-U:REMOTE_USER} (.+)
RewriteRule . - [E=RU:%1]
RequestHeader set REMOTE_USER %{RU}e


And then pass the title in additional headers through the construction



RequestHeader add Authorization "expr=%{env:zt-auth-before}" "expr=%{env:zt-auth-before} =~/.{1,}/"


But, unfortunately, this construction does not imply the creation of an identical header, the header of the user's request, and env is formed incorrectly.



Therefore, the method based on the standard rewrite turned out to be useless and complicated.



2. If we cannot do it as standard, then we need to turn to lua, we have previously seen that there are request processing blocks that are executed before certificates are processed, look again at the flowchart from the lua_load_resty_core article and the module_lua_writinghooks instruction with the early construction.



It turns out that we can use the same script ( How we at ZeroTech made friends Apple Safari and client certificates with websockets) to preserve the Authorization header before replacing it with FakeBasicAuth.







LuaHookAccessChecker /usr/local/etc/apache24/sslincludes/websocket_token.lua handler early


In Lua, it now looks like this:




require 'apache2'

function handler(r)
        local fmt = '%Y%m%d%H%M%S'
        local timeout = 3600 -- 1 hour
        local auth = r.headers_in['Authorization']

        r.notes['zt-cert-timeout'] = timeout
        r.notes['zt-cert-date-next'] = os.date(fmt,os.time()+timeout)
        r.notes['zt-cert-date-halfnext'] = os.date(fmt,os.time()+ (timeout/2))
        r.notes['zt-cert-date-now'] = os.date(fmt,os.time())

        if auth ~= nil then
                r.notes['zt-auth-before'] = auth
        end

        return apache2.OK
end


Note:



New constructions are in bold. And since we know that the env obtained from Lua is only available for expr expressions, we add a construction next to the encryption of the zt-cert token:



# outgoing cookies to the user




Header set Set-Cookie "expr=zt-cert=%{sha1:...


# pass headers to the proxied service




RequestHeader add Authorization "expr=%{env:zt-auth-before}" "expr=%{env:zt-auth-before} =~/.{1,}/"


The availability of data for transmission to the service was checked by transferring data back to the user to the browser:



Header add Authorization "expr=%{env:zt-auth-before}" "expr=%{env:zt-auth-before} =~/.{1,}/"


The most interesting thing here is a way to check for the presence of data so as not to transmit the header to the proxied service if it did not come from outside the user's browser. The second part of the construction is responsible for this:



"expr=%{env:zt-auth-before} =~/.{1,}/"


Completion: There are no



ready-made solutions on the Internet at the moment, about three hours were spent searching and trying to test the variations, because I didn't want to β€œreinvent the wheel”.



Added 5 lines, 3 of which can be safely removed. What do you think? - Write your options for answers in the comments to the article.



I did not want to write about this experience, since in fact it is only 2 lines and the Authorization header will reach the addressee, but I decided to share the information anyway, since it uses a good store of knowledge from previous research on certificates (we are talking about this article ). In addition, there are hardly any daredevils to write something of their own and so simple in an unknown language.






All Articles