From patchwork Thu May 11 04:34:52 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Maxim Cournoyer X-Patchwork-Id: 49926 Return-Path: X-Original-To: patchwork@mira.cbaines.net Delivered-To: patchwork@mira.cbaines.net Received: by mira.cbaines.net (Postfix, from userid 113) id E069827BBEE; Thu, 11 May 2023 05:36:17 +0100 (BST) X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on mira.cbaines.net X-Spam-Level: X-Spam-Status: No, score=-3.7 required=5.0 tests=BAYES_00,DKIM_ADSP_CUSTOM_MED, DKIM_INVALID,DKIM_SIGNED,FREEMAIL_FROM,MAILING_LIST_MULTI, RCVD_IN_MSPIKE_H2,SPF_HELO_PASS,URIBL_BLOCKED autolearn=unavailable autolearn_force=no version=3.4.6 Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mira.cbaines.net (Postfix) with ESMTPS id 2F39D27BBED for ; Thu, 11 May 2023 05:36:15 +0100 (BST) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1pwy2B-00085o-6F; Thu, 11 May 2023 00:36:07 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1pwy29-00084g-2d for guix-patches@gnu.org; Thu, 11 May 2023 00:36:05 -0400 Received: from debbugs.gnu.org ([209.51.188.43]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1pwy28-0004sm-QF; Thu, 11 May 2023 00:36:04 -0400 Received: from Debian-debbugs by debbugs.gnu.org with local (Exim 4.84_2) (envelope-from ) id 1pwy26-0007Kk-Fz; Thu, 11 May 2023 00:36:02 -0400 X-Loop: help-debbugs@gnu.org Subject: [bug#63375] [cuirass v3] doc: Document authentication. References: <20230508160745.10144-1-maxim.cournoyer@gmail.com> In-Reply-To: <20230508160745.10144-1-maxim.cournoyer@gmail.com> Resent-From: Maxim Cournoyer Original-Sender: "Debbugs-submit" Resent-CC: rekado@elephly.net, othacehe@gnu.org, efraim@flashner.co.il, guix-patches@gnu.org Resent-Date: Thu, 11 May 2023 04:36:02 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: followup 63375 X-GNU-PR-Package: guix-patches X-GNU-PR-Keywords: To: 63375@debbugs.gnu.org Cc: Maxim Cournoyer , rekado@elephly.net, othacehe@gnu.org, efraim@flashner.co.il X-Debbugs-Original-Xcc: rekado@elephly.net, othacehe@gnu.org, efraim@flashner.co.il Received: via spool by 63375-submit@debbugs.gnu.org id=B63375.168377971728128 (code B ref 63375); Thu, 11 May 2023 04:36:02 +0000 Received: (at 63375) by debbugs.gnu.org; 11 May 2023 04:35:17 +0000 Received: from localhost ([127.0.0.1]:49425 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1pwy1M-0007JZ-Q8 for submit@debbugs.gnu.org; Thu, 11 May 2023 00:35:17 -0400 Received: from mail-qv1-f47.google.com ([209.85.219.47]:48234) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1pwy1J-0007JG-F9 for 63375@debbugs.gnu.org; Thu, 11 May 2023 00:35:15 -0400 Received: by mail-qv1-f47.google.com with SMTP id 6a1803df08f44-61b5da092dfso37195436d6.0 for <63375@debbugs.gnu.org>; Wed, 10 May 2023 21:35:13 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20221208; t=1683779707; x=1686371707; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:from:to:cc:subject:date:message-id:reply-to; bh=+pjEMaT+IW4vSywdwA0TR1nCBnHA2/P1P10FLtluEAo=; b=H4z+SBUIeWOt1aBTg9tIC7kZXa8Ub+9Djv/QAkJ3XQrk9dtaLY/QgygYyEe8ziLKwE c4Joq0Ok8yz+AA09SnKLjy+lDll0hPsxcz0yv0J7SRL2dmgjl/iEWqvhXwd/oe4ebIz3 thReRVIBvg8AB+w32uR1gwOrkx1y8cBIktj+AHpMqSQO2Bf6uexLTnsTY0SMfEzInPnZ za+Wlmf9LU2ppqfFlh9NVDMvp9gEHH6/gJg1yc4ccBXb+33uRnS/mM47pQS71LgkHkCN 0s9/maz4BONu6ISFjAWC6l0pf1n+fJYvtiDgpXICKmlW12lKEFDDVWVqW5dSin51MLdR KXlA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1683779707; x=1686371707; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to; bh=+pjEMaT+IW4vSywdwA0TR1nCBnHA2/P1P10FLtluEAo=; b=jzZZz1XVpTpRu/mhJRNiUgq/Zb+ZdLSKxAkYz8pY6K1M+GzsSGsxqdXwu4Y89lscIL FV49VWauNe6Rji6EVHCsQ+hE69NNKm8xv79r4sUaPQ71f825WTRA1Efp0W3MLfWdDN/b o8Uybrcfr1TM4fWzruIn1j9CaPPMfmFOyBYLq+fENnSf66qzS4+0OUq1YdE3fdh0o5Oq 1uHhaWniX423DoO4OT2D5locExXlO+5aC1L7qFsD0qOMUkG/yQ/2SHR/reB/PKT54AY7 dB6JYk5qY+KhJpdOYeJORpD6Cwc9iD9AGaK6GRLwlzA71q8NtOLk9MuWMvzeemSQZAG7 VDeQ== X-Gm-Message-State: AC+VfDxgJsLhcG9Ff4Yznua2EXGjEAiNRxiuFy96Bbrjlal1yfV+GKQP pAe+qvwQrcyKllCUGTi6d0jAMHfT+9M= X-Google-Smtp-Source: ACHHUZ7lDiclGBKdOvyo5aPh3uFhWv6PqCeNxV4IrSE/XDM/XTNJK9i5BwWV5k5AbaoXaCCrtbN3Dw== X-Received: by 2002:a05:6214:e6e:b0:621:64c7:235f with SMTP id jz14-20020a0562140e6e00b0062164c7235fmr1025197qvb.27.1683779707267; Wed, 10 May 2023 21:35:07 -0700 (PDT) Received: from localhost.localdomain (dsl-156-94.b2b2c.ca. [66.158.156.94]) by smtp.gmail.com with ESMTPSA id y10-20020a0c8eca000000b0061b731bf3c2sm2028718qvb.80.2023.05.10.21.35.06 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 10 May 2023 21:35:06 -0700 (PDT) From: Maxim Cournoyer Date: Thu, 11 May 2023 00:34:52 -0400 Message-Id: <20230511043452.14263-1-maxim.cournoyer@gmail.com> X-Mailer: git-send-email 2.39.2 MIME-Version: 1.0 X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list X-BeenThere: guix-patches@gnu.org List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: guix-patches-bounces+patchwork=mira.cbaines.net@gnu.org Sender: guix-patches-bounces+patchwork=mira.cbaines.net@gnu.org X-getmail-retrieved-from-mailbox: Patches * etc/new-client-cert.scm: Add script. * doc/cuirass.texi (Authentication): Document it. * Makefile.am (noinst_SCRIPTS): Register it. --- Makefile.am | 2 +- doc/cuirass.texi | 86 ++++++++++++++++++++++++++++ etc/new-client-cert.scm | 121 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 208 insertions(+), 1 deletion(-) create mode 100755 etc/new-client-cert.scm base-commit: cf4e3e4ac4a9c8d6f0d82b0a173826f15bbca7f3 diff --git a/Makefile.am b/Makefile.am index a40a76d..62b0860 100644 --- a/Makefile.am +++ b/Makefile.am @@ -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 diff --git a/doc/cuirass.texi b/doc/cuirass.texi index db46a33..728ca7f 100644 --- a/doc/cuirass.texi +++ b/doc/cuirass.texi @@ -13,6 +13,7 @@ Copyright @copyright{} 2016, 2017 Mathieu Lirzin@* Copyright @copyright{} 2017, 2020, 2021 Mathieu Othacehe@* Copyright @copyright{} 2018, 2021 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. @@ -711,6 +713,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 diff --git a/etc/new-client-cert.scm b/etc/new-client-cert.scm new file mode 100755 index 0000000..4fac772 --- /dev/null +++ b/etc/new-client-cert.scm @@ -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 +;;; Copyright © 2023 Maxim Cournoyer +;;; +;;; 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 . + +(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))))