Security

What's protected: your cloud accounts, whose keys lnk cloud keeps, and the machines lnk box makes in them, against anyone but you reaching them. Neither plugin talks to the relay, and Local Link holds no cloud account: everything runs in, and is billed to, yours. What they do is clouds and boxes. What's on a box, its install, its workers and its users: What's on a Box; an image's: Box Images.

Limits

What Link makes boxes on, and what it needs here to reach them.

  • AWS and Google Cloud only, in your own accounts and bills.
  • Boxes are Ubuntu 24.04 or 22.04 LTS on x86_64 or arm64; an arm64 box has no GPUs.
  • Boxes need the cloud's CLI (aws, gcloud) and OpenSSH on your computer. lnk box create stops before making anything when ssh or ssh-keygen is missing.

Cloud accounts

Where your cloud keys are kept, how each plugin acts as an account, and the adapters and CLIs that run with them.

Keys

A cloud account's keys are kept in Link's settings folder, or the Keychain, and reach the cloud's CLI only in its environment.

  • A reused aws login (~/.aws) is named, not copied: the AWS CLI, and rclone for buckets, read it when they run.
  • A Google Cloud account is one credential file, kept in ~/.lnk/gcp/credentials/<name>.json, mode 600 in a 700 folder: a service account's key (--credential-file), or your login's application default credentials, made once by gcloud auth application-default login and copied there. lnk cloud disconnect deletes it.
  • gcloud (with a service account's key), rclone and Google's libraries all act as that file, so boxes and buckets are the same identity. It holds a refresh token or a private key: anyone who reads it acts as the account.
  • For a result-upload URL, gcloud receives the connected service account's credential-file path with --private-key-file. The private key's value stays in the file. The shell cannot select a different signing identity.
  • Every other plugin acts as an account through its adapter only, with the account lnk cloud env gives: lnk bucket asks the adapter's storage for its sign-in on each command, and keeps none of it.
  • An access key given instead is kept in ~/.lnk/cloud/accounts.toml, mode 600 in a 700 folder, as lnk bucket keeps its own. On a Mac its secret part is in the Keychain instead (Secrets in the Keychain).
  • A key reaches the CLI only in its environment, never on a command line, where other local users could see it.
  • The shell's own AWS_* and CLOUDSDK_* variables are removed first, so a profile set in the shell can't redirect a call to another account.
  • Adapters print an account on stdout to the plugin that ran them. lnk cloud list never includes keys; lnk cloud env does.

Adapters

lnk-aws and lnk-gcp run with your keys and the cloud's CLI. They're plugins like any other: installed only from Link's signed release, found next to lnk, else on the PATH.

The cloud's CLI

Link uses the CLI on your PATH, else one lnk cloud connect installed when you said yes: AWS's installer from awscli.amazonaws.com into ~/.lnk/aws (on Linux, its zip and install script; on macOS, its package, for the current user only), or Google's archive from dl.google.com into ~/.lnk/gcp.

How they're fetched

Both come over HTTPS only. Link doesn't pin them or check their signatures: it trusts those sites, as their own install instructions do. Neither needs administrator rights, and neither changes your shell's settings.

Reaching a box

What a box lets in, how Link knows which boxes are yours and their host keys, and what each SSH connection to a box hands it.

What a box exposes

A box opens SSH alone, to Link's short-lived certificates, and its metadata service gives no cloud credentials.

  • Only SSH, port 22, open to the whole internet, or no port at all on AWS through the tunnel (below). Key-only: Ubuntu's images refuse passwords.
  • A box takes a certificate this computer's authority signed, for it alone, for an hour (Certificates). No plain key of Link's is in its authorized_keys.
  • Nothing else is open. AWS security group lnk-box and Google Cloud firewall rule lnk-box-ssh allow only TCP 22. Link makes them the first time and removes them with the last box in their region or project.
  • Boxes go in the account's default VPC on AWS, and its default network on Google Cloud. That network's own rules let in every port from inside it, RDP and ping, so Link adds lnk-box-deny there, refusing everything else to its boxes ahead of those rules. A rule you add yourself at a normal priority still wins.
  • AWS instances take instance metadata only with a session token (IMDSv2), and have no instance role. Google Cloud instances have no service account (--no-service-account --no-scopes). So the metadata service, which any program on the box can reach, gives no cloud credentials: Link acts on your cloud as your account, from your computer, never as the box.
  • Every agent made on a box goes through Link's proxy (lnk agent route direct --default), never straight from the box or to the cloud's metadata service.

Certificates

Each computer has an SSH certificate authority of its own, and a box trusts the authorities of the computers that reach it: a copied key reaches nothing once its certificates expire, at most an hour later.

Who holds what

The authority's secret stays on the computer that made it. It's an ed25519 secret made by ssh-keygen, kept in the Keychain on a Mac (box/ssh-ca), else in ~/.lnk/box/ssh-ca, mode 600 in a 700 folder, and its public half in ssh-ca.pub. It never reaches a box or a cloud. This computer's key, ~/.lnk/box/id_ed25519, ed25519 with no passphrase, is what a certificate certifies.

What a box trusts

A box made here trusts this computer's authority from its first boot. Its cloud-init writes the authority's public half to /etc/ssh/lnk/user-cas.pub and the box's principal to /etc/ssh/lnk/principals/ubuntu, both root's. A drop-in, /etc/ssh/sshd_config.d/10-lnk.conf, points sshd's TrustedUserCAKeys and AuthorizedPrincipalsFile at them. The first boot also drops Link's plain keys from authorized_keys, so a box made from an older image takes none.

A certificate

A certificate names one principal, lnk-box-<machine id>, so it reaches one box. It lasts an hour, from five minutes before it's signed, and allows a terminal and forwarded ports, never your agent or X11. Before a connection, lnk box signs a new one with ssh-keygen -s when the box's in ~/.lnk/box/certs/ is over half an hour old. The secret is written to a 700 folder of ~/.lnk/box while ssh-keygen reads it, then removed.

Boxes made before certificates

A box made before certificates is given them the next time Link reaches it. Link gets in with this computer's key and adds the authority and the principal as a first boot would. Once a certificate alone gets in, Link's plain keys go, through the adapter's remove-keys and from authorized_keys, anyone else's kept. A box with no machine ID yet keeps the key until lnk box list reads its ID.

Which boxes are yours

Link knows a box as yours by its tags, which are as trusted as the cloud account.

  • Each box's machine is named lnk-<machine id> and tagged with its name (lnk-box), its machine ID (lnk-machine) and your account's owner ID (lnk-owner=acct-<id>), or a digest of the local user and machine name while signed out (lnk-owner=local-<digest>). On AWS its disk has the same tags, so the bill says whose each disk is.
  • lnk box list asks each connected account for the boxes with your tag, so a project shared with another Local Link user lists only yours.
  • Local ownership needs no Link login. A box created while this computer is signed out stays signed out; its SSH access uses the cloud account and this computer's certificates.
  • The tags are as trusted as the account: anyone who can tag instances there could make a machine look like yours. So lnk agent move <account> asks before moving to a box it found by its tags, and takes one unasked only if this computer made it (agents).
  • What a listing brings back goes into file names and prompts only as a valid name and ID; anything else is skipped.

Getting in from another computer

A box made on another of your computers is reached the first time by pinning its host keys from the cloud (below). Then this computer's key is let in through the account for a moment, while the box is made to trust this computer's authority too, and then the key goes.

On Google Cloud

Merged into the instance's ssh-keys metadata, never replacing the others, until remove-keys takes it out again. A box takes only its own keys: it's made with block-project-ssh-keys=TRUE, so the project's SSH keys, which anyone who can edit the project's metadata adds, don't let anyone in, and with OS Login off. A box made before this takes them until it's made again.

On AWS

Pushed with EC2 Instance Connect, which lets it in for 60 seconds, during which lnk box appends it to authorized_keys over SSH, to drop it once a certificate gets in.

Each computer's key

Each computer's key has the comment lnk <machine id> <machine name>, and its authority lnk-ca <machine id> <machine name>, so a lost computer's can be found in /etc/ssh/lnk/user-cas.pub and removed. Whoever controls the account could add a key this way too: the account is trusted, as it could replace the machine anyway.

Host keys

A box's SSH host keys are pinned before the first connection, from what the cloud says, and checked on every one after.

  • A new box's SSH host keys are read from its console output, the block cloud-init prints at the first boot, through the cloud's API as your account.
  • They're pinned in Link's own known_hosts before the first SSH connection, and kept in boxes.toml. SSH then refuses any other key: StrictHostKeyChecking=accept-new accepts only hosts it doesn't know.
  • They're kept under the box's own name, lnk-<cloud>-<its id> (ssh's HostKeyAlias), never its address, so a box at a new address, restarted here, from the cloud's console or by another computer, is checked against the same keys.
  • When the console shows none within 5 minutes, or the adapter can't read it, the key is accepted on first contact and pinned after, under the box's name too. That trusts the network between you and the cloud once.
  • A removed box's keys are forgotten, since its address can come back as another machine's.
  • The computer that pinned them keeps them in the cloud, since the console's first-boot output doesn't last: metadata lnk-host-keys on Google Cloud; on AWS, the ed25519 key in a lnk-host-key tag, which holds 256 characters. Another computer pins those before its first connection, and reads the console only when the cloud keeps none.
  • Anyone who controls the cloud account can change the host keys kept there.

One connection per box

The SSH calls a command makes to one box share one connection, which any program running as you can use while it's open.

  • The lnk box run calls a command makes to one box share one SSH connection, which the first opens with the key and host keys above. It closes a minute after the last one ends, and when the box is removed or a new box gets its address.
  • Its socket is in ~/.lnk/box/mux, a 700 folder. While it's open, any program running as you can run commands on the box through it without the key, as it could with the key and the authority's secret, which it can read too.
  • When that folder's path is too long for a socket (over 42 characters), each call connects on its own.

Through the cloud's tunnel

With LNK_BOX_SSH=tunnel, where port 22 can't be reached, every connection to a box goes through its cloud's own tunnel over HTTPS instead: ssh's ProxyCommand is the box's adapter's tunnel, which runs AWS Systems Manager's AWS-StartSSHSession or Google Cloud's start-iap-tunnel as the box's account. An AWS box made so opens no port: its security group, lnk-box-tunnel, made and removed like lnk-box, lets nothing in.

Still SSH

It's still SSH, end to end, with Link's certificate, and the box's host key checked as it was pinned: the tunnel carries the bytes and sees none of them in the clear.

Who can open a tunnel

Anyone the cloud lets open a session or an IAP tunnel to the box can open one: on AWS, whoever is allowed ssm:StartSession on the instance; on Google Cloud, whoever has roles/iap.tunnelResourceAccessor in its project. They still meet the box's SSH, which takes only Link's certificates.

Systems Manager's other documents

On AWS, the box's Systems Manager agent also takes other documents, a shell as ssm-user among them, which needs no SSH key: give the users that open tunnels ssm:StartSession on AWS-StartSSHSession alone (live tests, "Boxes from a cloud session").

Its ProxyCommand

It names the adapter, the account, the box's place and its id, each refused unless it's letters, digits, dots, dashes and underscores, none first a dash, and each quoted for the shell ssh runs it with. The account's credentials reach the cloud's CLI in its environment, never on a command line; AWS's own short-lived session token is on session-manager-plugin's, as aws ssm start-session runs it.

Your own SSH config

Every connection to a box runs your ssh, which reads your ~/.ssh/config, so a ProxyJump you need still works. What would hand the box something of this computer is turned off on the command line, which wins over that file:

  • ForwardAgent=no: a box never gets your SSH agent, which would let whoever controls it sign as you to any host your keys reach.
  • ForwardX11=no, and PermitLocalCommand=no, so no LocalCommand runs here.
  • ClearAllForwardings=yes, on every connection but lnk box forward's, so no LocalForward, RemoteForward or DynamicForward of yours opens. lnk box forward exists to forward one port or socket, and ssh can't clear the file's without clearing it too: a forward of yours under Host * opens on that connection as well.

Spot boxes

A spot box the cloud takes back is stopped, never deleted: its disk, with its lnk login and an agent's state, stays, and is billed, without you stopping it. What a spot box does on each cloud: box spec.

Removing

lnk box delete deletes the machine and its disk: on AWS it's terminated, its volume deleted with it; on Google Cloud it's deleted with --delete-disks all. Snapshots or anything else you made there yourself stay. Images you saved stay until lnk box image delete. A lnk box create whose machine couldn't be made deletes what Link made for boxes the same way.

Disconnecting

lnk cloud disconnect forgets the account and its key, and deletes nothing in it: Disconnect an account.

Gaps

What Link doesn't protect, as each is today.

  • Keys are in a file, and so is the SSH authority's secret off a Mac: whoever reads it can sign certificates for every box this computer reaches.
  • SSH is open to the internet.
  • The first contact's host key is trusted when neither the cloud nor a box's console has it.
  • The cloud account is trusted for tags, keys, host keys and a box's first boot, and for what lnk box spec prints of a box.
  • --yes makes a box from a spec without asking, however new the spec. What it makes is printed first.
  • An image holds whatever else is on the box's disk: Link checks for its own login, agents and the usual places secrets are left in the home folder, not for a secret anywhere else.
  • Two images of one name in a Google Cloud project shared with another Local Link user collide: the second save is refused.

See Known limitations.

Deadlines

A deadline is installed while the instance is made. On AWS the root-owned timer survives reboot; root can change it, so it is not a wall against a process running as root. On Google Cloud the cloud enforces deletion; no guest poweroff timer is installed because stopping the VM clears its scheduled deletion.

Attached resources

What a box gets of the resources its spec's [attach] names, which it never makes or deletes.

An attached identity

An attached identity is the existing service account or instance profile the spec names. Its metadata credentials reach whatever that identity is granted; grant only the service's resources.

Kept at removal

Attach lookups never create networks, firewall rules, addresses, identities or data disks. GCP removal deletes the boot disk alone; attached GCP disks have auto-delete disabled, and attached AWS volumes have DeleteOnTermination disabled.

Inventory by tag

Tag-based inventory queries fail on cloud errors and do not rely on the caller's login ownership label.