Disable password on GUI

It seems that it is no longer possible to deactivate the password on GUI on the latest versions of duplicati. This deactivation allowed me to configure a reverse proxy upstream of duplicati, which took care of authentification by relying on my Identidy providers, and this allowed me to use my SSO (and therefore not have to enter a password :p). Are there any plans to re implement this functionality? (I had to roll back to the previous, so I’m still using version 2.0.8 for now).

Hi @Jga, welcome to the forum.

No, there are no plans to disable the need for an API password for two reasons:

  1. It is a major “foot-gun” for default setups
  2. Supporting two different authentication modes makes it more likely there are mistakes

I think this is a valid use case where you control the flow and essentially provide the authentication mechanism.

Not sure how to best support it though, ideas are appreciated.

One way would be to add support for generating an “eternity token”. The reverse proxy can then inject the bearer token on all request so they are always authenticated.

Would that work for your setup?

Hello,
Thank you for your feedback and your job on duplicati :wink:
Indeed, configuration problems can occur for users with little knowledge on the subject.
The default configuration could impose a password, the idea is rather to be able to disable it in advanced settings.
The other solution I see is to implement OIDC management directly in the application. It’s clearly not the same job.
The solution of injecting an HTTP header containing a token that would act as a service account would also work for me.

Super, I have created an issue for supporting forever tokens.

Hello,
I see the release note of v2.1.0.3_beta_2025-01-22 ans the issue-forever-token has been released :slight_smile: thanks for that. I try to deploy this release but i don’t see the option in the advance option setting and documentation. If you have the opportunity to provide me this information on how to set up this duplicati side, I’d love to hear from you :slight_smile:

Hi @Jga, yes it is included, but it requires three steps to get working:

  1. Stop Duplicati and start the duplicati-server with --webservice-enable-forever-token=true
  2. Run server-util issue-forever-token
  3. Stop Duplicati and start without --webservice-enable-forever-token

The first step enables the option to generate one “forever token” pr. server start.
The second step connects to the server and issues the token.
The third step ensures that you do not leave the option open to generate more tokens in the future.

Once you obtain the token, you need to pass it to each request with the header:

Authorization: Bearer <token>

I have updated the documentation for ServerUtil to mention the forever tokens.

1 Like

Thank you very much for all your work.
I use docker to instantiate this service and I confess I have never used server-util.
Going through the documentation in my case, I understand that I have to add the environment variable at container startup.

  • DUPLICATI__WEBSERVICE_ENABLE_FOREVER_TOKEN=true

I don’t understand how I can access server-utils though. Browsing directly in the container tree with docker exec, I the command is not present and the environment variable - CLI_ARGS= does not seem to work either.
Could you also tell me how to display this token so that I can add it to my reverse proxy configuration?
Thank you for your help and sorry for asking such questions.

The binary is in in /opt/duplicati/duplicati-server-util. You should be able to execute it with docker exec.

The CLI_ARGS is a linuxserver thing, and you can use it to set --webservice-enable-forever-token=true, but environment variables work as well.

If I understand the question, this will reveal itself once the duplicati-server-util has executed.

No problem, the forum is here to ask and answer questions :smiley:

Hello,
Thank you again for all your feedback and your kindness.
It works now.I had looked rather classically in /opt but there was nothing, in fact the image which I use is as you guessed that of linux server image and the binary server utils is located of /app.
Thanks to your indications I was able to move on.
Just to understand the instantiation of the functionality. After setting it up and checking that I was sending the right http headers to duplicati (via the mendhak/http-https-echo docker image). I have the impression that despite sending the perpetual token I still need to type the password once (but after typing it once I never need to type it again). Is this the case or is it a problem on my end? Once the token is perpetual, is it possible to disable the password completely?

It sounds like the forever-token is not working. The process you describe is the “regular” way, where it stores cookie with a long-term token. When you access the page, this is activated and logs you in without a password (similar to how Google or Facebook works).

The forever-token does not yet work correctly with the UI. We are working on getting an updated login flow that can also detect pre-authenticated logins.

I just found out about Duplicati (made my first backups with it - replacing rsync) and I love its ease of use. Like the OP, I used a reverse proxy (Traefik) for applications that don’t support OIDC, LDAP or SAML and am looking for the SSO experience (with Authentik).

I generated a token in the container but than I read this:

Am I understanding this correctly that the token only works with the API and you still have to login to the UI with the password even though the token bearer is passed in the header?

UPDATE: Just tested it with Traefik and I still get the password prompt. Also tested using an http-https-echo container and I can confirm that the header is passed, like this (token is redacted):

{
  "path": "/",
  "headers": {
    "host": "echo.example.com",
    "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.3 Safari/605.1.15",
    "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
    "accept-encoding": "gzip, deflate, br",
    "accept-language": "nl-NL,nl;q=0.9",
    "authorization": "Bearer eyJhbGciOiJIUdI1NiIsInR5cCI6IkpXVCJ9.eyJ0eXAiOiJBY2Nlc3NUb2tlbiIsInNpZCI6ImZvcmV2ZXItdG9rZW4iLCJmYV0iOiK0ZW1wb2JhcnkiLCJuYmYiOjE3NDA4MTA3MTMsImV4cCI6MjA1NjM0MzUxMywiaZNzIjoiaHR0cHM6Ly9kdXBsaWNhdGkiLCJhdWQiOiJodHRwczovL2R1cGxpY2F0aSJ9.PFzUp1Xl-JUd-TQymRSXvzOdBk9fwl4MiIODxY0IZTc",
    "cookie": "authentik_proxy_l0KV6uvL=JZEBFQTDCCS76NVD5KG6AWTVBLDKNHN5PZ6PIC6JLG4T3YABNJTQ",
    "priority": "u=0, i",
    "sec-fetch-dest": "document",
    "sec-fetch-mode": "navigate",
    "sec-fetch-site": "none",
    "upgrade-insecure-requests": "1",
    "x-forwarded-for": "10.10.2.156",
    "x-forwarded-host": "echo.example.com",
    "x-forwarded-port": "443",
    "x-forwarded-proto": "https",
    "x-forwarded-server": "f2822b3e1207",
    "x-real-ip": "10.10.2.156"
  },
  "method": "GET",
  "body": "",
  "fresh": false,
  "hostname": "echo.example.com",
  "ip": "10.10.2.156",
  "ips": [
    "10.10.2.156"
  ],
  "protocol": "https",
  "query": {},
  "subdomains": [
    "echo"
  ],
  "xhr": false,
  "os": {
    "hostname": "7b07a8ffdfeb"
  },
  "connection": {}
}

Hi @crazyelectron-io, welcome to the forum :wave:

The problem is that the UI (html+js) does not understand the process with the forever token.
It will start by accessing /api/v1/auth/refresh to see if there is a cookie stored in the browser (client cannot read the cookie).

This call fails because there is no cookie, and the access token is not checked, even if it exists and is valid in this call. We have added support for this use in the ngclient UI where it probes to see if it can actually access things without having requested an access token. This has not been implemented in the current default UI (named ngax), so here you need to fake the call to /api/v1/auth/refresh such that it returns code 200 and a response like:

{
    "AccessToken":"some-random-string-here"
}

This will convince the UI that it is indeed logged in and continue by sending the random string as the access token. The proxy then replaces the random string with the correct auth header, and everything works.

Based on my personal experiments, I believe that there are yet to be done for this.

In web ui, the websocket connection wss://duplicati/notifications?token=some-string needs valid query parameter token which value is valid JWT token (after further observation, this parameter can be removed out).

However, rewriting response of /api/v1/auth/refresh causes the parameter to be some-string.

The solution here is remove query parameter token in http request.

sample HAProxy configuration:

http-request set-query %[query,regsub(?token=null&,?,g)]
http-request set-query %[query,regsub(?token=null,,g)]
http-request set-query %[query,regsub(&token=null,,g)]

PS: The response of /api/v1/auth/refresh must be

{
    "AccessToken": null
}

or web ui overrides authorization header with value Bearer some-string.

1 Like

Hello.

I am also new around here :smiley: . First of all thanks for this great software.

I thought of dropping a working config so people have something to start with. This is behind a Authentik forward auth provider + traefik reverse proxy. Both of these as well as duplicati itself are running on docker containers on Unraid 7.0.1

First, in your traefik.yml, you need to activate the rewrite plugin:

On the root of the file

experimental:
  plugins:
    traefik-plugin-rewrite-body:
      moduleName: "github.com/traefik/plugin-rewritebody"
      version: "v0.3.1"

Then in fileConfig.yml:

Under routers

duplicati:
  entryPoints:
    - https
  rule: 'Host(`duplicati.yourdomain.com`)'
  service: duplicati
  middlewares:
    - auth
    - securityHeaders
  priority: 100

duplicati-auth:
  entryPoints:
    - https
  rule: 'Host(`duplicati.yourdomain.com`) && Path(`/api/v1/auth/refresh`)'
  service: fake-200-service
  priority: 300
  middlewares:
    - duplicati-auth-fake

Under services

duplicati:
  loadBalancer:
    servers:
      - url: http://your.duplicatilocal.ip:8200

fake-200-service:
  loadBalancer:
    servers:
      - url: "http://your.mocknginxlocal.ip:8090"

Under middlewares:

duplicati-auth-fake:
  plugin:
     traefik-plugin-rewrite-body:
        rewrites:
          - regex: ".*"
            replacement: "{\"AccessToken\":\"your-generated-token-from-the-command-line\"}"
            methods:
              - GET
              - POST
            contentTypes:
              - "application/json"
              - "text/plain"
              - "text/html"
            ignoreCase: true
            modifyResponse: true
            forceStatusCode: 200
            forceContentType: "application/json"

In order for this to work, I had to create a dummy nginx webserver docker container(the fake-200-service) that just returned status 200. I didn’t succeed on forcing traefik to return 200 otherwise. It always returned 418. So I just installed the linuxservers nginx app from Unraid appstore and used this location block inside the default config in nginx/site-confs/default.conf:

location / {
  add_header Content-Type application/json;

  # Just return a dummy response
  return 200;
}

the auth middleware is my authentik middleware and the securityHeaders middleware is a middleware that I use on all my routers but its not necessairly needed.

I have not yet encountered the web sockets issue mentioned by @tachyon but I will update this post if I come across it and solve it.

Sorry for some indentation issues, the forum is not behaving :smiley:

This should completely remove the password prompt from the GUI :slight_smile:

PS: @kenkendk it looks like the commandline nuget needed in order to run the command line utility is missing both from the official duplicati image as well as from the linuxservers one. I was able to manually wget it, extract it and copy it to the correct location and I was able to create a (very) long lived token afterwards though :smiley:

1 Like

Is this the missing System.CommandLine.dll file? Or are the binaries missing from the Docker images?

Hello,

Thank you for your work on Duplicati! I use your application primarily for backing up my server, especially my Docker volumes.

I don’t understand why there isn’t an option to disable the password, or the reasoning behind not including one. Having this as an advanced option would be very convenient for my setup.

I use Authentik with Traefik as a reverse proxy in front, secured with 2FA. However, I also rely on fully automated deployments with Ansible and Docker Compose. This makes it quite cumbersome to generate the token using the binary utilities and automate its injection into the Traefik proxy.

Adding this feature would be a significant improvement. For now, I’m still using an older version of Duplicati because it better suits my workflow.

Thanks again!

PS: Additionally, since there are no constraints on the password, a regular user can already set it to something simple like “admin,” even when running Duplicati in Docker with Env.

I am with others on this. I use NGINX Proxy Manager and Authentik for authentication to the GUI (well, I used to anyway). The fact that the password is required is a nuisance but something I’d be willing to live with if the password actually allowed me access to the GUI after Authentik allows it via Proxy Provider and after typing the password. Instead, it spits out the dreaded “Connection to server was rejected due to invalid authentication.” message. The easiest way around this is to bypass Authentik however, this server is something that is normally publicly accessible so just having a basic password does not meet my security requirements and what I had prior using 2fa. Having the advanced option to disable the password all together would be the best option in my opinion. Even better would be allowing access to the GUI through oauth2, openid or saml but understand that this would be more difficult to implement than simply the option to remove the password altogether for those of us that have a more secure method already implemented.

Hi @felixilgatto, welcome to the forum :waving_hand:

There are two reasons for this choice.

  1. Security is hard
    The more features there are in a security mechanism, the harder it is to secure. Adding support for non-authenticated usage will make it more likely there are issues with the login.

  2. Secure by default
    To make it harder to deploy Duplicati in an insecure manner, there is not an option to disable authentication. If there was one, many users would simply turn it off, leaving them vulnerable.

Your use is different because you have explicitly added an authentication layer that you want to use instead of Duplicati’s. Unfortunately, there is no way that Duplicati can detect this.

I can relate to that. Would it improve things if there was an option to specify the token instead of generating it? I am thinking something like generating a random string during setup, let’s say the value is SECURE_TOKEN_1234 for example.

The setup will then pass this value to the server, using an environment variable, similar to:

env:
  - CUSTOM_AUTH_TOKEN: SECURE_TOKEN_1234

The server will then accept this value as if it was a valid access token. The Proxy then needs to only send in this as a header for requests to the API:

Authorization: Bearer SECURE_TOKEN_1234

I am open to other suggestions as well.

True. The only solution for this would be to have a list of bad passwords and then prevent those. But at least here the choice is explicitly made by the user to weaken security.

Hi @Rgamer84, welcome to the forum :waving_hand:

i have not used Authentik enough to understand the flow here. To get it working, Authentik needs to call the login endpoint with the password, extract the access token in the response and set it in the headers for all requests to the api endpoints.

Do you know which part of this flow that is not working?

I’m sorry, but this forced password requirement is incredibly frustrating. I secure access to my Duplicati instance at the network level with a firewall. I understand turning it on by default to eliminate a common “foot gun” for normal users, but removing the option to disable it is not a reasonable solution.

Your use is different because you have explicitly added an authentication layer that you want to use instead of Duplicati’s. Unfortunately, there is no way that Duplicati can detect this.

Unfortunately, I take great issue with this reasoning. It is not my job to “convince” Duplicati that my network is set up correctly, and I don’t appreciate the software treating me like I’m too stupid to secure it properly.

Please, give us an environment variable to completely disable web authentication. I don’t care if it’s named “I_PROMISE_I_KNOW_WHAT_IM_DOING_AND_THIS_IS_REALLY_STUPID_BUT_DISABLE_WEB_AUTH”. I just want the option.

For now, I am staying on a previous version that does not have this forced authentication garbage, because there’s no sane way to disable it.

1 Like

Couldn’t Duplicati detect this via the headers from the forwarding proxy service? I know my HA server can do this via the backend settings:

Duplicati could have a setting for “trusted proxies” and perhaps an extra parameter of a secure token if you want to be even more secure (HAProxy version 3.0.9 - Configuration Manual).

I mainly use this to get around all the forwarded connections looking like they come from the IP of the proxy rather than from the original requester.

Just a thought.