* Deferring evaluation of a get-secret procedure so -L doesn't evaluate it unless needed for build
@ 2024-01-04 17:28 Richard Sent
2024-01-05 12:17 ` Tomas Volf
0 siblings, 1 reply; 2+ messages in thread
From: Richard Sent @ 2024-01-04 17:28 UTC (permalink / raw)
To: help-guix
[-- Attachment #1: Type: text/plain, Size: 2499 bytes --]
Hi Guix!
Until recently I reconfigured my home & system by setting the
GUILE_LOAD_PATH env var, but I am now trying to transition to using the
-L argument.
I have a configuration repo that's broken down into separate modules,
(mostly) like so:
--8<---------------cut here---------------start------------->8---
lib
└── rsent
├── constants
│ └── wireguard.scm
├── home
│ └── pathfinder.scm
├── system
│ └── pathfinder.scm
└── utils
└── secrets.scm
--8<---------------cut here---------------end--------------->8---
wireguard.scm contains code that fetches secret values
(private+preshared keys) from my password store and defines a service
using that secret value. The code looks something like this:
--8<---------------cut here---------------start------------->8---
(define wireguard-lan-secret-service
(service
(wireguard-configuration
...
(private-key
(plain-file "private.key"
(get-secret*
"System/WireGuard/LAN/private.key"))))))
--8<---------------cut here---------------end--------------->8---
I've noticed that when I run `guix home reconfigure -L lib
lib/rsent/home/pathfinder.scm`, (get-secret* ...) is still evaluated,
meaning that I'm prompted for a password when I don't need to enter one
(home-environment doesn't support wireguard-service). I don't have this
problem if I run `GUILE_LOAD_PATH=lib guix home reconfigure
lib/rsent/home/pathfinder.scm`, presumably because Guix's -L doesn't
just add to the load path, but also evaluates every file for possible
package definitions.
I suspect I need to replace (plain-file ...) with another option,
perhaps (computed-file), as well as rework (get-secret*) into a gexp.
I'm struggling with the syntax though, so any help in this would be
appreciated. Or if there's a better solution, that would be amazing!
*On another note*, should Guix have another command line flag that
behaves identically to `$ guile -L`? Adds directory to the load path,
but does not do anything else? The distinction between Guile and Guix's
-L isn't emphasized enough in my experience. To a beginner, these two
sound identical.
Guile: -L DIRECTORY add DIRECTORY to the front of the module load
path
Guix: -L, --load-path=DIR prepend DIR to the package module search
path
Apologies for the wall of text, hopefully someone has some thoughts.
Richard
[-- Attachment #2: secrets.scm --]
[-- Type: text/plain, Size: 2559 bytes --]
(define-module (rsent utils secrets)
#:use-module (gnu services)
#:use-module (gnu services shepherd)
#:use-module (gnu services docker)
#:use-module (ice-9 popen)
#:use-module (ice-9 rdelim)
#:use-module (guix memoization)
#:export (get-secret*)
#:export (remove-unused-secret-services)
#:export (when-using-secrets)
#:export (secret-service))
(define (sudo-user)
"Returns a @code{string} representing the user the pass commands should run as. This
is necessary because reconfigure requires sudo to be used, which would run a sudoless
pass command as root. This causes pass to miss the password store."
(or (getenv "SUDO_USER") (getenv "USER")))
(define (use-secrets)
"Returns #t if secrets should be used."
(not (getenv "RSENT_NO_SECRETS")))
(define-syntax when-using-secrets
(syntax-rules ()
"When macro with a specific test. Must be written as a macro instead of
a function to avoid evaluating b1. b1 would be evaled before being
passed to the function, and trying to cheat with a sexp and (eval)
fails because the function doesn't have visibility to the record
definition fields."
((_ b1 ...)
(if (use-secrets) (begin b1 ...)))))
(define-syntax secret-service
(syntax-rules ()
"A secret service is a service that is replaced with #<unspecified>
when (use-secrets) is false."
((_ a b c c* ...)
(syntax-error "Too many arguments"))
((_ service-type service-configuration ...)
(when-using-secrets (service service-type service-configuration ...)))))
(define (get-secret key)
"Returns the associated secret for key. Key should be in the form of a password-store path."
;; TODO: Throw error if pass fails.
(format #t "~!Fetching secret for ~a... " key)
(let* ((port (open-input-pipe (string-append "sudo -u " (sudo-user) " pass ls " key)))
(str (read-line port))) ; from (ice-9 rdelim)
(close-pipe port)
(format #t "~!done\n")
str))
;; Memoize because guix seems to call this function twice per secret,
;; with a nontrivial time delay between them. This technically makes
;; it less secure since it leaves the secret in memory after it's
;; used, but the secrets are being written to the store anyway and
;; this particular attack vector would require a high level of access
;; to the machine.
(define get-secret* (memoize get-secret))
(define (remove-unused-secret-services services)
"Wrap the list of services with this when using services that may include secrets."
(filter (lambda (val)
(not (unspecified? val)))
services))
[-- Attachment #3: wireguard.scm --]
[-- Type: text/plain, Size: 1467 bytes --]
(define-module (rsent constants wireguard)
#:use-module (gnu services)
#:use-module (gnu services shepherd)
#:use-module (gnu services vpn)
#:use-module (guix records)
#:use-module (guix gexp)
#:use-module (gnu system)
#:use-module (rsent utils services)
#:use-module (rsent utils secrets)
#:export (wireguard-nickleslan-secret-service)
#:export (wireguard-nickleslan-operating-system))
(define wireguard-nickleslan-secret-service
(secret-service
(auto-start-disabled wireguard-service-type)
(wireguard-configuration
(interface "wg-nicklesbread")
(private-key
(plain-file "private.key"
(get-secret*
"System/WireGuard/NicklesBread/private.key")))
(addresses '("10.169.222.4/24"))
(dns '("10.1.1.1"))
(peers
(list
(wireguard-peer
(name "nickleslan")
(endpoint "nicleary.com:51820")
(public-key "EHoPXGJvQVVpQ6PZ/XQtHx0p5FWEVCS3y2oI2O+Y9zo=")
(preshared-key
(plain-file "preshared.key"
(get-secret*
"System/WireGuard/NicklesBread/preshared.key")))
(allowed-ips '("10.1.1.0/24"))))))))
(define (wireguard-nickleslan-operating-system base-operating-system)
(operating-system
(inherit base-operating-system)
(services
(append (remove-unused-secret-services (list wireguard-nickleslan-secret-service))
(operating-system-user-services base-operating-system)))))
^ permalink raw reply [flat|nested] 2+ messages in thread
* Re: Deferring evaluation of a get-secret procedure so -L doesn't evaluate it unless needed for build
2024-01-04 17:28 Deferring evaluation of a get-secret procedure so -L doesn't evaluate it unless needed for build Richard Sent
@ 2024-01-05 12:17 ` Tomas Volf
0 siblings, 0 replies; 2+ messages in thread
From: Tomas Volf @ 2024-01-05 12:17 UTC (permalink / raw)
To: Richard Sent; +Cc: help-guix
[-- Attachment #1: Type: text/plain, Size: 766 bytes --]
Hi,
I cannot help you with your question, but completely unsolicited feedback
regarding this snippet:
On 2024-01-04 12:28:28 -0500, Richard Sent wrote:
> --8<---------------cut here---------------start------------->8---
> (define wireguard-lan-secret-service
> (service
> (wireguard-configuration
> ...
> (private-key
> (plain-file "private.key"
> (get-secret*
> "System/WireGuard/LAN/private.key"))))))
> --8<---------------cut here---------------end--------------->8---
Storing secret keys in the store might not be the best idea, since they will be
world readable.
Have a nice day,
Tomas Volf
--
There are only two hard things in Computer Science:
cache invalidation, naming things and off-by-one errors.
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2024-01-05 12:18 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-01-04 17:28 Deferring evaluation of a get-secret procedure so -L doesn't evaluate it unless needed for build Richard Sent
2024-01-05 12:17 ` Tomas Volf
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).