Compare commits

..

3 Commits

Author SHA1 Message Date
bluepython508
3c779e8fe2 Update dependencies 2024-11-01 16:31:00 +00:00
bluepython508
bdacd9905e Rename Peer to 'Backing Agent' 2024-09-26 13:10:33 +01:00
bluepython508
85bf788307 Deduplicate returned identities 2024-09-26 12:58:46 +01:00
7 changed files with 51 additions and 47 deletions

View File

@@ -1,4 +1,4 @@
import Config import Config
config :frajtano, config :frajtano,
initial_peers: [] initial_backing_agents: []

View File

@@ -58,7 +58,7 @@
;; ;;
spawn) spawn)
shift shift
run rpc ":ok = Frajtano.Agent.spawn_peer({\"/usr/bin/env\", [$(list "$@")]})" run rpc ":ok = Frajtano.Agent.spawn_backing_agent({\"/usr/bin/env\", [$(list "$@")]})"
;; ;;
socket) socket)
echo $FRAJTANO_DIR/agent.sock echo $FRAJTANO_DIR/agent.sock

17
flake.lock generated
View File

@@ -2,15 +2,18 @@
"nodes": { "nodes": {
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1721924956, "lastModified": 1730272153,
"narHash": "sha256-Sb1jlyRO+N8jBXEX9Pg9Z1Qb8Bw9QyOgLDNMEpmjZ2M=", "narHash": "sha256-B5WRZYsRlJgwVHIV6DvidFN7VX7Fg9uuwkRW9Ha8z+w=",
"path": "/nix/store/xblysc3prg1zqsi59fr36nj6wdiqryzp-source", "owner": "NixOS",
"rev": "5ad6a14c6bf098e98800b091668718c336effc95", "repo": "nixpkgs",
"type": "path" "rev": "2d2a9ddbe3f2c00747398f3dc9b05f7f2ebb0f53",
"type": "github"
}, },
"original": { "original": {
"id": "nixpkgs", "owner": "NixOS",
"type": "indirect" "ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
} }
}, },
"root": { "root": {

View File

@@ -1,5 +1,6 @@
{ {
description = "frajtano: an ssh agent multiplexer"; description = "frajtano: an ssh agent multiplexer";
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
outputs = { outputs = {
self, self,
@@ -42,18 +43,18 @@
default = "${config.home.homeDirectory}/.ssh/frajtano"; default = "${config.home.homeDirectory}/.ssh/frajtano";
type = lib.types.path; type = lib.types.path;
}; };
initialPeers = lib.mkOption { initialBackingAgents = lib.mkOption {
description = "initially spawned peers - will be passed to /usr/bin/env"; description = "initially spawned backing_agents - will be passed to /usr/bin/env";
type = with lib.types; listOf (listOf str); type = with lib.types; listOf (listOf str);
default = []; default = [];
}; };
}; };
config = let config = let
peers = lib.strings.concatMapStringsSep ", " (args: ''{:spawn, {"${pkgs.coreutils}/bin/env", [${lib.concatMapStringsSep ", " (s: ''~S{${s}}'') args}]}}'') cfg.initialPeers; backing_agents = lib.strings.concatMapStringsSep ", " (args: ''{:spawn, {"${pkgs.coreutils}/bin/env", [${lib.concatMapStringsSep ", " (s: ''~S{${s}}'') args}]}}'') cfg.initialBackingAgents;
configFile = pkgs.writeText "config.exs" '' configFile = pkgs.writeText "config.exs" ''
import Config import Config
config :frajtano, initial_peers: [${peers}] config :frajtano, initial_backing_agents: [${backing_agents}]
''; '';
in lib.mkIf cfg.enable { in lib.mkIf cfg.enable {
home.sessionVariables.FRAJTANO_DIR = cfg.dir; home.sessionVariables.FRAJTANO_DIR = cfg.dir;

View File

@@ -1,5 +1,5 @@
defmodule Frajtano.Agent do defmodule Frajtano.Agent do
alias Frajtano.Peer alias Frajtano.BackingAgent
use GenServer use GenServer
require Logger require Logger
@@ -15,49 +15,49 @@ defmodule Frajtano.Agent do
} }
end end
def initial_peers() do def initial_backing_agents() do
initial_peers = Application.fetch_env!(:frajtano, :initial_peers) initial_backing_agents = Application.fetch_env!(:frajtano, :initial_backing_agents)
|> Enum.map(&GenServer.call(__MODULE__, {:add_peer, &1})) |> Enum.map(&GenServer.call(__MODULE__, {:add_backing_agent, &1}))
Logger.info("Started initial peers: #{inspect initial_peers}") Logger.info("Started initial backing_agents: #{inspect initial_backing_agents}")
end end
# select: list of specs, where specs are a tuple of match, guards, and outputs # select: list of specs, where specs are a tuple of match, guards, and outputs
# match is {key, pid, value}, :"$1" is a match variable # match is {key, pid, value}, :"$1" is a match variable
def peer_paths() do def backing_agent_paths() do
Registry.select(Frajtano.Peers, [{{:"$1", :_, :_}, [], [:"$1"]}]) Registry.select(Frajtano.BackingAgents, [{{:"$1", :_, :_}, [], [:"$1"]}])
end end
def peer_pids() do def backing_agent_pids() do
Registry.select(Frajtano.Peers, [{{:_, :"$1", :_}, [], [:"$1"]}]) Registry.select(Frajtano.BackingAgents, [{{:_, :"$1", :_}, [], [:"$1"]}])
end end
@impl true @impl true
def handle_call({:identities}, _from, _state) do def handle_call({:identities}, _from, _state) do
idents = idents =
Task.async_stream( Task.async_stream(
peer_pids(), backing_agent_pids(),
&{&1, Peer.identities(&1)}, &{&1, BackingAgent.identities(&1)},
ordered: false, ordered: false,
on_timeout: :kill_task on_timeout: :kill_task
) )
idents = for {:ok, {peer, {:ok, idents}}} <- idents, do: {idents, peer} idents = for {:ok, {backing_agent, {:ok, idents}}} <- idents, do: {idents, backing_agent}
{ {
:reply, :reply,
{:ok, Enum.flat_map(idents, &elem(&1, 0))}, {:ok, idents |> Enum.flat_map(&elem(&1, 0)) |> Enum.uniq},
for({idents, peer} <- idents, {key, _comment} <- idents, into: %{}, do: {key, peer}) for({idents, backing_agent} <- idents, {key, _comment} <- idents, into: %{}, do: {key, backing_agent})
} }
end end
@impl true @impl true
def handle_call({:sign, {key, _, _} = req}, _from, state) do def handle_call({:sign, {key, _, _} = req}, _from, state) do
{:reply, Peer.sign(state[key], req), state} {:reply, BackingAgent.sign(state[key], req), state}
end end
@impl true @impl true
def handle_call({:add_peer, spec}, _from, state) do def handle_call({:add_backing_agent, spec}, _from, state) do
case Peer.start(spec) do case BackingAgent.start(spec) do
{:ok, _} -> {:reply, :ok, state} {:ok, _} -> {:reply, :ok, state}
{:error, error} -> {:reply, {:error, error}, state} {:error, error} -> {:reply, {:error, error}, state}
end end
@@ -73,10 +73,10 @@ defmodule Frajtano.Agent do
end end
def assimilate(path) do def assimilate(path) do
GenServer.call(__MODULE__, {:add_peer, {:socket, path}}) GenServer.call(__MODULE__, {:add_backing_agent, {:socket, path}})
end end
def spawn_peer(spec) do def spawn_backing_agent(spec) do
GenServer.call(__MODULE__, {:add_peer, {:spawn, spec}}) GenServer.call(__MODULE__, {:add_backing_agent, {:spawn, spec}})
end end
end end

View File

@@ -17,12 +17,12 @@ defmodule Frajtano.Supervisor do
@impl true @impl true
def init(:ok) do def init(:ok) do
children = [ children = [
{DynamicSupervisor, name: Frajtano.PeerSupervisor}, {DynamicSupervisor, name: Frajtano.BackingAgentSupervisor},
{Registry, keys: :unique, name: Frajtano.Peers}, {Registry, keys: :unique, name: Frajtano.BackingAgents},
Frajtano.Agent, Frajtano.Agent,
{Task.Supervisor, name: Frajtano.ClientSupervisor}, {Task.Supervisor, name: Frajtano.ClientSupervisor},
{Frajtano.Listener, [Application.fetch_env!(:frajtano, :listen_path)]}, {Frajtano.Listener, [Application.fetch_env!(:frajtano, :listen_path)]},
{Task, &Frajtano.Agent.initial_peers/0}, {Task, &Frajtano.Agent.initial_backing_agents/0},
:systemd.ready(), :systemd.ready(),
] ]

View File

@@ -1,4 +1,4 @@
defmodule Frajtano.Peer do defmodule Frajtano.BackingAgent do
alias Frajtano.Proto alias Frajtano.Proto
require Logger require Logger
use GenServer, restart: :temporary use GenServer, restart: :temporary
@@ -17,7 +17,7 @@ defmodule Frajtano.Peer do
children = [ children = [
Supervisor.child_spec({MuonTrap.Daemon, [executable, args ++ [path]]}, restart: :temporary, significant: true), Supervisor.child_spec({MuonTrap.Daemon, [executable, args ++ [path]]}, restart: :temporary, significant: true),
Supervisor.child_spec({Frajtano.Peer, {path, :spawned, {executable, args}}}, restart: :permanent) Supervisor.child_spec({Frajtano.BackingAgent, {path, :spawned, {executable, args}}}, restart: :permanent)
] ]
Supervisor.init(children, strategy: :one_for_all, auto_shutdown: :any_significant) Supervisor.init(children, strategy: :one_for_all, auto_shutdown: :any_significant)
@@ -25,19 +25,19 @@ defmodule Frajtano.Peer do
end end
def start({:socket, path}) do def start({:socket, path}) do
DynamicSupervisor.start_child(Frajtano.PeerSupervisor, {__MODULE__, {path}}) DynamicSupervisor.start_child(Frajtano.BackingAgentSupervisor, {__MODULE__, {path}})
end end
def start({:spawn, spec}) do def start({:spawn, spec}) do
Logger.info("Spawning #{inspect spec}") Logger.info("Spawning #{inspect spec}")
DynamicSupervisor.start_child(Frajtano.PeerSupervisor, {Spawner, spec}) DynamicSupervisor.start_child(Frajtano.BackingAgentSupervisor, {Spawner, spec})
end end
def start_link({path}) do def start_link({path}) do
GenServer.start_link(__MODULE__, {path}, name: {:via, Registry, {Frajtano.Peers, path}}) GenServer.start_link(__MODULE__, {path}, name: {:via, Registry, {Frajtano.BackingAgents, path}})
end end
def start_link({_, _, _} = spec) do def start_link({_, _, _} = spec) do
GenServer.start_link(__MODULE__, spec, name: {:via, Registry, {Frajtano.Peers, spec}}) GenServer.start_link(__MODULE__, spec, name: {:via, Registry, {Frajtano.BackingAgents, spec}})
end end
@impl true @impl true
@@ -112,12 +112,12 @@ defmodule Frajtano.Peer do
{:stop, {:error, e}, %{}} {:stop, {:error, e}, %{}}
end end
def identities(peer) do def identities(backing_agent) do
ref = make_ref() ref = make_ref()
send(peer, {:send, {:agentc_request_identities, nil}, {self(), ref}}) send(backing_agent, {:send, {:agentc_request_identities, nil}, {self(), ref}})
# Needs to be less than the timeout in Frajtano.Agent.identities on the Task.async_stream call # Needs to be less than the timeout in Frajtano.Agent.identities on the Task.async_stream call
# That's 5000 by default # That's 5000 by default
timer = Process.send_after(peer, :timeout, 4500) timer = Process.send_after(backing_agent, :timeout, 4500)
receive do receive do
{^ref, msg} -> {^ref, msg} ->
@@ -132,13 +132,13 @@ defmodule Frajtano.Peer do
end end
end end
def sign(peer, request) do def sign(backing_agent, request) do
# Signing may take some time, as a password may need to be entered or similar # Signing may take some time, as a password may need to be entered or similar
# There is therefore no timeout # There is therefore no timeout
# If something requests identities afterwards, it will timeout, which also kills this signature request # If something requests identities afterwards, it will timeout, which also kills this signature request
# The SSH agent protocol strict ordering leaves fun problems with timeouts, as it turns out # The SSH agent protocol strict ordering leaves fun problems with timeouts, as it turns out
ref = make_ref() ref = make_ref()
send(peer, {:send, {:agentc_sign_request, request}, {self(), ref}}) send(backing_agent, {:send, {:agentc_sign_request, request}, {self(), ref}})
receive do receive do
{^ref, msg} -> msg {^ref, msg} -> msg