Keycloack: After logout, the access token can still be used - keycloak

I have an nginx/openresty client to a keycloack server for authorization using openid.
I am using lua-resty-openidc to allow access to services behind the proxy.
The user can access his profile at
https://<my-server>/auth/realms/<real-name>/account
and logout through
https://<my-server>/auth/realms/<real-name>/protocol/openid-connect/logout
The problem is that, even after logout, the user can still access the services behind the server, basically it seems the token he gets from keycloak is still valid or something.... This is also a behaviour that has been observed by other users, see for example this question on how to logout from keycloak the comments from ch271828n
How can I ensure that after logout the user will no longer be able to get access until he logs in anew?

I had to check the lua source code, but I think I have figured the logout behaviour out: Lua-resty-openidc establishes sessions, and they are terminated when a specific url access is detected (it is controlled by opts.logout_path which we will need to be set to an address in the path of service, e.g. .../service/logout)
In essence, there are two urls that need to be hit, one for keycloack logout, and one for openresty session logout. Accessing the keycloack logout url https://<keycloak-server>/auth/realms/<my-realm>/protocol/openid-connect/logout is done by lua after we access the opts.logout_path at https://<our-nginx-server>/service/logout
So after setting up everything correctly, all we have to do to logout is hit https://<our-nginx-server>/service/logout. This will destroy the session and log us out.
I think we need to set opts.revoke_tokens_on_logout to true, Also note that from my experiments, for some reason, setting up a redirect_after_logout_uri may result in the user not signing out due to redirections.
Here is an example of what we need to have for nginx.conf to make this work....
location /myservice/ {
access_by_lua_block {
local opts = {
redirect_uri_path = "/myservice/auth",
discovery = "https://<keycloak-server>/auth/realms/<my-realm>/.well-known/openid-configuration",
client_id = "<my-client-id>",
client_secret = "<the-clients-secret>",
logout_path = "/service/logout",
revoke_tokens_on_logout = true,
session_contents = {id_token=true} -- this is essential for safari!
}
-- call introspect for OAuth 2.0 Bearer Access Token validation
local res, err = require("resty.openidc").authenticate(opts)
if err then
ngx.status = 403
ngx.say(err)
ngx.exit(ngx.HTTP_FORBIDDEN)
end
}
# I disbled caching so the browser won't cache the site.
expires 0;
add_header Cache-Control private;
proxy_pass http://my-service-server.cloud:port/some/path/;
proxy_set_header Host $http_host;
proxy_http_version 1.1;
proxy_redirect off;
proxy_buffering off;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}

Related

Serving files with PocketBase

What I want is to restrict access to files for unauthorized user.
PocketBase documentation says I can retrieve the file URL and access files through it. The example URL for a file would be like this:
http://127.0.0.1:8090/api/files/example/kfzjt5oy8r34hvn/test_52iWbGinWd.png
I can prevent unauthorized users to get this URL, but authorized users can share URL with other one.
Any ideas?
I found a good way to secure files with nginx, by adding an extra location for my PocketBase server block and using an extra backend with one endpoint.
So, my nginx looks like this:
server {
listen 80;
server_name example.com;
location /api/files {
proxy_intercept_errors on;
error_page 404 = #fallback;
proxy_pass http://127.0.0.1:5000;
}
location / {
proxy_pass http://127.0.0.1:8090;
}
location #fallback {
proxy_pass http://127.0.0.1:8090;
}
}
Where my expressjs backend working on port :5000 checks JWT and responds with 404 if it is valid. Nginx will redirect to :8090 (PocketBase) if 404 returned on :5000.

Custom admin link in Ghost causes refuse to connect error

I am using Ghost Version 4+.
I created a custom admin link by customizing my config.production and now I am successfully redirected to my subdomain admin.mysite.com, but the problem is that everything is working fine but it refuses to connect in editing sections. I have attached a screenshot for reference.
Reference image to the error being shown
It's basically a frame showing "refused to connect" earlier which was working fine and showing a preview of all functions of the site in real time.
This was working fine on my main link - mysite.com/ghost but refuses to connect since I changed my config to a new link admin.mysite.com
This is my config file ->
{
"url": "https:/mysite.com",
"admin": {
"url": "https://admin.mysite.com/"
},
"server": {
"port": 2369,
"host": "127.0.0.1"
},
Important notes to consider :
When I open my subdomain(admin.mysite.com) it shows default Nginx page, so I used Cloudflare redirect to point it to admin.mysite.com/ghost which holds the admin area after changing my configuration above.
I have set my subdomain by an A record pointing to the IP address of my server. What I have not done is configuring Nginx. This is reference information hope anyone finds any solution to why I am facing this.
My assumption is that ghost is designed to connect everything at the default link which is mysite.com/ghost, the forum of ghost shows a way to do it Ghost Admin Security: Is there any way to redirect Admin-URL (Redirection Alternative)
I am not sure if I have to configure Nginx, DNS, or Config.
If you are using Nginx :
location / {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $http_host;
proxy_pass http://127.0.0.1:2369;
add_header X-Frame-Options "ALLOW-FROM URI";
}
Add this line
add_header X-Frame-Options "ALLOW-FROM URI";
If you are using Apache
Header always set X-Frame-Options "ALLOW-FROM URI"
For reference : X-Frame-Options Mozzila
Adding this header shall add a vulnerability of embedding a webpage within an Iframe despite being from the same origin.

Keycloak RBAC for nginx reverse proxy

I started to work with keycloak, and here is a setup I want to test.
I want to test this scenario:
It works, but I want to implement role-based access to apps behind Nginx proxy and I can't understand how exactly payload of jwt token generates.
Here is my JWT tokens payload:
"jti": "f5f07b6f-ccae-4f57-a8ea-ae02ebb3cb12",
"exp": 1569263630,
"nbf": 0,
"iat": 1569227630,
"iss": "https://keycloak.domain.local/auth/realms/LDAP_test",
"sub": "fedc6baf-4ba4-4fa6-924c-9501edf070f7",
"typ": "Serialized-ID",
"auth_time": 0,
"session_state": "aa0052ee-b5e1-45cc-bee4-e7bccdfa4a59",
"state_checker": "sC_nvlDXfjUDHhC15ZDpPauX5JkxhvVtYUOn62PhtV8"
I want my token to contain roles, username and email and i run out of ideas how to put it there. Is client somehow related to the content of the token or keycloak always gives everything he have into it?
Here are my Nginx client settings:
server {
server_name demo-a.domain.local;
location / {
proxy_pass http://10.10.10.168/index.html;
access_by_lua '
local opts = {
redirect_uri_path = "/redirect_uri",
accept_none_alg = true,
discovery = "https:/keycloak.domain.local/auth/realms/LDAP_test/.well-known/openid-configuration",
client_id = "nginx-gateway",
client_secret = "19fe43bc-4167-4433-816a-eb96da33f9a3",
redirect_uri_scheme = "https",
logout_path = "/logout",
redirect_after_logout_uri = "https://keycloak.domain.local/auth/realms/LDAP_test/protocol/openid-connect/logout?redirect_uri=https://www.nginx-gateway.domain.local/",
redirect_after_logout_with_id_token_hint = false,
session_contents = {id_token=true}
}
-- call introspect for OAuth 2.0 Bearer Access Token validation
local res, err = require("resty.openidc").authenticate(opts)
if err then
ngx.status = 403
ngx.say(err)
ngx.exit(ngx.HTTP_FORBIDDEN)
end
';
}
Thanks in advance!
Did you configure the client to be openid client? Is it UI or machine client?
To cause your JWT to have roles, one follows usual below flow:
Create client in Keycloak admin-console and configure it to support
open-id-connect.
Add users to Keycloak.
Assign roles to users.
Configure your client to have the client id of Keycloak client.
Exercise browser or machine flow which involves passing of JWT to the
client. JWT will contain the roles as claims.
Still debugging it, but here are a few pointers you will definitely need....
You need to be Authenticating Reverse Proxy with KeyCloak while using the keycloak behind an nginx proxy
Ok, completely forget about this question, but still here is a solution.
Just use a proper oidc proxy like gatekeeper (louketo now) or oauth2-proxy.

nginx ingress annotations to redirect to authentication and get back headers

I've the below nginx conf file to redirect all the requests (by default) to /auth (of my service) and then get back a response header (foo_id). This header will be forwarded to the original request URI triggered by user. The below works properly with the nginx.
...
location /
{
auth_request /auth;
auth_request_set $foo_id $upstream_http_foo_id;
proxy_pass $request_uri
proxy_set_header X-foo-Token $foo_id;
root html;
index index.html index.htm;
}
location /auth
{
internal;
proxy_pass https://myhost/myservice;
proxy_pass_request_body off;
proxy_set_header Content-Length "";
proxy_set_header X-Original-URI $request_uri;
}
But I need the corresponding ingress rules/annotations that are required to achieve the above use case. I couldn't get the respective auth/proxy-pass related annotations. Kindly help out.
You can use Nginx Ingress Annotations to achieve this goal.
In nutshell:
Assuming, you have an external auth service, which has a Kubernetes service configured.
You need an annotation, which sends auth requests to this service:
nginx.ingress.kubernetes.io/auth-url: http://auth-service.<NameSpace>.svc.cluster.local/auth
Also, you can use nginx.ingress.kubernetes.io/auth-snippet annotation to set your custom configuration for the auth request e.g.
nginx.ingress.kubernetes.io/auth-snippet: |
auth_request_set $foo_id $upstream_http_foo_id;
proxy_pass $request_uri
proxy_set_header X-foo-Token $foo_id;
If you need to return some headers on successful auth, you can use nginx.ingress.kubernetes.io/auth-response-headers:
nginx.ingress.kubernetes.io/auth-response-headers: X-Auth
And, nginx.ingress.kubernetes.io/auth-signin to specify the custom error page

Nginx reverse proxy on another web server with redirection

The compoments of the system are
(source: snag.gy)
:
"nginx server" - A
"3rd party server" - B
"User PC" (browser) - C
The server "A" and the user PC "C" are connected in the same network "Network A", but the server "A" and 3rd party machine "B" are connected in the network "Network B".
The goal is to access 3rd party machine "B" from the user browser "C" through the Server "A". In order to perform it, the server "A" should do reverse proxy of the 3rd party content.
Nginx server is installed and configured on the server "A". The rule for reverse proxy is:
location ~* ^/3rdparty/(.*)___(.*)___(.*)___(.*)$ {
proxy_pass https://$1.$2.$3.$4?$args;
proxy_redirect off;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
Using the rule we would like to do reverse proxy for URL:
http:///3rdparty/<3RD_PARTY_IP_URL> => https://<3RD_PARTY_IP_ADDRESS>
For example:
http:// 192.168.237.208/3rdparty/192___168___237___222 => https://192.168.237.222
Actually, we want everything appended to the URL to be redirected.
For example:
http://192.168.237.208/3rdparty/192___168___237___222/blah/blah => https://192.168.237.222/blah/blah
That configuration works for us for some another setup, but the problem in this case is: on 3rd party machine "B", there is another web server which does some redirections:
https://192.168.237.222 => https://192.168.237.222/users/sign_in
So, the final result when we put the desired URI in the browser is:
http://192.168.237.208/3rdparty/192___168___237___222
the webserver on "B" takes host name of the URL and appends the redirection rule (users/sign_in), which results that browser is requesting the following URL:
https://192.168.237.208/users/sign_in
which is not available on Server "A" (actually it is on "B") => we got http error code 302 (moved temporarelly).
Are there any missing configuration rules we didn't apply? Or any other http request header parameters should be updated/forwarded?
The backend connection to 192.168.237.222 is transparent to the client which is only connected to 192.168.237.208
When server B (backend) sends back a /users/sign_in redirection, it is reversely propagated to the client, which applies it against the server he is connected to, ie server A (gateway/proxy), resulting in the erroneous request of /users/sign_in on 192.168.237.208.
You need to keep in mind redirections are propagated back to and processed by the client, not the gateway/proxy.
I would do something like the following:
location ~* ^/3rdparty/([0-9]{1-3})___([0-9]{1-3})___([0-9]{1-3})___([0-9]{1-3})(/.*)?$ {
proxy_pass https://$1.$2.$3.$4$5$is_args$args;
proxy_redirect off;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
Details:
Modifying your regex to also match against requests with /blah/blah appended to the backend IP address and copy-pasting them at the end of the proxy_pass directive using another variable ($5)
Using $is_args nginx embedded variable instead of a question mark (avoiding to have it in a request if no args are to be passed to the backend)
Trying to refine the regex syntax to match IP address numbers (quick and dirty, the one I provide you with still leads to incorrect matches but is at least better than wildcards)
Now, you need to redirect clients on server B (192.168.237.222) to /3rdparty/192___168___237___222/users/sign_in which is a location that can be requested against server A (192.168.237.208, the only server clients are connected to) and happily forwarded to the right backend.
==========
You could also rely on the nginx proxy_redirect directive rather than setting it to off and make your backend server making redirection answers like https://192.168.237.221/users/sign_in. proxy_redirect would then allow you to rewrite those proxied answers to the right URI against 192.168.237.208.
To do so, try something like (watchout, I am still using the lazy IP address mask):
proxy_redirect ~*^https://([0-9]{3}).([0-9]{3}).([0-9]{3}).([0-9]{3})(/.*)?$ http://192.168.237.208/3rdparty/$1___$2___$3____$4$5;
I wonder if variables like $is_args and $args are working in this context:
proxy_redirect ~*^https://([0-9]{3}).([0-9]{3}).([0-9]{3}).([0-9]{3})(/.*)?$ http://192.168.237.208/3rdparty/$1___$2___$3____$4$5$is_args$args;