From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mp1 ([2001:41d0:2:4a6f::]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) by ms11 with LMTPS id s9ckLZpPcl9MegAA0tVLHw (envelope-from ) for ; Mon, 28 Sep 2020 21:03:22 +0000 Received: from aspmx1.migadu.com ([2001:41d0:2:4a6f::]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) by mp1 with LMTPS id uNm4KJpPcl+XWwAAbx9fmQ (envelope-from ) for ; Mon, 28 Sep 2020 21:03:22 +0000 Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by aspmx1.migadu.com (Postfix) with ESMTPS id 1260C94014A for ; Mon, 28 Sep 2020 21:03:22 +0000 (UTC) Received: from localhost ([::1]:37612 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1kN0Ip-0000Na-Nn for larch@yhetil.org; Mon, 28 Sep 2020 17:03:19 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:48080) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1kN0IY-0000NM-7H for guix-patches@gnu.org; Mon, 28 Sep 2020 17:03:03 -0400 Received: from debbugs.gnu.org ([209.51.188.43]:42623) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1kN0IX-00068x-U2 for guix-patches@gnu.org; Mon, 28 Sep 2020 17:03:01 -0400 Received: from Debian-debbugs by debbugs.gnu.org with local (Exim 4.84_2) (envelope-from ) id 1kN0IX-0004bQ-Qh for guix-patches@gnu.org; Mon, 28 Sep 2020 17:03:01 -0400 X-Loop: help-debbugs@gnu.org Subject: [bug#43681] [PATCH] gnu: Add webssh service. Resent-From: Oleg Pykhalov Original-Sender: "Debbugs-submit" Resent-CC: guix-patches@gnu.org Resent-Date: Mon, 28 Sep 2020 21:03:01 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: report 43681 X-GNU-PR-Package: guix-patches X-GNU-PR-Keywords: patch To: 43681@debbugs.gnu.org Cc: Oleg Pykhalov X-Debbugs-Original-To: guix-patches@gnu.org Received: via spool by submit@debbugs.gnu.org id=B.160132694117632 (code B ref -1); Mon, 28 Sep 2020 21:03:01 +0000 Received: (at submit) by debbugs.gnu.org; 28 Sep 2020 21:02:21 +0000 Received: from localhost ([127.0.0.1]:54169 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1kN0Hp-0004aH-VB for submit@debbugs.gnu.org; Mon, 28 Sep 2020 17:02:21 -0400 Received: from lists.gnu.org ([209.51.188.17]:60420) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1kN0Hm-0004a8-AH for submit@debbugs.gnu.org; Mon, 28 Sep 2020 17:02:17 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:47906) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1kN0Hm-0000I1-3X for guix-patches@gnu.org; Mon, 28 Sep 2020 17:02:14 -0400 Received: from mail-lj1-x242.google.com ([2a00:1450:4864:20::242]:41170) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1kN0Hi-00064H-Jr for guix-patches@gnu.org; Mon, 28 Sep 2020 17:02:13 -0400 Received: by mail-lj1-x242.google.com with SMTP id y4so2179346ljk.8 for ; Mon, 28 Sep 2020 14:02:09 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:mime-version :content-transfer-encoding; bh=xLBnYedrA+7BUt9QtwwakKe3fmwJ061cHS2ke+z41qw=; b=lDp+ORbzSP0KWDW1AuvXShefQHZW0RudEkIfR3q1DU+TljhTrk163W167JFhgAH79C 4AWSK1tEnFqJkWThmJpcaHZaSfDcmkqU3fY/QlRQakrXzNviIacWGaHX5IuJiWbVPy0P PAagdgtvdik/Pf0+uck7dcK2nFonqITinoik6LUEO873opb6thLsl8W82KEuxsrXWs/E iscrsiFnOP8OK6UDYaW4zWYTZeSxtM4MnPBkxW+3DwmZSniPfrsj7LSYCPBzaS4KLDTo PoJmko1iGbb362jHQrrFmLD4VRox0+Id++jXmWECW5x6ztIYvgIVSm7BEHjUz2I89stl KOaA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:mime-version :content-transfer-encoding; bh=xLBnYedrA+7BUt9QtwwakKe3fmwJ061cHS2ke+z41qw=; b=KrlMao0UFC7xxN6osfB42VogAttDDINvOkU5Teou3vFFd4PT1BlRh4CANh8ye0td18 AR1lJ7df7SuJgUcwgw1idkbx6yQWenwyrgxlz6k6HLPvxEQmpAAqZsuDb5q9bGf0RAOw 5++t3yf1J35YCLkh776EnBi3ttKk8F1uyzIQ6XAbhTQK42i4DwCcE+fPX7goFwD+HYU7 rRia5ZVHpv8bhejkl0+cyFOjr0FzG2nJD9ACizICQCB6yiBMV2fGJawWtBVhzWnQ+aIj vOOpS8Ow2CGWfbFeRgpW40uFNsmVZkaB+Nvm2qQXLXd9kwcdVHA/4dRsU+WC+MZH8xRa Rqng== X-Gm-Message-State: AOAM531w+/YDb1NBzMtxFqPcmQpySeOGQsZJaWlomhw9bjo3WGB76Y/9 tVmJuKJUOZAy5si19H6v7mh2kZUSfww= X-Google-Smtp-Source: ABdhPJwgeA5YpZ4cSS5PYYQaA4WYGs+4ZybVT5jVLsVKs+jUz+aIgtL/IzugcFJXGFHy3bG2o3LotA== X-Received: by 2002:a2e:8805:: with SMTP id x5mr152420ljh.130.1601326928047; Mon, 28 Sep 2020 14:02:08 -0700 (PDT) Received: from localhost.localdomain (95-55-4-176.dynamic.avangarddsl.ru. [95.55.4.176]) by smtp.gmail.com with ESMTPSA id c10sm3015008lfc.4.2020.09.28.14.02.07 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 28 Sep 2020 14:02:07 -0700 (PDT) From: Oleg Pykhalov Date: Tue, 29 Sep 2020 00:01:58 +0300 Message-Id: <20200928210158.15751-1-go.wigust@gmail.com> X-Mailer: git-send-email 2.28.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Received-SPF: pass client-ip=2a00:1450:4864:20::242; envelope-from=go.wigust@gmail.com; helo=mail-lj1-x242.google.com X-detected-operating-system: by eggs.gnu.org: No matching host in p0f cache. That's all we know. X-Spam_score_int: -8 X-Spam_score: -0.9 X-Spam_bar: / X-Spam_report: (-0.9 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, FREEMAIL_FROM=0.001, NUMERIC_HTTP_ADDR=1.242, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, WEIRD_PORT=0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-Spam-Score: 0.7 (/) X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list X-Spam-Score: -2.3 (--) X-BeenThere: guix-patches@gnu.org List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: guix-patches-bounces+larch=yhetil.org@gnu.org Sender: "Guix-patches" X-Scanner: scn0 Authentication-Results: aspmx1.migadu.com; dkim=fail (body hash did not verify) header.d=gmail.com header.s=20161025 header.b=lDp+ORbz; dmarc=fail reason="SPF not aligned (relaxed)" header.from=gmail.com (policy=none); spf=pass (aspmx1.migadu.com: domain of guix-patches-bounces@gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=guix-patches-bounces@gnu.org X-Spam-Score: 0.09 X-TUID: /K7W4H/oawgv * gnu/services/ssh.scm: (): New record. (%webssh-configuration-nginx, webssh-service-type): New variables. (webssh-account, webssh-activation, webssh-shepherd-service): New procedures. * doc/guix.texi: Document this. --- doc/guix.texi | 75 +++++++++++++++++++++++++ gnu/services/ssh.scm | 131 ++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 205 insertions(+), 1 deletion(-) diff --git a/doc/guix.texi b/doc/guix.texi index 452453241c..f745b04bb9 100644 --- a/doc/guix.texi +++ b/doc/guix.texi @@ -15546,6 +15546,81 @@ may cause undefined behaviour. @end table @end deftp +@cindex WebSSH +@deffn {Scheme Variable} webssh-service-type +This is the type for the @uref{https://webssh.huashengdun.org/, WebSSH} +program that runs a web SSH client. WebSSH can be run manually from the +command-line by passing arguments to the binary @command{wssh} from the +package @code{webssh}, but it can also be run as a Guix service. This +latter use case is documented here. + +For example, to specify a service running WebSSH on loopback interface +on port @code{8888} with reject policy with a list of allowed to +connection hosts, and NGINX as a reverse-proxy to this service listening +for HTTPS connection, add this call to the operating system's +@code{services} field: + +@lisp +(service webssh-service-type + (webssh-configuration (address "127.0.0.1") + (port 8888) + (policy 'reject) + (known-hosts '("localhost ecdsa-sha2-nistp256 AAAA…" + "127.0.0.1 ecdsa-sha2-nistp256 AAAA…")))) + +(service nginx-service-type + (nginx-configuration + (server-blocks + (list + (nginx-server-configuration + (inherit %webssh-configuration-nginx) + (server-name '("webssh.example.com")) + (listen '("443 ssl")) + (ssl-certificate (letsencrypt-certificate "webssh.example.com")) + (ssl-certificate-key (letsencrypt-key "webssh.example.com")) + (locations + (cons (nginx-location-configuration + (uri "/.well-known") + (body '("root /var/www;"))) + (nginx-server-configuration-locations %webssh-configuration-nginx)))))))) +@end lisp +@end deffn + +@deftp {Data Type} webssh-configuration +Data type representing the configuration for @code{webssh-service}. + +@table @asis +@item @code{package} (default: @var{webssh}) +@code{webssh} package to use. + +@item @code{user-name} (default: @var{"webssh"}) +User name or user ID that file transfers to and from that module should take +place. + +@item @code{group-name} (default: @var{"webssh"}) +Group name or group ID that will be used when accessing the module. + +@item @code{address} (default: @var{#f}) +IP address on which @command{webssh} listens for incoming connections. + +@item @code{port} (default: @var{8888}) +TCP port on which @command{webssh} listens for incoming connections. + +@item @code{policy} (default: @var{#f}) +Connection policy. @var{reject} policy requires to specify @var{known-hosts}. + +@item @code{known-hosts} (default: @var{'()}) +List of hosts which allowed for SSH connection from @command{webssh}. + +@item @code{log-file} (default: @file{"/var/log/webssh.log"}) +Name of the file where @command{rsync} writes its log file. + +@item @code{log-level} (default: @var{#f}) +Logging level. + +@end table +@end deftp + @defvr {Scheme Variable} %facebook-host-aliases This variable contains a string for use in @file{/etc/hosts} (@pxref{Host Names,,, libc, The GNU C Library Reference Manual}). Each diff --git a/gnu/services/ssh.scm b/gnu/services/ssh.scm index ced21c0742..1891db0487 100644 --- a/gnu/services/ssh.scm +++ b/gnu/services/ssh.scm @@ -5,6 +5,7 @@ ;;; Copyright © 2017 Clément Lassieur ;;; Copyright © 2019 Ricardo Wurmus ;;; Copyright © 2020 pinoaffe +;;; Copyright © 2020 Oleg Pykhalov ;;; ;;; This file is part of GNU Guix. ;;; @@ -26,6 +27,7 @@ #:use-module (gnu packages admin) #:use-module (gnu services) #:use-module (gnu services shepherd) + #:use-module (gnu services web) #:use-module (gnu system pam) #:use-module (gnu system shadow) #:use-module (guix gexp) @@ -50,7 +52,12 @@ autossh-configuration autossh-configuration? - autossh-service-type)) + autossh-service-type + + webssh-configuration + webssh-configuration? + webssh-service-type + %webssh-configuration-nginx)) ;;; Commentary: ;;; @@ -732,4 +739,126 @@ object." autossh-service-activation))) (default-value (autossh-configuration)))) + +;;; +;;; WebSSH +;;; + +(define-record-type* + webssh-configuration make-webssh-configuration + webssh-configuration? + (package webssh-configuration-package ;package + (default webssh)) + (user-name webssh-configuration-user-name ;string + (default "webssh")) + (group-name webssh-configuration-group-name ;string + (default "webssh")) + (policy webssh-configuration-policy ;symbol + (default #f)) + (known-hosts webssh-configuration-known-hosts ;list of strings + (default #f)) + (port webssh-configuration-port ;number + (default #f)) + (address webssh-configuration-address ;string + (default #f)) + (log-file webssh-configuration-log-file ;string + (default "/var/log/webssh.log")) + (log-level webssh-configuration-log-level ;symbol + (default #f))) + +(define %webssh-configuration-nginx + (nginx-server-configuration + (listen '("80")) + (locations + (list (nginx-location-configuration + (uri "/") + (body '("proxy_pass http://127.0.0.1:8888;" + "proxy_http_version 1.1;" + "proxy_read_timeout 300;" + "proxy_set_header Upgrade $http_upgrade;" + "proxy_set_header Connection \"upgrade\";" + "proxy_set_header Host $http_host;" + "proxy_set_header X-Real-IP $remote_addr;" + "proxy_set_header X-Real-PORT $remote_port;"))))))) + +(define webssh-account + ;; Return the user accounts and user groups for CONFIG. + (match-lambda + (($ _ user-name group-name _ _ _ _ _ _) + (list (user-group + (name group-name)) + (user-account + (name user-name) + (group group-name) + (comment "webssh privilege separation user") + (home-directory (string-append "/var/run/" user-name)) + (shell #~(string-append #$shadow "/sbin/nologin"))))))) + +(define webssh-activation + ;; Return the activation GEXP for CONFIG. + (match-lambda + (($ _ user-name group-name policy known-hosts _ _ + log-file _) + (with-imported-modules '((guix build utils)) + #~(begin + (let* ((home-dir (string-append "/var/run/" #$user-name)) + (ssh-dir (string-append home-dir "/.ssh")) + (known-hosts-file (string-append ssh-dir "/known_hosts"))) + (call-with-output-file #$log-file (const #t)) + (mkdir-p ssh-dir) + (case '#$policy + ((reject) + (if '#$known-hosts + (call-with-output-file known-hosts-file + (lambda (port) + (for-each (lambda (host) (display host port) (newline port)) + '#$known-hosts))) + (display-hint (G_ "webssh: reject policy requires `known-hosts'."))))) + (for-each (lambda (file) + (chown file + (passwd:uid (getpw #$user-name)) + (group:gid (getpw #$group-name)))) + (list #$log-file ssh-dir known-hosts-file)) + (chmod ssh-dir #o700))))))) + +(define webssh-shepherd-service + (match-lambda + (($ package user-name group-name policy _ port + address log-file log-level) + (list (shepherd-service + (provision '(webssh)) + (documentation "Run webssh daemon.") + (start #~(make-forkexec-constructor + `(,(string-append #$webssh "/bin/wssh") + ,(string-append "--log-file-prefix=" #$log-file) + ,@(case '#$log-level + ((debug) '("--logging=debug")) + (else '())) + ,@(case '#$policy + ((reject) '("--policy=reject")) + (else '())) + ,@(if #$port + (list (string-append "--port=" (number->string #$port))) + '()) + ,@(if #$address + (list (string-append "--address=" #$address)) + '())) + #:user #$user-name + #:group #$group-name)) + (stop #~(make-kill-destructor))))))) + +(define webssh-service-type + (service-type + (name 'webssh) + (extensions + (list (service-extension shepherd-root-service-type + webssh-shepherd-service) + (service-extension account-service-type + webssh-account) + (service-extension activation-service-type + webssh-activation))) + (default-value (webssh-configuration)) + (description + "Run the webssh."))) + ;;; ssh.scm ends here -- 2.28.0