2FA support
In a ticket for gotify server it was stated that 2FA should be added via external services: https://github.com/gotify/server/issues/461
This works well for the WebUI (I'm personally using traefik + authelia for 2FA in front of gotify).
Unfortunately this is a problem for the Android client application, which is not able to follow the redirect workflow imposed by 2FA. This seems to be a general problem I've seen for many other selfhosted projects that provide a WebUI + client - when adding external 2FA, usually the client application breaks.
So it seems there are 3 "solutions" to this dilemma:
- Try to make the client "redirect / 2FA aware" (is this even possible in a good way? since all 2FA stuff happens on the initial http requests and therefore "outside" the API level)
- Re-think the decision not to have "native" 2FA built into the server (which I guess would be easier to follow/implement client-side)
- "Just accept" / maybe somewhere document that 2FA + Client app is not supported
Any thoughts on this / am I missing something?
This sounds about right. Like said in the linked issue, I think that adding support for external auth providers should be the simplest solution (in comparison to natively implementing this).
Hi, I'm quite new to Gotify but I've used a lot of Authentik lately. Is there news on the subject ? I would love to be able to use Authentik to secure Gotify.
In a ticket for gotify server it was stated that 2FA should be added via external services: gotify/server#461
This works well for the WebUI (I'm personally using traefik + authelia for 2FA in front of gotify).
Unfortunately this is a problem for the Android client application, which is not able to follow the redirect workflow imposed by 2FA. This seems to be a general problem I've seen for many other selfhosted projects that provide a WebUI + client - when adding external 2FA, usually the client application breaks.
So it seems there are 3 "solutions" to this dilemma:
- Try to make the client "redirect / 2FA aware" (is this even possible in a good way? since all 2FA stuff happens on the initial http requests and therefore "outside" the API level)
- Re-think the decision not to have "native" 2FA built into the server (which I guess would be easier to follow/implement client-side)
- "Just accept" / maybe somewhere document that 2FA + Client app is not supported
Any thoughts on this / am I missing something?
RaphMad, i have a question. Did you managed to get Gotify working with authelia 2FA in front of it? I'm trying to get it to work, but i can't get any messages to my mobile phone. I use nginx proxy manager with Authelia, the code that i put in advance is:
location /authelia {
internal;
set $upstream_authelia http://192.168.1.55:9091/api/verify; #ADD YOUR IP AND PORT OF AUTHELIA
proxy_pass_request_body off;
proxy_pass $upstream_authelia;
proxy_set_header Content-Length "";
# Timeout if the real server is dead
proxy_next_upstream error timeout invalid_header http_500 http_502 http_503;
client_body_buffer_size 128k;
proxy_set_header Host $host;
proxy_set_header X-Original-URL $scheme://$http_host$request_uri;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $http_host;
proxy_set_header X-Forwarded-Uri $request_uri;
proxy_set_header X-Forwarded-Ssl on;
proxy_redirect http:// $scheme://;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_cache_bypass $cookie_session;
proxy_no_cache $cookie_session;
proxy_buffers 4 32k;
send_timeout 5m;
proxy_read_timeout 240;
proxy_send_timeout 240;
proxy_connect_timeout 240;
}
location / {
set $upstream_gotify http://192.168.1.57:3001; #CHANGE NAME AND IP AND PORT
proxy_pass $upstream_gotify; #change name of the service
auth_request /authelia;
auth_request_set $target_url $scheme://$http_host$request_uri;
auth_request_set $user $upstream_http_remote_user;
auth_request_set $groups $upstream_http_remote_groups;
proxy_set_header Remote-User $user;
proxy_set_header Remote-Groups $groups;
error_page 401 =302 https://auth.YOURDOMAIN.COM/?rd=$target_url; #change YOURDOMAIN.COM to your domain
client_body_buffer_size 128k;
proxy_next_upstream error timeout invalid_header http_500 http_502 http_503;
send_timeout 5m;
proxy_read_timeout 360;
proxy_send_timeout 360;
proxy_connect_timeout 360;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $http_host;
proxy_set_header X-Forwarded-Uri $request_uri;
proxy_set_header X-Forwarded-Ssl on;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_redirect http:// $scheme://;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_cache_bypass $cookie_session;
proxy_no_cache $cookie_session;
proxy_buffers 64 256k;
set_real_ip_from 192.168.1.0/16;
real_ip_header X-Forwarded-For;
real_ip_recursive on;
}
Can you help me out?
No, as far as I understood the mobile app needs to be able to understand/support the initial HTTP redirect any 2FA solution implies (browsers are built for this and have no problems) - so no real chance to solve this with authelia config.
Basically still waiting for app support, and its unfortunately one of those topics that can easily be seen as "out of scope" on that side either, so I don't blame the devs if it takes long / they decide to not support this use-case.
I just got it working!
In the authelia config:
access_control:
default_policy: deny
rules:
## bypass rule
- domain:
- "gotify.YOURDOMAIN.COM"
policy: bypass
resources:
- '^/api([/?].*)?$'
- domain: "gotify.YOURDOMAIN.COM"
policy: two_factor
And in NginX Proxy Manager under the advance tab:
location /authelia {
internal;
set $upstream_authelia http://192.168.1.55:9091/api/verify; #ADD YOUR IP AND PORT OF AUTHELIA
proxy_pass_request_body off;
proxy_pass $upstream_authelia;
proxy_set_header Content-Length "";
# Timeout if the real server is dead
proxy_next_upstream error timeout invalid_header http_500 http_502 http_503;
client_body_buffer_size 128k;
proxy_set_header Host $host;
proxy_set_header X-Original-URL $scheme://$http_host$request_uri;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $http_host;
proxy_set_header X-Forwarded-Uri $request_uri;
proxy_set_header X-Forwarded-Ssl on;
proxy_redirect http:// $scheme://;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_cache_bypass $cookie_session;
proxy_no_cache $cookie_session;
proxy_buffers 4 32k;
send_timeout 5m;
proxy_read_timeout 240;
proxy_send_timeout 240;
proxy_connect_timeout 240;
}
location / {
set $upstream_gotify http://192.168.1.57:3001; #CHANGE NAME AND IP AND PORT
proxy_pass $upstream_gotify; #change name of the service
auth_request /authelia;
auth_request_set $target_url $scheme://$http_host$request_uri;
auth_request_set $user $upstream_http_remote_user;
auth_request_set $groups $upstream_http_remote_groups;
proxy_set_header Remote-User $user;
proxy_set_header Remote-Groups $groups;
error_page 401 =302 https://auth.YOURDOMAIN.COM/?rd=$target_url; #change YOURDOMAIN.COM to your domain
client_body_buffer_size 128k;
proxy_next_upstream error timeout invalid_header http_500 http_502 http_503;
send_timeout 5m;
proxy_read_timeout 360;
proxy_send_timeout 360;
proxy_connect_timeout 360;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $http_host;
proxy_set_header X-Forwarded-Uri $request_uri;
proxy_set_header X-Forwarded-Ssl on;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_redirect http:// $scheme://;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_cache_bypass $cookie_session;
proxy_no_cache $cookie_session;
proxy_buffers 64 256k;
set_real_ip_from 192.168.1.0/16;
real_ip_header X-Forwarded-For;
real_ip_recursive on;
}
Rebooted authelia and when i want to login to the web GUI, i got 2FA from authelia, and when i sended a message to my android mobile phone, i received it. I'm not home, so i got it externally.
Was searching for this solution for months, so i thought i'll share it with you guys!
Ah thats cool, I'll also try it!
Hm I think it is still kind of an "out of band" solution - so you logged in on the web GUI (on your phone?) first, and then the app started working?
So it seems the app can handle the case where you are already logged in on the 2FA/Authelia level and you get forwarded to the actual gotify page without intervention. But did you also test the unauthenticated case where 2FA is required? (I'm asking because this would require the app to somehow render a frame that displays the web page containing the authelia login prompt, which seems very unlikely to already be implemented).
But I'm purely speculating here, haven't had the time to try anything by myself!
I noticed that not all messages where working. I couldn't login to the android app neighter, i tried to figure it out and i think i got it completly working now.
I can login to the app as well and all the messages are comming through. If i open the webinterface from gotify in https://gotify.YOURDOMAIN.COM i get authelia 2FA first.
In the authelia config:
access_control:
default_policy: deny
rules:
## bypass rule
- domain:
- "gotify.YOURDOMAIN.COM"
policy: bypass
resources:
- '^/api([/?].*)?$'
- '/message'
- '/health'
- '/static/'
- '/favicon.ico'
- '/'
- domain: "gotify.YOURDOMAIN.COM"
policy: two_factor
And in NginX Proxy Manager under the advance tab:
location /authelia {
internal;
set $upstream_authelia http://192.168.1.55:9091/api/verify; #ADD YOUR IP AND PORT OF AUTHELIA
proxy_pass_request_body off;
proxy_pass $upstream_authelia;
proxy_set_header Content-Length "";
# Timeout if the real server is dead
proxy_next_upstream error timeout invalid_header http_500 http_502 http_503;
client_body_buffer_size 128k;
proxy_set_header Host $host;
proxy_set_header X-Original-URL $scheme://$http_host$request_uri;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $http_host;
proxy_set_header X-Forwarded-Uri $request_uri;
proxy_set_header X-Forwarded-Ssl on;
proxy_redirect http:// $scheme://;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_cache_bypass $cookie_session;
proxy_no_cache $cookie_session;
proxy_buffers 4 32k;
send_timeout 5m;
proxy_read_timeout 240;
proxy_send_timeout 240;
proxy_connect_timeout 240;
}
location / {
set $upstream_gotify http://192.168.1.57:3001; #CHANGE NAME AND IP AND PORT
proxy_pass $upstream_gotify; #change name of the service
auth_request /authelia;
auth_request_set $target_url $scheme://$http_host$request_uri;
auth_request_set $user $upstream_http_remote_user;
auth_request_set $groups $upstream_http_remote_groups;
proxy_set_header Remote-User $user;
proxy_set_header Remote-Groups $groups;
error_page 401 =302 https://auth.YOURDOMAIN.COM/?rd=$target_url; #change YOURDOMAIN.COM to your domain
client_body_buffer_size 128k;
proxy_next_upstream error timeout invalid_header http_500 http_502 http_503;
send_timeout 5m;
proxy_read_timeout 360;
proxy_send_timeout 360;
proxy_connect_timeout 360;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $http_host;
proxy_set_header X-Forwarded-Uri $request_uri;
proxy_set_header X-Forwarded-Ssl on;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_redirect http:// $scheme://;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_cache_bypass $cookie_session;
proxy_no_cache $cookie_session;
proxy_buffers 64 256k;
set_real_ip_from 192.168.1.0/16;
real_ip_header X-Forwarded-For;
real_ip_recursive on;
}