veeti / manuale Goto Github PK
View Code? Open in Web Editor NEWA fully manual Let's Encrypt/ACME client
License: MIT License
A fully manual Let's Encrypt/ACME client
License: MIT License
It seems to be possible to query the authorization validity period:
Line 31 in 29c18a4
It would be nice to have a new command for it.
Allow creation of certificates and accounts (if supported?) using EC keys. Also allow the user to specify the curve.
(Pull requests welcome!)
Please change the issue
command to disallow conflicting parameters.
At present, if you specify a CSR, the domains will be used for printing on screen, but might not match the CSR at all. This can be very confusing when you are trying to debug errors.
Project is great, but it is missing the renew feature! Can you write it :DD. thanks
Hi
Apologies if I'm about to ask something that is more down to how Let's Encrypt works ...
I've just started trying your tool. I've authorised a domain and the completion has said "Authorization lasts until 2016-11-20T19:13:05Z". What isn't clear is what do I do in order to "renew" that authorisation?
If I just run "manuale authorize ", will that give me a fresh TXT record that I've got to plug into the DNS or will it extend the authorisation of the domain now that it has been authorised?
I'm trying to understand how renewal in general (authorisation and certificate) works with your tool.
Thanks.
Add support for the tls-sni-01
challenge.
For ease of automation, please split the generation of a challenge & the verification call.
Workflow example.
manuale authorize --generate-only --method dns example.com >somefile
grep TXT somefile | $custom_update_DNS --append example.com
manuale authorize --verify-only --method dns example.com
git clone https://github.com/veeti/manuale.git
cd manuale
git log | head
commit 1de8f90bc8ec633b0c53cf0d1b923fe8b39c82e4
Author: Veeti Paananen <[email protected]>
Date: Tue Feb 9 19:07:44 2016 +0200
1.0.2.dev0
commit c826e2483b5515a3e316bafa33fac248c6e91993
Author: Veeti Paananen <[email protected]>
Date: Tue Feb 9 18:54:27 2016 +0200
python --version
Python 3.4.1
python setup.py install
which manuale
/usr/bin/manuale
manuale -h
usage: manuale [-h] [--server SERVER] [--account ACCOUNT]
{register,authorize,issue,revoke,info,version} ...
Interact with ACME certification authorities such as Let's Encrypt.
No idea what you're doing? Register an account, authorize your domains and
issue a certificate or two. Call a command with -h for more instructions.
positional arguments:
{register,authorize,issue,revoke,info,version}
register Create a new account and register
authorize Verify domain ownership
issue Request a new certificate
revoke Revoke an issued certificate
info Shows account information from the service
version Show the version number
optional arguments:
-h, --help show this help message and exit
--server SERVER, -s SERVER
The ACME server to use (default:
https://acme-v01.api.letsencrypt.org/)
--account ACCOUNT, -a ACCOUNT
The account file to use or create (default:
account.json)
manuale register [email protected]
You're about to register a new account with the e-mail [email protected]. Continue? [Y/n] Y
Generating a new account key. This might take a second.
Key generated.
Registering...
Oops! An unhandled error occurred. Please file a bug.
stat: can't specify None for path argument
Traceback (most recent call last):
File "/usr/lib/python3.4/site-packages/manuale-1.0.2.dev0-py3.4.egg/manuale/cli.py", line 229, in main
args.func(args)
File "/usr/lib/python3.4/site-packages/manuale-1.0.2.dev0-py3.4.egg/manuale/cli.py", line 97, in _register
register(args.server, args.account, args.email)
File "/usr/lib/python3.4/site-packages/manuale-1.0.2.dev0-py3.4.egg/manuale/register.py", line 35, in register
registration = acme.register(email)
File "/usr/lib/python3.4/site-packages/manuale-1.0.2.dev0-py3.4.egg/manuale/acme.py", line 48, in register
"mailto:{}".format(email)
File "/usr/lib/python3.4/site-packages/manuale-1.0.2.dev0-py3.4.egg/manuale/acme.py", line 165, in post
header, protected = self.get_headers()
File "/usr/lib/python3.4/site-packages/manuale-1.0.2.dev0-py3.4.egg/manuale/acme.py", line 38, in get_headers
protected_header['nonce'] = self.get_nonce()
File "/usr/lib/python3.4/site-packages/manuale-1.0.2.dev0-py3.4.egg/manuale/acme.py", line 30, in get_nonce
return self.get('/directory').headers.get('Replay-Nonce')
File "/usr/lib/python3.4/site-packages/manuale-1.0.2.dev0-py3.4.egg/manuale/acme.py", line 157, in get
return requests.get(self.path(path), headers=_headers)
File "/usr/lib/python3.4/site-packages/requests/api.py", line 67, in get
return request('get', url, params=params, **kwargs)
File "/usr/lib/python3.4/site-packages/requests/api.py", line 53, in request
return session.request(method=method, url=url, **kwargs)
File "/usr/lib/python3.4/site-packages/requests/sessions.py", line 468, in request
resp = self.send(prep, **send_kwargs)
File "/usr/lib/python3.4/site-packages/requests/sessions.py", line 576, in send
r = adapter.send(request, **kwargs)
File "/usr/lib/python3.4/site-packages/requests/adapters.py", line 342, in send
self.cert_verify(conn, request.url, verify, cert)
File "/usr/lib/python3.4/site-packages/requests/adapters.py", line 187, in cert_verify
if not os.path.isdir(cert_loc):
File "/usr/lib64/python3.4/genericpath.py", line 42, in isdir
st = os.stat(s)
TypeError: stat: can't specify None for path argument
Hello, it seems that a new ACME protocol is due to launch in a few days.
https://community.letsencrypt.org/t/staging-endpoint-for-acme-v2/49605
Are there plans to support it in manuale
?
✔ user.tina ~/proj/manuale
$ ls -al
total 16
drwxr-xr-x 2 user user 512 May 25 18:53 .
drwxr-xr-x 71 user user 2048 May 17 15:45 ..
✔ user.tina ~/proj/manuale
$ git clone https://github.com/veeti/manuale .
Cloning into '.'...
remote: Counting objects: 200, done.
remote: Total 200 (delta 0), reused 0 (delta 0), pack-reused 200
Receiving objects: 100% (200/200), 40.43 KiB | 0 bytes/s, done.
Resolving deltas: 100% (123/123), done.
✔ user.tina ~/proj/manuale
$ python3.6 -m venv env
✔ user.tina ~/proj/manuale
$ env/bin/python setup.py install
running install
running bdist_egg
running egg_info
creating manuale.egg-info
writing manuale.egg-info/PKG-INFO
writing dependency_links to manuale.egg-info/dependency_links.txt
writing entry points to manuale.egg-info/entry_points.txt
writing requirements to manuale.egg-info/requires.txt
writing top-level names to manuale.egg-info/top_level.txt
writing manifest file 'manuale.egg-info/SOURCES.txt'
reading manifest file 'manuale.egg-info/SOURCES.txt'
reading manifest template 'MANIFEST.in'
writing manifest file 'manuale.egg-info/SOURCES.txt'
installing library code to build/bdist.openbsd-6.1-amd64/egg
running install_lib
running build_py
creating build
creating build/lib
creating build/lib/manuale
copying manuale/__init__.py -> build/lib/manuale
copying manuale/account.py -> build/lib/manuale
copying manuale/acme.py -> build/lib/manuale
copying manuale/authorize.py -> build/lib/manuale
copying manuale/cli.py -> build/lib/manuale
copying manuale/crypto.py -> build/lib/manuale
copying manuale/errors.py -> build/lib/manuale
copying manuale/helpers.py -> build/lib/manuale
copying manuale/info.py -> build/lib/manuale
copying manuale/issue.py -> build/lib/manuale
copying manuale/register.py -> build/lib/manuale
copying manuale/revoke.py -> build/lib/manuale
creating build/bdist.openbsd-6.1-amd64
creating build/bdist.openbsd-6.1-amd64/egg
creating build/bdist.openbsd-6.1-amd64/egg/manuale
copying build/lib/manuale/__init__.py -> build/bdist.openbsd-6.1-amd64/egg/manuale
copying build/lib/manuale/account.py -> build/bdist.openbsd-6.1-amd64/egg/manuale
copying build/lib/manuale/acme.py -> build/bdist.openbsd-6.1-amd64/egg/manuale
copying build/lib/manuale/authorize.py -> build/bdist.openbsd-6.1-amd64/egg/manuale
copying build/lib/manuale/cli.py -> build/bdist.openbsd-6.1-amd64/egg/manuale
copying build/lib/manuale/crypto.py -> build/bdist.openbsd-6.1-amd64/egg/manuale
copying build/lib/manuale/errors.py -> build/bdist.openbsd-6.1-amd64/egg/manuale
copying build/lib/manuale/helpers.py -> build/bdist.openbsd-6.1-amd64/egg/manuale
copying build/lib/manuale/info.py -> build/bdist.openbsd-6.1-amd64/egg/manuale
copying build/lib/manuale/issue.py -> build/bdist.openbsd-6.1-amd64/egg/manuale
copying build/lib/manuale/register.py -> build/bdist.openbsd-6.1-amd64/egg/manuale
copying build/lib/manuale/revoke.py -> build/bdist.openbsd-6.1-amd64/egg/manuale
byte-compiling build/bdist.openbsd-6.1-amd64/egg/manuale/__init__.py to __init__.cpython-36.pyc
byte-compiling build/bdist.openbsd-6.1-amd64/egg/manuale/account.py to account.cpython-36.pyc
byte-compiling build/bdist.openbsd-6.1-amd64/egg/manuale/acme.py to acme.cpython-36.pyc
byte-compiling build/bdist.openbsd-6.1-amd64/egg/manuale/authorize.py to authorize.cpython-36.pyc
byte-compiling build/bdist.openbsd-6.1-amd64/egg/manuale/cli.py to cli.cpython-36.pyc
byte-compiling build/bdist.openbsd-6.1-amd64/egg/manuale/crypto.py to crypto.cpython-36.pyc
byte-compiling build/bdist.openbsd-6.1-amd64/egg/manuale/errors.py to errors.cpython-36.pyc
byte-compiling build/bdist.openbsd-6.1-amd64/egg/manuale/helpers.py to helpers.cpython-36.pyc
byte-compiling build/bdist.openbsd-6.1-amd64/egg/manuale/info.py to info.cpython-36.pyc
byte-compiling build/bdist.openbsd-6.1-amd64/egg/manuale/issue.py to issue.cpython-36.pyc
byte-compiling build/bdist.openbsd-6.1-amd64/egg/manuale/register.py to register.cpython-36.pyc
byte-compiling build/bdist.openbsd-6.1-amd64/egg/manuale/revoke.py to revoke.cpython-36.pyc
creating build/bdist.openbsd-6.1-amd64/egg/EGG-INFO
copying manuale.egg-info/PKG-INFO -> build/bdist.openbsd-6.1-amd64/egg/EGG-INFO
copying manuale.egg-info/SOURCES.txt -> build/bdist.openbsd-6.1-amd64/egg/EGG-INFO
copying manuale.egg-info/dependency_links.txt -> build/bdist.openbsd-6.1-amd64/egg/EGG-INFO
copying manuale.egg-info/entry_points.txt -> build/bdist.openbsd-6.1-amd64/egg/EGG-INFO
copying manuale.egg-info/requires.txt -> build/bdist.openbsd-6.1-amd64/egg/EGG-INFO
copying manuale.egg-info/top_level.txt -> build/bdist.openbsd-6.1-amd64/egg/EGG-INFO
zip_safe flag not set; analyzing archive contents...
creating dist
creating 'dist/manuale-1.1.0-py3.6.egg' and adding 'build/bdist.openbsd-6.1-amd64/egg' to it
removing 'build/bdist.openbsd-6.1-amd64/egg' (and everything under it)
Processing manuale-1.1.0-py3.6.egg
Copying manuale-1.1.0-py3.6.egg to /home/user/proj/manuale/env/lib/python3.6/site-packages
Adding manuale 1.1.0 to easy-install.pth file
Installing manuale script to /home/user/proj/manuale/env/bin
Installed /home/user/proj/manuale/env/lib/python3.6/site-packages/manuale-1.1.0-py3.6.egg
Processing dependencies for manuale==1.1.0
Searching for requests
Reading https://pypi.python.org/simple/requests/
Downloading https://pypi.python.org/packages/72/46/4abc3f5aaf7bf16a52206bb0c68677a26c216c1e6625c78c5aef695b5359/requests-2.14.2.tar.gz#md5=4c3c169ed67466088a2a6947784fe444
Best match: requests 2.14.2
Processing requests-2.14.2.tar.gz
Writing /tmp/easy_install-ox29086u/requests-2.14.2/setup.cfg
Running requests-2.14.2/setup.py -q bdist_egg --dist-dir /tmp/easy_install-ox29086u/requests-2.14.2/egg-dist-tmp-cdeygr7_
warning: no files found matching 'test_requests.py'
creating /home/user/proj/manuale/env/lib/python3.6/site-packages/requests-2.14.2-py3.6.egg
Extracting requests-2.14.2-py3.6.egg to /home/user/proj/manuale/env/lib/python3.6/site-packages
Adding requests 2.14.2 to easy-install.pth file
Installed /home/user/proj/manuale/env/lib/python3.6/site-packages/requests-2.14.2-py3.6.egg
Searching for cryptography>=1.0
Reading https://pypi.python.org/simple/cryptography/
Downloading https://pypi.python.org/packages/ec/5f/d5bc241d06665eed93cd8d3aa7198024ce7833af7a67f6dc92df94e00588/cryptography-1.8.1.tar.gz#md5=9f28a9c141995cd2300d0976b4fac3fb
Best match: cryptography 1.8.1
Processing cryptography-1.8.1.tar.gz
Writing /tmp/easy_install-30e7tmsb/cryptography-1.8.1/setup.cfg
Running cryptography-1.8.1/setup.py -q bdist_egg --dist-dir /tmp/easy_install-30e7tmsb/cryptography-1.8.1/egg-dist-tmp-6i6tsjfg
_configtest.c:1: error: thread-local storage not supported for this target
Note: will not use '__thread' in the C code
***** The above error message can be safely ignored.
Installed /tmp/easy_install-30e7tmsb/cryptography-1.8.1/.eggs/cffi-1.10.0-py3.6-openbsd-6.1-amd64.egg
Searching for pycparser
Reading https://pypi.python.org/simple/pycparser/
Downloading https://pypi.python.org/packages/be/64/1bb257ffb17d01f4a38d7ce686809a736837ad4371bcc5c42ba7a715c3ac/pycparser-2.17.tar.gz#md5=ca98dcb50bc1276f230118f6af5a40c7
Best match: pycparser 2.17
Processing pycparser-2.17.tar.gz
Writing /tmp/easy_install-30e7tmsb/cryptography-1.8.1/temp/easy_install-i3hr8lo1/pycparser-2.17/setup.cfg
Running pycparser-2.17/setup.py -q bdist_egg --dist-dir /tmp/easy_install-30e7tmsb/cryptography-1.8.1/temp/easy_install-i3hr8lo1/pycparser-2.17/egg-dist-tmp-vavlbz1f
warning: no previously-included files matching 'yacctab.*' found under directory 'tests'
warning: no previously-included files matching 'lextab.*' found under directory 'tests'
warning: no previously-included files matching 'yacctab.*' found under directory 'examples'
warning: no previously-included files matching 'lextab.*' found under directory 'examples'
zip_safe flag not set; analyzing archive contents...
pycparser.ply.__pycache__.lex.cpython-36: module references __file__
pycparser.ply.__pycache__.lex.cpython-36: module MAY be using inspect.getsourcefile
pycparser.ply.__pycache__.ygen.cpython-36: module references __file__
pycparser.ply.__pycache__.yacc.cpython-36: module references __file__
pycparser.ply.__pycache__.yacc.cpython-36: module MAY be using inspect.getsourcefile
pycparser.ply.__pycache__.yacc.cpython-36: module MAY be using inspect.stack
creating /tmp/easy_install-30e7tmsb/cryptography-1.8.1/.eggs/pycparser-2.17-py3.6.egg
Extracting pycparser-2.17-py3.6.egg to /tmp/easy_install-30e7tmsb/cryptography-1.8.1/.eggs
Installed /tmp/easy_install-30e7tmsb/cryptography-1.8.1/.eggs/pycparser-2.17-py3.6.egg
no previously-included directories found matching 'docs/_build'
warning: no previously-included files matching '*' found under directory 'vectors'
build/temp.openbsd-6.1-amd64-3.6/_openssl.c:3484: error: expected identifier or '(' before numeric constant
build/temp.openbsd-6.1-amd64-3.6/_openssl.c:3485: error: expected identifier or '(' before numeric constant
build/temp.openbsd-6.1-amd64-3.6/_openssl.c:3486: error: expected identifier or '(' before numeric constant
build/temp.openbsd-6.1-amd64-3.6/_openssl.c: In function '_setup_ssl_threads':
build/temp.openbsd-6.1-amd64-3.6/_openssl.c:3589: warning: comparison is always false due to limited range of data type
error: Setup script exited with error: command 'cc' failed with exit status 1
✘ user.tina ~/proj/manuale
$ uname -mrsv
OpenBSD 6.1 GENERIC.MP#20 amd64
✔ user.tina ~/proj/manuale
$ file build/temp.openbsd-6.1-amd64-3.6/_openssl.c
build/temp.openbsd-6.1-amd64-3.6/_openssl.c: cannot stat 'build/temp.openbsd-6.1-amd64-3.6/_openssl.c' (No such file or directory)
✔ user.tina ~/proj/manuale
$ python3.6 --version
Python 3.6.0
Thank you for your simple client.
I helped me how the acme protocol works.
Would it be possible to provide templates for cert names? %s-public.pem
https://github.com/veeti/manuale/blob/master/manuale/issue.py#L78-L81
Files generated by manuale have permissions 0644. As both the account.json
and domain.pem
files contain private keys, we should ensure permissions are 0600
.
usage: manuale issue [-h] [--key-size KEY_SIZE] [--key-file KEY_FILE]
[--csr-file CSR_FILE] [--output OUTPUT]
domain [domain ...]
it implies it's possible to specify multiple domains, but only one domain is ever processed.
Pending verifications are heavily ratelimited. Screw up too many times and you're out for possibly days. Figure out if ACME allows retrying a challenge that's already in failed state.
Any chance you'd be willing to support 2.7? Would help me out a bunch. What's stopping this from being 2.7 compatible?.. hm, I guess just needs
from builtins import bytes
and to wrap the byte strings in bytes() ?
How no renew certificate?
After the prompt Press enter to continue.
I accidentally pressed ű
then ENTER
Oops! An unhandled error occurred. Please file a bug.
'utf-8' codec can't decode byte 0xc5 in position 0: unexpected end of data
Traceback (most recent call last):
File "/home/prg474/.local/lib/python3.4/site-packages/manuale/cli.py", line 239, in main
args.func(args)
File "/home/prg474/.local/lib/python3.4/site-packages/manuale/cli.py", line 105, in _authorize
authorize(args.server, account, args.domain, args.method)
File "/home/prg474/.local/lib/python3.4/site-packages/manuale/authorize.py", line 109, in authorize
input("Press enter to continue.")
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xc5 in position 0: unexpected end of data
# manuale/crypto.py:80: CryptographyDeprecationWarning: signer and verifier have been deprecated.
# Please use sign and verify instead.
signer = key.signer(padding.PKCS1v15(), hashes.SHA256())
@veeti Is it simply changing the name of functions?
Hi, does ManuaLE support ECC certs?
If so, how to apply?
Thanks.
Add support for the tls-sni-02
challenge.
This cannot be tested with Boulder/LE yet, but will be available in early 2018 with the updated API.
Good day.
I do have right now 48 certs. They renewed at different dates, so I have max 19 renew in a week. But yesterday 18 of them did not renew with error:
Generating a 2048 bit RSA key. This might take a second.
Key generated.
Requesting certificate issuance...
Connection or service request failed. Aborting.
Error creating new cert :: authorizations for these names not found or expired: mysite.com (type urn:acme:error:unauthorized, HTTP 403)
How can I determine why it happened ? Or can you help me, or can I help you.
All certs issued max 2 months ago and some of them in use about half year (of course with renewing every 2 months)
Using a command like manuale issue --csr-file cert.req my.chosen.domain
does produce the correct result in that the certificate is issued based on the contents of the public key in cert.req. But manuale still also generates a (new) private key, if --key-file is not also used. This generated private key is also saved to a file and the user might get the impression that the generated key is the one to use, even if he provided a CSR that contains a different key.
The scenario I'm trying to accommodate is to have the certificate's private key only on the system that needs it, i.e. the machine that will be using the certificate to provide a service. The system that makes the certificate request with manuale via acme doesn't need the key.
My suggestion is to modify issue.py so that if a CSR is provided, generating a private key is always skipped because the public key is in the CSR. Basically moving
Lines 33 to 45 in 99c9865
Lines 55 to 56 in 99c9865
Hi there, thanks for this great project.
At the moment, manuale does not officially support the use of EC keys. I know it is not optimal (mostly due to the function names) but just by commenting out 4 lines in crypto.py file, it is possible to use the client with the EC keys in addition to the RSA keys. Here is the part that needs to be commented out (crypto.py file lines 94-97):
if not isinstance(key, RSAPrivateKey):
raise ValueError("Key is not a private RSA key.")
elif key.key_size < 2048:
raise ValueError("The key must be 2048 bits or longer.")
I thought this little workaround might be useful for people who need EC certificates and wanted to share in an issue instead of sending a pull request.
Thanks again for providing a let's encrypt client with a sane user interface.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.