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

Older certs for a CN cannot be revoked #626

Closed
tecoboot opened this issue Aug 7, 2022 · 27 comments · Fixed by #690
Closed

Older certs for a CN cannot be revoked #626

tecoboot opened this issue Aug 7, 2022 · 27 comments · Fixed by #690

Comments

@tecoboot
Copy link

tecoboot commented Aug 7, 2022

With the unique_subject set to no, it is possible to add unlimited certs for a CN.
EasyRSA only maintains all info for the last issued cert for a CN. Earlier created certs for a CN are tracked by their serial and serial.pem is available. This makes it not so easy to manage.

EasyRSA has no function to list all issued certs and to revoke the older issued certs for a CN. List can be done with cat | grep index.txt, revoke is almost impossible.

This problem is created with the work_in_progress for renewal (#286).

IMHO, because EasyRSA is updated to manage multiple issued certs for a CN, it must keep track of all of these and not only the last issued cert.

One method solving the issue would be to restructure the database, with a single copy of items which will be kept forever and usage of symbolic links in various folders. This could be difficult to implement for non-*x systems.

Or just use current data for list and revoke. Maybe index.txt is enough to support commands like list-certs [ all | valid | revoked ] (instead of cat index.txt or egrep ^V index.txt / egrep ^R index.txt). And the revoke-all < filename_base > and revoke-older < filename_base > commands to revoke all or the older certs for a CN. Maybe add a revoke-serial < serial > .

Meanwhile, unique_subject = yes has to be supported. For users that want to keep the EasyRSA2 behaviour can put it on yes. So maybe implement a unique_subject yes | no command and keep it on yes until the issues are fixed..

@TinCanTech
Copy link
Collaborator

From version 3.1, there is a new renew mechanism, which does not keep the renewed files by serial number.

There is also a new command revoke-renewed, which can revoke renewed certificates.

There is also another new command rewind-renew, which undoes the old renew mechanism and then allows certificates to be revoked by using revoke-renewed.

I do not know what the plans are for EasyRSA versions prior to version 3.1 to correct the revoking of renewed certificates problem.

I do not expect there to be a way to switch between unique_subject mode yes or no. Once upgraded, your PKI will function as required.

@TinCanTech
Copy link
Collaborator

TinCanTech commented Aug 7, 2022

@tecoboot would you consider testing version 3.1.1, which is currently git/master ?

That would help us a great deal.

@TinCanTech TinCanTech self-assigned this Aug 7, 2022
@TinCanTech
Copy link
Collaborator

TinCanTech commented Aug 7, 2022

To test, simply make a copy of your current PKI and download git/master/easyrsa into your copy PKI.

To save your currently renewed certificates you need to use:

  • rewind-renew <serial-number>.
    This can be done for each unique commonName of your certificates.

Then you can revoke a renewed certificate by using:

  • revoke-renewed <commonName>.

Finally, update your Certificate Revocation List [CRL]:

  • gen-crl
    And upload the new CRL to your OpenVPN Server.

@TinCanTech
Copy link
Collaborator

TinCanTech commented Aug 7, 2022

EasyRSA version 3.1.1 (git/master) now includes these status reports:

  • show-expire - List certificates which will expire soon.
  • show-revoke - List certificates which are revoked.
  • show-renew - List certificates which have been renewed.

Tempting tools for upgrading .. 🍰 🍺

@TinCanTech
Copy link
Collaborator

Linking #609

@tecoboot
Copy link
Author

tecoboot commented Aug 8, 2022

OK, I'll produce test results. Attached test result with master. Issue 626 - problem.txt

Couple of findings:

  • vars handling is different in v3.0.6 and current master;
  • with unique_subject = no, it is not possible to add same client-1 with build-client-full, but it is possible with sign-req;
  • with easyrsa, current status of ollder issues certs cannot be shown;
  • with multiple certs for client-1, it is hard or impossible to revoke the older.
    Now I'll try the new rewind functions, please wait for results.

@tecoboot
Copy link
Author

tecoboot commented Aug 8, 2022

There is an issue with show-expire caused by the older but still valid certs:

.. show-expire:
./easyrsa: 3478: [: Illegal number:
./easyrsa: 3478: [: Illegal number:

Line is: if [ "$cert_expire_date_s" -lt "$cutoff_date_s" ]; then

Error (with bash -x): + '[' '' -lt 1667721397 ']'

Before: + cert_expire_date_s=

So $cert_expire_date_s is empty. The older certs were indeed not expired.

These are the certs that I want to see with show-older (or a better command).

@tecoboot
Copy link
Author

tecoboot commented Aug 8, 2022

The rewind-renew doesn't work for these older certs.
Issue 626 - revoke-renewed.txt

Easy-RSA error:
Unable to revoke as no renewed certificate was found.
Certificate was expected at: /root/easy-rsa/easyrsa3/pki/renewed/issued/17F4A03B99101C17986D7CC955855B86.crt

The problem is that the certs were not renewed but added with sign-req's.

@tecoboot
Copy link
Author

tecoboot commented Aug 8, 2022

Here first attempt to fix show-expire or basics for show-older:

root@host:~/easy-rsa/easyrsa3# git diff easyrsa
diff --git a/easyrsa3/easyrsa b/easyrsa3/easyrsa
index 256c878..1464a03 100755
--- a/easyrsa3/easyrsa
+++ b/easyrsa3/easyrsa
@@ -3475,10 +3475,17 @@ expire_status() {
        cert_date_to_timestamp_s "$cert_type_date" # Assigns timestamp_s
        cutoff_date_s="$timestamp_s"
 
-       if [ "$cert_expire_date_s" -lt "$cutoff_date_s" ]; then
-               # Cert expires in less than grace period
-               printf '%s%s\n' "$db_status | Serial: $db_serial | " \
+       if [ ! -z "$cert_expire_date_s" ]; then
+               if [ "$cert_expire_date_s" -lt "$cutoff_date_s" ]; then
+                       # Cert expires in less than grace period
+                       printf '%s%s\n' "$db_status | Serial: $db_serial | " \
                        "Expires: $cert_not_after_date | CN: $db_cn"
+               fi
+       else
+               # FIXME - show older certs
+               printf '%s%s\n' "FIXME - $db_status | Serial: $db_serial | " \
+               "Expires: $(openssl x509 -in ./pki/certs_by_serial/$db_serial.pem -text | \
+                               awk -F\: '/Not After/{print $2":"$3":"$4}') | CN: $db_cn"
        fi
 } # => expire_status()
 
root@host:~/easy-rsa/easyrsa3# ./easyrsa show-expire

Notice
------
* Showing certificates which expire in less than 90 days (--renew-days):

FIXME - V | Serial: 37D51BF4F7FA5A987F4730A3F3D255CD | Expires:  Nov 10 08:48:54 2024 GMT | CN: client-1
FIXME - V | Serial: 17F4A03B99101C17986D7CC955855B86 | Expires:  Nov 10 08:48:54 2024 GMT | CN: client-1

root@host:~/easy-rsa/easyrsa3# 

@TinCanTech
Copy link
Collaborator

TinCanTech commented Aug 8, 2022

@tecoboot thanks for testing.

  • vars handling is different in v3.0.6 and current master

True - However, the only serious difference is that the preferred location of vars is now to be within the PKI folder.

  • with unique_subject = no, it is not possible to add same client-1 with build-client-full,

That is correct functionality.

but it is possible with sign-req

That must be a bug, I'll have to take a closer look.
Edit: Fixed via 5b4fd2b

  • with easyrsa, current status of ollder issues certs cannot be shown

That can be addressed and fixed.

  • with multiple certs for client-1, it is hard or impossible to revoke the older

From what I can gather here; You created your client-1 multiple certificates in a completely unsupported way, on a PKI that should not allow you to create duplicate CN certificates.

  • Now I'll try the new rewind functions

rewind-renew only works for certificates which were renewed the old way, using easyrsa version 3.0.x.

rewind-renew will not work on a certificate which has been renewed using easyrsa version 3.1.x (git/master).

@TinCanTech
Copy link
Collaborator

To test, simply make a copy of your current PKI and download git/master/easyrsa into your copy PKI.

For rewind-renew to work, you must use this copy of your old PKI.

@TinCanTech
Copy link
Collaborator

Here first attempt to fix show-expire or basics for show-older

Thanks for your work, I'll take a closer look soon.

@TinCanTech
Copy link
Collaborator

Your work for show-older is based on certificates which should not exist, so I cannot proceed with that option.

@tecoboot
Copy link
Author

tecoboot commented Aug 8, 2022

  • vars handling is different in v3.0.6 and current master

True - However, the only serious difference is that the preferred location of vars is now to be within the PKI folder.

OK now.
I started with Debian Bullseye, with easyrsa 3.0.8. Then the file was copied, not moved.
This change needs an update in my install scripts.

@tecoboot
Copy link
Author

tecoboot commented Aug 8, 2022

Your work for show-older is based on certificates which should not exist, so I cannot proceed with that option.

OK, with the protection by 5b4fd2b it is not needed.

With the patch, the to be revoked certs also cannot exists.

I'm a bit concerned on installed easyrsa, that lacks the protection. Updating in for example Debian takes some time (years).

@TinCanTech
Copy link
Collaborator

@tecoboot good point.

@ecrist should 5b4fd2b be applied to 3.0 series ?

@tecoboot
Copy link
Author

tecoboot commented Aug 8, 2022

EasyRSA version 3.1.1 (git/master) now includes these status reports:

  • show-expire - List certificates which will expire soon.
  • show-revoke - List certificates which are revoked.
  • show-renew - List certificates which have been renewed.

Tempting tools for upgrading .. 🍰 🍺

The show-all function: ./easyrsa --renew-days=99999 show-expire

@tecoboot
Copy link
Author

tecoboot commented Aug 8, 2022

Have a fix for the "corrupt" PKI?
Now I have:

root@host:~/easy-rsa/easyrsa3# ./easyrsa --renew-days=99999 show-expire

Notice
------
* Showing certificates which expire in less than 99999 days (--renew-days):

V | Serial: F8959E69B85CE56299006C3EA2F600C7 | Expires: Apr  6 14:27:17 2077 GMT | CN: server
V | Serial: 036D8E15DFC0AF9F5E68CB4DA34A45FD | Expires: Apr  6 18:58:54 2077 GMT | CN: client-x
V | Serial: D812344919EAFF9B4C5FE26040356463 | Expires: Apr  6 18:58:55 2077 GMT | CN: client-y
./easyrsa: 3483: [: Illegal number: 
./easyrsa: 3483: [: Illegal number: 
./easyrsa: 3483: [: Illegal number: 
./easyrsa: 3483: [: Illegal number: 
./easyrsa: 3483: [: Illegal number: 

@TinCanTech
Copy link
Collaborator

The Illegal number can be fixed but it is important to understand why it is happening that either cert_expire_date_s and/or cutoff_date_s are empty values.

I would need to see your index.txt file and find pki/issued pki/revoked.

@tecoboot
Copy link
Author

tecoboot commented Aug 8, 2022

I revoked the fix and reran the testscript.
Result:

root@host:~/easy-rsa/easyrsa3# cat pki/index.txt
V       241110155612Z           A347D033EBB501418145AC840E2E4A5B        unknown /CN=client-1
V       241110155612Z           A6FAE9867E46635386C490CAF91F81FC        unknown /CN=client-1
R       241110155612Z   220808155612Z   A08D3B46F37861CDEF1804E02276C484        unknown /CN=client-1
root@host:~/easy-rsa/easyrsa3# find pki/issued pki/revoked
pki/issued
pki/revoked
pki/revoked/private_by_serial
pki/revoked/private_by_serial/A08D3B46F37861CDEF1804E02276C484.key
pki/revoked/reqs_by_serial
pki/revoked/reqs_by_serial/A08D3B46F37861CDEF1804E02276C484.req
pki/revoked/certs_by_serial
pki/revoked/certs_by_serial/A08D3B46F37861CDEF1804E02276C484.crt
root@host:~/easy-rsa/easyrsa3#

@tecoboot
Copy link
Author

tecoboot commented Aug 8, 2022

Here 2 errors:


... show-expire:
./easyrsa: 3482: [: Illegal number: 
./easyrsa: 3482: [: Illegal number: 

@TinCanTech
Copy link
Collaborator

TinCanTech commented Aug 8, 2022

From your index.txt file, there are three known certificates:

  • A3.., A6.. and A0...

However, there exists only one certificate file:

  • pki/revoked/certs_by_serial/A08D3B46F37861CDEF1804E02276C484.crt

You have over-written your original certificate twice.

Obviously, easyrsa needs to handle this situation correctly. I'll take a closer look.

Thanks for your time and feedback 👍

@TinCanTech
Copy link
Collaborator

TinCanTech commented Aug 8, 2022

This turned out to be a bug in my busybox date input.

The correct command is:

busybox date -u \
	-D "%Y-%m-%d %H:%M:%S%Z" \
	-d "$in_date" "+%b %d %H:%M:%S %Y %Z"

-D was incorrectly formatted.

easy-rsa/easyrsa3/easyrsa

Lines 3298 to 3309 in 5b4fd2b

ff_date_to_cert_date() {
in_date="$1"
# busybox
if busybox date --help > /dev/null 2>&1
then
cert_type_date="$(
busybox date -u -D "%b %e %H:%M:%S %Y" -d "$in_date" \
"+%b %d %H:%M:%S %Y %Z" 2>/dev/null
)"
return

There is still something going wrong though, otherwise it should fail for all dates ... weird. Still working on this one ..

@tecoboot
Copy link
Author

tecoboot commented Aug 11, 2022

I'll have made a revoke-untracked-certs cleanup script. It revokes all "untracked" serials.
Please confirm that easyrsa3 keeps all certs in pki subfolders.
I'm not so sure such a script should be made available with std distribution, or even integrate in easyrsa itself.
Opinions?

#!/bin/bash -e
# Bypass for https://github.com/OpenVPN/easy-rsa/issues/626
# Revoke untracked certs, e.g. created with multiple sign-req's for same CN, before 
# fix https://github.com/OpenVPN/easy-rsa/commit/5b4fd2b484adc6e2f506b62eb54fc38adc802766
#
# With EasyRSA 3, all generated valid certs are kept in the PKI folder
# So the serials of the valid certs in the openssl index.txt MUST be present in one of 
# the cert files maintained by easyrsa3.
# If a serial in index.txt is not backed in a cert file, it is untracked.
# This scripts revokes all such untracked certs

echo -n "Do you want to revoke untracked certs in $(pwd) ?
Warning, use at your own risk !!
Say yes: "
read response
if [ "$response" != yes ]; then
	echo "You didn't give permission"
	exit 1
fi

# Housekeeping
run_date=$(date +%y%m%d%H%M%SZ)
found_untracked_serials=no
tracked_serials_file=tracked-serials-$run_date

# Find tracked serials
rm -f $tracked_serials_file 	# just to be sure
find pki/*/ -type f -regex ".*crt" -exec bash -c \
	"openssl x509 -noout -serial -in {} | cut -d'=' -f2 >>$tracked_serials_file" \;

# Save current index.txt
cp pki/index.txt pki/index.txt.$run_date

# Revoke untracked certs
for cert_serial in $(egrep "^V.*$cert_serial" pki/index.txt | cut -f4)
do
	if grep -q "$cert_serial" $tracked_serials_file; then
		:	# found, this is OK
	else
		cert_expiration=$(grep "$cert_serial" pki/index.txt | cut -f2)
		cert_filename=$(grep "$cert_serial" pki/index.txt| cut -f5)
		cert_DN=$(grep "$cert_serial" pki/index.txt | cut -f6)
		printf -v new_index_line "R\t$cert_expiration\t$run_date,cessationOfOperation\t$cert_serial\t$cert_filename\t$cert_DN"
		sed -i "s|.*$cert_serial.*|$new_index_line|" pki/index.txt
		found_untracked_serials=yes
		echo "Untracked certificate with serial $cert_serial revoked"
	fi
done

#  Cleanup
if [ "$found_untracked_serials" = yes ]; then
	echo "Do not forget to generate a new CRL and push it to your systems"
	echo "Old index.txt is saved as pki/index.txt.$run_date"
	echo "Current tracked serials file is saved as $tracked_serials_file"
else
	echo "No untracked serials found"
	rm -f pki/index.txt.$run_date $tracked_serials_file
fi

exit 0

Results:

root@host:~/easy-rsa/easyrsa3# ./revoke-untracked-certs
Do you want to revoke untracked certs in /root/easy-rsa/easyrsa3 ?
Warning, use at your own risk !!
Say yes: yes
Untracked certificate with serial AAABBBCCC7B1FE1723C1F663C2F59800 revoked
Untracked certificate with serial A83EBCEE029D62C931D95FB51F381548 revoked
Untracked certificate with serial 1B4FA7A3CEB1A7DA6CEB5C7EEDDE6152 revoked
Do not forget to generate a new CRL and push it to your systems
Old index.txt is saved as pki/index.txt.220811094830Z
Current tracked serials file is saved as tracked-serials-220811094830Z
root@host:~/easy-rsa/easyrsa3#

root@host:~/easy-rsa/easyrsa3# ./revoke-untracked-certs
Do you want to revoke untracked certs in /root/easy-rsa/easyrsa3 ?
Warning, use at your own risk !!
Say yes: yes
No untracked serials found
root@host:~/easy-rsa/easyrsa3# 

root@host:~/easy-rsa/easyrsa3# find -regex ".*220811094830Z" -exec ls -al '{}' +
-rw------- 1 root root 930 Aug 11 09:48 ./pki/index.txt.220811094830Z
-rw-r--r-- 1 root root 330 Aug 11 09:48 ./tracked-serials-220811094830Z
root@host:~/easy-rsa/easyrsa3#

root@host:~/easy-rsa/easyrsa3# grep 220811094830Z pki/index.txt
R       241112062921Z   220811094830Z,cessationOfOperation      AAABBBCCC7B1FE1723C1F663C2F59800        unknown /CN=client-a
R       241112064104Z   220811094830Z,cessationOfOperation      A83EBCEE029D62C931D95FB51F381548        unknown /CN=client-b
R       241112105107Z   220811094830Z,cessationOfOperation      1B4FA7A3CEB1A7DA6CEB5C7EEDDE6152        unknown /CN=client-3
root@host:~/easy-rsa/easyrsa3#

root@host:~/easy-rsa/easyrsa3# ./easyrsa gen-crl
Using configuration from /root/easy-rsa/easyrsa3/pki/8c6e51af/temp.c7bcee00
root@host:~/easy-rsa/easyrsa3#

root@host:~/easy-rsa/easyrsa3# ./easyrsa show-crl | grep -A 4 AAABBBCCC7B1FE1723C1F663C2F59800
    Serial Number: AAABBBCCC7B1FE1723C1F663C2F59800
        Revocation Date: Aug 11 09:48:30 2022 GMT
        CRL entry extensions:
            X509v3 CRL Reason Code: 
                Cessation Of Operation
root@host:~/easy-rsa/easyrsa3# 

@TinCanTech
Copy link
Collaborator

For the record:

  • Version 3.1.x of Easy-RSA rewind-renew moves a certificate (etc) from the renewed/certs_by_serial folder to the renewed/issued folder and names it back to its commonName.

    This is done so that the certificate can then be revoked with revoke-renewed commonName.

  • The reason to rewind-renew individual certificates only is because:
    If a certificate has been renewed more than once the old way then a subsequent collision of commonName will occur, if all renewed certificates are rewound at the same time.

  • This new mechanism does not allow for any certificate to be renewed more than one time, until the previous renewed certificate is revoked. This is a complete solution, which is considerably better than the old way.

  • Version 3.0.x of Easy-RSA can not revoke renewed certificates at all.

I will not consider any code to change this new method. If bugs are found then they can be fixed but that is all.

@tecoboot
Copy link
Author

What to do with this one? I'm still not happy with the possibility having PKI's around that are not healthy but where easyrsa doesn't complain.
I can think of an in-depth verify function where index.txt is verified with all certs in pki subfolders.

@TinCanTech
Copy link
Collaborator

TinCanTech commented Aug 15, 2022

The solution is to back-port version 3.1 renew, revoke-renewed and rewind-renew to version 3.0. That would also include the *_move_*() and undo *_move_*() capabilities ..

I do not know what the plan is or even if a plan exists.

@ecrist Any comment ?

My answer is simple: Upgrade your easyrsa to version 3.1.1.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants