add some basic logging output to certmaster
[certmaster.git] / certmaster / minion / utils.py
1 """
2 Copyright 2007, Red Hat, Inc
3 see AUTHORS
4
5 This software may be freely redistributed under the terms of the GNU
6 general public license.
7
8 You should have received a copy of the GNU General Public License
9 along with this program; if not, write to the Free Software
10 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
11 """
12
13 import os
14 import socket
15 import string
16 import sys
17 import time
18 import traceback
19 import xmlrpclib
20 import glob
21 import traceback
22
23 import codes
24 from func import certs
25 from func.config import read_config
26 from func.commonconfig import FuncdConfig
27 from func import logger
28
29 # "localhost" is a lame hostname to use for a key, so try to get
30 # a more meaningful hostname. We do this by connecting to the certmaster
31 # and seeing what interface/ip it uses to make that connection, and looking
32 # up the hostname for that.
33 def get_hostname():
34
35 # FIXME: this code ignores http proxies (which granted, we don't
36 # support elsewhere either. It also hardcodes the port number
37 # for the certmaster for now
38 hostname = None
39 hostname = socket.gethostname()
40 try:
41 ip = socket.gethostbyname(hostname)
42 except:
43 return hostname
44 if ip != "127.0.0.1":
45 return hostname
46
47
48 config_file = '/etc/func/minion.conf'
49 config = read_config(config_file, FuncdConfig)
50
51 server = config.certmaster
52 port = 51235
53
54 try:
55 s = socket.socket()
56 s.settimeout(5)
57 s.connect((server, port))
58 (intf, port) = s.getsockname()
59 hostname = socket.gethostbyaddr(intf)[0]
60 s.close()
61 except:
62 s.close()
63 raise
64
65 return hostname
66
67
68
69 def create_minion_keys():
70 config_file = '/etc/func/minion.conf'
71 config = read_config(config_file, FuncdConfig)
72 cert_dir = config.cert_dir
73 master_uri = 'http://%s:51235/' % config.certmaster
74 hn = get_hostname()
75
76 if hn is None:
77 raise codes.FuncException("Could not determine a hostname other than localhost")
78
79 key_file = '%s/%s.pem' % (cert_dir, hn)
80 csr_file = '%s/%s.csr' % (cert_dir, hn)
81 cert_file = '%s/%s.cert' % (cert_dir, hn)
82 ca_cert_file = '%s/ca.cert' % cert_dir
83
84
85 if os.path.exists(cert_file) and os.path.exists(ca_cert_file):
86 return
87
88 keypair = None
89 try:
90 if not os.path.exists(cert_dir):
91 os.makedirs(cert_dir)
92 if not os.path.exists(key_file):
93 keypair = certs.make_keypair(dest=key_file)
94 if not os.path.exists(csr_file):
95 if not keypair:
96 keypair = certs.retrieve_key_from_file(key_file)
97 csr = certs.make_csr(keypair, dest=csr_file)
98 except Exception, e:
99 traceback.print_exc()
100 raise codes.FuncException, "Could not create local keypair or csr for minion funcd session"
101
102 result = False
103 log = logger.Logger().logger
104 while not result:
105 try:
106 log.debug("submitting CSR to certmaster %s" % master_uri)
107 result, cert_string, ca_cert_string = submit_csr_to_master(csr_file, master_uri)
108 except socket.gaierror, e:
109 raise codes.FuncException, "Could not locate certmaster at %s" % master_uri
110
111 # logging here would be nice
112 if not result:
113 log.warning("no response from certmaster %s, sleeping 10 seconds" % master_uri)
114 time.sleep(10)
115
116
117 if result:
118 log.debug("received certificate from certmaster %s, storing" % master_uri)
119 cert_fd = os.open(cert_file, os.O_RDWR|os.O_CREAT, 0644)
120 os.write(cert_fd, cert_string)
121 os.close(cert_fd)
122
123 ca_cert_fd = os.open(ca_cert_file, os.O_RDWR|os.O_CREAT, 0644)
124 os.write(ca_cert_fd, ca_cert_string)
125 os.close(ca_cert_fd)
126
127 def submit_csr_to_master(csr_file, master_uri):
128 """"
129 gets us our cert back from the certmaster.wait_for_cert() method
130 takes csr_file as path location and master_uri
131 returns Bool, str(cert), str(ca_cert)
132 """
133
134 fo = open(csr_file)
135 csr = fo.read()
136 s = xmlrpclib.ServerProxy(master_uri)
137
138 return s.wait_for_cert(csr)
139
140
141 # this is kind of handy, so keep it around for now
142 # but we really need to fix out server side logging and error
143 # reporting so we don't need it
144 def trace_me():
145 x = traceback.extract_stack()
146 bar = string.join(traceback.format_list(x))
147 return bar
148
149
150 def daemonize(pidfile=None):
151 """
152 Daemonize this process with the UNIX double-fork trick.
153 Writes the new PID to the provided file name if not None.
154 """
155
156 print pidfile
157 pid = os.fork()
158 if pid > 0:
159 sys.exit(0)
160 os.setsid()
161 os.umask(0)
162 pid = os.fork()
163
164
165 if pid > 0:
166 if pidfile is not None:
167 open(pidfile, "w").write(str(pid))
168 sys.exit(0)
169
170 def get_acls_from_config(acldir='/etc/func/minion-acl.d'):
171 """
172 takes a dir of .acl files
173 returns a dict of hostname+hash = [methods, to, run]
174
175 """
176
177 acls = {}
178 if not os.path.exists(acldir):
179 print 'acl dir does not exist: %s' % acldir
180 return acls
181
182 # get the set of files
183 acl_glob = '%s/*.acl' % acldir
184 files = glob.glob(acl_glob)
185
186 for acl_file in files:
187
188 try:
189 fo = open(acl_file, 'r')
190 except (IOError, OSError), e:
191 print 'cannot open acl config file: %s - %s' % (acl_file, e)
192 continue
193
194 for line in fo.readlines():
195 if line.startswith('#'): continue
196 if line.strip() == '': continue
197 line = line.replace('\n', '')
198 (host, methods) = line.split('=')
199 host = host.strip().lower()
200 methods = methods.strip()
201 methods = methods.replace(',',' ')
202 methods = methods.split()
203 if not acls.has_key(host):
204 acls[host] = []
205 acls[host].extend(methods)
206
207 return acls