openSSL Essentials
you should get familiar with OpenSSL in many ways as a system engineer. For example, you must create a CSR file for SSL renewal with Certificate Authority or create a self-signed certificate. I want to provide as many valuable examples as possible in this article.
objectives
- what is OpenSSL?
- what is CSR file?
- generating a new Private Key and CSR file
- generating CSR from existing Private Key
- generating CSR from existing Certificate and Private Key
- upload CSR to Certificate Authority
- how does CA validate your CSR?
- generating SSL Certificates
- private keys
- configuring SSL termination
- appendix
what is OpenSSL?
OpenSSL is a robust, commercial-grade, and full-featured toolkit for the Transport Layer Security (TLS) and Secure Sockets Layer (SSL) protocols. It is also a general-purpose cryptography library.
what is a CSR file?
CSR is a block of encoded text with data about your identification. As always, the Certificate Authority requires you to input the CSR for validation.
generating a new Private Key and CSR file
to generate your private key, specify the key algorithm, the key size, and an optional passphrase. The standard key algorithm is RSA, but you can also select ECDSA for specific situations.
you also should pick 2048 bits when using the RSA key algorithm and 256 bits when using the ECDSA algorithm. Any key size lower than 2048 is not secure, while a higher value may slow down the performance.
~$ openssl req -newkey rsa:2048 -nodes -keyout domain.key -out domain.csr
req PKCS#10 X.509 Certificate Signing Request ( CSR ) Management
-newkey rsa:2048 meaning that the generated key is 2048 bit with RSA algorithm.
-node meaning that the private key should not be encrypted with a pass phrase.
-new meaning that CRS is being generated.
now you need to perform CSR details.
- Common Name FQDN - fully qualified domain name - you want to secure with certificates such as www.domain.com and bread.domain.com. *.domain.com.
- Organization organization's legal name.
- Organization Unit (OU) organization's department.
- City or Locality where your organization is legally incorporated. Do not abbreviate.
- State or Province where your organization is legally incorporated. Do not abbreviate.
- Country official two-letter country code (e.g VN, US) where your organization is legally incorporated.
note
you are not required to enter a password or passphrase - optional field.
generating CSR from existing private key
use this if there is an existing private key that you would like to use to request a certificate from the Certificate Authority.
~$ openssl req -key domain.key -new -out domain.csr
req PKCS#10 X.509 Certificate Signing Request ( CSR ) Management
-key specifies an existing private key - domain.key - that will be used to generate a new CSR.
- new meaning is that CSR is being generated.
generating CSR from existing Certificate and Private Key
in which case we are using this?
when you want to renew an existing certificate but you or CA do not have the original CSR file for some reason. It saves you two ways:
- you don't need to enter the CSR information,
- you have the exact information from the existing certificate.
~$ openssl x509 -in domain.crt -signkey domain.key -x509toreq -out domain.csr
-x509toreq means that you use an X509 certificate to make a CSR.
upload CSR to Certificate Authority
it all depends on the way the Certificate Authority forms input the CSR content. You have to copy most of the content of CSR, then paste it into the CA's text box and continue completing the generation process.
after completing the validation process, you will receive the trusted SSL Certificate from the issuing Certificate Authority. This validation should take a short time.
how does CA validate your CSR?
CA always sends a text file validation to your organization to validate who you are. For instance:
File name:
5EC759A659AE545FF70383434AF91DDE.txt
Content 3631155FFA142E6490ACCB53 comodoca.com 5f029a8d387c5
what am I doing now? Well, good question. There are three things:
- build a small web server
- allow incoming port 80 (HTTP)
- create a new A record and point to this server's address.
why?
- CA will validate automatically by using port 80 (HTTP)
note
i recommend using a static web page; don't use any web page with a redirection URL.
i am using PHP to run a small web server.
~$ sudo apt install software-properties-common
~$ sudo add-apt-repository ppa:ondrej/php
~$ sudo apt install php7.3 php7.3-common php7.3-opcache php7.3-cli php7.3-gd php7.3-curl php7.3-mysql
preparing this one
~$ mkdir -p /var/lib/www
~$ vim /var/lib/www/index.html
~$ mkdir -p /var/lib/www/.well-known/pki-validation/
~$ cp /home/tphanix/5EC759A659AE545FF70383434AF91DDE.txt /var/lib/www/.well-known/pki-validation/
~$ php -S 0.0.0.0:80 -t /var/lib/www/
next, I create a new A record and point to the server's address in PowerDNS.
back to my computer, I call that URL.
~$ curl http://fusionflow.cloud/.well-known/pki-validation/5EC759A659AE545FF70383434AF91DDE.txt
~$ 3631155FFA142E6490ACCB53 comodoca.com 5f029a8d387c5
done. CA often takes a few hours later to do their job.
....
rarly afternoon, I received an email from CA with two cert bundle files.
- STAR_fusionflow_cloud.ca-bundle
- STAR_fusionflow_cloud.crt
~$ cat STAR_fusionflow_cloud.ca-bundle
--—BEGIN CERTIFICATE--—MIIGEzCCA/ugAwIBAgIQfVtRJrR2uhHbdBYLvFMNpzANBgkqhkiG9w0BAQwFADCBiDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTgxl6lFhd2zi+WJN44pDfwGF/Y4QA5C5BIG+3vzxhFoYt/jmPQT2BVPi7Fp2RBgvGQq6jG35LWjOhSbJuMLe/0CjraZwTiXWTb2qHSihrZe68Zk6s+go/lunrotEbaGmAhYLcmsJWTyXnW0OMGuf1pGg+pRyrbxmRE1a6Vqe8YAsOf4vmSyrcjC8azjUeqkk+B5yOGBQMkKW+ESPMFgKuOXwIlCypTPRpgSabuY0MLTDXJLR27lk8QyKGOHQ+SwMj4K00u/I5sUKUErmgQfky3xxzlIPK1aEn8=
--—END CERTIFICATE-——
—BEGIN CERTIFICATE--—MIIFgTCCBGmgAwIBAgIQOXJEOvkit1HX02wQ3TE1lTANBgkqhkiG9w0BAQwFADB7MQswCQYDVQQGEwJHQjEbMBkGA1UECAwSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRowGAYDVQQKDBFDb21vZG8gQ0EgTGltaXRlZDEhMB8GA1UEAwwYQUFBIENlcnRpZmljYXRlIFNlcnZpY2VzMB4XDTE5MDMxMjAwMDAwMFoXDTI4rvSNb3I8QzvAP+u431yqqcau8vzY7qN7Q/aGNnwU4M309z/+3ri0ivCRlv79Q2R+/czSAaF9ffgZGclCKxO/WIu6pKJmBHaIkU4MiRTOok3JMrO66BQavHHxW/BBC5gACiIDEOUMsfnNkjcZ7Tvx5Dq2+UUTJnWvu6rvP3t3O9LEApE9GQDTF1w52z97GA1FzZOFli9d31kWTz9RvdVFGD/tSo7oBmF0Ixa1DVBzJ0RHfxBdiSprhTEUxOipakyAvGp4z7h/jnZymQyd/teRCBaho1+V
--—END CERTIFICATE-----
~$ cat STAR_fusionflow_cloud.crt
--—BEGIN CERTIFICATE--—MIIGPTCCBSWgAwIBAgIQWCFo/8SsCXftkDPtIuR+SjANBgkqhkiG9w0BAQsFADCBjzELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMTcwNQYDVQQDEy5TZWN0aWdvIFJTQSBEb21haW4gVmFsaWRhdGlvbiBTZWN1cmUgU2VydmVyIENBlfOkl3L/y/NQSBc3pp1UgvvwXZCuKzVMdl5a3LtQo9mtjw5UsBfbcd6jhRRdAV/AIRDMwWKr2pfJlM03gE4oQHcyVySxeujtLqim0uLT+V70jhsYLOIfHNwxswEz7VoSd+p1aYAjeuaOkGjaOIoCnT4iVmaZqzxV8lkG6v3664q9AcNsl6eV4WzpGITJ2D7y2PC0pyz7si8QW8VoLvGNhEKeaxsT/kZThourimCq6/Yk378lQydAln9d+2hYLQXzipocllU3jRpQjsL7Qyo2iFE=
--—END CERTIFICATE-----
generating SSL Certificate
generating a self-signed certificate
sure, you do have it. There are two type of SSL certificate:
- trusted CA Signed SSL Certificate
- self-Signed SSL Certificate
here, I introduce the creation of the number two.
~$ openssl req -newkey rsa:2048 -nodes -keyout domain.key -x509 -days 365 -out domain.crt
-x509 means that it tells the req to create a self-signed certificate.
-days 365 means the certificate will be valid in 365 days.
note
a temporary CSR is generated to gather information associated with the certificate.
generating a self-signed certificate from an existing private key
~$ openssl req -key domain.key -new -x509 -days 365 -out domain.crt
-x509 tells req to create a self-signed certificate,
-days 365 this certificate will be valid for 365 days,
-new enables the CSR information prompt.
generating a self-signed from an existing private key and CSR
~$ openssl x509 -signkey domain.key -in domain.csr -req -days 365 -out domain.crt
private Key
create a private key
~$ openssl genrsa -des3 -out domain.key 2048
please enter a password to finish the process.
verify a private key
~$ openssl rsa -check -in domain.key
you have to enter the key's password if the key is encrypted.
verify a private key matches a certificate and CSR
there are three files:
- domain.key
- domain.crt
- domain.csr
~$ openssl rsa -noout -modulus -in domain.key | openssl md5
~$ openssl x509 -noout -modulus -in domain.crt | openssl md5
~$ openssl req -noout -modulus -in domain.csr | openssl md5
configuring SSL Termintation
with HA Proxy
do you know HA Proxy?
from these bundle files, I need to generate a PEM file.
$ cd /etc/ssl/fusionflow/
$ cat star.fusionflow.cloud.key > fusionflow_cloud_bundle.pem
$ cat STAR_fusionflow_cloud.crt >> fusionflow_cloud_bundle.pem
$ cat STAR_fusionflow_cloud.ca-bundle >> fusionflow_cloud_bundle.pem
declare this file to HA Proxy configuration.
~$ sudo vim /etc/haproxy/haproxy.cfg
global
log 127.0.0.1 local2
maxconn 2048
chroot /var/lib/haproxy
stats socket /run/haproxy/admin.sock mode 660 level admin
stats timeout 30s
user haproxy
group haproxy
daemon
tune.ssl.default-dh-param 2048
# ssl-default-bind-ciphers ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:ECDH+3DES:DH+3DES:RSA+AESGCM:RSA+AES:RSA+3DES:!aNULL:!MD5:!DSS
defaults
log global
mode http
option httplog
option dontlognull
timeout connect 5000
timeout client 50000
timeout server 50000
errorfile 400 /etc/haproxy/errors/400.http
errorfile 403 /etc/haproxy/errors/403.http
errorfile 408 /etc/haproxy/errors/408.http
errorfile 500 /etc/haproxy/errors/500.http
errorfile 502 /etc/haproxy/errors/502.http
errorfile 503 /etc/haproxy/errors/503.http
errorfile 504 /etc/haproxy/errors/504.http
frontend pm
bind *:443 ssl crt /etc/ssl/fusionflow/fusionflow_cloud_bundle.pem ciphers ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:ECDH+3DES:DH+3DES:RSA+AES:RSA+3DES:!ADH:!AECDH:!MD5:!DSS no-sslv3
mode http
default_backend pm
backend pm
mode http
option forwardfor
option httpchk HEAD / HTTP/1.1\r\nHost:localhost
server pm-1 10.17.10.11:80 check
http-request set-header X-Forwarded-Port %[dst_port]
http-request add-header X-Forwarded-Proto https if { ssl_fc }
then restart HA Proxy
$ sudo haproxy -c -V -f /etc/haproxy/haproxy.cfg
$ sudo systemctl restart haproxy
with Nginx
~$ sudo vim /etc/nginx/sites-available/domain.conf
server {
listen *:80;
server_name domain.com www.domain.com;
return 301 https://www.domain.com$request_uri;
}
server {
listen *:443 ssl http2;
server_name domain.com www.domain.com;
ssl on;
ssl_certificate /etc/nginx/ssl/STAR_fusionflow_cloud.crt;
ssl_certificate_key /etc/nginx/ssl/ star.fusionflow.cloud.key;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 5m;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA;
ssl_prefer_server_ciphers on;
ssl_dhparam /etc/nginx/ssl/dhparam.pem;
index index.html index.htm index.php;
root /home/www;
access_log /var/log/nginx/domain.com.access.log combined;
error_log /var/log/nginx/domain.com.error.log;
location / {
try_files $uri $uri/ /index.php?q=$uri&$args;
}
location ~ \.php$ {
include /etc/nginx/fastcgi_params;
try_files $uri =404;
fastcgi_pass unix:/run/php/php7.0-fpm.sock;
fastcgi_param SCRIPT_FILENAME /home/www/domain$fastcgi_script_name;
}
location ~* .(woff|eot|ttf|svg|mp4|webm|jpg|jpeg|png|gif|ico|css|js)$ {
expires 365d;
}
}
with Kubernetes
creating Kubernetes Secret
~$ kubectl create secret tls fusionflow-cloud-cert --key=/tmp/star.fusionflow.cloud.key --cert=/tmp/STAR_fusionflow_cloud.crtsecret/fusionflow-cloud-cert created
show these current secrets.
~$ kubectl get secretNAME TYPE DATA AGEdefault-token-pj5pb kubernetes.io/service-account-token 3 15dfusionflow-cloud-cert kubernetes.io/tls 2 2m17s
and Kubernetes ingress
~$ kubectl get ingress -ANAMESPACE NAME HOSTS PORTS AGEdefault cloud-gw cloud-gw.domain.com 80, 443 14dkube-system traefik-dashboard traefik.domain.com 80, 443 14d
update the new secret to these secrets.
~$ kubectl edit ingress cloud-gw-apiingress.extensions/cloud-gw-api edited
~$ kubectl edit ingress traefik-dashboard -n kube-systemingress.extensions/traefik-dashboard edited
with Docker
Traefik is an ingress in our Docker environment. So there are two things to do:
- update these SSL certificates to SSL declaration in Traefik.
- import these SSL certificates to Docker Secret.
go to Docker Swarm server, then import these SSLs into Docker Secret locally.
~$ docker secret create star.fusionflow.cloud.key star.fusionflow.cloud.key
7p17b9t8wf46z7sgcadfx2jsc
~$ docker secret create STAR_fusionflow_cloud.crt STAR_fusionflow_cloud.crt
q7b241yqyp3onqyg9xrzpkinm
back to my computer, then go to Traefik directory
/opt/traefik$ ls -lrt
total 44
-rw-r--r– 1 root root 207 Feb 27 07:34 Dockerfile
drwxr-xr-x 2 root root 4096 Feb 27 07:34 certs
-rw-r--r– 1 root root 499 Feb 27 07:35 README.md
-rw-r--r– 1 root root 1028 Jun 5 09:16 traefik.toml
-rw-r--r– 1 root root 1216 Jun 5 09:16 stack.yml
-rw-r--r– 1 root root 1704 Jun 5 09:16 star.fusionflow.cloud.key
-rw-r--r– 1 root root 6477 Jun 5 09:16 STAR_fusionflow_cloud.crt
and edit stack.yml
version: "3.1"
services:
lb:
image: "registry-aws.domain.com/traefik:aws-production"
ports:
- "80:80"
- "443:443"
- "8443:8443"
networks:
- swarm
- weavenet
- sky
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- traefik:/data
command:
- --configfile=/etc/traefik.toml
deploy:
restart_policy:
condition: on-failure
replicas: 1
placement:
constraints:
- "node.role == manager"
resources:
limits:
cpus: '2'
memory: 512m
secrets:
- star.fusionflow.cloud.key
- STAR_fusionflow_cloud.crt
networks:
swarm:
external:
name: swarm-overlay-default
weavenet:
external:
name: weavenet-default
sky:
external:
name: sky-demo
volumes:
traefik:
driver: local
secrets:
star.fusionflow.cloud.key:
external: true
STAR_fusionflow_cloud.crt:
external: true
edit traefik.toml
logLevel = "ERROR"
defaultEntryPoints = ["http", "https"]
[entryPoints]
[entryPoints.http]
address = ":80"
compress = true
[entryPoints.http.redirect]
entryPoint = "https"
[entryPoints.https]
address = ":443"
[entryPoints.https.tls]
MinVersion = "VersionTLS12"
[[entryPoints.https.tls.certificates]]
CertFile = "/run/secrets/STAR_fusionflow_cloud.crt"
KeyFile = "/run/secrets/star.fusionflow.cloud.key"
# To enable compression support using gzip format:
[accessLog]
[docker]
endpoint = "unix:///var/run/docker.sock"
domain = "docker.localhost"
watch = true
exposedbydefault = false
swarmmode = true
[web.auth.basic]
usersFile = "/etc/traefik.htpasswd"
[web.statistics]
RecentErrors = 100
then, push these changes to the GitLab repository. We will pull them into the Docker Swarm server later.
now, I will review the Docker file, create an image, and push it to the Docker private repository.
FROM traefik:v1.7-alpine
MAINTAINER "tien.phan@domain.com"
RUN apk add --update \
bash \
&& rm -rf /var/cache/apk/*
COPY ./certs/htpasswd /etc/traefik.htpasswd
ADD ./traefik.toml /etc/traefik.toml
~$ docker login --username=tien.phan --password=mypassword
~$ docker build -t registry-aws.domain.com/traefik:aws-production .
~$ docker push registry-aws.domain.com/traefik:aws-production
go to Docker Swarm server, pull the new image
~$ docker pull registry-aws.domain.com/traefik:aws-production
aws-production: Pulling from traefik
cbdbe7a5bc2a: Already exists
f16506d32a25: Pull complete
dbc48c2837a0: Pull complete
a6175123f565: Pull complete
d98de5a4d5d4: Pull complete
8b8846e49723: Pull complete
28db12fabf2a: Pull complete
Digest: sha256:5cdc797884454490653415eb0b1ee151ca1fa186ff1804ef1f49020a99b1ea31
Status: Downloaded newer image for registry-aws.domain.com/traefik:aws-production
deploy stack
/opt/traefik$ docker stack deploy --with-registry-auth -c stack.yml traefik
Updating service traefik_lb (id: tedcvs5rsqyujcm69183xq7pm)
references
- What is OpenSSL and how it works?
- How to Generate a CSR for Nginx (OpenSSL)
- OpenSSL Essentials: Working with SSL Certificates, Private Keys and CSRs
- Generate a private key and a CSR(Certificate Signing Request )
- Use of Public key in the certificate signing request ( CSR )
appendix
my colleague knows that I have been writing this article. So he would like to contribute one script. Surely, welcome him.
#!/bin/bash
echo "Step 1: Create CA (Certificate Authority)"
# create a 2048-bit private key, using the RSA algorithm
openssl genrsa -out ca.key 2048
# generate an X509 certificate with the above private key
openssl req -new -x509 -days 3650 -key ca.key -out ca.crt -subj "/C=VN/L=HCM/CN=n7n"
echo "Step 2: Create server key pair / certificate request"
# private key
openssl genrsa -out server.key 2048
# CSR (Certificate Signing Request)
openssl req -new -out server.csr -key server.key -subj "/C=VN/L=HCM/CN=n8n"
# certificate
openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt -days 360 -sha256
echo "Step 3: Verify a Private Key Matches a Certificate and CSR"
openssl verify -CAfile ca.crt server.crt
thank you, Mitchell Anicas from DigitalOcean, ssldragon, The SSL Store, and Nguyen Trong Nguyen, for your valuable resources. I enjoyed your stuff and then wrote this article.
for everyone, it will work for you.