reguire gnupg1 on f>=31; sense the system to use gpg1 when installed
[nodemanager.git] / plugins / sliverauth.py
1 #!/usr/bin/python3 -tt
2 # vim:set ts=4 sw=4 expandtab:
3 #
4 # NodeManager plugin for creating credentials in slivers
5 # (*) empower slivers to make API calls throught hmac
6 # (*) also create a ssh key - used by the OMF resource controller 
7 #     for authenticating itself with its Experiment Controller
8 # in order to avoid spamming the DB with huge amounts of such tags,
9 # (*) slices need to have the 'enable_hmac' tag set
10 # (*) or the 'omf_control' tag set, respectively
11
12 """
13 Sliver authentication support for NodeManager.
14
15 """
16
17 import os
18 import random
19 import string
20 import tempfile
21 import socket
22
23 import logger
24 import tools
25
26 def start():
27     logger.log("sliverauth: (dummy) plugin starting up...")
28
29 def GetSlivers(data, config, plc):
30     if 'OVERRIDES' in dir(config):
31         if config.OVERRIDES.get('sliverauth') == '-1':
32             logger.log("sliverauth:  Disabled", 2)
33             return
34
35     if 'slivers' not in data:
36         logger.log_missing_data("sliverauth.GetSlivers", 'slivers')
37         return
38
39     for sliver in data['slivers']:
40         path = '/vservers/%s' % sliver['name']
41         if not os.path.exists(path):
42             # ignore all non-plc-instantiated slivers
43             instantiation = sliver.get('instantiation', '')
44             if instantiation == 'plc-instantiated':
45                 logger.log("sliverauth: plc-instantiated slice %s does not yet exist. IGNORING!" % sliver['name'])
46             continue
47
48         system_slice = False
49         for chunk in sliver['attributes']:
50             if chunk['tagname'] == "system":
51                 if chunk['value'] in (True, 1, '1') or chunk['value'].lower() == "true":
52                     system_slice = True
53
54         for chunk in sliver['attributes']:
55             if chunk['tagname']=='enable_hmac' and not system_slice:
56                 manage_hmac (plc, sliver)
57
58             if chunk['tagname']=='omf_control':
59                 manage_sshkey (plc, sliver)
60
61
62 def SetSliverTag(plc, slice, tagname, value):
63     node_id = tools.node_id()
64     slivertags=plc.GetSliceTags({"name":slice, "node_id":node_id, "tagname":tagname})
65     if len(slivertags)==0:
66         # looks like GetSlivers reports about delegated/nm-controller slices that do *not* belong to this node
67         # and this is something that AddSliceTag does not like
68         try:
69             slivertag_id=plc.AddSliceTag(slice, tagname, value, node_id)
70         except:
71             logger.log_exc ("sliverauth.SetSliverTag (probably delegated) slice=%(slice)s tag=%(tagname)s node_id=%(node_id)d"%locals())
72             pass
73     else:
74         slivertag_id=slivertags[0]['slice_tag_id']
75         plc.UpdateSliceTag(slivertag_id, value)
76
77 def find_tag (sliver, tagname):
78     for attribute in sliver['attributes']:
79         # for legacy, try the old-fashioned 'name' as well
80         name = attribute.get('tagname', attribute.get('name', ''))
81         if name == tagname:
82             return attribute['value']
83     return None
84
85 def manage_hmac (plc, sliver):
86     hmac = find_tag (sliver, 'hmac')
87
88     if not hmac:
89         # let python do its thing 
90         random.seed()
91         d = [random.choice(string.letters) for x in range(32)]
92         hmac = "".join(d)
93         SetSliverTag(plc, sliver['name'], 'hmac', hmac)
94         logger.log("sliverauth: %s: setting hmac" % sliver['name'])
95
96     path = '/vservers/%s/etc/planetlab' % sliver['name']
97     if os.path.exists(path):
98         keyfile = '%s/key' % path
99         if (tools.replace_file_with_string(keyfile, hmac, chmod=0o400)):
100             logger.log ("sliverauth: (over)wrote hmac into %s " % keyfile)
101
102 # create the key if needed and returns the key contents
103 def generate_sshkey (sliver): 
104 # initial version was storing stuff in the sliver directly
105 #    keyfile="/vservers/%s/home/%s/.ssh/id_rsa"%(sliver['name'], sliver['name'])
106 # we're now storing this in the same place as the authorized_keys, which in turn
107 # gets mounted to the user's home directory in the sliver
108     keyfile="/home/%s/.ssh/id_rsa"%(sliver['name'])
109     pubfile="%s.pub"%keyfile
110     dotssh=os.path.dirname(keyfile)
111     # create dir if needed
112     if not os.path.isdir (dotssh):
113         os.mkdir (dotssh, 0o700)
114         logger.log_call ( [ 'chown', "%s:slices"%(sliver['name']), dotssh ] )
115     if not os.path.isfile(pubfile):
116         comment="%s@%s"%(sliver['name'], socket.gethostname())
117         logger.log_call( [ 'ssh-keygen', '-t', 'rsa', '-N', '', '-f', keyfile , '-C', comment] )
118         os.chmod (keyfile, 0o400)
119         logger.log_call ( [ 'chown', "%s:slices"%(sliver['name']), keyfile, pubfile ] )
120     with open(pubfile) as f:
121         return f.read().strip()
122
123 # a sliver can get created, deleted and re-created
124 # the slice having the tag is not sufficient to skip key geneneration
125 def manage_sshkey (plc, sliver):
126     # regardless of whether the tag is there or not, we need to grab the file
127     # if it's lost b/c e.g. the sliver was destroyed we cannot save the tags content
128     ssh_key = generate_sshkey(sliver)
129     old_tag = find_tag (sliver, 'ssh_key')
130     if ssh_key != old_tag:
131         SetSliverTag(plc, sliver['name'], 'ssh_key', ssh_key)
132         logger.log ("sliverauth: %s: setting ssh_key" % sliver['name'])