Le regex fix (#6184)
* Lets Encrypt updated for domains that don't contain the xmlid
* updated changelog
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6759428..88afdf0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -122,6 +122,7 @@
- Fixed Federations IMS so TR federations watcher will get updates.
- [#5129](https://github.com/apache/trafficcontrol/issues/5129) - Updated TM so that it returns a 404 if the endpoint is not supported.
- [#5992](https://github.com/apache/trafficcontrol/issues/5992) - Updated Traffic Router Integration tests to use a mock Traffic Monitor and Traffic Ops server
+- [#6093](https://github.com/apache/trafficcontrol/issues/6093) - Fixed Let's Encrypt to work for delivery services where the domain does not contain the XMLID.
### Changed
- Migrated completely off of bower in favor of npm
diff --git a/traffic_ops/app/db/migrations/2021090914220900_le_dns_challenge_xml_id.down.sql b/traffic_ops/app/db/migrations/2021090914220900_le_dns_challenge_xml_id.down.sql
new file mode 100644
index 0000000..d2e7e61
--- /dev/null
+++ b/traffic_ops/app/db/migrations/2021090914220900_le_dns_challenge_xml_id.down.sql
@@ -0,0 +1,18 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to you 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.
+ */
+
+ALTER TABLE dnschallenges DROP COLUMN IF EXISTS xml_id;
diff --git a/traffic_ops/app/db/migrations/2021090914220900_le_dns_challenge_xml_id.up.sql b/traffic_ops/app/db/migrations/2021090914220900_le_dns_challenge_xml_id.up.sql
new file mode 100644
index 0000000..64df6a0
--- /dev/null
+++ b/traffic_ops/app/db/migrations/2021090914220900_le_dns_challenge_xml_id.up.sql
@@ -0,0 +1,18 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to you 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.
+ */
+
+ALTER TABLE dnschallenges ADD COLUMN xml_id text NOT NULL;
diff --git a/traffic_ops/traffic_ops_golang/deliveryservice/acme.go b/traffic_ops/traffic_ops_golang/deliveryservice/acme.go
index 044a466..e89b8e6 100644
--- a/traffic_ops/traffic_ops_golang/deliveryservice/acme.go
+++ b/traffic_ops/traffic_ops_golang/deliveryservice/acme.go
@@ -82,7 +82,8 @@
// DNSProviderTrafficRouter is used in the lego library and contains a database in order to store the DNS challenges for ACME protocol.
type DNSProviderTrafficRouter struct {
- db *sqlx.DB
+ db *sqlx.DB
+ xmlId *string
}
// NewDNSProviderTrafficRouter returns a new DNSProviderTrafficRouter object.
@@ -100,8 +101,8 @@
tx, err := d.db.Begin()
fqdn, value := dns01.GetRecord(domain, keyAuth)
- q := `INSERT INTO dnschallenges (fqdn, record) VALUES ($1, $2)`
- response, err := tx.Exec(q, fqdn, value)
+ q := `INSERT INTO dnschallenges (fqdn, record, xml_id) VALUES ($1, $2, $3)`
+ response, err := tx.Exec(q, fqdn, value, *d.xmlId)
tx.Commit()
if err != nil {
log.Errorf("Inserting dns txt record for fqdn '" + fqdn + "' record '" + value + "': " + err.Error())
@@ -439,7 +440,7 @@
account = acmeAccount
}
- client, err := GetAcmeClient(account, userTx, db)
+ client, err := GetAcmeClient(account, userTx, db, req.Key)
if err != nil {
log.Errorf("acme: getting acme client for provider %s: %v", provider, err)
api.CreateChangeLogRawTx(api.ApiChange, "DS: "+*req.DeliveryService+", ID: "+strconv.Itoa(dsID)+", ACTION: FAILED to add SSL keys with "+provider, currentUser, logTx)
@@ -560,7 +561,7 @@
}
// GetAcmeClient uses the ACME account information in either cdn.conf or the database to create and register an ACME client.
-func GetAcmeClient(acmeAccount *config.ConfigAcmeAccount, userTx *sql.Tx, db *sqlx.DB) (*lego.Client, error) {
+func GetAcmeClient(acmeAccount *config.ConfigAcmeAccount, userTx *sql.Tx, db *sqlx.DB, xmlId *string) (*lego.Client, error) {
if acmeAccount.UserEmail == "" {
log.Errorf("An email address must be provided to use ACME with %v", acmeAccount.AcmeProvider)
return nil, errors.New("An email address must be provided to use ACME with " + acmeAccount.AcmeProvider)
@@ -610,6 +611,7 @@
client.Challenge.Remove(challenge.TLSALPN01)
trafficRouterDns := NewDNSProviderTrafficRouter()
trafficRouterDns.db = db
+ trafficRouterDns.xmlId = xmlId
if err != nil {
log.Errorf("Error creating Traffic Router DNS provider: %s", err.Error())
return nil, err
diff --git a/traffic_ops/traffic_ops_golang/deliveryservice/acme_renew.go b/traffic_ops/traffic_ops_golang/deliveryservice/acme_renew.go
index 7b92526..eb7c78a 100644
--- a/traffic_ops/traffic_ops_golang/deliveryservice/acme_renew.go
+++ b/traffic_ops/traffic_ops_golang/deliveryservice/acme_renew.go
@@ -145,7 +145,7 @@
return nil, errors.New("No acme account information in cdn.conf for " + keyObj.AuthType), http.StatusInternalServerError
}
- client, err := GetAcmeClient(acmeAccount, userTx, db)
+ client, err := GetAcmeClient(acmeAccount, userTx, db, &dsName)
if err != nil {
api.CreateChangeLogRawTx(api.ApiChange, "DS: "+dsName+", ID: "+strconv.Itoa(*dsID)+", ACTION: FAILED to add SSL keys with "+acmeAccount.AcmeProvider, currentUser, logTx)
return nil, errors.New("getting acme client: " + err.Error()), http.StatusInternalServerError
diff --git a/traffic_ops/traffic_ops_golang/deliveryservice/letsencrypt_dns_challenge.go b/traffic_ops/traffic_ops_golang/deliveryservice/letsencrypt_dns_challenge.go
index 45fb560..a9af8cd 100644
--- a/traffic_ops/traffic_ops_golang/deliveryservice/letsencrypt_dns_challenge.go
+++ b/traffic_ops/traffic_ops_golang/deliveryservice/letsencrypt_dns_challenge.go
@@ -33,6 +33,7 @@
type DnsRecord struct {
Fqdn *string `json:"fqdn" db:"fqdn"`
Record *string `json:"record" db:"record"`
+ XmlId *string `json:"xmlId" db:"xml_id"`
}
func GetDnsChallengeRecords(w http.ResponseWriter, r *http.Request) {
@@ -43,7 +44,7 @@
}
defer inf.Close()
- getQuery := `SELECT fqdn, record FROM dnschallenges`
+ getQuery := `SELECT fqdn, record, xml_id FROM dnschallenges`
queryParamsToQueryCols := map[string]dbhelpers.WhereColumnInfo{
"fqdn": dbhelpers.WhereColumnInfo{Column: "fqdn"},
@@ -73,7 +74,7 @@
defer rows.Close()
for rows.Next() {
record := DnsRecord{}
- if err := rows.Scan(&record.Fqdn, &record.Record); err != nil {
+ if err := rows.Scan(&record.Fqdn, &record.Record, &record.XmlId); err != nil {
return nil, errors.New("scanning dns challenge records: " + err.Error())
}
records = append(records, record)
diff --git a/traffic_router/core/src/main/java/org/apache/traffic_control/traffic_router/core/ds/LetsEncryptDnsChallenge.java b/traffic_router/core/src/main/java/org/apache/traffic_control/traffic_router/core/ds/LetsEncryptDnsChallenge.java
index 8261ba0..2260825 100644
--- a/traffic_router/core/src/main/java/org/apache/traffic_control/traffic_router/core/ds/LetsEncryptDnsChallenge.java
+++ b/traffic_router/core/src/main/java/org/apache/traffic_control/traffic_router/core/ds/LetsEncryptDnsChallenge.java
@@ -26,6 +26,9 @@
@JsonProperty
private String record;
+ @JsonProperty
+ private String xmlId;
+
public String getFqdn() {
return fqdn;
}
@@ -42,6 +45,14 @@
this.record = record;
}
+ public String getXmlId() {
+ return xmlId;
+ }
+
+ public void setXmlId(final String xmlId) {
+ this.xmlId = xmlId;
+ }
+
@Override
public boolean equals(final Object o) {
if (this == o) {
@@ -52,11 +63,12 @@
}
final LetsEncryptDnsChallenge that = (LetsEncryptDnsChallenge) o;
return fqdn.equals(that.fqdn) &&
- record.equals(that.record);
+ record.equals(that.record) &&
+ xmlId.equals(that.xmlId);
}
@Override
public int hashCode() {
- return Objects.hash(fqdn, record);
+ return Objects.hash(fqdn, record, xmlId);
}
}
diff --git a/traffic_router/core/src/main/java/org/apache/traffic_control/traffic_router/core/ds/LetsEncryptDnsChallengeWatcher.java b/traffic_router/core/src/main/java/org/apache/traffic_control/traffic_router/core/ds/LetsEncryptDnsChallengeWatcher.java
index 957bf06..5ebc89c 100644
--- a/traffic_router/core/src/main/java/org/apache/traffic_control/traffic_router/core/ds/LetsEncryptDnsChallengeWatcher.java
+++ b/traffic_router/core/src/main/java/org/apache/traffic_control/traffic_router/core/ds/LetsEncryptDnsChallengeWatcher.java
@@ -31,6 +31,7 @@
import java.io.*;
import java.time.Instant;
import java.util.HashMap;
+import java.util.Iterator;
import java.util.List;
public class LetsEncryptDnsChallengeWatcher extends AbstractResourceWatcher {
@@ -57,35 +58,33 @@
challengeList.forEach(challenge -> {
- final StringBuilder sb = new StringBuilder();
- sb.append(challenge.getFqdn());
- if (!challenge.getFqdn().endsWith(".")) {
- sb.append('.');
- }
- final String challengeDomain = sb.toString();
- final String fqdn = challengeDomain.substring(0, challengeDomain.length() - 1).replace("_acme-challenge.", "");
-
- ObjectNode deliveryServiceConfig = null;
- String dsLabel = "";
- final StringBuilder nameSb = new StringBuilder();
- nameSb.append("_acme-challenge");
- for (final String label : fqdn.split("\\.")) {
- deliveryServiceConfig = (ObjectNode) deliveryServicesNode.get(label);
- if (deliveryServiceConfig != null) {
- dsLabel = label;
- break;
- } else {
- nameSb.append('.');
- nameSb.append(label);
- }
+ final ObjectNode deliveryServiceConfig = (ObjectNode) deliveryServicesNode.get(challenge.getXmlId());
+ if (deliveryServiceConfig == null) {
+ LOGGER.error("finding deliveryservice in cr-config for " + challenge.getXmlId());
+ return;
}
- final String name = nameSb.toString();
+ String staticEntryString = challenge.getFqdn();
+ final ArrayNode domains = (ArrayNode) deliveryServiceConfig.get("domains");
+ if (domains == null || domains.size() == 0) {
+ LOGGER.error("no domains found in cr-config for deliveryservice " + challenge.getXmlId());
+ return;
+ }
- final ArrayNode staticDnsEntriesNode = updateStaticEntries(challenge, name, mapper, deliveryServiceConfig);
+ final Iterator<JsonNode> domainIter = domains.iterator();
+ while(domainIter.hasNext()) {
+ final JsonNode domainNode = domainIter.next();
+ staticEntryString = staticEntryString.replace(domainNode.asText() + ".", "");
+ }
+
+ if (staticEntryString.endsWith(".")) {
+ staticEntryString = staticEntryString.substring(0, staticEntryString.length() - 1);
+ }
+
+ final ArrayNode staticDnsEntriesNode = updateStaticEntries(challenge, staticEntryString, mapper, deliveryServiceConfig);
deliveryServiceConfig.set("staticDnsEntries", staticDnsEntriesNode);
- deliveryServicesNode.set(dsLabel, deliveryServiceConfig);
+ deliveryServicesNode.set(challenge.getXmlId(), deliveryServiceConfig);
});