From 272939aad601f7a0c736449edcfcc64dffe0a370 Mon Sep 17 00:00:00 2001 From: Julien Lepiller Date: Tue, 18 Oct 2016 23:16:31 +0200 Subject: [PATCH] gnu: Add openvpn services * gnu/services/vpn.scm: new file. * gnu/local.mk(GNU_SYSTEM_MODULES): add it --- gnu/local.mk | 1 + gnu/services/vpn.scm | 289 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 290 insertions(+) create mode 100644 gnu/services/vpn.scm diff --git a/gnu/local.mk b/gnu/local.mk index 18ba0c2..5c7ab97 100644 --- a/gnu/local.mk +++ b/gnu/local.mk @@ -406,6 +406,7 @@ GNU_SYSTEM_MODULES = \ %D%/services/sddm.scm \ %D%/services/spice.scm \ %D%/services/ssh.scm \ + %D%/services/vpn.scm \ %D%/services/web.scm \ %D%/services/xorg.scm \ \ diff --git a/gnu/services/vpn.scm b/gnu/services/vpn.scm new file mode 100644 index 0000000..8c6b720 --- /dev/null +++ b/gnu/services/vpn.scm @@ -0,0 +1,289 @@ +;;; GNU Guix --- Functional package management for GNU +;;; Copyright © 2016 Julien Lepiller +;;; +;;; This file is part of GNU Guix. +;;; +;;; GNU Guix is free software; you can redistribute it and/or modify it +;;; under the terms of the GNU General Public License as published by +;;; the Free Software Foundation; either version 3 of the License, or (at +;;; your option) any later version. +;;; +;;; GNU Guix is distributed in the hope that it will be useful, but +;;; WITHOUT ANY WARRANTY; without even the implied warranty of +;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;;; GNU General Public License for more details. +;;; +;;; You should have received a copy of the GNU General Public License +;;; along with GNU Guix. If not, see . + +(define-module (gnu services vpn) + #:use-module (gnu packages vpn) + #:use-module (gnu packages admin) + #:use-module (gnu services) + #:use-module (gnu services shepherd) + #:use-module (gnu system pam) + #:use-module (gnu system shadow) + #:use-module (guix gexp) + #:use-module (guix records) + #:use-module (srfi srfi-26) + #:use-module (ice-9 match) + #:export (openvpn-configuration + openvpn-configuration? + openvpn-client-service-type + openvpn-client-service + openvpn-server-service-type + openvpn-server-service + + openvpn-remote-server)) + +;;; +;;; OpenVPN. +;;; + +(define-record-type* + openvpn-remote-server make-openvpn-remote-server + openvpn-remote-server? + (name openvpn-remote-server-name + (default "my-server")) ; string (domain name or ip) + (port openvpn-remote-server-port + (default 1194))) ; int + +;; TODO: actually use this structure or something similar +(define-record-type* + openvpn-client-ccd make-openvpn-client-ccd + openvpn-client-ccd? + (name openvpn-client-cdd-name + (default "")) ; string + (subnet openvpn-client-ccd-subnet + (default #f)) ; string + (ip openvpn-client-ccd-ip + (default #f))) ; string + +;; openvpn can be started as server or client. Part of the configuration +;; is common to the two modes of operation, and some are specific to +;; the server. +;; TODO: add a list of clients to the server configuration that are +;; of a specific record type, so we can push their route (if required) +;; or add entries in ccd. +;; TODO: add a ccd configuration for the server +(define-record-type* + openvpn-configuration make-openvpn-configuration + openvpn-configuration? + ;; common configuration options + (pid-file openvpn-configuration-pid-file + (default "/var/run/openvpn/openvpn.pid")) + (proto openvpn-configuration-proto + (default 'udp)) ; 'udp | 'tcp + (dev openvpn-configuration-dev + (default 'tun)) ; 'tun | 'tap + (ca openvpn-configuration-ca + (default "/etc/openvpn/ca.crt")) ; string + (cert openvpn-configuration-cert + (default "/etc/openvpn/client.crt")) ; string + (key openvpn-configuration-key + (default "/etc/openvpn/client.key")) ; string + (tls-auth openvpn-configuration-tls-auth + (default #f)) ; boolean | string + (comp-lzo? openvpn-configuration-comp-lzo? + (default #t)) ; boolean + (persist-key? openvpn-configuration-persist-key? + (default #t)) ; boolean + (persist-tun? openvpn-configuration-persist-tun? + (default #t)) ; boolean + (verbosity openvpn-configuration-verbosity + (default 3)) + ;; client-only + (verify-key-usage? openvpn-configuration-verify-key-usage? + (default #t)) ; boolean + (bind? openvpn-configuration-bind + (default #f)) ; boolean + (resolv-retry? openvpn-configuration-resolv-retry? + (default #t)) ; boolean + (remote openvpn-configuration-remote + (default (list (openvpn-remote-server)))) ; list + ;; server-only + (port openvpn-configuration-port + (default 1194)) ; number + (subnet openvpn-configuration-subnet + (default "10.8.0.0/24")) ; CIDR notation (string) | #f + (subnet6 openvpn-configuration-subnet6 + (default #f)) ; CIDR notation (string) | #f + (ifconfig-pool-persist openvpn-configuration-ifconfig-pool-persist + (default "/etc/openvpn/ipp.txt")) ; string + (redirect-gateway? openvpn-configuration-redirect-gateway? + (default #f)) ; boolean + (client-to-client? openvpn-configuration-client-to-client? + (default #f)) ; boolean + (keep-alive-period openvpn-configuration-keep-alive-period + (default 10)) ; number + (keep-alive-timeout openvpn-configuration-keep-alive-timeout + (default 120)) ; number + (max-clients openvpn-configuration-max-clients + (default 100)) ; number + (status-file openvpn-configuration-status-file + (default "/var/run/openvpn/status"))) ; string + +(define %openvpn-accounts + (list (user-group (name "openvpn") (system? #t)) + (user-account + (name "openvpn") + (group "openvpn") + (system? #t) + (comment "openvpn privilege separation user") + (home-directory "/var/run/openvpn") + (shell #~(string-append #$shadow "/sbin/nologin"))))) + +(define (openvpn-activation config) + "Return the activation GEXP for CONFIG." + #~(begin + (mkdir-p (dirname #$(openvpn-configuration-pid-file config))))) + + +(define (openvpn-config-file config role) + "Return the openvpn configuration file corresponding to CONFIG." + (computed-file + (string-append "openvpn-" + (match role ('client "client") ('server "server")) + ".conf") + #~(call-with-output-file #$output + (lambda (port) + (display "# Generated by 'openvpn-service'.\n" port) + (display "user openvpn\n" port) + (display "group openvpn\n" port) + (format port "~a\n" #$(match role ('client "client") (_ ""))) + (format port "proto ~a\n" + #$(match (openvpn-configuration-proto config) + ('udp "udp") + ('tcp "tcp"))) + (format port "dev ~a\n" + #$(match (openvpn-configuration-dev config) + ('tun "tun") + ('tap "tap"))) + (format port "ca ~a\n" + #$(openvpn-configuration-ca config)) + (format port "cert ~a\n" + #$(openvpn-configuration-cert config)) + (format port "key ~a\n" + #$(openvpn-configuration-key config)) + #$(match (openvpn-configuration-tls-auth config) + (#f "") + (str #~(format port "tls-auth ~a ~a\n" + #$(openvpn-configuration-tls-auth config) + #$(match role ('client "1") ('server "0"))))) + #$(if (openvpn-configuration-persist-key? config) + #~(format port "persist-key\n")) + #$(if (openvpn-configuration-persist-tun? config) + #~(format port "persist-tun\n")) + #$(if (openvpn-configuration-comp-lzo? config) + #~(format port "comp-lzo\n")) + (format port "verb ~a\n" + #$(number->string (openvpn-configuration-verbosity config))) + ;; client-specific configuration + (format port "~a" + #$(if (eq? role 'client) + (do ((remaining + (openvpn-configuration-remote config) + (cdr remaining)) + (str "" (string-append str "remote " + (openvpn-remote-server-name + (car remaining)) + " " + (number->string + (openvpn-remote-server-port + (car remaining))) "\n"))) + ((null? remaining) str)) + "")) + #$(if (and (eq? role 'client) + (openvpn-configuration-verify-key-usage? config)) + #~(format port "remote-cert-tls server\n") + #~(display "" port)) + ;; server-specific configuration + #$(if (eq? role 'server) + #~(format port "port ~a\n" + #$(number->string + (openvpn-configuration-port config))) + #~(display "" port)) + #$(if (and (eq? role 'server) + (openvpn-configuration-subnet config)) + #~(format port "server ~a\n" + #$(openvpn-configuration-subnet config)) + #~(display "" port)) + #$(if (and (eq? role 'server) + (openvpn-configuration-subnet6 config)) + #~(format port "server-ipv6 ~a\n" + #$(openvpn-configuration-subnet6 config)) + #~(display "" port)) + #$(if (and (eq? role 'server) + (openvpn-configuration-ifconfig-pool-persist config)) + #~(format port "ifconfig-pool-persist ~a\n" + #$(openvpn-configuration-ifconfig-pool-persist config)) + #~(display "" port)) + #$(if (and (eq? role 'server) + (openvpn-configuration-redirect-gateway? config)) + #~(display "push \"redirect-gateway\"\n" port) + #~(display "" port)) + #$(if (and (eq? role 'server) + (openvpn-configuration-client-to-client? config)) + #~(display "client-to-client\n" port) + #~(display "" port)) + #$(if (eq? role 'server) + #~(format port "keep-alive ~a ~a\n" + #$(openvpn-configuration-keep-alive-period config) + #$(openvpn-configuration-keep-alive-timeout config)) + #~(display "" port)) + #$(if (eq? role 'server) + #~(format port "max-clients ~a\n" + #$(openvpn-configuration-max-clients config)) + #~(display "" port)) + #$(if (eq? role 'server) + #~(format port "status ~a\n" + #$(openvpn-configuration-status-file config)) + #~(display "" port)) + #t)))) + +(define (get-openvpn-shepherd-service role) + (lambda (config) + (define pid-file + (openvpn-configuration-pid-file config)) + + (define openvpn-command + #~(list (string-append #$openvpn "/sbin/openvpn") + "--writepid" #$pid-file + "--config" + #$(openvpn-config-file config role))) + + (list (shepherd-service + (documentation (string-append "OpenVPN " + (match role + ('client "client") + ('server "server")) + ".")) + (requirement '(networking syslogd)) + (provision (match role + ('client '(vpn-client-daemon)) + ('server '(vpn-server-daemon)))) + (start #~(make-forkexec-constructor #$openvpn-command + #:pid-file #$pid-file)) + (stop #~(make-kill-destructor)))))) + +(define openvpn-client-service-type + (service-type (name 'openvpn-client) + (extensions + (list (service-extension shepherd-root-service-type + (get-openvpn-shepherd-service 'client)) + (service-extension activation-service-type + openvpn-activation) + (service-extension account-service-type + (const %openvpn-accounts)))))) + +(define openvpn-server-service-type + (service-type (name 'openvpn-server) + (extensions + (list (service-extension shepherd-root-service-type + (get-openvpn-shepherd-service 'server)) + (service-extension activation-service-type + openvpn-activation) + (service-extension account-service-type + (const %openvpn-accounts)))))) + +;;; vpn.scm ends here -- 2.10.1