;;; Whispers --- Stealth VPN and ssh tunnelerq ;;; Copyright © 2023 Runciter <runciter@whispers-vpn.org> ;;; ;;; This file is part of Whispers. ;;; ;;; Whispers 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. ;;; ;;; Whispers 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 Whispers. If not, see <http://www.gnu.org/licenses/>. (define-module (whispers services ssh-tunneler) #:use-module (guix gexp) #:use-module (guix records) #:use-module (gnu services) #:use-module (gnu services shepherd) #:use-module (gnu services admin) #:use-module (gnu services mcron) #:use-module (whispers services whispers) #:use-module (gnu packages base) #:use-module (gnu packages admin) #:use-module (gnu packages linux) #:use-module (gnu packages ssh) #:use-module (whispers packages doc) #:use-module (gnu home services) #:use-module (gnu home services shepherd) #:export (ssh-connection-configuration make-ssh-connection-configuration ssh-connection-configuration? this-ssh-connection-configuration ssh-connection-configuration-forwards ssh-forward-configuration this-ssh-forward-configuration ssh-forward-configuration? make-ssh-forward-configuration ssh-forward-configuration-entry-port socks-proxy-configuration this-socks-proxy-configuration socks-proxy-configuration? make-socks-proxy-configuration dynamic-forward-configuration port-forward-configuration reverse-port-forward-configuration tunnel-forward-configuration persistent-ssh-name persistent-ssh-service-type home-persistent-ssh-service-type)) (define-record-type* <ssh-connection-configuration> ssh-connection-configuration make-ssh-connection-configuration ssh-connection-configuration? this-ssh-connection-configuration ;; A file-like object. (shepherd-package ssh-connection-configuration-shepherd-package (default shepherd)) ;; A file-like object. (ssh-package ssh-connection-configuration-ssh-package (default openssh)) ;; A file-like object. (netcat-package ssh-connection-configuration-netcat-package (default netcat-openbsd)) ;; A file-like object. (sshpass-package ssh-connection-configuration-sshpass-package (default sshpass)) ;; A file-like object. (ineutils-package ssh-connection-configuration-inetutils-package (default inetutils)) ;; A file-like object. (procps-package ssh-connection-configuration-procps-package (default procps)) ;; A guix record of type <socks-proxy-configuration>. (socks-proxy-config ssh-connection-configuration-socks-proxy-config (default (socks-proxy-configuration))) ;; A boolean value. (agent? ssh-connection-configuration-agent? (default #f)) (agent-socket ssh-connection-configuration-agent-socket (default "")) ;; A boolean value. ;; A string. ;; It is thinked so that the use-agent? switch of ;; <whispers-forwardings> binds both agent? and is-rea-file in ;; opposite states, without exposing this detail to the user. (id-rsa-file? ssh-connection-configuration-id-rsa-file? (default (not (ssh-connection-configuration-agent? this-ssh-connection-configuration))) (thunked)) ;; A string. (id-rsa-file ssh-connection-configuration-id-rsa-file (default "/root/.ssh/id_rsa")) ;; A boolean value. (clear-password? ssh-connection-configuration-clear-password? (default #f)) ;; A string. (sshd-user-password ssh-connection-configuration-sshd-user-password (default "none")) ;; A string. (sshd-user ssh-connection-configuration-sshd-user (default "root")) ;; A string. (sshd-host ssh-connection-configuration-sshd-host (default "127.0.0.1")) ;; An integer. (sshd-port ssh-connection-configuration-sshd-port (default 22)) ;; A boolean value. (gateway-ports? ssh-connection-configuration-gateway-ports? (default #t)) ;; A list of strings. (known-hosts-files ssh-connection-configuration-known-hosts-files (default (list "~/.ssh/known_hosts" "~/.ssh/known_hosts2"))) ;; A string. (strict-check ssh-connection-configuration-strict-check (default "yes")) ;; An integer. (server-alive-interval ssh-connection-configuration-server-alive-interval (default 30)) ;; An integer. (server-alive-count-max ssh-connection-configuration-server-alive-count-max (default 6)) ;; A string. (name-prefix ssh-connection-configuration-name-prefix (default "ssh-forwards")) ;; A boolean value. (suffix-name? ssh-connection-configuration-suffix-name? (default #t)) ;; A list of strings. (special-options ssh-connection-configuration-special-options (default (list))) ;; A list of <ssh-forward-configuration> records. (forwards ssh-connection-configuration-forwards (default '())) ;; A boolean value. (exit-forward-failure? ssh-connection-configuration-exit-forward-failure? (default #t)) ;; An integer. (connection-attempts ssh-connection-configuration-connection-attempts (default 1)) ;; A boolean value. (local-command? ssh-connection-configuration-local-command? (default (ssh-connection-configuration-pid-file? this-ssh-connection-configuration)) (thunked)) ;; A list of strings (extra-local-commands ssh-connection-configuration-extra-local-commands (default '())) ;; A boolean value. (require-networking? ssh-connection-configuration-require-networking? (default #t)) ;; A list of symbols. (extra-requires ssh-connection-configuration-extra-requires (default '())) ;; A boolean value. (elogind? ssh-connection-configuration-elogind? (default #f)) ;; A boolean value. (lieutenant? ssh-connection-configuration-lieutenant? (default #f)) ;; A string. (lieutenant-path ssh-connection-configuration-lieutenant-path (default "")) ;; A boolean value. (pid-file? ssh-connection-configuration-pid-file? (default #t)) ;; A boolean value. (pid-folder-override? ssh-connection-configuration-pid-folder-override? (default #f)) ;; A string. (pid-folder-override ssh-connection-configuration-pid-folder-override (default "/var/run")) ;; A boolean value. (timeout-override? ssh-connection-configuration-timeout-override? (default #f)) ;; An integer. (timeout-override ssh-connection-configuration-timeout-override (default 5)) ;; A boolean value. (dedicated-log-file? ssh-connection-configuration-dedicated-log-file? (default #f)) ;; A boolean value. (log-rotate? ssh-connection-configuration-log-rotate? (default #f)) ;; A boolean value. (log-folder-override? ssh-connection-configuration-log-folder-override? (default #f)) ;; A string. (log-folder-override ssh-connection-configuration-log-folder-override (default "/var/run")) ;; An integer between 0 and 3, both included. (verbosity ssh-connection-configuration-verbosity (default 0)) ;; A boolean value. (command? ssh-connection-configuration-command? (default #f)) ;; A string. (command ssh-connection-configuration-command (default '())) ;; A quoted cron job time specification. (resurrect-time-spec ssh-connection-configuration-resurrect-time-spec (default ''(next-minute '(47)))) ;; A boolean value. (flat-resurrect? ssh-connection-configuration-flat-resurrect? (default #f)) ;; A quoted cron job time specification. (force-resurrect-time-spec ssh-connection-configuration-force-resurrect-time-spec (default ''(next-hour '(3)))) ;; A boolean value. (flat-force-resurrect? ssh-connection-configuration-flat-force-resurrect? (default #f)) ;; A boolean value. (%cron-resurrect? ssh-connection-configuration-cron-resurrect? (default #f)) ;; A boolean value. (%cron-force-resurrect? ssh-connection-configuration-cron-force-resurrect? (default #f)) ;; A boolean value. (%auto-start? ssh-connection-configuration-auto-start? (default #f))) (define-record-type* <ssh-forward-configuration> ssh-forward-configuration make-ssh-forward-configuration ssh-forward-configuration? this-ssh-forward-configuration ;; A symbol which can be 'dynamic, 'port, 'reverse-port or 'tunnel (forward-type ssh-forward-configuration-forward-type (default 'dynamic)) ;; A symbol which can be 'preset or 'any when the 'forward-type field ;; is 'tunnel, and which can be 'port or 'socket otherwise. It is ;; ignored when the 'forward-type field is 'dynamic. (entry-type ssh-forward-configuration-entry-type (default 'port)) ;; A symbol which can be 'preset or 'any when the 'forward-type field ;; is 'tunnel, and which can be 'port or 'socket otherwise. It is ;; ignored when the 'forward-type field evaluates to 'dynamic. (exit-type ssh-forward-configuration-exit-type (default 'port)) ;; An integer (entry-port ssh-forward-configuration-entry-port (default 8971)) ;; An integer (exit-port ssh-forward-configuration-exit-port (default 22)) ;; A string (entry-socket ssh-forward-configuration-entry-socket (default "")) ;; A string (exit-socket ssh-forward-configuration-exit-socket (default "")) ;; A string (forward-host ssh-forward-configuration-exit-host (default "127.0.0.1")) ;; An integer (entry-tun ssh-forward-configuration-entry-tun (default 0)) ;; An integer (exit-tun ssh-forward-configuration-exit-tun (default 0))) (define-record-type* <socks-proxy-configuration> socks-proxy-configuration make-socks-proxy-configuration socks-proxy-configuration? this-socks-proxy-configuration ;; A boolean value (use-proxy? socks-proxy-configuration-use-proxy? (default #f)) ;; A boolean value (extend? socks-proxy-configuration-extend? (default (socks-proxy-configuration-use-proxy? this-socks-proxy-configuration)) (thunked)) ;; An integer (port socks-proxy-configuration-port (default (if (socks-proxy-configuration-extend? this-socks-proxy-configuration) (ssh-forward-configuration-entry-port (car (ssh-connection-configuration-forwards (socks-proxy-configuration-dynamic-forward this-socks-proxy-configuration)))) 8971)) (thunked)) ;; #f, or a guix record returned by a call to ;; (ssh-connection-configuration ;; (forwards (list (dynamic-forward-configuration ...))) ;; ...) (dynamic-forward socks-proxy-configuration-dynamic-forward (default #f))) (define-syntax dynamic-forward-configuration (syntax-rules () ((_ fields ...) (ssh-forward-configuration (inherit (ssh-forward-configuration)) fields ...)))) (define-syntax port-forward-configuration (syntax-rules () ((_ fields ...) (ssh-forward-configuration (inherit (ssh-forward-configuration (forward-type 'port) (entry-port 6947))) fields ...)))) (define-syntax reverse-port-forward-configuration (syntax-rules () ((_ fields ...) (ssh-forward-configuration (inherit (ssh-forward-configuration (forward-type 'reverse-port) (entry-port 6283))) fields ...)))) (define-syntax tunnel-forward-configuration (syntax-rules () ((_ fields ...) (ssh-forward-configuration (inherit (ssh-forward-configuration (forward-type 'tunnel) (entry-type 'any) (exit-type 'any))) fields ...)))) (define (persistent-ssh-socks-port config) "Returns an integer defining the localhost port that a persistent ssh connection can use to establish itself through a socks proxy, configurable by CONFIG, a record of the <ssh-connection-configuration> type." (socks-proxy-configuration-port (ssh-connection-configuration-socks-proxy-config config))) (define (persistent-ssh-forward-stance forward-conf) "Returns a string defining one of the forwarding stances of a persistent ssh connection, configurable by FORWARD-CONF, a record of the <ssh-forward-configuration> type." (let* ((forward-type (ssh-forward-configuration-forward-type forward-conf)) (entry-type (ssh-forward-configuration-entry-type forward-conf)) (exit-type (ssh-forward-configuration-exit-type forward-conf)) (entry-port (ssh-forward-configuration-entry-port forward-conf)) (entry-port-str (number->string entry-port)) (exit-port (ssh-forward-configuration-exit-port forward-conf)) (exit-port-str (number->string exit-port)) (entry-socket (ssh-forward-configuration-entry-socket forward-conf)) (exit-socket (ssh-forward-configuration-exit-socket forward-conf)) (exit-host (ssh-forward-configuration-exit-host forward-conf)) (entry-tun (ssh-forward-configuration-entry-tun forward-conf)) (entry-tun-str (number->string entry-tun)) (exit-tun (ssh-forward-configuration-exit-tun forward-conf)) (exit-tun-str (number->string exit-tun))) (cond ((equal? forward-type 'dynamic) (number->string entry-port)) ((or (equal? forward-type 'port) (equal? forward-type 'reverse-port)) (cond ((equal? entry-type 'port) (string-append entry-port-str ":" exit-host ":" exit-port-str)) ((equal? entry-type 'socket) (string-append entry-socket ":" exit-socket)) (#t #f))) ((equal? forward-type 'tunnel) (string-append (cond ((equal? entry-type 'preset) entry-tun-str) ((equal? entry-type 'any) "any") (#t #f)) ":" (cond ((equal? exit-type 'preset) exit-tun-str) ((equal? exit-type 'any) "any") (#t #f)))) (#t #f)))) (define (persistent-ssh-forward-switch forward-conf) "Returns a string defining one of the forwarding switches of a persistent ssh connection, configurable by FORWARD-CONF, a record of the <ssh-forward-configuration> type." (let ((forward-type (ssh-forward-configuration-forward-type forward-conf))) (cond ((equal? forward-type 'dynamic) "-D") ((equal? forward-type 'port) "-L") ((equal? forward-type 'reverse-port) "-R") ((equal? forward-type 'tunnel) "-w") (#t #f)))) (define (persistent-ssh-forward forward-conf) "Returns a list of 2 strings containing the switch and stance of one of the forwardings of a persistent ssh connection, configurable by FORWARD-CONF, a record of the <ssh-forward-configuration> type." (list (persistent-ssh-forward-switch forward-conf) (persistent-ssh-forward-stance forward-conf))) (define (persistent-ssh-name-suffix config) "Returns a string defining the suffix part of the shepherd service provision of the shepherd service daemonizing a persistent ssh connection, configurable by CONFIG, a record of the <ssh-connection-configuration> type." (let* ((forwards (ssh-connection-configuration-forwards config)) (typer ssh-forward-configuration-forward-type) (typer-str (lambda (forward) (symbol->string (typer forward)))) (stancer persistent-ssh-forward-stance) (socks-rec (ssh-connection-configuration-socks-proxy-config config)) (use-socks? (socks-proxy-configuration-use-proxy? socks-rec)) (socks-port (socks-proxy-configuration-port socks-rec)) (socks-port-str (number->string socks-port)) (flat? (ssh-connection-configuration-flat-resurrect? config))) (string-append "_" (string-join (map (lambda (forward) (string-append (typer-str forward) "@" (stancer forward))) forwards) "_") (if use-socks? (string-append "_proxy-port_" socks-port-str) "")))) (define (persistent-ssh-name config) "Returns a symbol defining the shpherd service provision of the shepherd service daemonizing a persistent ssh connection, configurable by CONFIG, a record of the <ssh-connection-configuration> type." (string->symbol (string-append (ssh-connection-configuration-name-prefix config) (if (ssh-connection-configuration-suffix-name? config) (persistent-ssh-name-suffix config) "")))) (define (persistent-ssh-pid-folder config) "Returns a string defining the path to the folder in which the pid file of a persistent ssh connection service is stored by default, configurable by CONFIG, a record of the <ssh-connection-configuration> type." (cond ((ssh-connection-configuration-pid-folder-override? config) (ssh-connection-configuration-pid-folder-override config)) ((ssh-connection-configuration-elogind? config) (string-append "/run/user/" (number->string (getuid)))) (else "/var/run"))) (define (persistent-ssh-pid-file-path config) "Returns a string defining the path to the pid file of a persistent ssh connection service, configurable by CONFIG, configurable by CONFIG, a record of the <ssh-connection-configuration> type." (string-append (persistent-ssh-pid-folder config) "/" (symbol->string (persistent-ssh-name config)) ".pid")) (define (persistent-ssh-log-folder config) "Returns a string defining the path to the folder in which the log file of a persistent ssh connection service is stored by default, configurable by CONFIG, a record of the <ssh-connection-configuration> type." (cond ((ssh-connection-configuration-log-folder-override? config) (ssh-connection-configuration-log-folder-override config)) ((ssh-connection-configuration-elogind? config) (string-append "/run/user/" (number->string (getuid)))) (else "/var/run"))) (define (persistent-ssh-log-file-path config) "Returns a string defining the path to the log file of a persistent ssh connection service, configurable by CONFIG, a record of the <ssh-connection-configuration> type." (string-append (persistent-ssh-log-folder config) "/" (symbol->string (persistent-ssh-name config)) ".log")) (define (persistent-ssh-local-command config) "Returns a string defining command executed locally after the forwards of a persistent ssh connection service have been succesfully created, configurable by CONFIG, a record of the <ssh-connection-configuration> type." (let ((procps-package (ssh-connection-configuration-procps-package config)) (clear-password? (ssh-connection-configuration-clear-password? config)) (extra-local-commands (ssh-connection-configuration-extra-local-commands config))) (append (list (file-append procps-package "/bin/ps") " --no-header --pid $PPID -o " (if clear-password? "ppid" "pid") " > " (persistent-ssh-pid-file-path config)) (map (lambda (command) (string-append " && " command)) extra-local-commands)))) (define (persistent-ssh-requires config) "Returns a list of symbols defining the other services required as dependencies by the shepherd service of a persistent ssh connection, configurable by CONFIG, a record of the <ssh-connection-configuration> type." (let* ((req-net? (ssh-connection-configuration-require-networking? config)) (extra-reqs (ssh-connection-configuration-extra-requires config)) (socks-rec (ssh-connection-configuration-socks-proxy-config config)) (inferior? (socks-proxy-configuration-extend? socks-rec)) (inferior-cnf (socks-proxy-configuration-dynamic-forward socks-rec)) (use-socks? (socks-proxy-configuration-use-proxy? socks-rec)) (socks-port (socks-proxy-configuration-port socks-rec)) (socks-port-str (number->string socks-port)) (flat? (ssh-connection-configuration-flat-force-resurrect? config))) (append (if req-net? (list 'networking) (list)) extra-reqs (if inferior? (list (persistent-ssh-name inferior-cnf)) (if use-socks? (list (string->symbol ;; FIXME: this just assumes a possible ;; default name, not always true and not ;; even the only possible default. (string-append "ssh-forwards_dynamic@" (number->string socks-port)))) (list)))))) (define (persistent-ssh-timeout config) "Returns an integer setting the pid file timout of the shepherd service daemonizing a persistent ssh connection, configurable by CONFIG, a record of the <ssh-connection-configuration> type." (if (ssh-connection-configuration-timeout-override? config) (ssh-connection-configuration-timeout-override config) #~(+ #$(ssh-connection-configuration-connection-attempts config) (default-pid-file-timeout)))) (define (persistent-ssh-constructor-gexp config) "Returns G-exp to a procedure starting the ssh client process of a persistent ssh connection, configurable by CONFIG, a record of the <ssh-connection-configuration> type." (let* ((sshpass-pkg (ssh-connection-configuration-sshpass-package config)) (password (ssh-connection-configuration-sshd-user-password config)) (ssh-pkg (ssh-connection-configuration-ssh-package config)) (netcat-pkg (ssh-connection-configuration-netcat-package config)) (verbosity (ssh-connection-configuration-verbosity config)) (eff? (ssh-connection-configuration-exit-forward-failure? config)) (tries (ssh-connection-configuration-connection-attempts config)) (tries-str (number->string tries)) (local-com? (ssh-connection-configuration-local-command? config)) (local-com (persistent-ssh-local-command config)) (gateway? (ssh-connection-configuration-gateway-ports? config)) (socks-rec (ssh-connection-configuration-socks-proxy-config config)) (use-socks? (socks-proxy-configuration-use-proxy? socks-rec)) (socks-port (socks-proxy-configuration-port socks-rec)) (socks-port-str (number->string socks-port)) (command? (ssh-connection-configuration-command? config)) (command (ssh-connection-configuration-command config)) (forwards (ssh-connection-configuration-forwards config)) (sshd-port (ssh-connection-configuration-sshd-port config)) (sshd-port-str (number->string sshd-port)) (agent? (ssh-connection-configuration-agent? config)) (agent-socket (ssh-connection-configuration-agent-socket config)) (id-rsa? (ssh-connection-configuration-id-rsa-file? config)) (id-rsa (ssh-connection-configuration-id-rsa-file config)) (sshd-user (ssh-connection-configuration-sshd-user config)) (sshd-host (ssh-connection-configuration-sshd-host config)) (dlf? (ssh-connection-configuration-dedicated-log-file? config)) (log-file (persistent-ssh-log-file-path config)) (pid-file? (ssh-connection-configuration-pid-file? config)) (pid-file (persistent-ssh-pid-file-path config)) (timeout (persistent-ssh-timeout config)) (special-opt (ssh-connection-configuration-special-options config)) (strict-check (ssh-connection-configuration-strict-check config)) (kh-files (ssh-connection-configuration-known-hosts-files config)) (sa-int (ssh-connection-configuration-server-alive-interval config)) (acount-max (ssh-connection-configuration-server-alive-count-max config))) #~(make-forkexec-constructor (append #$(if (ssh-connection-configuration-clear-password? config) #~(list #$(file-append sshpass-pkg "/bin/sshpass") "-p" #$password) #~(list)) (list #$(file-append ssh-pkg "/bin/ssh") "-o" "TCPKeepAlive=no" "-o" (string-append "ServerAliveInterval=" #$(number->string sa-int)) "-o" (string-append "ServerAliveCountMax=" #$(number->string acount-max)) "-o" (string-append "UserKnownHostsFile=" #$(string-join kh-files)) "-o" (string-append "StrictHostKeyChecking=" #$strict-check) ;; "-o" ;; "Tunnel=point-to-point" "-o" (string-append "ExitOnForwardFailure=" #$(if eff? "yes" "no")) "-o" (string-append "ConnectionAttempts=" #$tries-str)) #$(if local-com? #~(list "-o" "PermitLocalCommand=yes" "-o" (apply string-append (append (list "LocalCommand=") #$(append (list 'list) local-com)))) #~(list)) #$(if gateway? #~(list "-o" "GatewayPorts=yes") #~(list)) #$(if use-socks? #~(list "-o" (string-append "ProxyCommand=" #$netcat-pkg "/bin/nc" " -X 5 -x localhost:" #$socks-port-str " %h %p")) #~(list)) #$(append (list 'list) special-opt) (list "-p" #$sshd-port-str) #$(if id-rsa? #~(list "-i" #$id-rsa) #~(list)) #$(cond ((= verbosity 0) #~(list)) ((= verbosity 1) #~(list "-v")) ((= verbosity 2) #~(list "-v" "-v")) ((= verbosity 3) #~(list "-v" "-v" "-v")) (#t #f)) #$(if command? #~(list) #~(list "-N")) #$(append (list 'list) (apply append (map persistent-ssh-forward forwards))) (list (string-append #$sshd-user "@" #$sshd-host)) #$(if command? #~(list #$command) #~(list))) #:log-file #$(if dlf? log-file #f) #:pid-file #$(if pid-file? pid-file #f) #:pid-file-timeout #$timeout #:environment-variables '#$(if agent? (list (string-append "SSH_AUTH_SOCK=" agent-socket)) (list (string-append "SSH_AUTH_SOCK=" "/dev/null")))))) (define (persistent-ssh-resurrect-action config) "Returns a G-exp to a procedure used as the procedure of the 'resurrect action of the shepherd service supporting a persistent ssh connection , configurable by CONFIG, a record of the <ssh-connection-configuration> type." (let* ((name (persistent-ssh-name config)) (socks-rec (ssh-connection-configuration-socks-proxy-config config)) (inferior? (socks-proxy-configuration-extend? socks-rec)) (inferior-cnf (socks-proxy-configuration-dynamic-forward socks-rec)) (use-socks? (socks-proxy-configuration-use-proxy? socks-rec)) (socks-port (socks-proxy-configuration-port socks-rec)) (socks-port-str (number->string socks-port)) (flat? (ssh-connection-configuration-flat-resurrect? config))) #~(lambda (running) (unless (service-running? (lookup-service '#$name)) (perform-service-action (lookup-service '#$name) 'enable) (unless (or #$flat? (and (not #$inferior?) (not #$use-socks?))) (let ((inferior-name '#$(if inferior? (persistent-ssh-name inferior-cnf) (if use-socks? (string->symbol ;; FIXME: this just assumes a possible ;; default name, not always true and not ;; even the only possible default. (string-append "ssh-forwards_dynamic@" socks-port-str)) 'not-a-service)))) (perform-service-action (lookup-service inferior-name) 'resurrect))) (start-service (lookup-service '#$name))) #t))) (define (persistent-ssh-force-resurrect-action config) "Returns a G-exp to a procedure used as the procedure of the 'force-resurrect action of the shepherd service supporting a persistent ssh connection , configurable by CONFIG, a record of the <ssh-connection-configuration> type." (let* ((name (persistent-ssh-name config)) (socks-rec (ssh-connection-configuration-socks-proxy-config config)) (inferior? (socks-proxy-configuration-extend? socks-rec)) (inferior-cnf (socks-proxy-configuration-dynamic-forward socks-rec)) (use-socks? (socks-proxy-configuration-use-proxy? socks-rec)) (socks-port (socks-proxy-configuration-port socks-rec)) (socks-port-str (number->string socks-port)) (flat? (ssh-connection-configuration-flat-force-resurrect? config))) #~(lambda (running) (perform-service-action (lookup-service '#$name) 'enable) (stop-service (lookup-service '#$name)) (unless (or #$flat? (and (not #$inferior?) (not #$use-socks?))) (let ((inferior-name '#$(if inferior? (persistent-ssh-name inferior-cnf) (if use-socks? (string->symbol ;; FIXME: this just assumes a possible ;; default name, not always true and not ;; even the only possible default. (string-append "ssh-forwards_dynamic@" socks-port-str)) 'not-a-service)))) (perform-service-action (lookup-service inferior-name) 'force-resurrect))) (start-service (lookup-service '#$name)) #t))) (define (persistent-ssh-shepherd-services config) "Returns a list of shepherd services handling a ssh client daemon connection, configured by CONFIG, a record of the <ssh-connection-configuration> type." (let* ((name (persistent-ssh-name config)) (socks-rec (ssh-connection-configuration-socks-proxy-config config)) (inferior? (socks-proxy-configuration-extend? socks-rec)) (inferior-cnf (socks-proxy-configuration-dynamic-forward socks-rec)) (use-socks? (socks-proxy-configuration-use-proxy? socks-rec)) (socks-port (socks-proxy-configuration-port socks-rec)) (socks-port-str (number->string socks-port)) (reqs (persistent-ssh-requires config)) (constructor-gexp (persistent-ssh-constructor-gexp config)) (res-gexp (persistent-ssh-resurrect-action config)) (force-res-gexp (persistent-ssh-force-resurrect-action config)) (auto-start? (ssh-connection-configuration-auto-start? config))) (append (if inferior? (persistent-ssh-shepherd-services inferior-cnf) (list)) (list (shepherd-service (documentation "Persistent ssh client connection") (provision `(,name)) (requirement reqs) (start constructor-gexp) (stop #~(make-kill-destructor)) (actions (list (shepherd-action (name 'resurrect) (documentation "Resurrect this connection and its inferiors-proxies if they are stopped or disabled by the Shepherd.") (procedure res-gexp)) (shepherd-action (name 'force-resurrect) (documentation "Enable, stop and restart this connection and its inferior-proxies , regardless of their current status.") (procedure force-res-gexp)))) (auto-start? auto-start?)))))) (define (persistent-ssh-cron-jobs config) "Returns a list of cron job specifications to extend the mcron service with scheduled resurrection actions on the persistent ssh connection port forwards configured by CONFIG, a record of the <ssh-connection-configuration> type." (let* ((service-name-str (symbol->string (persistent-ssh-name config))) (lieutenant? (ssh-connection-configuration-lieutenant? config)) (lieutenant-path (ssh-connection-configuration-lieutenant-path config)) (lieutenant-socket (lieutenant-path->socket-file-path lieutenant-path)) (shepherd-pkg (ssh-connection-configuration-shepherd-package config)) (cron-resurrect? (ssh-connection-configuration-cron-resurrect? config)) (resurrect-time-spec (ssh-connection-configuration-resurrect-time-spec config)) (cron-force-resurrect? (ssh-connection-configuration-cron-force-resurrect? config)) (force-resurrect-time-spec (ssh-connection-configuration-force-resurrect-time-spec config))) (append (if cron-resurrect? (list #~(job #$resurrect-time-spec (lambda () (apply execl (append (list (string-append #$shepherd-pkg "/bin/herd") "herd") (if #$lieutenant? (list "-s" #$lieutenant-socket) (list)) (list "resurrect" #$service-name-str)))) (string-append "resurrect " #$service-name-str))) (list)) (if cron-force-resurrect? (list #~(job #$force-resurrect-time-spec (lambda () (apply execl (append (list (string-append #$shepherd-pkg "/bin/herd") "herd") (if #$lieutenant? (list "-s" #$lieutenant-socket) (list)) (list "force-resurrect" #$service-name-str)))) (string-append "force-resurrect " #$service-name-str))) (list))))) (define (persistent-ssh-log-rotation config) "Returns a list of log-rotation records specifying how to rotate the logs of a persistent ssh connection configurable by CONFIG, a record of the <ssh-connection-configuration> type." (if (and (ssh-connection-configuration-dedicated-log-file? config) (ssh-connection-configuration-log-rotate? config)) (list (log-rotation (frequency 'daily) (files `(,(persistent-ssh-log-file-path config))))) (list))) (define persistent-ssh-service-type (service-type (name 'persistent-ssh) (description "Persistent ssh connection service") (extensions (list (service-extension shepherd-root-service-type persistent-ssh-shepherd-services) (service-extension mcron-service-type persistent-ssh-cron-jobs) (service-extension rottlog-service-type persistent-ssh-log-rotation) (service-extension profile-service-type (lambda (config) (list ssh-tunneler-doc (ssh-connection-configuration-ssh-package config) (ssh-connection-configuration-netcat-package config) (ssh-connection-configuration-sshpass-package config) (ssh-connection-configuration-procps-package config) (ssh-connection-configuration-inetutils-package config) ssh-tunneler-tests))))) (default-value (ssh-connection-configuration)))) (define home-persistent-ssh-service-type (service-type (name 'persistent-ssh) (description "Persistent ssh connection normal user service") (extensions (list (service-extension home-shepherd-service-type persistent-ssh-shepherd-services) (service-extension home-profile-service-type (lambda (config) (list ssh-tunneler-doc (ssh-connection-configuration-ssh-package config) (ssh-connection-configuration-netcat-package config) (ssh-connection-configuration-sshpass-package config) (ssh-connection-configuration-procps-package config) (ssh-connection-configuration-inetutils-package config)) ssh-tunneler-tests)))) (default-value (ssh-connection-configuration))))