This commit is contained in:
Craig Everett 2017-12-12 08:31:25 +09:00
parent 428f7e0565
commit 11d5f3f767

208
zx
View File

@ -348,7 +348,7 @@ ensure_installed(PackageID = {Realm, Name, Version}) ->
end. end.
-spec ensure_installed(Realm, Name, Version) -> Result -spec ensure_installed(Realm, Name, Version) -> Result | no_return()
when Realm :: realm(), when Realm :: realm(),
Name :: name(), Name :: name(),
Version :: version(), Version :: version(),
@ -707,13 +707,10 @@ valid_package({Realm, Name}) ->
{false, false} -> {false, false} ->
ok = log(error, "Invalid realm ~tp and package ~tp", [Realm, Name]), ok = log(error, "Invalid realm ~tp and package ~tp", [Realm, Name]),
halt(1) halt(1)
end; end.
valid_package(Bad) ->
ok = log(error, "Invalid package() value: ~160tp", [Bad]),
halt(1).
-spec string_to_package(string()) -> ok | no_return(). -spec string_to_package(string()) -> package() | no_return().
%% @private %% @private
%% Convert a string to a package() type if possible. If not then halt the system. %% Convert a string to a package() type if possible. If not then halt the system.
@ -2306,35 +2303,31 @@ create_realm(ZompConf, Realm, ExAddress, ExPort, InPort, UserName, Email) ->
" There are no rules for this one. Any valid UTF-8 printables are legal.~n", " There are no rules for this one. Any valid UTF-8 printables are legal.~n",
ok = io:format(Instructions), ok = io:format(Instructions),
RealName = get_input(), RealName = get_input(),
create_realm(ZompConf, Realm, ExAddress, ExPort, InPort, UserName, Email, RealName).
-spec create_realm(ZompConf, Realm, ExAddress, ExPort, InPort, UserName, Email, RealName) ->
no_return()
when ZompConf :: [{Key :: atom(), Value :: term()}],
Realm :: realm(),
ExAddress :: inet:hostname() | inet:ip_address(),
ExPort :: inet:port_number(),
InPort :: inet:port_number(),
UserName :: string(),
Email :: string(),
RealName :: string().
create_realm(ZompConf, Realm, ExAddress, ExPort, InPort, UserName, Email, RealName) ->
ok = io:format("~nGenerating keys. This might take a while, so settle in...~n"), ok = io:format("~nGenerating keys. This might take a while, so settle in...~n"),
{ok, RealmKey, RealmPub} = generate_rsa({Realm, Realm ++ ".1.realm"}), {ok, RealmKey, RealmPub} = generate_rsa({Realm, Realm ++ ".1.realm"}),
{ok, PackageKey, PackagePub} = generate_rsa({Realm, Realm ++ ".1.package"}), {ok, PackageKey, PackagePub} = generate_rsa({Realm, Realm ++ ".1.package"}),
{ok, SysopKey, SysopPub} = generate_rsa({Realm, UserName ++ ".1"}), {ok, SysopKey, SysopPub} = generate_rsa({Realm, UserName ++ ".1"}),
AllKeys = [RealmKey, RealmPub, PackageKey, PackagePub, SysopKey, SysopPub], ok = log(info, "Generated 16k RSA pair ~ts ~ts", [RealmKey, RealmPub]),
DangerousKeys = [PackageKey, SysopKey], ok = log(info, "Generated 16k RSA pair ~ts ~ts", [PackageKey, PackagePub]),
Copy = ok = log(info, "Generated 16k RSA pair ~ts ~ts", [SysopKey, SysopPub]),
fun(From) ->
To = filename:basename(From),
case filelib:is_file(To) of
true ->
M = "Whoops! Keyfile local destination ~tp exists! Aborting",
ok = log(error, M, [To]),
ok = log(info, "Undoing all changes..."),
ok = lists:foreach(fun file:delete/1, AllKeys),
halt(0);
false ->
{ok, _} = file:copy(From, To),
log(info, "Copying to local directory: ~ts", [From])
end
end,
Drop =
fun(File) ->
ok = file:delete(File),
log(info, "Deleting ~ts", [File])
end,
ok = lists:foreach(Copy, AllKeys),
ok = lists:foreach(Drop, DangerousKeys),
Timestamp = calendar:now_to_universal_time(erlang:timestamp()), Timestamp = calendar:now_to_universal_time(erlang:timestamp()),
{ok, RealmPubData} = file:read_file(RealmPub), {ok, RealmPubData} = file:read_file(RealmPub),
RealmPubRecord = RealmPubRecord =
{{Realm, filename:basename(RealmPub, ".pub.der")}, {{Realm, filename:basename(RealmPub, ".pub.der")},
@ -2349,25 +2342,12 @@ create_realm(ZompConf, Realm, ExAddress, ExPort, InPort, UserName, Email) ->
{realm, Realm}, {realm, Realm},
crypto:hash(sha512, PackagePubData), crypto:hash(sha512, PackagePubData),
Timestamp}, Timestamp},
Message =
"~n"
" All of the keys generated have been moved to the current directory.~n"
"~n"
" MAKE AND SECURELY STORE COPIES OF THESE KEYS.~n"
"~n"
" The private package and sysop login keys have been deleted from the "
"key directory. These should only exist on your local system, not a prime "
"realm server (particularly if other services are run on that machine).~n"
" The package and sysop keys will need to be copied to the ~~/.zomp/keys/~s/~n"
" directory on your personal or dev machine.~n",
ok = io:format(Message, [Realm]),
UserRecord = UserRecord =
{{Realm, UserName}, {{Realm, UserName},
[filename:basename(SysopPub, ".pub.der")], [filename:basename(SysopPub, ".pub.der")],
Email, Email,
RealName}, RealName},
RealmFile = filename:join(zomp_dir(), Realm ++ ".realm"), RealmSettings =
RealmMeta =
[{realm, Realm}, [{realm, Realm},
{revision, 0}, {revision, 0},
{prime, {ExAddress, ExPort}}, {prime, {ExAddress, ExPort}},
@ -2376,23 +2356,86 @@ create_realm(ZompConf, Realm, ExAddress, ExPort, InPort, UserName, Email) ->
{sysops, [UserRecord]}, {sysops, [UserRecord]},
{realm_keys, [RealmPubRecord]}, {realm_keys, [RealmPubRecord]},
{package_keys, [PackagePubRecord]}], {package_keys, [PackagePubRecord]}],
Realms = ZompSettings =
case lists:keyfind(managed, 1, ZompConf) of [{managed, [Realm]},
{managed, M} -> [Realm | M];
false -> [Realm]
end,
ZompFile = filename:join(zomp_dir(), "zomp.conf"),
Update = fun({K, V}, ZC) -> lists:keystore(K, 1, ZC, {K, V}) end,
NewConf =
[{managed, Realms},
{external_address, ExAddress}, {external_address, ExAddress},
{external_port, ExPort}, {external_port, ExPort},
{internal_port, InPort}], {internal_port, InPort}],
NewZompConf = lists:foldl(Update, ZompConf, NewConf),
ok = write_terms(RealmFile, RealmMeta), RealmFN = Realm ++ ".realm",
ok = write_terms(ZompFile, NewZompConf), RealmConf = filename:join(zomp_dir(), RealmFN),
ok = log(info, "Realm ~ts created.", [Realm]), ok = write_terms(RealmConf, RealmSettings),
create_realmfile(Realm). {ok, CWD} = file:get_cwd(),
{ok, TempDir} = mktemp_dir("zomp"),
ok = file:set_cwd(TempDir),
KeyDir = filename:join("key", Realm),
ok = filelib:ensure_dir(KeyDir),
ok = file:make_dir(KeyDir),
KeyCopy =
fun(K) ->
{ok, _} = file:copy(K, filename:join(KeyDir, filename:basename(K))),
ok
end,
TarOpts = [compressed, {cwd, TempDir}],
ok = write_terms(RealmFN, RealmSettings),
ok = KeyCopy(PackagePub),
ok = KeyCopy(RealmPub),
PublicZRF = filename:join(CWD, Realm ++ ".zrf"),
Files = filelib:wildcard("**"),
ok = erl_tar:create(PublicZRF, [RealmFN, "key"], TarOpts),
ok = KeyCopy(SysopPub),
ok = write_terms("zomp.conf", ZompSettings),
PrimeZRF = filename:join(CWD, Realm ++ ".zpf"),
ok = erl_tar:create(PrimeZRF, [RealmFN, "zomp.conf", "key"], TarOpts),
ok = file:set_cwd(zomp_dir()),
KeyBundle = filename:join(CWD, Realm ++ ".zkf"),
ok = erl_tar:create(KeyBundle, [KeyDir], [compressed]),
ok = file:set_cwd(CWD),
ok = rm_rf(TempDir),
Message =
"============================================================================~n"
"DONE!~n"
"~n"
"The realm ~ts has been created and is accessible from the current system.~n"
"Three configuration bundles have been created in the current directory:~n"
"~n"
" 1. ~ts ~n"
"This is the PRIVATE realm file you will need to install on the realm's prime~n"
"node. It includes the your (the sysop's) public key.~n"
"~n"
" 2. ~ts ~n"
"This file is the PUBLIC realm file other zomp nodes and zx users will need to~n"
"access the realm. It does not include your (the sysop's) public key.~n"
"~n"
" 3. ~ts ~n"
"This is the bundle of ALL KEYS that are defined in this realm at the moment.~n"
"~n"
"Now you need to make copies of these three files and back them up.~n"
"~n"
"On the PRIME NODE you need to run `zx add realm ~ts` and follow the prompts~n"
"to cause it to begin serving that realm as prime. (Node restart required.)~n"
"~n"
"On all zx CLIENTS that want to access your new realm and on all subordinate~n"
"MIRROR NODES the command `zx add realm ~ts` will need to be run.~n"
"The method of public realm file distribution (~ts) is up to you.~n"
"~n"
"~n"
"Public & Private key installation (if you need to recover them or perform~n"
"sysop functions from another computer) is `zx add keybundle ~ts`.~n"
"============================================================================~n",
Substitutions =
[Realm,
PrimeZRF, PublicZRF, KeyBundle,
PrimeZRF,
PublicZRF, PublicZRF,
KeyBundle],
ok = io:format(Message, Substitutions),
halt(0).
-spec create_realmfile(realm()) -> no_return(). -spec create_realmfile(realm()) -> no_return().
@ -2451,7 +2494,7 @@ install(PackageID) ->
{TgzFile, TgzData} = lists:keyfind(TgzFile, 1, Files), {TgzFile, TgzData} = lists:keyfind(TgzFile, 1, Files),
{"zomp.meta", MetaBin} = lists:keyfind("zomp.meta", 1, Files), {"zomp.meta", MetaBin} = lists:keyfind("zomp.meta", 1, Files),
Meta = binary_to_term(MetaBin), Meta = binary_to_term(MetaBin),
{KeyID, Signature} = maps:get(sig, 1, Meta), {KeyID, Signature} = maps:get(sig, Meta),
{ok, PubKey} = loadkey(public, KeyID), {ok, PubKey} = loadkey(public, KeyID),
ok = ensure_package_dirs(PackageID), ok = ensure_package_dirs(PackageID),
PackageDir = filename:join("lib", PackageString), PackageDir = filename:join("lib", PackageString),
@ -2558,6 +2601,24 @@ receive_zrp(Socket, PackageID) ->
end. end.
-spec mktemp_dir(Prefix) -> Result
when Prefix :: string(),
Result :: {ok, TempDir :: file:filename()}
| {error, Reason :: file:posix()}.
mktemp_dir(Prefix) ->
Rand = integer_to_list(binary:decode_unsigned(crypto:strong_rand_bytes(8)), 36),
TempPath = filename:basedir(user_cache, Prefix),
TempDir = filename:join(TempPath, Rand),
Result1 = filelib:ensure_dir(TempDir),
Result2 = file:make_dir(TempDir),
case {Result1, Result2} of
{ok, ok} -> {ok, TempDir};
{ok, Error} -> Error;
{Error, _} -> Error
end.
%%% Utility functions %%% Utility functions
-spec read_meta() -> package_meta() | no_return(). -spec read_meta() -> package_meta() | no_return().
@ -2674,6 +2735,33 @@ installed(PackageID) ->
filelib:is_dir(PackageDir). filelib:is_dir(PackageDir).
-spec rm_rf(file:filename()) -> ok | {error, file:posix()}.
%% @private
%% Recursively remove files and directories, equivalent to `rm -rf' on unix.
rm_rf(Path) ->
case filelib:is_dir(Path) of
true ->
Pattern = filename:join(Path, "**"),
Contents = lists:reverse(lists:sort(filelib:wildcard(Pattern))),
ok = lists:foreach(fun rm/1, Contents),
file:del_dir(Path);
false ->
file:delete(Path)
end.
-spec rm(file:filename()) -> ok | {error, file:posix()}.
%% @private
%% An omnibus delete helper.
rm(Path) ->
case filelib:is_dir(Path) of
true -> file:del_dir(Path);
false -> file:delete(Path)
end.
%%% Input argument mangling %%% Input argument mangling
@ -3225,4 +3313,4 @@ log(Level, Format, Args) ->
warning -> "[WARNING]"; warning -> "[WARNING]";
error -> "[ERROR]" error -> "[ERROR]"
end, end,
io:format("~p ~s: " ++ Format ++ "~n", [self(), Tag | Args]). io:format("~s ~p: " ++ Format ++ "~n", [Tag, self() | Args]).