The Agent

Wherein you use a security agent to maintain your secrets and facilitate your secure use of Vanadium.

Prerequisites:
All tutorials require Vanadium installation, and must run in a bash shell.
Further, please download this script then source it:

source ~/Downloads/scenario-d-setup.sh

This command wipes tutorial files and defines shell environment variables. If you start a new terminal, and just want to re-establish the environment, see instructions here.

Motivation

In the principals tutorial you used the principal create command to create the principals Alice, Bob, etc. The operation made collections of files in the specified target directories, e.g.:

ls $V_TUT/cred/alice $V_TUT/cred/bob

Stored among these files are the private keys associated with Alice and Bob. The keys could be encrypted (there are ways to do that), but at the moment, they aren't. At no point during earlier tutorials did you - the tutee - get prompted for a passphrase, despite frequent use of the --v23.credentials {directory} flag.

Encrypting the file on disk, and requiring a password with each invocation of a command specifying --v23.credentials {directory} will solve the immediate problem of a clear text key on disk, but is onerous.

In addition to the nuisance of having to provide a decryption passphrase each time the credentials are used, having each program load the private key into process memory as part of its runtime initialization exposes the key to the risk of compromise.

This would be OK for the tutorials, which had the goal of teaching security concepts rather than being secure. You were running only official Vanadium apps or programs that you wrote, in your own authenticated session, on a machine under your control.

But easy access to the private key is not OK under normal circumstances. A private key should be private.

Furthermore, if several program were to access the same credentials directory simulataneously, they would risk corrupting the data during mutations. There is a need for coordinating concurrent access to credentials.

An agent holds the key

Part of the solution to these problems is v23agentd, a Vanadium utility analogous to ssh-agent. It loads credentials into its memory, and serves key management requests from other client processes.

A client process, needing to check the validity of blessings coming in with a request, can pass said blessings (implicitly via the runtime's use of a POSIX file descriptor) up to v23agentd, which then does the necessary crypto with the keys it holds, passing the result back to the client.

Launching an agent to serve credentials in a given directory (by invoking v23agentd --credentials={directory}) sets up a socket file in the directory, which the client connects to when using --v23.credentials {directory}. This happens in the Vanadium runtime - no new client code required.

Typically, launching an agent explicitly is not necessary -- as long as v23agentd is in its PATH, a program run with --v23.credentials {directory} will automatically launch an agent to serve the credentials. Other programs using --v23.credentials {directory} will connect to the same agent and safely share the credentials. The agent will stay up for as long as there are client connected to it. In fact, this is what happened in all previous tutorials, like the hello world tutorial, enabling the same credentials to be shared between client and server.

Occasionally, launching the agent explicitly is needed, such as when we do not trust the program to access the credentials and launch v23agentd.

To illustrate, rerun the existing client code against the existing server code using two explicit instances of the agent - one becomes Alice, the other becomes Bob.

The only difference between the following command sequence and previous usage is that v23agentd gets launched before we run the client and server.

# Clean up from previous attempt, if any.
kill_tut_process TUT_PID_SERVER
/bin/rm -f $V_TUT/server.txt

# Run an agent for server credentials.
$V_BIN/v23agentd --credentials=$V_TUT/cred/alice
$V_TUT/bin/server \
    --v23.credentials $V_TUT/cred/alice \
    --endpoint-file-name $V_TUT/server.txt \
    --perms '{"R": {"In": ["alice:family",
                           "alice:friend"]},
              "W": {"In": ["alice:family"]}}' &
TUT_PID_SERVER=$!

# Wait for startup.
sleep 2s

# Run an agent for client credentials.
$V_BIN/v23agentd --credentials=$V_TUT/cred/bob
$V_TUT/bin/client \
    --v23.credentials $V_TUT/cred/bob \
    --server `cat $V_TUT/server.txt`

# All done, kill the server and agents
kill_tut_process TUT_PID_SERVER
$V_BIN/v23agentd stop --credentials=$V_TUT/cred/alice
$V_BIN/v23agentd stop --credentials=$V_TUT/cred/bob

The above should run without errors, i.e. the client should report a fortune.

The crucial new behavior here is that neither the server nor client binary had access to private keys. The runtime off-loaded all crypto checks to the agent in a different process.

In the normal course of events, where you run code whose source code you've not written or may not have access to, this is a critical capability. The program runs with clear identity, but cannot secretly send the private keys to that identity elsewhere.

Become Alice

Set the environment variable V23_CREDENTIALS={directory} to run a script as a given identity. Here, the script just runs principal dump commands, introspecting its own identity:

cat <<EOF > $V_TUT/subshell.sh
  $V_BIN/principal dump
  $V_BIN/principal get forpeer ... | \
      $V_BIN/principal dumpblessings -
EOF
V23_CREDENTIALS=$V_TUT/cred/alice bash $V_TUT/subshell.sh

Don't do this now, but say you wanted to spend the day as Alice. To interactively issue many commands as a particular Vanadium principal, just set V23_CREDENTIALS accordingly.

Alice's vassals

If you're running as Alice, and you want to run some program foo with a distinct principal, use vbecome:

cat <<EOF > $V_TUT/subshell.sh
  echo " "
  echo "************************ Alice:"
  $V_BIN/principal dump
  echo " "
  echo "************************ Alice's vassal Andy:"
  $V_BIN/vbecome --name andy $V_BIN/principal dump
EOF
V23_CREDENTIALS=$V_TUT/cred/alice bash $V_TUT/subshell.sh

Compare the output of the two dump commands to see that the first one ran as simply alice, while the second, with vbecome, ran as alice:andy.

The public keys of the two differ, indicating that the vassal is a distinct principal. A use for this behavior would be some manager of many processes, running each process with a new identity.

Here's the server and client example again, using vbecome for the client. This example omits the --name flag on vbecome. In this case, vbecome uses the name of the executable being run as the blessing name. Thus, in this case, the blessing name will be alice:client (the script runs as alice, and the executable's name is client).

cat <<EOF > $V_TUT/subshell.sh
  $V_TUT/bin/server \
      --endpoint-file-name $V_TUT/server.txt \
      --perms '{"R": {"In": ["alice:client"]},
                "W": {"In": ["alice:family"]}}' &
  TUT_PID_SERVER=\$!
  sleep 2s # wait for startup
  $V_BIN/vbecome $V_TUT/bin/client \
      --server \`cat $V_TUT/server.txt\`
  kill \$TUT_PID_SERVER # Only making one call.
EOF
V23_CREDENTIALS=$V_TUT/cred/alice bash $V_TUT/subshell.sh

The RPC works because the client runs with a blessing derived from the principal running the server - specifically, the client runs with the blessing alice:client (note the server's permissions).

Secure blessing

Previous examples of blessing used a Unix pipe to transmit a blessing from one process running principal bless to some other process using principal set forpeer.

The problem in that is not the blessing being sent 'in the clear' - that's OK. The problem is that the person issuing the commands has direct access to credential data on both sides. The way blessings have been generated so far in the tutorials - in which you the tutee casually exploited your read access to the private keys of both the blessor and blessee - isn't practical for blessings being granted between disjoint principals remote from each other on a network.

The agent, and the command principal recvblessing, make the act of blessing much more secure.

The following example involves two processes, securely running with distinct agents started automatically (neither process can see private keys).

The first process, running as Bob, will get into a state where it waits for a blessing. The second process, running as Alice, will send the blessing.

First start Bob. He'll wait for the blessing, then dump it and exit as soon as he gets it.

cat <<EOF > $V_TUT/subshell1.sh
  $V_BIN/principal recvblessings \
      --remote-arg-file $V_TUT/recvblessings_args.json
  $V_BIN/principal dump
EOF
V23_CREDENTIALS=$V_TUT/cred/bob bash $V_TUT/subshell1.sh &

The next commands can be run in the same terminal, but you might want to start a second terminal to keep the process output disentangled.

Now run Alice. Alice will transmit the blessing, using instructions Bob left her in the file $V_TUT/recvblessings_args.json.

cat <<EOF > $V_TUT/subshell2.sh
  echo " "
  echo "Transmitting the blessing."
  $V_BIN/principal bless \
      --for 2h \
      --remote-arg-file $V_TUT/recvblessings_args.json \
      companion
EOF
V23_CREDENTIALS=$V_TUT/cred/alice bash $V_TUT/subshell2.sh

The bless command above transmits the blessing alice:companion from Alice to Bob. This blessing can be seen in the output of principal dump in the output from Bob's shell.

In this scenario, neither Alice nor Bob nor the principal programs running on either end had direct access to private keys. Only the two agents had the keys.

The file doesn't hold the blessing, it just holds communication overhead data; see $V_BIN/principal help recvblessings for more info.

Summary