diff options
| author | Ludovic Courtès <ludo@gnu.org> | 2013-12-30 22:46:21 +0100 |
|---|---|---|
| committer | Ludovic Courtès <ludo@gnu.org> | 2013-12-30 22:57:37 +0100 |
| commit | 554f26ece3c6e3fb04d8069e6be1095e622a97c5 (patch) | |
| tree | 4a64678b2f1c34c72a53e84264ca56a09b34c72c | |
| parent | dedb5d947ee2890524a5c6fb1343b3299e7731c3 (diff) | |
archive: Add '--generate-key'.
* guix/pk-crypto.scm (error-source, error-string): New procedures.
* guix/pki.scm (%private-key-file): New variable.
* guix/scripts/archive.scm (show-help): Document '--generate-key'.
(%options): Add "generate-key".
(generate-key-pair): New procedure.
(guix-archive): Call 'generate-key' when OPTS contains a
'generate-key' pair.
* doc/guix.texi (Setting Up the Daemon): Suggest generating a key pair.
(Invoking guix archive): Document '--generate-key'.
| -rw-r--r-- | doc/guix.texi | 22 | ||||
| -rw-r--r-- | guix/pk-crypto.scm | 18 | ||||
| -rw-r--r-- | guix/pki.scm | 4 | ||||
| -rw-r--r-- | guix/scripts/archive.scm | 74 |
4 files changed, 108 insertions, 10 deletions
diff --git a/doc/guix.texi b/doc/guix.texi index afa7654d544..ec529346c7a 100644 --- a/doc/guix.texi +++ b/doc/guix.texi | |||
| @@ -237,6 +237,14 @@ case, shared memory support is unavailable in the chroot environment. | |||
| 237 | The workaround is to make sure that @file{/dev/shm} is directly a | 237 | The workaround is to make sure that @file{/dev/shm} is directly a |
| 238 | @code{tmpfs} mount point.}. | 238 | @code{tmpfs} mount point.}. |
| 239 | 239 | ||
| 240 | Finally, you may want to generate a key pair to allow the daemon to | ||
| 241 | export signed archives of files from the store (@pxref{Invoking guix | ||
| 242 | archive}): | ||
| 243 | |||
| 244 | @example | ||
| 245 | # guix archive --generate-key | ||
| 246 | @end example | ||
| 247 | |||
| 240 | Guix may also be used in a single-user setup, with @command{guix-daemon} | 248 | Guix may also be used in a single-user setup, with @command{guix-daemon} |
| 241 | running as an unprivileged user. However, to maximize non-interference | 249 | running as an unprivileged user. However, to maximize non-interference |
| 242 | of build processes, the daemon still needs to perform certain operations | 250 | of build processes, the daemon still needs to perform certain operations |
| @@ -948,6 +956,20 @@ resulting archive to the standard output. | |||
| 948 | Read an archive from the standard input, and import the files listed | 956 | Read an archive from the standard input, and import the files listed |
| 949 | therein into the store. Abort if the archive has an invalid digital | 957 | therein into the store. Abort if the archive has an invalid digital |
| 950 | signature. | 958 | signature. |
| 959 | |||
| 960 | @item --generate-key[=@var{parameters}] | ||
| 961 | Generate a new key pair for the daemons. This is a prerequisite before | ||
| 962 | archives can be exported with @code{--export}. Note that this operation | ||
| 963 | usually takes time, because it needs to gather enough entropy to | ||
| 964 | generate the key pair. | ||
| 965 | |||
| 966 | The generated key pair is typically stored under @file{/etc/guix}, in | ||
| 967 | @file{signing-key.pub} (public key) and @file{signing-key.sec} (private | ||
| 968 | key, which must be kept secret.) When @var{parameters} is omitted, it | ||
| 969 | is a 4096-bit RSA key. Alternately, @var{parameters} can specify | ||
| 970 | @code{genkey} parameters suitable for Libgcrypt (@pxref{General | ||
| 971 | public-key related Functions, @code{gcry_pk_genkey},, gcrypt, The | ||
| 972 | Libgcrypt Reference Manual}). | ||
| 951 | @end table | 973 | @end table |
| 952 | 974 | ||
| 953 | To export store files as an archive to the standard output, run: | 975 | To export store files as an archive to the standard output, run: |
diff --git a/guix/pk-crypto.scm b/guix/pk-crypto.scm index d5b3eeb3508..50f709418c9 100644 --- a/guix/pk-crypto.scm +++ b/guix/pk-crypto.scm | |||
| @@ -25,6 +25,8 @@ | |||
| 25 | #:use-module (rnrs bytevectors) | 25 | #:use-module (rnrs bytevectors) |
| 26 | #:use-module (ice-9 match) | 26 | #:use-module (ice-9 match) |
| 27 | #:export (canonical-sexp? | 27 | #:export (canonical-sexp? |
| 28 | error-source | ||
| 29 | error-string | ||
| 28 | string->canonical-sexp | 30 | string->canonical-sexp |
| 29 | canonical-sexp->string | 31 | canonical-sexp->string |
| 30 | number->canonical-sexp | 32 | number->canonical-sexp |
| @@ -98,6 +100,22 @@ | |||
| 98 | (set-pointer-finalizer! ptr finalize-canonical-sexp!)) | 100 | (set-pointer-finalizer! ptr finalize-canonical-sexp!)) |
| 99 | sexp)) | 101 | sexp)) |
| 100 | 102 | ||
| 103 | (define error-source | ||
| 104 | (let* ((ptr (libgcrypt-func "gcry_strsource")) | ||
| 105 | (proc (pointer->procedure '* ptr (list int)))) | ||
| 106 | (lambda (err) | ||
| 107 | "Return the error source (a string) for ERR, an error code as thrown | ||
| 108 | along with 'gcry-error'." | ||
| 109 | (pointer->string (proc err))))) | ||
| 110 | |||
| 111 | (define error-string | ||
| 112 | (let* ((ptr (libgcrypt-func "gcry_strerror")) | ||
| 113 | (proc (pointer->procedure '* ptr (list int)))) | ||
| 114 | (lambda (err) | ||
| 115 | "Return the error description (a string) for ERR, an error code as | ||
| 116 | thrown along with 'gcry-error'." | ||
| 117 | (pointer->string (proc err))))) | ||
| 118 | |||
| 101 | (define string->canonical-sexp | 119 | (define string->canonical-sexp |
| 102 | (let* ((ptr (libgcrypt-func "gcry_sexp_new")) | 120 | (let* ((ptr (libgcrypt-func "gcry_sexp_new")) |
| 103 | (proc (pointer->procedure int ptr `(* * ,size_t ,int)))) | 121 | (proc (pointer->procedure int ptr `(* * ,size_t ,int)))) |
diff --git a/guix/pki.scm b/guix/pki.scm index 1ed84e55f00..759cd040e95 100644 --- a/guix/pki.scm +++ b/guix/pki.scm | |||
| @@ -23,6 +23,7 @@ | |||
| 23 | #:use-module (ice-9 match) | 23 | #:use-module (ice-9 match) |
| 24 | #:use-module (rnrs io ports) | 24 | #:use-module (rnrs io ports) |
| 25 | #:export (%public-key-file | 25 | #:export (%public-key-file |
| 26 | %private-key-file | ||
| 26 | current-acl | 27 | current-acl |
| 27 | public-keys->acl | 28 | public-keys->acl |
| 28 | acl->public-keys | 29 | acl->public-keys |
| @@ -69,6 +70,9 @@ element in KEYS must be a canonical sexp with type 'public-key'." | |||
| 69 | (define %public-key-file | 70 | (define %public-key-file |
| 70 | (string-append %config-directory "/signing-key.pub")) | 71 | (string-append %config-directory "/signing-key.pub")) |
| 71 | 72 | ||
| 73 | (define %private-key-file | ||
| 74 | (string-append %config-directory "/signing-key.sec")) | ||
| 75 | |||
| 72 | (define (ensure-acl) | 76 | (define (ensure-acl) |
| 73 | "Make sure the ACL file exists, and create an initialized one if needed." | 77 | "Make sure the ACL file exists, and create an initialized one if needed." |
| 74 | (unless (file-exists? %acl-file) | 78 | (unless (file-exists? %acl-file) |
diff --git a/guix/scripts/archive.scm b/guix/scripts/archive.scm index df538ed1b7d..a9e41553930 100644 --- a/guix/scripts/archive.scm +++ b/guix/scripts/archive.scm | |||
| @@ -23,6 +23,8 @@ | |||
| 23 | #:use-module (guix packages) | 23 | #:use-module (guix packages) |
| 24 | #:use-module (guix derivations) | 24 | #:use-module (guix derivations) |
| 25 | #:use-module (guix ui) | 25 | #:use-module (guix ui) |
| 26 | #:use-module (guix pki) | ||
| 27 | #:use-module (guix pk-crypto) | ||
| 26 | #:use-module (ice-9 match) | 28 | #:use-module (ice-9 match) |
| 27 | #:use-module (srfi srfi-1) | 29 | #:use-module (srfi srfi-1) |
| 28 | #:use-module (srfi srfi-11) | 30 | #:use-module (srfi srfi-11) |
| @@ -53,6 +55,9 @@ Export/import one or more packages from/to the store.\n")) | |||
| 53 | --import import from the archive passed on stdin")) | 55 | --import import from the archive passed on stdin")) |
| 54 | (newline) | 56 | (newline) |
| 55 | (display (_ " | 57 | (display (_ " |
| 58 | --generate-key[=PARAMETERS] | ||
| 59 | generate a key pair with the given parameters")) | ||
| 60 | (display (_ " | ||
| 56 | -e, --expression=EXPR build the package or derivation EXPR evaluates to")) | 61 | -e, --expression=EXPR build the package or derivation EXPR evaluates to")) |
| 57 | (display (_ " | 62 | (display (_ " |
| 58 | -S, --source build the packages' source derivations")) | 63 | -S, --source build the packages' source derivations")) |
| @@ -95,6 +100,17 @@ Export/import one or more packages from/to the store.\n")) | |||
| 95 | (option '("import") #f #f | 100 | (option '("import") #f #f |
| 96 | (lambda (opt name arg result) | 101 | (lambda (opt name arg result) |
| 97 | (alist-cons 'import #t result))) | 102 | (alist-cons 'import #t result))) |
| 103 | (option '("generate-key") #f #t | ||
| 104 | (lambda (opt name arg result) | ||
| 105 | (catch 'gcry-error | ||
| 106 | (lambda () | ||
| 107 | (let ((params | ||
| 108 | (string->canonical-sexp | ||
| 109 | (or arg "(genkey (rsa (nbits 4:4096)))")))) | ||
| 110 | (alist-cons 'generate-key params result))) | ||
| 111 | (lambda args | ||
| 112 | (leave (_ "invalid key generation parameters: ~s~%") | ||
| 113 | arg))))) | ||
| 98 | 114 | ||
| 99 | (option '(#\S "source") #f #f | 115 | (option '(#\S "source") #f #f |
| 100 | (lambda (opt name arg result) | 116 | (lambda (opt name arg result) |
| @@ -204,7 +220,41 @@ resulting archive to the standard output port." | |||
| 204 | (if (or (assoc-ref opts 'dry-run?) | 220 | (if (or (assoc-ref opts 'dry-run?) |
| 205 | (build-derivations store drv)) | 221 | (build-derivations store drv)) |
| 206 | (export-paths store files (current-output-port)) | 222 | (export-paths store files (current-output-port)) |
| 207 | (leave (_ "unable to export the given packages"))))) | 223 | (leave (_ "unable to export the given packages~%"))))) |
| 224 | |||
| 225 | (define (generate-key-pair parameters) | ||
| 226 | "Generate a key pair with PARAMETERS, a canonical sexp, and store it in the | ||
| 227 | right place." | ||
| 228 | (when (or (file-exists? %public-key-file) | ||
| 229 | (file-exists? %private-key-file)) | ||
| 230 | (leave (_ "key pair exists under '~a'; remove it first~%") | ||
| 231 | (dirname %public-key-file))) | ||
| 232 | |||
| 233 | (format (current-error-port) | ||
| 234 | (_ "Please wait while gathering entropy to generate the key pair; | ||
| 235 | this may take time...~%")) | ||
| 236 | |||
| 237 | (let* ((pair (catch 'gcry-error | ||
| 238 | (lambda () | ||
| 239 | (generate-key parameters)) | ||
| 240 | (lambda (key err) | ||
| 241 | (leave (_ "key generation failed: ~a: ~a~%") | ||
| 242 | (error-source err) | ||
| 243 | (error-string err))))) | ||
| 244 | (public (find-sexp-token pair 'public-key)) | ||
| 245 | (secret (find-sexp-token pair 'private-key))) | ||
| 246 | ;; Create the following files as #o400. | ||
| 247 | (umask #o266) | ||
| 248 | |||
| 249 | (with-atomic-file-output %public-key-file | ||
| 250 | (lambda (port) | ||
| 251 | (display (canonical-sexp->string public) port))) | ||
| 252 | (with-atomic-file-output %private-key-file | ||
| 253 | (lambda (port) | ||
| 254 | (display (canonical-sexp->string secret) port))) | ||
| 255 | |||
| 256 | ;; Make the public key readable by everyone. | ||
| 257 | (chmod %public-key-file #o444))) | ||
| 208 | 258 | ||
| 209 | (define (guix-archive . args) | 259 | (define (guix-archive . args) |
| 210 | (define (parse-options) | 260 | (define (parse-options) |
| @@ -220,13 +270,17 @@ resulting archive to the standard output port." | |||
| 220 | ;; Ask for absolute file names so that .drv file names passed from the | 270 | ;; Ask for absolute file names so that .drv file names passed from the |
| 221 | ;; user to 'read-derivation' are absolute when it returns. | 271 | ;; user to 'read-derivation' are absolute when it returns. |
| 222 | (with-fluids ((%file-port-name-canonicalization 'absolute)) | 272 | (with-fluids ((%file-port-name-canonicalization 'absolute)) |
| 223 | (let* ((opts (parse-options)) | 273 | (let ((opts (parse-options))) |
| 224 | (store (open-connection))) | 274 | (cond ((assoc-ref opts 'generate-key) |
| 225 | 275 | => | |
| 226 | (cond ((assoc-ref opts 'export) | 276 | generate-key-pair) |
| 227 | (export-from-store store opts)) | ||
| 228 | ((assoc-ref opts 'import) | ||
| 229 | (import-paths store (current-input-port))) | ||
| 230 | (else | 277 | (else |
| 231 | (leave | 278 | (let ((store (open-connection))) |
| 232 | (_ "either '--export' or '--import' must be specified")))))))) | 279 | (cond ((assoc-ref opts 'export) |
| 280 | (export-from-store store opts)) | ||
| 281 | ((assoc-ref opts 'import) | ||
| 282 | (import-paths store (current-input-port))) | ||
| 283 | (else | ||
| 284 | (leave | ||
| 285 | (_ "either '--export' or '--import' \ | ||
| 286 | must be specified~%"))))))))))) | ||
