1 (* OpenVPN module for Augeas
2 Author: Raphael Pinson <raphink@gmail.com>
3 Author: Justin Akers <dafugg@gmail.com>
5 Reference: http://openvpn.net/index.php/documentation/howto.html
6 Reference: https://community.openvpn.net/openvpn/wiki/Openvpn23ManPage
8 TODO: Inline file support
15 (************************************************************************
17 *************************************************************************)
20 let indent = Util.indent
22 (* Define separators *)
23 let sep = Util.del_ws_spc
25 (* Define value regexps.
26 Custom simplified ipv6 used instead of Rx.ipv6 as the augeas Travis instances
27 are limited to 2GB of memory. Using 'ipv6_re = Rx.ipv6' consumes an extra
28 2GB of memory and thus the test is OOM-killed.
30 let ipv6_re = /[0-9A-Fa-f:]+/
32 let ip_re = ipv4_re|ipv6_re
33 let num_re = Rx.integer
34 let fn_re = /[^#; \t\n][^#;\n]*[^#; \t\n]|[^#; \t\n]/
35 let fn_safe_re = /[^#; \t\r\n]+/
36 let an_re = /[a-z][a-z0-9_-]*/
37 let hn_re = Rx.hostname
38 let port_re = /[0-9]+/
39 let host_re = ip_re|hn_re
40 let proto_re = /(tcp|udp)/
41 let proto_ext_re = /(udp|tcp-client|tcp-server)/
42 let alg_re = /(none|[A-Za-z][A-Za-z0-9-]+)/
43 let ipv6_bits_re = ipv6_re . /\/[0-9]+/
45 (* Define store aliases *)
47 let num = store num_re
48 let filename = store fn_re
49 let filename_safe = store fn_safe_re
50 let hostname = store hn_re
51 let sto_to_dquote = store /[^"\n]+/ (* " Emacs, relax *)
52 let port = store port_re
53 let host = store host_re
54 let proto = store proto_re
55 let proto_ext = store proto_ext_re
57 (* define comments and empty lines *)
58 let comment = Util.comment_generic /[ \t]*[;#][ \t]*/ "# "
59 let comment_or_eol = eol | Util.comment_generic /[ \t]*[;#][ \t]*/ " # "
61 let empty = Util.empty
64 (************************************************************************
67 * - local => IP|hostname
69 * - proto => udp|tcp-client|tcp-server
70 * - proto-force => udp|tcp
71 * - mode => p2p|server
72 * - dev => (tun|tap)\d*
73 * - dev-node => filename
75 * - config => filename
79 * - ifconfig-pool-persist => filename
80 * - learn-address => filename
81 * - cipher => [A-Z0-9-]+
82 * - max-clients => num
85 * - status => filename
87 * - log-append => filename
88 * - client-config-dir => filename
94 * - connect-retry-max num
95 * - connect-timeout num
96 * - http-proxy-timeout num
98 * - ns-cert-type => "server"
99 * - resolv-retry => "infinite"
100 * - script-security => [0-3] (execve|system)?
101 * - ipchange => command
103 *************************************************************************)
105 let single_host = "local" | "tls-remote"
106 let single_ip = "lladdr"
107 let single_ipv6_bits = "iroute-ipv6"
109 | "ifconfig-ipv6-pool"
110 let single_num = "port"
117 | "connect-retry-max"
119 | "http-proxy-timeout"
124 | "max-routes-per-client"
137 | "management-log-cache"
140 | "server-poll-timeout"
157 | "client-config-dir"
162 | "client-config-dir"
172 let single_an = "user"
174 | "management-client-user"
175 | "management-client-group"
176 let single_cmd = "ipchange"
186 | "client-disconnect"
190 let single_entry (kw:regexp) (re:regexp)
191 = [ key kw . sep . store re . comment_or_eol ]
193 let single_opt_entry (kw:regexp) (re:regexp)
194 = [ key kw . (sep . store re)? .comment_or_eol ]
196 let single = single_entry single_num num_re
197 | single_entry single_fn fn_re
198 | single_entry single_an an_re
199 | single_entry single_host host_re
200 | single_entry single_ip ip_re
201 | single_entry single_ipv6_bits ipv6_bits_re
202 | single_entry single_cmd fn_re
203 | single_entry "proto" proto_ext_re
204 | single_entry "proto-force" proto_re
205 | single_entry "mode" /(p2p|server)/
206 | single_entry "dev" /(tun|tap)[0-9]*|null/
207 | single_entry "dev-type" /(tun|tap)/
208 | single_entry "topology" /(net30|p2p|subnet)/
209 | single_entry "cipher" alg_re
210 | single_entry "auth" alg_re
211 | single_entry "resolv-retry" "infinite"
212 | single_entry "script-security" /[0-3]( execve| system)?/
213 | single_entry "route-gateway" (host_re|/dhcp/)
214 | single_entry "mtu-disc" /(no|maybe|yes)/
215 | single_entry "remap-usr1" /SIG(HUP|TERM)/
216 | single_entry "socket-flags" /(TCP_NODELAY)/
217 | single_entry "auth-retry" /(none|nointeract|interact)/
218 | single_entry "tls-version-max" Rx.decimal
219 | single_entry "verify-hash" /([A-Za-z0-9]{2}:)+[A-Za-z0-9]{2}/
220 | single_entry "pkcs11-cert-private" /[01]/
221 | single_entry "pkcs11-protected-authentication" /[01]/
222 | single_entry "pkcs11-private-mode" /[A-Za-z0-9]+/
223 | single_entry "key-method" /[12]/
224 | single_entry "ns-cert-type" /(client|server)/
225 | single_entry "remote-cert-tls" /(client|server)/
227 let single_opt = single_opt_entry "comp-lzo" /(yes|no|adaptive)/
228 | single_opt_entry "syslog" fn_re
229 | single_opt_entry "daemon" fn_re
230 | single_opt_entry "auth-user-pass" fn_re
231 | single_opt_entry "explicit-exit-notify" num_re
232 | single_opt_entry "engine" fn_re
234 (************************************************************************
236 *************************************************************************)
238 let double_entry (kw:regexp) (a:string) (aval:regexp) (b:string) (bval:regexp)
240 . sep . [ label a . store aval ]
241 . sep . [ label b . store bval ]
245 let double_secopt_entry (kw:regexp) (a:string) (aval:regexp) (b:string) (bval:regexp)
247 . sep . [ label a . store aval ]
248 . (sep . [ label b . store bval ])?
253 let double = double_entry "keepalive" "ping" num_re "timeout" num_re
254 | double_entry "hash-size" "real" num_re "virtual" num_re
255 | double_entry "ifconfig" "local" ip_re "remote" ip_re
256 | double_entry "connect-freq" "num" num_re "sec" num_re
257 | double_entry "verify-x509-name" "name" hn_re "type"
258 /(subject|name|name-prefix)/
259 | double_entry "ifconfig-ipv6" "address" ipv6_bits_re "remote" ipv6_re
260 | double_entry "ifconfig-ipv6-push" "address" ipv6_bits_re "remote" ipv6_re
261 | double_secopt_entry "iroute" "local" ip_re "netmask" ip_re
262 | double_secopt_entry "stale-routes-check" "age" num_re "interval" num_re
263 | double_secopt_entry "ifconfig-pool-persist"
264 "file" fn_safe_re "seconds" num_re
265 | double_secopt_entry "secret" "file" fn_safe_re "direction" /[01]/
266 | double_secopt_entry "prng" "algorithm" alg_re "nsl" num_re
267 | double_secopt_entry "replay-window" "window-size" num_re "seconds" num_re
270 (************************************************************************
272 *************************************************************************)
274 let flag_words = "client-to-client"
281 | "mute-replay-warnings"
283 | "socks-proxy-retry"
284 | "remote-random-hostname"
285 | "show-proxy-settings"
299 | "persist-remote-ip"
307 | "suppress-timestamps"
311 | "management-client"
312 | "management-query-passwords"
313 | "management-query-proxy"
314 | "management-query-remote"
315 | "management-forget-disconnect"
317 | "management-signal"
318 | "management-up-down"
319 | "management-client-auth"
320 | "management-client-pf"
324 | "ifconfig-pool-linear"
330 | "auth-user-pass-optional"
331 | "client-cert-not-required"
332 | "username-as-common-name"
336 | "mute-replay-warnings"
338 | "use-prediction-resistance"
342 | "pkcs11-id-management"
355 let flag_entry (kw:regexp)
356 = [ key kw . comment_or_eol ]
358 let flag = flag_entry flag_words
361 (************************************************************************
364 * - server => IP IP [nopool]
365 * - server-bridge => IP IP IP IP
366 * - route => host host [host [num]]
368 * - tls-auth => filename [01]
369 * - remote => hostname/IP [num] [(tcp|udp)]
370 * - management => IP num filename
371 * - http-proxy => host port [filename|keyword] [method]
372 * - http-proxy-option => (VERSION decimal|AGENT string)
376 *************************************************************************)
378 let server = [ key "server"
379 . sep . [ label "address" . ip ]
380 . sep . [ label "netmask" . ip ]
381 . (sep . [ key "nopool" ]) ?
386 let ip_params = [ label "address" . ip ] . sep
387 . [ label "netmask" . ip ] . sep
388 . [ label "start" . ip ] . sep
389 . [ label "end" . ip ] in
390 [ key "server-bridge"
391 . sep . (ip_params|store /(nogw)/)
396 let route_net_kw = store (/(vpn_gateway|net_gateway|remote_host)/|host_re) in
398 . [ label "address" . route_net_kw ]
399 . (sep . [ label "netmask" . store (ip_re|/default/) ]
400 . (sep . [ label "gateway" . route_net_kw ]
401 . (sep . [ label "metric" . store (/default/|num_re)] )?
408 let route_net_re = /(vpn_gateway|net_gateway|remote_host)/ in
409 [ key "route-ipv6" . sep
410 . [ label "network" . store (route_net_re|ipv6_bits_re) ]
411 . (sep . [ label "gateway" . store (route_net_re|ipv6_re) ]
412 . (sep . [ label "metric" . store (/default/|num_re)] )?
417 let push = [ key "push" . sep
418 . Quote.do_dquote sto_to_dquote
422 let tls_auth = [ key "tls-auth" . sep
423 . [ label "key" . filename ] . sep
424 . [ label "is_client" . store /[01]/ ] . comment_or_eol
427 let remote = [ key "remote" . sep
428 . [ label "server" . host ]
429 . (sep . [label "port" . port]
430 . (sep . [label "proto" . proto]) ? ) ?
435 let auth_method_re = /(none|basic|ntlm)/ in
436 let auth_method = store auth_method_re in
438 . sep . [ label "server" . host ]
439 . sep . [ label "port" . port ]
440 . (sep . [ label "auth" . filename_safe ]
441 . (sep . [ label "auth-method" . auth_method ]) ? )?
445 let http_proxy_option = [ key "http-proxy-option"
446 . sep . [ label "option" . store /(VERSION|AGENT)/ ]
447 . sep . [ label "value" . filename ]
451 let socks_proxy = [ key "socks-proxy"
452 . sep . [ label "server" . host ]
453 . (sep . [ label "port" . port ]
454 . (sep . [ label "auth" . filename_safe ])? )?
458 let port_share = [ key "port-share"
459 . sep . [ label "host" . host ]
460 . sep . [ label "port" . port ]
461 . (sep . [ label "dir" . filename ])?
465 let route_delay = [ key "route-delay"
466 . (sep . [ label "seconds" . num ]
467 . (sep . [ label "win-seconds" . num ] ) ?
472 let inetd = [ key "inetd"
473 . (sep . [label "mode" . store /(wait|nowait)/ ]
474 . (sep . [ label "progname" . filename ] ) ?
479 let inactive = [ key "inactive"
480 . sep . [ label "seconds" . num ]
481 . (sep . [ label "bytes" . num ] ) ?
485 let client_nat = [ key "client-nat"
486 . sep . [ label "type" . store /(snat|dnat)/ ]
487 . sep . [ label "network" . ip ]
488 . sep . [ label "netmask" . ip ]
489 . sep . [ label "alias" . ip ]
493 let status = [ key "status"
494 . sep . [ label "file" . filename_safe ]
495 . (sep . [ label "repeat-seconds" . num ]) ?
499 let plugin = [ key "plugin"
500 . sep . [ label "file" . filename_safe ]
501 . (sep . [ label "init-string" . filename ]) ?
505 let management = [ key "management" . sep
506 . [ label "server" . ip ]
507 . sep . [ label "port" . port ]
508 . (sep . [ label "pwfile" . filename ] ) ?
512 let auth_user_pass_verify = [ key "auth-user-pass-verify"
513 . sep . [ Quote.quote_spaces (label "command") ]
514 . sep . [ label "method" . store /via-(env|file)/ ]
518 let static_challenge = [ key "static-challenge"
519 . sep . [ Quote.quote_spaces (label "text") ]
520 . sep . [ label "echo" . store /[01]/ ]
524 let cryptoapicert = [ key "cryptoapicert" . sep . Quote.dquote
525 . [ key /[A-Z]+/ . Sep.colon . store /[A-Za-z _-]+/ ]
526 . Quote.dquote . comment_or_eol
530 let envvar = /[^#;\/ \t\n][A-Za-z0-9_-]+/ in
531 [ key ("setenv"|"setenv-safe")
532 . sep . [ key envvar . sep . store fn_re ]
537 let redirect_flag = /(local|autolocal|def1|bypass-dhcp|bypass-dns|block-local)/ in
538 let redirect_key = "redirect-gateway" | "redirect-private" in
540 . (sep . [ label "flag" . store redirect_flag ] ) +
545 let ciphername = /[A-Za-z0-9!_-]+/ in
546 [ key "tls-cipher" . sep
547 . [label "cipher" . store ciphername]
548 . (Sep.colon . [label "cipher" . store ciphername])*
553 let usage = [label "usage" . store /[A-Za-z0-9]{1,2}/] in
554 [ key "remote-cert-ku" . sep . usage . (sep . usage)* . comment_or_eol ]
556 (* FIXME: Surely there's a nicer way to do this *)
557 let remote_cert_eku =
558 let oid = [label "oid" . store /[0-9]+\.([0-9]+\.)*[0-9]+/] in
559 let symbolic = [Quote.do_quote_opt
560 (label "symbol" . store /[A-Za-z0-9][A-Za-z0-9 _-]*[A-Za-z0-9]/)] in
561 [ key "remote-cert-eku" . sep . (oid|symbolic) . comment_or_eol ]
563 let status_version = [ key "status-version"
568 let ifconfig_pool = [ key "ifconfig-pool"
569 . sep . [ label "start" . ip ]
570 . sep . [ label "end" . ip ]
571 . (sep . [ label "netmask" . ip ])?
575 let ifconfig_push = [ key "ifconfig-push"
576 . sep . [ label "local" . ip ]
577 . sep . [ label "remote-netmask" . ip ]
578 . (sep . [ label "alias" . store /[A-Za-z0-9_-]+/ ] )?
582 let ignore_unknown_option = [ key "ignore-unknown-option"
583 . (sep . [ label "opt" . store /[A-Za-z0-9_-]+/ ] ) +
587 let tls_version_min = [ key "tls-version-min"
588 . sep . store Rx.decimal
589 . (sep . [ key "or-highest" ]) ?
593 let crl_verify = [ key "crl-verify"
594 . sep . filename_safe
595 . (sep . [ key "dir" ]) ?
599 let x509_username_field =
600 let fieldname = /[A-Za-z0-9_-]+/ in
601 let extfield = ([key /ext/ . Sep.colon . store fieldname]) in
602 let subjfield = ([label "subj" . store fieldname]) in
603 [ key "x509-username-field"
604 . sep . (extfield|subjfield)
629 | ignore_unknown_option
630 | auth_user_pass_verify
636 | x509_username_field
643 (************************************************************************
645 *************************************************************************)
647 let lns = ( comment | empty | single | single_opt | double | flag | other )*
649 let filter = (incl "/etc/openvpn/client.conf")
650 . (incl "/etc/openvpn/server.conf")
652 let xfm = transform lns filter