Skip to content

Commit

Permalink
Turn "keys" option into an attrset of attrsets.
Browse files Browse the repository at this point in the history
This now provides options for each key and also converts old style
string-only keys into the new format while emitting a warning.

At the moment only the "text" option is actually supported.

For applying the keys for string-values the permissions attribute is set
to "0600" instead of the default value "0640" in keyOptionsType in order
to correctly replicate the old behaviour even when we implement
permissions and ownership.

Signed-off-by: aszlig <[email protected]>
  • Loading branch information
aszlig committed Jun 24, 2014
1 parent 51cafce commit 3adc359
Show file tree
Hide file tree
Showing 2 changed files with 82 additions and 10 deletions.
76 changes: 69 additions & 7 deletions nix/keys.nix
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,55 @@

with pkgs.lib;

let
keyOptionsType = types.submodule {
text = mkOption {
example = "super secret stuff";
type = types.str;
description = ''
The text the key should contain. So if the key name is
<replaceable>password</replaceable> and <literal>foobar</literal>
is set here, the contents of the file
<filename>/run/keys/<replaceable>password</replaceable></filename>
will be <literal>foobar</literal>.
'';
};

user = mkOption {
default = "root";
type = types.str;
description = ''
The user which will be the owner of the key file.
'';
};

group = mkOption {
default = "root";
type = types.str;
description = ''
The group that will be set for the key file.
'';
};

permissions = mkOption {
default = "0640";
example = "0600";
type = types.str;
description = ''
The default permissions to set for the key file, needs to be in the
format accepted by <citerefentry><refentrytitle>chmod</refentrytitle>
<manvolnum>1</manvolnum></citerefentry>.
'';
};
};

keyType = mkOptionType {
name = "string or key options";
check = v: isString v || keyOptionsType.check v;
};

in

{

###### interface
Expand All @@ -24,18 +73,31 @@ with pkgs.lib;

deployment.keys = mkOption {
default = {};
example = { password = "foobar"; };
type = types.attrsOf types.str;
example = { password.text = "foobar"; };
type = types.attrsOf keyType;

apply = mapAttrs (k: v: let
warning = "Using plain strings for `deployment.keys' is"
+ " deprecated, please use `deployment.keys.${k}.text ="
+ " \"<value>\"` instead of `deployment.keys.${k} ="
+ " \"<value>\"`.";
in if isString v then builtins.trace warning {
text = v;
user = "root";
group = "root";
permissions = "0600";
} else v);

description = ''
The set of keys to be deployed to the machine. Each attribute
maps a key name to a key string. On the machine, the key can
be accessed as
<filename>/run/keys/<replaceable>name></replaceable></filename>.
Thus, <literal>{ password = "foobar"; }</literal> causes a
maps a key name to a file that can be accessed as
<filename>/run/keys/<replaceable>name</replaceable></filename>.
Thus, <literal>{ password.text = "foobar"; }</literal> causes a
file <filename>/run/keys/password</filename> to be created
with contents <literal>foobar</literal>. The directory
<filename>/run/keys</filename> is only accessible to root and
the <literal>keys</literal> group.
the <literal>keys</literal> group. So keep in mind to add any
users that need to have access to a particular key to this group.
'';
};

Expand Down
16 changes: 13 additions & 3 deletions nixops/backends/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,18 @@ def __init__(self, xml):
self.store_keys_on_machine = xml.find("attrs/attr[@name='storeKeysOnMachine']/bool").get("value") == "true"
self.ssh_port = int(xml.find("attrs/attr[@name='targetPort']/int").get("value"))
self.always_activate = xml.find("attrs/attr[@name='alwaysActivate']/bool").get("value") == "true"
self.keys = {k.get("name"): k.find("string").get("value") for k in xml.findall("attrs/attr[@name='keys']/attrs/attr")}
self.owners = [e.get("value") for e in xml.findall("attrs/attr[@name='owners']/list/string")]

def _extract_key_options(x):
opts = {}
for key in ('text', 'user', 'group', 'permissions'):
elem = x.find("attrs/attr[@name='{0}']/string".format(key))
if elem is not None:
opts[key] = elem.get("value")
return opts

self.keys = {k.get("name"): _extract_key_options(k) for k in
xml.findall("attrs/attr[@name='keys']/attrs/attr")}


class MachineState(nixops.resources.ResourceState):
Expand All @@ -32,7 +41,7 @@ class MachineState(nixops.resources.ResourceState):
ssh_port = nixops.util.attr_property("targetPort", 22, int)
public_vpn_key = nixops.util.attr_property("publicVpnKey", None)
store_keys_on_machine = nixops.util.attr_property("storeKeysOnMachine", True, bool)
keys = nixops.util.attr_property("keys", [], 'json')
keys = nixops.util.attr_property("keys", {}, 'json')
owners = nixops.util.attr_property("owners", [], 'json')

# Nix store path of the last global configuration deployed to this
Expand Down Expand Up @@ -177,7 +186,8 @@ def send_keys(self):
if self.store_keys_on_machine: return
self.run_command("mkdir -m 0750 -p /run/keys"
" && chown root:keys /run/keys")
for k, v in self.get_keys().items():
for k, opts in self.get_keys().items():
v = opts['text']
self.log("uploading key ‘{0}’...".format(k))
tmp = self.depl.tempdir + "/key-" + self.name
f = open(tmp, "w+"); f.write(v); f.close()
Expand Down

0 comments on commit 3adc359

Please sign in to comment.