Compare commits
1 commit
master
...
debian-sid
Author | SHA1 | Date | |
---|---|---|---|
f8be9f0351 |
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -1,2 +0,0 @@
|
|||
.vagrant
|
||||
*.swp
|
51
CHANGELOG.md
51
CHANGELOG.md
|
@ -1,51 +0,0 @@
|
|||
# Changelog
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Added
|
||||
|
||||
### Changed
|
||||
|
||||
### Fixed
|
||||
|
||||
### Removed
|
||||
|
||||
### Security
|
||||
|
||||
## [22.04] 2022-04-14
|
||||
|
||||
### Added
|
||||
|
||||
* Create a changelog
|
||||
* Add a version number and `version` command
|
||||
* Accept a `password-file` command line option to read password from a file
|
||||
* Accept `--days` and `--end-date` command line options
|
||||
* CA key length is configurable (minimum 4096)
|
||||
* Add `--non-interactive` command line option
|
||||
* Add `--replace-existing` command line option
|
||||
* Copy files if destination exists
|
||||
* Generate the CRL file after initialization of the CA
|
||||
* `cert-expirations.sh` script to print out certificates expiration dates
|
||||
|
||||
### Changed
|
||||
|
||||
* Rename internal function usage() to show_usage()
|
||||
* Split show_usage() for each subcommand
|
||||
* More readable variable names
|
||||
* verify_ca_password() looks for a previously set password and verifies it
|
||||
* Extract cert_end_date() function
|
||||
* Extract is_user() and is_group() functions
|
||||
* Extract ask_user_password() function
|
||||
* Extract variables for files
|
||||
* Use inline pass phrase arguments
|
||||
* Create files with a human readable date instead of epoch
|
||||
* Remove "set -e" and add many return code checks
|
||||
* Prevent use of uninitialized variables
|
||||
|
||||
### Fixed
|
||||
|
||||
* Check on $USER was always true
|
21
LICENSE
21
LICENSE
|
@ -1,21 +0,0 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2018 Evolix
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
167
README.md
167
README.md
|
@ -1,161 +1,24 @@
|
|||
# ShellPKI
|
||||
# shellpki
|
||||
|
||||
This script is a wrapper around OpenSSL to manage a small
|
||||
[PKI](https://en.wikipedia.org/wiki/Public_key_infrastructure).
|
||||
This script is a wrapper around openssl to manage all the pki stuff
|
||||
for openvpn.
|
||||
|
||||
## Contribution
|
||||
# Usage
|
||||
|
||||
After an update of this repo and if everything is working fine, some files must
|
||||
be copied to [ansible-roles/openvpn](https://gitea.evolix.org/evolix/ansible-roles/src/branch/unstable/openvpn/files/shellpki)
|
||||
First create the directory, put the script in it and the openssl
|
||||
configuration file. You may certainly need to edit the configuration.
|
||||
|
||||
## Install
|
||||
mkdir -p /etc/openvpn/ssl
|
||||
cp /path/to/shellpki.sh /etc/openvpn/ssl/
|
||||
cp /path/to/openssl.cnf /etc/openvpn/ssl/
|
||||
$EDITOR /etc/openvpn/ssl/openssl.cnf
|
||||
|
||||
### Debian
|
||||
Then you'll need to initialize the pki.
|
||||
|
||||
~~~
|
||||
useradd shellpki --system -M --home-dir /etc/shellpki --shell /usr/sbin/nologin
|
||||
mkdir /etc/shellpki
|
||||
install -m 0640 openssl.cnf /etc/shellpki/
|
||||
install -m 0755 shellpki /usr/local/sbin/shellpki
|
||||
chown -R shellpki: /etc/shellpki
|
||||
~~~
|
||||
cd /etc/openvpn/ssl
|
||||
sh shellpki.sh init
|
||||
|
||||
~~~
|
||||
# visudo -f /etc/sudoers.d/shellpki
|
||||
%shellpki ALL = (root) /usr/local/sbin/shellpki
|
||||
~~~
|
||||
Once it's done, you can create all the certificates you need.
|
||||
|
||||
### OpenBSD
|
||||
sh shellpki.sh create
|
||||
|
||||
~~~
|
||||
useradd -r 1..1000 -d /etc/shellpki -s /sbin/nologin _shellpki
|
||||
mkdir /etc/shellpki
|
||||
install -m 0640 openssl.cnf /etc/shellpki/
|
||||
install -m 0755 shellpki /usr/local/sbin/shellpki
|
||||
chown -R _shellpki:_shellpki /etc/shellpki
|
||||
~~~
|
||||
|
||||
~~~
|
||||
# visudo -f /etc/sudoers
|
||||
%_shellpki ALL = (root) /usr/local/sbin/shellpki
|
||||
~~~
|
||||
|
||||
## OpenVPN
|
||||
|
||||
If you want auto-generation of the OpenVPN config file in
|
||||
/etc/shellpki/openvpn, you need to create a template file in
|
||||
/etc/shellpki/ovpn.conf, eg. :
|
||||
|
||||
~~~
|
||||
client
|
||||
dev tun
|
||||
tls-client
|
||||
proto udp
|
||||
|
||||
remote ovpn.example.com 1194
|
||||
|
||||
nobind
|
||||
user nobody
|
||||
group nogroup
|
||||
persist-key
|
||||
persist-tun
|
||||
|
||||
cipher AES-256-GCM
|
||||
~~~
|
||||
|
||||
## Usage
|
||||
|
||||
~~~
|
||||
Usage: shellpki <subcommand> [options] [CommonName]
|
||||
~~~
|
||||
|
||||
Initialize PKI (create CA key and self-signed certificate) :
|
||||
|
||||
~~~
|
||||
shellpki init [options] <commonName_for_CA>
|
||||
|
||||
Options
|
||||
--non-interactive do not prompt the user, and exit if an error occurs
|
||||
~~~
|
||||
|
||||
Create a client certificate with key and CSR directly generated on server :
|
||||
|
||||
~~~
|
||||
shellpki create [options] <commonName>
|
||||
|
||||
Options
|
||||
-f, --file, --csr-file create a client certificate from a CSR (doesn't need key)
|
||||
-p, --password prompt the user for a password to set on the client key
|
||||
--password-file if provided with a path to a readable file, the first line is read and set as password on the client key
|
||||
--days specify how many days the certificate should be valid
|
||||
--end-date specify until which date the certificate should be valid, in YYYY/MM/DD hh:mm:ss format, UTC +0
|
||||
--non-interactive do not prompt the user, and exit if an error occurs
|
||||
--replace-existing if the certificate already exists, revoke it before creating a new one
|
||||
~~~
|
||||
|
||||
Revoke a client certificate :
|
||||
|
||||
~~~
|
||||
shellpki revoke [options] <commonName>
|
||||
|
||||
Options
|
||||
--non-interactive do not prompt the user, and exit if an error occurs
|
||||
~~~
|
||||
|
||||
List all certificates :
|
||||
|
||||
~~~
|
||||
shellpki list <options>
|
||||
|
||||
Options
|
||||
-a, --all list all certificates : valid and revoked ones
|
||||
-v, --valid list all valid certificates
|
||||
-r, --revoked list all revoked certificates
|
||||
~~~
|
||||
|
||||
Check expiration date of valid certificates :
|
||||
|
||||
~~~
|
||||
shellpki check
|
||||
~~~
|
||||
|
||||
Run OCSP_D server :
|
||||
|
||||
~~~
|
||||
shellpki ocsp <ocsp_uri:ocsp_port>
|
||||
~~~
|
||||
|
||||
Show version :
|
||||
|
||||
~~~
|
||||
shellpki version
|
||||
~~~
|
||||
|
||||
Show help :
|
||||
|
||||
~~~
|
||||
shellpki help
|
||||
~~~
|
||||
|
||||
## Loop
|
||||
|
||||
We can loop over a file to revoke or create many certificates at once.
|
||||
|
||||
To revoke :
|
||||
|
||||
~~~
|
||||
$ read CA_PASS
|
||||
$ for cert_name in $(cat /path/to/file_certs_to_revoke); do CA_PASSWORD=$CA_PASS shellpki revoke $cert_name --non-interactive ; done
|
||||
~~~
|
||||
|
||||
To create (without `--replace-existing`) or renew (with `--replace-existing`), with a password on the client key :
|
||||
|
||||
~~~
|
||||
$ read CA_PASS
|
||||
$ for cert_name in $(cat /path/to/file_certs_to_create); do apg -n 1 -m 16 -M lcN > /path/to/folder/to/store/${cert_name}.passwd; CA_PASSWORD=$CA_PASS shellpki create --replace-existing --non-interactive --password-file /path/to/folder/to/store/${cert_name}.passwd ${cert_name}; done
|
||||
~~~
|
||||
|
||||
## License
|
||||
|
||||
ShellPKI is an [Evolix](https://evolix.com) project and is licensed
|
||||
under the [MIT license](LICENSE).
|
||||
|
|
39
Vagrantfile
vendored
39
Vagrantfile
vendored
|
@ -1,39 +0,0 @@
|
|||
# -*- mode: ruby -*-
|
||||
# vi: set ft=ruby :
|
||||
|
||||
# Load ~/.VagrantFile if exist, permit local config provider
|
||||
vagrantfile = File.join("#{Dir.home}", '.VagrantFile')
|
||||
load File.expand_path(vagrantfile) if File.exists?(vagrantfile)
|
||||
|
||||
Vagrant.configure('2') do |config|
|
||||
config.vm.synced_folder "./", "/vagrant", type: "rsync", rsync__exclude: [ '.vagrant', '.git' ]
|
||||
config.ssh.shell="/bin/sh"
|
||||
|
||||
$deps = <<SCRIPT
|
||||
mkdir -p /etc/shellpki
|
||||
if [ "$(uname)" = "Linux" ]; then
|
||||
id shellpki 2>&1 >/dev/null || useradd shellpki --system -M --home-dir /etc/shellpki --shell /usr/sbin/nologin
|
||||
fi
|
||||
if [ "$(uname)" = "OpenBSD" ]; then
|
||||
id _shellpki 2>&1 >/dev/null || useradd -r 1..1000 -d /etc/shellpki -s /sbin/nologin _shellpki
|
||||
fi
|
||||
ln -sf /vagrant/openssl.cnf /etc/shellpki/
|
||||
ln -sf /vagrant/shellpki /usr/local/sbin/shellpki
|
||||
SCRIPT
|
||||
|
||||
nodes = [
|
||||
{ :name => "debian", :box => "debian/stretch64" },
|
||||
{ :name => "openbsd", :box => "generic/openbsd6" }
|
||||
]
|
||||
|
||||
nodes.each do |i|
|
||||
config.vm.define "#{i[:name]}" do |node|
|
||||
node.vm.hostname = "shellpki-#{i[:name]}"
|
||||
node.vm.box = "#{i[:box]}"
|
||||
|
||||
config.vm.provision "deps", type: "shell", :inline => $deps
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
end
|
|
@ -1,28 +0,0 @@
|
|||
#!/bin/sh
|
||||
|
||||
VERSION="22.04"
|
||||
|
||||
carp=$(/sbin/ifconfig carp0 2>/dev/null | grep 'status' | cut -d' ' -f2)
|
||||
|
||||
if [ "$carp" = "backup" ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "Warning : all times are in UTC !\n"
|
||||
|
||||
echo "CA certificate:"
|
||||
openssl x509 -enddate -noout -in /etc/shellpki/cacert.pem \
|
||||
| cut -d '=' -f 2 \
|
||||
| sed -e "s/^\(.*\)\ \(20..\).*/- \2 \1/"
|
||||
|
||||
echo ""
|
||||
|
||||
echo "Client certificates:"
|
||||
cat /etc/shellpki/index.txt \
|
||||
| grep ^V \
|
||||
| awk -F "/" '{print $1,$5}' \
|
||||
| awk '{print $2,$5}' \
|
||||
| sed 's/CN=//' \
|
||||
| sed -E 's/([[:digit:]]{2})([[:digit:]]{2})([[:digit:]]{2})([[:digit:]]{2})([[:digit:]]{2})([[:digit:]]{2})Z (.*)/- 20\1 \2 \3 \4:\5:\6 \7/' \
|
||||
| awk '{if ($3 == "01") $3="Jan"; else if ($3 == "02") $3="Feb"; else if ($3 == "03") $3="Mar"; else if ($3 == "04") $3="Apr"; else if ($3 == "05") $3="May"; else if ($3 == "06") $3="Jun"; else if ($3 == "07") $3="Jul"; else if ($3 == "08") $3="Aug"; else if ($3 == "09") $3="Sep"; else if ($3 == "10") $3="Oct"; else if ($3 == "11") $3="Nov"; else if ($3 == "12") $3="Dec"; print $0;}' \
|
||||
| sort -n -k 2 -k 3M -k 4
|
24
cn-filter.sh
24
cn-filter.sh
|
@ -1,24 +0,0 @@
|
|||
#!/bin/sh
|
||||
#
|
||||
# cn-filter.sh is a client-connect script for OpenVPN server
|
||||
# It allow clients to connect only if their CN is in $AUTH_FILE
|
||||
#
|
||||
# You need this parameters in your's server config :
|
||||
#
|
||||
# script-security 3
|
||||
# client-connect <path-to-cn-filter>/cn-filter.sh
|
||||
#
|
||||
|
||||
set -u
|
||||
|
||||
AUTH_FILE="/etc/openvpn/authorized_cns"
|
||||
|
||||
grep -qE "^${common_name}$" "${AUTH_FILE}"
|
||||
if [ "$?" -eq 0 ]; then
|
||||
logger -i -t openvpn-cn-filter -p auth.info "Accepted login for ${common_name} from ${trusted_ip} port ${trusted_port}"
|
||||
exit 0
|
||||
else
|
||||
logger -i -t openvpn-cn-filter -p auth.notice "Failed login for ${common_name} from ${trusted_ip} port ${trusted_port}"
|
||||
fi
|
||||
|
||||
exit 1
|
|
@ -1,21 +0,0 @@
|
|||
#!/bin/sh
|
||||
#
|
||||
# cn-validation.sh is a client-connect script for OpenVPN server
|
||||
# When connecting using the PAM plugin, it allow clients to connect only if their CN is equal to their UNIX username
|
||||
#
|
||||
# You need this parameters in your's server config :
|
||||
#
|
||||
# script-security 2
|
||||
# client-connect <path-to-cn-filter>/cn-validation.sh
|
||||
#
|
||||
|
||||
set -u
|
||||
|
||||
if [ "${common_name}" = "${username}" ]; then
|
||||
logger -i -t openvpn-cn-validation -p auth.info "Accepted login for ${common_name} from ${trusted_ip} port ${trusted_port}"
|
||||
exit 0
|
||||
else
|
||||
logger -i -t openvpn-cn-validation -p auth.notice "Failed login for CN ${common_name} / username ${username} from ${trusted_ip} port ${trusted_port}"
|
||||
fi
|
||||
|
||||
exit 1
|
6
debian/README.Debian
vendored
Normal file
6
debian/README.Debian
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
shellpki for Debian
|
||||
-------------------
|
||||
|
||||
<possible notes regarding this package - if none, delete this file>
|
||||
|
||||
-- Gregory Colpart <reg@debian.org> Sat, 05 Aug 2017 16:19:11 -0400
|
10
debian/README.source
vendored
Normal file
10
debian/README.source
vendored
Normal file
|
@ -0,0 +1,10 @@
|
|||
shellpki for Debian
|
||||
-------------------
|
||||
|
||||
<this file describes information about the source package, see Debian policy
|
||||
manual section 4.14. You WILL either need to modify or delete this file>
|
||||
|
||||
|
||||
|
||||
-- Gregory Colpart <reg@debian.org> Sat, 05 Aug 2017 16:19:11 -0400
|
||||
|
5
debian/changelog
vendored
Normal file
5
debian/changelog
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
shellpki (0.0~20170805-1) unstable; urgency=low
|
||||
|
||||
* Initial release
|
||||
|
||||
-- Gregory Colpart <reg@debian.org> Sat, 05 Aug 2017 16:19:11 -0400
|
1
debian/compat
vendored
Normal file
1
debian/compat
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
9
|
15
debian/control
vendored
Normal file
15
debian/control
vendored
Normal file
|
@ -0,0 +1,15 @@
|
|||
Source: shellpki
|
||||
Section: unknown
|
||||
Priority: optional
|
||||
Maintainer: Gregory Colpart <reg@debian.org>
|
||||
Build-Depends: debhelper (>= 9)
|
||||
Standards-Version: 3.9.5
|
||||
Homepage: <insert the upstream URL, if relevant>
|
||||
#Vcs-Git: git://anonscm.debian.org/collab-maint/shellpki.git
|
||||
#Vcs-Browser: http://anonscm.debian.org/?p=collab-maint/shellpki.git;a=summary
|
||||
|
||||
Package: shellpki
|
||||
Architecture: all
|
||||
Depends: ${misc:Depends}
|
||||
Description: <insert up to 60 chars description>
|
||||
<insert long description, indented with spaces>
|
38
debian/copyright
vendored
Normal file
38
debian/copyright
vendored
Normal file
|
@ -0,0 +1,38 @@
|
|||
Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
|
||||
Upstream-Name: shellpki
|
||||
Source: <url://example.com>
|
||||
|
||||
Files: *
|
||||
Copyright: <years> <put author's name and email here>
|
||||
<years> <likewise for another author>
|
||||
License: <special license>
|
||||
<Put the license of the package here indented by 1 space>
|
||||
<This follows the format of Description: lines in control file>
|
||||
.
|
||||
<Including paragraphs>
|
||||
|
||||
# If you want to use GPL v2 or later for the /debian/* files use
|
||||
# the following clauses, or change it to suit. Delete these two lines
|
||||
Files: debian/*
|
||||
Copyright: 2017 Gregory Colpart <reg@debian.org>
|
||||
License: GPL-2+
|
||||
This package 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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
.
|
||||
This package 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 this program. If not, see <http://www.gnu.org/licenses/>
|
||||
.
|
||||
On Debian systems, the complete text of the GNU General
|
||||
Public License version 2 can be found in "/usr/share/common-licenses/GPL-2".
|
||||
|
||||
# Please also look if there are files or directories which have a
|
||||
# different copyright/license attached and list them here.
|
||||
# Please avoid to pick license terms that are more restrictive than the
|
||||
# packaged work, as it may make Debian's contributions unacceptable upstream.
|
1
debian/dirs
vendored
Normal file
1
debian/dirs
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
/etc/shellpki
|
1
debian/docs
vendored
Normal file
1
debian/docs
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
README.md
|
2
debian/install
vendored
Normal file
2
debian/install
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
shellpki.sh usr/bin
|
||||
openssl.cnf etc/shellpki
|
32
debian/rules
vendored
Executable file
32
debian/rules
vendored
Executable file
|
@ -0,0 +1,32 @@
|
|||
#!/usr/bin/make -f
|
||||
# See debhelper(7) (uncomment to enable)
|
||||
# output every command that modifies files on the build system.
|
||||
#DH_VERBOSE = 1
|
||||
|
||||
# see EXAMPLES in dpkg-buildflags(1) and read /usr/share/dpkg/*
|
||||
DPKG_EXPORT_BUILDFLAGS = 1
|
||||
include /usr/share/dpkg/default.mk
|
||||
|
||||
# see FEATURE AREAS in dpkg-buildflags(1)
|
||||
#export DEB_BUILD_MAINT_OPTIONS = hardening=+all
|
||||
|
||||
# see ENVIRONMENT in dpkg-buildflags(1)
|
||||
# package maintainers to append CFLAGS
|
||||
#export DEB_CFLAGS_MAINT_APPEND = -Wall -pedantic
|
||||
# package maintainers to append LDFLAGS
|
||||
#export DEB_LDFLAGS_MAINT_APPEND = -Wl,--as-needed
|
||||
|
||||
|
||||
# main packaging script based on dh7 syntax
|
||||
%:
|
||||
dh $@
|
||||
|
||||
# debmake generated override targets
|
||||
# This is example for Cmake (See http://bugs.debian.org/641051 )
|
||||
#override_dh_auto_configure:
|
||||
# dh_auto_configure -- \
|
||||
# -DCMAKE_LIBRARY_PATH=$(DEB_HOST_MULTIARCH)
|
||||
|
||||
|
||||
|
||||
|
1
debian/source/format
vendored
Normal file
1
debian/source/format
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
3.0 (quilt)
|
|
@ -1,15 +0,0 @@
|
|||
[Unit]
|
||||
Description=Shellpki OCSP responder
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
User=shellpki
|
||||
Group=shellpki
|
||||
Type=simple
|
||||
ExecStart=/usr/local/sbin/shellpki ocsp ocsp.example.com:8888
|
||||
KillMode=process
|
||||
Restart=always
|
||||
RestartSec=2s
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
19
openssl.cnf
19
openssl.cnf
|
@ -1,17 +1,15 @@
|
|||
# VERSION="22.04"
|
||||
|
||||
[ ca ]
|
||||
default_ca = CA_default
|
||||
|
||||
[ CA_default ]
|
||||
dir = /etc/shellpki
|
||||
certs = $dir/certs
|
||||
new_certs_dir = $dir/tmp
|
||||
dir = /etc/openvpn/ssl/ca
|
||||
certs = /etc/openvpn/ssl/certs
|
||||
new_certs_dir = /etc/openvpn/ssl/ca/tmp
|
||||
database = $dir/index.txt
|
||||
certificate = $dir/cacert.pem
|
||||
serial = $dir/serial
|
||||
crl = $dir/crl.pem
|
||||
private_key = $dir/cakey.key
|
||||
crl = /etc/openvpn/ssl/crl.pem
|
||||
private_key = $dir/private.key
|
||||
RANDFILE = $dir/.rand
|
||||
default_days = 365
|
||||
default_crl_days= 365
|
||||
|
@ -36,11 +34,6 @@ subjectKeyIdentifier=hash
|
|||
authorityKeyIdentifier=keyid:always,issuer:always
|
||||
basicConstraints = CA:true
|
||||
|
||||
[ v3_ocsp ]
|
||||
basicConstraints = CA:FALSE
|
||||
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
|
||||
extendedKeyUsage = OCSPSigning
|
||||
|
||||
[ req_distinguished_name ]
|
||||
countryName = Country Name (2 letter code)
|
||||
countryName_default = FR
|
||||
|
@ -58,3 +51,5 @@ commonName_max = 64
|
|||
emailAddress = Email Address
|
||||
emailAddress_default = security@evolix.net
|
||||
emailAddress_max = 40
|
||||
|
||||
|
||||
|
|
1106
shellpki
1106
shellpki
|
@ -1,1106 +0,0 @@
|
|||
#!/bin/sh
|
||||
#
|
||||
# shellpki is a wrapper around OpenSSL to manage a small PKI
|
||||
#
|
||||
|
||||
set -u
|
||||
|
||||
VERSION="22.04"
|
||||
|
||||
show_version() {
|
||||
cat <<END
|
||||
shellpki version ${VERSION}
|
||||
|
||||
Copyright 2010-2022 Evolix <info@evolix.fr>,
|
||||
Thomas Martin <tmartin@evolix.fr>,
|
||||
Gregory Colpart <reg@evolix.fr>,
|
||||
Romain Dessort <rdessort@evolix.fr>,
|
||||
Benoit SĂ©rie <bserie@evolix.fr>,
|
||||
Victor Laborie <vlaborie@evolix.fr>,
|
||||
Daniel Jakots <djakots@evolix.fr>,
|
||||
Patrick Marchand <pmarchand@evolix.fr>,
|
||||
Jérémy Lecour <jlecour@evolix.fr>,
|
||||
Jérémy Dubois <jdubois@evolix.fr>
|
||||
and others.
|
||||
|
||||
shellpki comes with ABSOLUTELY NO WARRANTY. This is free software,
|
||||
and you are welcome to redistribute it under certain conditions.
|
||||
See the MIT Licence for details.
|
||||
END
|
||||
}
|
||||
|
||||
show_usage() {
|
||||
cat <<EOF
|
||||
Usage: ${0} <subcommand> [options] [CommonName]
|
||||
Warning: [options] always must be before [CommonName] and after <subcommand>
|
||||
|
||||
EOF
|
||||
show_usage_init
|
||||
show_usage_create
|
||||
show_usage_revoke
|
||||
show_usage_list
|
||||
show_usage_check
|
||||
show_usage_ocsp
|
||||
|
||||
cat <<EOF
|
||||
Show version :
|
||||
|
||||
${0} --version
|
||||
|
||||
Show help :
|
||||
|
||||
${0} --help
|
||||
EOF
|
||||
}
|
||||
|
||||
show_usage_init() {
|
||||
cat <<EOF
|
||||
Initialize PKI (create CA key and self-signed certificate) :
|
||||
|
||||
${0} init [options] <commonName_for_CA>
|
||||
|
||||
Options
|
||||
--non-interactive do not prompt the user, and exit if an error occurs
|
||||
|
||||
EOF
|
||||
}
|
||||
|
||||
show_usage_create() {
|
||||
cat <<EOF
|
||||
Create a client certificate with key and CSR directly generated on server :
|
||||
|
||||
${0} create [options] <commonName>
|
||||
|
||||
Options
|
||||
-f, --file, --csr-file create a client certificate from a CSR (doesn't need key)
|
||||
-p, --password prompt the user for a password to set on the client key
|
||||
--password-file if provided with a path to a readable file, the first line is read and set as password on the client key
|
||||
--days specify how many days the certificate should be valid
|
||||
--end-date specify until which date the certificate should be valid, in "YYYY/MM/DD hh:mm:ss" format, UTC +0
|
||||
--non-interactive do not prompt the user, and exit if an error occurs
|
||||
--replace-existing if the certificate already exists, revoke it before creating a new one
|
||||
|
||||
EOF
|
||||
}
|
||||
|
||||
show_usage_revoke() {
|
||||
cat <<EOF
|
||||
Revoke a client certificate :
|
||||
|
||||
${0} revoke [options] <commonName>
|
||||
|
||||
Options
|
||||
--non-interactive do not prompt the user, and exit if an error occurs
|
||||
|
||||
EOF
|
||||
}
|
||||
|
||||
show_usage_list() {
|
||||
cat <<EOF
|
||||
List certificates :
|
||||
|
||||
${0} list <options>
|
||||
|
||||
Options
|
||||
-a, --all list all certificates: valid and revoked ones
|
||||
-v, --valid list all valid certificates
|
||||
-r, --revoked list all revoked certificates
|
||||
|
||||
EOF
|
||||
}
|
||||
|
||||
show_usage_check() {
|
||||
cat <<EOF
|
||||
Check expiration date of valid certificates :
|
||||
|
||||
${0} check
|
||||
|
||||
EOF
|
||||
}
|
||||
|
||||
show_usage_ocsp() {
|
||||
cat <<EOF
|
||||
Run OCSP_D server :
|
||||
|
||||
${0} ocsp <ocsp_uri:ocsp_port>
|
||||
|
||||
EOF
|
||||
}
|
||||
|
||||
error() {
|
||||
echo "${1}" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
warning() {
|
||||
echo "${1}" >&2
|
||||
}
|
||||
|
||||
verify_ca_password() {
|
||||
"${OPENSSL_BIN}" rsa \
|
||||
-in "${CA_KEY}" \
|
||||
-passin pass:"${CA_PASSWORD}" \
|
||||
>/dev/null 2>&1
|
||||
}
|
||||
get_real_path() {
|
||||
# --canonicalize is supported on Linux
|
||||
# -f is supported on Linux and OpenBSD
|
||||
readlink -f -- "${1}"
|
||||
}
|
||||
|
||||
ask_ca_password() {
|
||||
attempt=${1:-0}
|
||||
max_attempts=3
|
||||
|
||||
trap 'unset CA_PASSWORD' 0
|
||||
|
||||
if [ ! -f "${CA_KEY}" ]; then
|
||||
error "You must initialize your PKI with \`shellpki init' !"
|
||||
fi
|
||||
if [ "${attempt}" -gt 0 ]; then
|
||||
warning "Invalid password, retry."
|
||||
fi
|
||||
if [ "${attempt}" -ge "${max_attempts}" ]; then
|
||||
error "Maximum number of attempts reached (${max_attempts})."
|
||||
fi
|
||||
if [ -z "${CA_PASSWORD:-}" ]; then
|
||||
if [ "${non_interactive}" -eq 1 ]; then
|
||||
error "In non-interactive mode, you must pass CA_PASSWORD as environment variable"
|
||||
fi
|
||||
stty -echo
|
||||
printf "Password for CA key: "
|
||||
read -r CA_PASSWORD
|
||||
stty echo
|
||||
printf "\n"
|
||||
fi
|
||||
if [ -z "${CA_PASSWORD:-}" ] || ! verify_ca_password; then
|
||||
unset CA_PASSWORD
|
||||
attempt=$(( attempt + 1 ))
|
||||
ask_ca_password "${attempt}"
|
||||
fi
|
||||
}
|
||||
ask_user_password() {
|
||||
trap 'unset PASSWORD' 0
|
||||
|
||||
if [ -z "${PASSWORD:-}" ]; then
|
||||
if [ "${non_interactive}" -eq 1 ]; then
|
||||
error "In non-interactive mode, you must pass PASSWORD as environment variable or use --password-file"
|
||||
fi
|
||||
stty -echo
|
||||
printf "Password for user key: "
|
||||
read -r PASSWORD
|
||||
stty echo
|
||||
printf "\n"
|
||||
fi
|
||||
if [ -z "${PASSWORD:-}" ]; then
|
||||
warning "Warning: empty password from input"
|
||||
fi
|
||||
}
|
||||
replace_existing_or_abort() {
|
||||
cn=${1:?}
|
||||
if [ "${non_interactive}" -eq 1 ]; then
|
||||
if [ "${replace_existing}" -eq 1 ]; then
|
||||
revoke --non-interactive "${cn}"
|
||||
else
|
||||
error "${cn} already exists, use \`--replace-existing' to force"
|
||||
fi
|
||||
else
|
||||
if [ "${replace_existing}" -eq 1 ]; then
|
||||
revoke "${cn}"
|
||||
else
|
||||
printf "%s already exists, do you want to revoke and recreate it ? [y/N] " "${cn}"
|
||||
read -r REPLY
|
||||
resp=$(echo "${REPLY}" | tr 'Y' 'y')
|
||||
|
||||
if [ "${resp}" = "y" ]; then
|
||||
revoke "${cn}"
|
||||
else
|
||||
error "Aborted"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
init() {
|
||||
umask 0177
|
||||
|
||||
[ -d "${CA_DIR}" ] || mkdir -m 0750 "${CA_DIR}"
|
||||
[ -d "${CRT_DIR}" ] || mkdir -m 0750 "${CRT_DIR}"
|
||||
[ -f "${INDEX_FILE}" ] || touch "${INDEX_FILE}"
|
||||
[ -f "${CRL}" ] || touch "${CRL}"
|
||||
[ -f "${SERIAL}" ] || echo "01" > "${SERIAL}"
|
||||
|
||||
non_interactive=0
|
||||
|
||||
# Parse options
|
||||
# based on https://gist.github.com/deshion/10d3cb5f88a21671e17a
|
||||
while :; do
|
||||
case ${1:-} in
|
||||
--non-interactive)
|
||||
non_interactive=1
|
||||
;;
|
||||
--)
|
||||
# End of all options.
|
||||
shift
|
||||
break
|
||||
;;
|
||||
-?*)
|
||||
# ignore unknown options
|
||||
warning "Warning: unknown option (ignored): \`$1'"
|
||||
;;
|
||||
*)
|
||||
# Default case: If no more options then break out of the loop.
|
||||
break
|
||||
;;
|
||||
esac
|
||||
|
||||
shift
|
||||
done
|
||||
|
||||
cn="${1:-}"
|
||||
if [ -z "${cn}" ]; then
|
||||
show_usage_init >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -f "${CA_KEY}" ]; then
|
||||
if [ "${non_interactive}" -eq 1 ]; then
|
||||
error "${CA_KEY} already exists, erase it manually if you want to start over."
|
||||
else
|
||||
printf "%s already exists, do you really want to erase it ? [y/N] " "${CA_KEY}"
|
||||
read -r REPLY
|
||||
resp=$(echo "${REPLY}" | tr 'Y' 'y')
|
||||
if [ "${resp}" = "y" ]; then
|
||||
rm -f "${CA_KEY}" "${CA_CERT}"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
passout_arg=""
|
||||
if [ -n "${CA_PASSWORD:-}" ]; then
|
||||
passout_arg="-passout pass:${CA_PASSWORD}"
|
||||
elif [ "${non_interactive}" -eq 1 ]; then
|
||||
error "In non-interactive mode, you must pass CA_PASSWORD as environment variable."
|
||||
fi
|
||||
|
||||
if [ ! -f "${CA_KEY}" ]; then
|
||||
"${OPENSSL_BIN}" genrsa \
|
||||
-out "${CA_KEY}" \
|
||||
${passout_arg} \
|
||||
-aes256 \
|
||||
"${CA_KEY_LENGTH}" \
|
||||
>/dev/null 2>&1
|
||||
# shellcheck disable=SC2181
|
||||
if [ "$?" -ne 0 ]; then
|
||||
error "Error generating the CA key"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -f "${CA_CERT}" ]; then
|
||||
if [ "${non_interactive}" -eq 1 ]; then
|
||||
error "${CA_CERT} already exists, erase it manually if you want to start over."
|
||||
else
|
||||
printf "%s already exists, do you really want to erase it ? [y/N] " "${CA_CERT}"
|
||||
read -r REPLY
|
||||
resp=$(echo "${REPLY}" | tr 'Y' 'y')
|
||||
if [ "${resp}" = "y" ]; then
|
||||
rm "${CA_CERT}"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ ! -f "${CA_CERT}" ]; then
|
||||
ask_ca_password 0
|
||||
fi
|
||||
|
||||
if [ ! -f "${CA_CERT}" ]; then
|
||||
"${OPENSSL_BIN}" req \
|
||||
-new \
|
||||
-batch \
|
||||
-sha512 \
|
||||
-x509 \
|
||||
-days 3650 \
|
||||
-extensions v3_ca \
|
||||
-passin pass:"${CA_PASSWORD}" \
|
||||
-key "${CA_KEY}" \
|
||||
-out "${CA_CERT}" \
|
||||
-config /dev/stdin <<EOF
|
||||
$(cat "${CONF_FILE}")
|
||||
commonName_default = ${cn}
|
||||
EOF
|
||||
# shellcheck disable=SC2181
|
||||
if [ "$?" -ne 0 ]; then
|
||||
error "Error generating the CA certificate"
|
||||
fi
|
||||
|
||||
"${OPENSSL_BIN}" ca \
|
||||
-config "${CONF_FILE}" \
|
||||
-passin pass:${CA_PASSWORD} \
|
||||
-gencrl \
|
||||
-out "${CRL}"
|
||||
fi
|
||||
}
|
||||
|
||||
ocsp() {
|
||||
umask 0177
|
||||
|
||||
ocsp_uri="${1:-}"
|
||||
if [ -z "${ocsp_uri}" ]; then
|
||||
show_usage_ocsp >&2
|
||||
exit 1
|
||||
fi
|
||||
ocsp_csr_file="${CSR_DIR}/ocsp.csr"
|
||||
|
||||
url=$(echo "${ocsp_uri}" | cut -d':' -f1)
|
||||
port=$(echo "${ocsp_uri}" | cut -d':' -f2)
|
||||
|
||||
if [ ! -f "${OCSP_KEY}" ]; then
|
||||
"${OPENSSL_BIN}" genrsa \
|
||||
-out "${OCSP_KEY}" \
|
||||
"${KEY_LENGTH}" \
|
||||
>/dev/null 2>&1
|
||||
# shellcheck disable=SC2181
|
||||
if [ "$?" -ne 0 ]; then
|
||||
error "Error generating the OCSP key"
|
||||
fi
|
||||
fi
|
||||
|
||||
"${OPENSSL_BIN}" req \
|
||||
-batch \
|
||||
-new \
|
||||
-key "${OCSP_KEY}" \
|
||||
-out "${ocsp_csr_file}" \
|
||||
-config /dev/stdin <<EOF
|
||||
$(cat "${CONF_FILE}")
|
||||
commonName_default = ${url}
|
||||
[ usr_cert ]
|
||||
authorityInfoAccess = OCSP;URI:http://${ocsp_uri}
|
||||
EOF
|
||||
# shellcheck disable=SC2181
|
||||
if [ "$?" -ne 0 ]; then
|
||||
error "Error generating the OCSP request"
|
||||
fi
|
||||
|
||||
if [ ! -f "${OCSP_CERT}" ]; then
|
||||
ask_ca_password 0
|
||||
fi
|
||||
|
||||
if [ ! -f "${OCSP_CERT}" ]; then
|
||||
"${OPENSSL_BIN}" ca \
|
||||
-extensions v3_ocsp \
|
||||
-in "${ocsp_csr_file}" \
|
||||
-out "${OCSP_CERT}" \
|
||||
-passin pass:"${CA_PASSWORD}" \
|
||||
-config "${CONF_FILE}"
|
||||
# shellcheck disable=SC2181
|
||||
if [ "$?" -ne 0 ]; then
|
||||
error "Error generating the OCSP certificate"
|
||||
fi
|
||||
fi
|
||||
|
||||
exec "${OPENSSL_BIN}" ocsp \
|
||||
-ignore_err \
|
||||
-index "${INDEX_FILE}" \
|
||||
-port "${port}" \
|
||||
-rsigner "${OCSP_CERT}" \
|
||||
-rkey "${OCSP_KEY}" \
|
||||
-CA "${CA_CERT}" \
|
||||
-text
|
||||
}
|
||||
|
||||
create() {
|
||||
from_csr=0
|
||||
ask_pass=0
|
||||
non_interactive=0
|
||||
replace_existing=0
|
||||
days=""
|
||||
end_date=""
|
||||
days_set=0
|
||||
end_date_set=0
|
||||
password_set=0
|
||||
password_file_set=0
|
||||
|
||||
# Parse options
|
||||
# based on https://gist.github.com/deshion/10d3cb5f88a21671e17a
|
||||
while :; do
|
||||
case ${1:-} in
|
||||
-f|--file|--csr-file)
|
||||
# csr-file option, with value separated by space
|
||||
if [ -n "$2" ]; then
|
||||
from_csr=1
|
||||
csr_file=$(get_real_path "${2}")
|
||||
# shellcheck disable=SC2181
|
||||
if [ "$?" -ne 0 ]; then
|
||||
error "Error accessing file \`${2}'"
|
||||
fi
|
||||
shift
|
||||
else
|
||||
error "Argument error: \`--csr-file' requires a value"
|
||||
fi
|
||||
;;
|
||||
--file=?*|--csr-file=?*)
|
||||
from_csr=1
|
||||
# csr-file option, with value separated by =
|
||||
csr_file=$(get_real_path "${1#*=}")
|
||||
# shellcheck disable=SC2181
|
||||
if [ "$?" -ne 0 ]; then
|
||||
error "Error accessing file \`${1#*=}'"
|
||||
fi
|
||||
;;
|
||||
--file=|--csr-file=)
|
||||
# csr-file options, without value
|
||||
error "Argument error: \`--csr-file' requires a value"
|
||||
;;
|
||||
-p|--password)
|
||||
ask_pass=1
|
||||
password_set=1
|
||||
;;
|
||||
--password-file)
|
||||
# password-file option, with value separated by space
|
||||
if [ -n "$2" ]; then
|
||||
password_file=$(get_real_path "${2}")
|
||||
# shellcheck disable=SC2181
|
||||
if [ "$?" -ne 0 ]; then
|
||||
error "Error accessing file \`${2}'"
|
||||
fi
|
||||
password_file_set=1
|
||||
shift
|
||||
else
|
||||
error "Argument error: \`--password-file' requires a value"
|
||||
fi
|
||||
;;
|
||||
--password-file=?*)
|
||||
# password-file option, with value separated by =
|
||||
password_file=$(get_real_path "${1#*=}")
|
||||
# shellcheck disable=SC2181
|
||||
if [ "$?" -ne 0 ]; then
|
||||
error "Error accessing file \`${1#*=}'"
|
||||
fi
|
||||
password_file_set=1
|
||||
;;
|
||||
--password-file=)
|
||||
# password-file options, without value
|
||||
error "Argument error: \`--password-file' requires a value"
|
||||
;;
|
||||
--days)
|
||||
# days option, with value separated by space
|
||||
if [ -n "$2" ]; then
|
||||
days=${2}
|
||||
days_set=1
|
||||
shift
|
||||
else
|
||||
error "Argument error: \`--days' requires a value"
|
||||
fi
|
||||
;;
|
||||
--days=?*)
|
||||
# days option, with value separated by =
|
||||
days=${1#*=}
|
||||
days_set=1
|
||||
;;
|
||||
--days=)
|
||||
# days options, without value
|
||||
error "Argument error: \`--days' requires a value"
|
||||
;;
|
||||
--end-date)
|
||||
# end-date option, with value separated by space
|
||||
if [ -n "$2" ]; then
|
||||
end_date=${2}
|
||||
end_date_set=1
|
||||
shift
|
||||
else
|
||||
error "Argument error: \`--end-date' requires a value"
|
||||
fi
|
||||
;;
|
||||
--end-date=?*)
|
||||
# end-date option, with value separated by =
|
||||
end_date=${1#*=}
|
||||
end_date_set=1
|
||||
;;
|
||||
--end-date=)
|
||||
# end-date options, without value
|
||||
error "Argument error: \`--end-date' requires a value"
|
||||
;;
|
||||
--non-interactive)
|
||||
non_interactive=1
|
||||
;;
|
||||
--replace-existing)
|
||||
replace_existing=1
|
||||
;;
|
||||
--)
|
||||
# End of all options.
|
||||
shift
|
||||
break
|
||||
;;
|
||||
-?*)
|
||||
# ignore unknown options
|
||||
warning "Warning: unknown option (ignored): \`$1'"
|
||||
;;
|
||||
*)
|
||||
# Default case: If no more options then break out of the loop.
|
||||
break
|
||||
;;
|
||||
esac
|
||||
|
||||
shift
|
||||
done
|
||||
|
||||
if [ "${days_set}" -eq 1 ] && [ "${end_date_set}" -eq 1 ]; then
|
||||
error "Argument error: \`--end-date' and \`--days' cannot be used together."
|
||||
fi
|
||||
|
||||
if [ "${password_set}" -eq 1 ] && [ "${password_file_set}" -eq 1 ]; then
|
||||
error "Argument error: \`--password' and \`--password-file' cannot be used together."
|
||||
fi
|
||||
|
||||
# The name of the certificate
|
||||
cn="${1:-}"
|
||||
|
||||
# Set expiration argument
|
||||
crt_expiration_arg=""
|
||||
if [ -n "${days}" ]; then
|
||||
if [ "${days}" -gt 0 ]; then
|
||||
crt_expiration_arg="-days ${days}"
|
||||
else
|
||||
error "Argument error: \"${days}\" is not a valid value for \`--days'."
|
||||
fi
|
||||
fi
|
||||
if [ -n "${end_date}" ]; then
|
||||
if [ "${SYSTEM}" = "linux" ]; then
|
||||
cert_end_date=$(TZ=:Zulu date --date "${end_date}" +"%Y%m%d%H%M%SZ" 2> /dev/null)
|
||||
# shellcheck disable=SC2181
|
||||
if [ "$?" -ne 0 ]; then
|
||||
error "Invalid end date format: \`${end_date}' can't be parsed by date(1). Expected format: YYYY/MM/DD [hh[:mm[:ss]]]."
|
||||
else
|
||||
crt_expiration_arg="-enddate ${cert_end_date}"
|
||||
fi
|
||||
elif [ "${SYSTEM}" = "openbsd" ]; then
|
||||
cert_end_date=$(TZ=:Zulu date -f "%C%y/%m/%d %H:%M:%S" -j "${end_date}" +"%Y%m%d%H%M%SZ" 2> /dev/null)
|
||||
# shellcheck disable=SC2181
|
||||
if [ "$?" -ne 0 ]; then
|
||||
error "Invalid end date format: \`${end_date}' can't be parsed by date(1). Expected format: YYYY/MM/DD hh:mm:ss."
|
||||
else
|
||||
crt_expiration_arg="-enddate ${cert_end_date}"
|
||||
fi
|
||||
else
|
||||
error "System ${SYSTEM} not supported."
|
||||
fi
|
||||
fi
|
||||
if [ "${non_interactive}" -eq 1 ]; then
|
||||
batch_arg="-batch"
|
||||
else
|
||||
batch_arg=""
|
||||
fi
|
||||
|
||||
if [ "${from_csr}" -eq 1 ]; then
|
||||
if [ "${ask_pass}" -eq 1 ]; then
|
||||
warning "Warning: -p|--password is ignored with -f|--file|--crt-file"
|
||||
fi
|
||||
if [ -n "${password_file:-}" ]; then
|
||||
warning "Warning: --password-file is ignored with -f|--file|--crt-file"
|
||||
fi
|
||||
|
||||
crt_file="${CRT_DIR}/${cn}.crt"
|
||||
|
||||
# ask for CA passphrase
|
||||
ask_ca_password 0
|
||||
|
||||
# check if csr_file is a CSR
|
||||
"${OPENSSL_BIN}" req \
|
||||
-noout \
|
||||
-subject \
|
||||
-in "${csr_file}" \
|
||||
>/dev/null 2>&1
|
||||
# shellcheck disable=SC2181
|
||||
if [ "$?" -ne 0 ]; then
|
||||
error "${csr_file} is not a valid CSR !"
|
||||
fi
|
||||
|
||||
# check if csr_file contain a CN
|
||||
"${OPENSSL_BIN}" req \
|
||||
-noout \
|
||||
-subject \
|
||||
-in "${csr_file}" \
|
||||
| grep -Eo "CN\s*=[^,/]*" \
|
||||
>/dev/null 2>&1
|
||||
# shellcheck disable=SC2181
|
||||
if [ "$?" -ne 0 ]; then
|
||||
error "${csr_file} doesn't contain a CommonName !"
|
||||
fi
|
||||
|
||||
# get CN from CSR
|
||||
cn=$("${OPENSSL_BIN}" req -noout -subject -in "${csr_file}" | grep -Eo "CN\s*=[^,/]*" | cut -d'=' -f2 | xargs)
|
||||
|
||||
# check if CN already exists
|
||||
if [ -f "${crt_file}" ]; then
|
||||
replace_existing_or_abort "${cn}"
|
||||
fi
|
||||
|
||||
# ca sign and generate cert
|
||||
if [ "${non_interactive}" -eq 1 ]; then
|
||||
batch_arg="-batch"
|
||||
else
|
||||
batch_arg=""
|
||||
fi
|
||||
"${OPENSSL_BIN}" ca \
|
||||
${batch_arg} \
|
||||
-config "${CONF_FILE}" \
|
||||
-in "${csr_file}" \
|
||||
-passin pass:"${CA_PASSWORD}" \
|
||||
-out "${crt_file}" \
|
||||
${crt_expiration_arg}
|
||||
# shellcheck disable=SC2181
|
||||
if [ "$?" -ne 0 ]; then
|
||||
error "Error generating the certificate"
|
||||
else
|
||||
echo "The certificate file is available at \`${crt_file}'"
|
||||
fi
|
||||
else
|
||||
if [ -z "${cn}" ]; then
|
||||
show_usage_create >&2
|
||||
exit 1
|
||||
fi
|
||||
csr_file="${CSR_DIR}/${cn}-${SUFFIX}.csr"
|
||||
crt_file="${CRT_DIR}/${cn}.crt"
|
||||
key_file="${KEY_DIR}/${cn}-${SUFFIX}.key"
|
||||
ovpn_file="${OVPN_DIR}/${cn}-${SUFFIX}.ovpn"
|
||||
pkcs12_file="${PKCS12_DIR}/${cn}-${SUFFIX}.p12"
|
||||
|
||||
# ask for CA passphrase
|
||||
ask_ca_password 0
|
||||
|
||||
if [ "${ask_pass}" -eq 1 ]; then
|
||||
ask_user_password
|
||||
fi
|
||||
|
||||
# check if CN already exists
|
||||
if [ -f "${crt_file}" ]; then
|
||||
replace_existing_or_abort "${cn}"
|
||||
fi
|
||||
|
||||
# generate private key
|
||||
pass_args=""
|
||||
if [ -n "${password_file:-}" ]; then
|
||||
pass_args="-aes256 -passout file:${password_file}"
|
||||
elif [ -n "${PASSWORD:-}" ]; then
|
||||
pass_args="-aes256 -passout pass:${PASSWORD}"
|
||||
fi
|
||||
"${OPENSSL_BIN}" genrsa \
|
||||
-out "${key_file}" \
|
||||
${pass_args} \
|
||||
"${KEY_LENGTH}" \
|
||||
>/dev/null 2>&1
|
||||
# shellcheck disable=SC2181
|
||||
if [ "$?" -eq 0 ]; then
|
||||
echo "The KEY file is available at \`${key_file}'"
|
||||
else
|
||||
error "Error generating the private key"
|
||||
fi
|
||||
|
||||
# generate csr req
|
||||
pass_args=""
|
||||
if [ -n "${password_file:-}" ]; then
|
||||
pass_args="-passin file:${password_file}"
|
||||
elif [ -n "${PASSWORD:-}" ]; then
|
||||
pass_args="-passin pass:${PASSWORD}"
|
||||
fi
|
||||
"${OPENSSL_BIN}" req \
|
||||
-batch \
|
||||
-new \
|
||||
-key "${key_file}" \
|
||||
-out "${csr_file}" \
|
||||
${pass_args} \
|
||||
-config /dev/stdin <<EOF
|
||||
$(cat "${CONF_FILE}")
|
||||
commonName_default = ${cn}
|
||||
EOF
|
||||
# shellcheck disable=SC2181
|
||||
if [ "$?" -ne 0 ]; then
|
||||
error "Error generating the CSR"
|
||||
fi
|
||||
|
||||
# ca sign and generate cert
|
||||
"${OPENSSL_BIN}" ca \
|
||||
${batch_arg} \
|
||||
-config "${CONF_FILE}" \
|
||||
-passin pass:${CA_PASSWORD} \
|
||||
-in "${csr_file}" \
|
||||
-out "${crt_file}" \
|
||||
${crt_expiration_arg}
|
||||
# shellcheck disable=SC2181
|
||||
if [ "$?" -ne 0 ]; then
|
||||
error "Error generating the certificate"
|
||||
fi
|
||||
|
||||
# check if CRT is a valid
|
||||
"${OPENSSL_BIN}" x509 \
|
||||
-noout \
|
||||
-subject \
|
||||
-in "${crt_file}" \
|
||||
>/dev/null 2>&1
|
||||
# shellcheck disable=SC2181
|
||||
if [ "$?" -ne 0 ]; then
|
||||
rm -f "${crt_file}"
|
||||
fi
|
||||
if [ ! -f "${crt_file}" ]; then
|
||||
error "Error in CSR creation"
|
||||
fi
|
||||
|
||||
chmod 640 "${crt_file}"
|
||||
|
||||
echo "The CRT file is available in ${crt_file}"
|
||||
|
||||
# generate pkcs12 format
|
||||
pass_args=""
|
||||
if [ -n "${password_file:-}" ]; then
|
||||
# Hack for pkcs12 :
|
||||
# If passin and passout files are the same path, it expects 2 lines
|
||||
# so we make a temporary copy of the password file
|
||||
password_file_out=$(mktemp)
|
||||
cp "${password_file}" "${password_file_out}"
|
||||
pass_args="-passin file:${password_file} -passout file:${password_file_out}"
|
||||
elif [ -n "${PASSWORD:-}" ]; then
|
||||
pass_args="-passin pass:${PASSWORD} -passout pass:${PASSWORD}"
|
||||
else
|
||||
pass_args="-passout pass:"
|
||||
fi
|
||||
"${OPENSSL_BIN}" pkcs12 \
|
||||
-export \
|
||||
-nodes \
|
||||
-inkey "${key_file}" \
|
||||
-in "${crt_file}" \
|
||||
-out "${pkcs12_file}" \
|
||||
${pass_args}
|
||||
# shellcheck disable=SC2181
|
||||
if [ "$?" -ne 0 ]; then
|
||||
error "Error generating the pkcs12 file"
|
||||
fi
|
||||
|
||||
if [ -n "${password_file_out:-}" ]; then
|
||||
# Hack for pkcs12 :
|
||||
# Destroy the temporary file
|
||||
rm -f "${password_file_out}"
|
||||
fi
|
||||
|
||||
chmod 640 "${pkcs12_file}"
|
||||
echo "The PKCS12 config file is available at \`${pkcs12_file}'"
|
||||
|
||||
# generate openvpn format
|
||||
if [ -e "${CA_DIR}/ovpn.conf" ]; then
|
||||
cat "${CA_DIR}/ovpn.conf" - > "${ovpn_file}" <<EOF
|
||||
<ca>
|
||||
$(cat "${CA_CERT}")
|
||||
</ca>
|
||||
|
||||
<cert>
|
||||
$(cat "${crt_file}")
|
||||
</cert>
|
||||
|
||||
<key>
|
||||
$(cat "${key_file}")
|
||||
</key>
|
||||
EOF
|
||||
chmod 640 "${ovpn_file}"
|
||||
echo "The OpenVPN config file is available at \`${ovpn_file}'"
|
||||
fi
|
||||
|
||||
# Copy files if destination exists
|
||||
if [ -d "${COPY_DIR}" ]; then
|
||||
for file in "${crt_file}" "${key_file}" "${pkcs12_file}" "${ovpn_file}"; do
|
||||
if [ -f "${file}" ]; then
|
||||
new_file="${COPY_DIR}/$(basename "${file}")"
|
||||
if [ "${replace_existing}" -eq 1 ]; then
|
||||
cp -f "${file}" "${COPY_DIR}/"
|
||||
else
|
||||
if [ "${non_interactive}" -eq 1 ]; then
|
||||
if [ -f "${new_file}" ]; then
|
||||
echo "File \`${file}' has not been copied to \`${new_file}', it already exists" >&2
|
||||
continue
|
||||
else
|
||||
cp "${file}" "${COPY_DIR}/"
|
||||
fi
|
||||
else
|
||||
cp -i "${file}" "${COPY_DIR}/"
|
||||
fi
|
||||
fi
|
||||
echo "File \`${file}' has been copied to \`${new_file}'"
|
||||
fi
|
||||
done
|
||||
|
||||
# shellcheck disable=SC2086
|
||||
chown -R ${PKI_USER}:${PKI_USER} "${COPY_DIR}/"
|
||||
chmod -R u=rwX,g=rwX,o= "${COPY_DIR}/"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
revoke() {
|
||||
non_interactive=0
|
||||
|
||||
# Parse options
|
||||
# based on https://gist.github.com/deshion/10d3cb5f88a21671e17a
|
||||
while :; do
|
||||
case ${1:-} in
|
||||
--non-interactive)
|
||||
non_interactive=1
|
||||
;;
|
||||
--)
|
||||
# End of all options.
|
||||
shift
|
||||
break
|
||||
;;
|
||||
-?*)
|
||||
# ignore unknown options
|
||||
warning "Warning: unknown option (ignored): \`$1'"
|
||||
;;
|
||||
*)
|
||||
# Default case: If no more options then break out of the loop.
|
||||
break
|
||||
;;
|
||||
esac
|
||||
|
||||
shift
|
||||
done
|
||||
|
||||
# The name of the certificate
|
||||
cn="${1:-}"
|
||||
|
||||
if [ -z "${cn}" ]; then
|
||||
show_usage_revoke >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
crt_file="${CRT_DIR}/${cn}.crt"
|
||||
# check if CRT exists
|
||||
if [ ! -f "${crt_file}" ]; then
|
||||
error "Unknow CN: ${cn} (\`${crt_file}' not found)"
|
||||
fi
|
||||
|
||||
# check if CRT is a valid
|
||||
"${OPENSSL_BIN}" x509 \
|
||||
-noout \
|
||||
-subject \
|
||||
-in "${crt_file}" \
|
||||
>/dev/null 2>&1
|
||||
# shellcheck disable=SC2181
|
||||
if [ "$?" -ne 0 ]; then
|
||||
error "${crt_file} is not a valid CRT, you must delete it !"
|
||||
fi
|
||||
|
||||
# ask for CA passphrase
|
||||
ask_ca_password 0
|
||||
|
||||
echo "Revoke certificate ${crt_file} :"
|
||||
"${OPENSSL_BIN}" ca \
|
||||
-config "${CONF_FILE}" \
|
||||
-passin pass:"${CA_PASSWORD}" \
|
||||
-revoke "${crt_file}"
|
||||
# shellcheck disable=SC2181
|
||||
if [ "$?" -eq 0 ]; then
|
||||
rm "${crt_file}"
|
||||
fi
|
||||
|
||||
"${OPENSSL_BIN}" ca \
|
||||
-config "${CONF_FILE}" \
|
||||
-passin pass:"${CA_PASSWORD}" \
|
||||
-gencrl \
|
||||
-out "${CRL}"
|
||||
}
|
||||
|
||||
list() {
|
||||
if [ ! -f "${INDEX_FILE}" ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ -z "${1:-}" ]; then
|
||||
show_usage_list >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
while :; do
|
||||
case "${1:-}" in
|
||||
-a|--all)
|
||||
list_valid=0
|
||||
list_revoked=0
|
||||
;;
|
||||
-v|--valid)
|
||||
list_valid=0
|
||||
list_revoked=1
|
||||
;;
|
||||
-r|--revoked)
|
||||
list_valid=1
|
||||
list_revoked=0
|
||||
;;
|
||||
-?*)
|
||||
warning "unknow option ${1} (ignored)"
|
||||
;;
|
||||
*)
|
||||
break
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
if [ "${list_valid}" -eq 0 ]; then
|
||||
certs=$(grep "^V" "${INDEX_FILE}")
|
||||
fi
|
||||
|
||||
if [ "${list_revoked}" -eq 0 ]; then
|
||||
certs=$(grep "^R" "${INDEX_FILE}")
|
||||
fi
|
||||
|
||||
if [ "${list_valid}" -eq 0 ] && [ "${list_revoked}" -eq 0 ]; then
|
||||
certs=$(cat "${INDEX_FILE}")
|
||||
fi
|
||||
|
||||
echo "${certs}" | grep -Eo "CN\s*=[^,/]*" | cut -d'=' -f2 | xargs -n1
|
||||
}
|
||||
|
||||
cert_end_date() {
|
||||
"${OPENSSL_BIN}" x509 -noout -enddate -in "${1}" | cut -d'=' -f2
|
||||
}
|
||||
|
||||
check() {
|
||||
# default expiration alert
|
||||
# TODO: permit override with parameters
|
||||
min_day=90
|
||||
cur_epoch=$(date -u +'%s')
|
||||
|
||||
for cert in "${CRT_DIR}"/*; do
|
||||
end_date=$(cert_end_date "${cert}")
|
||||
end_epoch=$(date -ud "${end_date}" +'%s')
|
||||
diff_epoch=$(( end_epoch - cur_epoch ))
|
||||
diff_day=$(( diff_epoch / 60 / 60 / 24 ))
|
||||
if [ "${diff_day}" -lt "${min_day}" ]; then
|
||||
if [ "${diff_day}" -le 0 ]; then
|
||||
echo "${cert} has expired"
|
||||
else
|
||||
echo "${cert} expire in ${diff_day} days"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
is_user() {
|
||||
getent passwd "${1}" >/dev/null
|
||||
}
|
||||
is_group() {
|
||||
getent group "${1}" >/dev/null
|
||||
}
|
||||
|
||||
main() {
|
||||
# Know what system we are on, because OpenBSD and Linux do not implement date(1) in the same way
|
||||
SYSTEM=$(uname | tr '[:upper:]' '[:lower:]')
|
||||
|
||||
# default config
|
||||
# TODO: override with /etc/default/shellpki
|
||||
CONF_FILE="/etc/shellpki/openssl.cnf"
|
||||
|
||||
if [ "$(uname)" = "OpenBSD" ]; then
|
||||
PKI_USER="_shellpki"
|
||||
else
|
||||
PKI_USER="shellpki"
|
||||
fi
|
||||
|
||||
if [ "${USER}" != "root" ] && [ "${USER}" != "${PKI_USER}" ]; then
|
||||
error "Please become root before running ${0} !"
|
||||
fi
|
||||
|
||||
# retrieve CA path from config file
|
||||
CA_DIR=$(grep -E "^dir" "${CONF_FILE}" | cut -d'=' -f2 | xargs -n1)
|
||||
CA_KEY=$(grep -E "^private_key" "${CONF_FILE}" | cut -d'=' -f2 | xargs -n1 | sed "s~\$dir~${CA_DIR}~")
|
||||
CA_CERT=$(grep -E "^certificate" "${CONF_FILE}" | cut -d'=' -f2 | xargs -n1 | sed "s~\$dir~${CA_DIR}~")
|
||||
OCSP_KEY="${CA_DIR}/ocsp.key"
|
||||
OCSP_CERT="${CA_DIR}/ocsp.pem"
|
||||
CRT_DIR=$(grep -E "^certs" "${CONF_FILE}" | cut -d'=' -f2 | xargs -n1 | sed "s~\$dir~${CA_DIR}~")
|
||||
TMP_DIR=$(grep -E "^new_certs_dir" "${CONF_FILE}" | cut -d'=' -f2 | xargs -n1 | sed "s~\$dir~${CA_DIR}~")
|
||||
INDEX_FILE=$(grep -E "^database" "${CONF_FILE}" | cut -d'=' -f2 | xargs -n1 | sed "s~\$dir~${CA_DIR}~")
|
||||
SERIAL=$(grep -E "^serial" "${CONF_FILE}" | cut -d'=' -f2 | xargs -n1 | sed "s~\$dir~${CA_DIR}~")
|
||||
CRL=$(grep -E "^crl" "${CONF_FILE}" | cut -d'=' -f2 | xargs -n1 | sed "s~\$dir~${CA_DIR}~")
|
||||
|
||||
# directories for clients key, csr, crt
|
||||
KEY_DIR="${CA_DIR}/private"
|
||||
CSR_DIR="${CA_DIR}/requests"
|
||||
PKCS12_DIR="${CA_DIR}/pkcs12"
|
||||
OVPN_DIR="${CA_DIR}/openvpn"
|
||||
|
||||
COPY_DIR="$(dirname "${CONF_FILE}")/copy_output"
|
||||
|
||||
CA_KEY_LENGTH=4096
|
||||
if [ "${CA_KEY_LENGTH}" -lt 4096 ]; then
|
||||
error "CA key must be at least 4096 bits long."
|
||||
fi
|
||||
KEY_LENGTH=2048
|
||||
if [ "${KEY_LENGTH}" -lt 2048 ]; then
|
||||
error "User key must be at least 2048 bits long."
|
||||
fi
|
||||
|
||||
OPENSSL_BIN=$(command -v openssl)
|
||||
SUFFIX=$(TZ=:Zulu /bin/date +"%Y%m%d%H%M%SZ")
|
||||
|
||||
if ! is_user "${PKI_USER}" || ! is_group "${PKI_USER}"; then
|
||||
error "You must create ${PKI_USER} user and group !"
|
||||
fi
|
||||
|
||||
if [ ! -e "${CONF_FILE}" ]; then
|
||||
error "${CONF_FILE} is missing"
|
||||
fi
|
||||
|
||||
mkdir -p "${CA_DIR}" "${CRT_DIR}" "${KEY_DIR}" "${CSR_DIR}" "${PKCS12_DIR}" "${OVPN_DIR}" "${TMP_DIR}"
|
||||
|
||||
command=${1:-help}
|
||||
|
||||
case "${command}" in
|
||||
init)
|
||||
shift
|
||||
init "$@"
|
||||
;;
|
||||
|
||||
ocsp)
|
||||
shift
|
||||
ocsp "$@"
|
||||
;;
|
||||
|
||||
create)
|
||||
shift
|
||||
create "$@"
|
||||
;;
|
||||
|
||||
revoke)
|
||||
shift
|
||||
revoke "$@"
|
||||
;;
|
||||
|
||||
list)
|
||||
shift
|
||||
list "$@"
|
||||
;;
|
||||
|
||||
check)
|
||||
shift
|
||||
check "$@"
|
||||
;;
|
||||
|
||||
version|--version)
|
||||
show_version
|
||||
exit 0
|
||||
;;
|
||||
|
||||
help|--help)
|
||||
show_usage
|
||||
exit 0
|
||||
;;
|
||||
|
||||
*)
|
||||
show_usage >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
# fix right
|
||||
chown -R "${PKI_USER}":"${PKI_USER}" "${CA_DIR}"
|
||||
chmod 750 "${CA_DIR}" "${CRT_DIR}" "${KEY_DIR}" "${CSR_DIR}" "${PKCS12_DIR}" "${OVPN_DIR}" "${TMP_DIR}"
|
||||
chmod 600 "${INDEX_FILE}"* "${SERIAL}"* "${CA_KEY}" "${CRL}"
|
||||
chmod 640 "${CA_CERT}"
|
||||
}
|
||||
|
||||
main "$@"
|
239
shellpki.sh
Executable file
239
shellpki.sh
Executable file
|
@ -0,0 +1,239 @@
|
|||
#!/bin/sh
|
||||
|
||||
PREFIX=/etc/openvpn/ssl
|
||||
CONFFILE=$PREFIX/openssl.cnf
|
||||
OPENSSL=$(which openssl)
|
||||
TIMESTAMP=$(/bin/date +"%s")
|
||||
WWWDIR=/var/www/htdocs/vpn/ssl
|
||||
|
||||
|
||||
if [ "$(id -u)" != "0" ]; then
|
||||
echo "Please become root before running ${0##*/}!" >&2
|
||||
echo >&2
|
||||
echo "Press return to continue..." >&2
|
||||
read REPLY
|
||||
exit 1
|
||||
fi
|
||||
|
||||
init() {
|
||||
echo "Do you confirm ${0##*/} initialization?"
|
||||
echo
|
||||
echo "Press return to continue..."
|
||||
read REPLY
|
||||
echo
|
||||
|
||||
if [ ! -d $PREFIX/ca ]; then mkdir -p $PREFIX/ca; fi
|
||||
if [ ! -d $PREFIX/ca/tmp ]; then mkdir -p $PREFIX/ca/tmp; fi
|
||||
if [ ! -d $PREFIX/certs ]; then mkdir -p $PREFIX/certs; fi
|
||||
if [ ! -d $PREFIX/files ]; then mkdir -p $PREFIX/files; fi
|
||||
if [ ! -f $PREFIX/ca/index.txt ]; then touch $PREFIX/ca/index.txt; fi
|
||||
if [ ! -f $PREFIX/files/ca/serial ]; then echo 01 > $PREFIX/ca/serial; fi
|
||||
|
||||
if [ ! -e "$CONFFILE" ]; then
|
||||
echo "$CONFFILE is missing" >&2
|
||||
echo >&2
|
||||
echo "Press return to continue..." >&2
|
||||
read REPLY
|
||||
exit 1
|
||||
fi
|
||||
|
||||
$OPENSSL dhparam -out $PREFIX/ca/dh2048.pem 2048
|
||||
$OPENSSL genrsa -out $PREFIX/ca/private.key 2048
|
||||
|
||||
$OPENSSL req \
|
||||
-config $CONFFILE \
|
||||
-new -x509 -days 3650 \
|
||||
-extensions v3_ca \
|
||||
-keyout $PREFIX/ca/private.key \
|
||||
-out $PREFIX/ca/cacert.pem
|
||||
|
||||
}
|
||||
|
||||
create() {
|
||||
echo "Please enter your CN (Common Name)"
|
||||
read cn
|
||||
echo
|
||||
echo "Your CN is '$cn'"
|
||||
echo "Press return to continue..."
|
||||
read REPLY
|
||||
echo
|
||||
|
||||
if [ -e $PREFIX/certs/$cn.crt ]; then
|
||||
echo "Please revoke actual $cn cert before creating one"
|
||||
echo
|
||||
echo "Press return to continue..."
|
||||
read REPLY
|
||||
exit 1
|
||||
fi
|
||||
|
||||
DIR=$PREFIX/files/$cn-$TIMESTAMP
|
||||
mkdir $DIR
|
||||
|
||||
# generate private key
|
||||
echo -n "Should private key be protected by a passphrase? [y/N] "
|
||||
read REPLY
|
||||
if [ "$REPLY" = "y" ] || [ "$REPLY" = "Y" ]; then
|
||||
$OPENSSL genrsa -aes128 -out $DIR/$cn.key 2048
|
||||
else
|
||||
$OPENSSL genrsa -out $DIR/$cn.key 2048
|
||||
fi
|
||||
|
||||
# generate csr req
|
||||
$OPENSSL req \
|
||||
-new \
|
||||
-key $DIR/$cn.key \
|
||||
-config $CONFFILE \
|
||||
-out $DIR/$cn.csr
|
||||
|
||||
# ca sign and generate cert
|
||||
$OPENSSL ca \
|
||||
-config $CONFFILE \
|
||||
-in $DIR/$cn.csr \
|
||||
-out $DIR/$cn.crt
|
||||
|
||||
# pem cert style
|
||||
cp $DIR/$cn.key $DIR/$cn.pem
|
||||
cat $DIR/$cn.crt >> $DIR/$cn.pem
|
||||
|
||||
# copy to public certs dir
|
||||
if [ -d "$WWWDIR" ]; then
|
||||
echo
|
||||
echo "copy cert to public certs dir"
|
||||
echo
|
||||
cp -i $DIR/$cn.crt $PREFIX/certs/
|
||||
cp -i $DIR/$cn.crt $WWWDIR/
|
||||
cp -i $DIR/$cn.key $WWWDIR/
|
||||
chown -R root:www $WWWDIR
|
||||
chmod -R u=rwX,g=rwX,o= $WWWDIR
|
||||
echo
|
||||
fi
|
||||
|
||||
# generate client configuration
|
||||
|
||||
if [ -e $PREFIX/template.conf ]; then
|
||||
|
||||
CA=/etc/openvpn/ssl/ca/cacert.pem
|
||||
CERT=/var/www/htdocs/vpn/ssl/$cn.crt
|
||||
KEY=/var/www/htdocs/vpn/ssl/$cn.key
|
||||
REP=/tmp
|
||||
|
||||
cp $PREFIX/template.conf $REP/$cn.conf
|
||||
echo "
|
||||
|
||||
<ca>
|
||||
$(cat $CA)
|
||||
</ca>
|
||||
|
||||
<cert>
|
||||
$(cat $CERT)
|
||||
</cert>
|
||||
|
||||
<key>
|
||||
$(cat $KEY)
|
||||
</key>
|
||||
" >> $REP/$cn.conf
|
||||
|
||||
echo "The configuration file is available in $REP/$cn.conf"
|
||||
fi
|
||||
}
|
||||
|
||||
revoke() {
|
||||
echo "Please enter CN (Common Name) to revoke"
|
||||
read cn
|
||||
echo
|
||||
echo "CN '$cn' will be revoked"
|
||||
echo "Press return to continue..."
|
||||
read REPLY
|
||||
echo
|
||||
|
||||
$OPENSSL ca \
|
||||
-config $CONFFILE \
|
||||
-revoke $PREFIX/certs/$cn.crt
|
||||
|
||||
rm -i $PREFIX/certs/$cn.crt
|
||||
if [ -d "$WWWDIR" ]; then
|
||||
rm -i $WWWDIR/$cn.crt
|
||||
rm -i $WWWDIR/$cn.key
|
||||
fi
|
||||
}
|
||||
|
||||
fromcsr() {
|
||||
echo "Please enter path for your CSR request file"
|
||||
read path
|
||||
echo
|
||||
|
||||
if [ ! -e $path ]; then
|
||||
echo "Error in path..." >&2
|
||||
echo >&2
|
||||
echo "Press return to continue..." >&2
|
||||
read REPLY
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Please enter the CN (Common Name)"
|
||||
read cn
|
||||
echo
|
||||
echo "Your CN is '$cn'"
|
||||
echo "Press return to continue..."
|
||||
read REPLY
|
||||
echo
|
||||
|
||||
DIR=$PREFIX/files/req_$cn-$TIMESTAMP
|
||||
mkdir $DIR
|
||||
|
||||
cp $path $DIR
|
||||
|
||||
# ca sign and generate cert
|
||||
$OPENSSL ca \
|
||||
-config $CONFFILE \
|
||||
-in $path \
|
||||
-out $DIR/$cn.crt
|
||||
|
||||
# copy to public certs dir
|
||||
echo
|
||||
echo "copy cert to public certs dir"
|
||||
echo
|
||||
cp -i $DIR/$cn.crt $PREFIX/certs/
|
||||
echo
|
||||
|
||||
}
|
||||
|
||||
|
||||
crl() {
|
||||
|
||||
$OPENSSL ca -gencrl \
|
||||
-config $CONFFILE \
|
||||
-out crl.pem
|
||||
|
||||
# TODO : a voir pour l'importation pdts Mozilla, Apple et Microsoft
|
||||
#openssl crl2pkcs7 -in crl.pem -certfile /etc/ssl/certs/cacert.pem -out p7.pem
|
||||
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
init)
|
||||
init
|
||||
;;
|
||||
|
||||
create)
|
||||
create
|
||||
;;
|
||||
|
||||
fromcsr)
|
||||
fromcsr
|
||||
;;
|
||||
|
||||
revoke)
|
||||
revoke
|
||||
;;
|
||||
|
||||
crl)
|
||||
crl
|
||||
;;
|
||||
|
||||
*)
|
||||
echo "Usage: ${0##*/} {init|create|fromcsr|revoke|crl}" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
Loading…
Reference in a new issue