1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 """Script to ensure permissions on files/dirs are accurate.
22
23 """
24
25 import errno
26 import os
27 import os.path
28 import optparse
29 import sys
30 import stat
31 import logging
32
33 from ganeti import constants
34 from ganeti import errors
35 from ganeti import runtime
36 from ganeti import ssconf
37 from ganeti import utils
38 from ganeti import cli
39
40
41 (DIR,
42 FILE,
43 QUEUE_DIR) = range(1, 4)
44
45 ALL_TYPES = frozenset([
46 DIR,
47 FILE,
48 QUEUE_DIR,
49 ])
50
51
53 """Top-level error class related to this script.
54
55 """
56
57
58 -def EnsurePermission(path, mode, uid=-1, gid=-1, must_exist=True,
59 _chmod_fn=os.chmod, _chown_fn=os.chown, _stat_fn=os.stat):
60 """Ensures that given path has given mode.
61
62 @param path: The path to the file
63 @param mode: The mode of the file
64 @param uid: The uid of the owner of this file
65 @param gid: The gid of the owner of this file
66 @param must_exist: Specifies if non-existance of path will be an error
67 @param _chmod_fn: chmod function to use (unittest only)
68 @param _chown_fn: chown function to use (unittest only)
69
70 """
71 logging.debug("Checking %s", path)
72 try:
73 st = _stat_fn(path)
74
75 fmode = stat.S_IMODE(st[stat.ST_MODE])
76 if fmode != mode:
77 logging.debug("Changing mode of %s from %#o to %#o", path, fmode, mode)
78 _chmod_fn(path, mode)
79
80 if max(uid, gid) > -1:
81 fuid = st[stat.ST_UID]
82 fgid = st[stat.ST_GID]
83 if fuid != uid or fgid != gid:
84 logging.debug("Changing owner of %s from UID %s/GID %s to"
85 " UID %s/GID %s", path, fuid, fgid, uid, gid)
86 _chown_fn(path, uid, gid)
87 except EnvironmentError, err:
88 if err.errno == errno.ENOENT:
89 if must_exist:
90 raise EnsureError("Path %s should exist, but does not" % path)
91 else:
92 raise EnsureError("Error while changing permissions on %s: %s" %
93 (path, err))
94
95
98 """Ensures that given path is a dir and has given mode, uid and gid set.
99
100 @param path: The path to the file
101 @param mode: The mode of the file
102 @param uid: The uid of the owner of this file
103 @param gid: The gid of the owner of this file
104 @param _lstat_fn: Stat function to use (unittest only)
105 @param _mkdir_fn: mkdir function to use (unittest only)
106 @param _ensure_fn: ensure function to use (unittest only)
107
108 """
109 logging.debug("Checking directory %s", path)
110 try:
111
112 st = _lstat_fn(path)
113 except EnvironmentError, err:
114 if err.errno != errno.ENOENT:
115 raise EnsureError("stat(2) on %s failed: %s" % (path, err))
116 _mkdir_fn(path)
117 else:
118 if not stat.S_ISDIR(st[stat.ST_MODE]):
119 raise EnsureError("Path %s is expected to be a directory, but isn't" %
120 path)
121
122 _ensure_fn(path, mode, uid=uid, gid=gid)
123
124
126 """Ensures permissions recursively down a directory.
127
128 This functions walks the path and sets permissions accordingly.
129
130 @param path: The absolute path to walk
131 @param uid: The uid used as owner
132 @param gid: The gid used as group
133 @param dir_perm: The permission bits set for directories
134 @param file_perm: The permission bits set for files
135
136 """
137 assert os.path.isabs(path), "Path %s is not absolute" % path
138 assert os.path.isdir(path), "Path %s is not a dir" % path
139
140 logging.debug("Recursively processing %s", path)
141
142 for root, dirs, files in os.walk(path):
143 for subdir in dirs:
144 EnsurePermission(os.path.join(root, subdir), dir_perm, uid=uid, gid=gid)
145
146 for filename in files:
147 EnsurePermission(os.path.join(root, filename), file_perm, uid=uid,
148 gid=gid)
149
150
152 """Sets the correct permissions on all job files in the queue.
153
154 @param path: Directory path
155 @param mode: Wanted file mode
156 @param uid: Wanted user ID
157 @param gid: Wanted group ID
158
159 """
160 for filename in utils.ListVisibleFiles(path):
161 if constants.JOB_FILE_RE.match(filename):
162 EnsurePermission(utils.PathJoin(path, filename), mode, uid=uid, gid=gid)
163
164
166 """Processes a path component.
167
168 @param path: A tuple of the path component to process
169
170 """
171 (pathname, pathtype, mode, uid, gid) = path[0:5]
172
173 assert pathtype in ALL_TYPES
174
175 if pathtype in (DIR, QUEUE_DIR):
176
177 assert len(path[5:]) == 0
178 if pathtype == DIR:
179 EnsureDir(pathname, mode, uid, gid)
180 elif pathtype == QUEUE_DIR:
181 EnsureQueueDir(pathname, mode, uid, gid)
182 elif pathtype == FILE:
183 (must_exist, ) = path[5:]
184 EnsurePermission(pathname, mode, uid=uid, gid=gid, must_exist=must_exist)
185
186
188 """Returns a tuple of path objects to process.
189
190 """
191 getent = runtime.GetEnts()
192 masterd_log = constants.DAEMONS_LOGFILES[constants.MASTERD]
193 noded_log = constants.DAEMONS_LOGFILES[constants.NODED]
194 confd_log = constants.DAEMONS_LOGFILES[constants.CONFD]
195 rapi_log = constants.DAEMONS_LOGFILES[constants.RAPI]
196
197 rapi_dir = os.path.join(constants.DATA_DIR, "rapi")
198
199 paths = [
200 (constants.DATA_DIR, DIR, 0755, getent.masterd_uid,
201 getent.masterd_gid),
202 (constants.CLUSTER_DOMAIN_SECRET_FILE, FILE, 0640,
203 getent.masterd_uid, getent.masterd_gid, False),
204 (constants.CLUSTER_CONF_FILE, FILE, 0640, getent.masterd_uid,
205 getent.confd_gid, False),
206 (constants.CONFD_HMAC_KEY, FILE, 0440, getent.confd_uid,
207 getent.masterd_gid, False),
208 (constants.SSH_KNOWN_HOSTS_FILE, FILE, 0644, getent.masterd_uid,
209 getent.masterd_gid, False),
210 (constants.RAPI_CERT_FILE, FILE, 0440, getent.rapi_uid,
211 getent.masterd_gid, False),
212 (constants.NODED_CERT_FILE, FILE, 0440, getent.masterd_uid,
213 getent.masterd_gid, False),
214 ]
215
216 ss = ssconf.SimpleStore()
217 for ss_path in ss.GetFileList():
218 paths.append((ss_path, FILE, constants.SS_FILE_PERMS,
219 getent.noded_uid, 0, False))
220
221 paths.extend([
222 (constants.QUEUE_DIR, DIR, 0700, getent.masterd_uid,
223 getent.masterd_gid),
224 (constants.QUEUE_DIR, QUEUE_DIR, 0600, getent.masterd_uid,
225 getent.masterd_gid),
226 (constants.JOB_QUEUE_LOCK_FILE, FILE, 0600,
227 getent.masterd_uid, getent.masterd_gid, False),
228 (constants.JOB_QUEUE_SERIAL_FILE, FILE, 0600,
229 getent.masterd_uid, getent.masterd_gid, False),
230 (constants.JOB_QUEUE_VERSION_FILE, FILE, 0600,
231 getent.masterd_uid, getent.masterd_gid, False),
232 (constants.JOB_QUEUE_ARCHIVE_DIR, DIR, 0700,
233 getent.masterd_uid, getent.masterd_gid),
234 (rapi_dir, DIR, 0750, getent.rapi_uid, getent.masterd_gid),
235 (constants.RAPI_USERS_FILE, FILE, 0640, getent.rapi_uid,
236 getent.masterd_gid, False),
237 (constants.RUN_GANETI_DIR, DIR, 0775, getent.masterd_uid,
238 getent.daemons_gid),
239 (constants.SOCKET_DIR, DIR, 0750, getent.masterd_uid,
240 getent.daemons_gid),
241 (constants.MASTER_SOCKET, FILE, 0770, getent.masterd_uid,
242 getent.daemons_gid, False),
243 (constants.BDEV_CACHE_DIR, DIR, 0755, getent.noded_uid,
244 getent.masterd_gid),
245 (constants.UIDPOOL_LOCKDIR, DIR, 0750, getent.noded_uid,
246 getent.masterd_gid),
247 (constants.DISK_LINKS_DIR, DIR, 0755, getent.noded_uid,
248 getent.masterd_gid),
249 (constants.CRYPTO_KEYS_DIR, DIR, 0700, getent.noded_uid,
250 getent.masterd_gid),
251 (constants.IMPORT_EXPORT_DIR, DIR, 0755, getent.noded_uid,
252 getent.masterd_gid),
253 (constants.LOG_DIR, DIR, 0770, getent.masterd_uid,
254 getent.daemons_gid),
255 (masterd_log, FILE, 0600, getent.masterd_uid, getent.masterd_gid,
256 False),
257 (confd_log, FILE, 0600, getent.confd_uid, getent.masterd_gid, False),
258 (noded_log, FILE, 0600, getent.noded_uid, getent.masterd_gid, False),
259 (rapi_log, FILE, 0600, getent.rapi_uid, getent.masterd_gid, False),
260 (constants.LOG_OS_DIR, DIR, 0750, getent.masterd_uid,
261 getent.daemons_gid),
262 ])
263
264 return tuple(paths)
265
266
268 """Configures the logging module.
269
270 """
271 formatter = logging.Formatter("%(asctime)s: %(message)s")
272
273 stderr_handler = logging.StreamHandler()
274 stderr_handler.setFormatter(formatter)
275 if opts.debug:
276 stderr_handler.setLevel(logging.NOTSET)
277 elif opts.verbose:
278 stderr_handler.setLevel(logging.INFO)
279 else:
280 stderr_handler.setLevel(logging.WARNING)
281
282 root_logger = logging.getLogger("")
283 root_logger.setLevel(logging.NOTSET)
284 root_logger.addHandler(stderr_handler)
285
286
288 """Parses the options passed to the program.
289
290 @return: Options and arguments
291
292 """
293 program = os.path.basename(sys.argv[0])
294
295 parser = optparse.OptionParser(usage="%%prog [--full-run]",
296 prog=program)
297 parser.add_option(cli.DEBUG_OPT)
298 parser.add_option(cli.VERBOSE_OPT)
299 parser.add_option("--full-run", "-f", dest="full_run", action="store_true",
300 default=False, help=("Make a full run and set permissions"
301 " on archived jobs (time consuming)"))
302
303 return parser.parse_args()
304
305
307 """Main routine.
308
309 """
310 (opts, _) = ParseOptions()
311
312 SetupLogging(opts)
313
314 if opts.full_run:
315 logging.info("Running in full mode")
316
317 getent = runtime.GetEnts()
318
319 try:
320 for path in GetPaths():
321 ProcessPath(path)
322
323 if opts.full_run:
324 RecursiveEnsure(constants.JOB_QUEUE_ARCHIVE_DIR, getent.masterd_uid,
325 getent.masterd_gid, 0700, 0600)
326 except EnsureError, err:
327 logging.error("An error occurred while setting permissions: %s", err)
328 return constants.EXIT_FAILURE
329
330 return constants.EXIT_SUCCESS
331