def _init_start_page(self, privdiruri):
ws = self.getServiceNamed("webish")
- startfile = os.path.join(self.basedir, "start.html")
+ startfile = os.path.join(self.basedir, "private", "start.html")
nodeurl_file = os.path.join(self.basedir, "node.url")
return ws.create_start_html(privdiruri, startfile, nodeurl_file)
def init_secret(self):
def make_secret():
return idlib.b2a(os.urandom(16)) + "\n"
- secret_s = self.get_or_create_config("secret", make_secret,
- filemode=0600)
+ secret_s = self.get_or_create_private_config("secret", make_secret)
self._secret = idlib.a2b(secret_s)
def init_storage(self):
c = ControlServer()
c.setServiceParent(self)
control_url = self.tub.registerReference(c)
- control_furl_file = os.path.join(self.basedir, "control.furl")
- open(control_furl_file, "w").write(control_url + "\n")
- os.chmod(control_furl_file, 0600)
-
+ self.write_private_config("control.furl", control_url + "\n")
def remote_get_versions(self):
return str(allmydata.__version__), str(self.OLDEST_SUPPORTED_VERSION)
from foolscap import Tub, eventual
from allmydata import get_package_versions_string
from allmydata.util import log as tahoe_log
-from allmydata.util import iputil, observer, humanreadable
+from allmydata.util import fileutil, iputil, observer, humanreadable
from allmydata.util.assertutil import precondition
# Just to get their versions:
else:
return d.isoformat(" ") + ".000Z"
+PRIV_README="""
+This directory contains files which contain private data for the Tahoe node,
+such as private keys. On Unix-like systems, the permissions on this directory
+are set to disallow users other than its owner from reading the contents of
+the files. See the 'configuration.txt' documentation file for details."""
+
class Node(service.MultiService):
# this implements common functionality of both Client nodes and Introducer
# nodes.
service.MultiService.__init__(self)
self.basedir = os.path.abspath(basedir)
self._tub_ready_observerlist = observer.OneShotObserverList()
- certfile = os.path.join(self.basedir, self.CERTFILE)
+ fileutil.make_dirs(os.path.join(self.basedir, "private"), 0700)
+ open(os.path.join(self.basedir, "private", "README"), "w").write(PRIV_README)
+ certfile = os.path.join(self.basedir, "private", self.CERTFILE)
self.tub = Tub(certFile=certfile)
- os.chmod(certfile, 0600)
self.tub.setOption("logLocalFailures", True)
self.tub.setOption("logRemoteFailures", True)
self.nodeid = b32decode(self.tub.tubID.upper()) # binary format
self.log("Node constructed. " + get_package_versions_string())
iputil.increase_rlimits()
- def get_config(self, name, mode="r", required=False):
+ def get_config(self, name, required=False):
"""Get the (string) contents of a config file, or None if the file
did not exist. If required=True, raise an exception rather than
returning None. Any leading or trailing whitespace will be stripped
from the data."""
fn = os.path.join(self.basedir, name)
try:
- return open(fn, mode).read().strip()
+ return open(fn, "r").read().strip()
except EnvironmentError:
if not required:
return None
raise
- def get_or_create_config(self, name, default_fn, mode="w", filemode=None):
- """Try to get the (string) contents of a config file, and return it.
- Any leading or trailing whitespace will be stripped from the data.
+ def write_private_config(self, name, value):
+ """Write the (string) contents of a private config file (which is a
+ config file that resides within the subdirectory named 'private'), and
+ return it. Any leading or trailing whitespace will be stripped from
+ the data.
+ """
+ privname = os.path.join(self.basedir, "private", name)
+ open(privname, "w").write(value.strip())
+
+ def get_or_create_private_config(self, name, default):
+ """Try to get the (string) contents of a private config file (which
+ is a config file that resides within the subdirectory named
+ 'private'), and return it. Any leading or trailing whitespace will be
+ stripped from the data.
- If the file does not exist, try to create it using default_fn, and
- then return the value that was written. If 'default_fn' is a string,
+ If the file does not exist, try to create it using default, and
+ then return the value that was written. If 'default' is a string,
use it as a default value. If not, treat it as a 0-argument callable
which is expected to return a string.
"""
- value = self.get_config(name)
+ privname = os.path.join("private", name)
+ value = self.get_config(privname)
if value is None:
- if isinstance(default_fn, (str, unicode)):
- value = default_fn
+ if isinstance(default, (str, unicode)):
+ value = default
else:
- value = default_fn()
- fn = os.path.join(self.basedir, name)
+ value = default()
+ fn = os.path.join(self.basedir, privname)
try:
- f = open(fn, mode)
- f.write(value)
- f.close()
- if filemode is not None:
- os.chmod(fn, filemode)
+ open(fn, "w").write(value)
except EnvironmentError, e:
self.log("Unable to write config file '%s'" % fn)
self.log(e)
# now we wait for the client to get started. we're looking for the
# control.furl file to appear.
- furl_file = os.path.join(clientdir, "control.furl")
+ furl_file = os.path.join(clientdir, "private", "control.furl")
def _check():
if pp.ended and pp.ended.value.status != 0:
# the twistd process ends normally (with rc=0) if the child
def __init__(self, test_client_dir):
#self.real_stderr = sys.stderr
log.startLogging(open("st.log", "a"), setStdout=False)
- f = open(os.path.join(test_client_dir, "control.furl"), "r")
+ f = open(os.path.join(test_client_dir, "private", "control.furl"), "r")
self.control_furl = f.read().strip()
f.close()
self.base_service = service.MultiService()
open(os.path.join(basedir, "introducer.furl"), "w").write("")
open(os.path.join(basedir, "vdrive.furl"), "w").write("")
c = client.Client(basedir)
- secret_file = os.path.join(basedir, "secret")
- self.failUnless(os.path.exists(secret_file))
+ secret_fname = os.path.join(basedir, "private", "secret")
+ self.failUnless(os.path.exists(secret_fname), secret_fname)
renew_secret = c.get_renewal_secret()
self.failUnless(idlib.b2a(renew_secret))
cancel_secret = c.get_cancel_secret()
-import os, time
+import os, stat, sys, time
from twisted.trial import unittest
from twisted.internet import defer
from twisted.python import log
self.failUnless("Z" in t)
t2 = formatTimeTahoeStyle("ignored", int(time.time()))
self.failUnless("Z" in t2)
+
+ def test_secrets_dir(self):
+ basedir = "test_node/test_secrets_dir"
+ fileutil.make_dirs(basedir)
+ n = TestNode(basedir)
+ self.failUnless(os.path.exists(os.path.join(basedir, "private")))
+
+ def test_secrets_dir_protected(self):
+ if "win32" in sys.platform.lower() or "cygwin" in sys.platform.lower():
+ # We don't know how to test that unprivileged users can't read this
+ # thing. (Also we don't know exactly how to set the permissions so
+ # that unprivileged users can't read this thing.)
+ raise unittest.SkipTest("We don't know how to set permissions on Windows.")
+ basedir = "test_node/test_secrets_dir_protected"
+ fileutil.make_dirs(basedir)
+ n = TestNode(basedir)
+ privdir = os.path.join(basedir, "private")
+ st = os.stat(privdir)
+ bits = stat.S_IMODE(st[stat.ST_MODE])
+ self.failUnless(bits & 0001 == 0, bits)
def _test_web_start(self, res):
basedir = self.clients[0].basedir
- startfile = os.path.join(basedir, "start.html")
+ startfile = os.path.join(basedir, "private", "start.html")
self.failUnless(os.path.exists(startfile))
start_html = open(startfile, "r").read()
self.failUnless(self.webish_url in start_html)
# exercise the remote-control-the-client foolscap interfaces in
# allmydata.control (mostly used for performance tests)
c0 = self.clients[0]
- control_furl_file = os.path.join(c0.basedir, "control.furl")
+ control_furl_file = os.path.join(c0.basedir, "private", "control.furl")
control_furl = open(control_furl_file, "r").read().strip()
# it doesn't really matter which Tub we use to connect to the client,
# so let's just use our IntroducerNode's
self.s.basedir = 'web/test_welcome'
fileutil.make_dirs("web/test_welcome")
+ fileutil.make_dirs("web/test_welcome/private")
self.ws.create_start_html("private_uri",
- "web/test_welcome/start.html",
+ "web/test_welcome/private/start.html",
"web/test_welcome/node.url")
return self.GET("/")
d.addCallback(_check)
def _check2(res):
self.failUnless('To view your personal private non-shared' in res)
self.failUnless('from your local filesystem:' in res)
- self.failUnless(os.path.abspath('web/test_welcome/start.html')
+ self.failUnless(os.path.abspath('web/test_welcome/private/start.html')
in res)
d.addCallback(_check2)
return d
def test_start_html(self):
fileutil.make_dirs("web")
- startfile = "web/start.html"
+ fileutil.make_dirs("web/private")
+ startfile = "web/private/start.html"
nodeurlfile = "web/node.url"
self.ws.create_start_html("private_uri", startfile, nodeurlfile)
if self.cleanup and hasattr(self, 'name'):
rm_dir(self.name)
-def make_dirs(dirname, mode=0777, strictmode=False):
+def make_dirs(dirname, mode=0777):
"""
- A threadsafe and idempotent version of os.makedirs(). If the dir already
- exists, do nothing and return without raising an exception. If this call
- creates the dir, return without raising an exception. If there is an
- error that prevents creation or if the directory gets deleted after
- make_dirs() creates it and before make_dirs() checks that it exists, raise
- an exception.
-
- @param strictmode if true, then make_dirs() will raise an exception if the
- directory doesn't have the desired mode. For example, if the
- directory already exists, and has a different mode than the one
- specified by the mode parameter, then if strictmode is true,
- make_dirs() will raise an exception, else it will ignore the
- discrepancy.
+ An idempotent version of os.makedirs(). If the dir already exists, do
+ nothing and return without raising an exception. If this call creates the
+ dir, return without raising an exception. If there is an error that
+ prevents creation or if the directory gets deleted after make_dirs() creates
+ it and before make_dirs() checks that it exists, raise an exception.
"""
tx = None
try:
raise tx
raise exceptions.IOError, "unknown error prevented creation of directory, or deleted the directory immediately after creation: %s" % dirname # careful not to construct an IOError with a 2-tuple, as that has a special meaning...
- tx = None
- if hasattr(os, 'chmod'):
- try:
- os.chmod(dirname, mode)
- except OSError, x:
- tx = x
-
- if strictmode and hasattr(os, 'stat'):
- s = os.stat(dirname)
- resmode = stat.S_IMODE(s.st_mode)
- if resmode != mode:
- if tx:
- raise tx
- raise exceptions.IOError, "unknown error prevented setting correct mode of directory, or changed mode of the directory immediately after creation. dirname: %s, mode: %04o, resmode: %04o" % (dirname, mode, resmode,) # careful not to construct an IOError with a 2-tuple, as that has a special meaning...
-
def rm_dir(dirname):
"""
A threadsafe and idempotent version of shutil.rmtree(). If the dir is
def render_private_vdrive(self, ctx, data):
basedir = IClient(ctx).basedir
- start_html = os.path.abspath(os.path.join(basedir, "start.html"))
+ start_html = os.path.abspath(os.path.join(basedir, "private", "start.html"))
if os.path.exists(start_html):
return T.p["To view your personal private non-shared filestore, ",
"use this browser to open the following file from ",
def _create_start_html(self, dummy, private_uri, startfile, nodeurl_file):
f = open(startfile, "w")
- os.chmod(startfile, 0600)
template = open(util.sibpath(__file__, "web/start.html"), "r").read()
# what is our webport?
s = self.listener