Add SSL Certificate Validation to Traffic Router#3380
Conversation
|
Refer to this link for build results (access rights to CI server needed): |
|
|
||
| start() { | ||
| systemctl start traffic_router | ||
| chmod 777 starttr.sh |
There was a problem hiding this comment.
this should probably just be done once, in the Dockerfile, not every time TR starts.
There was a problem hiding this comment.
Done in new commits.
| if (chopped != null && chopped.length > 1) { | ||
| chop = chopped[1]; | ||
| } | ||
| if (chop != null) { |
There was a problem hiding this comment.
Can't the body of this if be inside the body of the previous if (i.e. right under chop = chopped[1];)? chop can only not be null if that first if's condition is true. For that matter, you could also just declare chop inside of that as String chop = chopped[1]; since it's not going to be used outside of the if block.
There was a problem hiding this comment.
You are correct. It turns out that the split actually returns an empty string if there is no token. So in the new commit I have also added and extra check to make sure 'chop' has length.
| "certificate": { | ||
| "comment" : "The following is just a self signed certificate and key to use for testing this is NOT private data from a CA", | ||
| "key": "LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFb2dJQkFBS0NBUUVBc2Y0NnV5OGJ2\nQk5rMGhCaEVsbHdGT0dqREh6M1hJY1hteDRVNThNZG9Fa1JId0VTCjVONnd3NFV6bDAvRDcyMlJV\nODlMeHB4bldvclJmdVZNQldnOGVFcXBUb2NUS2NOZHhtZmdEUWZTcTZ1ODNTWkUKTmFCZFArK2g5\nYTJJRFZXWGFldVRhcVA3Q3lVVG52Sld5Mm1JalJWZkRGQWRWWHNhU1M4RGRYUWdibEJTelJ6NwpL\nMXFHVWt1RlZQc0R0ODZBYVF3TnN5R2ZDN3ltcUkzNU1FQ3hTdzNPd2lXSlAyZTg3U2E5UG9Pdjcr\nZUs2NVJnCmM3dzNkSXQxZUlyS3B6OWpQV1RPTkJOK0JhWFdvcHNXZ3UvdVd4Q1pnUk9qaXBWVUFK\nNHhrNFRGMjd0S28zRDUKSHd4RVZOeStlck9FTGI3VWxuaHMxaG1LdVI2RTZwREpHSyt1UFFJREFR\nQUJBb0lCQURkb1RYNFJkdysvOWNXUwpoYlZCbEN0YjVmYXdQQXhZblZlVE1Lekl4ME1yRzZKTUlr\nYU9yL1hkVkNjSkZKUkd4bE9SbHlRWGNmRDBmNnlCCjBuMW9hbEtENDFwbm4rYURRNERNdXlrelF6\naGZlS1piRUNhbGFnSEtKZCtsaWxHa1VFTVBxMDhxQnE3OGRyUW0KK2kvT0JVenQxeTJ0RHNTYVVw\nOXZmQ01tNkNXT1pKOHN6eWZrSk1ZZVVLdXptOWwzMGNNeW84anF0bi9LQjZWWApEVkZmNGpqU1FM\nTzBYbUo1M012TW1xUzlGaVRUdklpQm9iNnkxMEtWZjZpWk5MZnVnVExoWFhQaDFDNTJTUTVvCjZj\nVWdqanh5NFhmYUdub0Y1NlF4M0FIZWRpd0Z6eEFpWGlKOCswUFB3bWI2Q1FGOU1jSzV0QjQzcVZx\nNWNKNFcKTVF1SWpsMENnWUVBMkM3SFh6OHppODQ0anJHa1VvUmF3NE9QcitBdnFIVGY1NEJ2Ujhz\nTzc2NkljRzEvUytmdQoxbXQ2RnBRQVZGeHJvWmxEWHkrWGtTQ0toaEM3cmh6cHQzaVJuRkhkWFpT\nM25MM2ZjWnIzUCtFSXNyb3hLeVFRCmg3dy8xYnEzd3JwUnNOWjZpdDNVSHJvVlRLU2tZQzZvOUor\nYzRPYjVQRzVGcFRvZk81dmRWWHNDZ1lFQTBzYkYKd3pjYzEvcTdEVXpkTkVTaWtNMFdMMkFZanBH\nOFpEYTY0eUcrZ1JkWjhjWmpJUzlkVFpWWVpUODNFSnp2T2dyWgo1NmFYTVN2QVc2ak1oY1VDR05K\nN3JaaGl1Uk9HNVdWM3FzMnFkbmtkQlgrdGhrUE9HT3lNYnhsN1ltUlZXTG11CjBFV3NMQ3VHVFov\neDV5NG02WmNGQldRZUkreVMxanZTTXVkelVhY0NnWUJvVG1ISjlnK2o1Qk5yM2hCZjlCWnIKQVY1\naHlMU1YvaFpPZDZ5NW9pTUp5RmR5ajVKOUNHSTN1TkhHZFJDWG82UVc2NEVUT3o1Uk9yYzdxblV6\ndENXYwpiYU1zSGwvRm1Fempac1daNWVCb1JPYlNmWDNkeDkvbDdoR0t5VFdDMGkwNk9ySVRzS1o1\nVU9XWC9sU0ZSOTRqCmNhUGE2L2JUam8weUJKSXZTNndHWXdLQmdDN045dkpmbGFjY1JWY3h2MWt3\nK0l5QkRqRWMvTGNFQTdxWk1LenAKUEYxOEt2djJXdUx1bXFCMHpubEZMVndpRFRsdFdYQUlYVUNN\nLzUwYkFiZWV4TlZ3UUFpUGN6UzM4bGVVVFp0LwpLaUEreXNRQzB5eWlkK3l1OG94bE16SHBKODZa\nQlFtNHZ2L2I5bW5jWDZJL2JHS29wM1BJQksxamhrUE9hdUhrCjVZVzNBb0dBVEtVZlhkWTNad0hM\nWlUxSm1lRldKb29kOW10SHNrM1QyT2hCdDBwNUpSU3hVYWxOY0tkZEcvMVgKMFFCN1E1NWxMZEIz\nT1k2b1NDaEtuK1dUMWNwQ1N3d09PQlJNTmZPOVR1c252NkU2ZlNRWWVwdGNmV2lOb3VBWQpYaDVn\nQTRORzJHeUM5TW9tZUxQbUY0NmJsdU5UYzFxREMzeVZVcVQzdDkxY1RwNytWems9Ci0tLS0tRU5E\nIFJTQSBQUklWQVRFIEtFWS0tLS0tCg==", | ||
| "key": "LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFcEFJQkFBS0NBUUVBMHBBWXlmamRz\nZ0NSL0ZXeEQvWE1vbDNwYjRWazlHSFdqTnR4bkdvWHA3OGxxM2p1CitLd012ZlU2ZDVScmdXbHp6\nMlJjZnFhMHdjbkYrNU1abkdkbzRGaGwycnhnT3ZBV2NSb3ZXS3BUaXNUUkcrQXcKbnFneUZTWjNT\nalhUcE9YUjV4dUJJOWI1c3ZIN3RhbzdBcWFLR2I0V3d1TnA3cTZzcUtRYlRxaUlXcE9JQWtFKwpS\nVnJXblBVdHRvaHlTV08yL2dIbDQ2NlU1S0czdC9TU3lqZVRPZ1ZFU0xoQUhlWlk2dExyTGd1YmdM\nanlLVWE5CkpDcWJLa1laZ2UrdWlhKzVVMzZ6alhYRUdBUEdwWWNrM2Zqb1pYN01zM3hkUzV6Zit6\nUDQzSnVVWUtheE5KbVMKRXMvaU1Dc1VPK1htZEtQTFhmWGdPWDNoM0NQdC83U05HR3VlS1FJREFR\nQUJBb0lCQUF1REEwZnZpamRVSHFjYwpERDBpSkJqd1ozWElaamVTTGNldnE2dHdoWENQVzhEZk1M\nbDV0b3lnSHAweENSdWZKMHk4WU80dnNRd3pPdGJCCk9SSTYrUm5pMjFhMUc5RzlGSTBFY0hnNWY2\nM0RpdWNxUDU0ODlkZ0FMVjlxUi9Mbythdlg3aHlHZ1VwT1BvTzEKRmRyVVBoS2dPT0JZekk3WEQr\ndDhaVjNNaXYvZ25aWUphR2dYRDYrODFGaU4veXNXaE5SMEVYYW5Vck1wR2ZtdQpSVm1tYnlvRjNr\nWVV1d3V2SUZIMkdPVFo0VjViR3JGQUJUQUowSWt2MkdmcU9TR08xa1NiWVMxclZjb29uMyswClRz\ncGxLejRUT3NVblp5VUk1UVBpbEI5ak93cjVlbGFRM1lndnZCM0k5TUdvQjRTLy9XWkFVRGFQTjlE\nZXYyU1UKUm1jb2dqMENnWUVBN3oxaEh0c21QZmVOOTA3VWV4clVwMGRMOEJtbmJ5V1k0bkYrbFhp\nalRLQ0xpTjdtNGFSNQpTTHhtZ21nVkhrQ0dOZkEybXFVWHA2dTlNL1BXOGx4SjRjNExLRldCbTVJ\nYjFRdlRZM3hOWDNKWVZYZkhTWkNlCitvaDMwaStmcm9oYmNnZzRpaG9QeDU2aGhpbEdlSXZGSS9l\nMDg0M0Z0dU9mWkgrQ0s1dHRKOU1DZ1lFQTRWQnEKTTNUUlhzbUU3ZHQvSFFza2ladGFjTVdkQldx\nOVRXVlBIb3hBK3VjalFoandGVFhBZzhkZGFsbE9TS1hWNjVGawoxMlphU1lTbTd5VnplS3lVRk4w\nazBxNXpRQkRZQ1FNYVA2YjZYbGxiL2VMaWJQQTMxbU5uRGl2WjdkVWM3UUVqClhUUy9nclFLc2Zl\nenRVMXUwNG1SMTZ5THhZSHh3MW5IL3NselFKTUNnWUVBMHF6S2lkR1NxNThFZFhRRnlTS24KZ1dk\nWGgrZ1BlZUV4OExiaE1kODZicEF5VUNWNlM5bjZ0QUswZ3NJRlZzNmJZWVJYa1hjd2pZYSs1ckVq\nNStrOQpab3Q3Wjlsa2VRc0JWMnRDaTZrNnVZS0lKenVEVTFUM3FzZmlQRVdUNks1TFdPL0VXbGo0\nN0dEVS9MLzhQc3RXCm40WFM0MmRGWlBpdHRHSlV6dkhmL2VFQ2dZQXVpR3dXaW5hL0s4RmZXbWly\nTitUbzRvUFFMSS9jVVlvZEZPSTkKUGR3aHRXREx3dGk2bUtwVXpQVFhCUEN0QWtybTV0VTd3ekM4\nWkVBUnZkdFdQZFlyWk95NDhqeHRLODFpTnhqUgpzb3VjdHJuUCttNm03d21wSmtoZlhlRVpSRjAv\nK1c4elRiU0xxdUZXbGdDd1hmaVlpWjNzTy85MTMvdHRTL3FJCi9WUG5Md0tCZ1FDNGQ4OXNUL3E3\nNjZMREt6bEhySzVEUkE2TTRENEhMbjEwTWJQVExNdWNweEJlQkY1YnhvQkEKeHRFcVhBRCtyaWVr\nT3IybjJSRHlxeGt0MS9FY2JhcTI5bmNXVFFwZjZ2NkV3cTFGT3JhUDlpdTJFbERBcUcyMApXbmdG\nOUttM0VETUpBQkd5VHhieFgxZTZMRG1LaFcwWFFxRWptVmpGQi9uMFVQOGxJd2F2aGc9PQotLS0t\nLUVORCBSU0EgUFJJVkFURSBLRVktLS0tLQo=", |
There was a problem hiding this comment.
What's the purpose of these key replacements? Was there something wrong with the old values? Seems odd that the key would change but not the cert.
There was a problem hiding this comment.
These keys have been replaced specifically to cause the modulus validation check to fail in the integration tests. It sounds negative, but its actually a positive test.
| ADD $RPM / | ||
| ADD $TC_REPO /etc/yum.repos.d/ | ||
| ADD starttr.sh / | ||
| ADD shutdowntr.sh / |
There was a problem hiding this comment.
How come? Are you docker execing into the container to start and stop Traffic Router? What do you gain by doing that without making any changes?
There was a problem hiding this comment.
systemd has been disabled in the centos 7 docker container so we can no longer use the unit files to start/stop processes. The starttr.sh gets called by the run.sh, and yes, I do docker exec to run the shutdown. I also copy in new TR rpms so I can test code changes without having to rebuild the whole Docker image. We have done something similar in CIAB for the TR and ATS containers.
There was a problem hiding this comment.
Well typically you're not supposed to do that with containers, but I'd like the opinion of someone who actually uses these things - @rob05c , thoughts?
There was a problem hiding this comment.
I think this goes back to the whole "containers as VMs" vs "one process per container" thing, which really depends on how the container was designed. These scripts make it seem more like a "container as a VM". That said, I don't really see a problem with adding these scripts to this TR container, but it probably should've been done in a different PR since they don't seem to be required for adding TR SSL cert validation.
| private PrivateKeyDecoder privateKeyDecoder = new PrivateKeyDecoder(); | ||
| private CertificateDecoder certificateDecoder = new CertificateDecoder(); | ||
|
|
||
| @SuppressWarnings({"PMD.CyclomaticComplexity", "PMD.AvoidDeeplyNestedIfStmts", "PMD.NPathComplexity"}) |
There was a problem hiding this comment.
Again, I'm not a Java guy, but this strikes me as a potential codesmell (especially since it's done at the class and method levels - does the class-level one not apply to the methods?). If the cyclomatic complexity is high enough to issue a warning, maybe it should be reduced by breaking the method into parts?
There was a problem hiding this comment.
I moved this down to where it belongs. I accidentally left it at the top while I was developing the solution.
Set the startup/shutdown scripts for the TR docker container to executable
|
Refer to this link for build results (access rights to CI server needed): |
| ADD $RPM / | ||
| ADD $TC_REPO /etc/yum.repos.d/ | ||
| ADD starttr.sh / | ||
| ADD shutdowntr.sh / |
There was a problem hiding this comment.
I think this goes back to the whole "containers as VMs" vs "one process per container" thing, which really depends on how the container was designed. These scripts make it seem more like a "container as a VM". That said, I don't really see a problem with adding these scripts to this TR container, but it probably should've been done in a different PR since they don't seem to be required for adding TR SSL cert validation.
| import com.comcast.cdn.traffic_control.traffic_router.secure.HandshakeData; | ||
| import com.comcast.cdn.traffic_control.traffic_router.secure.KeyManager; | ||
| import org.apache.log4j.Logger; | ||
| //import org.apache.tomcat.util.modeler.Registry; |
There was a problem hiding this comment.
this comment can be removed
There was a problem hiding this comment.
done in latest commit
| if (replace) { | ||
| previous = sslHostConfigs.get(key); | ||
| } | ||
| super.addSslHostConfig( sslHostConfig, replace); |
There was a problem hiding this comment.
there is an unnecessary space after the opening (
| final List<X509Certificate> x509Chain = new ArrayList<>(); | ||
| boolean hostMatch = false; | ||
| boolean modMatch = false; | ||
| for ( final String encodedCertificate : encodedCertificates) { |
There was a problem hiding this comment.
there is an unnecessary space after the opening (
| } | ||
| x509Chain.add(certificate); | ||
| } | ||
| if ( hostMatch && modMatch) { |
There was a problem hiding this comment.
there is an unnecessary space after the opening (
| log.warn("Service name doesn't match the subject of the certificate = "+certificateData.getHostname()); | ||
| } | ||
| else if (!modMatch) { | ||
| log.error("Modulus not == for host: "+certificateData.getHostname()); |
There was a problem hiding this comment.
This error message isn't super clear. How about "private key modulus does not match public key modulus for certificate host: " + ...
|
Refer to this link for build results (access rights to CI server needed): |
|
I approved the code but still want to pull this down to test a little before merging. |
rawlinp
left a comment
There was a problem hiding this comment.
Actually, do you mind adding a line to CHANGELOG.md for this change?
|
Refer to this link for build results (access rights to CI server needed): |
* updated tr docker config so it would work * added start and stop scripts for TR docker containers * fixed bug in Tomcat which wasn't unregistering old SslHostConfigs * Added validation checks of SSL certs while being loaded by CertificateRegistry * Changed error message for Certificate Expired * Corrected a parsing error in certificate validation checking Set the startup/shutdown scripts for the TR docker container to executable * Corrected some code formatting and one NullPointerException * Added info to CHANGELOG.md (cherry picked from commit 0d2560d)
* Make SSL Cert lookup case-insensitive (#3331) * Fix case-sensitive SSL cert lookup * Set SSL certificate key names to lower case * Set requested SNI to lower case (cherry picked from commit 6504e7f) * Add SSL Certificate Validation to Traffic Router (#3380) * updated tr docker config so it would work * added start and stop scripts for TR docker containers * fixed bug in Tomcat which wasn't unregistering old SslHostConfigs * Added validation checks of SSL certs while being loaded by CertificateRegistry * Changed error message for Certificate Expired * Corrected a parsing error in certificate validation checking Set the startup/shutdown scripts for the TR docker container to executable * Corrected some code formatting and one NullPointerException * Added info to CHANGELOG.md (cherry picked from commit 0d2560d) * Corrected CHANGELOG * Another CHANGELOG correction
Which issue is fixed by this PR? If not related to an existing issue, what does this PR do?
When TR obtains SSL certificates it now checks each cert before accepting it. A cert could get rejected for being expired, a mismatch between the Delivery Service URL and the subjects of the cert or a mismatch between the modulus of the certificate's private key and the public keys. Rejected certs are just dropped and not used but TR continues to operate using the valid certs it can find.
Which TC components are affected by this PR?
What is the best way to verify this PR? Please include manual steps or automated tests.
(If no tests are part of this PR, please provide explanation as to why no tests are included.)
Unit tests have been added to the build process which test valid certs and examples of certs triggering each of the failure conditions.
T1. This can be manually tested by adding invalid certificates to a test CDN through Traffic Portal
R1. Watch the error logs of Traffic Router when it next retrieves the SSL certs for the expected error logs.
T2. After seeing the expected errors in the logs then attempt to make and SSL connection with TR for the offending Delivery Service URL. (Use curl with the --resolve option)
R2. The handshake will return the 'default' certificate.
Check all that apply