{"id":26492988,"url":"https://github.com/fishinthecalculator/sops-guix","last_synced_at":"2025-10-26T21:41:42.832Z","repository":{"id":240816756,"uuid":"803536180","full_name":"fishinthecalculator/sops-guix","owner":"fishinthecalculator","description":"Secure secret provisioning with Guix and SOPS","archived":false,"fork":false,"pushed_at":"2025-07-30T22:21:42.000Z","size":163,"stargazers_count":31,"open_issues_count":2,"forks_count":5,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-08-26T06:39:34.520Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Scheme","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/fishinthecalculator.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null},"funding":{"liberapay":"fishinthecalculator"}},"created_at":"2024-05-20T23:07:25.000Z","updated_at":"2025-08-25T17:29:40.000Z","dependencies_parsed_at":"2024-06-17T17:03:54.460Z","dependency_job_id":"3f3daf97-3252-4fc2-82c9-e94b9f2969a3","html_url":"https://github.com/fishinthecalculator/sops-guix","commit_stats":null,"previous_names":["fishinthecalculator/sops-guix"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/fishinthecalculator/sops-guix","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fishinthecalculator%2Fsops-guix","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fishinthecalculator%2Fsops-guix/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fishinthecalculator%2Fsops-guix/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fishinthecalculator%2Fsops-guix/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/fishinthecalculator","download_url":"https://codeload.github.com/fishinthecalculator/sops-guix/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fishinthecalculator%2Fsops-guix/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":281178955,"owners_count":26456678,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-26T02:00:06.575Z","response_time":61,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-03-20T09:37:41.990Z","updated_at":"2025-10-26T21:41:42.803Z","avatar_url":"https://github.com/fishinthecalculator.png","language":"Scheme","funding_links":["https://liberapay.com/fishinthecalculator"],"categories":["Channels"],"sub_categories":[],"readme":"# SOPS Guix\n\n[![CI](https://github.com/fishinthecalculator/sops-guix/actions/workflows/main.yml/badge.svg)](https://github.com/fishinthecalculator/sops-guix/actions/workflows/main.yml)\n\nThis project aims at implementing secure provisioning of secrets with Guix and [SOPS](https://getsops.io). It was strongly inspired from NixOS' [sops-nix](https://github.com/Mic92/sops-nix).\n\n## Secure secret provisioning with Guix\n\nThis channels exposes the `sops-secrets-service-type` Guix service and the `sops-secret` record to safely handle secrets with Guix. It works by putting encrypted secrets in the store and by adding a one-shot Shepherd service that decrypts them at startup in a ramfs/tmpfs filesystem. This means that clear text secrets never hit the disk and that you can (and actually are encouraged to) check in your SOPS secrets in the same version control system you use to track you Guix configurations.\n\nAssuming that the right private keys are also provided, `sops-secret`s can be included in Guix images, deployed with `guix deploy` and included in Guix System/Home containers.\n\n### Creating secrets with SOPS\n\nFirst of all you need to create encrypted secrets with SOPS. To do so I'm assuming you already have a GPG key for yourself and the machines you want to deploy secrets to. You should be able to list the private keys you have in your keyring with\n\n``` bash\nuser1@home:~ $ gpg --list-secret-keys\n/home/user1/.gnupg/pubring.kbx\n------------------------\nsec   ed25519 2023-12-01 [SC] [expires: 2907-11-30]\n      8D1060B96BB8B7249AED41CC193B701E2SODIJNS\nuid           [ultimate] user1@example.org\nssb   cv25519 2023-12-01 [E]\n\npub   rsa3072 1970-01-01 [SCE]\n      8C3E4F6EB38828939029AE7BE9B6AF0CD39DD935\nuid           [ unknown] root (Imported from SSH) \u003croot@localhost\u003e\n\npub   rsa3072 1970-01-01 [SCE]\n      ZZ3E4VREB38800039029AE7BE9B6AF0CD39AALH9\nuid           [ unknown] root (Imported from SSH) \u003croot@localhost\u003e\n```\n\n\nIf you don't have a suitable set of GPG keys it's pretty simple to find online how generate them. Once you have a suitable set of keys for yourself and your machines you are ready to create the only configuration you need for SOPS: a `.sops.yaml` file that you will place in your project's root directory, or anyway in the same directory where you keep your system configuration. In this file you define which keys will be able to access your secrets files, it may very well be something like:\n\n``` yaml\nkeys:\n    - \u0026user_user1 8D1060B96BB8B7249AED41CC193B701E2SODIJNS\n    - \u0026host_host1 8C3E4F6EB38828939029AE7BE9B6AF0CD39DD935\n    - \u0026host_host2 ZZ3E4VREB38800039029AE7BE9B6AF0CD39AALH9\n\ncreation_rules:\n    - path_regex: .*common\\.yaml$\n      key_groups:\n          - pgp:\n                - *user_user1\n                - *host_host1\n                - *host_host2\n    - path_regex: .*host1\\.yaml$\n      key_groups:\n          - pgp:\n                - *user_user1\n                - *host_host1\n```\n\nIn this file we define three keys called `user_user1`, `host_host1` and `host_host2` . The prefixes `host_` and `user_` are just a convention to indicate that some GPG keys belong to users and some belong to machines.\n\nWe now have defined two secrets file names patterns and we declared permissions for each key, it should be possible now to run the following in your projects root directory:\n\n``` bash\nsops common.yaml\n```\n\nThis will open your default editor with an example content to define your secrets value. You can edit it or delete it and your own content for example:\n\n``` yaml\nwireguard:\n    private: MYPRIVATEKEY\n```\n\nafter saving and closing the file you can see by `cat`ting the secret file that `sops` encrypted it before saving it, so you are free to check it in your VCS.\n\n### Making sure the right host keys are in the configured GnuPG keyring\n\nFor hosts to be able to decrypt secrets you need to provide in the `root` user keyring (or anyway the keyring located at the configured `gnupg-homedir`) the keys you defined in your `.sops.yaml`. So based on the above example you'd need to provide `8C3E4F6EB38828939029AE7BE9B6AF0CD39DD935`'s private key on `host1` and `ZZ3E4VREB38800039029AE7BE9B6AF0CD39AALH9`'s private key on `host2` .\n\nTo check that your key is correctly imported into the keyring run:\n\n``` bash\nuser1@host1:~ $ sudo gpg --list-secret-keys\n/root/.gnupg/pubring.kbx\n------------------------\npub   rsa3072 1970-01-01 [SCE]\n      8C3E4F6EB38828939029AE7BE9B6AF0CD39DD935\nuid           [ unknown] root (Imported from SSH) \u003croot@localhost\u003e\n```\n\nBy setting `generate-key?` to `#t` in `sops-service-configuration` a GPG key will be automatically derived for you from your system's `/etc/ssh/ssh_host_rsa_key` and added to the configured keyring. It is *discouraged* to do so and you are more than encouraged to autonomously provide a key in your configured keyring. While having `sops-guix` generate a keypair on your behalf is easier, you have less control on the key. For example some could have a requirement to rotate the key periodically. You can certainly enable it once, deploy your configuration, turn it off and handle the key from there, but having the key managed by `sops-guix` may not be the best default choice in every case.\n\n### Adding secrets to your `operating-system` record\n\nNow, supposing you have your `operating-system` file in the same directory where you have your `.sops.yaml` and `common.yaml` files, you can simply add the following to your configuration:\n\n``` scheme\n(use-modules (sops secrets)\n             (sops services sops)\n             (guix utils))\n\n(define project-root\n  (current-source-directory))\n\n(define sops.yaml\n  (local-file (string-append project-root \"/.sops.yaml\")\n              ;; This is because paths on the store\n              ;; can not start with dots.\n              \"sops.yaml\"))\n\n(define common.yaml\n  (local-file (string-append project-root \"/common.yaml\")))\n\n(operating-system\n  [...]\n  (services\n    (list\n       [...]\n       (service sops-secrets-service-type\n                (sops-service-configuration\n                  (gnupg-homedir \"/mnt/.gnupg\")\n                  (generate-key? #t)\n                  (config sops.yaml)\n                  (secrets\n                    (list\n                      (sops-secret\n                        (key '(\"wireguard\" \"private\"))\n                        (file common.yaml)\n                        (user \"user1\")\n                        (group \"users\")\n                        (permissions #o400)))))))))\n```\n\nUpon reconfiguration, this will yield the following content at `/run/secrets`:\n\n``` bash\nuser1@host1:~ $ sudo ls -la /run/secrets/\ntotal 12\ndrwxr-xr-x 1 root root    50 Jan  2 12:44 .\ndrwxr-xr-x 1 root root   254 Jan  2 12:44 ..\nlrwxrwxrwx 1 root root    53 Jan  2 12:44 .sops.yaml -\u003e /gnu/store/lyhyh91jw2n2asa1w0fc0zmv93yxkxip-sops.yaml\n-r-------- 1 user1 users  44 Jan  2 12:44 wireguard\nuser1@host1:~ $ cat /run/secrets/wireguard/private\nMYPRIVATEKEY\n```\n\n### Adding secrets to your `home-environment` record\n\n`sops-guix` also provides a Guix Home service that is able to provide most feature of the system service. Most significant limitations are:\n\n- AFAIK `home-environment`s can't configure ramfs mount points hence the `secrets-directory` option is not available. Secrets are stored in `/run/user/$UID/secrets` which usually is mounted on tmpfs.\n- `sops-secret-user` and `sops-secrets-group` are ignored. All secrets belong to the user running the Guix comman line but you can still set permissions.\n- There's no option to automatically generate GPG keys since probably users can easily generate one.\n\nNow, supposing you have your `home-environment` file in the same directory where you have your `.sops.yaml` and your secrets files, you can simply add the following to your configuration:\n\n``` scheme\n(use-modules (sops secrets)\n             (sops home services sops)\n             (guix utils))\n\n(define project-root\n  (current-source-directory))\n\n(define sops.yaml\n  (local-file (string-append project-root \"/.sops.yaml\")\n              ;; This is because paths on the store\n              ;; can not start with dots.\n              \"sops.yaml\"))\n\n(define user1.yaml\n  (local-file (string-append project-root \"/user1.yaml\")))\n\n(home-environment\n  [...]\n  (services\n    (list\n       [...]\n       (service home-sops-secrets-service-type\n                (home-sops-service-configuration\n                  (gnupg-homedir (string-append (getenv \"HOME\") \"/.gnupg\"))\n                  (config sops.yaml)\n                  (secrets\n                    (list\n                      (sops-secret\n                        (key '(\"wireguard\" \"private\"))\n                        (file user1.yaml)\n                        (permissions #o400)))))))))\n```\n\nUpon reconfiguration, this will yield the following content at `/run/secrets/$YOUR_UID/secrets`:\n\n``` bash\nuser1@host1:~ $ ls -la /run/user/$(id -u)/secrets\ntotal 12\ndrwxr-xr-x 1 user1 users    50 Jan  2 12:44 .\ndrwxr-xr-x 1 user1 users   254 Jan  2 12:44 ..\nlrwxrwxrwx 1 user1 users    53 Jan  2 12:44 .sops.yaml -\u003e /gnu/store/lyhyh91jw2n2asa1w0fc0zmv93yxkxip-sops.yaml\n-r-------- 1 user1 users    44 Jan  2 12:44 wireguard\nuser1@host1:~ $ cat /run/user/$(id -u)/secrets/wireguard/private\nMYPRIVATEKEY\n```\n\n## Configure\n\nTo configure Guix for using this channel you need to create a `.config/guix/channels.scm` file with the following content:\n\n``` scheme\n(cons* (channel\n        (name 'sops-guix)\n        (url \"https://github.com/fishinthecalculator/sops-guix\")\n        (branch \"main\")\n        ;; Enable signature verification:\n        (introduction\n         (make-channel-introduction\n          \"0bbaf1fdd25266c7df790f65640aaa01e6d2dbc9\"\n          (openpgp-fingerprint\n           \"8D10 60B9 6BB8 292E 829B  7249 AED4 1CC1 93B7 01E2\"))))\n       %default-channels)\n```\n\nOtherwise, if you already have a `.config/guix/channels.scm` you can simply prepend this channel to the preexisting ones:\n\n``` scheme\n(cons* (channel\n        (name 'sops-guix)\n        (url \"https://github.com/fishinthecalculator/sops-guix\")\n        (branch \"main\")\n        ;; Enable signature verification:\n        (introduction\n         (make-channel-introduction\n          \"0bbaf1fdd25266c7df790f65640aaa01e6d2dbc9\"\n          (openpgp-fingerprint\n           \"8D10 60B9 6BB8 292E 829B  7249 AED4 1CC1 93B7 01E2\"))))\n       (channel\n        (name 'nonguix)\n        (url \"https://gitlab.com/nonguix/nonguix\")\n        ;; Enable signature verification:\n        (introduction\n         (make-channel-introduction\n          \"897c1a470da759236cc11798f4e0a5f7d4d59fbc\"\n          (openpgp-fingerprint\n           \"2A39 3FFF 68F4 EF7A 3D29  12AF 6F51 20A0 22FB B2D5\"))))\n       %default-channels)\n```\n\n### What is a Guix channel?\n\nA [channel](https://guix.gnu.org/manual/devel/en/guix.html#Channels) is roughly the Guix equivalent of the AUR or container registries. It's a software repository providing Guix package and service definitions.\n\nYou can search for package and service definitions from this channel and many others at [toys.whereis.social](https://toys.whereis.social).\n\n## Contributing\n\nAll contributions are welcome. If you have commit access please remember to setup the authentication hook with\n\n```bash\ngit fetch origin keyring:keyring\nguix git authenticate --cache-key=channels/sops-guix 0bbaf1fdd25266c7df790f65640aaa01e6d2dbc9 '8D10 60B9 6BB8 292E 829B  7249 AED4 1CC1 93B7 01E2'\n```\n\n## License\n\nUnless otherwise stated all the files in this repository are to be considered under the GPL 3.0 terms. You are more than welcome to open issues or send patches.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffishinthecalculator%2Fsops-guix","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffishinthecalculator%2Fsops-guix","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffishinthecalculator%2Fsops-guix/lists"}