diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index 110be3f11..c7fe4868d 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -10,19 +10,19 @@ validation: unrecognized_links: warn markdown_extensions: + - admonition - attr_list + - footnotes + - meta + - plantuml_markdown - pymdownx.emoji: emoji_index: !!python/name:material.extensions.emoji.twemoji emoji_generator: !!python/name:material.extensions.emoji.to_svg - - pymdownx.tasklist: custom_checkbox: true - pymdownx.superfences - pymdownx.tabbed: alternate_style: true - - footnotes - - meta - - admonition - pymdownx.details - pymdownx.highlight: use_pygments: true diff --git a/docs/nix/default.nix b/docs/nix/default.nix index f3f16ca9a..6d0afd3ca 100644 --- a/docs/nix/default.nix +++ b/docs/nix/default.nix @@ -1,11 +1,17 @@ { pkgs, module-docs, ... }: +let + uml-c4 = pkgs.python3Packages.plantuml-markdown.override { plantuml = pkgs.plantuml-c4; }; +in pkgs.stdenv.mkDerivation { name = "clan-documentation"; src = ../.; nativeBuildInputs = - [ pkgs.python3 ] + [ + pkgs.python3 + uml-c4 + ] ++ (with pkgs.python3Packages; [ mkdocs mkdocs-material diff --git a/docs/site/drafts/secret-cli.md b/docs/site/drafts/secret-cli.md new file mode 100644 index 000000000..1fbc473c7 --- /dev/null +++ b/docs/site/drafts/secret-cli.md @@ -0,0 +1,324 @@ +## Secrets (CLI Reference) + +#### Adding Secrets (set) + +```bash +clan secrets set mysecret +> Paste your secret: +``` + +!!! note + As you type your secret won't be displayed. Press Enter to save the secret. + +#### List all Secrets (list) + +```bash +clan secrets list +``` + +#### Assigning Access (set) + +By default, secrets are encrypted for your key. To specify which users and machines can access a secret: + +```bash +clan secrets set --machine --machine --user --user +``` + +#### Displaying Secrets (get) + +```bash +clan secrets get mysecret +``` + +#### Rename + +TODO + +#### Remove + +TODO + +#### import-sops + +TODO + +### Users (Reference) + +Learn how to manage users and allowing access to existing secrets. + +#### list user + +Lists all added users + +```bash +clan secrets user list +``` + +``` {.console, title="Example output", .no-copy} +jon +sara +``` + +!!! Question "Who can execute this command?" + Everyone - completely public. + +#### add user + +add a user + +```bash +clan secrets users add {username} {public-key} +``` + +!!! Note + Changes can be trusted by maintainer review in version control. + +#### get user + +get a user public key + +```bash +clan secrets users get {username} +``` + +``` {.console, title="Example output", .no-copy} +age1zk8uzrte55wkg9lkqxu5x6twsj2ja4lehegks0cw4mkg6jv37d9qsjpt44 +``` + +#### remove user + +remove a user + +```bash +clan secrets users remove {username} +``` + +!!! Note + Changes can be trusted by maintainer review in version control. + +#### add-secret user + +Grants the user (`username`) access to the secret (`secret_name`) + +```bash +clan secrets users add-secret {username} {secret_name} +``` + +!!! Note + Requires the executor of the command to have access to the secret (`secret_name`). + +#### remove-secret user + +remove the user (`username`) from accessing the secret (`secret_name`) + +!!! Danger "Make sure at least one person has access." + It might still be possible for the machine to access the secret. (See [machines](#machines)) + + We highly recommend to use version control such as `git` which allows you to rollback secrets in case anything gets messed up. + +```bash +clan secrets users remove-secret {username} {secret_name} +``` + +!!! Question "Who can execute this command?" + Requires the executor of the command to have access to the secret (`secret_name`). + +### Machines (Reference) + +- [list](): list machines +- [add](): add a machine +- [get](): get a machine public key +- [remove](): remove a machine +- [add-secret](): allow a machine to access a secret +- [remove-secret](): remove a machine's access to a secret + +#### List machine + +New machines in Clan come with age keys stored in `./sops/machines/`. To list these machines: + +```bash +clan secrets machines list +``` + +#### Add machine + +For clan machines the machine key is generated automatically on demand if none exists. + +```bash +clan secrets machines add +``` + +If you already have a device key and want to add it manually, see: [How to obtain a remote key](#obtain-remote-keys-manually) + +#### get machine + +TODO + +#### remove machine + +TODO + +#### add-secret machine + +TODO + +#### remove-secret machine + +TODO + +### Groups (Reference) + +The Clan-CLI makes it easy to manage access by allowing you to create groups. + +- [list](): list groups +- [add-user](): add a user to group +- [remove-user](): remove a user from group +- [add-machine](): add a machine to group +- [remove-machine](): remove a machine from group +- [add-secret](): allow a user to access a secret +- [remove-secret](): remove a group's access to a secret + +#### List Groups + +```bash +clan secrets groups list +``` + +#### add-user + +Assign users to a new group, e.g., `admins`: + +```bash +clan secrets groups add-user admins +``` + +!!! info + The group is created if no such group existed before. + + The user must exist in beforehand (See: [users](#users-reference)) + + ```{.console, .no-copy} + . + ├── flake.nix + . ... + └── sops + ├── groups + │ └── admins + │ └── users + │ └── -> ../../../users/ + ``` + +#### remove-user + +TODO + +#### add-machine + +TODO + +#### remove-machine + +TODO + +#### add-secret + +```bash +clan secrets groups add-secret +``` + +#### remove-secret + +TODO + +### Key (Reference) + +- [generate]() generate age key +- [show]() show age public key +- [update]() re-encrypt all secrets with current keys (useful when changing keys) + +#### generate + +TODO + +#### show + +TODO + +#### update + +TODO + +## Further + +Secrets in the repository follow this structure: + +```{.console, .no-copy} +sops/ +├── secrets/ +│ └── / +│ ├── secret +│ └── users/ +│ └── / +``` + +The content of the secret is stored encrypted inside the `secret` file under `mysecret`. + +By default, secrets are encrypted with your key to ensure readability. + +### Obtain remote keys manually + +To fetch a **SSH host key** from a preinstalled system: + +```bash +ssh-keyscan | nix shell nixpkgs#ssh-to-age -c ssh-to-age +``` + +!!! Success + This command converts the SSH key into an age key on the fly. Since this is the format used by the clan secrets backend. + + Once added the **SSH host key** enables seamless integration of existing machines with clan. + +Then add the key by executing: + +```bash +clan secrets machines add +``` + +See also: [Machine reference](#machines-reference) + +### NixOS integration + +A NixOS machine will automatically import all secrets that are encrypted for the +current machine. At runtime it will use the host key to decrypt all secrets into +an in-memory, non-persistent filesystem using [sops-nix](https://github.com/Mic92/sops-nix). +In your nixos configuration you can get a path to secrets like this `config.sops.secrets..path`. For example: + +```nix +{ config, ...}: { + sops.secrets.my-password.neededForUsers = true; + + users.users.mic92 = { + isNormalUser = true; + passwordFile = config.sops.secrets.my-password.path; + }; +} +``` + +See the [readme](https://github.com/Mic92/sops-nix) of sops-nix for more +examples. + +### Migration: Importing existing sops-based keys / sops-nix + +`clan secrets` stores each secret in a single file, whereas [sops](https://github.com/Mic92/sops-nix) commonly allows to put all secrets in a yaml or json document. + +If you already happened to use sops-nix, you can migrate by using the `clan secrets import-sops` command by importing these files: + +```bash +% clan secrets import-sops --prefix matchbox- --group admins --machine matchbox nixos/matchbox/secrets/secrets.yaml +``` + +This will create secrets for each secret found in `nixos/matchbox/secrets/secrets.yaml` in a `./sops` folder of your repository. +Each member of the group `admins` in this case will be able to decrypt the secrets with their respective key. + +Since our clan secret module will auto-import secrets that are encrypted for a particular nixos machine, +you can now remove `sops.secrets. = { };` unless you need to specify more options for the secret like owner/group of the secret file. diff --git a/docs/site/getting-started/secrets.md b/docs/site/getting-started/secrets.md index e01a41d1e..df06ccd71 100644 --- a/docs/site/getting-started/secrets.md +++ b/docs/site/getting-started/secrets.md @@ -6,7 +6,7 @@ Clan utilizes the [sops](https://github.com/getsops/sops) format and integrates This documentation will guide you through managing secrets with the Clan CLI -## 1. Initializing Secrets +## Initializing Secrets (Quickstart) ### Create Your Master Keypair @@ -28,11 +28,11 @@ Generated age private key at '/home/joerg/.config/sops/age/keys.txt' for your us Also add your age public key to the repository with 'clan secrets users add YOUR_USER age1wkth7uhpkl555g40t8hjsysr20drq286netu8zptw50lmqz7j95sw2t3l7' (replace YOUR_USER with your actual username) ``` -!!! warning +!!! warning Make sure to keep a safe backup of the private key you've just created. If it's lost, you won't be able to get to your secrets anymore because they all need the master key to be unlocked. -!!! note +!!! note It's safe to add any secrets created by the clan CLI and placed in your repository to version control systems like `git`. ### Add Your Public Key @@ -41,7 +41,7 @@ Also add your age public key to the repository with 'clan secrets users add YOUR clan secrets users add ``` -!!! note +!!! note Choose the same username as on your Setup/Source Machine that you use to control the deployment with. Once run this will create the following files: @@ -53,6 +53,137 @@ sops/ └── key.json ``` +--- + +> If you followed the quickstart tutorial all necessary secrets are initialized at this point. + +- Continue with [deploying machines](./machines.md) +- Learn about the [basics concept](#concept) of clan secrets + +--- + +## Concept + +The secrets system conceptually knows two different entities: + +- **Machine**: consumes secrets +- **User**: manages access to secrets + +**A Users** Can add or revoke machines' access to secrets. + +**A machine** Can decrypt secrets that where encrypted specifically for that machine. + +!!! Danger + **Always make sure at least one _User_ has access to a secret**. Otherwise you could lock yourself out from accessing the secret. + +### Inherited implications + +By default clan uses [sops](https://github.com/getsops/sops) through [sops-nix](https://github.com/Mic92/sops-nix) for managing its secrets which inherits some implications that are important to understand: + +- **Public/Private keys**: Entities are identified via their public keys. Each Entity can use their respective private key to decrypt a secret. +- **Public keys are stored**: All Public keys are stored inside the repository +- **Secrets are stored Encrypted**: secrets are stored inside the repository encrypted with the respective public keys +- **Secrets are deployed encrypted**: Fully encrypted secrets are deployed to machines at deployment time. +- **Secrets are decrypted by sops on-demand**: Each machine decrypts its secrets at runtime and stores them at an ephemeral location. +- **Machine key-pairs are auto-generated**: When a machine is created **no user-interaction is required** to setup public/private key-pairs. +- **secrets are re-encrypted**: In case machines, users or groups are modified secrets get re-encrypted on demand. + + !!! Important + After revoking access to a secret you should also change the underlying secret. i.e. change the API key, or the password. + +--- + +### Machine and user keys + +The following diagrams illustrates how a user can provide a secret (i.e. a Password). + +- By using the **Clan CLI** a user encrypts the password with both the **User public-key** and the **machine's public-key** + +- The *Machine* can decrypt the password with its private-key on demand. + +- The *User* is able to decrypt the password to make changes to it. + +```plantuml +@startuml +!include C4_Container.puml + +Person(user, "User", "Someone who manages secrets") +ContainerDb(secret, "Secret") +Container(machine, "Machine", "A Machine. i.e. Needs the Secret for a given Service." ) + +Rel_R(user, secret, "Encrypt", "", "Pubkeys: User, Machine") +Rel_L(secret, user, "Decrypt", "", "user privkey") +Rel_R(secret, machine, "Decrypt", "", "machine privkey" ) + +@enduml +``` + +### Groups + +It is possible to create semantic groups to make access control more convenient. + +#### User groups + +Here we illustrate how machine groups work. + +Common use cases: + +- **Shared Management**: Access among multiple users. I.e. a subset of secrets/machines that have two admins + +```plantuml +@startuml +!include C4_Container.puml + +System_Boundary(c1, "Group") { + Person(user1, "User A", "has access") + Person(user2, "User B", "has access") +} + +ContainerDb(secret, "Secret") +Container(machine, "Machine", "A Machine. i.e. Needs the Secret for a given Service." ) + +Rel_R(c1, secret, "Encrypt", "", "Pubkeys: User A, User B, Machine") +Rel_R(secret, machine, "Decrypt", "", "machine privkey" ) + + +@enduml +``` + + + +--- + +#### Machine groups + +Here we illustrate how machine groups work. + +Common use cases: + +- **Shared secrets**: Among multiple machines such as Wifi passwords + +```plantuml +@startuml +!include C4_Container.puml +!include C4_Deployment.puml + +Person(user, "User", "Someone who manages secrets") +ContainerDb(secret, "Secret") +System_Boundary(c1, "Group") { + Container(machine1, "Machine A", "Both machines need the same secret" ) + Container(machine2, "Machine B", "Both machines need the same secret" ) +} + +Rel_R(user, secret, "Encrypt", "", "Pubkeys: machine A, machine B, User") +Rel(secret, c1, "Decrypt", "", "Both machine A or B can decrypt using their private key" ) + + +@enduml +``` + + + +--- + ## 2. Adding Machine Keys New machines in Clan come with age keys stored in `./sops/machines/`. To list these machines: