This is an automated email from the ASF dual-hosted git repository.
zrhoffman pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficcontrol.git
The following commit(s) were added to refs/heads/master by this push:
new 8fb776b960 Add the ability to traffic Router to customize default
https cert (#7135)
8fb776b960 is described below
commit 8fb776b9600dfbcd2ca9ae219af74f233fe19852
Author: Srijeet Chatterjee <[email protected]>
AuthorDate: Fri Oct 14 08:15:19 2022 -0600
Add the ability to traffic Router to customize default https cert (#7135)
* Add the ability to traffic Router to customize default https cert
* Add license
* Add debug
* stacktrace
* fix error log
* fix error log
* fix tests
---
CHANGELOG.md | 1 +
.../traffic_router/traffic_router_api.rst | 27 ++++++++++++++
.../traffic_router/secure/CertificateRegistry.java | 41 ++++++++++++++++------
.../traffic_router/utils/HttpsProperties.java | 38 +++++++++++++++-----
.../connector/src/test/java/conf/https.properties | 24 +++++++++++++
.../src/test/java/utils/HttpsPropertiesTest.java | 41 ++++++++++++++++++++++
6 files changed, 154 insertions(+), 18 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index f83dac81e7..20535d27b8 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,7 @@ The format is based on [Keep a
Changelog](http://keepachangelog.com/en/1.0.0/).
## [unreleased]
### Added
+- [#7089](https://github.com/apache/trafficcontrol/issues/7089) *Traffic
Router* Added the ability to specify HTTPS certificate attributes.
- [#7109](https://github.com/apache/trafficcontrol/pull/7109) *Traffic Router*
Removed `dnssec.zone.diffing.enabled` and `dnssec.rrsig.cache.enabled`
parameters.
- [#7075](https://github.com/apache/trafficcontrol/pull/7075) *Traffic Portal*
Added the `lastUpdated` field to all delivery service forms.
- [#7055](https://github.com/apache/trafficcontrol/issues/7055) *Traffic
Portal* Made `Clear Table Filters` option visible to the user.
diff --git a/docs/source/development/traffic_router/traffic_router_api.rst
b/docs/source/development/traffic_router/traffic_router_api.rst
index b6047d1ebd..fc6e4cdc8a 100644
--- a/docs/source/development/traffic_router/traffic_router_api.rst
+++ b/docs/source/development/traffic_router/traffic_router_api.rst
@@ -27,6 +27,33 @@ To override the self signed certificates with new ones from
a certificate author
The API can be configured via HTTPS on port 3443 in
:file:`/opt/traffic_router/conf/server.xml` or by setting a :term:`Parameter`
named ``secure.api.port`` with ``configFile`` ``server.xml`` on the Traffic
Router's :term:`Profile`. When ``systemctl start traffic_router`` is run, it
will generate self signed certificates at ``/opt/traffic_router/conf/``, create
a new Java Keystore named :file:`/opt/traffic_router/conf/keyStore.jks`, and
add the new certificate to the Keystore. The passw [...]
To override the self signed certificates with new ones from a certificate
authority, either replace the Java Keystore in the default location or update
the properties for the new Keystore location and password at
:file:`/opt/traffic_router/conf/https.properties` and then restart the Traffic
Router using ``systemctl``.
+Other attributes of the default certificate can also be customized by
specifying appropriate values for the following properties in
:file:`/opt/traffic_router/conf/https.properties`. These properties are listed
below:
+
+.. table:: HTTPS Certificate Attributes
+
+
+------------------------------------------+------------------------------------------------------------------------+---------------------------------------------------------+
+ | Name | Description
| Default
|
+
+==========================================+========================================================================+=========================================================+
+ | https.certificate.location | The location of the
certificate key store |
|
+
+------------------------------------------+------------------------------------------------------------------------+---------------------------------------------------------+
+ | https.password | The password for the
certificate key store |
|
+
+------------------------------------------+------------------------------------------------------------------------+---------------------------------------------------------+
+ | https.key.size | The size for the HTTPS
keys | 2048
|
+
+------------------------------------------+------------------------------------------------------------------------+---------------------------------------------------------+
+ | https.signature.algorithm | The HTTPS signing
algorithm to be used | SHA1WithRSA
|
+
+------------------------------------------+------------------------------------------------------------------------+---------------------------------------------------------+
+ | https.validity.years | The amount of time (in
years) for which the cert is valid | 3
|
+
+------------------------------------------+------------------------------------------------------------------------+---------------------------------------------------------+
+ | https.certificate.country | The country of the
certificate | US
|
+
+------------------------------------------+------------------------------------------------------------------------+---------------------------------------------------------+
+ | https.certificate.state | The state of the
certificate | CO
|
+
+------------------------------------------+------------------------------------------------------------------------+---------------------------------------------------------+
+ | https.certificate.locality | The locality of the
certificate | Denver
|
+
+------------------------------------------+------------------------------------------------------------------------+---------------------------------------------------------+
+ | https.certificate.organization | The organization of the
certificate | Apache Traffic Control
|
+
+------------------------------------------+------------------------------------------------------------------------+---------------------------------------------------------+
+ | https.certificate.organizational.unit | The organizational unit of
the certificate | Apache Foundation, Hosted by
Traffic Control, CDNDefault|
+
+------------------------------------------+------------------------------------------------------------------------+---------------------------------------------------------+
Traffic Router API endpoints only respond to ``GET`` requests.
diff --git
a/traffic_router/connector/src/main/java/org/apache/traffic_control/traffic_router/secure/CertificateRegistry.java
b/traffic_router/connector/src/main/java/org/apache/traffic_control/traffic_router/secure/CertificateRegistry.java
index c9d1514ab3..ded6481c62 100644
---
a/traffic_router/connector/src/main/java/org/apache/traffic_control/traffic_router/secure/CertificateRegistry.java
+++
b/traffic_router/connector/src/main/java/org/apache/traffic_control/traffic_router/secure/CertificateRegistry.java
@@ -55,6 +55,15 @@ import java.util.List;
import java.util.Map;
public class CertificateRegistry {
+ private static final String HTTPS_PROPERTIES_FILE =
"/opt/traffic_router/conf/https.properties";
+ private static final String HTTPS_KEY_SIZE = "https.key.size";
+ private static final String HTTPS_SIGNATURE_ALGORITHM =
"https.signature.algorithm";
+ private static final String HTTPS_VALIDITY_YEARS =
"https.validity.years";
+ private static final String HTTPS_CERTIFICATE_COUNTRY =
"https.certificate.country";
+ private static final String HTTPS_CERTIFICATE_STATE =
"https.certificate.state";
+ private static final String HTTPS_CERTIFICATE_LOCALITY =
"https.certificate.locality";
+ private static final String HTTPS_CERTIFICATE_ORGANIZATION =
"https.certificate.organization";
+ private static final String HTTPS_CERTIFICATE_OU =
"https.certificate.organizational.unit";
public static final String DEFAULT_SSL_KEY = "default.invalid";
private static final Logger log =
LogManager.getLogger(CertificateRegistry.class);
private CertificateDataConverter certificateDataConverter = new
CertificateDataConverter();
@@ -80,8 +89,23 @@ public class CertificateRegistry {
@SuppressWarnings({"PMD.UseArrayListInsteadOfVector",
"PMD.AvoidUsingHardCodedIP"})
private static HandshakeData createDefaultSsl() {
try {
+ final Map<String, String> httpsProperties = (new
HttpsProperties(HTTPS_PROPERTIES_FILE)).getHttpsPropertiesMap();
final KeyPairGenerator keyPairGenerator =
KeyPairGenerator.getInstance("RSA");
- keyPairGenerator.initialize(2048);
+ int keysize = 2048, validityLength = 3;
+ String country = "US", state = "CO", locality =
"Denver", organization = "Apache Traffic Control",
+ organizationalUnit = ";OU=Apache
Foundation; OU=Hosted by Traffic Control; OU=CDNDefault",
+ signingAlgorithm = "SHA1WithRSA";
+ if (httpsProperties != null) {
+ keysize =
Integer.parseInt(httpsProperties.getOrDefault(HTTPS_KEY_SIZE,
String.valueOf(keysize)));
+ country =
httpsProperties.getOrDefault(HTTPS_CERTIFICATE_COUNTRY, country);
+ state =
httpsProperties.getOrDefault(HTTPS_CERTIFICATE_STATE, state);
+ locality =
httpsProperties.getOrDefault(HTTPS_CERTIFICATE_LOCALITY, locality);
+ organization =
httpsProperties.getOrDefault(HTTPS_CERTIFICATE_ORGANIZATION, organization);
+ organizationalUnit =
httpsProperties.getOrDefault(HTTPS_CERTIFICATE_OU, organizationalUnit);
+ validityLength =
Integer.parseInt(httpsProperties.getOrDefault(HTTPS_VALIDITY_YEARS,
String.valueOf(validityLength)));
+ signingAlgorithm =
httpsProperties.getOrDefault(HTTPS_SIGNATURE_ALGORITHM, signingAlgorithm);
+ }
+ keyPairGenerator.initialize(keysize);
final KeyPair keyPair =
keyPairGenerator.generateKeyPair();
//Generate self signed certificate
@@ -93,20 +117,17 @@ public class CertificateRegistry {
// Generate cert details
final long now = System.currentTimeMillis();
final Date startDate = new
Date(System.currentTimeMillis());
-
- final X500Name dnName = new X500Name("C=US; ST=CO;
L=Denver; " +
- "O=Apache Traffic Control; OU=Apache
Foundation; OU=Hosted by Traffic Control; " +
- "OU=CDNDefault; CN="+DEFAULT_SSL_KEY);
+ final String certAttributes = "C=" + country + "; ST="
+ state + "; L=" + locality + "; O=" + organization + organizationalUnit + ";
CN=" + DEFAULT_SSL_KEY;
+ final X500Name dnName = new X500Name(certAttributes);
final BigInteger certSerialNumber = new
BigInteger(Long.toString(now));
final Calendar calendar = Calendar.getInstance();
calendar.setTime(startDate);
- calendar.add(Calendar.YEAR, 3);
+ calendar.add(Calendar.YEAR, validityLength);
final Date endDate = calendar.getTime();
-
// Build certificate
- final ContentSigner contentSigner = new
JcaContentSignerBuilder("SHA1WithRSA").build(keyPair.getPrivate());
+ final ContentSigner contentSigner = new
JcaContentSignerBuilder(signingAlgorithm).build(keyPair.getPrivate());
final JcaX509v3CertificateBuilder certBuilder = new
JcaX509v3CertificateBuilder(dnName, certSerialNumber, startDate, endDate,
dnName, keyPair.getPublic());
@@ -128,7 +149,7 @@ public class CertificateRegistry {
return new HandshakeData(DEFAULT_SSL_KEY,
DEFAULT_SSL_KEY, chain, keyPair.getPrivate());
}
catch (Exception e) {
- log.error("Could not generate the default certificate:
"+e.getMessage(),e);
+ log.error("Could not generate the default certificate:
", e);
return null;
}
}
@@ -151,7 +172,7 @@ public class CertificateRegistry {
private HandshakeData createApiDefaultSsl() {
try {
- final Map<String, String> httpsProperties = (new
HttpsProperties()).getHttpsPropertiesMap();
+ final Map<String, String> httpsProperties = (new
HttpsProperties(HTTPS_PROPERTIES_FILE)).getHttpsPropertiesMap();
final KeyStore ks = KeyStore.getInstance("JKS");
final String selfSignedKeystoreFile =
httpsProperties.get("https.certificate.location");
diff --git
a/traffic_router/connector/src/main/java/org/apache/traffic_control/traffic_router/utils/HttpsProperties.java
b/traffic_router/connector/src/main/java/org/apache/traffic_control/traffic_router/utils/HttpsProperties.java
index 1e1a297e6c..d4f19141fd 100644
---
a/traffic_router/connector/src/main/java/org/apache/traffic_control/traffic_router/utils/HttpsProperties.java
+++
b/traffic_router/connector/src/main/java/org/apache/traffic_control/traffic_router/utils/HttpsProperties.java
@@ -25,29 +25,51 @@ import java.util.Map;
public class HttpsProperties {
private static final Logger log =
LogManager.getLogger(HttpsProperties.class);
- private static final String HTTPS_PROPERTIES_FILE =
"/opt/traffic_router/conf/https.properties";
+ private static final String HTTPS_CERTIFICATE_OU =
"https.certificate.organizational.unit";
+
private final Map<String, String> httpsPropertiesMap;
- public HttpsProperties() {
- this.httpsPropertiesMap = loadHttpsProperties();
+ public HttpsProperties(final String fileName) {
+ this.httpsPropertiesMap = loadHttpsProperties(fileName);
}
public Map<String, String> getHttpsPropertiesMap() {
return httpsPropertiesMap;
}
- private static Map<String, String> loadHttpsProperties() {
+ private static Map<String, String> loadHttpsProperties(final String
fileName) {
try {
final Map<String, String> httpsProperties = new HashMap<>();
-
Files.readAllLines(Paths.get(HTTPS_PROPERTIES_FILE)).forEach(propString -> {
+ Files.readAllLines(Paths.get(fileName)).forEach(propString -> {
if (!propString.startsWith("#")) { // Ignores comments in
properties file
- final String[] prop = propString.split("=");
- httpsProperties.put(prop[0], prop[1]);
+ final String[] props = propString.split("=");
+ if (props.length < 2) {
+ log.error("Property malformed, should be in the form
key=value");
+ } else {
+ final String key = props[0];
+ final String val = props[1];
+ if (key.equals(HTTPS_CERTIFICATE_OU)) {
+ if (val.equals("") || val.length() < 2) {
+ log.error("Malformed " + HTTPS_CERTIFICATE_OU
+ " property value");
+ } else {
+ final String[] orgUnits = val.split(",");
+ String organizationalUnit = "";
+ StringBuilder sb = new
StringBuilder(organizationalUnit);
+ for (final String ou : orgUnits) {
+ sb = sb.append("; OU=" + ou);
+ }
+ organizationalUnit = sb.toString();
+ httpsProperties.put(key, organizationalUnit);
+ }
+ } else {
+ httpsProperties.put(key, val);
+ }
+ }
}
});
return httpsProperties;
} catch (Exception e) {
- log.error("Error loading https properties file.");
+ log.error("Error loading https properties file at "+ fileName+ ",
error: " +e.getMessage());
return null;
}
}
diff --git a/traffic_router/connector/src/test/java/conf/https.properties
b/traffic_router/connector/src/test/java/conf/https.properties
new file mode 100644
index 0000000000..2c781e5ba8
--- /dev/null
+++ b/traffic_router/connector/src/test/java/conf/https.properties
@@ -0,0 +1,24 @@
+#
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+https.certificate.location=/opt/traffic_router/conf/keyStore.jks
+https.password=changeit
+https.key.size=1024
+https.signature.algorithm=TestAlgorithm
+https.validity.years=TestValidity
+https.certificate.country=TestCountry
+https.certificate.state=TestState
+https.certificate.locality=TestLocality
+https.certificate.organization=TestOrg
+https.certificate.organizational.unit=Test Org Unit, Test Org Unit 2
\ No newline at end of file
diff --git
a/traffic_router/connector/src/test/java/utils/HttpsPropertiesTest.java
b/traffic_router/connector/src/test/java/utils/HttpsPropertiesTest.java
new file mode 100644
index 0000000000..3683e076e6
--- /dev/null
+++ b/traffic_router/connector/src/test/java/utils/HttpsPropertiesTest.java
@@ -0,0 +1,41 @@
+/*
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package utils;
+
+import org.apache.traffic_control.traffic_router.utils.HttpsProperties;
+import org.junit.Test;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.equalTo;
+import java.util.Map;
+
+public class HttpsPropertiesTest {
+ @Test
+ public void checkGetHttpsProperties() throws Exception {
+ final String fileName = "src/test/java/conf/https.properties";
+ HttpsProperties httpsProperties = new HttpsProperties(fileName);
+ Map<String, String> propsMap = httpsProperties.getHttpsPropertiesMap();
+ assertThat(propsMap.get("https.certificate.location"),
equalTo("/opt/traffic_router/conf/keyStore.jks"));
+ assertThat(propsMap.get("https.password"), equalTo("changeit"));
+ assertThat(propsMap.get("https.key.size"), equalTo("1024"));
+ assertThat(propsMap.get("https.signature.algorithm"),
equalTo("TestAlgorithm"));
+ assertThat(propsMap.get("https.validity.years"),
equalTo("TestValidity"));
+ assertThat(propsMap.get("https.certificate.country"),
equalTo("TestCountry"));
+ assertThat(propsMap.get("https.certificate.state"),
equalTo("TestState"));
+ assertThat(propsMap.get("https.certificate.locality"),
equalTo("TestLocality"));
+ assertThat(propsMap.get("https.certificate.organization"),
equalTo("TestOrg"));
+ assertThat(propsMap.get("https.certificate.organizational.unit"),
equalTo("; OU=Test Org Unit; OU= Test Org Unit 2"));
+ }
+}