Git Product home page Git Product logo

acmed's Introduction

ACMEd

Build Status License MIT OR Apache 2.0

The Automatic Certificate Management Environment (ACME), is an internet standard (RFC 8555) which allows to automate X.509 certificates signing by a Certification Authority (CA). ACMEd is one of the many clients for this protocol.

Key features

  • http-01, dns-01 and tls-alpn-01 challenges
  • IP identifier validation extension RFC 8738
  • RSA 2048, RSA 4096, ECDSA P-256, ECDSA P-384, ECDSA P-521, Ed25519 and Ed448 certificates and account keys
  • Internationalized domain names support
  • Fully customizable challenge validation action
  • Fully customizable archiving method (yes, you can use git or anything else)
  • Nice and simple configuration file
  • A pre-built set of hooks that can be used in most circumstances
  • Run as a deamon: no need to set-up timers, crontab or other time-triggered process
  • Retry of HTTPS request rejected with a badNonce or other recoverable errors
  • Customizable HTTPS requests rate limits
  • External account binding
  • Optional key pair reuse (useful for HPKP)
  • For a given certificate, each domain name may be validated using a different challenge
  • A standalone server dedicated to the tls-alpn-01 challenge validation (tacd)

Planned features

  • STAR certificates RFC 8739
  • Daemon and certificates management via the acmectl tool
  • HTTP/2 support

Project status

This project is usable, but is still a work in progress. Each release should works well and accordingly to its documentation. Because the API has not been stabilized yet, breaking changes may occur. Therefore, before any upgrade, you are invited to read the CHANGELOG and check if any change can break your setup.

Please keep in mind this software has neither been subject to a peer review nor to a security audit.

Documentation

The wiki will provides you with an overview as well as guides. You may contribute to it by creating a PR on the acmed-wiki repository.

For exhaustive references, the following man pages are available:

  • acmed (8)
  • acmed.toml (5)
  • tacd (8)

An easy way to read those pages without installing ACMEd is to downloads and pipe them to the man utility:

curl -sSf "https://raw.githubusercontent.com/breard-r/acmed/main/man/en/acmed.8" | man -l -
curl -sSf "https://raw.githubusercontent.com/breard-r/acmed/main/man/en/acmed.toml.5" | man -l -
curl -sSf "https://raw.githubusercontent.com/breard-r/acmed/main/man/en/tacd.8" | man -l -

Alternatively, using zsh, you can use the following variants. Useful on system where man is unable to read from stdin (yes BSD, that's you).

man =(curl -sSf "https://raw.githubusercontent.com/breard-r/acmed/main/man/en/acmed.8")
man =(curl -sSf "https://raw.githubusercontent.com/breard-r/acmed/main/man/en/acmed.toml.5")
man =(curl -sSf "https://raw.githubusercontent.com/breard-r/acmed/main/man/en/tacd.8")

Build from source

In order to compile ACMEd, you will need the Rust compiler and its package manager, Cargo. The minimum supported Rust version(MSRV) is 1.74, although it is recommended to use the latest stable one.

ACMEd depends OpenSSL 1.1.0 or higher.

On systems based on Debian/Ubuntu, you may need to install the libssl-dev, build-essential and pkg-config packages.

On Alpine Linux, you may need to install the openssl-dev and alpine-sdk packages.

$ make
$ make install

To build ACMEd and tacd inside a temporary Docker container, use the contrib/docker/build-docker.sh helper script. It currently supports Debian Buster / Stretch.

You can also build a container image for some k8s deployement, use the contrib/docker/build-docker-image.sh script. It supports the same targets as the build-docker.sh script.

When build succeed, you can start the container with docker run --rm -v /path/to/conf/dir:/etc/acmed acmed:buster.

Advanced options

You can specify a space or comma separated list of features to activate in the FEATURE variable. The possible features are:

  • openssl_dyn (default): use OpenSSL as the cryptographic library, dynamically linked (mutually exclusive with openssl_vendored).
  • openssl_vendored: use OpenSSL as the cryptographic library, statically linked (mutually exclusive with openssl_dyn).

You can also specify the target triple to build for in the TARGET variable. Please note that, if used, this variable must be specified for both make and make install.

For example, you can build statically linked binaries using the openssl_vendored feature and the x86_64-unknown-linux-musl target.

make FEATURES="openssl_vendored" TARGET="x86_64-unknown-linux-musl"

The following environment variables can be used to change default values at compile and/or install time:

  • PREFIX (install): system user prefix (default to /usr)
  • BINDIR (install): system binary directory (default to $PREFIX/bin)
  • DATADIR (install): system data directory (default to $PREFIX/share)
  • MAN5DIR (install): system directory where pages 5 manuals are located (default to $DATADIR/man/man5)
  • MAN8DIR (install): system directory where pages 8 manuals are located (default to $DATADIR/man/man8)
  • SYSCONFDIR (compile and install): system configuration directory (default to /etc)
  • VARLIBDIR (compile and install): directory for persistent data modified by ACMEd (default to /var/lib)
  • RUNSTATEDIR (compile): system run-time variable data (default to /run)
  • ACMED_DEFAULT_ACCOUNTS_DIR (compile): directory where account files are stored (default to $VARLIBDIR/acmed/accounts)
  • ACMED_DEFAULT_CERT_DIR (compile): directory where certificates and private keys are stored (default to $VARLIBDIR/acmed/certs)
  • ACMED_DEFAULT_CERT_FORMAT (compile): format for certificates and private keys files names (default to { name }_{ key_type }.{ file_type }.{ ext })
  • ACMED_DEFAULT_CONFIG_FILE (compile): main configuration file (default to $SYSCONFDIR/acmed/acmed.toml)
  • ACMED_DEFAULT_PID_FILE (compile): PID file for the main acmed process (default to $RUNSTATEDIR/acmed.pid)
  • TACD_DEFAULT_PID_FILE (compile): PID file for the tacd process (default to $RUNSTATEDIR/tacd.pid)

For example, the following will compile a binary that will use the /usr/share/etc/acmed/acmed.toml configuration file and will be installed in the /usr/local/bin directory :

make SYSCONFDIR="/usr/share/etc"
make BINDIR="/usr/local/bin" install

Packaging

Most of the time, when packaging, you want to install the program in a dedicated directory. This is possible using the DESTDIR variable.

make DESTDIR="/path/to/my/package/directory" install

Packager tip: If you package ACMEd in a way it does not run as root, you might want to create another package that provides the Polkit rule file located in the contrib/polkit directory. This package should depends on both acmed and Polkit.

Frequently Asked Questions

Why this project?

After testing multiple ACME clients, I found out none of them supported all the features I expected (see the key features above). It may have been possible to contribute or fork an existing project, however I believe those project made architectural choices incompatible with what i wanted, and therefore it would be as much or less work to start a new project from scratch.

Is it free and open-source software?

Yes, ACMEd is dual-licensed under the MIT and Apache 2.0 terms. See LICENSE-MIT.txt and LICENSE-APACHE-2.0.txt for details.

The man pages, the default hooks configuration file, the CHANGELOG.md and the README.md files are released under the GNU All-Permissive License.

Can it automatically change my server configuration?

Short answer: No.

Long answer: At some points in a certificate's life, ACMEd triggers some hooks in order to let you customize how some actions are done, therefore you can use those hooks to modify any server configuration you wish. However, this may not be what you are looking for since it cannot proactively detect which certificates should be emitted since ACMEd only manages certificates that have already been declared in the configuration files.

How should I configure my TLS server?

You decide. ACMEd only retrieve the certificate for you, it does not impose any specific configuration or limitation on how to use it. For the record, if you are looking for security recommendations on TLS deployment, you can follow the ANSSI TLS guide (the english version might not be the latest version of this document, if possible use the french one).

Is it suitable for beginners?

It depends on your definition of a beginner. This software is intended to be used by system administrators with a certain knowledge of their environment. Furthermore, it is also expected to know the bases of the ACME protocol. Let's Encrypt wrote a nice article about how it works.

It doesn't work!

ACMEd releases do work properly. Knowing that new users tend to shoot themselves in the foot with hooks, you might want to check those before considering moving away to a different software. Files path and permissions are very common traps, you definitely want to check those.

By the way, don't forget to change the log verbosity using --log-level trace.

Should ACMEd run as root?

Running ACMEd as root is the simplest configuration since you do not have to worry about access rights, especially within hooks (Eg: restart a service).

However, if you are concerned with safety, you should create a dedicated user for ACMEd. Before doing so, please consider the following points:

  • Will my services be able to read both the private key and the certificate?
  • Will the ACMEd user be able to execute the hooks?

The last one could be achieved using either sudo or Polkit (see the contrib/polkit directory).

Why is there no option to run ACMEd as a specific user or group?

The reason some services has such an option is because at startup they may have to load data only accessible by root, hence they have to change the user themselves after those data are loaded. For example, this is wildly used in web servers so they load a private key, which should only be accessible by root. Since ACMEd does not have such requirement, it should be run directly as the correct user.

How can I run ACMEd with systemd?

The contrib/systemd contains examples of a service file as well as a sysusers.d and a tmpfiles.d file. Those files might need adjustments in order to work on your system (e.g. paths, user, group,...), but it's probably a good starting point.

Does ACMEd uses any threading or parallelization?

Yes, ACMEd is asynchronous and uses a multi-thread runtime. In order to check the number of threads, you may run the following command:

ps -T -p "$(pgrep acmed)"

Can I use the same account on different endpoints?

Yes, that is possible. However, you should aware that some certificate authorities (e.g. Let's Encrypt) have policies restricting the use multiple accounts. Please check your CA's documentation.

Why is RSA 2048 the default certificate key type?

Short answer: it is sufficiently secured, has good performances and is wildly supported.

Before choosing a different algorithm for your certificate's signature, you might want to consider all of those three points.

  • For security, you may refer to the table 2 of the NIST SP 800-57 Part 1.
  • For performances, you can launch the following command on your machine: openssl speed rsa2048 rsa3072 rsa4096 ecdsap256 ecdsap384 ecdsap521 ed25519 ed448. Your server will be affected by the signature performances and the clients connecting to it will be affected by the verification performances.
  • Nowadays, every client support ECDSA. Therefore, unless you have very specific requirements, you can safely use it. At time of writing, EdDSA certificates are not yet supported, but it might become a thing in the future.

Currently, security and client support aren't the main concerns since every possible type of certificates is good enough on those two points. The performances clearly favors ECDSA P-256, Ed25519 and RSA 2048. The later has been chosen as the default because it's the most wildly used as Certification Authorities root and intermediate certificates. This choice may change in favor of ECDSA since Let's Encrypt issued a full ECDSA certificates chain.

Why is ECDSA P-256 the default account key type?

RFC 8555 section 6.2 defines ECDSA P-256 as the only account key type that any ACME servers must implement. It is therefore the best choice for the default value.

Why can I chose the CSR's digest type but not the certificate's?

Well, you sign the CSR, so obviously you can chose which digest to use. However, the certificate is signed by the certificate authority, so its digest choice is up to your CA. I agree that being able to chose the CSR's digest type is of low importance, sorry if it gave you false hopes about the certificate.

acmed's People

Contributors

breard-r avatar chmodas avatar clarfonthey avatar dbrgn avatar dependabot-preview[bot] avatar eduardosm avatar jcgruenhage avatar jpastuszek avatar magiclen avatar mateusz834 avatar nka11 avatar rzerres avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

acmed's Issues

Runtime failure on 32-bit platforms on musl 1.2.0+

Looks like fix introduced in 220f580 now emits a warning, due to rust-lang/libc#1848 and the test added along with the fix fails on Alpine Linux edge:

---- tests::certificate::cert_expiration_date_past stdout ----
thread 'tests::certificate::cert_expiration_date_past' panicked at 'called `Result::unwrap()` on an `Err` value: Error { message: "error:0D0D90AD:asn1 encoding routines:ASN1_TIME_adj:error getting time:crypto/asn1/a_time.c:330:" }', acme_common/src/tests/certificate.rs:179:37
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

Stop using a deprecated function when creating a certificate in tacd

The function acme_common::crypto::openssl_certificate::gen_certificate() uses X509Extension::new which is deprecated by the openssl crate since version 0.10.51.

Currently, the extensions module does not provide the structure for the acmeIdentifier extension introduced by RFC 8737, leaving no other choice than using X509Extension::new_from_der.

The use of deprecated function warning has been silenced, however it is not a long-term solution. The call to X509Extension::new should be replaced by something else.

Improve error messages

When starting acmed like this:

acmed -f --log-stderr --log-level debug

...I get the following error:

Error: IO error: Permission denied (os error 13)

I'm sure I'll find out what the reason is, but the error message could be improved, to say where the permission fails. Is it the PID file? The accounts directory? Something else?

EdDSA support (Ed25519 and Ed448) with OpenSSL

The problem

OpenSSL supports EdDSA since version 1.1.1, so in theory ACMEd can use it to support Ed25519 and Ed448 certificates and account keys.

However, if certificates are not a problem, account keys are because OpenSSL's EdDSA support is not complete yet:
The ACME protocol requires that account keys be exported in the JWK (JSON Web Keys) format. To do so, we uses bindings to OpenSSL's EVP_PKEY_get1_* functions (EVP_PKEY_get1_RSA for RSA and EVP_PKEY_get1_EC_KEY for ECDSA). Unfortunately, OpenSSL does not currently provides the EVP_PKEY_get1_ED25519 and EVP_PKEY_get1_ED448 functions we need. Those functions has recently been added in OpenSSL (openssl/openssl@7c664b1) and should be available in version 3.0.0, which has not been released when this issue has been created.

Having those functions coming with OpenSSL 3.0.0 is a thing, but having bindings to those functions is a different topic. Although the people behind the openssl crate already started to work on OpenSSL 3.0.0 support (sfackler/rust-openssl#1263), bindings for those new EVP_PKEY_get1_* functions are yet to be done (sfackler/rust-openssl#1334).

On ACMEd's side, the EdDSA has already implemented wherever possible and is subject to conditional compilation and therefore not activated. I do not plan to release partial EdDSA support: either it is supported for both certificates and account keys or not supported at all. However, Ed25519 and Ed448 are separated: one can be activated without the other.

The plan

So, the plan to support EdDSA in ACMEd is the following :

  1. ✔️ Implement EdDSA support in ACMED wherever possible.
  2. 🛑 Wait for OpenSSL 3.0.0 to be released.
  3. 🛑 Have bindings to EVP_PKEY_get1_ED25519 and EVP_PKEY_get1_ED448 implemented in the openssl crate.
  4. 🛑 Use those bindings in ACMEd and activate Ed25519 and Ed448 when compiling against OpenSSL 3.0.0 or later.

What can you do to help?

Contributing to the openssl crate in order to add bindings to EdDSA deserializers seems to be the most effective (and only?) way to help ACMEd on this topic.

Race condition when requesting multiple certificates on the same non-existent account

If several certificates uses the same account and this account doesn't exists yet, they will both try to create it, resulting in a race condition. Hence, most certificates requests will fail at the first call, but will succeed in the next one (1 hour later or manual restart).
This faulty behavior has been introduced in version 0.6 with parallel certificate renewals. Since it is not blocking and we are in early development, it will not be fixed until version 0.7.

Expose file paths of cert and private key to post-operation hook

Use case: As a large provider with many certificates, we want to run acmed in a centralized way, and after a new certificate has been ordered successfully, we want to distribute the chain and key to the clients. Right now, we'd need to guess the file locations based on the identifiers and the key type, but having the locations exposed as variables would be a lot more robust.

Make {{proof}} available in base64url format, for challenge-tls-alpn-01.

The ualpn daemon from uacme can pass through connections on port 443. To use it, I need to have {{proof}} available in a different format.

uacme/ualpn: https://github.com/ndilieto/uacme#tls-alpn-01-challenge-support
man page for ualpn, with examples: https://ndilieto.github.io/uacme/ualpn.html

Need the proof encoded in base64url format, when using challenge-tls-alpn-01.
Having it available as {{proof_b64u}} or some other identifier would make it work.

Right now I make this work by using a script to crop and reencode {{proof}}, and run ualpn. But would like to use ualpn directly.

[[hook]]
name = "ualpn-auth"
type = ["challenge-tls-alpn-01"]
cmd = "/etc/acmed/fix-ualpn.php"
stdin_str = "auth {{identifier_tls_alpn}} {{proof}}"

[[hook]]
name = "ualpn-auth-need-patch"
type = ["challenge-tls-alpn-01"]
cmd = "/usr/bin/ualpn"
stdin_str = "auth {{identifier_tls_alpn}} {{proof_b64u}}"

[[hook]]
name = "ualpn-unauth"
type = ["challenge-tls-alpn-01-clean"]
cmd = "/usr/bin/ualpn"
stdin_str = "unauth {{identifier_tls_alpn}}"

[[group]]
name = "ualpn"
hooks = [
    "ualpn-auth",
    "ualpn-unauth",
]

script:

#!/usr/bin/php
<?php

function base64url_encode($data) {
  $b64 = base64_encode($data);
  $url = strtr($b64, '+/', '-_');
  return rtrim($url, '=');
}

$line = trim(fgets(STDIN)); // reads one line from STDIN
$lines = explode(" ", $line);

$len = strlen("1.3.6.1.5.5.7.1.31=critical,DER:04:20");
$cert = substr($lines[2], $len);
$cert = str_replace(":", "", $cert);
$cert = hex2bin($cert);
$lines[2] = base64url_encode($cert);

$fixed = vsprintf("%s %s %s\n", $lines);

$ualpn = popen("/usr/bin/ualpn", "w");
fwrite($ualpn, $fixed); // pipe fixed string to ualpn
fclose($ualpn);

Lock the dependencies by committing the Cargo.lock

Why this issue?

Recently, there has been a lot of supply-chain attacks (1, 2, 3). While the most notorious are part of the JavaScript ecosystem, such an attack is conceivable in the Rust ecosystem.

Because ACMEd does not commit the Cargo.lock file, it is vulnerable to such an attack from one of its dependencies. This issue will be closed when this is solved.

What does committing the Cargo.lock implies?

Locking the dependencies is not a trivial job. It comes with two major sources of work:

  1. create more frequent releases in order to keep up with dependencies improvement
  2. analyze the changes in all dependencies

When will it come to ACMEd?

Currently, ACMEd is still in a development phase and subject to breaking changes. Most of its improvement comes with little to no warning, hence a little chaos.

My current and personal point of view on this matter is that ACMEd should only lock the dependencies once it reaches the first stable version. Therefore, all version 0.x.y will not include the Cargo.lock file, but versions 1.0.0 and later will include it.

Meanwhile

Those who whishes to package ACMEd for their system may want to enforce dependencies lock. While ACMEd is still in development, those people may create and include their own Cargo.lock file. The counterpart of doing so is that you would have to maintain this file yourself, and therefore be responsible for all the subsequent work.

Good news: ACMEd is starting to be quite mature and therefore the first stable version should not be in an eternity.
Bad news: before declaring ACMEd stable, I really wish to refactor a few things, which may still take some time, depending on my personal life.

--pid-file and foreground dosn't work toghether

Hello,

I'm actually trying to write an init script for FreeBSD for acmed 0.6.0 :

#!/bin/sh

# PROVIDE: acmed
# REQUIRE: LOGIN
# KEYWORD: shutdown

# Add the following lines to /etc/rc.conf to enable `acmed':
#
# acmed_enable="YES"
#

. /etc/rc.subr

name="acmed"
rcvar=acmed_enable

command="/usr/local/bin/acmed"
pidfile="/var/run/$name.pid"

# read configuration and set defaults
load_rc_config "$name"
: ${acmed_enable="NO"}
: ${acmed_configfile:="/usr/local/etc/acmed/$name.toml"}

required_files="${acmed_configfile}"

command_args="--pid-file ${pidfile} --config ${acmed_configfile} --log-syslog --log-level info --foregroung"

run_rc_command "$1"

the --foregroung option seems to doesn't work with --pid-file option :

$ sudo service start acmed
Starting acmed.
error: The argument '--foregroung' cannot be used with '--pid-file <FILE>'

USAGE:
    acmed --config <FILE> --foregroung --log-level <LEVEL> --pid-file <FILE> --log-syslog

For more information try --help
/usr/local/etc/rc.d/acmed: WARNING: failed to start acmed

It is normal the normal behavior or a bug ?

Best regards,
Yan

crypto and TLS implementation support

Hi, this looks like a very interesting project! I'm thinking about building ACME support into a project for which I would like to use rustls (which relies on ring). Would you be open to changes that allow the use of ring and rustls as an alternative to openssl?

README clarification on key sizes

The README why-is-rsa-2048-the-default section makes two statements that could benefit a user a bit more to clarify.

It is not obvious at the first sight, but RSA 4096 is NOT twice more secure than RSA 2048. In fact, it adds a lot more calculation while providing only a small security improvement.

Appears to be related to the term security level? I came across an article that explained it a bit more clearly here:

In this regard, a common RSA 2048-bit public key provides a security level of 112 bits. However, ECDSA requires only 224-bit sized public keys to provide the same 112-bit security level.

In the next common level of 128 bits, RSA requires a 3072-bit key, while ECDSA only 256 bits.

A similar comparison of security level without comparing to ECDSA is presented on Wikipedia:

1024-bit RSA keys are equivalent in strength to 80-bit symmetric keys, 2048-bit RSA keys to 112-bit symmetric keys, 3072-bit RSA keys to 128-bit symmetric keys, and 15360-bit RSA keys to 256-bit symmetric keys.

This isn't an area I understand that well, but I thought increasing the bits still has a notable impact on computational costs? (to attack)

2(1-bit), 4, 8, 16 ,32, 64 (8 bits), the amount of values doubles with each additional bit added. The above information doesn't clarify symmetric key size equivalent bits (which I've referred to as security level above, but is what these bit values are meant to be representing?), but we can see that between 2048 and 3072 RSA sizes, 16 bits should be well over 2x the amount of computation to break?

This StackExchange answer shows RSA-4096 should be ~150 bits, with a delta of ~40 bits. This article seems to address the topic well in regards to understanding the statement the README has(although I don't quite know if the 2x strength you mention implying it's less of an increase is valid?). Perhaps both, or one of these links would be suitable to add to the README?


If so, using RSA 4096 in the final certificate will not add any additional security since a system's global security level is equal to the level of its weakest point.

This also had me curious, and I'm not sure if it's entirely valid either? See this StackExchange discussion.

I think you're referring to security here in terms of CA certs in the chain, but not the end-entity cert strength which is still useful for encryption security of messages being exchanged, whereas the other certs are for validation, but breaking them won't decrypt any prior recorded encrypted messages.

The link seems to indicate that there is still benefit (security wise) for end-entity certs that have larger key sizes(possibly not for DHE though where it's only used for signing but not in the key exchange according to one of the comments there).

Build failure on 32-bit platforms

From this i386 log:

error[E0308]: mismatched types
   --> acme_common/src/crypto/openssl_certificate.rs:111:39
    |
111 |         let now = Asn1Time::from_unix(timestamp)?;
    |                                       ^^^^^^^^^ expected `i32`, found `i64`
    |
help: you can convert an `i64` to `i32` and panic if the converted value wouldn't fit
    |
111 |         let now = Asn1Time::from_unix(timestamp.try_into().unwrap())?;
    |                                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Maybe the timestamp should not hardcode i64..

Compliance with Let's Encrypt Integration Guide

Let's Encrypt has an integration guide for hosting providers and client software that's meant to be used with Let's Encrypt that has a few things that they want those users to implement, and most clients do fail to implement those in one way or another.

The integration guide is available over at https://letsencrypt.org/docs/integration-guide/. I'll try to summarize the requirements below, and whether acmed is implementing it in a compliant way, if that requirement is applicable at all. I'll not add a checkmark iff there is a recommendation that is definitely applicable to acmed and which acmed doesn't apply properly.

  • One Account or Many?: Let's Encrypt recommends to use a single account. While it's required for acmed to support multiple accounts, because different providers could require different key algorithms for example, the documentation could be improved here, to recommend sticking to a single account. Even worse than not recommending to use a single account though, the README even encourages using multiple accounts (at least when using multiple endpoints). Edit/Clarification: the recommendation from LE is just about larger hosting providers, and the recommendation from the README is just about different endpoints, so my initial assessment here was off.
  • Multi-domain (SAN) Certificates: This is just some text about what drawbacks it might have to have multiple certificates vs one certificate with multiple domains, not really applicable to acmed
  • Storing and Reusing Certificates and Keys: acmed provides hooks for storing files and reusing them properly, so this is definitely okay. What Let's Encrypt says here is to not generate certificates in volatile environments such as short lived containers, which is not really applicable to acmed.
  • Picking a Challenge Type: As acmed supports all current challenge types, this is in good shape right now.
  • Central Validation Servers: Not really applicable to acmed, as this depends more on the deployment than the acme client.
  • Implement OCSP Stapling: Definitely not applicable, this is the responsibility of the TLS server.
  • Firewall Configuration: Not applicable
  • Supported Key Algorithms: Except for 3072 bit RSA, all algorithms supported by Let's Encrypt are supported by acmed as well.
  • HTTPS by default: Not applicable, depends on the service being secured with TLS, not the acme client.
  • When to Renew: Let's Encrypt recommends that you renew certificates at a third of their total lifetime. With the current 90-day certificates, this means 30 days before expiration. What acmed does right now is 3 weeks before the end of the expiration, so 21 days. This is not the only way that things could be improved here though: Especially if you're running very big deployments with thousands of certificates, you can reduce the cost for Let's Encrypt by spacing out the renewals randomly. My suggestions here would be to allow setting a range here, instead of a concrete value, and to take a random time between the start of the range and the end of the range for the renewal, so something like 35 days until expiry and 30 days until expiry, and then it chooses to do it at 33.78 days before expiry for example. With the current structure of how acmed handles this though, it's not that easy to do this.
  • Retrying failures: What Let's Encrypt states here is that renewal failures should not be treated as fatal errors, but that those should be gracefully retried with an exponential backoff. acmed is pretty far off here (as far as I can tell, I'm not quite sure actually). As far as I can tell, acmed will check each certificate for whether it needs renewal once per hour (at least that's the default) and not do any sort of exponential backoff. Some specific errors are also treated as recoverable, which are then retried 20 times with 1s delay in between. Whether retrying recoverable errors in such quick succession is in line with what Let's Encrypt wants aside, the more important thing would be to implement some for of exponential backoff. Their suggested schedule is retrying after 1 minute, then 10 minutes, then 100 minutes and each subsequent retry after 1 day. I think it'd be in the spirit of acmed to have this as something that's configurable per endpoint, with defaulting to the schedule suggested by Let's Encrypt.

Summarizing: acmed is already doing a lot of things right here, with the documentation on accounts being one thing that could be improved and scheduling of renewals and retrying failures being the other thing.

Exponential backoff for retrying renewals

Splitting this out of #71.

Let's Encrypt recommends that renewal failures should not be treated as fatal errors, but that those should be gracefully retried with an exponential backoff. acmed is pretty far off here (as far as I can tell, I'm not quite sure actually). As far as I can tell, acmed will check each certificate for whether it needs renewal once per hour (at least that's the default) and not do any sort of exponential backoff. Some specific errors are also treated as recoverable, which are then retried 20 times with 1s delay in between. Whether retrying recoverable errors in such quick succession is in line with what Let's Encrypt wants aside, the more important thing would be to implement some for of exponential backoff. Their suggested schedule is retrying after 1 minute, then 10 minutes, then 100 minutes and each subsequent retry after 1 day. I think it'd be in the spirit of acmed to have this as something that's configurable per endpoint, with defaulting to the schedule suggested by Let's Encrypt.

This again is something that's tricky to implement with the current architecture, but should become fairly trivial with the async rewrite.

Use a different template engine

Overview

This issue tracks the criteria the template engine used to render hooks should have and which are eligible. At time of writing, none is eligible, hence the current one is kept. Please submit other crates if they are worth investigating.

Features

Dynamic templates

The crate does not require to have the templates at compile time. Hooks are defined by the user, therefore their templates are not available when ACMEd is compiled.

Jinja2-like

I really like the Jinja2 style. Not all features are required, only the most basics (variables display, basic tests and loops).

Does not have many dependencies

See issue #1
Dependencies of dependencies are counted too.

Method to count dependencies:

  • Create a new empty project using cargo new --bin test_<lib_name>
  • Add the lib to the Cargo.toml file
  • Run cargo build
  • Run cargo tree | grep -v '*' | wc -l | sed 's/$/-2/' | bc

The minus 2 is intended to remove the project and the library itself from the count.

  • ✔️ : n ≤ 5
  • ✅ : 5 < n ≤ 10
  • ⚠️ : 10 < n ≤ 30
  • ❌ : 30 < n

Does not depends on pest

ACMEd already uses nom and should not depend on a second parser.

Comparison table

Crate Dynamic templates Jinja2-like Dependencies No pest Comment
askama ✔️ 33 ❌ ✔️ Impossible to use in ACMEd
tera ✔️ ✔️ 89 ❌ Would use if fewer dependencies and pest wouldn't be one of them
liquid ✔️ ✔️ 115 ❌ Would use if fewer dependencies and pest wouldn't be one of them
templar ✔️ ✔️ 43 ❌ Current version (0.5) unusable because of debug print
tinytemplate ✔️ ⚠️ 10 ✔️ ✔️ Quite simple and effective
ramhorns ✔️ 19 ⚠️ ✔️ Would use if it was a Jinja2-like and had a little fewer dependencies
handlebars ✔️ 45 ❌ Currently used

Compilation instruction on Alpine

Hi,

i come with a good first issue, i want to list necessary packages to compile Acmed on Alpine Linux in README file;

  • openssl-dev to avoid compilation problem with openssl crate
  • alpine-sdk similar to debian-essentials
  • git of course
  • rust
  • cargo

Cargo.lock not updated/committed for 0.22.0

trying to build with --locked reports an error, because the version wasn't bumped in the lockfile:

error: the lock file acmed-0.22.0/Cargo.lock needs to be updated but --locked was passed to prevent this
If you want to try to generate the lock file without accessing the network, remove the --locked flag and use --offline instead.

Certificate meta-information file

There will be a point where it is necessary to store informations about a certificate that do not belong to the configuration. For example, this may be necessary to implement STAR certificates (RFC 8739). I have not read the full specification, but from the few I read it seems necessary to keep track of some state on the client side, at least in order to know if it's a "traditional" certificate or a STAR.

Where to store this file?

There is multiple possibilities, the one I find the more promising being:

  • one file per certificate stored along with the private key and the certificate
  • one file per certificate stored in a dedicated directory
  • one unique file stored I don't know where which contains the entries for all the certificates

I would prefer the one file per certificate approche since it would be coherent with what has already be done for the accounts and is easier for people to manage.
Using this approche, the file_name_format will be used with:

  • file_type set to something like meta
  • ext set to bin (if bincode is chosen)

Which file format?

The file format itself should be the same one as the account's. Currently that is the binary format provided by the bincode crate, however it might evolve if necessary.

Because the file's content may evolve in the future, this should be taken into consideration from the beginning. I don't remember how bincode allows this, but I think the following method should work: try to load the latest structure, if it fails try the previous ones until it matches and upgrade to the latest.

When?

I would like to limit new features before the async rewrite, however it doesn't prevent discussing this future improvement.

Scheduling renewals

Splitting this out of #71.

acmed is currently renewing certificates with a fixed offset from the expiry of the certificate. It's configurable, but defaults to 3 weeks. The recommendation from Let's Encrypt here is a third of the certificate lifetime, so that means 30 days before expiry. The default should therefore probably be changed here.

Another thing to do about scheduling renewals, to help acme providers with load spikes, is adding some randomization to the renewal time. Let's Encrypt suggests to space out certificate renewals by renewing some certificates a few days early. This is difficult to implement with the current architecture, but should be nearly trivial to implement once the async rewrite is done.

Reduce the executable size

The use static linking along with multiple dependencies which have their own dependencies makes the final executable completely bloated (8.8Mo in release mode and 58Mo in debug mode). Using many dependencies is very useful at the beginning in order to quickly have a working prototype. However, this situation is unacceptable and has to be fixed before the 1.0 release.

Reasons to fix this:

  1. event-stream incident
  2. left-pad affair
  3. waste of resources
  4. credibility issues

Exposing prometheus/opentelemetry compatible metrics.

Related to #80 and kinda also #24, it'd be helpful to have better insight into what acmed is doing, by exposing certain metrics on it. As everything in acmed is related to http requests in one form or another, it's probably enough to expose counters for http requests, including labels for

  • endpoint
  • acme resource
  • path <- this one should probably be optional, due to it blowing up the size of the metrics collected when there is a lot of certificates
  • status code

In addition to that, it might also make sense to export some metrics on a few other areas, like the hooks and rate limit state, but I think those metrics are less useful than the ones for the http requests.

While it's also possible to expose certain metrics for the certificates themselves, I'm not sure whether that makes sense, as it's easily possible to do externally (see https://gist.github.com/jcgruenhage/8ef2f2fbc0af48189ee4585fb8054e40 for example), where as these are metrics only available to acmed itself.

Use more external crates

ACMEd uses custom code for some things that are already provided by external crates.

  • There are multiple crates providing jws and jwk. josekit is one of those also using openssl. Can josekit replacing the existing jws and jwk code?
  • There are a lot of crates that provide config parsing. Can some of those (config, for example) replace the custom config parser (and layering) of ACMEd?

Pure Rust implementation?

Hi. Is it possible to provide a pure Rust-based generation of certificates (that avoids openssl)? There seem to be libraries/apps that generate certificates using Rust already: https://crates.io/crates/certainly

Avoiding openssl makes sense for users who are worried about openssl vulnerabilities and [unnecessary] complexity.

Using the pure-rust implementation could then be hidden under a feature flag if openssl is desired to be used by default.

Announcement : 2020.02.29 CAA Rechecking Bug

What happened

As you may already know, a bug in the CAA rechecking part of Boulder has been found. Hence, Let's Encrypt is revoking every certificate emitted when the bug has been known to be present, which means (almost ?) all certificates.

How does ACMEd handles this?

Force-renew certificates based on various parameters is not yet supported. In the future, it will be included in a tool, acmectl, which role will be to manage the deamon.

What should I do?

⚠️ Please read the whole procedure before taking any action. Especially the last warning.

The recommended procedure is to manually delete your certificates so ACMEd will create and request signing for new ones. Depending on your archiving policy, deleting certificates could be done in various ways. The most commons are:

  • Move the certificate file to an archive directory.
  • Use git rm and then git commit.
  • Simply rm the file if you do not keep any archive.

Only the certificate files (.crt.pem) needs to be removed. You may also remove the certificate's private key (.pk.pem) but this is not necessary.
You should not remove your accounts keys (.priv-key.pem and .pub-key.pem) as those are not affected at all.

Onece your certificates has been removed, you can either restart ACMEd or wait for it to automatically detect that the files are missing. If you chose to wait, it could last up to an hour.

⚠️ Attention!

Some simplistic configurations could lead to errors. The most common case if when the post-operation hook contains a command which restarts a service that uses several certificates (Eg: a web server). In such case, this post-operation hook will fail for every certificate but the last since the service restart will fail due to the missing certificates.
If you are in such situation, you should remove the certificates one by one and restart ACMEd after each deletion.

Use as a library

Hi,

Can this be used as a library to set up, generate the challanges and download the certificates?
I used to use onur's acme package, but it doesn't support acme v2 unfortunately.

I have my own (rust) server running, and I made calls to the library to know which challanges to serve when the boulder server was verifying the challanges, and after that was done, i made another call to the library to get the signed certificate data.

It seems acmed is a standalone app, but can it be used as a library as well?

Create a project logo

A logo for ACMEd would be a nice to have. Because I don't have any graphical talent, all I can do is to write the specifications:

  • Flat design
  • No cliché as the main component: padlocks, keys and shields are overused and therefore should be limited as a second-level component
  • References to Wile E. Coyote and the Road Runner are a plus
  • Two single-color versions in grayscale (a bright and a dark one)
  • SVG format

In addition to those requirements, it should be released under one of the following free licenses:

Fine grained rate-limits

acmed currently allows setting rate limits on endpoints, that specify how many
requests can be made per interval. It does not take the kind of request that is
made to the endpoint into consideration, which means that the rate limits
actually set by CAs (see https://letsencrypt.org/docs/rate-limits/) can not
really be represented by this.

My suggestion is to extend the current mechanism by giving rate-limits an
optional scope. This scope can be the path in the HTTP request, a path prefix,
an ACME resource (as listed in the directory response), etc. A request is then
only blocked, if there is a currently blocking rate-limit on that endpoint, that
matches this request. The specifics on how to model that and how to expose the
configuration for this aren't trivial, but I think if at least path (prefixes)
and ACME resources can be used as a scope, a few important rate limits can now
be set properly, that couldn't be set before, like the one for the newOrder
resource (300/3h), a shared one for the ACME resources newNonce, newAccount,
newOrder and revokeCert (20/1s), and the one for /directory
and anything under /acme (40/1s).

Even after that, there is a bunch of limits that this doesn't catch, but it'd be
a big improvement over the status quo.

Allow for not using any pidfile

It's pointless to require a pidfile with any modern process supervisor, or when just running acmed locally in a terminal for debug purposes, especially that it just serves as a lockfile with extra steps.

Functional/integration testing

ACMEd already include a few unit tests within its code. Although that's a good thing, it's also insufficient. One way to improve it is to create functional/integration tests (I don't know/care about the exact differences).

Currently, I have a local directory containing a few configuration files for pebble and ACMEd and I run them once in a while in order to tests is everything works. However, there is no automation at all and those files have a few issues that requires me to comment/uncomment some parts in order to run some tests. It would be nice if a similar process would be included in the repository and, maybe automated.

Functionalities

A single command should run those tests. A bash or sh script should do.

All the outputs (certificates, keys, etc.) should be located in a directory, maybe something like /tmp/acmed-test. This directory should be cleaned at the beginning of the tests so the files stays afterwards in order to be manually checked.

The goal is to test all possible ACMEd features (challenges, key types, internationalized domain name and so on), however most tests may be added after the script itself is done.

Bonus: run it as a GitHub action

Which domain name?

RFC 2606 reserves a few top level DNS names for specific purposes and recommends to use the .test TLD for testing. Therefore, all domains included in those tests should use the .test TLD.

I would also recommend to use exclusively the acmed.test domain name as well as any other subdomain that might help.

External tools

Testing ACMEd requires a few external tools:

  • an ACME server: pebble is a good choice, if not the only
  • an authoritative DNS server: dnsmasq should be easy ton configure so it can resolve the acmed.test domain name, I don't know if it's possible to dynamically add entries so it could help validating with the dns-01 challenge
  • an HTTP server: Python's http.server or lighttpd should do the trick for the http-01 challenge, however I don't know for tls-alpn-01
  • some certificate checking tool: openssl x509 should do the job although it's not always easy to parse

Implementation consideration

Daemons

All daemons should be killed at the end of tests. This might require to store their respective PID.

Pebble

As far as I know, Pebble can only be run with or without the external account requirement. Therefore, two Pebble instance should be run.

Pebble should always run in strict mode.

ACMEd

Currently, an easy way to run ACMEd for such tests is the following:

cargo run --bin acmed -- --config "/path/to/acmed-config.toml" --root-cert "/path/to/pebble/root_cert.pem" --no-pid-file --foreground --log-stderr --log-level trace

Include config directories

Listing all files in include can be cumbersome. It would be more easy if include supported directories or globs instead of single files.

Unable to renew the certificate: Unsupported EC key

I just started with default configuration where I have added the certificate with (default) rsa2048 algorithm but I got this error.
It looks like the account file is using EC type that is not supported?

$ target/release/acmed -f --log-stderr --log-level trace -c test.toml
[2019-11-14T17:01:10Z INFO  acmed::config] Loading configuration file: test.toml
[2019-11-14T17:01:10Z TRACE acmed::certificate] crt-1: Testing file path: /tmp/acc/eHh4.priv-key.pem
[2019-11-14T17:01:10Z TRACE acmed::certificate] crt-1: Writing file "/tmp/acc/eHh4.pub-key.pem"
[2019-11-14T17:01:10Z TRACE acmed::certificate] crt-1: Writing file "/tmp/acc/eHh4.priv-key.pem"
[2019-11-14T17:01:10Z INFO  acmed::certificate] crt-1: Account xxx created.
[2019-11-14T17:01:10Z TRACE acmed::certificate] crt-1: Testing file path: /tmp/certs/hello.xxx.net_rsa2048.pk.pem
[2019-11-14T17:01:10Z DEBUG acmed::certificate] crt-1: certificate does not exist: requesting one
[2019-11-14T17:01:10Z DEBUG acmed::certificate] crt-1: GET: https://acme-staging-v02.api.letsencrypt.org/directory
[2019-11-14T17:01:11Z DEBUG acmed::certificate] crt-1: HEAD: https://acme-staging-v02.api.letsencrypt.org/acme/new-nonce
[2019-11-14T17:01:12Z TRACE acmed::certificate] crt-1: New nonce: 0001V2hQXxOcbr6ly8PN0IpUdfSiV8F6_OFScmN1cOp0b3E
[2019-11-14T17:01:12Z TRACE acmed::certificate] crt-1: Reading file "/tmp/acc/eHh4.priv-key.pem"
[2019-11-14T17:01:12Z WARN  acmed::certificate] crt-1: Unable to renew the certificate: Unsupported EC key

I have tried this on two systems, one with OpenSSL 1.0.2k-fips 26 Jan 2017 and other that has LibreSSL 2.9.2 with same result.

$ target/release/acmed --version
ACMEd 0.6.1 x86_64-unknown-linux-gnu

Compiled with:
  LibreSSL 2.9.2
  http_req 0.5.3

My configuration:

[global]
accounts_directory = "/tmp/acc"
certificates_directory = "/tmp/certs"

[[rate-limit]]
name = "LE min"
number = 20
period = "1s"

[[endpoint]]
name = "letsencrypt v2 staging"
url = "https://acme-staging-v02.api.letsencrypt.org/directory"
rate_limits = ["LE min"]
tos_agreed = true

[[account]]
name = "xxx"
email = "[email protected]"

[[certificate]]
account = "xxx"
endpoint = "letsencrypt v2 staging"
domains = [{ challenge = "dns-01", dns = "hello.xxx.net" }]
hooks = []

Providing static linked binaries

Hey @breard-r

Thank you for your effor in acmed. It's a nice peace of software. However, can you please consider providing static linked binaries (including libc and openssl)? I would like to use acmed on CentOS6 and there is some problem in OpenSSL version (since i'm a total rust noob i can't even install rust tools env).

PID file + foreground

It seems that when running acmed in foreground using --foreground, a PID file is still created. is this required?

(Furthermore, it appears that the PID file is not cleaned up when the process stops with an error on parsing the config file...)

Random failure in podman deployment

Hello,

I deployed ACMED in a podman environment.
It works well and generates a cert at launch if it does not exist. Unfortunately it
fails when trying to determine renew period with randomness...

[2023-11-10T16:23:39Z TRACE acmed::main_event_loop] Adding certificate: nginx_default_rsa4096
[2023-11-10T16:23:39Z DEBUG acmed::certificate] certificate "nginx_default_rsa4096": checking for renewal (identifiers: )
[2023-11-10T16:23:39Z TRACE acmed::storage] certificate "nginx_default_rsa4096": testing file path: /var/certs/nginx_default_rsa4096.pk.pem
[2023-11-10T16:23:39Z TRACE acmed::storage] certificate "nginx_default_rsa4096": testing file path: /var/certs/nginx_default_rsa4096.crt.pem
[2023-11-10T16:23:39Z TRACE acmed::storage] certificate "nginx_default_rsa4096": reading file "/var/certs/nginx_default_rsa4096.crt.pem"
[2023-11-10T16:23:39Z DEBUG acmed::certificate] certificate "nginx_default_rsa4096": certificate expires in 89 days (30 days delay)
thread 'main' panicked at /usr/local/cargo/registry/src/index.crates.io-6f17d22bba15001f/rand-0.8.5/src/rng.rs:134:9:
cannot sample empty range
stack backtrace:
   0:     0x55c70a28324c - std::backtrace_rs::backtrace::libunwind::trace::he43a6a3949163f8c
                               at /rustc/cc66ad468955717ab92600c770da8c1601a4ff33/library/std/src/../../backtrace/src/backtrace/libunwind.rs:93:5
   1:     0x55c70a28324c - std::backtrace_rs::backtrace::trace_unsynchronized::h50db52ca99f692e7
                               at /rustc/cc66ad468955717ab92600c770da8c1601a4ff33/library/std/src/../../backtrace/src/backtrace/mod.rs:66:5
   2:     0x55c70a28324c - std::sys_common::backtrace::_print_fmt::hd37d595f2ceb2d3c
                               at /rustc/cc66ad468955717ab92600c770da8c1601a4ff33/library/std/src/sys_common/backtrace.rs:67:5
   3:     0x55c70a28324c - <std::sys_common::backtrace::_print::DisplayBacktrace as core::fmt::Display>::fmt::h678bbcf9da6d7d75
                               at /rustc/cc66ad468955717ab92600c770da8c1601a4ff33/library/std/src/sys_common/backtrace.rs:44:22
   4:     0x55c70a132c3c - core::fmt::rt::Argument::fmt::h3a159adc080a6fc9
                               at /rustc/cc66ad468955717ab92600c770da8c1601a4ff33/library/core/src/fmt/rt.rs:138:9
   5:     0x55c70a132c3c - core::fmt::write::hb8eaf5a8e45a738e
                               at /rustc/cc66ad468955717ab92600c770da8c1601a4ff33/library/core/src/fmt/mod.rs:1094:21
   6:     0x55c70a27f1ce - std::io::Write::write_fmt::h9663fe36b2ee08f9
                               at /rustc/cc66ad468955717ab92600c770da8c1601a4ff33/library/std/src/io/mod.rs:1714:15
   7:     0x55c70a283034 - std::sys_common::backtrace::_print::hcd4834796ee88ad2
                               at /rustc/cc66ad468955717ab92600c770da8c1601a4ff33/library/std/src/sys_common/backtrace.rs:47:5
   8:     0x55c70a283034 - std::sys_common::backtrace::print::h1360e9450e4f922a
                               at /rustc/cc66ad468955717ab92600c770da8c1601a4ff33/library/std/src/sys_common/backtrace.rs:34:9
   9:     0x55c70a289ce3 - std::panicking::default_hook::{{closure}}::h2609fa95cd5ab1f4
                               at /rustc/cc66ad468955717ab92600c770da8c1601a4ff33/library/std/src/panicking.rs:270:22
  10:     0x55c70a289a2f - std::panicking::default_hook::h6d75f5747cab6e8d
                               at /rustc/cc66ad468955717ab92600c770da8c1601a4ff33/library/std/src/panicking.rs:290:9
  11:     0x55c70a28a1d6 - std::panicking::rust_panic_with_hook::h57e78470c47c84de
                               at /rustc/cc66ad468955717ab92600c770da8c1601a4ff33/library/std/src/panicking.rs:707:13
  12:     0x55c70a28a0b4 - std::panicking::begin_panic_handler::{{closure}}::h3dfd2453cf356ecb
                               at /rustc/cc66ad468955717ab92600c770da8c1601a4ff33/library/std/src/panicking.rs:597:13
  13:     0x55c70a288e46 - std::sys_common::backtrace::__rust_end_short_backtrace::hdb177d43678e4d7e
                               at /rustc/cc66ad468955717ab92600c770da8c1601a4ff33/library/std/src/sys_common/backtrace.rs:170:18
  14:     0x55c70a289e72 - rust_begin_unwind
                               at /rustc/cc66ad468955717ab92600c770da8c1601a4ff33/library/std/src/panicking.rs:595:5
  15:     0x55c70a058893 - core::panicking::panic_fmt::hd1e971d8d7c78e0e
                               at /rustc/cc66ad468955717ab92600c770da8c1601a4ff33/library/core/src/panicking.rs:67:14
  16:     0x55c70a058923 - core::panicking::panic::hd907d42d09324a32
                               at /rustc/cc66ad468955717ab92600c770da8c1601a4ff33/library/core/src/panicking.rs:117:5
  17:     0x55c70a0ce237 - acmed::main_event_loop::MainEventLoop::run::{{closure}}::h5010543ffbdf423e
  18:     0x55c70a0d2fde - acmed::inner_main::{{closure}}::h516ef2296ac78188
  19:     0x55c70a0d173c - acmed::main::hc37f30cf3252ae80
  20:     0x55c70a06c26a - std::sys_common::backtrace::__rust_begin_short_backtrace::h54e4788db0d8db37
  21:     0x55c70a06c691 - std::rt::lang_start::{{closure}}::hf3a7d1b7532d3c6f
  22:     0x55c70a27922b - core::ops::function::impls::<impl core::ops::function::FnOnce<A> for &F>::call_once::hbcc4f8a3f5ada78c
                               at /rustc/cc66ad468955717ab92600c770da8c1601a4ff33/library/core/src/ops/function.rs:284:13
  23:     0x55c70a27922b - std::panicking::try::do_call::he5f117a9e13dadde
                               at /rustc/cc66ad468955717ab92600c770da8c1601a4ff33/library/std/src/panicking.rs:502:40
  24:     0x55c70a27922b - std::panicking::try::h2f3af9afce3a0443
                               at /rustc/cc66ad468955717ab92600c770da8c1601a4ff33/library/std/src/panicking.rs:466:19
  25:     0x55c70a27922b - std::panic::catch_unwind::h6d6c387f38ef05ea
                               at /rustc/cc66ad468955717ab92600c770da8c1601a4ff33/library/std/src/panic.rs:142:14
  26:     0x55c70a27922b - std::rt::lang_start_internal::{{closure}}::h6ca09d5905711415
                               at /rustc/cc66ad468955717ab92600c770da8c1601a4ff33/library/std/src/rt.rs:148:48
  27:     0x55c70a27922b - std::panicking::try::do_call::ha9fd18ea06654a4b
                               at /rustc/cc66ad468955717ab92600c770da8c1601a4ff33/library/std/src/panicking.rs:502:40
  28:     0x55c70a27922b - std::panicking::try::hda5c2a4432362341
                               at /rustc/cc66ad468955717ab92600c770da8c1601a4ff33/library/std/src/panicking.rs:466:19
  29:     0x55c70a27922b - std::panic::catch_unwind::h440f731b142bc235
                               at /rustc/cc66ad468955717ab92600c770da8c1601a4ff33/library/std/src/panic.rs:142:14
  30:     0x55c70a27922b - std::rt::lang_start_internal::hc0b4e50f058f62ce
                               at /rustc/cc66ad468955717ab92600c770da8c1601a4ff33/library/std/src/rt.rs:148:20
  31:     0x55c70a06c686 - std::rt::lang_start::hfb1428766cb0010e
  32:     0x7f506606409b - __libc_start_main
  33:     0x55c70a063ada - _start
  34:                0x0 - <unknown>
Aborted
exit code: 134

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.