summaryrefslogtreecommitdiffstats
path: root/src/lib/Bcfg2/Server/Plugins/Cfg/CfgPublicKeyCreator.py
blob: 3f2d1030b22bb4e7ad350323628118d23a2cf8cf (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
84
85
86
87
88
89
90
91
92
93
94
95
96
97
""" The CfgPublicKeyCreator invokes
:class:`Bcfg2.Server.Plugins.Cfg.CfgPrivateKeyCreator.CfgPrivateKeyCreator`
to create SSH keys on the fly. """

import os
import sys
import tempfile
import lxml.etree
from Bcfg2.Utils import Executor
from Bcfg2.Server.Plugin import StructFile, PluginExecutionError
from Bcfg2.Server.Plugins.Cfg import CfgCreator, CfgCreationError, get_cfg


class CfgPublicKeyCreator(CfgCreator, StructFile):
    """ .. currentmodule:: Bcfg2.Server.Plugins.Cfg

    The CfgPublicKeyCreator creates SSH public keys on the fly. It is
    invoked by :class:`CfgPrivateKeyCreator.CfgPrivateKeyCreator` to
    handle the creation of the public key, and can also call
    :class:`CfgPrivateKeyCreator.CfgPrivateKeyCreator` to trigger the
    creation of a keypair when a public key is created. """

    #: Different configurations for different clients/groups can be
    #: handled with Client and Group tags within pubkey.xml
    __specific__ = False

    #: Handle XML specifications of private keys
    __basenames__ = ['pubkey.xml']

    #: No text content on any tags, so encryption support disabled
    encryption = False

    def __init__(self, fname):
        CfgCreator.__init__(self, fname)
        StructFile.__init__(self, fname)
        self.cfg = get_cfg()
        self.core = self.cfg.core
        self.cmd = Executor()

    def create_data(self, entry, metadata):
        if entry.get("name").endswith(".pub"):
            privkey = entry.get("name")[:-4]
        else:
            raise CfgCreationError("Cfg: Could not determine private key for "
                                   "%s: Filename does not end in .pub" %
                                   entry.get("name"))

        privkey_entry = lxml.etree.Element("Path", name=privkey)
        try:
            self.core.Bind(privkey_entry, metadata)
        except PluginExecutionError:
            raise CfgCreationError("Cfg: Could not bind %s (private key for "
                                   "%s): %s" % (privkey, self.name,
                                                sys.exc_info()[1]))

        try:
            eset = self.cfg.entries[privkey]
            creator = eset.best_matching(metadata,
                                         eset.get_handlers(metadata,
                                                           CfgCreator))
        except KeyError:
            raise CfgCreationError("Cfg: No private key defined for %s (%s)" %
                                   (self.name, privkey))
        except PluginExecutionError:
            raise CfgCreationError("Cfg: No privkey.xml defined for %s "
                                   "(private key for %s)" % (privkey,
                                                             self.name))

        specificity = creator.get_specificity(metadata)
        fname = self.get_filename(**specificity)

        # if the private key didn't exist, then creating it may have
        # created the private key, too.  check for it first.
        if os.path.exists(fname):
            return open(fname).read()
        else:
            # generate public key from private key
            fd, privfile = tempfile.mkstemp()
            try:
                os.fdopen(fd, 'w').write(privkey_entry.text)
                cmd = ["ssh-keygen", "-y", "-f", privfile]
                self.debug_log("Cfg: Extracting SSH public key from %s: %s" %
                               (privkey, " ".join(cmd)))
                result = self.cmd.run(cmd)
                if not result.success:
                    raise CfgCreationError("Cfg: Failed to extract public key "
                                           "from %s: %s" % (privkey,
                                                            result.error))
                self.write_data(result.stdout, **specificity)
                return result.stdout
            finally:
                os.unlink(privfile)

    def handle_event(self, event):
        CfgCreator.handle_event(self, event)
        StructFile.HandleEvent(self, event)
    handle_event.__doc__ = CfgCreator.handle_event.__doc__