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