kasmtech / workspaces-images

Other
747 stars 242 forks source link

running KASM Stand Alone + Nginx Proxy Manager+ Authentik #116

Open moutasem1989 opened 1 month ago

moutasem1989 commented 1 month ago

I am interested in running Desktop as a Stand Alone Web Service but i suppose they all function more ore less the same. I made this Docker Compose to try and spin it up:

services:
  kasmweb:
    image: kasmweb/desktop:1.16.0
    container_name: kasmweb
    ports:
        - 6901:6901
    stdin_open: true
    tty: true
    shm_size: '2gb'
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - /etc/timezone:/etc/timezone:ro
    devices:
      - /dev/dri:/dev/dri
    env_file: /dockerfiles/kasmweb.env
    networks:
      - public

networks:
  public:
    external: true

It does run with errors related to being in Stand Alone and not connected to KASM Workspace. One Environment variable they mention in the documentation is VNC_PW=password which in turn is used in Basic HTTP Authentication i assume:

User : kasm_user
Password: password

Going to https://<ip>:6901 will get you to the Desktop GUI in your browser and it will work smoothly.

image

Because I like to secure my services even when used to test software I disabled the ports so the service is accessed only through NPM and enable Websockets for the Proxy Host. You will get again to the HTTP Authentication but even with correct cridentials it will error out:

 2024-10-17 10:41:04,174 [INFO] websocket 8: got client connection from 172.19.0.15
 2024-10-17 10:41:04,186 [DEBUG] websocket 8: using SSL socket
 2024-10-17 10:41:04,195 [DEBUG] websocket 8: X-Forwarded-For ip '192.168.20.59'
 2024-10-17 10:41:04,195 [INFO] websocket 8: Authentication attempt failed, BasicAuth required, but client didn't send any
 2024-10-17 10:41:04,195 [INFO] websocket 8: 172.19.0.15 192.168.20.59 - "GET / HTTP/1.1" 401 158
 2024-10-17 10:41:04,195 [DEBUG] websocket 8: No connection after handshake
 2024-10-17 10:41:04,195 [DEBUG] websocket 8: handler exit

For some reason NPM is not forwarding the cridentials to the KASM Host. Despite that I did try setting up a Reverse Proxy Authentication in Authentik and tried setting up Basic HTTP Authentication:

image

Note that proxy_pass http://authentik.company:9000/should be changed accordingly for the NPM setup. According to this Websockets issue adding this to the NPM configuration is needed:

    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";

or:

    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_http_version 1.1;

However neither did work for me.

How can I put KASM Service behind Nginx Proxy Manager and allow HTTP Basic Authentication to work? If does not work, Can Basic HTTP Authentication be disabled? How can I use Authentik reverse proxy authentication with KASM websockets and Basic HTTP Authentication on NPM?

moutasem1989 commented 1 month ago

trying to bypass HTTP Basic Authentication in NPM, I have tried adding the following as Custom Nginx Configuration:

location / {
    proxy_pass https://kasmweb:6901;
    proxy_set_header Authorization "Basic a2FzbV91c2VyOnBhc3N3b3Jk";
    proxy_pass_header Authorization;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_http_version 1.1;
}

I got the following log on KASM container:

 2024-10-17 13:44:45,623 [INFO] websocket 56: got client connection from 172.19.0.15
 2024-10-17 13:44:45,634 [DEBUG] websocket 56: using SSL socket
 2024-10-17 13:44:45,634 [DEBUG] websocket 56: X-Forwarded-For ip '192.168.20.59'
 2024-10-17 13:44:45,639 [DEBUG] websocket 56: BasicAuth matched
 2024-10-17 13:44:45,639 [DEBUG] websocket 56: Invalid WS request, maybe a HTTP one
 2024-10-17 13:44:45,639 [DEBUG] websocket 56: Requested file '/index.html'
 2024-10-17 13:44:45,640 [INFO] websocket 56: 172.19.0.15 192.168.20.59 kasm_user "GET /index.html HTTP/1.1" 200 24135
 2024-10-17 13:44:45,640 [DEBUG] websocket 56: No connection after handshake
 2024-10-17 13:44:45,640 [DEBUG] websocket 56: handler exit

Now Authentication with header does work but the issue remains with WS

moutasem1989 commented 1 month ago
location / {
    proxy_pass https://kasmweb:6901;
    proxy_set_header Authorization "Basic a2FzbV91c2VyOnBhc3N3b3Jk";
    proxy_pass_header Authorization;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-Scheme $scheme;
    proxy_set_header X-Forwarded-Proto  $scheme;
    proxy_set_header X-Forwarded-For    $remote_addr;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection $http_connection;
    proxy_http_version 1.1;
}

Thanks to this discussion, this worked for Nginx proxy Manager and it connects imediatly. Authentik has broken configurations for NPM as they added WS support. reverting to older configurations fixed the issue: here is the working custom configurations for NPM:


# Increase buffer size for large headers
# This is needed only if you get 'upstream sent too big header while reading response
# header from upstream' error when trying to access an application protected by goauthentik
proxy_buffers 8 16k;
proxy_buffer_size 32k;

# Make sure not to redirect traffic to a port 4443
port_in_redirect off;

location / {
    # Put your proxy_pass to your application here
    proxy_pass          $forward_scheme://$server:$port;
    # Set any other headers your application might need
    # proxy_set_header Host $host;
    # proxy_set_header ...
    #proxy_pass https://kasmweb:6901;
    proxy_set_header Authorization "Basic a2FzbV91c2VyOnBhc3N3b3Jk";
    proxy_pass_header Authorization;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-Scheme $scheme;
    proxy_set_header X-Forwarded-Proto  $scheme;
    proxy_set_header X-Forwarded-For    $remote_addr;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection $http_connection;
    proxy_http_version 1.1;
    ##############################
    # authentik-specific config
    ##############################
    auth_request     /outpost.goauthentik.io/auth/nginx;
    error_page       401 = u/goauthentik_proxy_signin;
    auth_request_set $auth_cookie $upstream_http_set_cookie;
    add_header       Set-Cookie $auth_cookie;

    # translate headers from the outposts back to the actual upstream
    auth_request_set $authentik_username $upstream_http_x_authentik_username;
    auth_request_set $authentik_groups $upstream_http_x_authentik_groups;
    auth_request_set $authentik_email $upstream_http_x_authentik_email;
    auth_request_set $authentik_name $upstream_http_x_authentik_name;
    auth_request_set $authentik_uid $upstream_http_x_authentik_uid;

    proxy_set_header X-authentik-username $authentik_username;
    proxy_set_header X-authentik-groups $authentik_groups;
    proxy_set_header X-authentik-email $authentik_email;
    proxy_set_header X-authentik-name $authentik_name;
    proxy_set_header X-authentik-uid $authentik_uid;
    #add_header X-calibre-web-user "CloudAdmin" always;
}

# all requests to /outpost.goauthentik.io must be accessible without authentication
location /outpost.goauthentik.io {
    proxy_pass              https://authentik-server:9443/outpost.goauthentik.io;
    # ensure the host of this vserver matches your external URL you've configured
    # in authentik
    proxy_set_header        Host $host;
    proxy_set_header        X-Original-URL $scheme://$http_host$request_uri;
    add_header              Set-Cookie $auth_cookie;
    auth_request_set        $auth_cookie $upstream_http_set_cookie;
    proxy_pass_request_body off;
    proxy_set_header        Content-Length "";
}

# Special location for when the /auth endpoint returns a 401,
# redirect to the /start URL which initiates SSO
location u/goauthentik_proxy_signin {
    internal;
    add_header Set-Cookie $auth_cookie;
    return 302 /outpost.goauthentik.io/start?rd=$request_uri;
    # For domain level, use the below error_page to redirect to your authentik server with the full redirect path
    # return 302 https://authentik.company/outpost.goauthentik.io/start?rd=$scheme://$http_host$request_uri;
}