doc: Document authentication.
* etc/new-client-cert.scm: Add script. * doc/cuirass.texi (Authentication): Document it. * Makefile.am (noinst_SCRIPTS): Register it. Signed-off-by: Ludovic Courtès <ludo@gnu.org>
This commit is contained in:
parent
b76c04199f
commit
7c9fc0645e
|
@ -25,7 +25,7 @@
|
|||
bin_SCRIPTS = \
|
||||
bin/cuirass
|
||||
|
||||
noinst_SCRIPTS = pre-inst-env
|
||||
noinst_SCRIPTS = pre-inst-env etc/new-client-cert.scm
|
||||
|
||||
guilesitedir = $(datarootdir)/guile/site/@GUILE_EFFECTIVE_VERSION@
|
||||
guileobjectdir = $(libdir)/guile/@GUILE_EFFECTIVE_VERSION@/site-ccache
|
||||
|
|
|
@ -13,6 +13,7 @@ Copyright @copyright{} 2016, 2017 Mathieu Lirzin@*
|
|||
Copyright @copyright{} 2017, 2020, 2021 Mathieu Othacehe@*
|
||||
Copyright @copyright{} 2018, 2021, 2023 Ludovic Courtès@*
|
||||
Copyright @copyright{} 2018 Clément Lassieur
|
||||
Copyright @copyright{} 2023 Maxim Cournoyer@*
|
||||
|
||||
@quotation
|
||||
Permission is granted to copy, distribute and/or modify this document
|
||||
|
@ -57,6 +58,7 @@ Documentation License''.
|
|||
* Parameters:: Cuirass parameters.
|
||||
* Build modes:: Build modes.
|
||||
* Invocation:: How to run Cuirass.
|
||||
* Authentication:: Configuring TLS authentication.
|
||||
* Web API:: Description of the Web API.
|
||||
* Database:: About the database schema.
|
||||
|
||||
|
@ -707,6 +709,90 @@ Display the actual version of @code{cuirass}.
|
|||
Display an help message that summarize all the options provided.
|
||||
@end table
|
||||
|
||||
@c *********************************************************************
|
||||
@node Authentication
|
||||
@chapter Authentication
|
||||
@cindex authentication
|
||||
|
||||
Cuirass does not provide its own authentication mechanism; by default,
|
||||
any user can do anything via its web interface. To restrict this to
|
||||
only authorized users, one approach is to proxy the Cuirass web site via
|
||||
a web server such as Nginx and configure the web server to require
|
||||
client certificate verification for pages under the @samp{/admin}
|
||||
prefix. The following minimal Nginx configuration can be used to
|
||||
accomplish this on a Guix System:
|
||||
|
||||
@lisp
|
||||
(service nginx-service-type
|
||||
(nginx-configuration
|
||||
(server-blocks
|
||||
(list
|
||||
;; TLS is required for authentication; serve the site via
|
||||
;; HTTPS only.
|
||||
(nginx-server-configuration
|
||||
(listen '("80"))
|
||||
(raw-content
|
||||
(list "return 308 https://$host$request_uri;")))
|
||||
|
||||
(nginx-server-configuration
|
||||
(listen '("443 ssl"))
|
||||
(server-name '("ci.your-host.org"))
|
||||
(ssl-certificate "/etc/certs/ci.your-host.org.crt")
|
||||
(ssl-certificate-key "/etc/certs/ci.your-host.org.key")
|
||||
(locations
|
||||
(list
|
||||
;; Proxy the whole Cuirass web site...
|
||||
(nginx-location-configuration
|
||||
(uri "/")
|
||||
(body (list "proxy_pass http://localhost:8081;")))
|
||||
;; ... but require authentication for the admin pages.
|
||||
(nginx-location-configuration
|
||||
(uri "~ ^/admin")
|
||||
(body
|
||||
(list "if ($ssl_client_verify != SUCCESS) \
|
||||
@{ return 403; @} proxy_pass http://localhost:8081;")))))
|
||||
(raw-content
|
||||
;; Register your self-generated certificate authority.
|
||||
(list "ssl_client_certificate /etc/ssl-ca/certs/ca.crt;"
|
||||
"ssl_verify_client optional;")))))))
|
||||
@end lisp
|
||||
|
||||
Your host TLS certificate could have been obtained via Let's Encrypt or
|
||||
directly via the @command{openssl} command, among other means. To
|
||||
create a private certificate authority (CA) that can sign user
|
||||
certificates, a convenience script is provided. It's main requirement
|
||||
is to have the @command{guix} command available. It can be invoked
|
||||
like:
|
||||
|
||||
@example
|
||||
sudo -E ./etc/new-client-cert.scm --generate-ca
|
||||
@end example
|
||||
|
||||
It should generate the @file{/etc/ssl-ca/private/ca.key} private key as
|
||||
well as the @file{/etc/ssl-ca/certs/ca.crt} certificate authority as
|
||||
used in the Nginx configuration above.
|
||||
|
||||
To issue a new user certificate, run the same script from your home
|
||||
directory with:
|
||||
|
||||
@example
|
||||
sudo -E ./etc/new-client-cert.scm
|
||||
@end example
|
||||
|
||||
You will be asked to input the password for the CA private key, if any,
|
||||
and again for your new certificate; save it carefully. The script
|
||||
requires to run as root to have access to the private certificate
|
||||
authority key; it outputs the new user certificate files to the current
|
||||
working directory.
|
||||
|
||||
After your new CA-signed user certificate is generated, it needs to be
|
||||
registered with your web browser. To do so using GNU IceCat, for
|
||||
example, you can navigate to @samp{Parameters -> Security -> Show
|
||||
certificates} and then click the @samp{Import...} button and select your
|
||||
@file{.pk12} personal certificate file. The web interface of Cuirass
|
||||
should now only allow authenticated users to perform administrative
|
||||
tasks.
|
||||
|
||||
@c *********************************************************************
|
||||
@node Web API
|
||||
@chapter Web API
|
||||
|
|
|
@ -0,0 +1,121 @@
|
|||
#!/usr/bin/env -S guix shell guile openssl -- guile \\
|
||||
--no-auto-compile -e main -s
|
||||
!#
|
||||
;;;; cuirass.scm -- Cuirass public interface.
|
||||
;;; Copyright © 2023 Ricardo Wurmus <rekado@elephly.net>
|
||||
;;; Copyright © 2023 Maxim Cournoyer <maxim.cournoyer@gmail.com>
|
||||
;;;
|
||||
;;; This file is part of Cuirass.
|
||||
;;;
|
||||
;;; Cuirass is free software: you can redistribute it and/or modify
|
||||
;;; it under the terms of the GNU General Public License as published by
|
||||
;;; the Free Software Foundation, either version 3 of the License, or
|
||||
;;; (at your option) any later version.
|
||||
;;;
|
||||
;;; Cuirass is distributed in the hope that it will be useful,
|
||||
;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
;;; GNU General Public License for more details.
|
||||
;;;
|
||||
;;; You should have received a copy of the GNU General Public License
|
||||
;;; along with Cuirass. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
(use-modules (ice-9 format)
|
||||
(ice-9 match)
|
||||
(guix build utils))
|
||||
|
||||
(define %user (or (getenv "SUDO_USER")
|
||||
(getenv "USER")))
|
||||
|
||||
(define %user-id (passwd:uid (getpwnam %user)))
|
||||
|
||||
(define %group-id (passwd:gid (getpwnam %user)))
|
||||
|
||||
(define %CA-directory
|
||||
"/etc/ssl-ca")
|
||||
|
||||
(define subject-template
|
||||
"/C=DE/ST=Berlin/L=Berlin/O=GNU Guix/OU=Cuirass/CN=~a")
|
||||
|
||||
(define CA-key
|
||||
(string-append %CA-directory "/private/ca.key"))
|
||||
(define CA-cert
|
||||
(string-append %CA-directory "/certs/ca.crt"))
|
||||
|
||||
(define* (output who file)
|
||||
(string-append (getcwd) "/" who file))
|
||||
|
||||
(define (key-file who)
|
||||
"Return the absolute file name of the key file for WHO."
|
||||
(output who ".key"))
|
||||
|
||||
(define (csr-file who)
|
||||
"Return the absolute file name of the CSR file for WHO."
|
||||
(output who ".csr"))
|
||||
|
||||
(define (client-cert-file who)
|
||||
"Return the absolute file name of the client certificate file for
|
||||
WHO."
|
||||
(output who ".crt"))
|
||||
|
||||
(define (exported-cert-file who)
|
||||
"Return the absolute file name of the pkcs12 client certificate file
|
||||
for WHO. This is the file that users should import into their
|
||||
browsers."
|
||||
(output who ".p12"))
|
||||
|
||||
(define (generate-ca!)
|
||||
"Generate a private certificate authority (CA) valid for 10 years."
|
||||
(mkdir-p (dirname CA-key))
|
||||
(mkdir-p (dirname CA-cert))
|
||||
(invoke "openssl" "req" "-newkey" "rsa" "-x509" "-days" "3650"
|
||||
"-noenc" ;no password
|
||||
"-subj" (format #false "~@?" subject-template "Cuirass CA")
|
||||
"-keyout" CA-key "-out" CA-cert))
|
||||
|
||||
(define (generate-csr! who)
|
||||
"Generate a new certificate signing request and key for WHO."
|
||||
(let ((key (key-file who))
|
||||
(csr (csr-file who)))
|
||||
(invoke "openssl" "req" "-newkey" "rsa"
|
||||
"-noenc" ;no password
|
||||
"-subj" (format #false "~@?" subject-template who)
|
||||
"-keyout" key
|
||||
"-out" csr)
|
||||
(chown key %user-id %group-id)
|
||||
(chown csr %user-id %group-id)))
|
||||
|
||||
(define* (generate-client-certificate! who #:key (expiry 365))
|
||||
"Generate a client certificate for WHO."
|
||||
(let ((cert (client-cert-file who)))
|
||||
(invoke "openssl" "x509" "-req"
|
||||
"-in" (csr-file who)
|
||||
"-CA" CA-cert
|
||||
"-CAkey" CA-key
|
||||
"-out" cert
|
||||
"-days" (number->string expiry))
|
||||
(chown cert %user-id %group-id)))
|
||||
|
||||
(define (export-p12! who)
|
||||
(let ((key (key-file who))
|
||||
(exported-cert (exported-cert-file who)))
|
||||
(invoke "openssl" "pkcs12" "-export"
|
||||
"-in" (client-cert-file who)
|
||||
"-inkey" key
|
||||
"-out" exported-cert)
|
||||
(chown key %user-id %group-id)
|
||||
(chown exported-cert %user-id %group-id)))
|
||||
|
||||
(define (main args)
|
||||
(match (command-line)
|
||||
((script)
|
||||
(set-program-arguments (list script %user))
|
||||
(apply main args))
|
||||
((script "--generate-ca")
|
||||
(generate-ca!))
|
||||
((script who)
|
||||
(generate-csr! who)
|
||||
(generate-client-certificate! who)
|
||||
(export-p12! who))
|
||||
((script . rest)
|
||||
(format (current-error-port) "usage: ~a [--generate-ca|name]~%" script))))
|
Loading…
Reference in New Issue