[Nix-dev] store & passwords - once again

Marc Weber marco-oweber at gmx.de
Sun Aug 12 15:44:34 CEST 2012


Would a patch like this be accepted until ACLs get implemented?

At least this way you can declare passwords without putting them into
the store (if you take some care).

The idea is to create /var/passwords/mysql-345344

and have the mysql startup script read them creating the databases.

The store only contains the path to /var/passwords/.., thus only the
mysql/apache/.. user can read it.

Maybe I'd prefer a ACL solution, too. But there is much more to think
about?

diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc
index 0d4efc4..2f1b141 100644
--- a/src/libexpr/primops.cc
+++ b/src/libexpr/primops.cc
@@ -666,6 +666,123 @@ struct FilterFromExpr : PathFilter
         return state.forceBool(res);
     }
 };
+/* arguments:
+   attr with names:
+   {
+     dir =  "/root/passwords/";
+     prefix =  "mysql-";
+     contents  = "contents";
+     uid  = uids.apache
+     gid  = gids.apache
+     mode  = ..
+   }
+
+   The function calculates a hash based on the (string) contents writing a
+   dir/${prefix}${hash} file with 700 mode
+
+   This way you configure passwords in configuration.nix without writing them
+   to the store - which also implies that you have to copy your dir to another
+   machine to duplicate it .. copying your store contents is no longer enough.
+
+   Upstart jobs are usualyl run as root anyway.
+
+   The perfect solution would be: https://github.com/NixOS/nix/issues/8
+   But until that exists - this is faster to implement
+
+   // TODO add some caching, allow setting user/group ids and mode?
+   */
+static void prim_writeFileHashed(EvalState & state, Value * * args, Value & v)
+{
+    PathSet context;
+
+    Value & a = *args[0];
+
+    state.forceAttrs(a);
+
+    string dir = "";
+    string prefix = "";
+    string contents = "";
+    unsigned int mode = ~0400;
+
+    int u_id = -1;
+    int g_id = -1;
+
+    // dir
+    Bindings::iterator j = a.attrs->find(state.symbols.create("dir"));
+    if (j == a.attrs->end())
+        throw TypeError("`dir' attribute missing in a call to `writeFileHashed'");
+    dir = state.forceStringNoCtx(*j->value);
+
+    // optional prefix
+    j = a.attrs->find(state.symbols.create("prefix"));
+    if (j != a.attrs->end()){
+      prefix = state.forceStringNoCtx(*j->value);
+    }
+
+    // contents
+    j = a.attrs->find(state.symbols.create("contents"));
+    if (j == a.attrs->end())
+        throw TypeError("`contents' attribute missing in a call to `writeFileHashed'");
+    contents = state.forceStringNoCtx(*j->value);
+
+
+    // uid
+    j = a.attrs->find(state.symbols.create("uid"));
+    if (j != a.attrs->end()){
+      u_id = state.forceInt(*j->value);
+    }
+    // gid
+    j = a.attrs->find(state.symbols.create("gid"));
+    if (j != a.attrs->end()){
+      g_id = state.forceInt(*j->value);
+    }
+
+    // mode
+    j = a.attrs->find(state.symbols.create("mode"));
+    if (j != a.attrs->end()){
+      string mode_str = state.forceStringNoCtx(*j->value);
+      if (mode_str.length() != 3){
+        // should also verify that 0-7 only ..
+        throw EvalError(format("bad mode string! %s") % mode_str);
+      }
+      if (mode_str[0] < '0' || mode_str[0] > '7'
+         || mode_str[1] < '0' || mode_str[1] > '7'
+         || mode_str[2] < '0' || mode_str[2] > '7'
+         )
+        throw EvalError(format("bad mode string! %s") % mode_str);
+      int dec = atoi(mode_str.c_str());
+
+      mode = ~( (dec        % 10) 
+              + ((dec /  10 % 10) << 3)
+              + ((dec / 100 % 10) << 6) );
+    }
+
+    Hash h = hashString(htMD5, contents);
+
+    string path = (format("%1%/%2%%3%") % dir % prefix % printHash(h)).str();
+
+    // unlink so that the file is always recreated with umask
+    unlink(path.c_str());
+
+    // can't use writeFile function because fchown is used
+    int old_mode = umask(mode);
+    FILE *f = fopen(path.c_str(), "w");
+    umask(old_mode);
+
+    if (!f)
+      throw EvalError(format("Couldn't create file `%1%'") % path);
+
+    // try setting owner:
+    if (u_id != -1 || g_id != -1){
+      if (fchown(fileno(f), u_id, g_id)){
+        fclose(f);
+        throw EvalError(format("Couldn't set uid, gid of file `%1%' - take care - the file may have been truncated!") % path);
+      }
+    }
+
+    // everything is fine - write and close
+    writeFull(fileno(f), (unsigned char *) contents.data(), contents.size());
+    fclose(f);
+
+    mkString(v, path, context);
+};
 
 
 static void prim_filterSource(EvalState & state, Value * * args, Value & v)
@@ -1104,6 +1221,9 @@ void EvalState::createBaseEnv()
     addPrimOp("__toFile", 2, prim_toFile);
     addPrimOp("__filterSource", 2, prim_filterSource);
 
+
+    addPrimOp("writeFileHashed", 1, prim_writeFileHashed);
+
     // Attribute sets
     addPrimOp("__attrNames", 1, prim_attrNames);
     addPrimOp("__getAttr", 2, prim_getAttr);


More information about the nix-dev mailing list