Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

nixos/qbittorrent: init #287923

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open

Conversation

fsnkty
Copy link
Member

@fsnkty fsnkty commented Feb 11, 2024

Description of changes

create a module to use qbittorrent as a service.

user notes

in ui settings changes

the config file does become editable once qbittorrent starts so changing settings within the UI does work however they will be need to be overwritten(removed) if you wish to "reapply" the exact config from setting serverConfig

password formatting

the password format that qbittorrent expects can be generated using this tool ( thanks Fea )

alternative UI's

custom webuis managed with nix are possible, example of VueTorrent use with fetchzip

serverConfig.Preferences.WebUI = {
  AlternativeUIEnabled = true;
  RootFolder = "${pkgs.fetchzip {
    url = "https://github.com/VueTorrent/VueTorrent/releases/download/v2.7.2/vuetorrent.zip";
    hash = "sha256-bJyI7RvVCf0M5vs8Qi+uAHv74CWxSDZ0Bb6zWJ4x4CM=";
  }}";
};

use before merge example

assumes you have this (https://github.com/fsnkty/nixpkgs/tree/init-nixos-qbittorrent) branch as an input named qbit

{
  inputs,
  pkgs,
  ...
}:
{
  imports = [ "${inputs.qbit}/nixos/modules/services/torrent/qbittorrent.nix" ];
  services.qbittorrent = {
    enable = true;
    package = inputs.qbit.legacyPackages.${pkgs.system}.qbittorrent-nox;
    serverConfig.LegalNotice.Accepted = true;
  };
}

blocking issues

  • sufficient test(s,ing) (Thank you undefined-landmark!)
  • serverConfig
    • gendeepINI gets a fitting type to use for this option.
    • qBittorrent.conf
      • more feedback / info on what does/does not cause qbit to "take in" new config.
      • understanding where the write permission on /var/lib/qBittorrent/qBittorrent/config/qBittorrent.conf comes from / why its possible (Thank you undefined-landmark again!)
  • openFirewall

nice to haves

pr notes

for service hardening I don't believe any of the following can be used simply because of the services purpose.
IPAddressDeny PrivateNetwork or restriction of AF_NETLINK AF_INET or AF_INET6 address families.
ProtectSystem cant be used without entirely declarative config

in my previous attempt at this here i was advised to simply make use of the service from the package, I'm unsure how to do this.
I started with an aim to follow https://github.com/qbittorrent/qBittorrent/blob/master/dist/unix/systemd/qbittorrent-nox%40.service.in I don't understand why it sets PrivateTmp to false however and have otherwise changed it significantly with service hardening and nix(os) specifics.

Things done

  • Built on platform(s)
    • x86_64-linux
    • aarch64-linux
    • x86_64-darwin
    • aarch64-darwin
  • For non-Linux: Is sandboxing enabled in nix.conf? (See Nix manual)
    • sandbox = relaxed
    • sandbox = true
  • Tested, as applicable:
  • Tested compilation of all packages that depend on this change using nix-shell -p nixpkgs-review --run "nixpkgs-review rev HEAD". Note: all changes have to be committed, also see nixpkgs-review usage
  • Tested basic functionality of all binary files (usually in ./result/bin/)
  • 24.05 Release Notes (or backporting 23.05 and 23.11 Release notes)
    • (Package updates) Added a release notes entry if the change is major or breaking
    • (Module updates) Added a release notes entry if the change is significant
    • (Module addition) Added a release notes entry if adding a new NixOS module
  • Fits CONTRIBUTING.md.

Add a 👍 reaction to pull requests you find important.

@github-actions github-actions bot added 6.topic: nixos Issues or PRs affecting NixOS modules, or package usability issues specific to NixOS 8.has: module (update) This PR changes an existing module in `nixos/` labels Feb 11, 2024
@fsnkty fsnkty mentioned this pull request Feb 11, 2024
13 tasks
@fsnkty fsnkty force-pushed the init-nixos-qbittorrent branch from d4984ad to dc84cc2 Compare February 11, 2024 03:42
@ofborg ofborg bot added 10.rebuild-darwin: 0 This PR does not cause any packages to rebuild on Darwin 10.rebuild-linux: 1-10 labels Feb 11, 2024
@fsnkty fsnkty force-pushed the init-nixos-qbittorrent branch from dc84cc2 to 35bc83d Compare February 17, 2024 01:19
@github-actions github-actions bot added 8.has: documentation This PR adds or changes documentation 8.has: changelog labels Feb 17, 2024
@fsnkty

This comment was marked as outdated.

@camilosampedro
Copy link

@nu-nu-ko

I've added you to contributors here now that I've taken parts of your work in the tests and since you had added yourself to them in your branch I assume you're still keen to? let me know if not and ill remove of course.

If you are allowed, I'd like to be a contributor, thank you!

still unsure how to do assertions for options on other options so for now I've left it be

Do you mean during tests?

@fsnkty

This comment was marked as outdated.

@camilosampedro
Copy link

camilosampedro commented Feb 18, 2024

I've seen this in deluge:

https://github.com/NixOS/nixpkgs/blob/nixos-23.11/nixos/modules/services/torrent/deluge.nix

When you use declarative = true, it tries to use the options that are required to be declarative, and if they are not set it errors:

The option `services.deluge.authFile' is used but not defined.

But it doesn't happen the other way, if authFile is declared and declarative = false

@fsnkty

This comment was marked as outdated.

@camilosampedro
Copy link

Is it weird if there were two separate sets of options?

qbittorrent-nox
qbittorrent-nox-declarative?

@camilosampedro
Copy link

Also, related and unrelated. I was seeing that the web ui and other services were being unresponsive after a couple of hours.

I thought that the IO of qbittorrent was bringing the system down, but after 2 days of trying everything, it was my network device's firmware. 😅

I'll try to retest it this week.

Copy link
Member

@nevivurn nevivurn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Afaik, it is possible to pass in the webui and torrenting port via command line arguments, and I assume these arguments take precedence over other configuration options. This should let us implement openFirewall without forcing users to use the declarative config.

a better way to set CapabilityBoundingSet and SystemCallFilter to restrict all.

CapabilityBoundingSet is an allowlist, the strictest setting is just the empty string. Although, given that we 1) don't run qbittorrent as root, 2) set NoNewPrivileges, 3) don't set AmbientCapabilities, omitting it entirely is probably fine.

I personally think @system-service is a good enough default for SystemCallFilter.

I don't understand why it sets PrivateTmp to false

This comment in the upstream PR mentions adding torrents through the command line. Not sure if this is actually true.

ProtectSystem needs to be disabled if we aren't using declaritiveConfig

I don't think ProtectSystem should be enabled here. It will break users with per-category/torrent save paths, no matter what.

imo, we don't need lock down every option by default. Users that want to further harden their system can easily add these options in their own nixos configuration.

@fsnkty fsnkty force-pushed the init-nixos-qbittorrent branch from d60b403 to bc0a267 Compare February 20, 2024 04:26
@fsnkty

This comment was marked as outdated.

Copy link
Member

@nevivurn nevivurn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see why we wouldn't set it regardless personally, leaving it empty seems to do nothing as according to systemd-analyze security qbittorrent on my system.

👍

I agree to some extent, id much rather every service does at least up to "breaks as little as possible"

I completely agree, we can enable everything that probably won't break users.

There are still a couple options left that I feel like are a tad bit too opinionated, namely

  • non-default torrenting port
  • umask 0066

nixos/modules/services/torrent/qbittorrent.nix Outdated Show resolved Hide resolved
@fsnkty

This comment was marked as outdated.

@nevivurn
Copy link
Member

an empty list isn't equivalent sooo 😅

Ah, did not realize the empty list wasn't doing anything. "" should work.

there is no default? its randomly selected from within a range from what i can tell. the non default web port is questionable.. but I think defaulting to 8080 is kinda dumb, happy to change it.

The bittorrent port is chosen randomly by default, and it is quite unexpected for nixos to fix an arbitrary port number by default. Similar with the web port, I don't see a reason for the NixOS module to deviate from the upstream default. With this module, it would be trivial for users to change it as needed, anyway.

umask 0066

how is this opinionated? in what case may this break something?

I think this would break users trying to access downloaded files without needing to run as the qbittorrent user, likely a common use case. Plus, if we're going for paranoid settings, why is it not 0077?

@fsnkty fsnkty force-pushed the init-nixos-qbittorrent branch from e3913e0 to c289233 Compare February 24, 2024 12:16
@wegank wegank added the 2.status: merge conflict This PR has merge conflicts with the target branch label Mar 20, 2024
@fsnkty fsnkty force-pushed the init-nixos-qbittorrent branch from c289233 to 07d92b9 Compare March 27, 2024 23:34
@github-actions github-actions bot removed 8.has: documentation This PR adds or changes documentation 8.has: changelog labels Mar 27, 2024
@fsnkty fsnkty force-pushed the init-nixos-qbittorrent branch 2 times, most recently from aa2f5a4 to 145b6e3 Compare March 27, 2024 23:43
@fsnkty
Copy link
Member Author

fsnkty commented Oct 8, 2024

@fsnkty wouldn't it be best to make torrentingPort default to null? Then people wouldn't have to set it if they didn't want to.

ok that commit is named poorly, should be "default null torrentingPort" but whatever itll get squashed xP
but yeah good catch, should be alright to not specify a torrenting port now 👍

@fsnkty
Copy link
Member Author

fsnkty commented Oct 8, 2024

edit: misread comment timeline but these questions still nice to know

anyone aware of qbit writing global section config values?
also anyone aware of config value depth greater than 5?

@fsnkty fsnkty force-pushed the init-nixos-qbittorrent branch 2 times, most recently from 06914f9 to ed44619 Compare October 8, 2024 03:42
@fsnkty
Copy link
Member Author

fsnkty commented Oct 8, 2024

alright for now I have no clue why qbit is seemingly able to enable write permissions on that file.
if you want to reapply from config you can

  1. stop the qbit service
  2. remove the file /var/lib/qBittorrent/qBittorrent/config/qBittorrent.conf
  3. apply new system config

its hacky but for now thats whatever.
fyi custom webuis work, e.g..

serverConfig = { 
  Preferences = {
    WebUI = {
      AlternativeUIEnabled = true;
      RootFolder = "${pkgs.fetchzip {
        url = "https://github.com/VueTorrent/VueTorrent/releases/download/v2.7.2/vuetorrent.zip";
        hash = "sha256-bJyI7RvVCf0M5vs8Qi+uAHv74CWxSDZ0Bb6zWJ4x4CM=";
      }}";
    };
  };
};

@fsnkty fsnkty force-pushed the init-nixos-qbittorrent branch from 61fc435 to ed44619 Compare October 8, 2024 06:41
@sajenim
Copy link

sajenim commented Oct 11, 2024

to help with coverage

[Application]
FileLogger\Age=1
FileLogger\AgeType=1
FileLogger\Backup=true
FileLogger\DeleteOld=true
FileLogger\Enabled=true
FileLogger\MaxSizeBytes=66560
FileLogger\Path=/app/qBittorrent/data/logs

[AutoRun]
OnTorrentAdded\Enabled=false
OnTorrentAdded\Program=
enabled=false
program=

[BitTorrent]
Session\BTProtocol=Both
Session\DHTEnabled=false
Session\DefaultSavePath=/data/torrents
Session\DisableAutoTMMByDefault=false
Session\DisableAutoTMMTriggers\CategorySavePathChanged=false
Session\DisableAutoTMMTriggers\DefaultSavePathChanged=false
Session\ExcludedFileNames=
Session\GlobalDLSpeedLimit=8000
Session\GlobalUPSpeedLimit=200
Session\LSDEnabled=false
Session\PeXEnabled=false
Session\Port=32372
Session\Preallocation=true
Session\QueueingSystemEnabled=false
Session\TempPath=/data/torrents
Session\TempPathEnabled=false

[Core]
AutoDeleteAddedTorrentFile=Never

[Meta]
MigrationVersion=6

[Network]
PortForwardingEnabled=false
Proxy\OnlyForTorrents=false

[Preferences]
Advanced\RecheckOnCompletion=false
Advanced\trackerPort=9000
Advanced\trackerPortForwarding=false
Connection\ResolvePeerCountries=true
Downloads\SavePath=/config/downloads/
Downloads\TempPath=/config/downloads/temp/
DynDNS\DomainName=changeme.dyndns.org
DynDNS\Enabled=false
DynDNS\Password=
DynDNS\Service=DynDNS
DynDNS\Username=
General\Locale=en
MailNotification\email=
MailNotification\enabled=false
MailNotification\password=
MailNotification\req_auth=true
MailNotification\req_ssl=false
MailNotification\[email protected]
MailNotification\smtp_server=smtp.changeme.com
MailNotification\username=
Scheduler\days=EveryDay
Scheduler\end_time=@Variant(\0\0\0\xf\x4J\xa2\0)
Scheduler\start_time=@Variant(\0\0\0\xf\x1\xb7t\0)
WebUI\Address=*
WebUI\AlternativeUIEnabled=false
WebUI\AuthSubnetWhitelist=127.0.0.1/32, 192.168.20.0/24
WebUI\AuthSubnetWhitelistEnabled=true
WebUI\BanDuration=3600
WebUI\CSRFProtection=true
WebUI\ClickjackingProtection=true
WebUI\CustomHTTPHeaders=
WebUI\CustomHTTPHeadersEnabled=false
WebUI\HTTPS\CertificatePath=
WebUI\HTTPS\Enabled=false
WebUI\HTTPS\KeyPath=
WebUI\HostHeaderValidation=false
WebUI\LocalHostAuth=false
WebUI\MaxAuthenticationFailCount=5
WebUI\Port=8080
WebUI\ReverseProxySupportEnabled=false
WebUI\RootFolder=/app/vuetorrent
WebUI\SecureCookie=true
WebUI\ServerDomains=*
WebUI\SessionTimeout=3600
WebUI\TrustedReverseProxiesList=
WebUI\UseUPnP=false

[RSS]
AutoDownloader\DownloadRepacks=true
AutoDownloader\SmartEpisodeFilter=s(\\d+)e(\\d+), (\\d+)x(\\d+), "(\\d{4}[.\\-]\\d{1,2}[.\\-]\\d{1,2})", "(\\d{1,2}[.\\-]\\d{1,2}[.\\-]\\d{4})"

@Pandapip1 Pandapip1 added the 2.status: merge conflict This PR has merge conflicts with the target branch label Oct 23, 2024
@Titaniumtown
Copy link
Contributor

In regards to the serverConfig changes, on a nixos-rebuild switch, shouldn't it stop the qbittorrent service, apply the new settings, then start it? I don't observe this functionality locally.

@fsnkty
Copy link
Member Author

fsnkty commented Nov 13, 2024

In regards to the serverConfig changes, on a nixos-rebuild switch, shouldn't it stop the qbittorrent service, apply the new settings, then start it? I don't observe this functionality locally.

that would be an approach if I was more sure of it.
my personal ideal would be the serverConfig acting as a base and then reapplying differences in the existing file as to keep both managed and unmanaged config, iirc nextcloud module here does something similar to this?
since that would afaict remove the need for the ability to disable managed config outright.

I haven't looked into doing either just yet as I wasn't sure they'd be whole solutions. well that and I've been very low availability to do much of anything 😅

@adrlau
Copy link
Contributor

adrlau commented Nov 27, 2024

Regarding (could not reply there) #357000 (comment)

Legal notice was added as my train of thought went, let's add all the cli options as explicit options, and let all the rest just be set through the rfc42 style settings. (Legal notice I think might have just showed up in a log as an option and not necessarily in the help message). I do think the config file takes presidence over launch flags though.

My take on the config file was that it should probably just stay mutable, and either apply a diff of changes like discussed above, or just override the config at service relaunch. (Thought I will not argue against anyone or any work done)

Edit: in my testing the config file seemed to be properly owerwritten at least on rebuild if you changed options in the settings.

Edit: would be cool to see a nixos native way of binding a interface with systemd, if one doesn't fully trust the binding innside qbittorrent. (At least think systemd had options for this)

I'd be happy to contribute some tests or other work, when I get the time again.

@fsnkty
Copy link
Member Author

fsnkty commented Nov 27, 2024

Regarding (could not reply there) #357000 (comment)

Legal notice was added as my train of thought went, let's add all the cli options as explicit options, and let all the rest just be set through the rfc42 style settings. (Legal notice I think might have just showed up in a log as an option and not necessarily in the help message). I do think the config file takes presidence over launch flags though.

I like the idea but my first idea would be allowing arbitrary command line arguments, especially because some can take custom values right

My take on the config file was that it should probably just stay mutable, and either apply a diff of changes like discussed above, or just override the config at service relaunch. (Thought I will not argue against anyone or any work done)

what I atm believe to be the ideal would be the applying a diff over the existing ( remaining mutable file ) approach but don't yet know how to approach this in a reliable way

applying changes at the relaunch of the service is an approach I don't have as much confidence in but if others can vouch for known good approach to it I'd like to look into it some more

Edit: in my testing the config file seemed to be properly owerwritten at least on rebuild if you changed options in the settings.

love some more confirmation on that! had others confirm it to me personally earlier but haven't gotten consistency with it myself though I haven't given it a proper test in a good while

Edit: would be cool to see a nixos native way of binding a interface with systemd, if one doesn't fully trust the binding innside qbittorrent. (At least think systemd had options for this)

great point, definitely something to look into

I'd be happy to contribute some tests or other work, when I get the time again.

sweet! would love to have your view on how and what should be tested!

@magneticflux-
Copy link
Contributor

magneticflux- commented Dec 11, 2024

what I atm believe to be the ideal would be the applying a diff over the existing ( remaining mutable file ) approach but don't yet know how to approach this in a reliable way

I don't see how that could be accomplished in a reliable way without a full codec for the qbittorrent config that specified serialization and merging rules (which is very likely infeasible). Just being able to generate the custom INI format is already an accomplishment!

The only realistic options I see are fully mutable (just let qbittorrent create its stuff under --profile and never touch it) or fully immutable (mount a Nix store path as the config file, forcing it to be read-only). Personally, I would like to just have it mutable so I can quickly change settings in the UI, but more dedicated users might want to painstakingly translate their existing config to Nix.


I would also appreciate including an automatic nginx virtual host option based on other services. As a start, you could add this to the config:

nginx = mkOption {
  type = types.submodule {
    options = {
      enable = mkEnableOption "the nginx reverse proxy virtual host";
      hostname = mkOption {
        description = ''
          The hostname for the nginx virtual host.
        '';
        example = "qbittorrent.example.com";
        type = types.str;
      };
    };
  };
  description = "nginx config TODO";
  default = { };
};

and this to the result:

services.nginx = mkIf cfg.nginx.enable {
  enable = true;
  virtualHosts.${cfg.nginx.hostname} = {
    locations."/" = {
      proxyPass = "http://localhost:${toString cfg.webuiPort}/";
    };
  };
};

In this case I also don't want the web UI port to be opened in the firewall, but the torrenting port (if specified) should be, so having separate options there would be nice.


Should we even allow specifying an absolute state path instead of the SystemD default of /var/lib/ here? It's recommended to do this over tmpfiles.d, and most other services don't both making this easily customizable (but power users can always override this!).

Just specify StateDirectory = "qbittorrent"; and if the user opts-in to an immutable config you can still make a read-only symlink to the Nix store using tmpfiles.d.

@undefined-landmark
Copy link

undefined-landmark commented Jan 6, 2025

Would love to see a qbittorrent service available for NixOS. Most of the services I run on my server are already available as native NixOS services, adding qbittorrent to that list would be great. It's nice to see that there's already work done in this direction.

Are there any blocking issues at the moment where help would be appreciated? If so I'd be happy to look into it. Just saw that the blocking issues are listed in the first post...

With regards to the config, which seems to be one of the open issues, I think @magneticflux- summarized it well. Either make it fully mutable or fully immutable. Depending on the serverConfig value. So basically what #357000 did. This is the most simple solution, I think. More refined merging could also be added at a later stage. Because that would definitely be nice.

@undefined-landmark
Copy link

undefined-landmark commented Jan 7, 2025

For those following this thread. I made a PR with an initial testing setup at fsnkty's fork. See: fsnkty#1.

@undefined-landmark
Copy link

undefined-landmark commented Jan 9, 2025

I added an extraArgs option, to pass command line arguments to qbittorrent, to the PR. See: fsnkty#1.

@undefined-landmark
Copy link

Wrt the blocking issue:

understanding where the write permission on /var/lib/qBittorrent/qBittorrent/config/qBittorrent.conf comes from / why its possible

After a quick look at the qbittorrent source code it seems that the qBittorrent.conf file is deleted and than written again: https://github.com/qbittorrent/qBittorrent/blob/c622d50814f4fea5c4fbe13b9bde360dea60ec9b/src/base/settingsstorage.cpp#L168

The qBittorrent.conf that is generated by this module has readonly permissions (1400) for the user qbittorrent but is located in a folder, "${cfg.profileDir}/qBittorrent/config/, with full permissions (700). Because of this it's possible for the application to delete the link to the nix store and replace it with a new config file with full permissions.

@undefined-landmark
Copy link

undefined-landmark commented Jan 24, 2025

Tried to parse the qBittorrent.conf file and turn it into an attribute set.

My solution was to turn the transform the ini into a TOML file, as there is a builtin fromTOML function in nix. It seems to function well and almost all of the lines of the config, that was posted by sajenim, are parsed correctly. The only line giving issues atm, as far as I can see is AutoDownloader\SmartEpisodeFilter=s(\\d+)e(\\d+), (\\d+)x(\\d+), "(\\d{4}[.\\-]\\d{1,2}[.\\-]\\d{1,2})", "(\\d{1,2}[.\\-]\\d{1,2}[.\\-]\\d{4})"

Here is the proof of concept:

let
  lib = import <nixpkgs/lib>;

  # Reads the config file and turns it into a list of lines
  #
  # Empty lines are removed.
  readQbitConf = path: let
    iniFile = builtins.readFile path;
    lines = lib.strings.splitString "\n" iniFile;
    nonEmpty = lib.filter (line: line != "") lines;
  in
    nonEmpty;

  # Adds the blockname to subsequent lines and removes blocks
  #
  # So:
  # [
  # "[Application]"
  # "FileLogger\\Age=1"
  # "FileLogger\\Backup=true"
  # ]
  # Becomes:
  # [
  # "Application\\FileLogger\\Age=1"
  # "Application\\FileLogger\\Backup=true"
  # ]
  addBlockName = lines: let
    isBlock = line:
      builtins.match "^\\[.*]$" line != null;

    getBlockName = line:
      builtins.substring 1 (builtins.stringLength line - 2) line;

    accumulateBlockNames = acc: line:
      if isBlock line
      then {
        currBlock = getBlockName line;
        outLines = acc.outLines;
      }
      else {
        currBlock = acc.currBlock;
        outLines = acc.outLines ++ ["${acc.currBlock}\\${line}"];
      };

    initialAcc = {
      currBlock = "";
      outLines = [];
    };

    blockLines = lib.foldl accumulateBlockNames initialAcc lines;
    flatBlockLines = lib.flatten blockLines.outLines;
  in
    flatBlockLines;

  # Transforms ini key to TOML key and converts TOML non-compliant values into
  # literal strings
  #
  # The qBittorrent config contains values that are not available in TOML. This
  # function turns any values that are not either digits only, true or false into
  # literal strings.
  #
  # So:
  # [
  # "Application\\FileLogger\\Age=1"
  # "Application\\FileLogger\\Backup=true"
  # "Application\\FileLogger\\Path=/app/qBittorrent/data/logs"
  # ]
  # Becomes:
  # [
  # "Application.FileLogger.Age=1"
  # "Application.FileLogger.Backup=true"
  # "Application.FileLogger.Path='/app/qBittorrent/data/logs'"
  # ] 
  formatLines = lines: let
    addDots = line:
      builtins.replaceStrings ["\\"] ["."] line;

    isAcceptedType = value:
      builtins.match "^[[:digit:]]+$|^true$|^false$" value != null;

    correctType = value:
      if isAcceptedType value
      then value
      else "'${value}'";

    correctValueTypes = line: let
      splitKeyValue = builtins.match "(.*)=(.*)" line;
      key = builtins.head splitKeyValue;
      value = builtins.tail splitKeyValue;
      correctValue = correctType (builtins.toString value);
    in "${key}=${correctValue}";

    formatLine = line: let
      dotted = addDots line;
      correctedTypes = correctValueTypes dotted;
    in
      correctedTypes;

    formattedLines = builtins.map formatLine lines;
  in
    formattedLines;

  # Parse a qBittorrent config and convert it to an attribute set
  #
  # This function transforms the qBittorrent ini format into a TOML format.
  # After it is turned into a TOML format it can be converted to an attribute
  # set by leveraging the builtins.fromTOML function.
  parseQbitConf = path: let
    qbitConf = readQbitConf path;
    blockLines = addBlockName qbitConf;
    processedLines = formatLines blockLines;
    tomlFile = lib.concatStringsSep "\n" processedLines;
    parsedToml = builtins.fromTOML tomlFile;
  in
    parsedToml;
in
  parseQbitConf ./qBittorrent.conf

It is the first piece of nix code that I wrote, aside from my configuration file, so any pointers are appreciated :).

Another, maybe simpler option, would be to introduce a dependency to another program/language to make the conversion to TOML a bit easier and more compact.

fsnkty and others added 2 commits January 29, 2025 11:11
optional torretingPort firewall

feathecutie's improved genDeepINI

update desc for Password_PBKDF2 & misc

openFirewall mkEnableOption
remove `.` from mkEnableOption desc

update maintainer name & reword openFirewall desc
only set config if not null and remove write perm

silly copy n paste mistake

return stickybit

Add default for serverConfig and add tests

Add extraArgs option and test

Make webuiPort option optional

Add interaction via api to tests

Use reboot instead of shutdown and start

nixfmt tests

Add restartTriggers so that a new config is loaded on change

Use specialisations in tests

To simulate module option changes

nixfmt tests
@fsnkty fsnkty force-pushed the init-nixos-qbittorrent branch from c2a49e2 to d9c2b86 Compare January 28, 2025 22:12
@ofborg ofborg bot removed the 2.status: merge conflict This PR has merge conflicts with the target branch label Jan 28, 2025
@fsnkty
Copy link
Member Author

fsnkty commented Jan 28, 2025

huge thanks to undefined-landmark for effectively resolving a couple blocking issues here, now as far as im concerned there is just the one left, the genINI type.
I think at this rate it would be more than fine to seperate the generated/declaritive config concern into a PR of its own, or take another approach to handling it, as the INI format that qbit uses seems to not match existing INI handling in nixpkgs from my memory.

for now I've just included their changes, removed resolved changes, and rebased onto master once again but there does seem to be some more changes required to have tests run, I've likely missed something.

as for the fitting type of genINI being a blocking issue, i would very much appreciate experienced contributors to nixpkgs comments on if thats absolutely true and why, as well as any recomendations for alternatives etc.

@undefined-landmark
Copy link

I enjoyed helping out a lot, learned a lot about Nix along the way.

Sound plan to make the advanced config handling into a separate PR. With my poc, posted before, I think it's possible to read the qbittorent ini format and merge it with the declarative config, but it will take some more thought to refine it and make it ready to merge.

I can take a look at the queued and skipped tests. In my PR I always ran the tests manually so I didn't investigate the CI way to run tests. Seems like there needs to be a passthru specified someplace.

For the fitting types. You don't mean fitting types to the serverConfig option right? Do you have any pointers on fitting types to a function? Like I said, I'm pretty new to Nix, so I'm not so sure what it entails to fit types to a function.


serverConfig = mkOption {
default = null;
type = unspecified;
Copy link
Contributor

@eclairevoyant eclairevoyant Jan 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is to ensure merge would work correctly, but that we can also set some defaults; see https://nixos.org/manual/nixos/unstable/#sec-freeform-modules.

Also, I'd like to set Preferences.WebUI.UseUPnP = false; as a default, as this would avoid unnecessary exposure by default.

Suggested change
type = unspecified;
type = submodule {
freeformType = attrsOf (attrsOf anything);
options.Preferences.WebUI.UseUPnP = mkEnableOption "UPnP for access to the qbittorrent WebUI";
};

Copy link

@undefined-landmark undefined-landmark Jan 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the suggestions! At the moment setting any settings through serverConfig would make the settings "immutable". So on reboot or change all settings would be reset to default, except for the ones set in serverConfig. So if we apply this suggestion someone could unexpectedly find all their settings reset on a reboot/change.

I agree that it'd be good to disable UPnP by default. Unfortunately I don't think there is any other way to enable/disable UPnP than through serverConfig/qbittorrent.conf. Once we make merging of serverConfig with the current qbittorrent.conf possible your suggestion can be added.

];
};
};
config = lib.mkIf cfg.enable {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit

Suggested change
config = lib.mkIf cfg.enable {
config = mkIf cfg.enable {

Comment on lines +112 to +113
extraArgs = lib.mkOption {
type = lib.types.listOf lib.types.str;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit

Suggested change
extraArgs = lib.mkOption {
type = lib.types.listOf lib.types.str;
extraArgs = mkOption {
type = listOf str;

port
path
nullOr
unspecified
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
unspecified
listOf
attrsOf
anything
submodule

Copy link
Contributor

@eclairevoyant eclairevoyant left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We'd also want to remove this, oops.

};

serverConfig = mkOption {
default = null;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
default = null;

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
6.topic: nixos Issues or PRs affecting NixOS modules, or package usability issues specific to NixOS 8.has: module (new) This PR adds a module in `nixos/` 8.has: module (update) This PR changes an existing module in `nixos/` 10.rebuild-darwin: 0 This PR does not cause any packages to rebuild on Darwin 10.rebuild-linux: 1-10
Projects
None yet
Development

Successfully merging this pull request may close these issues.