1 ##############################################################################
3 # Copyright (c) 2010 Vifib SARL and Contributors. All Rights Reserved.
5 # WARNING: This program as such is intended to be used by professional
6 # programmers who take the whole responsibility of assessing all potential
7 # consequences resulting from its eventual inadequacies and bugs
8 # End users who are looking for a ready-to-use solution with commercial
9 # guarantees and support are strongly adviced to contract a Free Software
12 # This program is Free Software; you can redistribute it and/or
13 # modify it under the terms of the GNU General Public License
14 # as published by the Free Software Foundation; either version 3
15 # of the License, or (at your option) any later version.
17 # This program is distributed in the hope that it will be useful,
18 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # GNU General Public License for more details.
22 # You should have received a copy of the GNU General Public License
23 # along with this program; if not, write to the Free Software
24 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
26 ##############################################################################
33 from slapos
.recipe
.librecipe
import GenericBaseRecipe
34 from slapos
.recipe
.librecipe
.inotify
import subfiles
36 # This authority only works with dropbear sshkey generator
37 def sshkeys_authority(args
):
38 requests_directory
= args
['requests']
39 keygen_binary
= args
['sshkeygen']
41 for request_filename
in subfiles(requests_directory
):
43 with
open(request_filename
) as request_file
:
44 request
= json
.load(request_file
)
46 key_type
= request
.get('type', 'rsa')
47 size
= str(request
.get('size', 2048))
49 private_key
= request
['private_key']
50 public_key
= request
['public_key']
54 if not os
.path
.exists(private_key
):
55 if os
.path
.exists(public_key
):
57 keygen_cmd
= [keygen_binary
, '-t', key_type
, '-f', private_key
,
59 # If the keygeneration return an non-zero status, it means there's a
60 # big problem. Let's exit in this case
61 subprocess
.check_call(keygen_cmd
, env
=os
.environ
.copy())
63 if not os
.path
.exists(public_key
):
64 keygen_cmd
= [keygen_binary
, '-f', private_key
, '-y']
66 keygen
= subprocess
.Popen(keygen_cmd
, stdout
=subprocess
.PIPE
,
67 stdin
=subprocess
.PIPE
,
68 stderr
=subprocess
.STDOUT
,
69 env
=os
.environ
.copy())
73 # If the keygeneration return an non-zero status, it means there's a
74 # big problem. Let's exit in this case
75 if keygen
.wait() != 0:
76 raise subprocess
.CalledProcessError("%r returned a non-zero status" % \
79 for line
in keygen
.stdout
:
81 # Don't worry, just regex to detect the ssh public key line
82 matchresult
= re
.match(r
'ssh-.*?=+', line
)
84 public_key_value
= matchresult
.group(0)
87 with
open(public_key
, 'w') as public_key_file
:
88 public_key_file
.write(public_key_value
)
92 class Recipe(GenericBaseRecipe
):
96 requests
=self
.options
['request-directory'],
97 sshkeygen
=self
.options
['keygen-binary'],
100 wrapper
= self
.createPythonScript(self
.options
['wrapper'],
101 __name__
+ '.sshkeys_authority', args
)
104 class Request(GenericBaseRecipe
):
106 def _options(self
, options
):
107 if 'name' not in options
:
108 options
['name'] = self
.name
110 keys_directory
= options
['keys-directory']
112 self
.private_key
= os
.path
.join(keys_directory
,
113 hashlib
.sha256(options
['name']).hexdigest())
114 self
.public_key
= self
.private_key
+ '.pub'
116 if os
.path
.exists(self
.public_key
):
117 with
open(self
.public_key
) as key
:
118 options
['public-key-value'] = key
.read()
120 options
['public-key-value'] = ''
123 requests_directory
= self
.options
['request-directory']
124 request_file
= os
.path
.join(requests_directory
, self
.options
['name'])
127 private_key
=self
.private_key
,
128 public_key
=self
.public_key
,
130 if 'size' in self
.options
:
131 request
.update(size
=int(self
.options
['size'], 10))
132 if 'type' in self
.options
:
133 request
.update(type=self
.options
['type'])
135 with
open(request_file
, 'w') as file_
:
136 json
.dump(request
, file_
)
138 public_key_link
, private_key_link
= (self
.options
['public-key'],
139 self
.options
['private-key'],
141 # XXX: Copy and past from certificate_authority/__init__.py:Request
142 # We should factorize that
143 for link
in [public_key_link
, private_key_link
]:
144 if os
.path
.islink(link
):
146 elif os
.path
.exists(link
):
147 raise OSError("%r should be a symbolic link." % link
)
149 os
.symlink(self
.public_key
, public_key_link
)
150 os
.symlink(self
.private_key
, private_key_link
)
153 wrapper
= self
.createPythonScript(
154 self
.options
['wrapper'],
155 'slapos.recipe.librecipe.execute.execute_wait',
156 [ [self
.options
['executable']],
157 [self
.private_key
, self
.public_key
] ])
160 return [request_file
, wrapper
, public_key_link
, private_key_link
]