Compare commits

...

18 Commits

Author SHA1 Message Date
Adrien Ferrand
c71f383502 Handle unexpected key type migration. 2020-11-06 11:09:23 +01:00
Adrien Ferrand
198f5a99bc Merge pull request #8431 from atombrella/ec_dsa_2163
Implements support for ECDSA keys. Fixes #2163.
2020-11-04 23:43:46 +01:00
Mads Jensen
47c1045f6d Implements support for ECDSA keys. Fixes #2163.
Thanks to @pahrohfit and @Tomoyuki-GH for previous efforts to implement
suport for this.

Co-Authored-By: Robert Dailey <rob@wargam.es>
Co-Authored-By: Tomoyuki-GH <55397638+Tomoyuki-GH@users.noreply.github.com>
2020-11-04 15:16:48 +01:00
Brad Warren
e570e8ad32 Generate plugin snap configs as needed (#8411)
While reviewing https://github.com/certbot/certbot/pull/8404, it occurred to me that we're keeping both the generated files and the script used to generate them in `git`. Keeping both around seems unnecessary and is almost asking for the files to get out of sync at some point in the future. I fixed that by removing the files, adding them to `.gitignore`, and updating `build_remote.py` to generate them as needed.

* Remove generated files.

* Add generated files to gitignore.

* Reuse generate_dnsplugins_all.sh in build_remote
2020-10-30 14:12:57 -07:00
Brad Warren
df138d0027 Document that logs aren't always created. (#8410) 2020-10-30 13:15:47 -07:00
Brad Warren
9567352002 Update tools/snap/generate_* comments. (#8412) 2020-10-30 13:08:57 -07:00
Brad Warren
6c7b99f7e0 Remove fedora test farm tests (#8415)
While working on https://github.com/certbot/certbot/issues/8400, I noticed our Fedora AMIs are quite out of date. I considered updating them and what we could do to avoid the AMIs becoming so out-of-date in the future, but I think we don't actually need these tests.

I pulled a new count of Certbot users by OS and we have less than 7,000 Fedora users meaning only ~0.26% of Certbot users run Fedora. (I think Fedora is a popular desktop OS, but not as popular of a server OS which is where Certbot normally runs.)

Also, Certbot is regularly updated on Fedora including Fedora Rawhide or the rolling release version of Fedora which is similar to Debian sid/unstable. Rawhide changes far too frequently for it to make sense for us to run tests there in my opinon, but that also means that many problems such as Certbot's unit tests failing to run because of Fedora changes will be caught there by our Fedora maintainers before we'd even see it. This is how https://github.com/certbot/certbot/issues/7106 became an issue and how I learned [Certbot worked on Python 3.9 before we could run tests on it](https://github.com/certbot/certbot/issues/8134#issuecomment-655106169).

Because of all this, I think we should just simplify things and remove these tests. If a problem arises in the future, we can always add them back.
2020-10-28 15:52:20 -07:00
ohemorange
3673ca77a5 Fix LXD setup in snap README (#8416)
Fixes #8409.

Change the line in the README to allow `sudo /snap/bin/lxd.migrate -yes` to fail (for example, if there's nothing to migrate), but the whole command to succeed.

I tested this on a clean Focal install and confirmed it works.
2020-10-28 15:51:16 -07:00
Brad Warren
bb45c9aa41 Add Ubuntu 20.10 test farm tests (#8414)
Fixes https://github.com/certbot/certbot/issues/8400.

I had to switch the package installed in `apacheconftest` to `libapache2-mod-wsgi-py3` because Ubuntu 20.10 removed the Python 2 version of this module.

I didn't add this AMI to `tests/letstest/auto_targets.yaml` because like Ubuntu 20.04, `certbot-auto` has never worked on the OS.

* Add Ubuntu 20.20 test farm tests

* Try Python 3 WSGI
2020-10-28 15:08:16 -07:00
Brad Warren
4c347f5576 Switch to using python directly (#8413)
Windows installer tests failed last night because they suddenly switched to Python 3.9.

This is happening despite bf07ec20b0/.azure-pipelines/templates/jobs/packaging-jobs.yml (L92-L95) just a few lines earlier than what I modified in the PR here.

I think what's going on is `py -3` is finding and preferring the newer version of Python 3, Python 3.9, which was [just recently added to the image](https://github.com/actions/virtual-environments/issues/1740#issuecomment-717849233).

The [documentation for UsePythonVersion](https://docs.microsoft.com/en-us/azure/devops/pipelines/tasks/tool/use-python-version?view=azure-devops) says that:

> After running this task with "Add to PATH," the `python` command in subsequent scripts will be for the highest available version of the interpreter matching the version spec and architecture.

So let's just use `python` instead of `py`.
2020-10-28 14:12:32 -07:00
alexzorin
bf07ec20b0 run: dont report new certs when only re-installing (#8392) 2020-10-27 12:48:07 -07:00
ohemorange
fc864543a7 Simplify/document snap creation (#8404)
This PR adds the following documentation improvements to fix https://github.com/certbot/certbot/issues/7958:

- Simplify building external plugins
- Separate out certbot snap instructions from plugin instructions
- Mention that dnsimple is just an example for the plugin instructions
- Mention remote build for other architectures
- Mention snap doc exists elsewhere in developer guide (`contributing.rst`)

* Set up generate_dnsplugins_all.sh for all files and parametrize snapcraft and postrefreshhook files

* Create constraints file in the generate_dnsplugins_all script

* Separate out plugin and certbot snaps and update instructions

* Add remote build instructions

* Add pointers to the README to contributing.rst
2020-10-27 10:22:40 -07:00
Mark Dumay
4fa1df3075 Added links for gehirn and sakuracloud DNS plugins (#8406) 2020-10-26 17:22:00 -07:00
Adrien Ferrand
cfd0a6ff1f Remove usage of buildkit (#8408)
Fixes #8355 

During the troubleshooting of #8355, I came to the conclusion that using buildkit was creating the problem. Without it all docker images are built correctly. Initially buildkit was enabled to avoid a building problem in Azure Pipeline, but I also found in my recent tests that this problem was not there anymore.

You can find more details about the troubleshooting and reasoning in #8355.

As a consequence, I disable the usage of buildkit in this PR which will solve the issue.
2020-10-26 15:20:27 -07:00
Adrien Ferrand
00ed56afd6 Execute basic integration tests against Certbot dockers during CI (#8396)
Fixes #8202

This PR adds an Azure Pipeline job to execute certbot plugins --prepare for each Docker image created during the CI on amd64.

* Prepare basic integration tests for certbot dockers

* Add a displayName for the integration tests task
2020-10-23 11:02:35 -07:00
alexzorin
b6e3a3ad02 register: remove report_new_account, use logger (#8393) 2020-10-22 17:33:45 -07:00
Brad Warren
c250957ab0 Add .envrc. (#8382) 2020-10-22 14:01:30 -07:00
alexzorin
4eb0b560c5 manual: deprecate --manual-public-ip-logging-ok (#8381)
* manual: deprecate --manual-public-ip-logging-ok

* remove unused cli.report_config_interaction code

Co-authored-by: ohemorange <ebportnoy@gmail.com>
2020-10-22 12:12:54 -07:00
96 changed files with 1027 additions and 1204 deletions

View File

@@ -31,6 +31,25 @@ jobs:
path: $(Build.ArtifactStagingDirectory)
artifact: docker_$(DOCKER_ARCH)
displayName: Store Docker artifact
- job: docker_run
dependsOn: docker_build
pool:
vmImage: ubuntu-18.04
steps:
- task: DownloadPipelineArtifact@2
inputs:
artifact: docker_amd64
path: $(Build.SourcesDirectory)
displayName: Retrieve Docker images
- bash: set -e && docker load --input $(Build.SourcesDirectory)/images.tar
displayName: Load Docker images
- bash: |
set -ex
DOCKER_IMAGES=$(docker images --filter reference='*/certbot' --filter reference='*/dns-*' --format '{{.Repository}}:{{.Tag}}')
for DOCKER_IMAGE in ${DOCKER_IMAGES}
do docker run --rm "${DOCKER_IMAGE}" plugins --prepare
done
displayName: Run integration tests for Docker images
- job: installer_build
pool:
vmImage: vs2017-win2016
@@ -84,7 +103,7 @@ jobs:
# a recent version of pip, but we also to disable the isolated feature as described in
# https://github.com/certbot/certbot/issues/8256
- script: |
py -3 -m venv venv
python -m venv venv
venv\Scripts\python -m pip install pip==20.2.3 setuptools==50.3.0 wheel==0.35.1
venv\Scripts\python tools\pip_install.py -e certbot-ci
env:

12
.envrc Normal file
View File

@@ -0,0 +1,12 @@
# This file is just a nicety for developers who use direnv. When you cd under
# the Certbot repo, Certbot's virtual environment will be automatically
# activated and then deactivated when you cd elsewhere. Developers have to have
# direnv set up and run `direnv allow` to allow this file to execute on their
# system. You can find more information at https://direnv.net/.
. venv3/bin/activate
# direnv doesn't support modifying PS1 so we unset it to squelch the error
# it'll otherwise print about this being done in the activate script. See
# https://github.com/direnv/direnv/wiki/PS1. If you would like your shell
# prompt to change like it normally does, see
# https://github.com/direnv/direnv/wiki/Python#restoring-the-ps1.
unset PS1

1
.gitignore vendored
View File

@@ -64,3 +64,4 @@ certbot-dns*/certbot-dns*_amd64*.txt
certbot-dns*/certbot-dns*_arm*.txt
/certbot_amd64*.txt
/certbot_arm*.txt
certbot-dns*/snap

View File

@@ -154,6 +154,7 @@ Authors
* [Luca Olivetti](https://github.com/olivluca)
* [Luke Rogers](https://github.com/lukeroge)
* [Maarten](https://github.com/mrtndwrd)
* [Mads Jensen](https://github.com/atombrella)
* [Maikel Martens](https://github.com/krukas)
* [Malte Janduda](https://github.com/MalteJ)
* [Mantas Mikulėnas](https://github.com/grawity)
@@ -213,6 +214,7 @@ Authors
* [Richard Barnes](https://github.com/r-barnes)
* [Richard Panek](https://github.com/kernelpanek)
* [Robert Buchholz](https://github.com/rbu)
* [Robert Dailey](https://github.com/pahrohfit)
* [Robert Habermann](https://github.com/frennkie)
* [Robert Xiao](https://github.com/nneonneo)
* [Roland Shoemaker](https://github.com/rolandshoemaker)

View File

@@ -52,7 +52,7 @@ function Cleanup() {
# if our environment asks us to enable modules, do our best!
if [ "$1" = --debian-modules ] ; then
sudo apt-get install -y apache2
sudo apt-get install -y libapache2-mod-wsgi
sudo apt-get install -y libapache2-mod-wsgi-py3
sudo apt-get install -y libapache2-mod-macro
for mod in ssl rewrite macro wsgi deflate userdir version mime setenvif ; do

View File

@@ -0,0 +1,14 @@
This directory contains your keys and certificates.
`privkey.pem` : the private key for your certificate.
`fullchain.pem`: the certificate file used in most server software.
`chain.pem` : used for OCSP stapling in Nginx >=1.3.7.
`cert.pem` : will break many server configurations, and should not be used
without reading further documentation (see link below).
WARNING: DO NOT MOVE OR RENAME THESE FILES!
Certbot expects these files to remain in this location in order
to function properly!
We recommend not moving these files. For more information, see the Certbot
User Guide at https://certbot.eff.org/docs/using.html#where-are-my-certificates.

View File

@@ -0,0 +1,18 @@
-----BEGIN CERTIFICATE-----
MIIC2zCCAcOgAwIBAgIIBvrEnbPRYu8wDQYJKoZIhvcNAQELBQAwKDEmMCQGA1UE
AxMdUGViYmxlIEludGVybWVkaWF0ZSBDQSAxMjZjNGIwHhcNMjAxMDEyMjEwNzQw
WhcNMjUxMDEyMjEwNzQwWjAjMSEwHwYDVQQDExhjLmVuY3J5cHRpb24tZXhhbXBs
ZS5jb20wWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAARjMhuW0ENPPC33PjB5XsYU
CRw640kPQENIDatcTJaENZIZdqKd6rI6jc+lpbmXot7Zi52clJlSJS+V6oDAt2Lh
o4HYMIHVMA4GA1UdDwEB/wQEAwIFoDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYB
BQUHAwIwDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUj7Kd3ENqxlPf8B2bIGhsjydX
mPswHwYDVR0jBBgwFoAUEiGxlkRsi+VvcogH5dVD3h1laAcwMQYIKwYBBQUHAQEE
JTAjMCEGCCsGAQUFBzABhhVodHRwOi8vMTI3LjAuMC4xOjQwMDIwIwYDVR0RBBww
GoIYYy5lbmNyeXB0aW9uLWV4YW1wbGUuY29tMA0GCSqGSIb3DQEBCwUAA4IBAQCl
k0JXsa8y7fg41WWMDhw60bPW77O0FtOmTcnhdI5daYNemQVk+Q5EMaBLQ/oGjgXd
9QXFzXH1PL904YEnSLt+iTpXn++7rQSNzQsdYqw0neWk4f5pEBiN+WORpb6mwobV
ifMtBOkNEHvrJ2Pkci9U1lLwtKD/DSew6QtJU5DSkmH1XdGuMJiubygEIvELtvgq
cP9S368ZvPmPGmKaJQXBiuaR8MTjY/Bkr79aXQMjKbf+mpn7h0POCcePk1DY/rm6
Da+X16lf0hHyQhSUa7Vgyim6rK1/hlw+Z00i+sQCKD9Ih7kXuuGqfSDC33cfO8Tj
o/MXO8lcxkrem5zU5QWP
-----END CERTIFICATE-----

View File

@@ -0,0 +1,20 @@
-----BEGIN CERTIFICATE-----
MIIDUDCCAjigAwIBAgIIbi787yVrcMAwDQYJKoZIhvcNAQELBQAwIDEeMBwGA1UE
AxMVUGViYmxlIFJvb3QgQ0EgMGM1MjI1MCAXDTIwMTAxMjIwMjI0NloYDzIwNTAx
MDEyMjEyMjQ2WjAoMSYwJAYDVQQDEx1QZWJibGUgSW50ZXJtZWRpYXRlIENBIDEy
NmM0YjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALGeVk1BMJraeqRq
mJ2+hgso8VOAv2s2CVxUJjIVcn7f2adE8NyTsSQ1brlsnKCUYUw7yLTQH0izLQRB
qKVIDFkUqo5/FuTJ2QlfA2EwBL8J7s/7L7vj3L0DiVpwgxPSyFEwdl/Y5y7ofsX5
CIhCFcaMAmTIuKLiSfCJjGwkbEMuolm+lO8Mikxxc/JtDVUC479ugU7PU9O09bMH
nm+sD6Bgd+KMoPkCCCoeShJS9X3Ziq9HGc7Z6nhM/zirFARt2XkonEdAZ8br01zY
MRiY9txhlWQ7mUkOtzOSoEuYJNoUbvMUf0+tNzto26WRyF7dJmh7lTBsYrvAwUTx
PzNyst0CAwEAAaOBgzCBgDAOBgNVHQ8BAf8EBAMCAoQwHQYDVR0lBBYwFAYIKwYB
BQUHAwEGCCsGAQUFBwMCMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFBIhsZZE
bIvlb3KIB+XVQ94dZWgHMB8GA1UdIwQYMBaAFOaKTaXg37vKgRt7d79YOjAoAtJT
MA0GCSqGSIb3DQEBCwUAA4IBAQAU2mZii7PH2pkw2lNM0QqPbcW/UYyvFoUeM8Aq
uCtsI2s+oxCJTqzfLsA0N8NY4nHLQ5wAlNJfJekngni8hbmJTKU4JFTMe7kLQO8P
fJbk0pTzhhHVQw7CVwB6Pwq3u2m/JV+d6xDIDc+AVkuEl19ZJU0rTWyooClfFLZV
EdZmEiUtA3PGlxoYwYhoGHYlhFxsoFONhCsBEdN7k7FKtFGVxN7oc5SKmKp0YZTW
fcrEtrdNThATO4ymhCC2zh33NI/MT1O74fpaAc2k6LcTl57MKiLfTYX4LTL6v9JG
9tlNqjFVRRmzEbtXTPcCb+w9g1VqoOGok7mGXYLTYtShCuvE
-----END CERTIFICATE-----

View File

@@ -0,0 +1,38 @@
-----BEGIN CERTIFICATE-----
MIIC2zCCAcOgAwIBAgIILlmGtZhUFEwwDQYJKoZIhvcNAQELBQAwKDEmMCQGA1UE
AxMdUGViYmxlIEludGVybWVkaWF0ZSBDQSAxMjZjNGIwHhcNMjAxMDEyMjA1MDM0
WhcNMjUxMDEyMjA1MDM0WjAjMSEwHwYDVQQDExhjLmVuY3J5cHRpb24tZXhhbXBs
ZS5jb20wWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAARHEzR8JPWrEmpmgM+F2bk5
9mT0u6CjzmJG0QpbaqprLiG5NGpW84VQ5TFCrmC4KxYfigCfMhfHRNfFYvNUK3V/
o4HYMIHVMA4GA1UdDwEB/wQEAwIFoDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYB
BQUHAwIwDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQU1CsVL+bPnzaxxQ5jUENmQJIO
lKwwHwYDVR0jBBgwFoAUEiGxlkRsi+VvcogH5dVD3h1laAcwMQYIKwYBBQUHAQEE
JTAjMCEGCCsGAQUFBzABhhVodHRwOi8vMTI3LjAuMC4xOjQwMDIwIwYDVR0RBBww
GoIYYy5lbmNyeXB0aW9uLWV4YW1wbGUuY29tMA0GCSqGSIb3DQEBCwUAA4IBAQBn
2D8loC7pfk28JYpFLr5lmFKJWWmtLGlpsWDj61fVjtTfGKLziJz+MM6il4Y3hIz5
58qiFK0ue0M63dIBJ33N+XxSEXon4Q0gy/zRWfH9jtPJ3FwfjkU/RT9PAUClYi0G
ptNWnTmgQkNzousbcAtRNXuuShH3856vhUnwkX+xM+cbIDi1JVmFjcGrEEQJ0rUF
mv2ZTyfbWbUs3v4rReETi2NVzr1Ql6J+ByNcMvHODzFy3t0L6yelAw2ca1I+c9HU
+Z0tnp/ykR7eXNuVLivok8UBf5OC413lh8ZO5g+Bgzh/LdtkUuavg1MYtEX0H6mX
9U7y3nVI8WEbPGf+HDeu
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDUDCCAjigAwIBAgIIbi787yVrcMAwDQYJKoZIhvcNAQELBQAwIDEeMBwGA1UE
AxMVUGViYmxlIFJvb3QgQ0EgMGM1MjI1MCAXDTIwMTAxMjIwMjI0NloYDzIwNTAx
MDEyMjEyMjQ2WjAoMSYwJAYDVQQDEx1QZWJibGUgSW50ZXJtZWRpYXRlIENBIDEy
NmM0YjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALGeVk1BMJraeqRq
mJ2+hgso8VOAv2s2CVxUJjIVcn7f2adE8NyTsSQ1brlsnKCUYUw7yLTQH0izLQRB
qKVIDFkUqo5/FuTJ2QlfA2EwBL8J7s/7L7vj3L0DiVpwgxPSyFEwdl/Y5y7ofsX5
CIhCFcaMAmTIuKLiSfCJjGwkbEMuolm+lO8Mikxxc/JtDVUC479ugU7PU9O09bMH
nm+sD6Bgd+KMoPkCCCoeShJS9X3Ziq9HGc7Z6nhM/zirFARt2XkonEdAZ8br01zY
MRiY9txhlWQ7mUkOtzOSoEuYJNoUbvMUf0+tNzto26WRyF7dJmh7lTBsYrvAwUTx
PzNyst0CAwEAAaOBgzCBgDAOBgNVHQ8BAf8EBAMCAoQwHQYDVR0lBBYwFAYIKwYB
BQUHAwEGCCsGAQUFBwMCMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFBIhsZZE
bIvlb3KIB+XVQ94dZWgHMB8GA1UdIwQYMBaAFOaKTaXg37vKgRt7d79YOjAoAtJT
MA0GCSqGSIb3DQEBCwUAA4IBAQAU2mZii7PH2pkw2lNM0QqPbcW/UYyvFoUeM8Aq
uCtsI2s+oxCJTqzfLsA0N8NY4nHLQ5wAlNJfJekngni8hbmJTKU4JFTMe7kLQO8P
fJbk0pTzhhHVQw7CVwB6Pwq3u2m/JV+d6xDIDc+AVkuEl19ZJU0rTWyooClfFLZV
EdZmEiUtA3PGlxoYwYhoGHYlhFxsoFONhCsBEdN7k7FKtFGVxN7oc5SKmKp0YZTW
fcrEtrdNThATO4ymhCC2zh33NI/MT1O74fpaAc2k6LcTl57MKiLfTYX4LTL6v9JG
9tlNqjFVRRmzEbtXTPcCb+w9g1VqoOGok7mGXYLTYtShCuvE
-----END CERTIFICATE-----

View File

@@ -0,0 +1,5 @@
-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgNgefv2dad4U1VYEi
0WkdHuqywi5QXAe30OwNTTGjhbihRANCAARHEzR8JPWrEmpmgM+F2bk59mT0u6Cj
zmJG0QpbaqprLiG5NGpW84VQ5TFCrmC4KxYfigCfMhfHRNfFYvNUK3V/
-----END PRIVATE KEY-----

View File

@@ -0,0 +1,14 @@
This directory contains your keys and certificates.
`privkey.pem` : the private key for your certificate.
`fullchain.pem`: the certificate file used in most server software.
`chain.pem` : used for OCSP stapling in Nginx >=1.3.7.
`cert.pem` : will break many server configurations, and should not be used
without reading further documentation (see link below).
WARNING: DO NOT MOVE OR RENAME THESE FILES!
Certbot expects these files to remain in this location in order
to function properly!
We recommend not moving these files. For more information, see the Certbot
User Guide at https://certbot.eff.org/docs/using.html#where-are-my-certificates.

View File

@@ -0,0 +1 @@
../../archive/c.encryption-example.com/cert.pem

View File

@@ -0,0 +1 @@
../../archive/c.encryption-example.com/chain.pem

View File

@@ -0,0 +1 @@
../../archive/c.encryption-example.com/fullchain.pem

View File

@@ -0,0 +1 @@
../../archive/c.encryption-example.com/privkey.pem

View File

@@ -0,0 +1,17 @@
# renew_before_expiry = 30 days
version = 1.10.0.dev0
archive_dir = sample-config/archive/c.encryption-example.com
cert = sample-config/live/c.encryption-example.com/cert.pem
privkey = sample-config/live/c.encryption-example.com/privkey.pem
chain = sample-config/live/c.encryption-example.com/chain.pem
fullchain = sample-config/live/c.encryption-example.com/fullchain.pem
# Options used in the renewal process
[renewalparams]
authenticator = apache
installer = apache
account = 48d6b9e8d767eccf7e4d877d6ffa81e3
key_type = ecdsa
config_dir = sample-config-ec
elliptic_curve = secp256r1
manual_public_ip_logging_ok = True

View File

@@ -2,6 +2,10 @@
import io
import os
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.asymmetric.ec import EllipticCurvePrivateKey
from cryptography.hazmat.primitives.serialization import load_pem_private_key
try:
import grp
POSIX_MODE = True
@@ -16,6 +20,16 @@ SYSTEM_SID = 'S-1-5-18'
ADMINS_SID = 'S-1-5-32-544'
def assert_elliptic_key(key, curve):
with open(key, 'rb') as file:
privkey1 = file.read()
key = load_pem_private_key(data=privkey1, password=None, backend=default_backend())
assert isinstance(key, EllipticCurvePrivateKey)
assert isinstance(key.curve, curve)
def assert_hook_execution(probe_path, probe_content):
"""
Assert that a certbot hook has been executed

View File

@@ -9,12 +9,17 @@ import shutil
import subprocess
import time
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.asymmetric.ec import SECP256R1, SECP384R1
from cryptography.hazmat.primitives.asymmetric.rsa import RSAPrivateKey
from cryptography.hazmat.primitives.serialization import load_pem_private_key
from cryptography.x509 import NameOID
import pytest
from certbot_integration_tests.certbot_tests import context as certbot_context
from certbot_integration_tests.certbot_tests.assertions import assert_cert_count_for_lineage
from certbot_integration_tests.certbot_tests.assertions import assert_elliptic_key
from certbot_integration_tests.certbot_tests.assertions import assert_equals_group_owner
from certbot_integration_tests.certbot_tests.assertions import assert_equals_group_permissions
from certbot_integration_tests.certbot_tests.assertions import assert_equals_world_read_permissions
@@ -289,7 +294,7 @@ def test_renew_with_changed_private_key_complexity(context):
assert_cert_count_for_lineage(context.config_dir, certname, 1)
context.certbot(['renew'])
assert_cert_count_for_lineage(context.config_dir, certname, 2)
key2 = join(context.config_dir, 'archive', certname, 'privkey2.pem')
assert os.stat(key2).st_size > 3000
@@ -421,20 +426,90 @@ def test_reuse_key(context):
assert len({cert1, cert2, cert3}) == 3
def test_incorrect_key_type(context):
with pytest.raises(subprocess.CalledProcessError):
context.certbot(['--key-type="failwhale"'])
def test_ecdsa(context):
"""Test certificate issuance with ECDSA key."""
"""Test issuance for ECDSA CSR based request (legacy supported mode)."""
key_path = join(context.workspace, 'privkey-p384.pem')
csr_path = join(context.workspace, 'csr-p384.der')
cert_path = join(context.workspace, 'cert-p384.pem')
chain_path = join(context.workspace, 'chain-p384.pem')
misc.generate_csr([context.get_domain('ecdsa')], key_path, csr_path, key_type=misc.ECDSA_KEY_TYPE)
context.certbot(['auth', '--csr', csr_path, '--cert-path', cert_path, '--chain-path', chain_path])
misc.generate_csr(
[context.get_domain('ecdsa')],
key_path, csr_path,
key_type=misc.ECDSA_KEY_TYPE
)
context.certbot([
'auth', '--csr', csr_path, '--cert-path', cert_path,
'--chain-path', chain_path,
])
certificate = misc.read_certificate(cert_path)
assert 'ASN1 OID: secp384r1' in certificate
def test_default_key_type(context):
"""Test default key type is RSA"""
certname = context.get_domain('renew')
context.certbot([
'certonly',
'--cert-name', certname, '-d', certname
])
filename = join(context.config_dir, 'archive/{0}/privkey1.pem').format(certname)
with open(filename, 'rb') as file:
privkey1 = file.read()
key = load_pem_private_key(data=privkey1, password=None, backend=default_backend())
assert isinstance(key, RSAPrivateKey)
def test_default_curve_type(context):
"""test that the curve used when not specifying any is secp256r1"""
certname = context.get_domain('renew')
context.certbot([
'--key-type', 'ecdsa', '--cert-name', certname, '-d', certname
])
key1 = join(context.config_dir, 'archive/{0}/privkey1.pem'.format(certname))
assert_elliptic_key(key1, SECP256R1)
def test_renew_with_ec_keys(context):
"""Test proper renew with updated private key complexity."""
certname = context.get_domain('renew')
context.certbot([
'certonly',
'--cert-name', certname,
'--key-type', 'ecdsa', '--elliptic-curve', 'secp256r1',
'--force-renewal', '-d', certname,
])
key1 = join(context.config_dir, "archive", certname, 'privkey1.pem')
assert 200 < os.stat(key1).st_size < 250 # ec keys of 256 bits are ~225 bytes
assert_elliptic_key(key1, SECP256R1)
assert_cert_count_for_lineage(context.config_dir, certname, 1)
context.certbot(['renew', '--elliptic-curve', 'secp384r1'])
assert_cert_count_for_lineage(context.config_dir, certname, 2)
key2 = join(context.config_dir, 'archive', certname, 'privkey2.pem')
assert_elliptic_key(key2, SECP384R1)
assert 280 < os.stat(key2).st_size < 320 # ec keys of 384 bits are ~310 bytes
# We expect here that the command will fail because without --key-type specified,
# Certbot must errored out to prevent changing an existing certificate key type,
# without explicit user consent (by specifying both --cert-name and --key-type).
with pytest.raises(subprocess.CalledProcessError):
context.certbot([
'certonly',
'--force-renewal',
'-d', certname
])
def test_ocsp_must_staple(context):
"""Test that OCSP Must-Staple is correctly set in the generated certificate."""
if context.acme_server == 'pebble':
@@ -657,4 +732,4 @@ def test_preferred_chain(context):
with open(conf_path, 'r') as f:
assert 'preferred_chain = {}'.format(requested) in f.read(), \
'Expected preferred_chain to be set in renewal config'
'Expected preferred_chain to be set in renewal config'

View File

@@ -92,7 +92,6 @@ def _prepare_args_env(certbot_args, directory_url, http_01_port, tls_alpn_01_por
'--no-verify-ssl',
'--http-01-port', str(http_01_port),
'--https-port', str(tls_alpn_01_port),
'--manual-public-ip-logging-ok',
'--config-dir', config_dir,
'--work-dir', os.path.join(workspace, 'work'),
'--logs-dir', os.path.join(workspace, 'logs'),

View File

@@ -280,7 +280,11 @@ def load_sample_data_path(workspace):
if os.name == 'nt':
# Fix the symlinks on Windows if GIT is not configured to create them upon checkout
for lineage in ['a.encryption-example.com', 'b.encryption-example.com']:
for lineage in [
'a.encryption-example.com',
'b.encryption-example.com',
'c.encryption-example.com',
]:
current_live = os.path.join(copied, 'live', lineage)
for name in os.listdir(current_live):
if name != 'README':

View File

@@ -1,21 +0,0 @@
#!/bin/sh -e
# This file is generated by tools/generate_dnsplugins_postrefreshhook.sh and should not be edited manually.
# get certbot version
if [ ! -f "$SNAP/certbot-shared/certbot-version.txt" ]; then
echo "No certbot version available; not doing version comparison check" >> "$SNAP_DATA/debuglog"
exit 0
fi
cb_installed=$(cat $SNAP/certbot-shared/certbot-version.txt)
# get required certbot version for plugin. certbot version must be at least the plugin's
# version. note that this is not the required version in setup.py, but the version number itself.
cb_required=$(grep -oP "version = '\K.*(?=')" $SNAP/setup.py)
$SNAP/bin/python3 -c "import sys; from packaging import version; sys.exit(1) if version.parse('$cb_installed') < version.parse('$cb_required') else sys.exit(0)" || exit_code=$?
if [ "$exit_code" -eq 1 ]; then
echo "Certbot is version $cb_installed but needs to be at least $cb_required before" \
"this plugin can be updated; will try again on next refresh."
exit 1
fi

View File

@@ -1,41 +0,0 @@
# This file is generated by tools/generate_dnsplugins_snapcraft.sh and should not be edited manually.
name: certbot-dns-cloudflare
summary: Cloudflare DNS Authenticator plugin for Certbot
description: Cloudflare DNS Authenticator plugin for Certbot
confinement: strict
grade: stable
base: core20
adopt-info: certbot-dns-cloudflare
parts:
certbot-dns-cloudflare:
plugin: python
source: .
constraints: [$SNAPCRAFT_PART_SRC/snap-constraints.txt]
override-pull: |
snapcraftctl pull
snapcraftctl set-version `grep ^version $SNAPCRAFT_PART_SRC/setup.py | cut -f2 -d= | tr -d "'[:space:]"`
build-environment:
- SNAP_BUILD: "True"
# To build cryptography and cffi if needed
build-packages: [gcc, libffi-dev, libssl-dev, python3-dev]
certbot-metadata:
plugin: dump
source: .
stage: [setup.py, certbot-shared]
override-pull: |
snapcraftctl pull
mkdir -p $SNAPCRAFT_PART_SRC/certbot-shared
slots:
certbot:
interface: content
content: certbot-1
read:
- $SNAP/lib/python3.8/site-packages
plugs:
certbot-metadata:
interface: content
content: metadata-1
target: $SNAP/certbot-shared

View File

@@ -1,21 +0,0 @@
#!/bin/sh -e
# This file is generated by tools/generate_dnsplugins_postrefreshhook.sh and should not be edited manually.
# get certbot version
if [ ! -f "$SNAP/certbot-shared/certbot-version.txt" ]; then
echo "No certbot version available; not doing version comparison check" >> "$SNAP_DATA/debuglog"
exit 0
fi
cb_installed=$(cat $SNAP/certbot-shared/certbot-version.txt)
# get required certbot version for plugin. certbot version must be at least the plugin's
# version. note that this is not the required version in setup.py, but the version number itself.
cb_required=$(grep -oP "version = '\K.*(?=')" $SNAP/setup.py)
$SNAP/bin/python3 -c "import sys; from packaging import version; sys.exit(1) if version.parse('$cb_installed') < version.parse('$cb_required') else sys.exit(0)" || exit_code=$?
if [ "$exit_code" -eq 1 ]; then
echo "Certbot is version $cb_installed but needs to be at least $cb_required before" \
"this plugin can be updated; will try again on next refresh."
exit 1
fi

View File

@@ -1,41 +0,0 @@
# This file is generated by tools/generate_dnsplugins_snapcraft.sh and should not be edited manually.
name: certbot-dns-cloudxns
summary: CloudXNS DNS Authenticator plugin for Certbot
description: CloudXNS DNS Authenticator plugin for Certbot
confinement: strict
grade: stable
base: core20
adopt-info: certbot-dns-cloudxns
parts:
certbot-dns-cloudxns:
plugin: python
source: .
constraints: [$SNAPCRAFT_PART_SRC/snap-constraints.txt]
override-pull: |
snapcraftctl pull
snapcraftctl set-version `grep ^version $SNAPCRAFT_PART_SRC/setup.py | cut -f2 -d= | tr -d "'[:space:]"`
build-environment:
- SNAP_BUILD: "True"
# To build cryptography and cffi if needed
build-packages: [gcc, libffi-dev, libssl-dev, python3-dev]
certbot-metadata:
plugin: dump
source: .
stage: [setup.py, certbot-shared]
override-pull: |
snapcraftctl pull
mkdir -p $SNAPCRAFT_PART_SRC/certbot-shared
slots:
certbot:
interface: content
content: certbot-1
read:
- $SNAP/lib/python3.8/site-packages
plugs:
certbot-metadata:
interface: content
content: metadata-1
target: $SNAP/certbot-shared

View File

@@ -1,21 +0,0 @@
#!/bin/sh -e
# This file is generated by tools/generate_dnsplugins_postrefreshhook.sh and should not be edited manually.
# get certbot version
if [ ! -f "$SNAP/certbot-shared/certbot-version.txt" ]; then
echo "No certbot version available; not doing version comparison check" >> "$SNAP_DATA/debuglog"
exit 0
fi
cb_installed=$(cat $SNAP/certbot-shared/certbot-version.txt)
# get required certbot version for plugin. certbot version must be at least the plugin's
# version. note that this is not the required version in setup.py, but the version number itself.
cb_required=$(grep -oP "version = '\K.*(?=')" $SNAP/setup.py)
$SNAP/bin/python3 -c "import sys; from packaging import version; sys.exit(1) if version.parse('$cb_installed') < version.parse('$cb_required') else sys.exit(0)" || exit_code=$?
if [ "$exit_code" -eq 1 ]; then
echo "Certbot is version $cb_installed but needs to be at least $cb_required before" \
"this plugin can be updated; will try again on next refresh."
exit 1
fi

View File

@@ -1,41 +0,0 @@
# This file is generated by tools/generate_dnsplugins_snapcraft.sh and should not be edited manually.
name: certbot-dns-digitalocean
summary: DigitalOcean DNS Authenticator plugin for Certbot
description: DigitalOcean DNS Authenticator plugin for Certbot
confinement: strict
grade: stable
base: core20
adopt-info: certbot-dns-digitalocean
parts:
certbot-dns-digitalocean:
plugin: python
source: .
constraints: [$SNAPCRAFT_PART_SRC/snap-constraints.txt]
override-pull: |
snapcraftctl pull
snapcraftctl set-version `grep ^version $SNAPCRAFT_PART_SRC/setup.py | cut -f2 -d= | tr -d "'[:space:]"`
build-environment:
- SNAP_BUILD: "True"
# To build cryptography and cffi if needed
build-packages: [gcc, libffi-dev, libssl-dev, python3-dev]
certbot-metadata:
plugin: dump
source: .
stage: [setup.py, certbot-shared]
override-pull: |
snapcraftctl pull
mkdir -p $SNAPCRAFT_PART_SRC/certbot-shared
slots:
certbot:
interface: content
content: certbot-1
read:
- $SNAP/lib/python3.8/site-packages
plugs:
certbot-metadata:
interface: content
content: metadata-1
target: $SNAP/certbot-shared

View File

@@ -1,21 +0,0 @@
#!/bin/sh -e
# This file is generated by tools/generate_dnsplugins_postrefreshhook.sh and should not be edited manually.
# get certbot version
if [ ! -f "$SNAP/certbot-shared/certbot-version.txt" ]; then
echo "No certbot version available; not doing version comparison check" >> "$SNAP_DATA/debuglog"
exit 0
fi
cb_installed=$(cat $SNAP/certbot-shared/certbot-version.txt)
# get required certbot version for plugin. certbot version must be at least the plugin's
# version. note that this is not the required version in setup.py, but the version number itself.
cb_required=$(grep -oP "version = '\K.*(?=')" $SNAP/setup.py)
$SNAP/bin/python3 -c "import sys; from packaging import version; sys.exit(1) if version.parse('$cb_installed') < version.parse('$cb_required') else sys.exit(0)" || exit_code=$?
if [ "$exit_code" -eq 1 ]; then
echo "Certbot is version $cb_installed but needs to be at least $cb_required before" \
"this plugin can be updated; will try again on next refresh."
exit 1
fi

View File

@@ -1,41 +0,0 @@
# This file is generated by tools/generate_dnsplugins_snapcraft.sh and should not be edited manually.
name: certbot-dns-dnsimple
summary: DNSimple DNS Authenticator plugin for Certbot
description: DNSimple DNS Authenticator plugin for Certbot
confinement: strict
grade: stable
base: core20
adopt-info: certbot-dns-dnsimple
parts:
certbot-dns-dnsimple:
plugin: python
source: .
constraints: [$SNAPCRAFT_PART_SRC/snap-constraints.txt]
override-pull: |
snapcraftctl pull
snapcraftctl set-version `grep ^version $SNAPCRAFT_PART_SRC/setup.py | cut -f2 -d= | tr -d "'[:space:]"`
build-environment:
- SNAP_BUILD: "True"
# To build cryptography and cffi if needed
build-packages: [gcc, libffi-dev, libssl-dev, python3-dev]
certbot-metadata:
plugin: dump
source: .
stage: [setup.py, certbot-shared]
override-pull: |
snapcraftctl pull
mkdir -p $SNAPCRAFT_PART_SRC/certbot-shared
slots:
certbot:
interface: content
content: certbot-1
read:
- $SNAP/lib/python3.8/site-packages
plugs:
certbot-metadata:
interface: content
content: metadata-1
target: $SNAP/certbot-shared

View File

@@ -1,21 +0,0 @@
#!/bin/sh -e
# This file is generated by tools/generate_dnsplugins_postrefreshhook.sh and should not be edited manually.
# get certbot version
if [ ! -f "$SNAP/certbot-shared/certbot-version.txt" ]; then
echo "No certbot version available; not doing version comparison check" >> "$SNAP_DATA/debuglog"
exit 0
fi
cb_installed=$(cat $SNAP/certbot-shared/certbot-version.txt)
# get required certbot version for plugin. certbot version must be at least the plugin's
# version. note that this is not the required version in setup.py, but the version number itself.
cb_required=$(grep -oP "version = '\K.*(?=')" $SNAP/setup.py)
$SNAP/bin/python3 -c "import sys; from packaging import version; sys.exit(1) if version.parse('$cb_installed') < version.parse('$cb_required') else sys.exit(0)" || exit_code=$?
if [ "$exit_code" -eq 1 ]; then
echo "Certbot is version $cb_installed but needs to be at least $cb_required before" \
"this plugin can be updated; will try again on next refresh."
exit 1
fi

View File

@@ -1,41 +0,0 @@
# This file is generated by tools/generate_dnsplugins_snapcraft.sh and should not be edited manually.
name: certbot-dns-dnsmadeeasy
summary: DNS Made Easy DNS Authenticator plugin for Certbot
description: DNS Made Easy DNS Authenticator plugin for Certbot
confinement: strict
grade: stable
base: core20
adopt-info: certbot-dns-dnsmadeeasy
parts:
certbot-dns-dnsmadeeasy:
plugin: python
source: .
constraints: [$SNAPCRAFT_PART_SRC/snap-constraints.txt]
override-pull: |
snapcraftctl pull
snapcraftctl set-version `grep ^version $SNAPCRAFT_PART_SRC/setup.py | cut -f2 -d= | tr -d "'[:space:]"`
build-environment:
- SNAP_BUILD: "True"
# To build cryptography and cffi if needed
build-packages: [gcc, libffi-dev, libssl-dev, python3-dev]
certbot-metadata:
plugin: dump
source: .
stage: [setup.py, certbot-shared]
override-pull: |
snapcraftctl pull
mkdir -p $SNAPCRAFT_PART_SRC/certbot-shared
slots:
certbot:
interface: content
content: certbot-1
read:
- $SNAP/lib/python3.8/site-packages
plugs:
certbot-metadata:
interface: content
content: metadata-1
target: $SNAP/certbot-shared

View File

@@ -1,21 +0,0 @@
#!/bin/sh -e
# This file is generated by tools/generate_dnsplugins_postrefreshhook.sh and should not be edited manually.
# get certbot version
if [ ! -f "$SNAP/certbot-shared/certbot-version.txt" ]; then
echo "No certbot version available; not doing version comparison check" >> "$SNAP_DATA/debuglog"
exit 0
fi
cb_installed=$(cat $SNAP/certbot-shared/certbot-version.txt)
# get required certbot version for plugin. certbot version must be at least the plugin's
# version. note that this is not the required version in setup.py, but the version number itself.
cb_required=$(grep -oP "version = '\K.*(?=')" $SNAP/setup.py)
$SNAP/bin/python3 -c "import sys; from packaging import version; sys.exit(1) if version.parse('$cb_installed') < version.parse('$cb_required') else sys.exit(0)" || exit_code=$?
if [ "$exit_code" -eq 1 ]; then
echo "Certbot is version $cb_installed but needs to be at least $cb_required before" \
"this plugin can be updated; will try again on next refresh."
exit 1
fi

View File

@@ -1,41 +0,0 @@
# This file is generated by tools/generate_dnsplugins_snapcraft.sh and should not be edited manually.
name: certbot-dns-gehirn
summary: Gehirn Infrastructure Service DNS Authenticator plugin for Certbot
description: Gehirn Infrastructure Service DNS Authenticator plugin for Certbot
confinement: strict
grade: stable
base: core20
adopt-info: certbot-dns-gehirn
parts:
certbot-dns-gehirn:
plugin: python
source: .
constraints: [$SNAPCRAFT_PART_SRC/snap-constraints.txt]
override-pull: |
snapcraftctl pull
snapcraftctl set-version `grep ^version $SNAPCRAFT_PART_SRC/setup.py | cut -f2 -d= | tr -d "'[:space:]"`
build-environment:
- SNAP_BUILD: "True"
# To build cryptography and cffi if needed
build-packages: [gcc, libffi-dev, libssl-dev, python3-dev]
certbot-metadata:
plugin: dump
source: .
stage: [setup.py, certbot-shared]
override-pull: |
snapcraftctl pull
mkdir -p $SNAPCRAFT_PART_SRC/certbot-shared
slots:
certbot:
interface: content
content: certbot-1
read:
- $SNAP/lib/python3.8/site-packages
plugs:
certbot-metadata:
interface: content
content: metadata-1
target: $SNAP/certbot-shared

View File

@@ -1,21 +0,0 @@
#!/bin/sh -e
# This file is generated by tools/generate_dnsplugins_postrefreshhook.sh and should not be edited manually.
# get certbot version
if [ ! -f "$SNAP/certbot-shared/certbot-version.txt" ]; then
echo "No certbot version available; not doing version comparison check" >> "$SNAP_DATA/debuglog"
exit 0
fi
cb_installed=$(cat $SNAP/certbot-shared/certbot-version.txt)
# get required certbot version for plugin. certbot version must be at least the plugin's
# version. note that this is not the required version in setup.py, but the version number itself.
cb_required=$(grep -oP "version = '\K.*(?=')" $SNAP/setup.py)
$SNAP/bin/python3 -c "import sys; from packaging import version; sys.exit(1) if version.parse('$cb_installed') < version.parse('$cb_required') else sys.exit(0)" || exit_code=$?
if [ "$exit_code" -eq 1 ]; then
echo "Certbot is version $cb_installed but needs to be at least $cb_required before" \
"this plugin can be updated; will try again on next refresh."
exit 1
fi

View File

@@ -1,41 +0,0 @@
# This file is generated by tools/generate_dnsplugins_snapcraft.sh and should not be edited manually.
name: certbot-dns-google
summary: Google Cloud DNS Authenticator plugin for Certbot
description: Google Cloud DNS Authenticator plugin for Certbot
confinement: strict
grade: stable
base: core20
adopt-info: certbot-dns-google
parts:
certbot-dns-google:
plugin: python
source: .
constraints: [$SNAPCRAFT_PART_SRC/snap-constraints.txt]
override-pull: |
snapcraftctl pull
snapcraftctl set-version `grep ^version $SNAPCRAFT_PART_SRC/setup.py | cut -f2 -d= | tr -d "'[:space:]"`
build-environment:
- SNAP_BUILD: "True"
# To build cryptography and cffi if needed
build-packages: [gcc, libffi-dev, libssl-dev, python3-dev]
certbot-metadata:
plugin: dump
source: .
stage: [setup.py, certbot-shared]
override-pull: |
snapcraftctl pull
mkdir -p $SNAPCRAFT_PART_SRC/certbot-shared
slots:
certbot:
interface: content
content: certbot-1
read:
- $SNAP/lib/python3.8/site-packages
plugs:
certbot-metadata:
interface: content
content: metadata-1
target: $SNAP/certbot-shared

View File

@@ -1,21 +0,0 @@
#!/bin/sh -e
# This file is generated by tools/generate_dnsplugins_postrefreshhook.sh and should not be edited manually.
# get certbot version
if [ ! -f "$SNAP/certbot-shared/certbot-version.txt" ]; then
echo "No certbot version available; not doing version comparison check" >> "$SNAP_DATA/debuglog"
exit 0
fi
cb_installed=$(cat $SNAP/certbot-shared/certbot-version.txt)
# get required certbot version for plugin. certbot version must be at least the plugin's
# version. note that this is not the required version in setup.py, but the version number itself.
cb_required=$(grep -oP "version = '\K.*(?=')" $SNAP/setup.py)
$SNAP/bin/python3 -c "import sys; from packaging import version; sys.exit(1) if version.parse('$cb_installed') < version.parse('$cb_required') else sys.exit(0)" || exit_code=$?
if [ "$exit_code" -eq 1 ]; then
echo "Certbot is version $cb_installed but needs to be at least $cb_required before" \
"this plugin can be updated; will try again on next refresh."
exit 1
fi

View File

@@ -1,41 +0,0 @@
# This file is generated by tools/generate_dnsplugins_snapcraft.sh and should not be edited manually.
name: certbot-dns-linode
summary: Linode DNS Authenticator plugin for Certbot
description: Linode DNS Authenticator plugin for Certbot
confinement: strict
grade: stable
base: core20
adopt-info: certbot-dns-linode
parts:
certbot-dns-linode:
plugin: python
source: .
constraints: [$SNAPCRAFT_PART_SRC/snap-constraints.txt]
override-pull: |
snapcraftctl pull
snapcraftctl set-version `grep ^version $SNAPCRAFT_PART_SRC/setup.py | cut -f2 -d= | tr -d "'[:space:]"`
build-environment:
- SNAP_BUILD: "True"
# To build cryptography and cffi if needed
build-packages: [gcc, libffi-dev, libssl-dev, python3-dev]
certbot-metadata:
plugin: dump
source: .
stage: [setup.py, certbot-shared]
override-pull: |
snapcraftctl pull
mkdir -p $SNAPCRAFT_PART_SRC/certbot-shared
slots:
certbot:
interface: content
content: certbot-1
read:
- $SNAP/lib/python3.8/site-packages
plugs:
certbot-metadata:
interface: content
content: metadata-1
target: $SNAP/certbot-shared

View File

@@ -1,21 +0,0 @@
#!/bin/sh -e
# This file is generated by tools/generate_dnsplugins_postrefreshhook.sh and should not be edited manually.
# get certbot version
if [ ! -f "$SNAP/certbot-shared/certbot-version.txt" ]; then
echo "No certbot version available; not doing version comparison check" >> "$SNAP_DATA/debuglog"
exit 0
fi
cb_installed=$(cat $SNAP/certbot-shared/certbot-version.txt)
# get required certbot version for plugin. certbot version must be at least the plugin's
# version. note that this is not the required version in setup.py, but the version number itself.
cb_required=$(grep -oP "version = '\K.*(?=')" $SNAP/setup.py)
$SNAP/bin/python3 -c "import sys; from packaging import version; sys.exit(1) if version.parse('$cb_installed') < version.parse('$cb_required') else sys.exit(0)" || exit_code=$?
if [ "$exit_code" -eq 1 ]; then
echo "Certbot is version $cb_installed but needs to be at least $cb_required before" \
"this plugin can be updated; will try again on next refresh."
exit 1
fi

View File

@@ -1,41 +0,0 @@
# This file is generated by tools/generate_dnsplugins_snapcraft.sh and should not be edited manually.
name: certbot-dns-luadns
summary: LuaDNS Authenticator plugin for Certbot
description: LuaDNS Authenticator plugin for Certbot
confinement: strict
grade: stable
base: core20
adopt-info: certbot-dns-luadns
parts:
certbot-dns-luadns:
plugin: python
source: .
constraints: [$SNAPCRAFT_PART_SRC/snap-constraints.txt]
override-pull: |
snapcraftctl pull
snapcraftctl set-version `grep ^version $SNAPCRAFT_PART_SRC/setup.py | cut -f2 -d= | tr -d "'[:space:]"`
build-environment:
- SNAP_BUILD: "True"
# To build cryptography and cffi if needed
build-packages: [gcc, libffi-dev, libssl-dev, python3-dev]
certbot-metadata:
plugin: dump
source: .
stage: [setup.py, certbot-shared]
override-pull: |
snapcraftctl pull
mkdir -p $SNAPCRAFT_PART_SRC/certbot-shared
slots:
certbot:
interface: content
content: certbot-1
read:
- $SNAP/lib/python3.8/site-packages
plugs:
certbot-metadata:
interface: content
content: metadata-1
target: $SNAP/certbot-shared

View File

@@ -1,21 +0,0 @@
#!/bin/sh -e
# This file is generated by tools/generate_dnsplugins_postrefreshhook.sh and should not be edited manually.
# get certbot version
if [ ! -f "$SNAP/certbot-shared/certbot-version.txt" ]; then
echo "No certbot version available; not doing version comparison check" >> "$SNAP_DATA/debuglog"
exit 0
fi
cb_installed=$(cat $SNAP/certbot-shared/certbot-version.txt)
# get required certbot version for plugin. certbot version must be at least the plugin's
# version. note that this is not the required version in setup.py, but the version number itself.
cb_required=$(grep -oP "version = '\K.*(?=')" $SNAP/setup.py)
$SNAP/bin/python3 -c "import sys; from packaging import version; sys.exit(1) if version.parse('$cb_installed') < version.parse('$cb_required') else sys.exit(0)" || exit_code=$?
if [ "$exit_code" -eq 1 ]; then
echo "Certbot is version $cb_installed but needs to be at least $cb_required before" \
"this plugin can be updated; will try again on next refresh."
exit 1
fi

View File

@@ -1,41 +0,0 @@
# This file is generated by tools/generate_dnsplugins_snapcraft.sh and should not be edited manually.
name: certbot-dns-nsone
summary: NS1 DNS Authenticator plugin for Certbot
description: NS1 DNS Authenticator plugin for Certbot
confinement: strict
grade: stable
base: core20
adopt-info: certbot-dns-nsone
parts:
certbot-dns-nsone:
plugin: python
source: .
constraints: [$SNAPCRAFT_PART_SRC/snap-constraints.txt]
override-pull: |
snapcraftctl pull
snapcraftctl set-version `grep ^version $SNAPCRAFT_PART_SRC/setup.py | cut -f2 -d= | tr -d "'[:space:]"`
build-environment:
- SNAP_BUILD: "True"
# To build cryptography and cffi if needed
build-packages: [gcc, libffi-dev, libssl-dev, python3-dev]
certbot-metadata:
plugin: dump
source: .
stage: [setup.py, certbot-shared]
override-pull: |
snapcraftctl pull
mkdir -p $SNAPCRAFT_PART_SRC/certbot-shared
slots:
certbot:
interface: content
content: certbot-1
read:
- $SNAP/lib/python3.8/site-packages
plugs:
certbot-metadata:
interface: content
content: metadata-1
target: $SNAP/certbot-shared

View File

@@ -1,21 +0,0 @@
#!/bin/sh -e
# This file is generated by tools/generate_dnsplugins_postrefreshhook.sh and should not be edited manually.
# get certbot version
if [ ! -f "$SNAP/certbot-shared/certbot-version.txt" ]; then
echo "No certbot version available; not doing version comparison check" >> "$SNAP_DATA/debuglog"
exit 0
fi
cb_installed=$(cat $SNAP/certbot-shared/certbot-version.txt)
# get required certbot version for plugin. certbot version must be at least the plugin's
# version. note that this is not the required version in setup.py, but the version number itself.
cb_required=$(grep -oP "version = '\K.*(?=')" $SNAP/setup.py)
$SNAP/bin/python3 -c "import sys; from packaging import version; sys.exit(1) if version.parse('$cb_installed') < version.parse('$cb_required') else sys.exit(0)" || exit_code=$?
if [ "$exit_code" -eq 1 ]; then
echo "Certbot is version $cb_installed but needs to be at least $cb_required before" \
"this plugin can be updated; will try again on next refresh."
exit 1
fi

View File

@@ -1,41 +0,0 @@
# This file is generated by tools/generate_dnsplugins_snapcraft.sh and should not be edited manually.
name: certbot-dns-ovh
summary: OVH DNS Authenticator plugin for Certbot
description: OVH DNS Authenticator plugin for Certbot
confinement: strict
grade: stable
base: core20
adopt-info: certbot-dns-ovh
parts:
certbot-dns-ovh:
plugin: python
source: .
constraints: [$SNAPCRAFT_PART_SRC/snap-constraints.txt]
override-pull: |
snapcraftctl pull
snapcraftctl set-version `grep ^version $SNAPCRAFT_PART_SRC/setup.py | cut -f2 -d= | tr -d "'[:space:]"`
build-environment:
- SNAP_BUILD: "True"
# To build cryptography and cffi if needed
build-packages: [gcc, libffi-dev, libssl-dev, python3-dev]
certbot-metadata:
plugin: dump
source: .
stage: [setup.py, certbot-shared]
override-pull: |
snapcraftctl pull
mkdir -p $SNAPCRAFT_PART_SRC/certbot-shared
slots:
certbot:
interface: content
content: certbot-1
read:
- $SNAP/lib/python3.8/site-packages
plugs:
certbot-metadata:
interface: content
content: metadata-1
target: $SNAP/certbot-shared

View File

@@ -1,21 +0,0 @@
#!/bin/sh -e
# This file is generated by tools/generate_dnsplugins_postrefreshhook.sh and should not be edited manually.
# get certbot version
if [ ! -f "$SNAP/certbot-shared/certbot-version.txt" ]; then
echo "No certbot version available; not doing version comparison check" >> "$SNAP_DATA/debuglog"
exit 0
fi
cb_installed=$(cat $SNAP/certbot-shared/certbot-version.txt)
# get required certbot version for plugin. certbot version must be at least the plugin's
# version. note that this is not the required version in setup.py, but the version number itself.
cb_required=$(grep -oP "version = '\K.*(?=')" $SNAP/setup.py)
$SNAP/bin/python3 -c "import sys; from packaging import version; sys.exit(1) if version.parse('$cb_installed') < version.parse('$cb_required') else sys.exit(0)" || exit_code=$?
if [ "$exit_code" -eq 1 ]; then
echo "Certbot is version $cb_installed but needs to be at least $cb_required before" \
"this plugin can be updated; will try again on next refresh."
exit 1
fi

View File

@@ -1,41 +0,0 @@
# This file is generated by tools/generate_dnsplugins_snapcraft.sh and should not be edited manually.
name: certbot-dns-rfc2136
summary: RFC 2136 DNS Authenticator plugin for Certbot
description: RFC 2136 DNS Authenticator plugin for Certbot
confinement: strict
grade: stable
base: core20
adopt-info: certbot-dns-rfc2136
parts:
certbot-dns-rfc2136:
plugin: python
source: .
constraints: [$SNAPCRAFT_PART_SRC/snap-constraints.txt]
override-pull: |
snapcraftctl pull
snapcraftctl set-version `grep ^version $SNAPCRAFT_PART_SRC/setup.py | cut -f2 -d= | tr -d "'[:space:]"`
build-environment:
- SNAP_BUILD: "True"
# To build cryptography and cffi if needed
build-packages: [gcc, libffi-dev, libssl-dev, python3-dev]
certbot-metadata:
plugin: dump
source: .
stage: [setup.py, certbot-shared]
override-pull: |
snapcraftctl pull
mkdir -p $SNAPCRAFT_PART_SRC/certbot-shared
slots:
certbot:
interface: content
content: certbot-1
read:
- $SNAP/lib/python3.8/site-packages
plugs:
certbot-metadata:
interface: content
content: metadata-1
target: $SNAP/certbot-shared

View File

@@ -1,21 +0,0 @@
#!/bin/sh -e
# This file is generated by tools/generate_dnsplugins_postrefreshhook.sh and should not be edited manually.
# get certbot version
if [ ! -f "$SNAP/certbot-shared/certbot-version.txt" ]; then
echo "No certbot version available; not doing version comparison check" >> "$SNAP_DATA/debuglog"
exit 0
fi
cb_installed=$(cat $SNAP/certbot-shared/certbot-version.txt)
# get required certbot version for plugin. certbot version must be at least the plugin's
# version. note that this is not the required version in setup.py, but the version number itself.
cb_required=$(grep -oP "version = '\K.*(?=')" $SNAP/setup.py)
$SNAP/bin/python3 -c "import sys; from packaging import version; sys.exit(1) if version.parse('$cb_installed') < version.parse('$cb_required') else sys.exit(0)" || exit_code=$?
if [ "$exit_code" -eq 1 ]; then
echo "Certbot is version $cb_installed but needs to be at least $cb_required before" \
"this plugin can be updated; will try again on next refresh."
exit 1
fi

View File

@@ -1,41 +0,0 @@
# This file is generated by tools/generate_dnsplugins_snapcraft.sh and should not be edited manually.
name: certbot-dns-route53
summary: Route53 DNS Authenticator plugin for Certbot
description: Route53 DNS Authenticator plugin for Certbot
confinement: strict
grade: stable
base: core20
adopt-info: certbot-dns-route53
parts:
certbot-dns-route53:
plugin: python
source: .
constraints: [$SNAPCRAFT_PART_SRC/snap-constraints.txt]
override-pull: |
snapcraftctl pull
snapcraftctl set-version `grep ^version $SNAPCRAFT_PART_SRC/setup.py | cut -f2 -d= | tr -d "'[:space:]"`
build-environment:
- SNAP_BUILD: "True"
# To build cryptography and cffi if needed
build-packages: [gcc, libffi-dev, libssl-dev, python3-dev]
certbot-metadata:
plugin: dump
source: .
stage: [setup.py, certbot-shared]
override-pull: |
snapcraftctl pull
mkdir -p $SNAPCRAFT_PART_SRC/certbot-shared
slots:
certbot:
interface: content
content: certbot-1
read:
- $SNAP/lib/python3.8/site-packages
plugs:
certbot-metadata:
interface: content
content: metadata-1
target: $SNAP/certbot-shared

View File

@@ -1,21 +0,0 @@
#!/bin/sh -e
# This file is generated by tools/generate_dnsplugins_postrefreshhook.sh and should not be edited manually.
# get certbot version
if [ ! -f "$SNAP/certbot-shared/certbot-version.txt" ]; then
echo "No certbot version available; not doing version comparison check" >> "$SNAP_DATA/debuglog"
exit 0
fi
cb_installed=$(cat $SNAP/certbot-shared/certbot-version.txt)
# get required certbot version for plugin. certbot version must be at least the plugin's
# version. note that this is not the required version in setup.py, but the version number itself.
cb_required=$(grep -oP "version = '\K.*(?=')" $SNAP/setup.py)
$SNAP/bin/python3 -c "import sys; from packaging import version; sys.exit(1) if version.parse('$cb_installed') < version.parse('$cb_required') else sys.exit(0)" || exit_code=$?
if [ "$exit_code" -eq 1 ]; then
echo "Certbot is version $cb_installed but needs to be at least $cb_required before" \
"this plugin can be updated; will try again on next refresh."
exit 1
fi

View File

@@ -1,41 +0,0 @@
# This file is generated by tools/generate_dnsplugins_snapcraft.sh and should not be edited manually.
name: certbot-dns-sakuracloud
summary: Sakura Cloud DNS Authenticator plugin for Certbot
description: Sakura Cloud DNS Authenticator plugin for Certbot
confinement: strict
grade: stable
base: core20
adopt-info: certbot-dns-sakuracloud
parts:
certbot-dns-sakuracloud:
plugin: python
source: .
constraints: [$SNAPCRAFT_PART_SRC/snap-constraints.txt]
override-pull: |
snapcraftctl pull
snapcraftctl set-version `grep ^version $SNAPCRAFT_PART_SRC/setup.py | cut -f2 -d= | tr -d "'[:space:]"`
build-environment:
- SNAP_BUILD: "True"
# To build cryptography and cffi if needed
build-packages: [gcc, libffi-dev, libssl-dev, python3-dev]
certbot-metadata:
plugin: dump
source: .
stage: [setup.py, certbot-shared]
override-pull: |
snapcraftctl pull
mkdir -p $SNAPCRAFT_PART_SRC/certbot-shared
slots:
certbot:
interface: content
content: certbot-1
read:
- $SNAP/lib/python3.8/site-packages
plugs:
certbot-metadata:
interface: content
content: metadata-1
target: $SNAP/certbot-shared

View File

@@ -8,11 +8,19 @@ Certbot adheres to [Semantic Versioning](https://semver.org/).
* Added timeout to DNS query function calls for dns-rfc2136 plugin.
* Confirmation when deleting certificates
*
* CLI flag `--key-type` has been added to specify 'rsa' or 'ecdsa' (default 'rsa').
Only accepts a single value at this time.
* CLI flag `--elliptic-curve` has been added which takes an NIST/SECG elliptic curve. Either of
`secp256r1`, `secp284r1` and `secp521r1` are accepted values.
* The command `certbot certficates` lists the which type of the private key that was used
for the private key.
### Changed
* certbot-auto was deprecated on Debian based systems.
* CLI flag `--manual-public-ip-logging-ok` is now a no-op, generates a
deprecation warning, and will be removed in a future release.
*
### Fixed
@@ -52,7 +60,7 @@ More details about these changes can be found on our GitHub repo.
### Added
* Added the ability to remove email and phone contact information from an account
* Added the ability to remove email and phone contact information from an account
using `update_account --register-unsafely-without-email`
### Changed
@@ -64,7 +72,7 @@ More details about these changes can be found on our GitHub repo.
* The problem causing the Apache plugin in the Certbot snap on ARM systems to
fail to load the Augeas library it depends on has been fixed.
* The `acme` library can now tell the ACME server to clear contact information by passing an empty
`tuple` to the `contact` field of a `Registration` message.
`tuple` to the `contact` field of a `Registration` message.
* Fixed the `*** stack smashing detected ***` error in the Certbot snap on some systems.
More details about these changes can be found on our GitHub repo.

View File

@@ -106,6 +106,8 @@ Current Features
* Can get domain-validated (DV) certificates.
* Can revoke certificates.
* Adjustable RSA key bit-length (2048 (default), 4096, ...).
* Adjustable [EC](https://en.wikipedia.org/wiki/Elliptic-curve_cryptography)
key (`secp256r1` (default), `secp384r1`, `secp521r1`).
* Can optionally install a http -> https redirect, so your site effectively
runs https only (Apache only)
* Fully automated.

View File

@@ -11,7 +11,6 @@ import josepy as jose
import pyrfc3339
import pytz
import six
import zope.component
from acme import fields as acme_fields
from acme import messages
@@ -94,21 +93,6 @@ class Account(object):
self.meta == other.meta)
def report_new_account(config):
"""Informs the user about their new ACME account."""
reporter = zope.component.queryUtility(interfaces.IReporter)
if reporter is None:
return
reporter.add_message(
"Your account credentials have been saved in your Certbot "
"configuration directory at {0}. You should make a secure backup "
"of this folder now. This configuration directory will also "
"contain certificates and private keys obtained by Certbot "
"so making regular backups of this folder is ideal.".format(
config.config_dir),
reporter.MEDIUM_PRIORITY)
class AccountMemoryStorage(interfaces.AccountStorage):
"""In-memory account storage."""

View File

@@ -65,6 +65,7 @@ def rename_lineage(config):
disp.notification("Successfully renamed {0} to {1}."
.format(certname, new_certname), pause=False)
def certificates(config):
"""Display information about certs configured with Certbot
@@ -87,6 +88,7 @@ def certificates(config):
# Describe all the certs
_describe_certs(config, parsed_certs, parse_failures)
def delete(config):
"""Delete Certbot files associated with a certificate lineage."""
certnames = get_certnames(config, "delete", allow_multiple=True)
@@ -123,11 +125,13 @@ def lineage_for_certname(cli_config, certname):
logger.debug("Traceback was:\n%s", traceback.format_exc())
return None
def domains_for_certname(config, certname):
"""Find the domains in the cert with name certname."""
lineage = lineage_for_certname(config, certname)
return lineage.names() if lineage else None
def find_duplicative_certs(config, domains):
"""Find existing certs that match the given domain names.
@@ -172,6 +176,7 @@ def find_duplicative_certs(config, domains):
return _search_lineages(config, update_certs_for_domain_matches, (None, None))
def _archive_files(candidate_lineage, filetype):
""" In order to match things like:
/etc/letsencrypt/archive/example.com/chain1.pem.
@@ -193,6 +198,7 @@ def _archive_files(candidate_lineage, filetype):
return pattern
return None
def _acceptable_matches():
""" Generates the list that's passed to match_and_check_overlaps. Is its own function to
make unit testing easier.
@@ -203,6 +209,7 @@ def _acceptable_matches():
return [lambda x: x.fullchain_path, lambda x: x.cert_path,
lambda x: _archive_files(x, "cert"), lambda x: _archive_files(x, "fullchain")]
def cert_path_to_lineage(cli_config):
""" If config.cert_path is defined, try to find an appropriate value for config.certname.
@@ -219,6 +226,7 @@ def cert_path_to_lineage(cli_config):
lambda x: cli_config.cert_path[0], lambda x: x.lineagename)
return match[0]
def match_and_check_overlaps(cli_config, acceptable_matches, match_func, rv_func):
""" Searches through all lineages for a match, and checks for duplicates.
If a duplicate is found, an error is raised, as performing operations on lineages
@@ -284,20 +292,23 @@ def human_readable_cert_info(config, cert, skip_filter_checks=False):
valid_string = "{0} ({1})".format(cert.target_expiry, status)
serial = format(crypto_util.get_serial_from_cert(cert.cert_path), 'x')
certinfo.append(" Certificate Name: {0}\n"
" Serial Number: {1}\n"
" Domains: {2}\n"
" Expiry Date: {3}\n"
" Certificate Path: {4}\n"
" Private Key Path: {5}".format(
certinfo.append(" Certificate Name: {}\n"
" Serial Number: {}\n"
" Key Type: {}\n"
" Domains: {}\n"
" Expiry Date: {}\n"
" Certificate Path: {}\n"
" Private Key Path: {}".format(
cert.lineagename,
serial,
cert.private_key_type,
" ".join(cert.names()),
valid_string,
cert.fullchain,
cert.privkey))
return "".join(certinfo)
def get_certnames(config, verb, allow_multiple=False, custom_prompt=None):
"""Get certname from flag, interactively, or error out.
"""
@@ -337,10 +348,12 @@ def get_certnames(config, verb, allow_multiple=False, custom_prompt=None):
# Private Helpers
###################
def _report_lines(msgs):
"""Format a results report for a category of single-line renewal outcomes"""
return " " + "\n ".join(str(msg) for msg in msgs)
def _report_human_readable(config, parsed_certs):
"""Format a results report for a parsed cert"""
certinfo = []
@@ -348,6 +361,7 @@ def _report_human_readable(config, parsed_certs):
certinfo.append(human_readable_cert_info(config, cert))
return "\n".join(certinfo)
def _describe_certs(config, parsed_certs, parse_failures):
"""Print information about the certs we know about"""
out = [] # type: List[str]
@@ -369,6 +383,7 @@ def _describe_certs(config, parsed_certs, parse_failures):
disp = zope.component.getUtility(interfaces.IDisplay)
disp.notification("\n".join(out), pause=False, wrap=False)
def _search_lineages(cli_config, func, initial_rv, *args):
"""Iterate func over unbroken lineages, allowing custom return conditions.

View File

@@ -51,7 +51,6 @@ from certbot._internal.cli.cli_utils import (
)
# These imports depend on cli_constants and cli_utils.
from certbot._internal.cli.report_config_interaction import report_config_interaction
from certbot._internal.cli.verb_help import VERB_HELP, VERB_HELP_MAP
from certbot._internal.cli.group_adder import _add_all_groups
from certbot._internal.cli.subparsers import _create_subparsers
@@ -314,6 +313,16 @@ def prepare_and_parse_args(plugins, args, detect_defaults=False):
helpful.add(
"security", "--rsa-key-size", type=int, metavar="N",
default=flag_default("rsa_key_size"), help=config_help("rsa_key_size"))
helpful.add(
"security", "--key-type", choices=['rsa', 'ecdsa'], type=str,
default=flag_default("key_type"), help=config_help("key_type"))
helpful.add(
"security", "--elliptic-curve", type=str, choices=[
'secp256r1',
'secp384r1',
'secp521r1',
], metavar="N",
default=flag_default("elliptic_curve"), help=config_help("elliptic_curve"))
helpful.add(
"security", "--must-staple", action="store_true",
dest="must_staple", default=flag_default("must_staple"),

View File

@@ -230,6 +230,10 @@ class HelpfulArgumentParser(object):
raise errors.Error(
"Parameters --hsts and --auto-hsts cannot be used simultaneously.")
if isinstance(parsed_args.key_type, list) and len(parsed_args.key_type) > 1:
raise errors.Error(
"Only *one* --key-type type may be provided at this time.")
return parsed_args
def set_test_server(self, parsed_args):

View File

@@ -1,27 +0,0 @@
"""This is a module that reports config option interaction that should be
checked by set_by_cli"""
import six
from certbot._internal.cli import VAR_MODIFIERS
def report_config_interaction(modified, modifiers):
"""Registers config option interaction to be checked by set_by_cli.
This function can be called by during the __init__ or
add_parser_arguments methods of plugins to register interactions
between config options.
:param modified: config options that can be modified by modifiers
:type modified: iterable or str (string_types)
:param modifiers: config options that modify modified
:type modifiers: iterable or str (string_types)
"""
if isinstance(modified, six.string_types):
modified = (modified,)
if isinstance(modifiers, six.string_types):
modifiers = (modifiers,)
for var in modified:
VAR_MODIFIERS.setdefault(var, set()).update(modifiers)

View File

@@ -158,7 +158,7 @@ def register(config, account_storage, tos_cb=None):
logger.warning(msg)
raise errors.Error(msg)
if not config.dry_run:
logger.info("Registering without email!")
logger.debug("Registering without email!")
# If --dry-run is used, and there is no staging account, create one with no email.
if config.dry_run:
@@ -175,7 +175,6 @@ def register(config, account_storage, tos_cb=None):
regr = perform_registration(acme, config, tos_cb)
acc = account.Account(regr, key)
account.report_new_account(config)
account_storage.save(acc, acme)
eff.prepare_subscription(config, acc)
@@ -313,7 +312,6 @@ class Client(object):
:rtype: tuple
"""
# We need to determine the key path, key PEM data, CSR path,
# and CSR PEM data. For a dry run, the paths are None because
# they aren't permanently saved to disk. For a lineage with
@@ -336,16 +334,41 @@ class Client(object):
# The key is set to None here but will be created below.
key = None
key_size = self.config.rsa_key_size
elliptic_curve = None
# key-type defaults to a list, but we are only handling 1 currently
if isinstance(self.config.key_type, list):
self.config.key_type = self.config.key_type[0]
if self.config.elliptic_curve and self.config.key_type == 'ecdsa':
elliptic_curve = self.config.elliptic_curve
self.config.auth_chain_path = "./chain-ecdsa.pem"
self.config.auth_cert_path = "./cert-ecdsa.pem"
self.config.key_path = "./key-ecdsa.pem"
elif self.config.rsa_key_size and self.config.key_type.lower() == 'rsa':
key_size = self.config.rsa_key_size
# Create CSR from names
if self.config.dry_run:
key = key or util.Key(file=None,
pem=crypto_util.make_key(self.config.rsa_key_size))
key = key or util.Key(
file=None,
pem=crypto_util.make_key(
bits=key_size,
elliptic_curve=elliptic_curve,
key_type=self.config.key_type,
),
)
csr = util.CSR(file=None, form="pem",
data=acme_crypto_util.make_csr(
key.pem, domains, self.config.must_staple))
else:
key = key or crypto_util.init_save_key(self.config.rsa_key_size,
self.config.key_dir)
key = key or crypto_util.init_save_key(
key_size=key_size,
key_dir=self.config.key_dir,
key_type=self.config.key_type,
elliptic_curve=elliptic_curve,
)
csr = crypto_util.init_save_csr(key, domains, self.config.csr_dir)
orderr = self._get_order_and_authorizations(csr.data, self.config.allow_subset_of_names)

View File

@@ -57,6 +57,8 @@ CLI_DEFAULTS = dict(
https_port=443,
break_my_certs=False,
rsa_key_size=2048,
elliptic_curve="secp256r1",
key_type="rsa",
must_staple=False,
redirect=None,
auto_hsts=False,

View File

@@ -11,7 +11,7 @@ import josepy as jose
import zope.component
from acme import errors as acme_errors
from acme.magic_typing import Union, Iterable, Optional # pylint: disable=unused-import
from acme.magic_typing import Union, Iterable, Optional, List, Tuple # pylint: disable=unused-import
import certbot
from certbot import crypto_util
from certbot import errors
@@ -130,7 +130,37 @@ def _get_and_save_cert(le_client, config, domains=None, certname=None, lineage=N
return lineage
def _handle_subset_cert_request(config, domains, cert):
def _handle_unexpected_key_type_migration(config, cert):
# type: (configuration.NamespaceConfig, storage.RenewableCert) -> None
"""
This function ensures that the user will not implicitly migrate an existing key
from one type to another in the situation where a certificate for that lineage
already exist and they have not provided explicitly --key-type and --cert-name.
:param config: Current configuration provided by the client
:param cert: Matching certificate that could be renewed
"""
if (config.verb in ["certonly", "run"]
and (not cli.set_by_cli("key_type") or not cli.set_by_cli("certname"))):
new_key_type = config.key_type.upper()
cur_key_type = cert.private_key_type.upper()
if new_key_type != cur_key_type:
logger.error("A certificate already exists for that name, the list of provided "
"domains or a subset of this list. This certificate uses a key of %s "
"type, and you have not provided the --key-type flag. By default in this "
"case Certbot would generate a new certificate with a key of %s type. "
"Please confirm that you really want to change the type of the key by "
"setting both --cert-name and --key-type CLI flags.",
cur_key_type, new_key_type)
raise errors.Error("Command canceled.")
def _handle_subset_cert_request(config, # type: configuration.NamespaceConfig
domains, # type: List[str]
cert # type: storage.RenewableCert
):
# type: (...) -> Tuple[str, Optional[storage.RenewableCert]]
"""Figure out what to do if a previous cert had a subset of the names now requested
:param config: Configuration object
@@ -147,6 +177,8 @@ def _handle_subset_cert_request(config, domains, cert):
:rtype: `tuple` of `str`
"""
_handle_unexpected_key_type_migration(config, cert)
existing = ", ".join(cert.names())
question = (
"You have an existing certificate that contains a portion of "
@@ -177,7 +209,10 @@ def _handle_subset_cert_request(config, domains, cert):
raise errors.Error(USER_CANCELLED)
def _handle_identical_cert_request(config, lineage):
def _handle_identical_cert_request(config, # type: configuration.NamespaceConfig
lineage, # type: storage.RenewableCert
):
# type: (...) -> Tuple[str, Optional[storage.RenewableCert]]
"""Figure out what to do if a lineage has the same names as a previously obtained one
:param config: Configuration object
@@ -191,6 +226,8 @@ def _handle_identical_cert_request(config, lineage):
:rtype: `tuple` of `str`
"""
_handle_unexpected_key_type_migration(config, lineage)
if not lineage.ensure_deployed():
return "reinstall", lineage
if renewal.should_renew(config, lineage):
@@ -266,6 +303,7 @@ def _find_lineage_for_domains(config, domains):
return _handle_subset_cert_request(config, domains, subset_names_cert)
return None, None
def _find_cert(config, domains, certname):
"""Finds an existing certificate object given domains and/or a certificate name.
@@ -289,7 +327,12 @@ def _find_cert(config, domains, certname):
logger.info("Keeping the existing certificate")
return (action != "reinstall"), lineage
def _find_lineage_for_domains_and_certname(config, domains, certname):
def _find_lineage_for_domains_and_certname(config, # type: configuration.NamespaceConfig
domains, # type: List[str]
certname # type: str
):
# type: (...) -> Tuple[str, Optional[storage.RenewableCert]]
"""Find appropriate lineage based on given domains and/or certname.
:param config: Configuration object
@@ -316,8 +359,9 @@ def _find_lineage_for_domains_and_certname(config, domains, certname):
if lineage:
if domains:
if set(cert_manager.domains_for_certname(config, certname)) != set(domains):
_handle_unexpected_key_type_migration(config, lineage)
_ask_user_to_confirm_new_names(config, domains, certname,
lineage.names()) # raises if no
lineage.names()) # raises if no
return "renew", lineage
# unnecessarily specified domains or no domains specified
return _handle_identical_cert_request(config, lineage)
@@ -386,6 +430,7 @@ def _ask_user_to_confirm_new_names(config, new_domains, certname, old_domains):
if not obj.yesno(msg, "Update cert", "Cancel", default=True):
raise errors.ConfigurationError("Specified mismatched cert name and domains.")
def _find_domains_or_certname(config, installer, question=None):
"""Retrieve domains and certname from config or user input.
@@ -490,11 +535,9 @@ def _determine_account(config):
return True
msg = ("Please read the Terms of Service at {0}. You "
"must agree in order to register with the ACME "
"server at {1}".format(
terms_of_service, config.server))
"server. Do you agree?".format(terms_of_service))
obj = zope.component.getUtility(interfaces.IDisplay)
result = obj.yesno(msg, "Agree", "Cancel",
cli_flag="--agree-tos", force_interactive=True)
result = obj.yesno(msg, cli_flag="--agree-tos", force_interactive=True)
if not result:
raise errors.Error(
"Registration cannot proceed without accepting "
@@ -518,6 +561,7 @@ def _determine_account(config):
try:
acc, acme = client.register(
config, account_storage, tos_cb=_tos_cb)
logger.info("Account registered.")
except errors.MissingCommandlineFlag:
raise
except errors.Error:
@@ -1010,6 +1054,7 @@ def delete(config, unused_plugins):
"""
cert_manager.delete(config)
def certificates(config, unused_plugins):
"""Display information about certs configured with Certbot
@@ -1025,6 +1070,7 @@ def certificates(config, unused_plugins):
"""
cert_manager.certificates(config)
# TODO: coop with renewal config
def revoke(config, unused_plugins):
"""Revoke a previously obtained certificate.
@@ -1110,7 +1156,9 @@ def run(config, plugins):
cert_path = new_lineage.cert_path if new_lineage else None
fullchain_path = new_lineage.fullchain_path if new_lineage else None
key_path = new_lineage.key_path if new_lineage else None
_report_new_cert(config, cert_path, fullchain_path, key_path)
if should_get_cert:
_report_new_cert(config, cert_path, fullchain_path, key_path)
_install_cert(config, le_client, domains, new_lineage)
@@ -1155,6 +1203,7 @@ def _csr_get_and_save_cert(config, le_client):
os.path.normpath(config.chain_path), os.path.normpath(config.fullchain_path))
return cert_path, fullchain_path
def renew_cert(config, plugins, lineage):
"""Renew & save an existing cert. Do not install it.

View File

@@ -84,8 +84,7 @@ permitted by DNS standards.)
help='Path or command to execute for the authentication script')
add('cleanup-hook',
help='Path or command to execute for the cleanup script')
add('public-ip-logging-ok', action='store_true',
help='Automatically allows public IP logging (default: Ask)')
util.add_deprecated_argument(add, 'public-ip-logging-ok', 0)
def prepare(self): # pylint: disable=missing-function-docstring
if self.config.noninteractive_mode and not self.conf('auth-hook'):
@@ -114,8 +113,6 @@ permitted by DNS standards.)
return [challenges.HTTP01, challenges.DNS01]
def perform(self, achalls): # pylint: disable=missing-function-docstring
self._verify_ip_logging_ok()
responses = []
for achall in achalls:
if self.conf('auth-hook'):
@@ -125,20 +122,6 @@ permitted by DNS standards.)
responses.append(achall.response(achall.account_key))
return responses
def _verify_ip_logging_ok(self):
if not self.conf('public-ip-logging-ok'):
cli_flag = '--{0}'.format(self.option_name('public-ip-logging-ok'))
msg = ('NOTE: The IP of this machine will be publicly logged as '
"having requested this certificate. If you're running "
'certbot in manual mode on a machine that is not your '
"server, please ensure you're okay with that.\n\n"
'Are you OK with your IP being logged?')
display = zope.component.getUtility(interfaces.IDisplay)
if display.yesno(msg, cli_flag=cli_flag, force_interactive=True):
setattr(self.config, self.dest('public-ip-logging-ok'), True)
else:
raise errors.PluginError('Must agree to IP logging to proceed')
def _perform_achall_with_script(self, achall, achalls):
env = dict(CERTBOT_DOMAIN=achall.domain,
CERTBOT_VALIDATION=achall.validation(achall.account_key),

View File

@@ -10,7 +10,7 @@ import time
import traceback
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives.asymmetric import ec, rsa
from cryptography.hazmat.primitives.serialization import load_pem_private_key
import OpenSSL
import six
@@ -40,7 +40,7 @@ logger = logging.getLogger(__name__)
STR_CONFIG_ITEMS = ["config_dir", "logs_dir", "work_dir", "user_agent",
"server", "account", "authenticator", "installer",
"renew_hook", "pre_hook", "post_hook", "http01_address",
"preferred_chain"]
"preferred_chain", "key_type", "elliptic_curve"]
INT_CONFIG_ITEMS = ["rsa_key_size", "http01_port"]
BOOL_CONFIG_ITEMS = ["must_staple", "allow_subset_of_names", "reuse_key",
"autorenew"]
@@ -506,6 +506,10 @@ def _update_renewal_params_from_key(key_path, config):
with open(key_path, 'rb') as file_h:
key = load_pem_private_key(file_h.read(), password=None, backend=default_backend())
if isinstance(key, rsa.RSAPrivateKey):
config.key_type = 'rsa'
config.rsa_key_size = key.key_size
elif isinstance(key, ec.EllipticCurvePrivateKey):
config.key_type = 'ecdsa'
config.elliptic_curve = key.curve.name
else:
raise errors.Error('Key at {0} is of an unsupported type: {1}.'.format(key_path, type(key)))

View File

@@ -10,6 +10,9 @@ import configobj
import parsedatetime
import pytz
import six
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.asymmetric.rsa import RSAPrivateKey
from cryptography.hazmat.primitives.serialization import load_pem_private_key
import certbot
from certbot import crypto_util
@@ -46,6 +49,7 @@ def renewal_conf_files(config):
result.sort()
return result
def renewal_file_for_certname(config, certname):
"""Return /path/to/certname.conf in the renewal conf directory"""
path = os.path.join(config.renewal_configs_dir, "{0}.conf".format(certname))
@@ -1055,6 +1059,23 @@ class RenewableCert(interfaces.RenewableCert):
target, values)
return cls(new_config.filename, cli_config)
@property
def private_key_type(self):
"""
:returns: The type of algorithm for the private, RSA or ECDSA
:rtype: str
"""
with open(self.configuration["privkey"], "rb") as priv_key_file:
key = load_pem_private_key(
data=priv_key_file.read(),
password=None,
backend=default_backend()
)
if isinstance(key, RSAPrivateKey):
return "RSA"
else:
return "ECDSA"
def save_successor(self, prior_version, new_cert,
new_privkey, new_chain, cli_config):
"""Save new cert and chain as a successor of a prior version.

View File

@@ -11,14 +11,16 @@ import warnings
import re
# See https://github.com/pyca/cryptography/issues/4275
from cryptography import x509 # type: ignore
from cryptography.exceptions import InvalidSignature
from cryptography.exceptions import InvalidSignature, UnsupportedAlgorithm
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.asymmetric.ec import ECDSA
from cryptography.hazmat.primitives.asymmetric.ec import EllipticCurvePublicKey
from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.hazmat.primitives.asymmetric.ec import ECDSA, EllipticCurvePublicKey
from cryptography.hazmat.primitives.asymmetric.padding import PKCS1v15
from cryptography.hazmat.primitives.asymmetric.rsa import RSAPublicKey
from cryptography.hazmat.primitives.serialization import Encoding, NoEncryption, PrivateFormat
from OpenSSL import crypto
from OpenSSL import SSL # type: ignore
import pyrfc3339
import six
import zope.component
@@ -34,7 +36,9 @@ logger = logging.getLogger(__name__)
# High level functions
def init_save_key(key_size, key_dir, keyname="key-certbot.pem"):
def init_save_key(
key_size, key_dir, key_type="rsa", elliptic_curve="secp256r1", keyname="key-certbot.pem"
):
"""Initializes and saves a privkey.
Inits key and saves it in PEM format on the filesystem.
@@ -42,8 +46,10 @@ def init_save_key(key_size, key_dir, keyname="key-certbot.pem"):
.. note:: keyname is the attempted filename, it may be different if a file
already exists at the path.
:param int key_size: RSA key size in bits
:param int key_size: key size in bits if key size is rsa.
:param str key_dir: Key save directory.
:param str key_type: Key Type [rsa, ecdsa]
:param str elliptic_curve: Name of the elliptic curve if key type is ecdsa.
:param str keyname: Filename of key
:returns: Key
@@ -53,7 +59,9 @@ def init_save_key(key_size, key_dir, keyname="key-certbot.pem"):
"""
try:
key_pem = make_key(key_size)
key_pem = make_key(
bits=key_size, elliptic_curve=elliptic_curve or "secp256r1", key_type=key_type,
)
except ValueError as err:
logger.error("", exc_info=True)
raise err
@@ -65,7 +73,10 @@ def init_save_key(key_size, key_dir, keyname="key-certbot.pem"):
os.path.join(key_dir, keyname), 0o600, "wb")
with key_f:
key_f.write(key_pem)
logger.debug("Generating key (%d bits): %s", key_size, key_path)
if key_type == 'rsa':
logger.debug("Generating RSA key (%d bits): %s", key_size, key_path)
else:
logger.debug("Generating ECDSA key (%d bits): %s", key_size, key_path)
return util.Key(key_path, key_pem)
@@ -174,18 +185,45 @@ def import_csr_file(csrfile, data):
return PEM, util.CSR(file=csrfile, data=data_pem, form="pem"), domains
def make_key(bits):
"""Generate PEM encoded RSA key.
def make_key(bits=1024, key_type="rsa", elliptic_curve=None):
"""Generate PEM encoded RSA|EC key.
:param int bits: Number of bits, at least 1024.
:param int bits: Number of bits if key_type=rsa. At least 1024 for RSA.
:returns: new RSA key in PEM form with specified number of bits
:param str ec_curve: The elliptic curve to use.
:returns: new RSA or ECDSA key in PEM form with specified number of bits
or of type ec_curve when key_type ecdsa is used.
:rtype: str
"""
assert bits >= 1024 # XXX
key = crypto.PKey()
key.generate_key(crypto.TYPE_RSA, bits)
if key_type == 'rsa':
if bits < 1024:
raise errors.Error("Unsupported RSA key length: {}".format(bits))
key = crypto.PKey()
key.generate_key(crypto.TYPE_RSA, bits)
elif key_type == 'ecdsa':
try:
name = elliptic_curve.upper()
if name in ('SECP256R1', 'SECP384R1', 'SECP512R1'):
_key = ec.generate_private_key(
curve=getattr(ec, elliptic_curve.upper(), None)(),
backend=default_backend()
)
else:
raise errors.Error("Unsupported elliptic curve: {}".format(elliptic_curve))
except TypeError:
raise errors.Error("Unsupported elliptic curve: {}".format(elliptic_curve))
except UnsupportedAlgorithm as e:
raise six.raise_from(e, errors.Error(str(e)))
_key_pem = _key.private_bytes(
encoding=Encoding.PEM,
format=PrivateFormat.TraditionalOpenSSL,
encryption_algorithm=NoEncryption()
)
key = crypto.load_privatekey(crypto.FILETYPE_PEM, _key_pem)
else:
raise errors.Error("Invalid key_type specified: {}. Use [rsa|ecdsa]".format(key_type))
return crypto.dump_privatekey(crypto.FILETYPE_PEM, key)

View File

@@ -198,6 +198,13 @@ class IConfig(zope.interface.Interface):
"register multiple emails, ex: u1@example.com,u2@example.com. "
"(default: Ask).")
rsa_key_size = zope.interface.Attribute("Size of the RSA key.")
elliptic_curve = zope.interface.Attribute(
"The SECG elliptic curve name to use. Please see RFC 8446 "
"for supported values."
)
key_type = zope.interface.Attribute(
"Type of generated private key"
"(Only *ONE* per invocation can be provided at this time)")
must_staple = zope.interface.Attribute(
"Adds the OCSP Must Staple extension to the certificate. "
"Autoconfigures OCSP Stapling for supported setups "
@@ -260,6 +267,7 @@ class IConfig(zope.interface.Interface):
"offered chain will be used."
)
class IInstaller(IPlugin):
"""Generic Certbot Installer Interface.

View File

@@ -55,10 +55,6 @@ class Plugin(object):
def add_parser_arguments(cls, add):
"""Add plugin arguments to the CLI argument parser.
NOTE: If some of your flags interact with others, you can
use cli.report_config_interaction to register this to ensure
values are correctly saved/overridable during renewal.
:param callable add: Function that proxies calls to
`argparse.ArgumentParser.add_argument` prepending options
with unique plugin name prefix.

View File

@@ -2,10 +2,16 @@ The following command has been used to generate test keys:
for x in 256 512 2048; do openssl genrsa -out rsa${k}_key.pem $k; done
For the elliptic curve private keys, this command was used:
for k in "prime256v1" "secp384r1" "secp521r1" do
openssl genpkey -algorithm ${k} -out ec_${k}_key.pem
done
and for the CSR PEM (Certificate Signing Request):
openssl req -new -out csr-Xsans_X.pem -key rsa512_key.pem [-config csr-Xsans_X.conf | -subj '/CN=example.com'] [-outform DER > csr_X.der]
and for the certificate:
openssl req -new -out cert_X.pem -key rsaX_key.pem -subj '/CN=example.com' -x509 [-outform DER > cert_X.der]
openssl req -new -out cert_X.pem -key rsaX_key.pem -subj '/CN=example.com' -x509 [-outform DER > cert_X.der]

View File

@@ -0,0 +1,8 @@
-----BEGIN EC PARAMETERS-----
BggqhkjOPQMBBw==
-----END EC PARAMETERS-----
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIDqQPQl69kuh+DrecC8SFPt21f0F/HHDP3T4/Lf0zIVFoAoGCCqGSM49
AwEHoUQDQgAEHou50Ee9u+8Vial6VbUHExlzsiCHtORlW0X0pKo5RspIKB0QyKwo
dUXvBbv95I9yCO5+MlGkKjwLHtIEze0Hww==
-----END EC PRIVATE KEY-----

View File

@@ -0,0 +1,9 @@
-----BEGIN EC PARAMETERS-----
BgUrgQQAIg==
-----END EC PARAMETERS-----
-----BEGIN EC PRIVATE KEY-----
MIGkAgEBBDAgvZGw5C7Mp26N0cXA+vIg5K/J5MJw+MVGYfGF4ZutuCLeYMrWT68R
A0h6hJvDtMSgBwYFK4EEACKhZANiAAR1uQYZeU5Kml5o53Q8/PCdwUbqdgCSkV0C
J5a6bhDRMp20fdp2T/mbkdxuVEl81lqfKPZhsd4CZsLaVIU3RUoGgIT1R3QKawpJ
SuXq37yWFX2hqlgt+lsBufZ8RD5QnZc=
-----END EC PRIVATE KEY-----

View File

@@ -0,0 +1,10 @@
-----BEGIN EC PARAMETERS-----
BgUrgQQAIw==
-----END EC PARAMETERS-----
-----BEGIN EC PRIVATE KEY-----
MIHcAgEBBEIACWWVKm1qAIejZ6qmqk9D69wQW5FAe3Er0IxWAMkonTEhu8EH5Q2i
2vT2bESm730zhGTe2Pn11b85H6UI9hxhCHygBwYFK4EEACOhgYkDgYYABAEQi1WF
m3suHjPyWACyOJYGUn1Kx6rfBo0PjC7X2TU9jr8umLkIpaaF5UsBuMBmdz1IHL0U
k0gQtoOQ0Qu8N74GuAGzGR0S3RYIv6gfYVz3dS1K4n4b307Lx62bnvtlNxcIvt3w
hmS5OdvQ1Kdxh6oqbSVhhbQmJcgab78Txx3R2QeCxw==
-----END EC PRIVATE KEY-----

View File

@@ -0,0 +1,18 @@
-----BEGIN CERTIFICATE-----
MIIC2zCCAcOgAwIBAgIIBvrEnbPRYu8wDQYJKoZIhvcNAQELBQAwKDEmMCQGA1UE
AxMdUGViYmxlIEludGVybWVkaWF0ZSBDQSAxMjZjNGIwHhcNMjAxMDEyMjEwNzQw
WhcNMjUxMDEyMjEwNzQwWjAjMSEwHwYDVQQDExhjLmVuY3J5cHRpb24tZXhhbXBs
ZS5jb20wWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAARjMhuW0ENPPC33PjB5XsYU
CRw640kPQENIDatcTJaENZIZdqKd6rI6jc+lpbmXot7Zi52clJlSJS+V6oDAt2Lh
o4HYMIHVMA4GA1UdDwEB/wQEAwIFoDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYB
BQUHAwIwDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUj7Kd3ENqxlPf8B2bIGhsjydX
mPswHwYDVR0jBBgwFoAUEiGxlkRsi+VvcogH5dVD3h1laAcwMQYIKwYBBQUHAQEE
JTAjMCEGCCsGAQUFBzABhhVodHRwOi8vMTI3LjAuMC4xOjQwMDIwIwYDVR0RBBww
GoIYYy5lbmNyeXB0aW9uLWV4YW1wbGUuY29tMA0GCSqGSIb3DQEBCwUAA4IBAQCl
k0JXsa8y7fg41WWMDhw60bPW77O0FtOmTcnhdI5daYNemQVk+Q5EMaBLQ/oGjgXd
9QXFzXH1PL904YEnSLt+iTpXn++7rQSNzQsdYqw0neWk4f5pEBiN+WORpb6mwobV
ifMtBOkNEHvrJ2Pkci9U1lLwtKD/DSew6QtJU5DSkmH1XdGuMJiubygEIvELtvgq
cP9S368ZvPmPGmKaJQXBiuaR8MTjY/Bkr79aXQMjKbf+mpn7h0POCcePk1DY/rm6
Da+X16lf0hHyQhSUa7Vgyim6rK1/hlw+Z00i+sQCKD9Ih7kXuuGqfSDC33cfO8Tj
o/MXO8lcxkrem5zU5QWP
-----END CERTIFICATE-----

View File

@@ -0,0 +1,20 @@
-----BEGIN CERTIFICATE-----
MIIDUDCCAjigAwIBAgIIbi787yVrcMAwDQYJKoZIhvcNAQELBQAwIDEeMBwGA1UE
AxMVUGViYmxlIFJvb3QgQ0EgMGM1MjI1MCAXDTIwMTAxMjIwMjI0NloYDzIwNTAx
MDEyMjEyMjQ2WjAoMSYwJAYDVQQDEx1QZWJibGUgSW50ZXJtZWRpYXRlIENBIDEy
NmM0YjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALGeVk1BMJraeqRq
mJ2+hgso8VOAv2s2CVxUJjIVcn7f2adE8NyTsSQ1brlsnKCUYUw7yLTQH0izLQRB
qKVIDFkUqo5/FuTJ2QlfA2EwBL8J7s/7L7vj3L0DiVpwgxPSyFEwdl/Y5y7ofsX5
CIhCFcaMAmTIuKLiSfCJjGwkbEMuolm+lO8Mikxxc/JtDVUC479ugU7PU9O09bMH
nm+sD6Bgd+KMoPkCCCoeShJS9X3Ziq9HGc7Z6nhM/zirFARt2XkonEdAZ8br01zY
MRiY9txhlWQ7mUkOtzOSoEuYJNoUbvMUf0+tNzto26WRyF7dJmh7lTBsYrvAwUTx
PzNyst0CAwEAAaOBgzCBgDAOBgNVHQ8BAf8EBAMCAoQwHQYDVR0lBBYwFAYIKwYB
BQUHAwEGCCsGAQUFBwMCMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFBIhsZZE
bIvlb3KIB+XVQ94dZWgHMB8GA1UdIwQYMBaAFOaKTaXg37vKgRt7d79YOjAoAtJT
MA0GCSqGSIb3DQEBCwUAA4IBAQAU2mZii7PH2pkw2lNM0QqPbcW/UYyvFoUeM8Aq
uCtsI2s+oxCJTqzfLsA0N8NY4nHLQ5wAlNJfJekngni8hbmJTKU4JFTMe7kLQO8P
fJbk0pTzhhHVQw7CVwB6Pwq3u2m/JV+d6xDIDc+AVkuEl19ZJU0rTWyooClfFLZV
EdZmEiUtA3PGlxoYwYhoGHYlhFxsoFONhCsBEdN7k7FKtFGVxN7oc5SKmKp0YZTW
fcrEtrdNThATO4ymhCC2zh33NI/MT1O74fpaAc2k6LcTl57MKiLfTYX4LTL6v9JG
9tlNqjFVRRmzEbtXTPcCb+w9g1VqoOGok7mGXYLTYtShCuvE
-----END CERTIFICATE-----

View File

@@ -0,0 +1,38 @@
-----BEGIN CERTIFICATE-----
MIIC2zCCAcOgAwIBAgIILlmGtZhUFEwwDQYJKoZIhvcNAQELBQAwKDEmMCQGA1UE
AxMdUGViYmxlIEludGVybWVkaWF0ZSBDQSAxMjZjNGIwHhcNMjAxMDEyMjA1MDM0
WhcNMjUxMDEyMjA1MDM0WjAjMSEwHwYDVQQDExhjLmVuY3J5cHRpb24tZXhhbXBs
ZS5jb20wWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAARHEzR8JPWrEmpmgM+F2bk5
9mT0u6CjzmJG0QpbaqprLiG5NGpW84VQ5TFCrmC4KxYfigCfMhfHRNfFYvNUK3V/
o4HYMIHVMA4GA1UdDwEB/wQEAwIFoDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYB
BQUHAwIwDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQU1CsVL+bPnzaxxQ5jUENmQJIO
lKwwHwYDVR0jBBgwFoAUEiGxlkRsi+VvcogH5dVD3h1laAcwMQYIKwYBBQUHAQEE
JTAjMCEGCCsGAQUFBzABhhVodHRwOi8vMTI3LjAuMC4xOjQwMDIwIwYDVR0RBBww
GoIYYy5lbmNyeXB0aW9uLWV4YW1wbGUuY29tMA0GCSqGSIb3DQEBCwUAA4IBAQBn
2D8loC7pfk28JYpFLr5lmFKJWWmtLGlpsWDj61fVjtTfGKLziJz+MM6il4Y3hIz5
58qiFK0ue0M63dIBJ33N+XxSEXon4Q0gy/zRWfH9jtPJ3FwfjkU/RT9PAUClYi0G
ptNWnTmgQkNzousbcAtRNXuuShH3856vhUnwkX+xM+cbIDi1JVmFjcGrEEQJ0rUF
mv2ZTyfbWbUs3v4rReETi2NVzr1Ql6J+ByNcMvHODzFy3t0L6yelAw2ca1I+c9HU
+Z0tnp/ykR7eXNuVLivok8UBf5OC413lh8ZO5g+Bgzh/LdtkUuavg1MYtEX0H6mX
9U7y3nVI8WEbPGf+HDeu
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDUDCCAjigAwIBAgIIbi787yVrcMAwDQYJKoZIhvcNAQELBQAwIDEeMBwGA1UE
AxMVUGViYmxlIFJvb3QgQ0EgMGM1MjI1MCAXDTIwMTAxMjIwMjI0NloYDzIwNTAx
MDEyMjEyMjQ2WjAoMSYwJAYDVQQDEx1QZWJibGUgSW50ZXJtZWRpYXRlIENBIDEy
NmM0YjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALGeVk1BMJraeqRq
mJ2+hgso8VOAv2s2CVxUJjIVcn7f2adE8NyTsSQ1brlsnKCUYUw7yLTQH0izLQRB
qKVIDFkUqo5/FuTJ2QlfA2EwBL8J7s/7L7vj3L0DiVpwgxPSyFEwdl/Y5y7ofsX5
CIhCFcaMAmTIuKLiSfCJjGwkbEMuolm+lO8Mikxxc/JtDVUC479ugU7PU9O09bMH
nm+sD6Bgd+KMoPkCCCoeShJS9X3Ziq9HGc7Z6nhM/zirFARt2XkonEdAZ8br01zY
MRiY9txhlWQ7mUkOtzOSoEuYJNoUbvMUf0+tNzto26WRyF7dJmh7lTBsYrvAwUTx
PzNyst0CAwEAAaOBgzCBgDAOBgNVHQ8BAf8EBAMCAoQwHQYDVR0lBBYwFAYIKwYB
BQUHAwEGCCsGAQUFBwMCMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFBIhsZZE
bIvlb3KIB+XVQ94dZWgHMB8GA1UdIwQYMBaAFOaKTaXg37vKgRt7d79YOjAoAtJT
MA0GCSqGSIb3DQEBCwUAA4IBAQAU2mZii7PH2pkw2lNM0QqPbcW/UYyvFoUeM8Aq
uCtsI2s+oxCJTqzfLsA0N8NY4nHLQ5wAlNJfJekngni8hbmJTKU4JFTMe7kLQO8P
fJbk0pTzhhHVQw7CVwB6Pwq3u2m/JV+d6xDIDc+AVkuEl19ZJU0rTWyooClfFLZV
EdZmEiUtA3PGlxoYwYhoGHYlhFxsoFONhCsBEdN7k7FKtFGVxN7oc5SKmKp0YZTW
fcrEtrdNThATO4ymhCC2zh33NI/MT1O74fpaAc2k6LcTl57MKiLfTYX4LTL6v9JG
9tlNqjFVRRmzEbtXTPcCb+w9g1VqoOGok7mGXYLTYtShCuvE
-----END CERTIFICATE-----

View File

@@ -0,0 +1,5 @@
-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgNgefv2dad4U1VYEi
0WkdHuqywi5QXAe30OwNTTGjhbihRANCAARHEzR8JPWrEmpmgM+F2bk59mT0u6Cj
zmJG0QpbaqprLiG5NGpW84VQ5TFCrmC4KxYfigCfMhfHRNfFYvNUK3V/
-----END PRIVATE KEY-----

View File

@@ -0,0 +1,79 @@
# add some stuff here
# assets/integration_tests
cert = MAGICDIR/live/sample-renewal-ec/cert.pem
privkey = MAGICDIR/live/sample-renewal-ec/privkey.pem
chain = MAGICDIR/live/sample-renewal-ec/chain.pem
fullchain = MAGICDIR/live/sample-renewal-ec/fullchain.pem
renew_before_expiry = 4 years
# Options and defaults used in the renewal process
[renewalparams]
no_self_upgrade = False
apache_enmod = a2enmod
no_verify_ssl = False
ifaces = None
apache_dismod = a2dismod
register_unsafely_without_email = False
apache_handle_modules = True
uir = None
installer = None
nginx_ctl = nginx
config_dir = MAGICDIR
text_mode = False
func = <function obtain_cert at 0x7f093a163c08>
staging = True
prepare = False
work_dir = /var/lib/letsencrypt
tos = False
init = False
http01_port = 80
duplicate = False
noninteractive_mode = True
key_path = None
nginx = False
nginx_server_root = /etc/nginx
fullchain_path = /home/ubuntu/letsencrypt/chain.pem
email = None
csr = None
agree_dev_preview = None
redirect = None
verb = certonly
verbose_count = -3
config_file = None
renew_by_default = False
hsts = False
apache_handle_sites = True
authenticator = standalone
domains = isnot.org,
key_type = ecdsa
elliptic_curve = secp256r1
apache_challenge_location = /etc/apache2
checkpoints = 1
manual_test_mode = False
apache = False
cert_path = /home/ubuntu/letsencrypt/cert.pem
webroot_path = None
reinstall = False
expand = False
strict_permissions = False
apache_server_root = /etc/apache2
account = None
dry_run = False
manual_public_ip_logging_ok = False
chain_path = /home/ubuntu/letsencrypt/chain.pem
break_my_certs = False
standalone = True
manual = False
server = https://acme-staging-v02.api.letsencrypt.org/directory
webroot = False
os_packages_only = False
apache_init_script = None
user_agent = None
apache_le_vhost_ext = -le-ssl.conf
debug = False
logs_dir = /var/log/letsencrypt
apache_vhost_root = /etc/apache2/sites-available
configurator = None
must_staple = True
[[webroot_map]]

View File

@@ -44,6 +44,7 @@ apache_handle_sites = True
authenticator = standalone
domains = isnot.org,
rsa_key_size = 2048
elliptic_curve = secp256r1
apache_challenge_location = /etc/apache2
checkpoints = 1
manual_test_mode = False

View File

@@ -93,7 +93,7 @@ def load_pyopenssl_private_key(*names):
return OpenSSL.crypto.load_privatekey(loader, load_vector(*names))
def make_lineage(config_dir, testfile):
def make_lineage(config_dir, testfile, ec=False):
"""Creates a lineage defined by testfile.
This creates the archive, live, and renewal directories if
@@ -119,7 +119,7 @@ def make_lineage(config_dir, testfile):
if not os.path.exists(directory):
filesystem.makedirs(directory)
sample_archive = vector_path('sample-archive')
sample_archive = vector_path('sample-archive{}'.format('-ec' if ec else ''))
for kind in os.listdir(sample_archive):
shutil.copyfile(os.path.join(sample_archive, kind),
os.path.join(archive_dir, kind))

View File

@@ -80,8 +80,8 @@ its preferences in accordance with its own policy or its administrators'
preferences, and use different cryptographic mechanisms or parameters,
or a different priority order, than the defaults provided by Certbot.
If you don't use Certbot to configure your server directly, because the
client doesn't integrate with your server software or because you chose
If you don't use Certbot to configure your server directly, because the
client doesn't integrate with your server software or because you chose
not to use this integration, then the cryptographic defaults haven't been
modified, and the cryptography chosen by the server will still be whatever
the default for your software was. For example, if you obtain a
@@ -254,7 +254,7 @@ https://docs.aws.amazon.com/ElasticLoadBalancing/latest/DeveloperGuide/elb-secur
U.S. Government 18F
~~~~~~~~~~~~~~~~~~~
The 18F site (https://18f.gsa.gov/) is using
The 18F site (https://18f.gsa.gov/) is using
::

View File

@@ -1,4 +1,4 @@
usage:
usage:
certbot [SUBCOMMAND] [options] [-d DOMAIN] [-d DOMAIN] ...
Certbot can obtain and install HTTPS/TLS/SSL certificates. By default,
@@ -188,6 +188,12 @@ security:
Security parameters & server settings
--rsa-key-size N Size of the RSA key. (default: 2048)
--key-type type The type of algorithm to use for the the private key.
Either ``rsa`` or ``ecdsa``. (default: ``rsa``).
--elliptic-curve The elliptic curve to use when choosing ``ecdsa`` as the key
type. Accepted values are SECG curve names as defined by
the cryptography library. ``secp256r1``, ``secp384r1``,
``secp521r1``. (default: secp256r1).
--must-staple Adds the OCSP Must Staple extension to the
certificate. Autoconfigures OCSP Stapling for
supported setups (Apache version >= 2.3.3 ). (default:

View File

@@ -375,6 +375,9 @@ The script used to generate the snapcraft.yaml files for our own externally
snapped plugins can be found at
https://github.com/certbot/certbot/blob/master/tools/snap/generate_dnsplugins_snapcraft.sh.
For more information on building externally snapped plugins, see the section on
:ref:`Building snaps`.
Once you have created your own snap, if you have the snap file locally,
it can be installed for use with Certbot by running:
@@ -534,6 +537,15 @@ Use of EFFOSCCP is subject to the `EFF Code of Conduct
<https://www.eff.org/pages/eppcode>`_. When investigating an alleged Code of
Conduct violation, EFF may review discussion channels or direct messages.
.. _Building snaps:
Building the Certbot and DNS plugin snaps
=========================================
Instructions for how to manually build and run the Certbot snap and the externally
snapped DNS plugins that the Certbot project supplies are located in the README
file at https://github.com/certbot/certbot/tree/master/tools/snap.
Updating certbot-auto and letsencrypt-auto
==========================================

View File

@@ -191,6 +191,7 @@ Once installed, you can find documentation on how to use each plugin at:
* `certbot-dns-digitalocean <https://certbot-dns-digitalocean.readthedocs.io>`_
* `certbot-dns-dnsimple <https://certbot-dns-dnsimple.readthedocs.io>`_
* `certbot-dns-dnsmadeeasy <https://certbot-dns-dnsmadeeasy.readthedocs.io>`_
* `certbot-dns-gehirn <https://certbot-dns-gehirn.readthedocs.io>`_
* `certbot-dns-google <https://certbot-dns-google.readthedocs.io>`_
* `certbot-dns-linode <https://certbot-dns-linode.readthedocs.io>`_
* `certbot-dns-luadns <https://certbot-dns-luadns.readthedocs.io>`_
@@ -198,6 +199,7 @@ Once installed, you can find documentation on how to use each plugin at:
* `certbot-dns-ovh <https://certbot-dns-ovh.readthedocs.io>`_
* `certbot-dns-rfc2136 <https://certbot-dns-rfc2136.readthedocs.io>`_
* `certbot-dns-route53 <https://certbot-dns-route53.readthedocs.io>`_
* `certbot-dns-sakuracloud <https://certbot-dns-sakuracloud.readthedocs.io>`_
Manual
------
@@ -317,6 +319,7 @@ This returns information in the following format::
Domains: example.com, www.example.com
Expiry Date: 2017-02-19 19:53:00+00:00 (VALID: 30 days)
Certificate Path: /etc/letsencrypt/live/example.com/fullchain.pem
Key Type: RSA
Private Key Path: /etc/letsencrypt/live/example.com/privkey.pem
``Certificate Name`` shows the name of the certificate. Pass this name
@@ -344,7 +347,6 @@ control Certbot's behavior when re-creating
a certificate with the same name as an existing certificate.
If you don't specify a requested behavior, Certbot may ask you what you intended.
``--force-renewal`` tells Certbot to request a new certificate
with the same domains as an existing certificate. Each domain
must be explicitly specified via ``-d``. If successful, this certificate
@@ -378,7 +380,6 @@ If you prefer, you can specify the domains individually like this:
Consider using ``--cert-name`` instead of ``--expand``, as it gives more control
over which certificate is modified and it lets you remove domains as well as adding them.
``--allow-subset-of-names`` tells Certbot to continue with certificate generation if
only some of the specified domain authorizations can be obtained. This may
be useful if some domains specified in a certificate no longer point at this
@@ -409,6 +410,19 @@ replace that set entirely::
certbot certonly --cert-name example.com -d example.org,www.example.org
Migrating to certificates based on ECDSA keys
---------------------------------------------
As of version 1.10, Certbot supports two types of private key algorithms:
``rsa`` and ``ecdsa``. You may freely upgrade an existing certificate with a
new private key. This requires issuing a new command, or changing the renewal
file for the certificates so it will happen on the next renewal. The two
options that you need for the renewal command are ``--key-type`` and
``--elliptic-curve <name>`` in case you either want to be explicit or want to
use something else than the default curve ``secp256r1``::
certbot renew --key-type ecdsa --cert-name example.com -d example.org,www.example.org
Revoking certificates
---------------------

View File

@@ -7,6 +7,10 @@
# certificate on a system with several certificates should not be placed
# here.
# Use ECC for the private key
key-type = ecdsa
elliptic-curve = secp384r1
# Use a 4096 bit RSA key instead of 2048
rsa-key-size = 4096

View File

@@ -83,24 +83,6 @@ class MetaTest(unittest.TestCase):
self.assertIsNotNone(meta.creation_host)
self.assertIsNotNone(meta.register_to_eff)
class ReportNewAccountTest(test_util.ConfigTestCase):
"""Tests for certbot._internal.account.report_new_account."""
def _call(self):
from certbot._internal.account import report_new_account
report_new_account(self.config)
@mock.patch("certbot._internal.account.zope.component.queryUtility")
def test_no_reporter(self, mock_zope):
mock_zope.return_value = None
self._call()
@mock.patch("certbot._internal.account.zope.component.queryUtility")
def test_it(self, mock_zope):
self._call()
call_list = mock_zope().add_message.call_args_list
self.assertTrue(self.config.config_dir in call_list[0][0][0])
class AccountMemoryStorageTest(unittest.TestCase):
"""Tests for certbot._internal.account.AccountMemoryStorage."""

View File

@@ -359,6 +359,21 @@ class ParseTest(unittest.TestCase):
self.assertFalse(cli.option_was_set(
'authenticator', cli.flag_default('authenticator')))
def test_ecdsa_key_option(self):
elliptic_curve_option = 'elliptic_curve'
elliptic_curve_option_value = cli.flag_default(elliptic_curve_option)
self.parse('--elliptic-curve {0}'.format(elliptic_curve_option_value).split())
self.assertIs(cli.option_was_set(elliptic_curve_option, elliptic_curve_option_value), True)
def test_invalid_key_type(self):
key_type_option = 'key_type'
key_type_value = cli.flag_default(key_type_option)
self.parse('--key-type {0}'.format(key_type_value).split())
self.assertIs(cli.option_was_set(key_type_option, key_type_value), True)
with self.assertRaises(SystemExit):
self.parse("--key-type foo")
def test_encode_revocation_reason(self):
for reason, code in constants.REVOCATION_REASONS.items():
namespace = self.parse(['--reason', reason])
@@ -505,43 +520,6 @@ class SetByCliTest(unittest.TestCase):
verb = 'renew'
self.assertTrue(_call_set_by_cli('webroot_map', args, verb))
def test_report_config_interaction_str(self):
cli.report_config_interaction('manual_public_ip_logging_ok',
'manual_auth_hook')
cli.report_config_interaction('manual_auth_hook', 'manual')
self._test_report_config_interaction_common()
def test_report_config_interaction_iterable(self):
cli.report_config_interaction(('manual_public_ip_logging_ok',),
('manual_auth_hook',))
cli.report_config_interaction(('manual_auth_hook',), ('manual',))
self._test_report_config_interaction_common()
def _test_report_config_interaction_common(self):
"""Tests implied interaction between manual flags.
--manual implies --manual-auth-hook which implies
--manual-public-ip-logging-ok. These interactions don't actually
exist in the client, but are used here for testing purposes.
"""
args = ['--manual']
verb = 'renew'
for v in ('manual', 'manual_auth_hook', 'manual_public_ip_logging_ok'):
self.assertTrue(_call_set_by_cli(v, args, verb))
# https://github.com/python/mypy/issues/2087
cli.set_by_cli.detector = None # type: ignore
args = ['--manual-auth-hook', 'command']
for v in ('manual_auth_hook', 'manual_public_ip_logging_ok'):
self.assertTrue(_call_set_by_cli(v, args, verb))
self.assertFalse(_call_set_by_cli('manual', args, verb))
def _call_set_by_cli(var, args, verb):
with mock.patch('certbot._internal.cli.helpful_parser') as mock_parser:

View File

@@ -93,25 +93,22 @@ class RegisterTest(test_util.ConfigTestCase):
mock_client.new_account_and_tos().terms_of_service = "http://tos"
mock_client().external_account_required.side_effect = self._false_mock
with mock.patch("certbot._internal.eff.prepare_subscription") as mock_prepare:
with mock.patch("certbot._internal.account.report_new_account"):
mock_client().new_account_and_tos.side_effect = errors.Error
self.assertRaises(errors.Error, self._call)
self.assertFalse(mock_prepare.called)
mock_client().new_account_and_tos.side_effect = errors.Error
self.assertRaises(errors.Error, self._call)
self.assertFalse(mock_prepare.called)
mock_client().new_account_and_tos.side_effect = None
self._call()
self.assertTrue(mock_prepare.called)
mock_client().new_account_and_tos.side_effect = None
self._call()
self.assertTrue(mock_prepare.called)
def test_it(self):
with mock.patch("certbot._internal.client.acme_client.BackwardsCompatibleClientV2") as mock_client:
mock_client().external_account_required.side_effect = self._false_mock
with mock.patch("certbot._internal.account.report_new_account"):
with mock.patch("certbot._internal.eff.handle_subscription"):
self._call()
with mock.patch("certbot._internal.eff.handle_subscription"):
self._call()
@mock.patch("certbot._internal.account.report_new_account")
@mock.patch("certbot._internal.client.display_ops.get_email")
def test_email_retry(self, _rep, mock_get_email):
def test_email_retry(self, mock_get_email):
from acme import messages
self.config.noninteractive_mode = False
msg = "DNS problem: NXDOMAIN looking up MX for example.com"
@@ -124,8 +121,7 @@ class RegisterTest(test_util.ConfigTestCase):
self.assertEqual(mock_get_email.call_count, 1)
self.assertTrue(mock_prepare.called)
@mock.patch("certbot._internal.account.report_new_account")
def test_email_invalid_noninteractive(self, _rep):
def test_email_invalid_noninteractive(self):
from acme import messages
self.config.noninteractive_mode = True
msg = "DNS problem: NXDOMAIN looking up MX for example.com"
@@ -145,28 +141,25 @@ class RegisterTest(test_util.ConfigTestCase):
with mock.patch("certbot._internal.eff.prepare_subscription") as mock_prepare:
with mock.patch("certbot._internal.client.acme_client.BackwardsCompatibleClientV2") as mock_clnt:
mock_clnt().external_account_required.side_effect = self._false_mock
with mock.patch("certbot._internal.account.report_new_account"):
self.config.email = None
self.config.register_unsafely_without_email = True
self.config.dry_run = False
self._call()
mock_logger.info.assert_called_once_with(mock.ANY)
self.assertTrue(mock_prepare.called)
self.config.email = None
self.config.register_unsafely_without_email = True
self.config.dry_run = False
self._call()
mock_logger.debug.assert_called_once_with(mock.ANY)
self.assertTrue(mock_prepare.called)
@mock.patch("certbot._internal.account.report_new_account")
@mock.patch("certbot._internal.client.display_ops.get_email")
def test_dry_run_no_staging_account(self, _rep, mock_get_email):
def test_dry_run_no_staging_account(self, mock_get_email):
"""Tests dry-run for no staging account, expect account created with no email"""
with mock.patch("certbot._internal.client.acme_client.BackwardsCompatibleClientV2") as mock_client:
mock_client().external_account_required.side_effect = self._false_mock
with mock.patch("certbot._internal.eff.handle_subscription"):
with mock.patch("certbot._internal.account.report_new_account"):
self.config.dry_run = True
self._call()
# check Certbot did not ask the user to provide an email
self.assertFalse(mock_get_email.called)
# check Certbot created an account with no email. Contact should return empty
self.assertFalse(mock_client().new_account_and_tos.call_args[0][0].contact)
self.config.dry_run = True
self._call()
# check Certbot did not ask the user to provide an email
self.assertFalse(mock_get_email.called)
# check Certbot created an account with no email. Contact should return empty
self.assertFalse(mock_client().new_account_and_tos.call_args[0][0].contact)
def test_with_eab_arguments(self):
with mock.patch("certbot._internal.client.acme_client.BackwardsCompatibleClientV2") as mock_client:
@@ -336,7 +329,11 @@ class ClientTest(ClientTestCommon):
self._test_obtain_certificate_common(mock.sentinel.key, csr)
mock_crypto_util.init_save_key.assert_called_once_with(
self.config.rsa_key_size, self.config.key_dir)
key_size=self.config.rsa_key_size,
key_dir=self.config.key_dir,
key_type=self.config.key_type,
elliptic_curve=None, # elliptic curve is not set
)
mock_crypto_util.init_save_csr.assert_called_once_with(
mock.sentinel.key, self.eg_domains, self.config.csr_dir)
mock_crypto_util.cert_and_chain_from_fullchain.assert_called_once_with(
@@ -372,7 +369,11 @@ class ClientTest(ClientTestCommon):
self.client.config.dry_run = True
self._test_obtain_certificate_common(key, csr)
mock_crypto.make_key.assert_called_once_with(self.config.rsa_key_size)
mock_crypto.make_key.assert_called_once_with(
bits=self.config.rsa_key_size,
elliptic_curve=None, # not making an elliptic private key
key_type=self.config.key_type,
)
mock_acme_crypto.make_csr.assert_called_once_with(
mock.sentinel.key_pem, self.eg_domains, self.config.must_staple)
mock_crypto.init_save_key.assert_not_called()

View File

@@ -4,7 +4,7 @@ import unittest
try:
import mock
except ImportError: # pragma: no cover
except ImportError: # pragma: no cover
from unittest import mock
import OpenSSL
import zope.component
@@ -32,6 +32,7 @@ CERT_LEAF = test_util.load_vector('cert_leaf.pem')
CERT_ISSUER = test_util.load_vector('cert_intermediate_1.pem')
CERT_ALT_ISSUER = test_util.load_vector('cert_intermediate_2.pem')
class InitSaveKeyTest(test_util.TempDirTestCase):
"""Tests for certbot.crypto_util.init_save_key."""
def setUp(self):
@@ -174,11 +175,54 @@ class ImportCSRFileTest(unittest.TestCase):
class MakeKeyTest(unittest.TestCase):
"""Tests for certbot.crypto_util.make_key."""
def test_it(self): # pylint: disable=no-self-use
def test_rsa(self): # pylint: disable=no-self-use
# RSA Key Type Test
from certbot.crypto_util import make_key
# Do not test larger keys as it takes too long.
OpenSSL.crypto.load_privatekey(OpenSSL.crypto.FILETYPE_PEM, make_key(1024))
def test_ec(self): # pylint: disable=no-self-use
# ECDSA Key Type Tests
from certbot.crypto_util import make_key
# Do not test larger keys as it takes too long.
# Try a good key size for ECDSA
OpenSSL.crypto.load_privatekey(
OpenSSL.crypto.FILETYPE_PEM, make_key(1024))
OpenSSL.crypto.FILETYPE_PEM, make_key(elliptic_curve="secp256r1", key_type='ecdsa'))
def test_bad_key_sizes(self):
from certbot.crypto_util import make_key
# Try a bad key size for RSA and ECDSA
with self.assertRaises(errors.Error) as e:
make_key(bits=512, key_type='rsa')
self.assertEqual(
"Unsupported RSA key length: 512",
str(e.exception),
"Unsupported RSA key length: 512"
)
def test_bad_elliptic_curve_name(self):
from certbot.crypto_util import make_key
with self.assertRaises(errors.Error) as e:
make_key(elliptic_curve="nothere", key_type='ecdsa')
self.assertEqual(
"Unsupported elliptic curve: nothere",
str(e.exception),
"Unsupported elliptic curve: nothere"
)
def test_bad_key_type(self):
from certbot.crypto_util import make_key
# Try a bad --key-type
with self.assertRaises(errors.Error) as e:
OpenSSL.crypto.load_privatekey(
OpenSSL.crypto.FILETYPE_PEM, make_key(1024, key_type='unf'))
self.assertEqual(
"Invalid key_type specified: unf. Use [rsa|ecdsa]",
str(e.exception),
"Invalid key_type specified: unf. Use [rsa|ecdsa]",
)
class VerifyCertSetup(unittest.TestCase):

View File

@@ -49,14 +49,47 @@ RSA2048_KEY_PATH = test_util.vector_path('rsa2048_key.pem')
SS_CERT_PATH = test_util.vector_path('cert_2048.pem')
class TestHandleIdenticalCerts(unittest.TestCase):
"""Test for certbot._internal.main._handle_identical_cert_request"""
def test_handle_identical_cert_request_pending(self):
class TestHandleCerts(unittest.TestCase):
"""Test for certbot._internal.main._handle_* methods"""
@mock.patch("certbot._internal.main._handle_unexpected_key_type_migration")
def test_handle_identical_cert_request_pending(self, mock_handle_migration):
mock_lineage = mock.Mock()
mock_lineage.ensure_deployed.return_value = False
# pylint: disable=protected-access
ret = main._handle_identical_cert_request(mock.Mock(), mock_lineage)
self.assertEqual(ret, ("reinstall", mock_lineage))
self.assertTrue(mock_handle_migration.called)
@mock.patch("certbot._internal.main._handle_unexpected_key_type_migration")
def test_handle_subset_cert_request(self, mock_handle_migration):
mock_config = mock.Mock()
mock_config.expand = True
mock_lineage = mock.Mock()
mock_lineage.names.return_value = ["dummy1", "dummy2"]
ret = main._handle_subset_cert_request(mock_config, ["dummy1"], mock_lineage)
self.assertEqual(ret, ("renew", mock_lineage))
self.assertTrue(mock_handle_migration.called)
@mock.patch("certbot._internal.main.cli.set_by_cli")
def test_handle_unexpected_key_type_migration(self, mock_set):
config = mock.Mock()
config.verb = "certonly"
config.key_type = "rsa"
cert = mock.Mock()
cert.private_key_type = "ecdsa"
mock_set.return_value = True
main._handle_unexpected_key_type_migration(config, cert)
mock_set.return_value = False
with self.assertRaises(errors.Error) as raised:
main._handle_unexpected_key_type_migration(config, cert)
self.assertTrue("Command canceled." in str(raised.exception))
mock_set.side_effect = lambda var: var != "key_type"
with self.assertRaises(errors.Error) as raised:
main._handle_unexpected_key_type_migration(config, cert)
self.assertTrue("Command canceled." in str(raised.exception))
class RunTest(test_util.ConfigTestCase):
@@ -163,9 +196,10 @@ class CertonlyTest(unittest.TestCase):
@mock.patch('certbot._internal.cert_manager.lineage_for_certname')
@mock.patch('certbot._internal.cert_manager.domains_for_certname')
@mock.patch('certbot._internal.renewal.renew_cert')
@mock.patch('certbot._internal.main._handle_unexpected_key_type_migration')
@mock.patch('certbot._internal.main._report_new_cert')
def test_find_lineage_for_domains_and_certname(self, mock_report_cert,
mock_renew_cert, mock_domains, mock_lineage):
mock_handle_type, mock_renew_cert, mock_domains, mock_lineage):
domains = ['example.com', 'test.org']
mock_domains.return_value = domains
mock_lineage.names.return_value = domains
@@ -175,6 +209,7 @@ class CertonlyTest(unittest.TestCase):
self.assertTrue(mock_domains.call_count == 1)
self.assertTrue(mock_renew_cert.call_count == 1)
self.assertTrue(mock_report_cert.call_count == 1)
self.assertTrue(mock_handle_type.call_count == 1)
# user confirms updating lineage with new domains
self._call(('certonly --webroot -d example.com -d test.com '
@@ -183,11 +218,12 @@ class CertonlyTest(unittest.TestCase):
self.assertTrue(mock_domains.call_count == 2)
self.assertTrue(mock_renew_cert.call_count == 2)
self.assertTrue(mock_report_cert.call_count == 2)
self.assertTrue(mock_handle_type.call_count == 2)
# error in _ask_user_to_confirm_new_names
self.mock_get_utility().yesno.return_value = False
self.assertRaises(errors.ConfigurationError, self._call,
('certonly --webroot -d example.com -d test.com --cert-name example.com').split())
'certonly --webroot -d example.com -d test.com --cert-name example.com'.split())
@mock.patch('certbot._internal.cert_manager.domains_for_certname')
@mock.patch('certbot.display.ops.choose_names')
@@ -481,7 +517,8 @@ class DetermineAccountTest(test_util.ConfigTestCase):
self.assertTrue(self.config.email is None)
@mock.patch('certbot._internal.client.display_ops.get_email')
def test_no_accounts_no_email(self, mock_get_email):
@mock.patch('certbot._internal.main.logger')
def test_no_accounts_no_email(self, mock_logger, mock_get_email):
mock_get_email.return_value = 'foo@bar.baz'
with mock.patch('certbot._internal.main.client') as client:
@@ -493,6 +530,7 @@ class DetermineAccountTest(test_util.ConfigTestCase):
self.assertEqual(self.accs[0].id, self.config.account)
self.assertEqual('foo@bar.baz', self.config.email)
mock_logger.info.assert_called_once_with('Account registered.')
def test_no_accounts_email(self):
self.config.email = 'other email'
@@ -980,6 +1018,7 @@ class MainTest(test_util.ConfigTestCase):
mock_lineage.should_autorenew.return_value = due_for_renewal
mock_lineage.has_pending_deployment.return_value = False
mock_lineage.names.return_value = ['isnot.org']
mock_lineage.private_key_type = 'RSA'
mock_certr = mock.MagicMock()
mock_key = mock.MagicMock(pem='pem_key')
mock_client = mock.MagicMock()
@@ -1279,13 +1318,16 @@ class MainTest(test_util.ConfigTestCase):
@test_util.patch_get_utility()
@mock.patch('certbot._internal.main._find_lineage_for_domains_and_certname')
@mock.patch('certbot._internal.main._init_le_client')
def test_certonly_reinstall(self, mock_init, mock_renewal, mock_get_utility):
@mock.patch('certbot._internal.main._report_new_cert')
def test_certonly_reinstall(self, mock_report_new_cert, mock_init,
mock_renewal, mock_get_utility):
mock_renewal.return_value = ('reinstall', mock.MagicMock())
mock_init.return_value = mock_client = mock.MagicMock()
self._call(['-d', 'foo.bar', '-a', 'standalone', 'certonly'])
self.assertFalse(mock_client.obtain_certificate.called)
self.assertFalse(mock_client.obtain_and_enroll_certificate.called)
self.assertEqual(mock_get_utility().add_message.call_count, 0)
mock_report_new_cert.assert_not_called()
#self.assertTrue('donate' not in mock_get_utility().add_message.call_args[0][0])
def _test_certonly_csr_common(self, extra_args=None):

View File

@@ -32,8 +32,7 @@ class AuthenticatorTest(test_util.TempDirTestCase):
# initialization.
self.config = mock.MagicMock(
http01_port=0, manual_auth_hook=None, manual_cleanup_hook=None,
manual_public_ip_logging_ok=False, noninteractive_mode=False,
validate_hooks=False,
noninteractive_mode=False, validate_hooks=False,
config_dir=os.path.join(self.tempdir, "config_dir"),
work_dir=os.path.join(self.tempdir, "work_dir"),
backup_dir=os.path.join(self.tempdir, "backup_dir"),
@@ -60,19 +59,7 @@ class AuthenticatorTest(test_util.TempDirTestCase):
self.assertEqual(self.auth.get_chall_pref('example.org'),
[challenges.HTTP01, challenges.DNS01])
@test_util.patch_get_utility()
def test_ip_logging_not_ok(self, mock_get_utility):
mock_get_utility().yesno.return_value = False
self.assertRaises(errors.PluginError, self.auth.perform, [])
@test_util.patch_get_utility()
def test_ip_logging_ok(self, mock_get_utility):
mock_get_utility().yesno.return_value = True
self.auth.perform([])
self.assertTrue(self.config.manual_public_ip_logging_ok)
def test_script_perform(self):
self.config.manual_public_ip_logging_ok = True
self.config.manual_auth_hook = (
'{0} -c "from __future__ import print_function;'
'from certbot.compat import os;'
@@ -105,7 +92,6 @@ class AuthenticatorTest(test_util.TempDirTestCase):
@test_util.patch_get_utility()
def test_manual_perform(self, mock_get_utility):
self.config.manual_public_ip_logging_ok = True
self.assertEqual(
self.auth.perform(self.achalls),
[achall.response(achall.account_key) for achall in self.achalls])
@@ -116,7 +102,6 @@ class AuthenticatorTest(test_util.TempDirTestCase):
self.assertFalse(kwargs['wrap'])
def test_cleanup(self):
self.config.manual_public_ip_logging_ok = True
self.config.manual_auth_hook = ('{0} -c "import sys; sys.stdout.write(\'foo\')"'
.format(sys.executable))
self.config.manual_cleanup_hook = '# cleanup'

View File

@@ -74,6 +74,30 @@ class RenewalTest(test_util.ConfigTestCase):
assert self.config.rsa_key_size == 2048
def test_reuse_ec_key_renewal_params(self):
self.config.elliptic_curve = 'INVALID_CURVE'
self.config.reuse_key = True
self.config.dry_run = True
self.config.key_type = 'ecdsa'
config = configuration.NamespaceConfig(self.config)
rc_path = test_util.make_lineage(
self.config.config_dir,
'sample-renewal-ec.conf',
ec=True,
)
lineage = storage.RenewableCert(rc_path, config)
le_client = mock.MagicMock()
le_client.obtain_certificate.return_value = (None, None, None, None)
from certbot._internal import renewal
with mock.patch('certbot._internal.renewal.hooks.renew_hook'):
renewal.renew_cert(self.config, None, le_client, lineage)
assert self.config.elliptic_curve == 'secp256r1'
class RestoreRequiredConfigElementsTest(test_util.ConfigTestCase):
"""Tests for certbot._internal.renewal.restore_required_config_elements."""

View File

@@ -3,6 +3,11 @@
targets:
#-----------------------------------------------------------------------------
#Ubuntu
- ami: ami-0f2e2c076f4c2f941
name: ubuntu20.10
type: ubuntu
virt: hvm
user: ubuntu
- ami: ami-0758470213bdd23b1
name: ubuntu20.04
type: ubuntu
@@ -37,18 +42,6 @@ targets:
virt: hvm
user: admin
#-----------------------------------------------------------------------------
# Fedora
- ami: ami-0fcbe88944a53b4c8
name: fedora31
type: centos
virt: hvm
user: fedora
- ami: ami-00bbc6858140f19ed
name: fedora30
type: centos
virt: hvm
user: fedora
#-----------------------------------------------------------------------------
# CentOS
- ami: ami-9887c6e7
name: centos7

View File

@@ -47,16 +47,6 @@ targets:
type: centos
virt: hvm
user: ec2-user
- ami: ami-0fcbe88944a53b4c8
name: fedora31
type: centos
virt: hvm
user: fedora
- ami: ami-00bbc6858140f19ed
name: fedora30
type: centos
virt: hvm
user: fedora
#-----------------------------------------------------------------------------
# CentOS
# These Marketplace AMIs must, irritatingly, have their terms manually

View File

@@ -3,6 +3,11 @@
targets:
#-----------------------------------------------------------------------------
#Ubuntu
- ami: ami-0f2e2c076f4c2f941
name: ubuntu20.10
type: ubuntu
virt: hvm
user: ubuntu
- ami: ami-0758470213bdd23b1
name: ubuntu20.04
type: ubuntu
@@ -38,16 +43,6 @@ targets:
type: centos
virt: hvm
user: ec2-user
- ami: ami-0fcbe88944a53b4c8
name: fedora31
type: centos
virt: hvm
user: fedora
- ami: ami-00bbc6858140f19ed
name: fedora30
type: centos
virt: hvm
user: fedora
#-----------------------------------------------------------------------------
# CentOS
# These Marketplace AMIs must, irritatingly, have their terms manually

View File

@@ -12,19 +12,6 @@ IFS=$'\n\t'
# given value is only the base of the tag because the things like the CPU
# architecture are also added to the full tag.
# As of writing this, runs of this script consistently fail in Azure
# Pipelines, but they are fixed by using Docker BuildKit. A log of the failures
# that were occurring can be seen at
# https://gist.github.com/2227a05622299ce17bff9b0da714a1ff. Since using
# BuildKit is supposed to offer benefits anyway (see
# https://docs.docker.com/develop/develop-images/build_enhancements/ for more
# information), let's use it.
#
# This variable is set inside the script itself rather than in something like
# the CI config to have a consistent experience when this script is run
# locally.
export DOCKER_BUILDKIT=1
WORK_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )"
REPO_ROOT="$(dirname "$(dirname "${WORK_DIR}")")"
source "$WORK_DIR/lib/common"

View File

@@ -1,7 +1,10 @@
# Certbot Snaps
# Building Certbot Snaps
## Local Testing and Development
These instructions are recommended when testing anything about the snap setup for ease of debugging.
The architecture of the built snap is limited to the architecture of the system it is built on.
### Initial VM Set Up
These steps need to be done once to set up your VM and do not need to be run again to rebuild the snap.
@@ -9,37 +12,89 @@ These steps need to be done once to set up your VM and do not need to be run aga
1. Start with a Focal VM. You need a full virtual machine using something like DigitalOcean, EC2, or VirtualBox. Docker won't work. Another version of Ubuntu can probably be used, but Focal was used when writing these instructions.
2. Set up a user other than root with sudo privileges for use with snapcraft and run all of the following commands with it. A command to do this for a user named certbot looks like `adduser certbot && usermod -aG sudo certbot && su - certbot`.
3. Install git and python with `sudo apt update && sudo apt install -y git python`.
4. Set up lxd for use with snapcraft by running `sudo snap install lxd && sudo /snap/bin/lxd.migrate -yes && sudo /snap/bin/lxd waitready && sudo /snap/bin/lxd init --auto` (errors here are ok; it may already
4. Set up lxd for use with snapcraft by running `sudo snap install lxd && sudo /snap/bin/lxd.migrate -yes; sudo /snap/bin/lxd waitready && sudo /snap/bin/lxd init --auto` (errors here are ok; it may already
have been installed on your system).
5. Add your current user to the lxd group and update your shell to have the new assignment by running `sudo usermod -a -G lxd ${USER} && newgrp lxd`.
6. Install snapcraft with `sudo snap install --classic snapcraft`.
7. `cd ~` (or any other directory where you want our source files to be)
8. Run `git clone git://github.com/certbot/certbot`
9. `cd certbot`
9. `cd certbot` (All further instructions are relative to this directory.)
### Build the Snaps
### Certbot Snap
These are the steps to build and install the snaps. If you have run these steps before, you may want to run the commands in the section below to clean things up before building the snap again.
#### Reset the Environment
If the snap has been built before, the instructions below clean up the build environment so it can reliably be used again.
1. `snapcraft clean --use-lxd`
2. [Optional] `mv certbot_*_amd64.snap certbot_amd64.snap.bak`
#### Build the Certbot Snap
These are the steps to build and install the Certbot snap. If you have run these steps before, you may want to run the commands in the section above to clean things up or save a previous build before building the snap again (running `snapcraft` again will overwrite the previous snap).
1. Run `snapcraft --use-lxd`.
2. Install the generated snap with `sudo snap install --dangerous --classic certbot_*_amd64.snap`. You can transfer the snap to a different machine to run it there instead if you prefer.
3. Run `tools/merge_requirements.py tools/dev_constraints.txt <(tools/strip_hashes.py letsencrypt-auto-source/pieces/dependency-requirements.txt) > certbot-dns-dnsimple/snap-constraints.txt` (this is a workaround for https://github.com/certbot/certbot/issues/8100).
4. `cd certbot-dns-dnsimple`
5. `snapcraft --use-lxd`
6. Run `sudo snap set certbot trust-plugin-with-root=ok`.
7. Install the generated snap with `sudo snap install --dangerous certbot-dns-dnsimple_*_amd64.snap`. Again, you can transfer the snap to a different machine to run it there instead if you prefer.
8. Connect the plugin with `sudo snap connect certbot:plugin certbot-dns-dnsimple`.
9. Connect the plugin metadata with `sudo snap connect certbot-dns-dnsimple:certbot-metadata certbot:certbot-metadata`. Install the plugin again to test refresh; logs are at `/var/snap/certbot-dns-dnsimple/current/debuglog`.
10. Now you can run Certbot as normal. For example, `certbot plugins` should display the DNSimple plugin as installed.
### Reset the Environment
#### Run
The instructions below clean up the build environment so it can reliably be used again.
Run Certbot as normal. For example, `certbot plugins` should display the Apache and Nginx plugins.
1. `cd ~/certbot` (or to an alternate path where you put our source files)
2. `snapcraft clean --use-lxd`
3. `rm certbot_*_amd64.snap`
4. `cd certbot-dns-dnsimple`
5. `rm certbot-dns-dnsimple_*_amd64.snap`
6. `snapcraft clean --use-lxd`
7. `cd ..`
### Certbot Plugin Snaps
These instructions use the `certbot-dns-dnsimple` plugin as an example, but all of Certbot's other plugin snaps can be built in the same way.
#### Reset the Environment
If the plugin snap has been built before, the instructions below clean up the build environment so it can reliably be used again.
1. `cd certbot-dns-dnsimple`
2. `snapcraft clean --use-lxd`
3. [Optional] `mv certbot-dns-dnsimple_*_amd64.snap certbot-dns-simple_amd64.snap.bak`
4. `cd ..`
#### Build a Certbot Plugin Snap
These are the steps to build and install the Certbot DNSimple plugin snap. If you have run these steps before, you may want to run the commands in the section above to clean things up or save a previous build before building the snap again (running `snapcraft` again will overwrite the previous snap).
1. Run `tools/snap/generate_dnsplugins_all.sh` to generate all necessary files for all plugin snaps.
2. `cd certbot-dns-dnsimple`
3. `snapcraft --use-lxd`
4. Run `sudo snap set certbot trust-plugin-with-root=ok`.
5. Install the generated snap with `sudo snap install --dangerous certbot-dns-dnsimple_*_amd64.snap`. Again, you can transfer the snap to a different machine to run it there instead if you prefer.
6. Connect the plugin with `sudo snap connect certbot:plugin certbot-dns-dnsimple`.
7. Connect the plugin metadata with `sudo snap connect certbot-dns-dnsimple:certbot-metadata certbot:certbot-metadata`. Install the plugin again to test refresh; if the plugin's hook creates any logs, they are at `/var/snap/certbot-dns-dnsimple/current/debuglog`.
#### Run
Run Certbot as normal. For example, `certbot plugins` should display the DNSimple plugin as installed.
## Building for Other Architectures
To build for an unavailable architecture or for multiple architectures simultaneously, we recommend using snapcraft's remote build feature.
It is easiest to run this from a local machine.
### Initial Local Setup
1. Create or log into an Ubuntu One account [here](https://login.launchpad.net/).
2. Install git and python with `sudo apt update && sudo apt install -y git python`.
3. Install snapcraft with `sudo snap install --classic snapcraft`.
4. `cd ~` (or any other directory where you want our source files to be)
5. Run `git clone git://github.com/certbot/certbot`
6. `cd certbot` (All further instructions are relative to this directory.)
7. To trigger `snapcraft` to request access to your Launchpad account, run
`snapcraft remote-build --launchpad-accept-public-upload --status`. A URL where you need
to grant this access will be printed to your terminal and automatically open in your browser
if one is available.
### Build Snaps Remotely
Certbot provides a wrapper around snapcraft's remote build to make building all of our plugins easier. To see all available
options, run `python3 tools/snap/build_remote.py --help`.
For example, to build all available snaps for all architectures, run `python3 tools/snap/build_remote.py ALL --archs amd64 arm64 armhf`.
To build only the certbot snap on only amd64, run `python3 tools/snap/build_remote.py certbot --archs armhf`.
The command will upload the entire contents of the working directory, so if the remote build
appears to hang, try using a clean clone of the `certbot` repository.

View File

@@ -34,15 +34,6 @@ def _build_snap(target, archs, status, lock):
workspace = CERTBOT_DIR
else:
workspace = join(CERTBOT_DIR, target)
with tempfile.NamedTemporaryFile() as f:
subprocess.check_output(
('"{0}" tools/strip_hashes.py letsencrypt-auto-source/pieces/dependency-requirements.txt '
'| grep -v python-augeas > "{1}"').format(sys.executable, f.name),
shell=True, cwd=CERTBOT_DIR)
subprocess.check_output(
('"{0}" tools/merge_requirements.py tools/dev_constraints.txt '
'"{1}" > "{2}/snap-constraints.txt"').format(sys.executable, f.name, workspace),
shell=True, cwd=CERTBOT_DIR)
retry = 3
while retry:
@@ -165,6 +156,12 @@ def main():
targets.remove('DNS_PLUGINS')
targets.update(PLUGINS)
# If we're building anything other than just Certbot, we need to
# generate the snapcraft files for the DNS plugins.
if targets != set(('certbot',)):
subprocess.run(['tools/snap/generate_dnsplugins_all.sh'],
check=True, cwd=CERTBOT_DIR)
print('Start remote snap builds...')
print(f' - archs: {", ".join(archs)}')
print(f' - projects: {", ".join(sorted(targets))}')

View File

@@ -0,0 +1,15 @@
#!/bin/bash
# Generate all necessary files for building snaps for all DNS plugins
set -eu
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
CERTBOT_DIR="$(dirname "$(dirname "${DIR}")")"
for PLUGIN_PATH in "${CERTBOT_DIR}"/certbot-dns-*; do
bash "${CERTBOT_DIR}"/tools/snap/generate_dnsplugins_snapcraft.sh $PLUGIN_PATH
bash "${CERTBOT_DIR}"/tools/snap/generate_dnsplugins_postrefreshhook.sh $PLUGIN_PATH
# Create constraints file
"${CERTBOT_DIR}"/tools/merge_requirements.py tools/dev_constraints.txt \
<("${CERTBOT_DIR}"/tools/strip_hashes.py letsencrypt-auto-source/pieces/dependency-requirements.txt) \
> "${PLUGIN_PATH}"/snap-constraints.txt
done

View File

@@ -1,15 +1,15 @@
#!/bin/bash
# Generate the hooks/post-refresh file for all DNS plugins
# Generate the hooks/post-refresh file for a DNS plugin
# Usage: bash generate_dnsplugins_postrefreshhook.sh path/to/dns/plugin
# For example, from the certbot home directory:
# tools/snap/generate_dnsplugins_postrefreshhook.sh certbot-dns-dnsimple
set -eu
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
CERTBOT_DIR="$(dirname "$(dirname "${DIR}")")"
for PLUGIN_PATH in "${CERTBOT_DIR}"/certbot-dns-*; do
mkdir -p "${PLUGIN_PATH}/snap/hooks"
cat <<EOF > "${PLUGIN_PATH}/snap/hooks/post-refresh"
PLUGIN_PATH=$1
mkdir -p "${PLUGIN_PATH}/snap/hooks"
cat <<EOF > "${PLUGIN_PATH}/snap/hooks/post-refresh"
#!/bin/sh -e
# This file is generated by tools/generate_dnsplugins_postrefreshhook.sh and should not be edited manually.
# This file is generated automatically and should not be edited manually.
# get certbot version
if [ ! -f "\$SNAP/certbot-shared/certbot-version.txt" ]; then
@@ -31,4 +31,3 @@ if [ "\$exit_code" -eq 1 ]; then
exit 1
fi
EOF
done

View File

@@ -1,16 +1,16 @@
#!/bin/bash
# Generate the snapcraft.yaml file for all DNS plugins
# Generate the snapcraft.yaml file for a DNS plugins
# Usage: bash generate_dnsplugins_snapcraft.sh path/to/dns/plugin
# For example, from the certbot home directory:
# tools/snap/generate_dnsplugins_snapcraft.sh certbot-dns-dnsimple
set -e
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
CERTBOT_DIR="$(dirname "$(dirname "${DIR}")")"
for PLUGIN_PATH in "${CERTBOT_DIR}"/certbot-dns-*; do
PLUGIN=$(basename "${PLUGIN_PATH}")
DESCRIPTION=$(grep description "${PLUGIN_PATH}/setup.py" | sed -E 's|\s+description="(.*)",|\1|g')
mkdir -p "${PLUGIN_PATH}/snap"
cat <<EOF > "${PLUGIN_PATH}/snap/snapcraft.yaml"
# This file is generated by tools/generate_dnsplugins_snapcraft.sh and should not be edited manually.
PLUGIN_PATH=$1
PLUGIN=$(basename "${PLUGIN_PATH}")
DESCRIPTION=$(grep description "${PLUGIN_PATH}/setup.py" | sed -E 's|\s+description="(.*)",|\1|g')
mkdir -p "${PLUGIN_PATH}/snap"
cat <<EOF > "${PLUGIN_PATH}/snap/snapcraft.yaml"
# This file is generated automatically and should not be edited manually.
name: ${PLUGIN}
summary: ${DESCRIPTION}
description: ${DESCRIPTION}
@@ -52,4 +52,3 @@ plugs:
content: metadata-1
target: \$SNAP/certbot-shared
EOF
done