Skip to content

kbfs service: init #25610

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

Merged
merged 7 commits into from
May 22, 2017
Merged

kbfs service: init #25610

merged 7 commits into from
May 22, 2017

Conversation

cmacrae
Copy link
Contributor

@cmacrae cmacrae commented May 8, 2017

Motivation for this change

It'd be nice to have a Keybase File System service on NixOS.
There's already been interest expressed (#22603).

These changes will lend themselves to those using any Keybase clients (CLI or GUI) by providing a kbfs mount.

Things done
  • Tested using sandboxing
    (nix.useSandbox on NixOS,
    or option build-use-sandbox in nix.conf
    on non-NixOS)
  • Built on platform(s)
    • NixOS
    • macOS
    • Linux
  • Tested compilation of all pkgs that depend on this change using nix-shell -p nox --run "nox-review wip"
  • Tested execution of all binary files (usually in ./result/bin/)
  • Fits CONTRIBUTING.md.

Sorry, something went wrong.

@pSub pSub added the 6.topic: nixos Issues or PRs affecting NixOS modules, or package usability issues specific to NixOS label May 9, 2017
@peterhoeg
Copy link
Member

Have you actually tried this? I was trying some time ago to get this running (you also need a keybase service service by the way) but I was unable to launch kbfs in a systemd user service due to mounts not working:

May 09 17:31:56 mildred kbfsfuse[13251]: 2017/05/09 17:31:56 mount helper error: fusermount: mount failed: Operation not permitted

@peterhoeg
Copy link
Member

Ahhh, the trick was /run/wrappers in the path - I had pkgs.fuse which doesn't work.

@cmacrae
Copy link
Contributor Author

cmacrae commented May 9, 2017

@peterhoeg Yeah, fusermount has a wrapper which needs to be called instead of the standard binary :)
Sorry, what do you mean I need a keybase service? These commits provide a kbfs service purely for running the Keybase Filesystem as a user service - for use with Keybase clients.


systemd.user.services.kbfs = {
description = "Keybase File System";
path = [ "/run/wrappers" ];
Copy link
Member

Choose a reason for hiding this comment

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

I think this should be /run/wrappers/bin instead

Copy link
Contributor Author

Choose a reason for hiding this comment

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

bin gets appended automatically. Using /run/wrappers/bin results in /run/wrappers/bin/bin

description = "Mountpoint for the Keybase filesystem.";
};

createMountPoint = mkOption {
Copy link
Member

@Mic92 Mic92 May 9, 2017

Choose a reason for hiding this comment

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

In which case the user does not wants to create the mountpoint? It will not work if the directory does not exists.

Copy link
Member

Choose a reason for hiding this comment

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

In other words, I don't think this needs to be an option. If the directory exists, everything is ok. If not, mkdir will fail with an error message.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure, I can understand your point. I just figured it might be useful for people who might want more control over the mountpoint's permissions. But if you feel it's unnecessary, I can take that out.

Copy link
Member

Choose a reason for hiding this comment

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

I'm with @Mic92 on this.

serviceConfig = {
ExecStart = "${cfg.package}/bin/kbfsfuse ${toString cfg.extraFlags} ${cfg.mountPoint}";
RestartSec = 3;
Restart = "always";
Copy link
Member

Choose a reason for hiding this comment

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

Restart = "on-failure" ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, on-failure should probably be used here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Now set to on-failure

postStop = "fusermount -u ${cfg.mountPoint}";
serviceConfig = {
ExecStart = "${cfg.package}/bin/kbfsfuse ${toString cfg.extraFlags} ${cfg.mountPoint}";
RestartSec = 3;
Copy link
Member

@Mic92 Mic92 May 9, 2017

Choose a reason for hiding this comment

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

What is the purpose of three seconds? Defaults to 100ms.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I sometimes found that fusermount would not have executed cleanly without this. Though, it was intermittent (sometimes it worked fine) - can do some thorough testing without this. If it seems fine, I'll just remove it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Okay, I've removed the RestartSec param

${optionalString cfg.createMountPoint
"test -d ${cfg.mountPoint} || mkdir -p ${cfg.mountPoint}"
}'';
postStop = "fusermount -u ${cfg.mountPoint}";
Copy link
Member

Choose a reason for hiding this comment

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

This doesn't work - systemd requires a full path, so it will have to be /run/wrappers/bin/fusermount.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Works fine for me? I've been using it the past week with no issues.
With the exec call in the kbfsfuse binary to fusermount, and this postStop operation, that's why I have path = [ "/run/wrappers" ]; defined.

Have you actually tested and confirmed this is the case? Because, if so, you'd actually see that any parameters for a systemd service that execute something (preStart, ExecStart, postStop, etc.) are in fact written to a shell script in the nix store, then the resulting systemd unit actually uses a full path to these scripts:

[Unit]
Description=Keybase File System

[Service]
Environment="LOCALE_ARCHIVE=/nix/store/mry6hpzxg44idk6pmqhcjvxzr665y43p-glibc-locales-2.25/lib/locale/locale-archive"
Environment="PATH=/run/wrappers/bin:/nix/store/k8dyhz9hrjydl23ny3131am6cws1rc07-coreutils-8.26/bin:/nix/store/dqzqrrampaf665laggc4ng33dfbxv5q7-findutils-4.6.0/bin:<*SNIP*>
Environment="TZDIR=/nix/store/ib356ccx25xixzjmj0fmgdv61kj1f398-tzdata-2016j/share/zoneinfo"



ExecStart=/nix/store/sqv84hq2j879z92pg71h2pmjr0kg58cc-kbfs-20170209.d1db463-bin/bin/kbfsfuse -label kbfs /home/cmacrae/keybase
ExecStartPre=/nix/store/n44y06vk1b18knzng7w9dswkr0dnbm38-unit-script/bin/kbfs-pre-start
ExecStopPost=/nix/store/0ag4fi7jgh1x23y3f7mxpvvihhkbqza5-unit-script/bin/kbfs-post-stop
Restart=always
RestartSec=3

Copy link
Member

Choose a reason for hiding this comment

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

I have tested it but it was using ExecStopPost instead of postStop and all the ExecXXXX require full paths. Sorry about the confusion.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah that makes sense then. No worries at all 👍

Copy link
Member

Choose a reason for hiding this comment

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

So here we need /run/wrappers/bin/fusermount inside serviceConfig.ExecStopPost.

@peterhoeg
Copy link
Member

Sorry, what do you mean I need a keybase service? These commits provide a kbfs service purely for running the Keybase Filesystem as a user service - for use with Keybase clients.

If you want to run the Keybase GUI app, the keybase service is also needed. I mistakenly thought kbfs also required that service but that doesn't seem to be the case. If you're not planning on adding it as part of this, no problem, I'll do it later.

cmacrae added 2 commits May 11, 2017 10:59

Verified

This commit was signed with the committer’s verified signature.
vcunat Vladimír Čunát

Verified

This commit was signed with the committer’s verified signature.
vcunat Vladimír Čunát
…rvice
@cmacrae
Copy link
Contributor Author

cmacrae commented May 11, 2017

Cheers @peterhoeg - no I personally don't plan on using the GUI app at this moment.
If there's interest in it, someone can add it, or perhaps if I end up using it in the near future, I'll contribute a service for that also :)

@peterhoeg
Copy link
Member

@cmacrae, if I try running kbfs as a service without having launched the keybase server, I get a lot of these:

May 11 22:32:42 dolores kbfsfuse[12295]: 2017-05-11T22:32:42.545579 ▶ [WARN kbfs connection.go:370] 023 (CONN KeybaseDaemonRPC e41ff7d2) Connection: error dialing transport: dial unix /run/user/1000/keybase/keybased.sock: connect: no such file or directory
May 11 22:32:42 dolores kbfsfuse[12295]: 2017-05-11T22:32:42.545627 ▶ [WARN kbfs connection.go:535] 025 KeybaseDaemonRPC: connection error: "dial unix /run/user/1000/keybase/keybased.sock: connect: no such file or directory"; retrying in 2s
May 11 22:32:44 dolores kbfsfuse[12295]: 2017-05-11T22:32:44.545793 ▶ [WARN kbfs connection.go:370] 026 (CONN CryptoClient f336a259) Connection: error dialing transport: dial unix /run/user/1000/keybase/keybased.sock: connect: no such file or directory
May 11 22:32:44 dolores kbfsfuse[12295]: 2017-05-11T22:32:44.545835 ▶ [WARN kbfs connection.go:535] 027 CryptoClient: connection error: "dial unix /run/user/1000/keybase/keybased.sock: connect: no such file or directory"; retrying in 2s

@cmacrae
Copy link
Contributor Author

cmacrae commented May 11, 2017

@peterhoeg You're absolutely right... that's poor testing from me; I guess it was just working because I'd always had a keybase process forked in the background from using the CLI.

I'll implement this now :)

@cmacrae
Copy link
Contributor Author

cmacrae commented May 11, 2017

Cool, so with the above changes, I've added a keybase service - there wasn't much to be done here.
In light of that, I've then added a requirement of the keybase service for the kbfs service, such that:

  • If kbfs starts, but keybase isn't up, it'll start it (kbfs automatically retries in a loop if keybase isn't up initially)
  • If keybase stops gracefully/ungracefully, kbfs is automatically stopped (as it can't survive without it).

I've also enabled the keybase service at the end of the kbfs implementation, is this good practice?

Cheers for pointing this out @peterhoeg - bit silly of me not to notice 😉

@cmacrae
Copy link
Contributor Author

cmacrae commented May 11, 2017

I'm not sure the failure Travis is reporting is related ☝️
error: attribute ‘docker_17_04’ missing, at /Users/travis/.nox/nixpkgs/pkgs/top-level/all-packages.nix:13:2

Don't see how I could have impacted that with the changes I made 🙂

@peterhoeg
Copy link
Member

I'm not sure the failure Travis is reporting is related

It wasn't you. Fixed in 49d3b43

@cmacrae
Copy link
Contributor Author

cmacrae commented May 12, 2017

@peterhoeg 👍🏻

@peterhoeg
Copy link
Member

I have a few comments but I will only have time over the weekend to go through it. I'll get back to you then.

@peterhoeg peterhoeg self-assigned this May 12, 2017
@cmacrae
Copy link
Contributor Author

cmacrae commented May 12, 2017

Alright, thanks @peterhoeg 👍


mountPoint = mkOption {
type = types.str;
default = "/keybase";
Copy link
Member

Choose a reason for hiding this comment

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

As this service is expected to be used by multiple users, defaulting to /keybase may not be the best thing. I would recommend "%h/keybase" instead.

Copy link
Contributor Author

@cmacrae cmacrae May 18, 2017

Choose a reason for hiding this comment

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

Cool, I didn't know about %h! Okay, sounds good
EDIT: Ah, I see it's a systemd thing

description = "Mountpoint for the Keybase filesystem.";
};

createMountPoint = mkOption {
Copy link
Member

Choose a reason for hiding this comment

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

I'm with @Mic92 on this.

};

package = mkOption {
type = types.package;
Copy link
Member

Choose a reason for hiding this comment

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

There is only one package that provides kbfs, so it seems redundant to make it configurable.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Cool, I'll strip the option out 👍


systemd.user.services.kbfs = {
description = "Keybase File System";
requires = [ "keybase.service" ];
Copy link
Member

Choose a reason for hiding this comment

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

We should also have after = [ "keybase.service" ]; although kbfs WILL still throw an error in the beginning because the keybase service isn't up.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure, makes sense - will add this.

requires = [ "keybase.service" ];
path = [ "/run/wrappers" ];
preStart = ''
${optionalString cfg.createMountPoint
Copy link
Member

Choose a reason for hiding this comment

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

Ref my note above about the mountpoint, we cannot use preStart and postStop scripts any longer because they don't know anything about %h or %t which we might use to specify the mount point. Should be ExecStartPre inside serviceConfig.

Copy link
Contributor Author

@cmacrae cmacrae May 18, 2017

Choose a reason for hiding this comment

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

Okay, I'll switch to using ExecStartPre :)

What does %t evaluate to? Is there a page of documentation noting these (%h, %t, etc.) and their value? I'd like to read up on them

EDIT: Ah, I see it's a systemd thing

${optionalString cfg.createMountPoint
"test -d ${cfg.mountPoint} || mkdir -p ${cfg.mountPoint}"
}'';
postStop = "fusermount -u ${cfg.mountPoint}";
Copy link
Member

Choose a reason for hiding this comment

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

So here we need /run/wrappers/bin/fusermount inside serviceConfig.ExecStopPost.

};
};

environment.systemPackages = [ cfg.package ];
Copy link
Member

Choose a reason for hiding this comment

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

We don't need to pollute the global environment with the kbfs package.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oh right? Okay. I only did this as I saw it implemented in other services (compton).
What with the abstraction layer providing an enable param, I think it's the expected behavior that it will take care of also ensuring the package is present on the system, no?

Copy link
Member

Choose a reason for hiding this comment

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

The service will pull in the package anyway, so it will be installed - you just will not be able to run kbfs directly from the command line. This is why it makes sense to put keybase into environment.systemPackages as you will use the keybase binary directly, but not kbfs.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Okay, I see. Is that inferred from the use of things like ${pkgs.kbfs}/bin/kbfsfuse?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Such that, Nix see's we want to evaluate ${pkgs.kbfs} and thus ensures it's present?

Copy link
Member

Choose a reason for hiding this comment

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

Exactly!

description = "Whether to start the Keybase service.";
};

package = mkOption {
Copy link
Member

Choose a reason for hiding this comment

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

Same as above, there is only one package.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure, I'll remove this param

description = "Keybase service";
serviceConfig = {
ExecStart = ''
${cfg.package}/bin/keybase service --auto-forked
Copy link
Member

Choose a reason for hiding this comment

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

Are you sure about ---auto-forked? What does that give us?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I don't think it's necessary, really. I only chucked it in there as that's what you get when running from a shell - so, yeah we can probably drop this. I'll remove it and test it, along with all the other changes.

${cfg.package}/bin/keybase service --auto-forked
'';
Restart = "on-failure";
};
Copy link
Member

Choose a reason for hiding this comment

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

We should probably harden things a little for both keybase and kbfs. PrivateTmp = true springs to mind.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yep, good catch

@cmacrae
Copy link
Contributor Author

cmacrae commented May 18, 2017

Okay, so I've implemented the changes outlined above, all seems to work good.

One weird side affect of changing to using serviceConfig.ExecStartPre/Post is that now every time the service is started... it seems to create a '$HOME' directory in the user's home directory, so for me that's:
/home/cmacrae/'$HOME'

I'm guessing this is a result of using mkdir -p %h/keybase (%h/keybase now being the default mountPoint value).

ExecStart = "${pkgs.kbfs}/bin/kbfsfuse ${toString cfg.extraFlags} ${cfg.mountPoint}";
ExecStopPost = "/run/wrappers/bin/fusermount -u ${cfg.mountPoint}";
Restart = "on-failure";
PrivateTmp = true;
Copy link
Member

@peterhoeg peterhoeg May 18, 2017

Choose a reason for hiding this comment

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

There's some padding missing here. When fixing that, please also squash all the commits and push again.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done :) I hope that will suffice

kbfs service: Use `%h/keybase` as the default mountpoint

kbfs service: remove createMountPoint opt & move pre/post steps to serviceConfig

kbfs service: Remove package option

kbfs service: Add `after = keybase.service` to systemd unit

kbfs service: Use private tmp

keybase service: Remove package option

keybase service: Use private tmp

kbfs service: Fix `PrivateTmp` indentation
@cmacrae
Copy link
Contributor Author

cmacrae commented May 18, 2017

Hopefully that'll do it :) Only thing now is to work out the above (the creation of this $HOME directory)

@peterhoeg peterhoeg merged commit abe0da4 into NixOS:master May 22, 2017
@peterhoeg
Copy link
Member

@cmacrae, it works perfectly fine here without any odd directories showing up. Did you by chance set the mountpoint to "$HOME/keybase" instead of "%h/keybase" ?

@cmacrae
Copy link
Contributor Author

cmacrae commented May 22, 2017

@peterhoeg Nope, as the default is now set to %h/keybase I just left that and didn't specify a mountpoint. When I look at this '$HOME' directory, it's only contents are: .local/share/keybase (with all the keybase files residing in that directory).

@peterhoeg
Copy link
Member

That's so odd - it's working for me.

@cmacrae
Copy link
Contributor Author

cmacrae commented May 25, 2017

@peterhoeg So, it looks like it's the keybase service that's actually creating this directory

@peterhoeg
Copy link
Member

What does systemctl --user show keybase | grep HOME show?

@mpcsh
Copy link
Contributor

mpcsh commented Jul 14, 2017

@cmacrae I'm interested in running the GUI app, and it requires the mountpoint to be /keybase, so I think it would be worth it to change the mountpoint to that. Also, the keybase service needs to have a requires and after - I think it should be network-online.service - because as it stands, even using services.keybase.enable = true and services.kbfs.enable = true in configuration.nix does nothing

@peterhoeg
Copy link
Member

it requires the mountpoint to be /keybase

The keybase-gui launch script assumes the fs mounted at /keybase but you can simply launch the binary directly. That's what I do when running keybase-gui as a systemd service:

  systemd.user.services.keybase-gui =
    let
      services = [ "keybase.service" "kbfs.service" ];
    in {
      description = "Keybase GUI";
      requires = services;
      after = services;
      serviceConfig = {
        ExecStart = "${pkgs.keybase-gui}/share/keybase/Keybase";
        PrivateTmp = true;
      };
    };
  };
}

Also, the keybase service needs to have a requires and after - I think it should be network-online.service

That is incorrect. keybase and kbfs both run as user services while network.service and network-online.service are system services. User services don't know anything about system services.

because as it stands, even using services.keybase.enable = true and services.kbfs.enable = true in configuration.nix does nothing

That's a different issue. Can you dump your configuration somewhere and we'll see how to get it working?

@mpcsh
Copy link
Contributor

mpcsh commented Jul 31, 2017

@peterhoeg sorry for the delay! You're right about user services, I wasn't thinking of that. Regardless, here's the relevant portion of my configuration.nix:

environment.systemPackages = with pkgs; [
  keybase
  keybase-gui
  kbfs
];

services.kbfs = {
  enable = true;
  mountPoint = "/keybase";
};

The problem I'm having is that neither keybase.service nor kbfs.service actually start when I log in.

@peterhoeg
Copy link
Member

Here is my entire keybase.nix config:

{ config, lib, pkgs, ... }:

let
  slice = "keybase.slice";

in {
  services.kbfs = {
    enable = true;
    mountPoint = "%t/kbfs";
    extraFlags = [
      "-label %u"
    ];
  };

  systemd.user.services = {
    keybase.serviceConfig.Slice = slice;
    kbfs = {
      environment = {
        KEYBASE_RUN_MODE = "prod";
      };
      serviceConfig.Slice = slice;
    };

    keybase-gui =
    let
      services = [ "keybase.service" "kbfs.service" ];
    in {
      description = "Keybase GUI";
      requires = services;
      after = services;
      serviceConfig = {
        ExecStart = "${pkgs.keybase-gui}/share/keybase/Keybase";
        PrivateTmp = true;
        Slice = slice;
      };
    };
  };
}

Then systemctl --user start keybase-gui brings up the GUI and starts whatever needs to be started.

@Janik-Haag Janik-Haag added the 12. first-time contribution This PR is the author's first one; please be gentle! label Jun 12, 2023
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 12. first-time contribution This PR is the author's first one; please be gentle!
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants