EasyRSA

From Lolly's Wiki
Jump to navigationJump to search

Kategorie: Security Kategorie: Linux

create CA user

# groupadd -g 22000 ca && adduser --uid 22000 --gid 22000 --gecos "CA user" --encrypt-home ca

Do everything CA specific as CA user!

# su - ca
ca@rzeasyrsa:~$ ecryptfs-mount-private 
ca@rzeasyrsa:~$ cd 
ca@rzeasyrsa:~$ exec bash

Setup EasyRSA

Ubuntu packets

# aptitude install openvpn easy-rsa

Create your CA

mkdir --mode=0700 OpenVPN-CA
cd OpenVPN-CA
for i in /usr/share/easy-rsa/* ; do ln -s $i ; done
rm -f vars clean-all
cp /usr/share/easy-rsa/vars .

Edit the defaults

Setup proper defaults in your vars file. Source it every time before you do CA work.

Base setup (Only one time at the beginning!!!)

Really just do this before you start with your CA. It will delete everything: keys and certificates!!!

$ cd OpenVPN-CA
$ . vars
$ /usr/share/easy-rsa/clean-all

Generate DH parameter

$ cd OpenVPN-CA
$ . vars
$ KEY_SIZE=4096 ./build-dh

or

$ cd OpenVPN-CA/keys
$ openssl dhparam -2 -out dh4096.pem 4096

Generate TLS-auth parameter

$ cd OpenVPN-CA/keys
$ /usr/sbin/openvpn --genkey --secret ta.key

User certificates with passwords in scripts

If you want to work with password encrypted keys and wat to batch process many users, you might find this helpful.

Add a line after # output_password = secret:

# output_password = secret
output_password = $ENV::KEY_PASS

After that the openssl calls taking the needed password from the environment variable KEY_PASS.

You can call it like this for example:

KEY_PASS="password" ./build-key-pass --batch user

Create your CA certificate

$ cd OpenVPN-CA
$ . vars
$ ./buid-ca

Check it with

$ openssl x509 -noout -text -in keys/ca.crt

Create the server certificate

$ cd OpenVPN-CA
$ . vars
$ ./build-key-server openvpn-server

For example server keys with 5 years validity:

$ KEY_EXPIRE=1825 ./build-key-server openvpn-server

Create your OpenVPN config

get_ovpn.sh

I wrote a little helper script called get_ovpn.sh:

#!/bin/bash

# Written by Lars Timmann L@rs.Timmann.de> 2016
# You may use it for free but on your own risk!!!

TYPE="client"
KEY_DIR="OpenVPN-CA/keys"

function usage() {
  if [ "_${1}_" != "_help_" ]
  then
    printf "ERROR: $*\n"
  fi
  printf "Options:\n"
  cat <<EOF
  -h|--help		                             This help
  -c|--config-type	Default: client              (client|server)
  -k|--key-dir		Default: OpenVPN-CA/keys     Directory where certificates and keys can be found
  -t|--template		Default: ${configtype}.ovpn  The template to use     
  -u|--user		                             User to create config for
  -s|--server		                             Servername for --config-type=server
  --what-ever=value	Replace <WHAT_EVER> in template with value e.g.: --server-net=... replaces <SERVER_NET> with the given value
EOF
  exit 1
}

while [ $# -gt 0 ]
do
  #if [ $# -ge 2 ]; then value=$2; fi
  case $1 in
  -h|--help)
    usage "help"
    ;;
  --?*=?*|-?*=?*)
    param=${1%=*}
    value=${1#*=}
    shift;
    ;;
  --?*=|-?*=)
    param=${1%=*}
    usage "${param} needs a vlaue!"
    ;;
  *)
    if [ $# -lt 2 ] ; then usage "$1 needs a value!"; fi
    param=$1
    value=$2
    shift; shift;
    ;;
  esac

  case $param in
  -t|--template)
    TEMPLATE=${value}
    ;;
  -k|--key-dir)
    KEY_DIR=${value}
    ;;
  -u|--user)
    OVPN_USER=${value}
    ;;
  -c|--config-type)
    TYPE=${value}
    ;;
  -s|--server-name)
    SERVER=${value}
    ;;
  *)
    param=${param#--}
    param=${param/-/_}
    export ${param^^}=${value}
    ;;
  esac
done
TEMPLATE=${TEMPLATE:-"${TYPE}.ovpn"}

[ -z "${SERVER}"    -a "_${TYPE}_" == "_server_" ] && usage "For which server?\n" 
[ -z "${OVPN_USER}" -a "_${TYPE}_" == "_client_" ] && usage "For which user?\n" 
[ ! -f "${TEMPLATE}" ] && usage "Template file ${TEMPLATE} not found!\n"
[ ! -d "${KEY_DIR}" ] && usage "Key directory ${KEY_DIR} not found!\n"
[ ! -f "${KEY_DIR}/ta.key" ] && usage "TLS Auth ${KEY_DIR}/ta.key not found!\n"
[ ! -f "${KEY_DIR}/ca.crt" ] && usage "CA Certificate ${KEY_DIR}/ca.crt not found!\n"
[ ! -f "${KEY_DIR}/${SERVER}.key" -a "_${TYPE}_" == "_server_" ] && usage "Private key ${KEY_DIR}/${SERVER}.key not found!\n"
[ ! -f "${KEY_DIR}/${SERVER}.crt" -a "_${TYPE}_" == "_server_" ] && usage "Certificate ${KEY_DIR}/${SERVER}.crt not found!\n"
[ ! -f "${KEY_DIR}/${OVPN_USER}.key" -a "_${TYPE}_" == "_client_" ] && usage "Private key ${KEY_DIR}/${OVPN_USER}.key not found!\n"
[ ! -f "${KEY_DIR}/${OVPN_USER}.crt" -a "_${TYPE}_" == "_client_" ] && usage "Certificate ${KEY_DIR}/${OVPN_USER}.crt not found!\n"

export SERVER
gawk \
  -v user="${OVPN_USER}" \
  -v key_dir="${KEY_DIR}" \
  -v configtype="${TYPE}" \
  -v server="${SERVER}" \
'
function print_fingerprint(certfile){
  command="openssl x509 -noout -fingerprint -in "certfile;
  FS="=";
  while(command | getline);
  retval=$2;
  close(command);
  return retval;
}
function print_part(part,certfile){
  command="openssl x509 -noout -text -in "certfile;
  while(command | getline){
    if ($1 == part) {
      for(i=2;i<=NF;i++){
        if(i==NF) gsub(/\//,", ", $i)
        retval=retval""$i;
        if(i<NF) retval=retval" ";
      }
    }
  };
  close(command);
  return retval;
}
function print_cert(name,certfile){
  # Header
  #printf "# %s\n",certfile;
  while(getline < certfile){if(/^#/) print $0};
  close(certfile);
  printf "<%s>\n",name;
  while(getline < certfile){if(!/^#/) print $0};
  close(certfile);
  printf "</%s>\n",name;
}
{
  # Static part
  rest=$0;
  while(match(rest,/<[A-Z0-9_]+>/)) {
    matched=substr(rest,RSTART+1,RLENGTH-2);
    ##print "Matched:",matched;
    if (ENVIRON[matched]) gsub("<"matched">",ENVIRON[matched]);
    rest=substr(rest,RSTART+RLENGTH);
  }
  print $0;
}
END{
  # Dynamic part
  if(configtype=="client") {
    printf "remote-cert-tls server\n";
  } else {
    printf "remote-cert-tls client\n";
  }

  # TLS Auth
  print_cert("tls-auth",key_dir"/ta.key");
  printf "key-direction %d\n",(configtype=="client");
  printf "\n";

  print_cert("dh",key_dir"/dh4096.pem");
  printf "\n";

  # Ca Certificate
  if (configtype=="client") {
    printf "verify-x509-name \"%s\"\n",print_part("Subject:",key_dir"/"server".crt");
  }
  printf "verify-hash %s\n",print_fingerprint(key_dir"/ca.crt");
  print_cert("ca",key_dir"/ca.crt");
  printf "\n";

  # User Data
  if (configtype=="client") {
    print_cert("cert",key_dir"/"user".crt");
    printf "\n";
    print_cert("key",key_dir"/"user".key");
    printf "\n";
  } else {
    print_cert("cert",key_dir"/"server".crt");
    printf "\n";
    # key secret/<SERVER>.key is in template
  }
  #print ENVIRON["SERVER_NET"];
}' ${TEMPLATE}
ca@rzeasyrsa:~$ ./get_ovpn.sh --help
Options:
  -h|--help		                             This help
  -c|--config-type	Default: client              (client|server)
  -k|--key-dir		Default: OpenVPN-CA/keys     Directory where certificates and keys can be found
  -t|--template		Default: .ovpn  The template to use     
  -u|--user		                             User to create config for
  -s|--server		                             Servername for --config-type=server
  --what-ever=value	Replace <WHAT_EVER> in template with value e.g.: --server-net=... replaces <SERVER_NET> with the given value

OpenVPN Server

OpenVPN Server Template

  1. I am using the mysql-auth-plugin from https://github.com/chantra/openvpn-mysql-auth
  2. On the OpenVPN-Server the user openvpn has uid 1195 and gid 1195 and I have a TMP-dir for this user in the /etc/fstab like this:
none            /run/openvpn_tmp                 tmpfs           nodev,noexec,nosuid,size=5m,mode=0700,uid=1195,gid=1195 0 0


Example server.ovpn:

local <SERVER_IP>
port <SERVER_PORT>
tmp-dir /run/openvpn_tmp
management <MANAGEMENT_IP> <MANAGEMENT_PORT> /etc/openvpn/management-password
proto udp
dev tun
tun-mtu 1500
mssfix

topology subnet
server <SERVER_NET> <SERVER_NETMASK>
push "redirect-gateway def1 bypass-dhcp"
push "dhcp-option DNS <DNS1>"
push "dhcp-option DNS <DNS2>"

push "route 192.168.18.0 255.255.255.0 net_gateway"
push "route 192.168.0.0 255.255.0.0"
push "route 10.0.0.0 255.0.0.0"
push "route 172.28.0.0 255.255.0.0"

client-to-client
duplicate-cn
keepalive 10 120
auth   SHA512
cipher AES-256-CBC
tls-cipher DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-CAMELLIA256-SHA:DHE-RSA-AES256-SHA:DHE-RSA-CAMELLIA128-SHA:DHE-RSA-AES128-SHA:CAMELLIA256-SHA:AES256-SHA:CAMELLIA128-SHA:AES128-SHA
reneg-sec 36000
comp-lzo adaptive
max-clients 25
user openvpn
group openvpn
persist-key
persist-tun

status /var/log/openvpn/<SERVER>-status.log 2
status-version 2
log-append  /var/log/openvpn/<SERVER>-openvpn.log
verb 3
plugin /usr/lib/openvpn/libopenvpn-mysql-auth.so -c /etc/openvpn/auth/<SERVER>_auth_mysql.conf

key secret/<SERVER>.key  # This file should be kept secret

remote-cert-tls client
username-as-common-name

Generate OpenVPN Config for server

ca@rzeasyrsa:~$ ./get_ovpn.sh  \
  --server openvpn \
  --config-type server \
  --server-ip=192.168.18.23 \
  --server-port=1234 \
  --server-net=10.214.60.128 \
  --server-netmask=255.255.255.128 \
  --management-ip=192.168.17.23 \
  --management-port=11234 \
  --dns1=192.168.0.50 \
  --dns2=192.168.0.30 \
  --template server.ovpn \
  --key-dir=OpenVPN-CA/keys

OpenVPN Client

OpenVPN client template

Example client.ovpn:

client
dev tun
proto udp
remote <SERVER_IP> <SERVER_PORT>
tls-client
ns-cert-type server
comp-lzo

auth-user-pass
auth   SHA512
cipher AES-256-CBC
tls-cipher DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-CAMELLIA256-SHA:DHE-RSA-AES256-SHA:DHE-RSA-CAMELLIA128-SHA:DHE-RSA-AES128-SHA:CAMELLIA256-SHA:AES256-SHA:CAMELLIA128-SHA:AES128-SHA
#tls-version-min 1.2

route-delay 5 30
persist-key
persist-tun
nobind
mssfix
push-peer-info
reneg-sec 0
tun-mtu 1500
verb 3
#auth-nocache

Generate OpenVPN Config for server

ca@rzeasyrsa:~$ ./get_ovpn.sh  \
  --config-type client \
  --server-ip   192.168.18.23 \
  --server-port 1234 \
  --template    client.ovpn \
  --key-dir     OpenVPN-CA/keys \
  --user        vpnclient