rippled: expose more options, make compatible with new rippled version

This commit is contained in:
Jaka Hudoklin 2015-03-21 12:49:52 +01:00
parent 37c95d6a48
commit 4947bacd52

View File

@ -1,5 +1,3 @@
# configuration building is commented out until better tested.
{ config, lib, pkgs, ... }:
with lib;
@ -7,29 +5,189 @@ with lib;
let
cfg = config.services.rippled;
rippledStateCfgFile = "/var/lib/rippled/rippled.cfg";
b2i = val: if val then "1" else "0";
dbCfg = db: ''
type=${db.type}
path=${db.path}
${optionalString (db.compression != null) ("compression=${b2i db.compression}") }
${optionalString (db.onlineDelete != null) ("online_delete=${toString db.onlineDelete}")}
${optionalString (db.advisoryDelete != null) ("advisory_delete=${toString db.advisoryDelete}")}
${db.extraOpts}
'';
rippledCfg = ''
[server]
${concatMapStringsSep "\n" (n: "port_${n}") (attrNames cfg.ports)}
${concatMapStrings (p: ''
[port_${p.name}]
ip=${p.ip}
port=${toString p.port}
protocol=${concatStringsSep "," p.protocol}
${optionalString (p.user != "") "user=${p.user}"}
${optionalString (p.password != "") "user=${p.password}"}
admin=${if p.admin then "allow" else "no"}
${optionalString (p.ssl.key != null) "ssl_key=${p.ssl.key}"}
${optionalString (p.ssl.cert != null) "ssl_cert=${p.ssl.cert}"}
${optionalString (p.ssl.chain != null) "ssl_chain=${p.ssl.chain}"}
'') (attrValues cfg.ports)}
[database_path]
${cfg.databasePath}
[node_db]
type=HyperLevelDB
path=/var/lib/rippled/db/hyperldb
${dbCfg cfg.nodeDb}
[debug_logfile]
/var/log/rippled/debug.log
${optionalString (cfg.tempDb != null) ''
[temp_db]
${dbCfg cfg.tempDb}''}
''
+ optionalString (cfg.peerIp != null) ''
[peer_ip]
${cfg.peerIp}
${optionalString (cfg.importDb != null) ''
[import_db]
${dbCfg cfg.importDb}''}
[peer_port]
${toString cfg.peerPort}
[ips]
${concatStringsSep "\n" cfg.ips}
''
+ cfg.extraConfig;
[ips_fixed]
${concatStringsSep "\n" cfg.ipsFixed}
[validators]
${concatStringsSep "\n" cfg.validators}
[node_size]
${cfg.nodeSize}
[ledger_history]
${toString cfg.ledgerHistory}
[fetch_depth]
${toString cfg.fetchDepth}
[validation_quorum]
${toString cfg.validationQuorum}
[sntp_servers]
${concatStringsSep "\n" cfg.sntpServers}
[rpc_startup]
{ "command": "log_level", "severity": "${cfg.logLevel}" }
'' + cfg.extraConfig;
portOptions = { name, ...}: {
options = {
name = mkOption {
internal = true;
default = name;
};
ip = mkOption {
default = "127.0.0.1";
description = "Ip where rippled listens.";
type = types.str;
};
port = mkOption {
description = "Port where rippled listens.";
type = types.int;
};
protocol = mkOption {
description = "Protocols expose by rippled.";
type = types.listOf (types.enum ["http" "https" "ws" "wss" "peer"]);
};
user = mkOption {
description = "When set, these credentials will be required on HTTP/S requests.";
type = types.str;
default = "";
};
password = mkOption {
description = "When set, these credentials will be required on HTTP/S requests.";
type = types.str;
default = "";
};
admin = mkOption {
description = "Controls whether or not administrative commands are allowed.";
type = types.bool;
default = false;
};
ssl = {
key = mkOption {
description = ''
Specifies the filename holding the SSL key in PEM format.
'';
default = null;
type = types.nullOr types.path;
};
cert = mkOption {
description = ''
Specifies the path to the SSL certificate file in PEM format.
This is not needed if the chain includes it.
'';
default = null;
type = types.nullOr types.path;
};
chain = mkOption {
description = ''
If you need a certificate chain, specify the path to the
certificate chain here. The chain may include the end certificate.
'';
default = null;
type = types.nullOr types.path;
};
};
};
};
dbOptions = {
type = mkOption {
description = "Rippled database type.";
type = types.enum ["rocksdb" "nudb" "sqlite"];
default = "rocksdb";
};
path = mkOption {
description = "Location to store the database.";
type = types.path;
default = cfg.databasePath;
};
compression = mkOption {
description = "Whether to enable snappy compression.";
type = types.nullOr types.bool;
default = null;
};
onlineDelete = mkOption {
description = "Enable automatic purging of older ledger information.";
type = types.addCheck (types.nullOr types.int) (v: v > 256);
default = cfg.ledgerHistory;
};
advisoryDelete = mkOption {
description = ''
If set, then require administrative RPC call "can_delete"
to enable online deletion of ledger records.
'';
type = types.nullOr types.bool;
default = null;
};
extraOpts = mkOption {
description = "Extra database options.";
type = types.lines;
default = "";
};
};
rippledCfgFile = pkgs.writeText "rippled.cfg" rippledCfg;
in
{
@ -37,236 +195,176 @@ in
###### interface
options = {
services.rippled = {
enable = mkEnableOption "Whether to enable rippled";
enable = mkOption {
default = false;
description = "Whether to enable rippled";
package = mkOption {
description = "Which rippled package to use.";
type = types.package;
default = pkgs.rippled;
};
#
# Rippled has a simple configuration file layout that is easy to
# build with nix. Many of the options are defined here but are
# commented out until the code to append them to the config above
# is written and they are tested.
#
# If you find a yourself implementing more options, please submit a
# pull request.
#
ports = mkOption {
description = "Ports exposed by rippled";
type = types.attrsOf types.optionSet;
options = [portOptions];
default = {
rpc = {
port = 5005;
admin = true;
protocol = ["http"];
};
peer = {
port = 51235;
ip = "0.0.0.0";
protocol = ["peer"];
};
ws_public = {
port = 5006;
ip = "0.0.0.0";
protocol = ["ws" "wss"];
};
};
};
nodeDb = mkOption {
description = "Rippled main database options.";
type = types.nullOr types.optionSet;
options = [dbOptions];
default = {
type = "rocksdb";
extraOpts = ''
open_files=2000
filter_bits=12
cache_mb=256
file_size_pb=8
file_size_mult=2;
'';
};
};
tempDb = mkOption {
description = "Rippled temporary database options.";
type = types.nullOr types.optionSet;
options = [dbOptions];
default = null;
};
importDb = mkOption {
description = "Settings for performing a one-time import.";
type = types.nullOr types.optionSet;
options = [dbOptions];
default = null;
};
nodeSize = mkOption {
description = ''
Rippled size of the node you are running.
"tiny", "small", "medium", "large", and "huge"
'';
type = types.enum ["tiny" "small" "medium" "large" "huge"];
default = "small";
};
/*
ips = mkOption {
default = [ "r.ripple.com 51235" ];
example = [ "192.168.0.1" "192.168.0.1 3939" "r.ripple.com 51235" ];
description = ''
List of hostnames or ips where the Ripple protocol is served.
For a starter list, you can either copy entries from:
For a starter list, you can either copy entries from:
https://ripple.com/ripple.txt or if you prefer you can let it
default to r.ripple.com 51235
A port may optionally be specified after adding a space to the
address. By convention, if known, IPs are listed in from most
A port may optionally be specified after adding a space to the
address. By convention, if known, IPs are listed in from most
to least trusted.
'';
type = types.listOf types.str;
default = ["r.ripple.com 51235"];
};
ipsFixed = mkOption {
default = null;
example = [ "192.168.0.1" "192.168.0.1 3939" "r.ripple.com 51235" ];
description = ''
List of IP addresses or hostnames to which rippled should always
attempt to maintain peer connections with. This is useful for
manually forming private networks, for example to configure a
validation server that connects to the Ripple network through a
List of IP addresses or hostnames to which rippled should always
attempt to maintain peer connections with. This is useful for
manually forming private networks, for example to configure a
validation server that connects to the Ripple network through a
public-facing server, or for building a set of cluster peers.
A port may optionally be specified after adding a space to the address
'';
};
*/
peerIp = mkOption {
default = null;
example = "0.0.0.0";
description = ''
IP address or domain to bind to allow external connections from peers.
Defaults to not binding, which disallows external connections from peers.
'';
type = types.listOf types.str;
default = [];
};
peerPort = mkOption {
default = 51235;
validators = mkOption {
description = ''
If peerIp is supplied, corresponding port to bind to for peer connections.
List of nodes to always accept as validators. Nodes are specified by domain
or public key.
'';
type = types.listOf types.str;
default = [
"n949f75evCHwgyP4fPVgaHqNHxUVN15PsJEZ3B3HnXPcPjcZAoy7 RL1"
"n9MD5h24qrQqiyBC8aeqqCWvpiBiYQ3jxSr91uiDvmrkyHRdYLUj RL2"
"n9L81uNCaPgtUJfaHh89gmdvXKAmSt5Gdsw2g1iPWaPkAHW5Nm4C RL3"
"n9KiYM9CgngLvtRCQHZwgC2gjpdaZcCcbt3VboxiNFcKuwFVujzS RL4"
"n9LdgEtkmGB9E2h3K4Vp7iGUaKuq23Zr32ehxiU8FWY7xoxbWTSA RL5"
];
};
/*
peerPortProxy = mkOption {
type = types.int;
example = 51236;
databasePath = mkOption {
description = ''
An optional, additional listening port number for peers. Incoming
connections on this port will be required to provide a PROXY Protocol
handshake, described in this document (external link):
http://haproxy.1wt.eu/download/1.5/doc/proxy-protocol.txt
The PROXY Protocol is a popular method used by elastic load balancing
service providers such as Amazon, to identify the true IP address and
port number of external incoming connections.
In addition to enabling this setting, it will also be required to
use your provider-specific control panel or administrative web page
to configure your server instance to receive PROXY Protocol handshakes,
and also to restrict access to your instance to the Elastic Load Balancer.
Path to the ripple database.
'';
type = types.path;
default = "/var/lib/rippled/db";
};
peerPrivate = mkOption {
default = null;
example = 0;
validationQuorum = mkOption {
description = ''
0: Request peers to broadcast your address. Normal outbound peer connections [default]
1: Request peers not broadcast your address. Only connect to configured peers.
'';
};
peerSslCipherList = mkOption {
default = null;
example = "ALL:!LOW:!EXP:!MD5:@STRENGTH";
description = ''
A colon delimited string with the allowed SSL cipher modes for peer. The
choices for for ciphers are defined by the OpenSSL API function
SSL_CTX_set_cipher_list, documented here (external link):
http://pic.dhe.ibm.com/infocenter/tpfhelp/current/index.jsp?topic=%2Fcom.ibm.ztpf-ztpfdf.doc_put.cur%2Fgtpc2%2Fcpp_ssl_ctx_set_cipher_list.html
The default setting of "ALL:!LOW:!EXP:!MD5:@STRENGTH", which allows
non-authenticated peer connections (they are, however, secure).
'';
};
nodeSeed = mkOption {
default = null;
example = "RASH BUSH MILK LOOK BAD BRIM AVID GAFF BAIT ROT POD LOVE";
description = ''
This is used for clustering. To force a particular node seed or key, the
key can be set here. The format is the same as the validation_seed field.
To obtain a validation seed, use the rippled validation_create command.
'';
};
clusterNodes = mkOption {
default = null;
example = [ "n9KorY8QtTdRx7TVDpwnG9NvyxsDwHUKUEeDLY3AkiGncVaSXZi5" ];
description = ''
To extend full trust to other nodes, place their node public keys here.
Generally, you should only do this for nodes under common administration.
Node public keys start with an 'n'. To give a node a name for identification
place a space after the public key and then the name.
'';
};
sntpServers = mkOption {
default = null;
example = [ "time.nist.gov" "pool.ntp.org" ];
description = ''
IP address or domain of NTP servers to use for time synchronization.
'';
};
# TODO: websocket options
rpcAllowRemote = mkOption {
default = false;
description = ''
false: Allow RPC connections only from 127.0.0.1. [default]
true: Allow RPC connections from any IP.
'';
};
rpcAdminAllow = mkOption {
example = [ "10.0.0.4" ];
description = ''
List of IP addresses allowed to have admin access.
'';
};
rpcAdminUser = mkOption {
type = types.str;
description = ''
As a server, require this as the admin user to be specified. Also, require
rpc_admin_user and rpc_admin_password to be checked for RPC admin functions.
The request must specify these as the admin_user and admin_password in the
request object.
'';
};
rpcAdminPassword = mkOption {
type = types.str;
description = ''
As a server, require this as the admin pasword to be specified. Also,
require rpc_admin_user and rpc_admin_password to be checked for RPC admin
functions. The request must specify these as the admin_user and
admin_password in the request object.
'';
};
rpcIp = mkOption {
type = types.str;
description = ''
IP address or domain to bind to allow insecure RPC connections.
Defaults to not binding, which disallows RPC connections.
The minimum number of trusted validations a ledger must have before
the server considers it fully validated.
'';
type = types.int;
default = 3;
};
rpcPort = mkOption {
type = types.int;
description = ''
If rpcIp is supplied, corresponding port to bind to for peer connections.
'';
};
rpcUser = mkOption {
type = types.str;
ledgerHistory = mkOption {
description = ''
Require a this user to specified and require rpcPassword to
be checked for RPC access via the rpcIp and rpcPort. The user and password
must be specified via HTTP's basic authentication method.
As a client, supply this to the server via HTTP's basic authentication
method.
The number of past ledgers to acquire on server startup and the minimum
to maintain while running.
'';
type = types.either types.int (types.enum ["full"]);
default = 1296000; # 1 month
};
rpcPassword = mkOption {
type = types.str;
fetchDepth = mkOption {
description = ''
Require a this password to specified and require rpc_user to
be checked for RPC access via the rpcIp and rpcPort. The user and password
must be specified via HTTP's basic authentication method.
As a client, supply this to the server via HTTP's basic authentication
method.
The number of past ledgers to serve to other peers that request historical
ledger data (or "full" for no limit).
'';
type = types.either types.int (types.enum ["full"]);
default = "full";
};
rpcStartup = mkOption {
example = [ ''"command" : "log_level"'' ''"partition" : "ripplecalc"'' ''"severity" : "trace"'' ];
description = "List of RPC commands to run at startup.";
};
rpcSecure = mkOption {
default = false;
sntpServers = mkOption {
description = ''
false: Server certificates are not provided for RPC clients using SSL [default]
true: Client RPC connections wil be provided with SSL certificates.
Note that if rpc_secure is enabled, it will also be necessasry to configure the
certificate file settings located in rpcSslCert, rpcSslChain, and rpcSslKey
IP address or domain of NTP servers to use for time synchronization.;
'';
type = types.listOf types.str;
default = [
"time.windows.com"
"time.apple.com"
"time.nist.gov"
"pool.ntp.org"
];
};
logLevel = mkOption {
description = "Logging verbosity.";
type = types.enum ["debug" "error" "info"];
default = "error";
};
*/
extraConfig = mkOption {
default = "";
@ -275,8 +373,11 @@ in
'';
};
config = mkOption {
internal = true;
default = pkgs.writeText "rippled.conf" rippledCfg;
};
};
};
@ -288,27 +389,21 @@ in
{ name = "rippled";
description = "Ripple server user";
uid = config.ids.uids.rippled;
home = "/var/lib/rippled";
home = cfg.databasePath;
createHome = true;
};
systemd.services.rippled = {
path = [ pkgs.rippled ];
after = [ "network.target" ];
wantedBy = [ "multi-user.target" ];
serviceConfig = {
ExecStart = "${pkgs.rippled}/bin/rippled --fg -q --conf ${rippledStateCfgFile}";
WorkingDirectory = "/var/lib/rippled";
ExecStart = "${cfg.package}/bin/rippled --fg --conf ${cfg.config}";
User = "rippled";
};
};
networking.firewall.allowedTCPPorts = mkIf (cfg.peerIp != null) [ cfg.peerPort ];
environment.systemPackages = [ cfg.package ];
system.activationScripts.rippled = ''
mkdir -p /var/{lib,log}/rippled
chown -R rippled /var/{lib,log}/rippled
ln -sf ${rippledCfgFile} ${rippledStateCfgFile}
'';
};
}