~stepbrobd/ngipkgs

e40efeec85a8c37e13ba62ece71d5cf7b9bb9673 — Fedi Jamoussi 2 months ago 7704882
openfire: init at 4.9.0 (#360)

* openfire: init at 4.9.0

* openfire: init project

Co-authored-by: Valentin Gagarin <valentin@gagarin.work>

---------

Co-authored-by: Valentin Gagarin <valentin@gagarin.work>
A pkgs/by-name/openfire/package.nix => pkgs/by-name/openfire/package.nix +56 -0
@@ 0,0 1,56 @@
{
  lib,
  maven,
  fetchFromGitHub,
  jdk_headless,
  makeWrapper,
}:
maven.buildMavenPackage rec {
  pname = "openfire";
  version = "4.9.0";

  src = fetchFromGitHub {
    owner = "igniterealtime";
    repo = "Openfire";
    rev = "v${version}";
    hash = "sha256-exZDH3wROQyw8WIQU1WZB3QoXseiSHueo3hiQrjQZGM=";
  };

  mvnJdk = jdk_headless;
  mvnHash = "sha256-PovHnAR10IxDTyoXCH4LCWZzIv6cNMl9JI0B4stDBo8=";

  # some deps require internet for tests
  mvnParameters = "-Dmaven.test.skip";

  nativeBuildInputs = [makeWrapper];

  installPhase = ''
    runHook preInstall

    mkdir -p $out/{bin,opt}

    cp -R ./distribution/target/distribution-base/* $out/opt
    ln -s $out/opt/lib $out/lib

    for file in openfire.sh openfirectl; do
      wrapProgram $out/opt/bin/$file \
        --set JAVA_HOME ${jdk_headless.home}

      install -Dm555 $out/opt/bin/$file -t $out/bin
    done

    # Used to determine if the Openfire state directory needs updating
    echo ${version} > $out/opt/version

    runHook postInstall
  '';

  meta = {
    description = "An XMPP server licensed under the Open Source Apache License";
    homepage = "https://github.com/igniterealtime/Openfire";
    license = lib.licenses.asl20;
    maintainers = with lib.maintainers; [];
    mainProgram = "openfire";
    platforms = lib.platforms.all;
  };
}

A projects/Openfire-IPv6/default.nix => projects/Openfire-IPv6/default.nix +15 -0
@@ 0,0 1,15 @@
{pkgs, ...} @ args: {
  packages = {
    inherit (pkgs) openfire;
  };
  nixos = {
    modules.services.openfire-server = ./service.nix;
    examples = {
      base = {
        path = ./example.nix;
        description = "Basic configuration, mainly used for testing purposes.";
      };
    };
    tests.openfire-server = import ./test.nix args;
  };
}

A projects/Openfire-IPv6/example.nix => projects/Openfire-IPv6/example.nix +6 -0
@@ 0,0 1,6 @@
{...}: {
  services.openfire-server = {
    enable = true;
    openFirewall = true;
  };
}

A projects/Openfire-IPv6/service.nix => projects/Openfire-IPv6/service.nix +185 -0
@@ 0,0 1,185 @@
{
  config,
  lib,
  pkgs,
  ...
}: let
  cfg = config.services.openfire-server;
in {
  options.services.openfire-server = {
    enable = lib.mkEnableOption "Openfire XMPP server";
    package = lib.mkPackageOption pkgs "openfire" {};

    autoUpdateState = lib.mkOption {
      type = lib.types.bool;
      default = true;
      description = ''
        When enabled, the state directory will be automatically updated to
        match the installed package version.

        For manually doing this, please refer to the
        [Openfire Upgrade Guide](https://download.igniterealtime.org/openfire/docs/latest/documentation/upgrade-guide.html).
      '';
    };

    servicePort = lib.mkOption {
      type = lib.types.port;
      default = 9090;
      description = ''
        The port on which Openfire should listen for insecure Admin Console access.
      '';
    };

    securePort = lib.mkOption {
      type = lib.types.port;
      default = 9091;
      description = ''
        The port on which Openfire should listen for secure Admin Console access.
      '';
    };

    openFirewall = lib.mkOption {
      type = lib.types.bool;
      default = false;
      description = ''
        Whether to open ports in the firewall for the server.
      '';
    };

    dataDir = lib.mkOption {
      type = lib.types.str;
      default = "${cfg.package}/opt";
      defaultText = lib.literalExpression ''"''${config.services.openfire.package}/opt"'';
      description = ''
        Where to load readonly data from.
      '';
    };

    stateDir = lib.mkOption {
      type = lib.types.str;
      default = "/var/lib/openfire";
      description = ''
        Where to store runtime data (logs, plugins, ...).

        If left at the default, this will be automatically created on server
        startup if it does not already exist. If changed, it is the admin's
        responsibility to make sure that the directory exists and is writeable
        by the `openfire` user.
      '';
    };
  };

  config = lib.mkIf cfg.enable {
    users.users.openfire = {
      description = "openfire server daemon user";
      home = cfg.stateDir;
      createHome = false;
      isSystemUser = true;
      group = "openfire";
    };
    users.groups.openfire = {};

    systemd.services.openfire-server = {
      path = [pkgs.rsync];
      description = "Openfire Server Daemon";
      serviceConfig = lib.mkMerge [
        {
          ExecStart = "${cfg.stateDir}/bin/openfire.sh";
          User = "openfire";
          Group = "openfire";
          Restart = "on-failure";
          WorkingDirectory = cfg.stateDir;
        }
        (lib.mkIf (cfg.stateDir == "/var/lib/openfire") {
          StateDirectory = "openfire";
        })
      ];
      environment.OPENFIRE_HOME = cfg.stateDir;
      wantedBy = ["multi-user.target"];
      after = ["network.target"];

      # Files under `OPENFIRE_HOME` require read-write permissions for Openfire
      # to work correctly, so we can't directly run it from the nix store.
      #
      # Instead, we need to copy those files into a directory which has proper
      # permissions, but we must only do this once, otherwise we risk
      # ovewriting server state information every time the server is upgraded.
      #
      # As such, if `version` already exists, we assume the rest of
      # the files do as well, and copy nothing.
      preStart = let
        # Update Openfire
        # https://download.igniterealtime.org/openfire/docs/latest/documentation/upgrade-guide.html
        updateState = ''
          tmpDir="/tmp/openfire-backup"
          oldVersion=$(cat "${cfg.stateDir}/version")
          newVersion=$(cat "${cfg.dataDir}/version")

          if [ $oldVersion != $newVersion ]; then
            echo "Attempting to update Openfire from $oldVersion to $newVersion"

            # Back up the Openfire state directory
            rsync -a "${cfg.stateDir}/" $tmpDir/

            # Clear old state
            rm -rf "${cfg.stateDir}/*"

            # Install new state
            rsync -a --chmod=u=rwX,go=rX "${cfg.package}/opt/" "${cfg.stateDir}/"

            # Copy old configuration
            # TODO: only backup these directories?
            rsync -a $tmpDir/plugins/ ${cfg.stateDir}/ --exclude=admin
            for dir in conf embedded-db enterprise resources/security; do
              [ -e $tmpDir/$dir ] && rsync -a $tmpDir/$dir ${cfg.stateDir}/;
            done

            rm -rf $tmpDir

            echo "Update complete"
          fi
        '';

        oldStateMessage = ''
          oldVersion=$(cat "${cfg.stateDir}/version")
          newVersion=$(cat "${cfg.dataDir}/version")

          cat <<EOF
          You are trying to run Openfire $newVersion
          with a systemd state directory created by Openfire $oldVersion.
          Until you update the state directory, Openfire will continue using $oldVersion.

          Possible workarounds:
          1. Enable "services.openfire-server.autoUpdateState" to automatically handle this.
          2. Export your data from Openfire $oldVersion,
              clear the state directory, and
              import your data to Openfire $newVersion

              See Openfire documentation for migrating state:
              https://download.igniterealtime.org/openfire/docs/latest/documentation/upgrade-guide.html
          EOF
        '';
      in ''
        set -e

        # Install package to state directory (initial run)
        if [ ! -e "${cfg.stateDir}"/version ]; then
          rsync -a --chmod=u=rwX,go=rX "${cfg.package}/opt/" "${cfg.stateDir}/"
        else
          if [ ${toString cfg.autoUpdateState} ]; then
            ${updateState}
          else
            ${oldStateMessage}
          fi
        fi
      '';
    };

    networking.firewall = lib.mkIf cfg.openFirewall {
      allowedTCPPorts = [
        cfg.servicePort
        cfg.securePort
      ];
    };
  };
}

A projects/Openfire-IPv6/test.nix => projects/Openfire-IPv6/test.nix +64 -0
@@ 0,0 1,64 @@
{
  lib,
  pkgs,
  sources,
  ...
}: {
  # NOTE:
  # - Run the test interactively to access the server: nix run .#nixosTests.Openfire-IPv6.openfire-server.driverInteractive
  # - Diable `Restrict Admin Console Access` in the `Server Settings`, else you won't be able to login.

  name = "openfire";
  meta = {
    maintainers = [];
  };

  nodes = {
    server = {config, ...}: {
      imports = [
        sources.modules.default
        sources.modules."services.openfire-server"
      ];

      services.openfire-server = {
        enable = true;
        openFirewall = true;
      };

      services.openssh = {
        enable = true;
        settings = {
          PermitRootLogin = "yes";
          PermitEmptyPasswords = "yes";
        };
      };
      security.pam.services.sshd.allowNullPassword = true;

      virtualisation.forwardPorts = let
        cfg = config.services.openfire-server;
      in [
        {
          from = "host";
          host.port = 2222;
          guest.port = 22;
        }
        {
          from = "host";
          host.port = cfg.servicePort;
          guest.port = cfg.servicePort;
        }
        {
          from = "host";
          host.port = cfg.securePort;
          guest.port = cfg.securePort;
        }
      ];
    };
  };

  testScript = ''
    start_all()
    server.wait_for_unit("openfire-server.service")
    server.wait_for_open_port(9090)
  '';
}