aboutsummaryrefslogtreecommitdiff
path: root/src/property/ssh.lisp
blob: df014c3c9035ddf68a1b0717cddec5d897cc1eff (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
;;; Consfigurator -- Lisp declarative configuration management system

;;; Copyright (C) 2021  Sean Whitton <spwhitton@spwhitton.name>

;;; This file 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, or (at your option)
;;; any later version.

;;; This file 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 this program.  If not, see <http://www.gnu.org/licenses/>.

(in-package :consfigurator.property.ssh)
(named-readtables:in-readtable :consfigurator)

(defprop authorized-keys :posix (&rest keys)
  "Permits using KEYS to SSH in as the current user."
  (:desc (declare (ignore keys))
         (strcat (get-connattr :remote-user) " has authorized_keys"))
  (:apply
   (file:directory-exists ".ssh")
   (apply #'file:contains-lines ".ssh/authorized_keys" keys))
  (:unapply
   (apply #'file:lacks-lines ".ssh/authorized_keys" keys)))

(defprop %update-known-hosts :posix (file host &key short-hostname)
  (:apply
   (file:map-file-lines
    file
    (lambda (lines)
      (loop with (identifier . keys)
              = (sshd:get-host-public-keys host :short-hostname short-hostname)
            for line in lines
            for index = (position #\Space line)
            for line-identifier = (subseq line 0 index)
            and line-key = (subseq line (1+ index))
            when (or (not (string= line-identifier identifier))
                     (member line-key keys :test #'string=))
              collect line into accum
              and do (deletef keys line-key :test #'string=)
            finally
               (return
                 (nconc accum
                        (loop for key in keys
                              collect (format nil "~A ~A" identifier key))))))))
  (:unapply
   (destructuring-bind (identifier . keys)
       (sshd:get-host-public-keys host :short-hostname short-hostname)
     (file:lacks-lines file
                       (loop for key in keys
                             collect (format nil "~A ~A" identifier key))))))

(defproplist known-host :posix (host &key short-hostname)
  "Ensures that the SSH host keys of HOST are stored in ~/.ssh/known_hosts.
If SHORT-HOSTNAME, include the part of HOST's hostname before the first dot as
one of the hostnames identifying HOST.  Removes any other host keys
identifying HOST, to simplify refreshing keys."
  (:desc #?"${(get-hostname host)} is known host to ssh client")
  (file:directory-exists ".ssh")
  (%update-known-hosts ".ssh/known_hosts" host :short-hostname short-hostname))

(defproplist globally-known-host :posix (host &key short-hostname)
  "Ensures that SSH host keys of HOST are stored in /etc/ssh/ssh_known_hosts.
If SHORT-HOSTNAME, include the part of HOST's hostname before the first dot as
one of the hostnames identifying HOST.  Removes any other host keys
identifying HOST, to simplify refreshing keys."
  (:desc #?"${(get-hostname host)} is globally known host to ssh client")
  (%update-known-hosts
   "/etc/ssh/ssh_known_hosts" host :short-hostname short-hostname))

(defproplist parent-is-globally-known-host :posix (&key short-hostname)
  "Ensures that the SSH host keys of the parent host are stored in
/etc/ssh/ssh_known_hosts; SHORT-HOSTNAME is as for SSH:GLOBALLY-KNOWN-HOST."
  (:desc "Parent host is globally known host to ssh client")
  (%update-known-hosts
   "/etc/ssh/ssh_known_hosts" (make-host :hostattrs
                                         (get-hostattrs :parent-hostattrs))
   :short-hostname short-hostname))