about summary refs log tree commit diff
path: root/nixos/modules/services/misc/rippled.nix
blob: 2c1fec9f6d7cc839cc79f51ae0c9f662235ddc83 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
# configuration building is commented out until better tested.

{ config, lib, pkgs, ... }:

with lib;

let
  cfg = config.services.rippled;

  rippledStateCfgFile = "/var/lib/rippled/rippled.cfg";

  rippledCfg = ''
    [node_db]
    type=HyperLevelDB
    path=/var/lib/rippled/db/hyperldb

    [debug_logfile]
    /var/log/rippled/debug.log

  ''
  + optionalString (cfg.peerIp != null) ''
    [peer_ip]
    ${cfg.peerIp}

    [peer_port]
    ${toString cfg.peerPort}

  ''
  + cfg.extraConfig;

  rippledCfgFile = pkgs.writeText "rippled.cfg" rippledCfg;
    
in

{

  ###### interface

  options = {

    services.rippled = {

      enable = mkOption {
        default = false;
	description = "Whether to enable 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.
      #

      /*
      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: 
	  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 
	  to least trusted.
	'';
      };

      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 
	  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.
        '';
      };

      peerPort = mkOption {
	default = 51235;
	description = ''
	  If peerIp is supplied, corresponding port to bind to for peer connections.
	'';
      };

      /*
      peerPortProxy = mkOption {
        type = types.int;
	example = 51236;
	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.
	'';
      };

      peerPrivate = mkOption {
        default = null;
	example = 0;
	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.
	'';
      };

      rpcPort = mkOption {
        type = types.int;
        description = ''
          If rpcIp is supplied, corresponding port to bind to for peer connections.
        '';
      };

      rpcUser = mkOption {
        type = types.str;
	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.
	'';
      };

      rpcPassword = mkOption {
        type = types.str;
	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.
	'';
      };

      rpcStartup = mkOption {
        example = [ ''"command" : "log_level"'' ''"partition" : "ripplecalc"'' ''"severity" : "trace"'' ];
	description = "List of RPC commands to run at startup.";
      };

      rpcSecure = mkOption {
        default = false;
	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
	'';
      };
      */

      extraConfig = mkOption {
        default = "";
	description = ''
	  Extra lines to be added verbatim to the rippled.cfg configuration file.
	'';
      };

    };

  };


  ###### implementation

  config = mkIf cfg.enable {

    users.extraUsers = singleton
      { name = "rippled";
        description = "Ripple server user";
        uid = config.ids.uids.rippled;
	home = "/var/lib/rippled";
      };

    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";
      };
    };

    networking.firewall.allowedTCPPorts = mkIf (cfg.peerIp != null) [ cfg.peerPort ];

    system.activationScripts.rippled = ''
      mkdir -p /var/{lib,log}/rippled
      chown -R rippled /var/{lib,log}/rippled
      ln -sf ${rippledCfgFile} ${rippledStateCfgFile}
    '';
  };
}