This is an automated email from the ASF dual-hosted git repository.
ocket8888 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 aea565c Acme auto renew (#5514)
aea565c is described below
commit aea565cb4cddf6b4e0e402adb99c9915b4a97469
Author: mattjackson220 <[email protected]>
AuthorDate: Tue Feb 16 14:41:54 2021 -0700
Acme auto renew (#5514)
* Added ACME providers to autorenewal script
* updated Changelog and added note to docs
* updates per comments
* updates per comments
---
CHANGELOG.md | 1 +
docs/source/admin/traffic_ops.rst | 15 ++++++
docs/source/admin/traffic_router.rst | 20 ++++++-
...liveryservices_sslkeys_generate_letsencrypt.rst | 2 +-
docs/source/api/v1/letsencrypt_autorenew.rst | 54 ++++---------------
...liveryservices_sslkeys_generate_letsencrypt.rst | 2 +-
docs/source/api/v2/letsencrypt_autorenew.rst | 54 ++++---------------
...liveryservices_sslkeys_generate_letsencrypt.rst | 2 +-
docs/source/api/v3/letsencrypt_autorenew.rst | 54 ++++---------------
docs/source/api/v4/acme_autorenew.rst | 49 +++++++++++++++++
...liveryservices_sslkeys_generate_letsencrypt.rst | 2 +-
docs/source/api/v4/letsencrypt_autorenew.rst | 54 ++++---------------
infrastructure/cdn-in-a-box/traffic_ops/config.sh | 4 ++
traffic_ops/app/conf/cdn.conf | 4 ++
.../templates/send_mail/autorenewcerts_mail.html | 24 +++++++++
traffic_ops/traffic_ops_golang/config/config.go | 7 +++
.../traffic_ops_golang/deliveryservice/acme.go | 10 ++--
.../deliveryservice/autorenewcerts.go | 61 ++++++++++++++++++----
.../deliveryservice/letsencryptcert.go | 2 +-
traffic_ops/traffic_ops_golang/routing/routes.go | 3 +-
20 files changed, 227 insertions(+), 197 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index a3a730a..8bc4391 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -22,6 +22,7 @@ The format is based on [Keep a
Changelog](http://keepachangelog.com/en/1.0.0/).
- Traffic Ops Client: New Login function with more options, including falling
back to previous minor versions. See traffic_ops/v3-client documentation for
details.
- Added license files to the RPMs
- Added ACME certificate renewals and ACME account registration using external
account binding
+- Added functionality to automatically renew ACME certificates.
### Fixed
- [#5288](https://github.com/apache/trafficcontrol/issues/5288) - Fixed the
ability to create and update a server with MTU value >= 1280.
diff --git a/docs/source/admin/traffic_ops.rst
b/docs/source/admin/traffic_ops.rst
index 4b620eb..3a2c732 100644
--- a/docs/source/admin/traffic_ops.rst
+++ b/docs/source/admin/traffic_ops.rst
@@ -316,6 +316,13 @@ This file deals with the configuration parameters of
running Traffic Ops itself.
:kid: The key ID provided by the :abbr:`ACME (Automatic
Certificate Management Environment)` provider for
ref:`external_account_binding`.
:hmac_encoded: The :abbr:`HMAC (Hashed Message Authentication Code)`
key provided by the :abbr:`ACME (Automatic Certificate Management Environment)`
provider for ref:`external_account_binding`. This should be in Base64 URL
encoded.
+:acme_renewal: This object contains the information for the automatic renewal
script for certificates.
+
+ .. versionadded:: 5.1
+
+ :renew_days_before_expiration: Set the number of days before expiration
date to renew certificates.
+ :summary_email: The email address to use for summarizing certificate
expiration and renewal status. If it is blank, no email will be sent.
+
:geniso: This object contains configuration options for system ISO generation.
:iso_root_path: Sets the filesystem path to the root of the ISO
generation directory. For default installations, this should usually be set to
:file:`/opt/traffic_ops/app/public`.
@@ -350,8 +357,16 @@ This file deals with the configuration parameters of
running Traffic Ops itself.
:user_email: A required email address to create an account with Let's
Encrypt or to receive expiration updates. If this is not included then `rate
limits <https://letsencrypt.org/docs/rate-limits>`_ may apply for the number of
certificates.
:send_expiration_email: A boolean option to send email summarizing
certificate expiration status
+
+ .. deprecated:: 5.1
+ Future versions of Traffic Ops will not support this
legacy configuration option, see acme_renewal: { summary_email: <string> }
instead.
+
:convert_self_signed: A boolean option to convert self signed to Let's
Encrypt certificates as they expire. This only works for certificates labeled
as Self Signed in the Certificate Source field.
:renew_days_before_expiration: Set the number of days before expiration
date to renew certificates.
+
+ .. deprecated:: 5.1
+ Future versions of Traffic Ops will not support this
legacy configuration option, see acme_renewal: { renew_days_before_expiration:
<int> } instead.
+
:environment: This specifies which Let's Encrypt environment to use:
'staging' or 'production'. It defaults to 'production'.
:portal: This section provides information regarding a connected UI with which
users interact, so that emails can include links to it.
diff --git a/docs/source/admin/traffic_router.rst
b/docs/source/admin/traffic_router.rst
index 6ccce57..56a8ae6 100644
--- a/docs/source/admin/traffic_router.rst
+++ b/docs/source/admin/traffic_router.rst
@@ -833,9 +833,27 @@ Let's Encrypt can be set up through :ref:`cdn.conf` by
updating the following fi
+------------------------------+---------+----------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| renew_days_before_expiration | int | No | Number of days
before expiration date to renew certificates
|
+------------------------------+---------+----------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
- | environment | string | No | Let's Encrypt
environment to use. Options are 'staging' or 'production'. Defaults to
'production'
|
+ | environment | string | No | Let's Encrypt
environment to use. Options are 'staging' or 'production'. Defaults to
'production'.
|
+------------------------------+---------+----------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+Automatic Certificate Renewal
+-----------------------------
+If desired, an automated certificate renewal script is located at
:file:`/traffic_ops/etc/cron.d/autorenew_certs`. This job is setup to be run,
but the file must be updated with the username and password for Traffic Ops in
order to be run. In :ref:`cdn.conf` the following fields can be defined in
order to alter the number of days in advance to renew and send a summary email
after renewal.
+
+.. note:: In order for this to work, the AuthType field for the certificate
must match the ACME provider in the :ref:`cdn.conf`.
+
+.. important:: After the automatic renewal script has run, a queue and
snapshot must be run manually in order for the certificates to be used.
+
+.. table:: Fields to update to run the automatic renewal script under
`acme_renewal`:
+
+
+------------------------------+---------+----------+----------------------------------------------------------------------------------------------------------------------------+
+ | Name | Type | Required | Description
|
+
+==============================+=========+==========+============================================================================================================================+
+ | summary_email | boolean | No | The email address
to use for summarizing certificate expiration and renewal status. If it is
blank, no email will be sent. |
+
+------------------------------+---------+----------+----------------------------------------------------------------------------------------------------------------------------+
+ | renew_days_before_expiration | int | No | Number of days
before expiration date to renew certificates. Default is 30 days.
|
+
+------------------------------+---------+----------+----------------------------------------------------------------------------------------------------------------------------+
+
.. table:: Fields to update for sending emails under `smtp`
+------------+------------------+----------------------------------------------------------------------+
diff --git
a/docs/source/api/v1/deliveryservices_sslkeys_generate_letsencrypt.rst
b/docs/source/api/v1/deliveryservices_sslkeys_generate_letsencrypt.rst
index bde5f78..c73c40c 100644
--- a/docs/source/api/v1/deliveryservices_sslkeys_generate_letsencrypt.rst
+++ b/docs/source/api/v1/deliveryservices_sslkeys_generate_letsencrypt.rst
@@ -62,7 +62,7 @@ Response Structure
{ "alerts": [{
"level": "success",
- "text": "Beginning async call to Let's Encrypt for ds-01. This
may take a few minutes."
+ "text": "Beginning async call to Let's Encrypt for ds-01. This
may take a few minutes."
}]}
.. [#needOne] Either the ``key`` or the ``deliveryservice`` field must be
provided. If both are provided, then they must match.
diff --git a/docs/source/api/v1/letsencrypt_autorenew.rst
b/docs/source/api/v1/letsencrypt_autorenew.rst
index 854a931..c8a29e1 100644
--- a/docs/source/api/v1/letsencrypt_autorenew.rst
+++ b/docs/source/api/v1/letsencrypt_autorenew.rst
@@ -36,29 +36,6 @@ No parameters available
Response Structure
------------------
-:LetsEncryptExpirations: A list of objects with information regarding
certificate expiration for all Let's Encrypt certificates
-
- :XmlId: The :term:`Delivery Service`'s uniquely identifying
:ref:`ds-xmlid`
- :Version: An integer that defines the "version" of the key - which
may be thought of as the sequential generation; that is, the higher the number
the more recent the key
- :Expiration: The expiration date of the certificate for the
:term:`Delivery Service` in :rfc:`3339` format
- :AuthType: The authority type of the certificate for the
:term:`Delivery Service`
- :Error: Any errors received in the renewal process
-
-:SelfSignedExpirations: A list of objects with information regarding
certificate expiration for all self signed certificates
-
- :XmlId: The :term:`Delivery Service`'s uniquely identifying
:ref:`ds-xmlid`
- :Version: An integer that defines the "version" of the key - which
may be thought of as the sequential generation; that is, the higher the number
the more recent the key
- :Expiration: The expiration date of the certificate for the
:term:`Delivery Service` in :rfc:`3339` format
- :AuthType: The authority type of the certificate for the
:term:`Delivery Service`
- :Error: Any errors received in the renewal process
-
-:OtherExpirations: A list of objects with information regarding
certificate expiration for all other certificates
-
- :XmlId: The :term:`Delivery Service`'s uniquely identifying
:ref:`ds-xmlid`
- :Version: An integer that defines the "version" of the key - which
may be thought of as the sequential generation; that is, the higher the number
the more recent the key
- :Expiration: The expiration date of the certificate for the
:term:`Delivery Service` in :rfc:`3339` format
- :AuthType: The authority type of the certificate for the
:term:`Delivery Service`
- :Error: Any errors received in the renewal process
.. code-block:: http
:caption: Response Example
@@ -66,24 +43,13 @@ Response Structure
HTTP/1.1 200 OK
Content-Type: application/json
- { "response": {
- "LetsEncryptExpirations": [
- {
- "XmlId":"demo2",
- "Version":1,
- "Expiration":"2020-08-18T13:53:06Z",
- "AuthType":"Lets Encrypt",
- "Error":null
- }
- ],
- "SelfSignedExpirations": [
- {
- "XmlId":"demo1",
- "Version":3,
- "Expiration":"2020-08-18T13:53:06Z",
- "AuthType":"Self Signed",
- "Error":null
- }
- ],
- "OtherExpirations":null
- }}
+ { "alerts": [
+ {
+ "text": "This endpoint is deprecated, please use
letsencrypt/autorenew instead",
+ "level": "warning"
+ },
+ {
+ "text": "Beginning async call to renew certificates.
This may take a few minutes.",
+ "level": "success"
+ }
+ ]}
diff --git
a/docs/source/api/v2/deliveryservices_sslkeys_generate_letsencrypt.rst
b/docs/source/api/v2/deliveryservices_sslkeys_generate_letsencrypt.rst
index 929ed2e..42dc4b9 100644
--- a/docs/source/api/v2/deliveryservices_sslkeys_generate_letsencrypt.rst
+++ b/docs/source/api/v2/deliveryservices_sslkeys_generate_letsencrypt.rst
@@ -60,7 +60,7 @@ Response Structure
{ "alerts": [{
"level": "success",
- "text": "Beginning async call to Let's Encrypt for ds-01. This
may take a few minutes."
+ "text": "Beginning async call to Let's Encrypt for ds-01. This
may take a few minutes."
}]}
.. [#needOne] Either the ``key`` or the ``deliveryservice`` field must be
provided. If both are provided, then they must match.
diff --git a/docs/source/api/v2/letsencrypt_autorenew.rst
b/docs/source/api/v2/letsencrypt_autorenew.rst
index 014b013..4cdbce4 100644
--- a/docs/source/api/v2/letsencrypt_autorenew.rst
+++ b/docs/source/api/v2/letsencrypt_autorenew.rst
@@ -34,29 +34,6 @@ No parameters available
Response Structure
------------------
-:LetsEncryptExpirations: A list of objects with information regarding
certificate expiration for all Let's Encrypt certificates
-
- :XmlId: The :term:`Delivery Service`'s uniquely identifying
:ref:`ds-xmlid`
- :Version: An integer that defines the "version" of the key - which
may be thought of as the sequential generation; that is, the higher the number
the more recent the key
- :Expiration: The expiration date of the certificate for the
:term:`Delivery Service` in :rfc:`3339` format
- :AuthType: The authority type of the certificate for the
:term:`Delivery Service`
- :Error: Any errors received in the renewal process
-
-:SelfSignedExpirations: A list of objects with information regarding
certificate expiration for all self signed certificates
-
- :XmlId: The :term:`Delivery Service`'s uniquely identifying
:ref:`ds-xmlid`
- :Version: An integer that defines the "version" of the key - which
may be thought of as the sequential generation; that is, the higher the number
the more recent the key
- :Expiration: The expiration date of the certificate for the
:term:`Delivery Service` in :rfc:`3339` format
- :AuthType: The authority type of the certificate for the
:term:`Delivery Service`
- :Error: Any errors received in the renewal process
-
-:OtherExpirations: A list of objects with information regarding
certificate expiration for all other certificates
-
- :XmlId: The :term:`Delivery Service`'s uniquely identifying
:ref:`ds-xmlid`
- :Version: An integer that defines the "version" of the key - which
may be thought of as the sequential generation; that is, the higher the number
the more recent the key
- :Expiration: The expiration date of the certificate for the
:term:`Delivery Service` in :rfc:`3339` format
- :AuthType: The authority type of the certificate for the
:term:`Delivery Service`
- :Error: Any errors received in the renewal process
.. code-block:: http
:caption: Response Example
@@ -64,24 +41,13 @@ Response Structure
HTTP/1.1 200 OK
Content-Type: application/json
- { "response": {
- "LetsEncryptExpirations": [
- {
- "XmlId":"demo2",
- "Version":1,
- "Expiration":"2020-08-18T13:53:06Z",
- "AuthType":"Lets Encrypt",
- "Error":null
- }
- ],
- "SelfSignedExpirations": [
- {
- "XmlId":"demo1",
- "Version":3,
- "Expiration":"2020-08-18T13:53:06Z",
- "AuthType":"Self Signed",
- "Error":null
- }
- ],
- "OtherExpirations":null
- }}
+ { "alerts": [
+ {
+ "text": "This endpoint is deprecated, please use
letsencrypt/autorenew instead",
+ "level": "warning"
+ },
+ {
+ "text": "Beginning async call to renew certificates.
This may take a few minutes.",
+ "level": "success"
+ }
+ ]}
diff --git
a/docs/source/api/v3/deliveryservices_sslkeys_generate_letsencrypt.rst
b/docs/source/api/v3/deliveryservices_sslkeys_generate_letsencrypt.rst
index 2d6230a..9f35f8d 100644
--- a/docs/source/api/v3/deliveryservices_sslkeys_generate_letsencrypt.rst
+++ b/docs/source/api/v3/deliveryservices_sslkeys_generate_letsencrypt.rst
@@ -60,7 +60,7 @@ Response Structure
{ "alerts": [{
"level": "success",
- "text": "Beginning async call to Let's Encrypt for ds-01. This
may take a few minutes."
+ "text": "Beginning async call to Let's Encrypt for ds-01. This
may take a few minutes."
}]}
.. [#needOne] Either the ``key`` or the ``deliveryservice`` field must be
provided. If both are provided, then they must match.
diff --git a/docs/source/api/v3/letsencrypt_autorenew.rst
b/docs/source/api/v3/letsencrypt_autorenew.rst
index e858a9d..39d7a8d 100644
--- a/docs/source/api/v3/letsencrypt_autorenew.rst
+++ b/docs/source/api/v3/letsencrypt_autorenew.rst
@@ -34,29 +34,6 @@ No parameters available
Response Structure
------------------
-:LetsEncryptExpirations: A list of objects with information regarding
certificate expiration for all Let's Encrypt certificates
-
- :XmlId: The :term:`Delivery Service`'s uniquely identifying
:ref:`ds-xmlid`
- :Version: An integer that defines the "version" of the key - which
may be thought of as the sequential generation; that is, the higher the number
the more recent the key
- :Expiration: The expiration date of the certificate for the
:term:`Delivery Service` in :rfc:`3339` format
- :AuthType: The authority type of the certificate for the
:term:`Delivery Service`
- :Error: Any errors received in the renewal process
-
-:SelfSignedExpirations: A list of objects with information regarding
certificate expiration for all self signed certificates
-
- :XmlId: The :term:`Delivery Service`'s uniquely identifying
:ref:`ds-xmlid`
- :Version: An integer that defines the "version" of the key - which
may be thought of as the sequential generation; that is, the higher the number
the more recent the key
- :Expiration: The expiration date of the certificate for the
:term:`Delivery Service` in :rfc:`3339` format
- :AuthType: The authority type of the certificate for the
:term:`Delivery Service`
- :Error: Any errors received in the renewal process
-
-:OtherExpirations: A list of objects with information regarding
certificate expiration for all other certificates
-
- :XmlId: The :term:`Delivery Service`'s uniquely identifying
:ref:`ds-xmlid`
- :Version: An integer that defines the "version" of the key - which
may be thought of as the sequential generation; that is, the higher the number
the more recent the key
- :Expiration: The expiration date of the certificate for the
:term:`Delivery Service` in :rfc:`3339` format
- :AuthType: The authority type of the certificate for the
:term:`Delivery Service`
- :Error: Any errors received in the renewal process
.. code-block:: http
:caption: Response Example
@@ -64,24 +41,13 @@ Response Structure
HTTP/1.1 200 OK
Content-Type: application/json
- { "response": {
- "LetsEncryptExpirations": [
- {
- "XmlId":"demo2",
- "Version":1,
- "Expiration":"2020-08-18T13:53:06Z",
- "AuthType":"Lets Encrypt",
- "Error":null
- }
- ],
- "SelfSignedExpirations": [
- {
- "XmlId":"demo1",
- "Version":3,
- "Expiration":"2020-08-18T13:53:06Z",
- "AuthType":"Self Signed",
- "Error":null
- }
- ],
- "OtherExpirations":null
- }}
+ { "alerts": [
+ {
+ "text": "This endpoint is deprecated, please use
letsencrypt/autorenew instead",
+ "level": "warning"
+ },
+ {
+ "text": "Beginning async call to renew certificates.
This may take a few minutes.",
+ "level": "success"
+ }
+ ]}
diff --git a/docs/source/api/v4/acme_autorenew.rst
b/docs/source/api/v4/acme_autorenew.rst
new file mode 100644
index 0000000..bf65a0a
--- /dev/null
+++ b/docs/source/api/v4/acme_autorenew.rst
@@ -0,0 +1,49 @@
+..
+..
+.. 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.
+..
+
+.. _to-api-acnme-autorenew:
+
+******************
+``acme_autorenew``
+******************
+
+``POST``
+========
+Generates SSL certificates and private keys for all :term:`Delivery Services`
that have certificates expiring within the configured time. This
uses:abbr:`ACME (Automatic Certificate Management Environment)` or Let's
Encrypt depending on the certificate.
+
+:Auth. Required: Yes
+:Roles Required: "admin" or "operations"
+:Response Type: ``undefined``
+
+Request Structure
+-----------------
+No parameters available
+
+
+Response Structure
+------------------
+
+.. code-block:: http
+ :caption: Response Example
+
+ HTTP/1.1 202 Accepted
+ Content-Type: application/json
+
+ { "alerts": [
+ {
+ "text": "Beginning async call to renew certificates.
This may take a few minutes.",
+ "level": "success"
+ }
+ ]}
diff --git
a/docs/source/api/v4/deliveryservices_sslkeys_generate_letsencrypt.rst
b/docs/source/api/v4/deliveryservices_sslkeys_generate_letsencrypt.rst
index 67fc9ae..ddeab1d 100644
--- a/docs/source/api/v4/deliveryservices_sslkeys_generate_letsencrypt.rst
+++ b/docs/source/api/v4/deliveryservices_sslkeys_generate_letsencrypt.rst
@@ -60,7 +60,7 @@ Response Structure
{ "alerts": [{
"level": "success",
- "text": "Beginning async call to Let's Encrypt for ds-01. This
may take a few minutes."
+ "text": "Beginning async call to Let's Encrypt for ds-01. This
may take a few minutes."
}]}
.. [#needOne] Either the ``key`` or the ``deliveryservice`` field must be
provided. If both are provided, then they must match.
diff --git a/docs/source/api/v4/letsencrypt_autorenew.rst
b/docs/source/api/v4/letsencrypt_autorenew.rst
index 516d4bd..bf33acb 100644
--- a/docs/source/api/v4/letsencrypt_autorenew.rst
+++ b/docs/source/api/v4/letsencrypt_autorenew.rst
@@ -34,29 +34,6 @@ No parameters available
Response Structure
------------------
-:LetsEncryptExpirations: A list of objects with information regarding
certificate expiration for all Let's Encrypt certificates
-
- :XmlId: The :term:`Delivery Service`'s uniquely identifying
:ref:`ds-xmlid`
- :Version: An integer that defines the "version" of the key - which
may be thought of as the sequential generation; that is, the higher the number
the more recent the key
- :Expiration: The expiration date of the certificate for the
:term:`Delivery Service` in :rfc:`3339` format
- :AuthType: The authority type of the certificate for the
:term:`Delivery Service`
- :Error: Any errors received in the renewal process
-
-:SelfSignedExpirations: A list of objects with information regarding
certificate expiration for all self signed certificates
-
- :XmlId: The :term:`Delivery Service`'s uniquely identifying
:ref:`ds-xmlid`
- :Version: An integer that defines the "version" of the key - which
may be thought of as the sequential generation; that is, the higher the number
the more recent the key
- :Expiration: The expiration date of the certificate for the
:term:`Delivery Service` in :rfc:`3339` format
- :AuthType: The authority type of the certificate for the
:term:`Delivery Service`
- :Error: Any errors received in the renewal process
-
-:OtherExpirations: A list of objects with information regarding
certificate expiration for all other certificates
-
- :XmlId: The :term:`Delivery Service`'s uniquely identifying
:ref:`ds-xmlid`
- :Version: An integer that defines the "version" of the key - which
may be thought of as the sequential generation; that is, the higher the number
the more recent the key
- :Expiration: The expiration date of the certificate for the
:term:`Delivery Service` in :rfc:`3339` format
- :AuthType: The authority type of the certificate for the
:term:`Delivery Service`
- :Error: Any errors received in the renewal process
.. code-block:: http
:caption: Response Example
@@ -64,24 +41,13 @@ Response Structure
HTTP/1.1 200 OK
Content-Type: application/json
- { "response": {
- "LetsEncryptExpirations": [
- {
- "XmlId":"demo2",
- "Version":1,
- "Expiration":"2020-08-18T13:53:06Z",
- "AuthType":"Lets Encrypt",
- "Error":null
- }
- ],
- "SelfSignedExpirations": [
- {
- "XmlId":"demo1",
- "Version":3,
- "Expiration":"2020-08-18T13:53:06Z",
- "AuthType":"Self Signed",
- "Error":null
- }
- ],
- "OtherExpirations":null
- }}
+ { "alerts": [
+ {
+ "text": "This endpoint is deprecated, please use
letsencrypt/autorenew instead",
+ "level": "warning"
+ },
+ {
+ "text": "Beginning async call to renew certificates.
This may take a few minutes.",
+ "level": "success"
+ }
+ ]}
diff --git a/infrastructure/cdn-in-a-box/traffic_ops/config.sh
b/infrastructure/cdn-in-a-box/traffic_ops/config.sh
index 9dc3f6c..1d1ccaa 100755
--- a/infrastructure/cdn-in-a-box/traffic_ops/config.sh
+++ b/infrastructure/cdn-in-a-box/traffic_ops/config.sh
@@ -152,6 +152,10 @@ cat <<-EOF >/opt/traffic_ops/app/conf/cdn.conf
"renew_days_before_expiration": 30,
"environment": "staging"
},
+ "acme_renewal": {
+ "summary_email": "",
+ "renew_days_before_expiration": 30
+ },
"acme_accounts": [
{
"acme_provider" : "",
diff --git a/traffic_ops/app/conf/cdn.conf b/traffic_ops/app/conf/cdn.conf
index b6905ae..771d9ba 100644
--- a/traffic_ops/app/conf/cdn.conf
+++ b/traffic_ops/app/conf/cdn.conf
@@ -82,6 +82,10 @@
"renew_days_before_expiration": 30,
"environment": "production"
},
+ "acme_renewal": {
+ "summary_email": "",
+ "renew_days_before_expiration": 30
+ },
"acme_accounts": [
{
"acme_provider" : "",
diff --git a/traffic_ops/app/templates/send_mail/autorenewcerts_mail.html
b/traffic_ops/app/templates/send_mail/autorenewcerts_mail.html
index c9fdab1..a68494a 100644
--- a/traffic_ops/app/templates/send_mail/autorenewcerts_mail.html
+++ b/traffic_ops/app/templates/send_mail/autorenewcerts_mail.html
@@ -56,6 +56,30 @@ td, th {
</tbody>
</table>
+ <h1>ACME Certificates Summary</h1>
+ <table>
+ <thead>
+ <tr>
+ <th>XmlId</th>
+ <th>Version</th>
+ <th>Expiration</th>
+ <th>AuthType</th>
+ <th>Error</th>
+ </tr>
+ </thead>
+ <tbody>
+ {{range .AcmeExpirations}}
+ <tr>
+ <td>{{.XmlId}}</td>
+ <td>{{.Version}}</td>
+ <td>{{.Expiration}}</td>
+ <td>{{.AuthType}}</td>
+ <td>{{.Error}}</td>
+ </tr>
+ {{end}}
+ </tbody>
+ </table>
+
<h1>Self Signed Certificates Summary</h1>
<table>
<thead>
diff --git a/traffic_ops/traffic_ops_golang/config/config.go
b/traffic_ops/traffic_ops_golang/config/config.go
index 7f95a33..521f5e8 100644
--- a/traffic_ops/traffic_ops_golang/config/config.go
+++ b/traffic_ops/traffic_ops_golang/config/config.go
@@ -48,6 +48,7 @@ type Config struct {
SMTP *ConfigSMTP `json:"smtp"`
ConfigPortal `json:"portal"`
ConfigLetsEncrypt `json:"lets_encrypt"`
+ ConfigAcmeRenewal `json:"acme_renewal"`
AcmeAccounts []ConfigAcmeAccount `json:"acme_accounts"`
DB ConfigDatabase `json:"db"`
Secrets []string `json:"secrets"`
@@ -159,6 +160,12 @@ type ConfigLetsEncrypt struct {
Environment string `json:"environment"`
}
+// ConfigAcmeRenewal continas configuration information for automated ACME
renewals.
+type ConfigAcmeRenewal struct {
+ SummaryEmail string `json:"summary_email"`
+ RenewDaysBeforeExpiration int `json:"renew_days_before_expiration"`
+}
+
// ConfigAcmeAccount contains all account information for a single ACME
provider to be registered with External Account Binding
type ConfigAcmeAccount struct {
AcmeProvider string `json:"acme_provider"`
diff --git a/traffic_ops/traffic_ops_golang/deliveryservice/acme.go
b/traffic_ops/traffic_ops_golang/deliveryservice/acme.go
index 35476b4..30cdb49 100644
--- a/traffic_ops/traffic_ops_golang/deliveryservice/acme.go
+++ b/traffic_ops/traffic_ops_golang/deliveryservice/acme.go
@@ -49,6 +49,7 @@ import (
const validAccountStatus = "valid"
+// RenewAcmeCertificate renews the SSL certificate for a delivery service if
possible through ACME protocol.
func RenewAcmeCertificate(w http.ResponseWriter, r *http.Request) {
inf, userErr, sysErr, errCode := api.NewInfo(r, []string{"xmlid"}, nil)
if userErr != nil || sysErr != nil {
@@ -132,14 +133,13 @@ func renewAcmeCerts(cfg *config.Config, dsName string,
ctx context.Context, curr
return nil, errors.New("decoding cert for XMLID " + dsName + "
: " + err.Error()), http.StatusInternalServerError
}
- acmeAccount := getAcmeAccountConfig(cfg, keyObj.AuthType)
+ acmeAccount := GetAcmeAccountConfig(cfg, keyObj.AuthType)
if acmeAccount == nil {
return nil, errors.New("No acme account information in cdn.conf
for " + keyObj.AuthType), http.StatusInternalServerError
}
client, err := GetAcmeClient(acmeAccount, userTx, db)
if err != nil {
- log.Errorf(dsName+": Error getting acme client: %s",
err.Error())
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
}
@@ -192,7 +192,8 @@ func renewAcmeCerts(cfg *config.Config, dsName string, ctx
context.Context, curr
return nil, nil, http.StatusOK
}
-func getAcmeAccountConfig(cfg *config.Config, acmeProvider string)
*config.ConfigAcmeAccount {
+// GetAcmeAccountConfig returns the ACME account information from cdn.conf for
a given provider.
+func GetAcmeAccountConfig(cfg *config.Config, acmeProvider string)
*config.ConfigAcmeAccount {
for _, acmeCfg := range cfg.AcmeAccounts {
if acmeCfg.AcmeProvider == acmeProvider {
return &acmeCfg
@@ -212,7 +213,7 @@ func getDSIdAndVersionFromName(db *sqlx.DB, xmlId string)
(*int, *int64, error)
return &dsID, &certVersion, nil
}
-// GetAcmeClient uses the ACME account information in either cdn.conf or the
database to create and register an ACME client
+// 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) {
if acmeAccount.UserEmail == "" {
log.Errorf("An email address must be provided to use ACME with
%v", acmeAccount.AcmeProvider)
@@ -321,6 +322,7 @@ func GetAcmeClient(acmeAccount *config.ConfigAcmeAccount,
userTx *sql.Tx, db *sq
return client, nil
}
+// ConvertPrivateKeyToKeyPem converts an rsa.PrivateKey to be PEM encoded.
func ConvertPrivateKeyToKeyPem(userPrivateKey *rsa.PrivateKey) ([]byte, error)
{
userKeyDer := x509.MarshalPKCS1PrivateKey(userPrivateKey)
if userKeyDer == nil {
diff --git a/traffic_ops/traffic_ops_golang/deliveryservice/autorenewcerts.go
b/traffic_ops/traffic_ops_golang/deliveryservice/autorenewcerts.go
index 287409d..cea7e55 100644
--- a/traffic_ops/traffic_ops_golang/deliveryservice/autorenewcerts.go
+++ b/traffic_ops/traffic_ops_golang/deliveryservice/autorenewcerts.go
@@ -52,27 +52,42 @@ type DsExpirationInfo struct {
type ExpirationSummary struct {
LetsEncryptExpirations []DsExpirationInfo
SelfSignedExpirations []DsExpirationInfo
+ AcmeExpirations []DsExpirationInfo
OtherExpirations []DsExpirationInfo
}
const emailTemplateFile =
"/opt/traffic_ops/app/templates/send_mail/autorenewcerts_mail.html"
+const API_ACME_AUTORENEW = "acme_autorenew"
+// RenewCertificatesDeprecated renews all SSL certificates that are expiring
within a certain time limit with a deprecation alert.
+//// This will renew Let's Encrypt and ACME certificates.
+func RenewCertificatesDeprecated(w http.ResponseWriter, r *http.Request) {
+ renewCertificates(w, r, true)
+}
+
+// RenewCertificates renews all SSL certificates that are expiring within a
certain time limit.
+// This will renew Let's Encrypt and ACME certificates.
func RenewCertificates(w http.ResponseWriter, r *http.Request) {
+ renewCertificates(w, r, false)
+}
+
+func renewCertificates(w http.ResponseWriter, r *http.Request, deprecated
bool) {
+ deprecation := util.StrPtr(API_ACME_AUTORENEW)
inf, userErr, sysErr, errCode := api.NewInfo(r, nil, nil)
if userErr != nil || sysErr != nil {
- api.HandleErr(w, r, inf.Tx.Tx, errCode, userErr, sysErr)
+ api.HandleErrOptionalDeprecation(w, r, inf.Tx.Tx, errCode,
userErr, sysErr, deprecated, deprecation)
return
}
defer inf.Close()
if inf.Config.RiakEnabled == false {
- api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError,
errors.New("the Riak service is unavailable"), errors.New("getting SSL keys
from Riak by xml id: Riak is not configured"))
+ api.HandleErrOptionalDeprecation(w, r, inf.Tx.Tx,
http.StatusInternalServerError, errors.New("the Riak service is unavailable"),
errors.New("getting SSL keys from Riak by xml id: Riak is not configured"),
deprecated, deprecation)
return
}
rows, err := inf.Tx.Tx.Query(`SELECT xml_id, ssl_key_version FROM
deliveryservice WHERE ssl_key_version != 0`)
if err != nil {
- api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError,
nil, err)
+ api.HandleErrOptionalDeprecation(w, r, inf.Tx.Tx,
http.StatusInternalServerError, nil, err, deprecated, deprecation)
return
}
defer rows.Close()
@@ -91,7 +106,16 @@ func RenewCertificates(w http.ResponseWriter, r
*http.Request) {
go RunAutorenewal(existingCerts, inf.Config, ctx, inf.User)
- api.WriteRespAlert(w, r, tc.SuccessLevel, "Beginning async call to
renew Let's Encrypt certificates. This may take a few minutes.")
+ var alerts tc.Alerts
+ if deprecated {
+ alerts.AddAlerts(api.CreateDeprecationAlerts(deprecation))
+ }
+
+ alerts.AddAlert(tc.Alert{
+ Text: "Beginning async call to renew certificates. This may
take a few minutes.",
+ Level: tc.SuccessLevel.String(),
+ })
+ api.WriteAlerts(w, r, http.StatusAccepted, alerts)
}
func RunAutorenewal(existingCerts []ExistingCerts, cfg *config.Config, ctx
context.Context, currentUser *auth.CurrentUser) {
@@ -151,8 +175,11 @@ func RunAutorenewal(existingCerts []ExistingCerts, cfg
*config.Config, ctx conte
return
}
- // Renew only certificates within configured limit
- if expiration.After(time.Now().Add(time.Hour * 24 *
time.Duration(cfg.ConfigLetsEncrypt.RenewDaysBeforeExpiration))) {
+ // Renew only certificates within configured limit. Default is
30 days.
+ if cfg.ConfigAcmeRenewal.RenewDaysBeforeExpiration == 0 {
+ cfg.ConfigAcmeRenewal.RenewDaysBeforeExpiration = 30
+ }
+ if expiration.After(time.Now().Add(time.Hour * 24 *
time.Duration(cfg.ConfigAcmeRenewal.RenewDaysBeforeExpiration))) {
continue
}
@@ -183,12 +210,26 @@ func RunAutorenewal(existingCerts []ExistingCerts, cfg
*config.Config, ctx conte
} else if keyObj.AuthType == tc.SelfSignedCertAuthType {
keysFound.SelfSignedExpirations =
append(keysFound.SelfSignedExpirations, dsExpInfo)
} else {
- keysFound.OtherExpirations =
append(keysFound.OtherExpirations, dsExpInfo)
+ acmeAccount := GetAcmeAccountConfig(cfg,
keyObj.AuthType)
+ if acmeAccount == nil {
+ keysFound.OtherExpirations =
append(keysFound.OtherExpirations, dsExpInfo)
+ } else {
+ userErr, sysErr, statusCode :=
renewAcmeCerts(cfg, keyObj.DeliveryService, ctx, currentUser)
+ if userErr != nil {
+ dsExpInfo.Error = userErr
+ } else if sysErr != nil {
+ dsExpInfo.Error = sysErr
+ } else if statusCode != http.StatusOK {
+ dsExpInfo.Error = errors.New("Status
code not 200: " + strconv.Itoa(statusCode))
+ }
+ keysFound.AcmeExpirations =
append(keysFound.AcmeExpirations, dsExpInfo)
+ }
+
}
}
- if cfg.SMTP.Enabled && cfg.ConfigLetsEncrypt.SendExpEmail {
+ if cfg.SMTP.Enabled && cfg.ConfigAcmeRenewal.SummaryEmail != "" {
errCode, userErr, sysErr := AlertExpiringCerts(keysFound, *cfg)
if userErr != nil || sysErr != nil {
log.Errorf("cert autorenewal: sending email: errCode:
%d userErr: %v sysErr: %v", errCode, userErr, sysErr)
@@ -200,12 +241,12 @@ func RunAutorenewal(existingCerts []ExistingCerts, cfg
*config.Config, ctx conte
func AlertExpiringCerts(certsFound ExpirationSummary, config config.Config)
(int, error, error) {
header := "From: " + config.ConfigTO.EmailFrom.String() + "\r\n" +
- "To: " + config.ConfigLetsEncrypt.Email + "\r\n" +
+ "To: " + config.ConfigAcmeRenewal.SummaryEmail + "\r\n" +
"MIME-version: 1.0;\r\n" +
"Content-Type: text/html; charset=\"UTF-8\";\r\n" +
"Subject: Certificate Expiration Summary\r\n\r\n"
- return api.SendEmailFromTemplate(config, header, certsFound,
emailTemplateFile, config.ConfigLetsEncrypt.Email)
+ return api.SendEmailFromTemplate(config, header, certsFound,
emailTemplateFile, config.ConfigAcmeRenewal.SummaryEmail)
}
type ExistingCerts struct {
diff --git a/traffic_ops/traffic_ops_golang/deliveryservice/letsencryptcert.go
b/traffic_ops/traffic_ops_golang/deliveryservice/letsencryptcert.go
index 431f9ec..a062ef8 100644
--- a/traffic_ops/traffic_ops_golang/deliveryservice/letsencryptcert.go
+++ b/traffic_ops/traffic_ops_golang/deliveryservice/letsencryptcert.go
@@ -179,7 +179,7 @@ func GenerateLetsEncryptCertificates(w http.ResponseWriter,
r *http.Request) {
go GetLetsEncryptCertificates(inf.Config, req, ctx, inf.User)
- api.WriteRespAlert(w, r, tc.SuccessLevel, "Beginning async call to
Let's Encrypt for "+*req.DeliveryService+". This may take a few minutes.")
+ api.WriteRespAlert(w, r, tc.SuccessLevel, "Beginning async call to
Let's Encrypt for "+*req.DeliveryService+". This may take a few minutes.")
}
diff --git a/traffic_ops/traffic_ops_golang/routing/routes.go
b/traffic_ops/traffic_ops_golang/routing/routes.go
index ef76227..a3c813a 100644
--- a/traffic_ops/traffic_ops_golang/routing/routes.go
+++ b/traffic_ops/traffic_ops_golang/routing/routes.go
@@ -153,6 +153,7 @@ func Routes(d ServerData) ([]Route, []RawRoute,
http.Handler, error) {
//Delivery service ACME
{api.Version{4, 0}, http.MethodPost,
`deliveryservices/xmlId/{xmlid}/sslkeys/renew$`,
deliveryservice.RenewAcmeCertificate, auth.PrivLevelOperations, Authenticated,
nil, 2534390573, noPerlBypass},
+ {api.Version{4, 0}, http.MethodPost, `acme_autorenew/?$`,
deliveryservice.RenewCertificates, auth.PrivLevelOperations, Authenticated,
nil, 2534390574, noPerlBypass},
// API Capability
{api.Version{4, 0}, http.MethodGet, `api_capabilities/?$`,
apicapability.GetAPICapabilitiesHandler, auth.PrivLevelReadOnly, Authenticated,
nil, 48132065893, noPerlBypass},
@@ -505,7 +506,7 @@ func Routes(d ServerData) ([]Route, []RawRoute,
http.Handler, error) {
//Delivery service LetsEncrypt
{api.Version{4, 0}, http.MethodPost,
`deliveryservices/sslkeys/generate/letsencrypt/?$`,
deliveryservice.GenerateLetsEncryptCertificates, auth.PrivLevelOperations,
Authenticated, nil, 4534390523, noPerlBypass},
{api.Version{4, 0}, http.MethodGet,
`letsencrypt/dnsrecords/?$`, deliveryservice.GetDnsChallengeRecords,
auth.PrivLevelOperations, Authenticated, nil, 4534390553, noPerlBypass},
- {api.Version{4, 0}, http.MethodPost,
`letsencrypt/autorenew/?$`, deliveryservice.RenewCertificates,
auth.PrivLevelOperations, Authenticated, nil, 4534390563, noPerlBypass},
+ {api.Version{4, 0}, http.MethodPost,
`letsencrypt/autorenew/?$`, deliveryservice.RenewCertificatesDeprecated,
auth.PrivLevelOperations, Authenticated, nil, 4534390563, noPerlBypass},
{api.Version{4, 0}, http.MethodGet,
`deliveryservices/{id}/health/?$`, deliveryservice.GetHealth,
auth.PrivLevelReadOnly, Authenticated, nil, 42345901013, noPerlBypass},