Merge pull request #226288 from Luflosi/kubo-better-api-access

nixos/kubo: improve API access
This commit is contained in:
John Ericson 2023-04-15 15:07:01 -04:00 committed by GitHub
commit 0794f40589
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 109 additions and 62 deletions

View File

@ -137,7 +137,9 @@ In addition to numerous new and upgraded packages, this release has the followin
- `keepassx` and `keepassx2` have been removed, due to upstream [stopping development](https://www.keepassx.org/index.html%3Fp=636.html). Consider [KeePassXC](https://keepassxc.org) as a maintained alternative.
- The `services.kubo.settings` option is now no longer stateful. If you changed any of the options in `services.kubo.settings` in the past and then removed them from your NixOS configuration again, those changes are still in your Kubo configuration file but will now be reset to the default. If you're unsure, you may want to make a backup of your configuration file (probably /var/lib/ipfs/config) and compare after the update.
- The [services.kubo.settings](#opt-services.kubo.settings) option is now no longer stateful. If you changed any of the options in [services.kubo.settings](#opt-services.kubo.settings) in the past and then removed them from your NixOS configuration again, those changes are still in your Kubo configuration file but will now be reset to the default. If you're unsure, you may want to make a backup of your configuration file (probably /var/lib/ipfs/config) and compare after the update.
- The Kubo HTTP API will no longer listen on localhost and will instead only listen on a Unix domain socket by default. Read the [services.kubo.settings.Addresses.API](#opt-services.kubo.settings.Addresses.API) option description for more information.
- The EC2 image module no longer fetches instance metadata in stage-1. This results in a significantly smaller initramfs, since network drivers no longer need to be included, and faster boots, since metadata fetching can happen in parallel with startup of other services.
This breaks services which rely on metadata being present by the time stage-2 is entered. Anything which reads EC2 metadata from `/etc/ec2-metadata` should now have an `after` dependency on `fetch-ec2-metadata.service`
@ -340,7 +342,7 @@ In addition to numerous new and upgraded packages, this release has the followin
[headscale's example configuration](https://github.com/juanfont/headscale/blob/main/config-example.yaml)
can be directly written as attribute-set in Nix within this option.
- `services.kubo` now unmounts `ipfsMountDir` and `ipnsMountDir` even if it is killed unexpectedly when 'autoMount` is enabled.
- `services.kubo` now unmounts `ipfsMountDir` and `ipnsMountDir` even if it is killed unexpectedly when `autoMount` is enabled.
- `nixos/lib/make-disk-image.nix` can now mutate EFI variables, run user-provided EFI firmware or variable templates. This is now extensively documented in the NixOS manual.

View File

@ -22,6 +22,18 @@ let
configFile = settingsFormat.generate "kubo-config.json" customizedConfig;
# Create a fake repo containing only the file "api".
# $IPFS_PATH will point to this directory instead of the real one.
# For some reason the Kubo CLI tools insist on reading the
# config file when it exists. But the Kubo daemon sets the file
# permissions such that only the ipfs user is allowed to read
# this file. This prevents normal users from talking to the daemon.
# To work around this terrible design, create a fake repo with no
# config file, only an api file and everything should work as expected.
fakeKuboRepo = pkgs.writeTextDir "api" ''
/unix/run/ipfs.sock
'';
kuboFlags = utils.escapeSystemdExecArgs (
optional cfg.autoMount "--mount" ++
optional cfg.enableGC "--enable-gc" ++
@ -38,6 +50,22 @@ let
splitMulitaddr = addrRaw: lib.tail (lib.splitString "/" addrRaw);
multiaddrsToListenStreams = addrIn:
let
addrs = if builtins.typeOf addrIn == "list"
then addrIn else [ addrIn ];
unfilteredResult = map multiaddrToListenStream addrs;
in
builtins.filter (addr: addr != null) unfilteredResult;
multiaddrsToListenDatagrams = addrIn:
let
addrs = if builtins.typeOf addrIn == "list"
then addrIn else [ addrIn ];
unfilteredResult = map multiaddrToListenDatagram addrs;
in
builtins.filter (addr: addr != null) unfilteredResult;
multiaddrToListenStream = addrRaw:
let
addr = splitMulitaddr addrRaw;
@ -154,13 +182,18 @@ in
options = {
Addresses.API = mkOption {
type = types.str;
default = "/ip4/127.0.0.1/tcp/5001";
description = lib.mdDoc "Where Kubo exposes its API to";
type = types.oneOf [ types.str (types.listOf types.str) ];
default = [ ];
description = lib.mdDoc ''
Multiaddr or array of multiaddrs describing the address to serve the local HTTP API on.
In addition to the multiaddrs listed here, the daemon will also listen on a Unix domain socket.
To allow the ipfs CLI tools to communicate with the daemon over that socket,
add your user to the correct group, e.g. `users.users.alice.extraGroups = [ config.services.kubo.group ];`
'';
};
Addresses.Gateway = mkOption {
type = types.str;
type = types.oneOf [ types.str (types.listOf types.str) ];
default = "/ip4/127.0.0.1/tcp/8080";
description = lib.mdDoc "Where the IPFS Gateway can be reached";
};
@ -248,7 +281,7 @@ in
];
environment.systemPackages = [ cfg.package ];
environment.variables.IPFS_PATH = cfg.dataDir;
environment.variables.IPFS_PATH = fakeKuboRepo;
# https://github.com/lucas-clemente/quic-go/wiki/UDP-Receive-Buffer-Size
boot.kernel.sysctl."net.core.rmem_max" = mkDefault 2500000;
@ -338,27 +371,23 @@ in
wantedBy = [ "sockets.target" ];
socketConfig = {
ListenStream =
let
fromCfg = multiaddrToListenStream cfg.settings.Addresses.Gateway;
in
[ "" ] ++ lib.optional (fromCfg != null) fromCfg;
[ "" ] ++ (multiaddrsToListenStreams cfg.settings.Addresses.Gateway);
ListenDatagram =
let
fromCfg = multiaddrToListenDatagram cfg.settings.Addresses.Gateway;
in
[ "" ] ++ lib.optional (fromCfg != null) fromCfg;
[ "" ] ++ (multiaddrsToListenDatagrams cfg.settings.Addresses.Gateway);
};
};
systemd.sockets.ipfs-api = {
wantedBy = [ "sockets.target" ];
# We also include "%t/ipfs.sock" because there is no way to put the "%t"
# in the multiaddr.
socketConfig.ListenStream =
let
fromCfg = multiaddrToListenStream cfg.settings.Addresses.API;
in
[ "" "%t/ipfs.sock" ] ++ lib.optional (fromCfg != null) fromCfg;
socketConfig = {
# We also include "%t/ipfs.sock" because there is no way to put the "%t"
# in the multiaddr.
ListenStream =
[ "" "%t/ipfs.sock" ] ++ (multiaddrsToListenStreams cfg.settings.Addresses.API);
SocketMode = "0660";
SocketUser = cfg.user;
SocketGroup = cfg.group;
};
};
};

View File

@ -359,7 +359,7 @@ in {
ksm = handleTest ./ksm.nix {};
kthxbye = handleTest ./kthxbye.nix {};
kubernetes = handleTestOn ["x86_64-linux"] ./kubernetes {};
kubo = handleTest ./kubo.nix {};
kubo = runTest ./kubo.nix;
ladybird = handleTest ./ladybird.nix {};
languagetool = handleTest ./languagetool.nix {};
latestKernel.login = handleTest ./login.nix { latestKernel = true; };

View File

@ -1,10 +1,10 @@
import ./make-test-python.nix ({ pkgs, ...} : {
{ lib, ...} : {
name = "kubo";
meta = with pkgs.lib.maintainers; {
maintainers = [ mguentner ];
meta = with lib.maintainers; {
maintainers = [ mguentner Luflosi ];
};
nodes.machine = { ... }: {
nodes.machine = { config, ... }: {
services.kubo = {
enable = true;
# Also will add a unix domain socket socket API address, see module.
@ -12,58 +12,74 @@ import ./make-test-python.nix ({ pkgs, ...} : {
settings.Addresses.API = "/ip4/127.0.0.1/tcp/2324";
dataDir = "/mnt/ipfs";
};
users.users.alice = {
isNormalUser = true;
extraGroups = [ config.services.kubo.group ];
};
};
nodes.fuse = { ... }: {
nodes.fuse = { config, ... }: {
services.kubo = {
enable = true;
settings.Addresses.API = "/ip4/127.0.0.1/tcp/2324";
autoMount = true;
};
users.users.alice = {
isNormalUser = true;
extraGroups = [ config.services.kubo.group ];
};
users.users.bob = {
isNormalUser = true;
};
};
testScript = ''
start_all()
# IPv4 activation
machine.succeed("ipfs --api /ip4/127.0.0.1/tcp/2324 id")
ipfs_hash = machine.succeed(
"echo fnord | ipfs --api /ip4/127.0.0.1/tcp/2324 add | awk '{ print $2 }'"
)
machine.succeed(f"ipfs cat /ipfs/{ipfs_hash.strip()} | grep fnord")
# Unix domain socket activation
with subtest("Automatic socket activation"):
ipfs_hash = machine.succeed(
"echo fnord0 | su alice -l -c 'ipfs add --quieter'"
)
machine.succeed(f"ipfs cat /ipfs/{ipfs_hash.strip()} | grep fnord0")
machine.stop_job("ipfs")
ipfs_hash = machine.succeed(
"echo fnord2 | ipfs --api /unix/run/ipfs.sock add | awk '{ print $2 }'"
)
machine.succeed(
f"ipfs --api /unix/run/ipfs.sock cat /ipfs/{ipfs_hash.strip()} | grep fnord2"
)
with subtest("IPv4 socket activation"):
machine.succeed("ipfs --api /ip4/127.0.0.1/tcp/2324 id")
ipfs_hash = machine.succeed(
"echo fnord | ipfs --api /ip4/127.0.0.1/tcp/2324 add --quieter"
)
machine.succeed(f"ipfs cat /ipfs/{ipfs_hash.strip()} | grep fnord")
# Test if setting dataDir works properly with the hardened systemd unit
machine.succeed("test -e /mnt/ipfs/config")
machine.succeed("test ! -e /var/lib/ipfs/")
machine.stop_job("ipfs")
# Test FUSE mountpoint
# The FUSE mount functionality is broken as of v0.13.0 and v0.17.0.
# See https://github.com/ipfs/kubo/issues/9044.
# Workaround: using CID Version 1 avoids that.
ipfs_hash = fuse.succeed(
"echo fnord3 | ipfs --api /ip4/127.0.0.1/tcp/2324 add --quieter --cid-version=1"
).strip()
with subtest("Unix domain socket activation"):
ipfs_hash = machine.succeed(
"echo fnord2 | ipfs --api /unix/run/ipfs.sock add --quieter"
)
machine.succeed(
f"ipfs --api /unix/run/ipfs.sock cat /ipfs/{ipfs_hash.strip()} | grep fnord2"
)
fuse.succeed(f"cat /ipfs/{ipfs_hash} | grep fnord3")
with subtest("Setting dataDir works properly with the hardened systemd unit"):
machine.succeed("test -e /mnt/ipfs/config")
machine.succeed("test ! -e /var/lib/ipfs/")
# Force Kubo to crash and wait for it to restart
# Tests the unmounting of /ipns and /ipfs
fuse.systemctl("kill --signal=SIGKILL ipfs.service")
fuse.wait_for_unit("ipfs.service", timeout = 30)
with subtest("FUSE mountpoint"):
fuse.fail("echo a | su bob -l -c 'ipfs add --quieter'")
# The FUSE mount functionality is broken as of v0.13.0 and v0.17.0.
# See https://github.com/ipfs/kubo/issues/9044.
# Workaround: using CID Version 1 avoids that.
ipfs_hash = fuse.succeed(
"echo fnord3 | su alice -l -c 'ipfs add --quieter --cid-version=1'"
).strip()
fuse.succeed(f"cat /ipfs/{ipfs_hash} | grep fnord3")
fuse.succeed(f"cat /ipfs/{ipfs_hash} | grep fnord3")
with subtest("Unmounting of /ipns and /ipfs"):
# Force Kubo to crash and wait for it to restart
fuse.systemctl("kill --signal=SIGKILL ipfs.service")
fuse.wait_for_unit("ipfs.service", timeout = 30)
fuse.succeed(f"cat /ipfs/{ipfs_hash} | grep fnord3")
'';
})
}