Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[THREESCALE-11404] Adding support for CRL and OCSP #1503

Merged
merged 12 commits into from
Mar 10, 2025

Conversation

tkan145
Copy link
Contributor

@tkan145 tkan145 commented Oct 30, 2024

What

https://issues.redhat.com/browse/THREESCALE-11404

Notes

Verification steps

  • Checkout this branch and build a new runtime-image
make runtime-image IMAGE_NAME=apicast-test
  • Get into dev-environment
cd dev-environments/listen-tls

Prepare certs

  • Prepare files
cd cert
touch index.txt
echo 1000 > serial
echo 1000 > crlnumber
  • Edit Makefile as follow
diff --git a/dev-environments/listen-tls/cert/Makefile b/dev-environments/listen-tls/cert/Makefile
index 1739aa70..4d587d01 100644
--- a/dev-environments/listen-tls/cert/Makefile
+++ b/dev-environments/listen-tls/cert/Makefile
@@ -1,15 +1,88 @@
+DOMAIN=example.com
+
 clean:
 	- rm *.crt *.key *.pem *.csr
 
-ca: 
-	openssl genrsa -out rootCA.key 2048 
-	openssl req -batch -new -x509 -nodes -key rootCA.key -sha256 -days 1024 -out rootCA.pem
+all: ca intermediate ca-chain ocsp server client revoked_client crl client-chain revoked-client-chain
 
-clientcerts:	
+ca:
+	openssl genrsa -out rootCA.key.pem 2048
+	openssl req -config root-ca.cnf \
+	  -key rootCA.key.pem \
+	  -new -x509 -days 3650 -sha256 -extensions v3_ca \
+      -out rootCA.cert.pem \
+	  -subj "/C=US/CN=ca.$(DOMAIN)"
+
+intermediate:
+	openssl genrsa -out intermediate.key.pem 2048
+	openssl req -config intermediate.cnf \
+      -key intermediate.key.pem \
+      -new -sha256 \
+      -out intermediate.csr.pem \
+      -subj "/CN=intermediate-cert.$(DOMAIN)"
+	openssl ca -config root-ca.cnf \
+      -extensions v3_intermediate_ca -days 2650 -notext -batch \
+      -in intermediate.csr.pem \
+      -out intermediate.cert.pem
+
+ca-chain:
+	cat intermediate.cert.pem rootCA.cert.pem > ca-chain.cert.pem
+
+crl:
+	openssl ca -config intermediate.cnf \
+	  -gencrl -out intermediate.crl.pem
+
+ocsp:
+	openssl genrsa -out ocsp.$(DOMAIN).key.pem 2048
+	openssl req -config intermediate.cnf -new -sha256 \
+      -key ocsp.$(DOMAIN).key.pem \
+      -out ocsp.$(DOMAIN).csr.pem \
+      -nodes \
+      -subj "/CN=ocsp.$(DOMAIN)"
+	openssl ca -config intermediate.cnf \
+	  -extensions v3_ocsp -days 2650 -notext -batch \
+	  -in ocsp.$(DOMAIN).csr.pem \
+	  -out ocsp.$(DOMAIN).cert.pem
+
+server:
 	openssl req -subj '/CN=$(DOMAIN)'  -newkey rsa:4096 -nodes \
 			-sha256 \
 			-days 3650 \
 			-keyout $(DOMAIN).key \
-			-out $(DOMAIN).csr 
+			-out $(DOMAIN).csr
 	chmod +r $(DOMAIN).key
-	openssl x509 -req -in $(DOMAIN).csr -CA rootCA.pem -CAkey rootCA.key -CAcreateserial -out $(DOMAIN).crt -days 500 -sha256
+	openssl ca -config root-ca.cnf \
+	  -extensions v3_intermediate_ca -days 2650 -notext -batch \
+	  -in $(DOMAIN).csr \
+	  -out $(DOMAIN).pem
+
+client:
+	openssl genrsa -out client.key.pem 2048
+	openssl req -config intermediate.cnf -new -sha256 \
+		  -key client.key.pem \
+		  -out client.csr.pem \
+		  -nodes \
+		  -subj "/CN=client-cert.$(DOMAIN)"
+	echo -e "y\ny\n" | openssl ca -config intermediate.cnf \
+		  -extensions v3_leaf -days 375 -notext -md sha256 \
+		  -in client.csr.pem \
+		  -out client.cert.pem
+
+revoked_client:
+	openssl genrsa -out revoked_client.key.pem 2048
+	openssl req -config intermediate.cnf -new -sha256 \
+		  -key revoked_client.key.pem \
+		  -out revoked_client.csr.pem \
+		  -nodes \
+		  -subj "/CN=revoked_client-cert.$(DOMAIN)"
+	echo -e "y\ny\n" | openssl ca -config intermediate.cnf \
+		  -extensions v3_leaf -days 375 -notext -md sha256 \
+		  -in revoked_client.csr.pem \
+		  -out revoked_client.cert.pem
+	openssl ca -config intermediate.cnf -revoke revoked_client.cert.pem
+
+client-chain:
+	cat client.cert.pem intermediate.cert.pem > client-chain.cert.pem
+
+revoked-client-chain:
+	cat revoked_client.cert.pem intermediate.cert.pem > revoked_client-chain.cert.pem
  • Create root-ca.cnf
[ ca ]
default_ca      = CA_default            # The default ca section

[ CA_default ]
dir             = .             # Where everything is kept
certs           = $dir/certs            # Where the issued certs are kept
database        = $dir/index.txt        # database index file.
                                        # several certs with same subject.
new_certs_dir   = $dir            # default place for new certs.
certificate     = $dir/rootCA.cert.pem       # The CA certificate
serial          = $dir/serial           # The current serial number
crlnumber       = $dir/crlnumber        # the current crl number
                                        # must be commented out to leave a V1 CRL
private_key     = $dir/rootCA.key.pem # The private key

name_opt        = ca_default            # Subject Name options
cert_opt        = ca_default            # Certificate field options

default_days    = 365                   # how long to certify for
default_crl_days= 30                    # how long before next CRL
default_md      = sha256                # use SHA-256 by default
preserve        = no                    # keep passed DN ordering
policy          = policy_match

[ policy_match ]
organizationalUnitName  = optional
commonName              = supplied
emailAddress            = optional

[ policy_anything ]
countryName             = optional
stateOrProvinceName     = optional
localityName            = optional
organizationName        = optional
organizationalUnitName  = optional
commonName              = supplied
emailAddress            = optional

[ req ]
default_bits            = 4096
default_md              = sha256
default_keyfile         = privkey.pem
distinguished_name      = req_distinguished_name
x509_extensions         = v3_ca
string_mask             = nombstr

[ req_distinguished_name ]
countryName                     = Country Name (2 letter code)
countryName_default             = US
countryName_min                 = 2
countryName_max                 = 2
stateOrProvinceName             = State or Province Name (full name)
stateOrProvinceName_default     = CA
localityName                    = Locality Name (eg, city)
localityName_default            = Testland
0.organizationName              = Organization Name (eg, company)
0.organizationName_default      = Testers
organizationalUnitName          = Organizational Unit Name (eg, section)
commonName                      = Common Name (eg, your name or your server\'s hostname)
commonName_max                  = 64
emailAddress                    = Email Address
emailAddress_max                = 64
emailAddress_default            = sec@test.testing

[ v3_ca ]
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid:always,issuer
basicConstraints = critical,CA:true

[ v3_intermediate_ca ]
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer
basicConstraints = critical, CA:true, pathlen:0
keyUsage = critical, digitalSignature, cRLSign, keyCertSign
  • Create intermediate.cnf
[ ca ]
default_ca      = CA_default            # The 

[ CA_default ]
dir             = .
certificate     = $dir/intermediate.cert.pem   
database        = $dir/index.txt
new_certs_dir   = $dir
serial          = $dir/serial
crlnumber       = $dir/crlnumber

private_key     = $dir/intermediate.key.pem

name_opt        = ca_default
cert_opt        = ca_default

default_days    = 365
default_crl_days= 30
default_md      = sha256
preserve        = no
policy          = policy_anything

[ policy_match ]
organizationalUnitName  = optional
commonName              = supplied
emailAddress            = optional

[ policy_anything ]
countryName             = optional
stateOrProvinceName     = optional
localityName            = optional
organizationName        = optional
organizationalUnitName  = optional
commonName              = supplied
emailAddress            = optional

[ req ]
default_bits            = 4096
default_md              = sha256
default_keyfile         = privkey.pem
distinguished_name      = req_distinguished_name
x509_extensions         = v3_ca
string_mask             = nombstr

[ req_distinguished_name ]
countryName                     = Country Name (2 letter code)
countryName_default             = US
countryName_min                 = 2
countryName_max                 = 2
stateOrProvinceName             = State or Province Name (full name)
stateOrProvinceName_default     = CA
localityName                    = Locality Name (eg, city)
localityName_default            = Testland
0.organizationName              = Organization Name (eg, company)
0.organizationName_default      = Testers
organizationalUnitName          = Organizational Unit Name (eg, section)
commonName                      = Common Name (eg, your name or your server\'s hostname)
commonName_max                  = 64
emailAddress                    = Email Address
emailAddress_max                = 64
emailAddress_default            = sec@test.testing

[ v3_ca ]
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid:always,issuer
basicConstraints = critical,CA:true

[ v3_intermediate_ca ]
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer
basicConstraints = critical, CA:true, pathlen:0
keyUsage = critical, digitalSignature, cRLSign, keyCertSign
extendedKeyUsage = OCSPSigning

[ v3_ocsp ]
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
extendedKeyUsage = OCSPSigning

[ v3_leaf ]
extendedKeyUsage = serverAuth, clientAuth
authorityInfoAccess = OCSP;URI:http://ocsp:2560
  • Generate certs
make all

Prepare docker

  • Go back to listen-tls folder
  • Create docker-entrypoint.sh
cat << EOF > docker-entrypoint.sh
#!/bin/sh

#This entrypoint is responsible for leaving the OSCP running to accept requests
openssl ocsp -url http://0.0.0.0:2560 -text \
      -index /cert/index.txt \
      -CA /cert/ca-chain.cert.pem \
      -rkey /cert/ocsp.example.com.key.pem \
      -rsigner /cert/ocsp.example.com.cert.pem
EOF
  • Generate a Dockerfile for OCSP server
cat << EOF > ocsp.Dockerfile
FROM mirror.gcr.io/library/alpine:3

RUN apk --no-cache add openssl

COPY cert /cert

COPY docker-entrypoint.sh docker-entrypoint.sh
EXPOSE 2560
ENTRYPOINT [ "/bin/sh", "docker-entrypoint.sh" ]
EOF
  • Edit docker-compose.yaml
diff --git a/dev-environments/listen-tls/docker-compose.yml b/dev-environments/listen-tls/docker-compose.yml
index 476ca81f..2923eed3 100644
--- a/dev-environments/listen-tls/docker-compose.yml
+++ b/dev-environments/listen-tls/docker-compose.yml
@@ -10,8 +10,9 @@ services:
     - two.upstream
     environment:
       APICAST_HTTPS_PORT: 8443
-      APICAST_HTTPS_CERTIFICATE: /var/run/secrets/apicast/example.com.crt
+      APICAST_HTTPS_CERTIFICATE: /var/run/secrets/apicast/example.com.pem
       APICAST_HTTPS_CERTIFICATE_KEY: /var/run/secrets/apicast/example.com.key
+      APICAST_HTTPS_VERIFY_DEPTH: 2
       THREESCALE_CONFIG_FILE: /tmp/config.json
       THREESCALE_DEPLOYMENT_ENV: staging
       APICAST_CONFIGURATION_LOADER: lazy
@@ -38,3 +39,13 @@ services:
     image: quay.io/kuadrant/authorino-examples:talker-api
     expose:
       - "8080"
+  ocsp:
+    build:
+      dockerfile: ./ocsp.Dockerfile
+    ports:
+      - "2560:2560"
+    expose:
+      - "2560"
+    volumes:
+      - ./cert:/cert
+

Verify CRL

  • Edit apicast-config.json
diff --git a/dev-environments/listen-tls/apicast-config.json b/dev-environments/listen-tls/apicast-config.json
index 06014cab..79dcc31d 100644
--- a/dev-environments/listen-tls/apicast-config.json
+++ b/dev-environments/listen-tls/apicast-config.json
@@ -11,6 +11,18 @@
           "host": "backend"
         },
         "policy_chain": [
+          {
+            "name": "apicast.policy.tls_validation",
+            "configuration": {
+                "whitelist": [
+                    {"pem_certificate": " <intermediate_cert> "}
+                ],
+                "revoke_list": [
+                    {"pem_certificate": "<crl_cert>"}
+                ],
+                "revocation_check_type": "crl"
+            }
+          },
           {
             "name": "apicast.policy.apicast"
           }

Replace <intermediate_cert> with the content from cert/intermediate.cert.pem and <crl_cert> with content from cert/intermediate.crl.pem

  • Start the gateway
make gateway IMAGE_NAME=apicast-test
  • Send request, you should receive HTTP1/1 200
curl --resolve example.com:8443:127.0.0.1 -v --cacert cert/rootCA.cert.pem --cert cert/client.cert.pem --key cert/client.key.pem "https://example.com:8443/?user_key=123"
  • Send another request with revoked certificate
curl --resolve example.com:8443:127.0.0.1 -v --cacert cert/rootCA.cert.pem --cert cert/revoked_client.cert.pem --key cert/revoked_client.key.pem "https://example.com:8443/?user_key=123"

This time you should receive 400

  • Stop the gateway
CTRL-C

Verify OCSP

  • Edit apicast-config.json as follow
diff --git a/dev-environments/listen-tls/apicast-config.json b/dev-environments/listen-tls/apicast-config.json                                                                                                                            
index 06014cab..74242147 100644                                                                                                                                                                                                           
--- a/dev-environments/listen-tls/apicast-config.json                                                                                                                                                                                     
+++ b/dev-environments/listen-tls/apicast-config.json                                                                                                                                                                                     
@@ -11,6 +11,19 @@                                                                                                                                                                                                                        
           "host": "backend"                                                                                                                                                                                                              
         },                                                                                                                                                                                                                               
         "policy_chain": [                                                                                                                                                                                                                
+            {                                                                                                                                                                                                                            
+                "name": "tls_validation",                                                                                                                                                                                                
+                "version": "builtin",                                                                                                                                                                                                    
+                "configuration": {                                                                                                                                                                                                       
+                    "whitelist": [                                                                                                                                                                                                       
+                        {                                                                                                                                                                                                                
+                            {"pem_certificate": " <intermediate_cert> "}                                                                                                                                                                                       
+                        }                                                                                                                                                                                                                
+                    ],                                                                                                                                                                                                                                                                                                                                                                                                
+                    "revocation_check_type": "ocsp"                                                                                                                                                                                      
+                }                                                                                                                                                                                                                        
+          },                                                                                                                                                                                                                             
           {                                                                                                                                                                                                                              
             "name": "apicast.policy.apicast"                                                                                                                                                                                             
           }                                                                                                                                                                                                                              
  • Start gateway again
make gateway IMAGE_NAME=apicast-test
  • Send request, you should receive 200
curl --resolve example.com:8443:127.0.0.1 -v --cacert cert/rootCA.cert.pem --cert cert/client-chain.cert.pem --key cert/client.key.pem "https://example.com:8443/?user_key=123" --http1.1
  • Send request with revoked certificate chain
curl --resolve example.com:8443:127.0.0.1 -v --cacert cert/rootCA.cert.pem --cert cert/revoked_client-chain.cert.pem --key cert/revoked_client.key.pem "https://example.com:8443/?user_key=123" --http1.1

You should receive 400 this time. Also check for the following line the the log

TLS certificate validation failed, err: failed to validate OCSP response: certificate status "revoked" in the OCSP response,

NOTE: if you see unkown status in the log, stop the container and get inside the cert folder and run make crl to update the index file

@tkan145 tkan145 requested a review from a team as a code owner October 30, 2024 06:43
@tkan145 tkan145 force-pushed the THREESCALE-11404-crl-and-ocsp branch 4 times, most recently from cd952f8 to 680ea5f Compare February 5, 2025 06:54
@tkan145 tkan145 changed the title WIP - [THREESCALE-11404] Adding support for CRL and OCSP [THREESCALE-11404] Adding support for CRL and OCSP Feb 10, 2025
@tkan145 tkan145 force-pushed the THREESCALE-11404-crl-and-ocsp branch from 680ea5f to 3f595c0 Compare February 10, 2025 05:51
@tkan145 tkan145 force-pushed the THREESCALE-11404-crl-and-ocsp branch 4 times, most recently from 20e5708 to 5bad578 Compare March 3, 2025 07:12
@tkan145 tkan145 force-pushed the THREESCALE-11404-crl-and-ocsp branch from 5bad578 to 8b1cb6b Compare March 3, 2025 08:01
@tkan145 tkan145 force-pushed the THREESCALE-11404-crl-and-ocsp branch from 635d76c to b03fc49 Compare March 3, 2025 09:13
tkan145 added 5 commits March 5, 2025 17:24
Previously, when the certificate was provided as a single line string,
the openssl library would normalize the string before converting it to
a format valid for X509. However, this was ignored when we migrated to
lua-resty-openssl.
@tkan145 tkan145 force-pushed the THREESCALE-11404-crl-and-ocsp branch from 3c0b6bf to 17b9e5b Compare March 6, 2025 06:52
Copy link
Member

@eguzki eguzki left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great job! 🎖️

consider adding a new dev environment (on a follow up PR) with all the settings from the verification step included. It is nice to have a OCSP scenario easy to reproduce.

@tkan145 tkan145 merged commit 5f7cb92 into 3scale:master Mar 10, 2025
11 of 12 checks passed
@tkan145 tkan145 deleted the THREESCALE-11404-crl-and-ocsp branch March 10, 2025 00:02
tkan145 added a commit to tkan145/APIcast that referenced this pull request Mar 19, 2025
In PR 3scale#1503 we return nil and error in case of an error, however some
code paths do not handle the error but instead check the error field of
the response object and lead to unexpected behavior.

With this PR, we will return a response object with the error field set.
@tkan145 tkan145 mentioned this pull request Mar 19, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants