[GH-ISSUE #796] Client certificate is not recognised by addon even if installed on Firefox #521

Closed
opened 2026-03-02 11:50:32 +03:00 by kerem · 3 comments
Owner

Originally created by @t00 on GitHub (Dec 31, 2024).
Original GitHub issue: https://github.com/karakeep-app/karakeep/issues/796

Describe the Bug

I am using client certificates in nginx reverse proxy to protect all internet-exposed sites from being hacked.
Extension works great without a client certificate but when it is enabled, it shows CORS errors.

In nginx.conf (commented out client certificate):

#    ssl_client_certificate /etc/nginx/ssl/ca.crt;
#    ssl_verify_client on;

...
    location / {
        proxy_pass http://127.0.0.1:3000;
        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-Host $server_name;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_pass_header Access-Control-Allow-Origin;
        proxy_pass_header Access-Control-Allow-Methods;
        proxy_pass_header Access-Control-Allow-Headers;
        proxy_pass_header Access-Control-Allow-Credentials;
        proxy_pass_header Access-Control-Max-Age;
        client_max_body_size 0;

        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $http_connection;
        proxy_http_version 1.1;
    }

Firefox logs:

Unchecked lastError value: Error: The menu id open-hoarder already exists in menus.create. [background.ts-BDF_1IYB.js:1](moz-extension://f896e839-e534-4dbd-abad-613ee46321e7/assets/background.ts-BDF_1IYB.js)
Unchecked lastError value: Error: The menu id add-link already exists in menus.create. [background.ts-BDF_1IYB.js:1](moz-extension://f896e839-e534-4dbd-abad-613ee46321e7/assets/background.ts-BDF_1IYB.js)
Unchecked lastError value: Error: The menu id open-hoarder already exists in menus.create. [background.ts-BDF_1IYB.js:1](moz-extension://f896e839-e534-4dbd-abad-613ee46321e7/assets/background.ts-BDF_1IYB.js)
Unchecked lastError value: Error: The menu id add-link already exists in menus.create. [background.ts-BDF_1IYB.js:1](moz-extension://f896e839-e534-4dbd-abad-613ee46321e7/assets/background.ts-BDF_1IYB.js)
LoginRecipes: Falling back to a synchronous message for: moz-extension://f896e839-e534-4dbd-abad-613ee46321e7. [LoginRecipes.sys.mjs:284:16](resource://gre/modules/LoginRecipes.sys.mjs)
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at https://my.domain/api/trpc/apiKeys.exchange?batch=1. (Reason: CORS header ‘Access-Control-Allow-Origin’ missing). Status code: 400.

Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at https://my.domain/api/trpc/apiKeys.exchange?batch=1. (Reason: CORS request did not succeed). Status code: (null).

In the same browser, when navigating to https://my.domain/ everything works fine with a client certificate installed and enabled.

Steps to Reproduce

  1. Generate a client certificate, example guide
  2. Configure nginx reverse proxy and use (1) as well as a valid SSL certificate (from Let's encrypt or self-signed)
  3. Install (1) in Firefox
  4. Check if the domain opens up correctly in Firefox (would not in a browser without (1)
  5. Install hoarder extension and configure - error

Expected Behaviour

In a browser with a client certificate already installed, extension api requests should make use of it.

Screenshots or Additional Context

No response

Device Details

No response

Exact Hoarder Version

1.2.3

Have you checked the troubleshooting guide?

  • I have checked the troubleshooting guide and I haven't found a solution to my problem
Originally created by @t00 on GitHub (Dec 31, 2024). Original GitHub issue: https://github.com/karakeep-app/karakeep/issues/796 ### Describe the Bug I am using client certificates in nginx reverse proxy to protect all internet-exposed sites from being hacked. Extension works great without a client certificate but when it is enabled, it shows CORS errors. In nginx.conf (commented out client certificate): ``` # ssl_client_certificate /etc/nginx/ssl/ca.crt; # ssl_verify_client on; ... location / { proxy_pass http://127.0.0.1:3000; 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-Host $server_name; proxy_set_header X-Forwarded-Proto $scheme; proxy_pass_header Access-Control-Allow-Origin; proxy_pass_header Access-Control-Allow-Methods; proxy_pass_header Access-Control-Allow-Headers; proxy_pass_header Access-Control-Allow-Credentials; proxy_pass_header Access-Control-Max-Age; client_max_body_size 0; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $http_connection; proxy_http_version 1.1; } ``` Firefox logs: ``` Unchecked lastError value: Error: The menu id open-hoarder already exists in menus.create. [background.ts-BDF_1IYB.js:1](moz-extension://f896e839-e534-4dbd-abad-613ee46321e7/assets/background.ts-BDF_1IYB.js) Unchecked lastError value: Error: The menu id add-link already exists in menus.create. [background.ts-BDF_1IYB.js:1](moz-extension://f896e839-e534-4dbd-abad-613ee46321e7/assets/background.ts-BDF_1IYB.js) Unchecked lastError value: Error: The menu id open-hoarder already exists in menus.create. [background.ts-BDF_1IYB.js:1](moz-extension://f896e839-e534-4dbd-abad-613ee46321e7/assets/background.ts-BDF_1IYB.js) Unchecked lastError value: Error: The menu id add-link already exists in menus.create. [background.ts-BDF_1IYB.js:1](moz-extension://f896e839-e534-4dbd-abad-613ee46321e7/assets/background.ts-BDF_1IYB.js) LoginRecipes: Falling back to a synchronous message for: moz-extension://f896e839-e534-4dbd-abad-613ee46321e7. [LoginRecipes.sys.mjs:284:16](resource://gre/modules/LoginRecipes.sys.mjs) Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at https://my.domain/api/trpc/apiKeys.exchange?batch=1. (Reason: CORS header ‘Access-Control-Allow-Origin’ missing). Status code: 400. Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at https://my.domain/api/trpc/apiKeys.exchange?batch=1. (Reason: CORS request did not succeed). Status code: (null). ``` In the same browser, when navigating to https://my.domain/ everything works fine with a client certificate installed and enabled. ### Steps to Reproduce 1. Generate a client certificate, [example guide](https://stackoverflow.com/a/47115211/291496) 2. Configure nginx reverse proxy and use (1) as well as a valid SSL certificate (from Let's encrypt or self-signed) 3. Install (1) in Firefox 4. Check if the domain opens up correctly in Firefox (would not in a browser without (1) 5. Install hoarder extension and configure - error ### Expected Behaviour In a browser with a client certificate already installed, extension api requests should make use of it. ### Screenshots or Additional Context _No response_ ### Device Details _No response_ ### Exact Hoarder Version 1.2.3 ### Have you checked the troubleshooting guide? - [X] I have checked the troubleshooting guide and I haven't found a solution to my problem
kerem closed this issue 2026-03-02 11:50:33 +03:00
Author
Owner

@t00 commented on GitHub (Dec 31, 2024):

I found a workaround for this problem here. It is related to CORS preflight (OPTIONS) requests not having client certificate enabled.

After setting network.cors_preflight.allow_client_cert to true the extension started working fine. Is there a way to disable CORS entirely and globally as the client certificate provides more than enough security by itself?

<!-- gh-comment-id:2566635121 --> @t00 commented on GitHub (Dec 31, 2024): I found a workaround for this problem [here](https://stackoverflow.com/questions/73916302/how-can-i-use-an-https-client-certificate-protected-api-with-firefox). It is related to CORS preflight (OPTIONS) requests not having client certificate enabled. After setting `network.cors_preflight.allow_client_cert` to `true` the extension started working fine. Is there a way to disable CORS entirely and globally as the client certificate provides more than enough security by itself?
Author
Owner

@MohamedBassem commented on GitHub (Jan 2, 2025):

@t00 I don't think we can do this. For firefox to know whether CORS is enabled, or not, it needs to do the preflight request to the server. However, the preflight request will get rejected by your nginx setup (due to the lack of certs), and the request will fail.

One idea that might work (not sure), is for you to change nginx's config to allow OPTIONS requests without client certs. But I don't think there's much we can do from Hoarder's side.

<!-- gh-comment-id:2567767997 --> @MohamedBassem commented on GitHub (Jan 2, 2025): @t00 I don't think we can do this. For firefox to know whether CORS is enabled, or not, it needs to do the preflight request to the server. However, the preflight request will get rejected by your nginx setup (due to the lack of certs), and the request will fail. One idea that **might work** (not sure), is for you to change nginx's config to allow `OPTIONS` requests without client certs. But I don't think there's much we can do from Hoarder's side.
Author
Owner

@t00 commented on GitHub (Jan 2, 2025):

Just for fun I tried to configure it in nginx:

map "$ssl_client_verify:$request_method" $fail_client {
    default 1;
    "~^SUCCESS:.+$" 0;
    "~^.+:OPTIONS" 0;
}

server {
    if ( $fail_client = 1 ) {
        return 405;
    }
}

Unfortunately it only partially works - it allows options request (status code 204) but fails (status code 405) the subsequent POST request which internally in a browser likely does not contain the SSL Client Certificate if network.cors_preflight.allow_client_cert if set to false.

10.1.1.1 - - [02/Jan/2025:14:25:06 +0000] "OPTIONS /api/trpc/bookmarks.createBookmark?batch=1 HTTP/1.1" 204 0 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:133.0) Gecko/20100101 Firefox/133.0"
10.1.1.1 - - [02/Jan/2025:14:25:06 +0000] "POST /api/trpc/bookmarks.createBookmark?batch=1 HTTP/1.1" 405 157 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:133.0) Gecko/20100101 Firefox/133.0"

It looks like /api/trpc produces header Access-Control-Allow-Origin * which is causing Firefox to prevent sending credentials and SSL client certificate is assumed to be a credential - source.

I understand that Access-Control-Allow-Origin * is needed to allow making requests from an extension but there is an alternative - whitelisting origins which are allowed and returning matching origin or host instead of *.

Example configuration to illustrate how this might work:

# same as current
HOARDER_CORS_ALLOW_ORIGIN=

# same as current
HOARDER_CORS_ALLOW_ORIGIN=*

# allow only my domain or firefox extension in the ORIGIN header (when present) or HOST header
# last to try is my.domain so it will be left in Access-Control-Allow-Origin if no match
HOARDER_CORS_ALLOW_ORIGIN=moz-extension://f896e839-e534-4dbd-abad-613ee46321e7 my.domain

# allow only my domain or firefox extension but fall back to *
HOARDER_CORS_ALLOW_ORIGIN=my.domain moz-extension://f896e839-e534-4dbd-abad-613ee46321e7 *

My current nginx configuration is doing all the magic:

map "$ssl_client_verify:$request_method:$http_origin" $fail_client {
    default 1;
    "~^SUCCESS:.+$" 0;
    "~^.+:OPTIONS:.*$" 0;
    "~^.+:.*:moz-extension://f896e839-e534-4dbd-abad-613ee46321e7$" 0;
}

server {
    listen 443 ssl;
    server_name my.domain;
    ssl_client_certificate /etc/nginx/ssl/ca.crt;
    ssl_verify_client optional;

    if ( $fail_client = 1 ) {
        return 405;
    }

    if ( $http_origin = '' ) {
        set $http_origin $host;
    }

    location / {
        proxy_pass http://127.0.0.1:3000;
        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-Host $server_name;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_hide_header Access-Control-Allow-Origin;
        add_header Access-Control-Allow-Origin "$http_origin";
        proxy_pass_header Access-Control-Allow-Methods;
        proxy_pass_header Access-Control-Allow-Headers;
        proxy_pass_header Access-Control-Allow-Credentials;
        proxy_pass_header Access-Control-Max-Age;
        client_max_body_size 0;

        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $http_connection;
        proxy_http_version 1.1;
    }
}

<!-- gh-comment-id:2567945019 --> @t00 commented on GitHub (Jan 2, 2025): Just for fun I tried to configure it in nginx: ``` map "$ssl_client_verify:$request_method" $fail_client { default 1; "~^SUCCESS:.+$" 0; "~^.+:OPTIONS" 0; } server { if ( $fail_client = 1 ) { return 405; } } ``` Unfortunately it only partially works - it allows options request (status code 204) but fails (status code 405) the subsequent POST request which internally in a browser likely does not contain the SSL Client Certificate if `network.cors_preflight.allow_client_cert` if set to `false`. ``` 10.1.1.1 - - [02/Jan/2025:14:25:06 +0000] "OPTIONS /api/trpc/bookmarks.createBookmark?batch=1 HTTP/1.1" 204 0 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:133.0) Gecko/20100101 Firefox/133.0" 10.1.1.1 - - [02/Jan/2025:14:25:06 +0000] "POST /api/trpc/bookmarks.createBookmark?batch=1 HTTP/1.1" 405 157 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:133.0) Gecko/20100101 Firefox/133.0" ``` It looks like /api/trpc produces header `Access-Control-Allow-Origin *` which is causing Firefox to prevent sending credentials and SSL client certificate is assumed to be a credential - [source](https://stackoverflow.com/a/42662490/291496). I understand that `Access-Control-Allow-Origin *` is needed to allow making requests from an extension but there is an alternative - whitelisting origins which are allowed and returning matching origin or host instead of *. Example configuration to illustrate how this might work: ``` # same as current HOARDER_CORS_ALLOW_ORIGIN= # same as current HOARDER_CORS_ALLOW_ORIGIN=* # allow only my domain or firefox extension in the ORIGIN header (when present) or HOST header # last to try is my.domain so it will be left in Access-Control-Allow-Origin if no match HOARDER_CORS_ALLOW_ORIGIN=moz-extension://f896e839-e534-4dbd-abad-613ee46321e7 my.domain # allow only my domain or firefox extension but fall back to * HOARDER_CORS_ALLOW_ORIGIN=my.domain moz-extension://f896e839-e534-4dbd-abad-613ee46321e7 * ``` My current nginx configuration is doing all the magic: ``` map "$ssl_client_verify:$request_method:$http_origin" $fail_client { default 1; "~^SUCCESS:.+$" 0; "~^.+:OPTIONS:.*$" 0; "~^.+:.*:moz-extension://f896e839-e534-4dbd-abad-613ee46321e7$" 0; } server { listen 443 ssl; server_name my.domain; ssl_client_certificate /etc/nginx/ssl/ca.crt; ssl_verify_client optional; if ( $fail_client = 1 ) { return 405; } if ( $http_origin = '' ) { set $http_origin $host; } location / { proxy_pass http://127.0.0.1:3000; 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-Host $server_name; proxy_set_header X-Forwarded-Proto $scheme; proxy_hide_header Access-Control-Allow-Origin; add_header Access-Control-Allow-Origin "$http_origin"; proxy_pass_header Access-Control-Allow-Methods; proxy_pass_header Access-Control-Allow-Headers; proxy_pass_header Access-Control-Allow-Credentials; proxy_pass_header Access-Control-Max-Age; client_max_body_size 0; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $http_connection; proxy_http_version 1.1; } } ```
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
starred/karakeep#521
No description provided.