go-acme / lego

Let's Encrypt/ACME client and library written in Go
https://go-acme.github.io/lego/
MIT License
7.98k stars 1.02k forks source link

Suggestion: renew else run mode #216

Open benpye opened 8 years ago

benpye commented 8 years ago

It would be very useful to have the ability to renew if possible, otherwise issue the initial certificate. This would be useful when used with Docker as it would allow a container to create and renew certificates on a cron job easily. Additionally, when run with the --days argument it'd be helpful for there to be some way to clearly tell if the certificate has actually been renewed.

xenolf commented 8 years ago

Hey there!

It would be very useful to have the ability to renew if possible, otherwise issue the initial certificate. This would be useful when used with Docker as it would allow a container to create and renew certificates on a cron job easily.

The issue here is that renewal is always possible as long as you do not run into rate limits. By "issue the initial certificate" are you talking about re-downloading the initial certificate?

Additionally, when run with the --days argument it'd be helpful for there to be some way to clearly tell if the certificate has actually been renewed.

What would you suggest?

benpye commented 8 years ago

Renewal doesn't take place if executed with the --days argument right, that's the situation in which I would see this being useful. This is something that letsencrypt.sh .

The only way I can think to get a way to tell if it has been renewed would be to use the return code for the application.

xenolf commented 8 years ago

Well. Renewal only takes place if the number of days to the expiration day is less than the number supplied via --days. The return code has been brought up for this kind of functionality before, but I was not in favor of it because my understanding of return codes is that a non-zero return code indicates failure. In the event of not renewing the certificate when using --days is not an error. But I might need to re-think this stance as there really seems to be no other way.

mholt commented 8 years ago

Maybe exit code 0 = renewed successfully, 1 = no renewal but no error either, and >= 2 is an error...

Although that's less convenient than just a boolean 'did or did not error'.

LukeHandle commented 8 years ago

If error codes are the route taken, avoiding the "standardzed" exit/error codes would probably be a good idea.

What's about a status message to stdout (does current verbose log go to stderr?)? If needs be, a flag that shows no output apart from the status message.

xenolf commented 8 years ago

Yeah. I was more thinking something like 0 for normal execution and 3 if something changed.

jauderho commented 8 years ago

For me, "renew else run" would mean that I could combine both actions into one script.

If cert exist & < 30 days, then renew. If cert does not exist, go through the new cert creation process

Hope that helps. Maybe call it "lego go" =)

oscar-b commented 8 years ago

@xenolf Any news on this? Just ran into the issue myself, since I want to avoid restarting our VPN server if there are no new certs (using --days "30").

From the previously linked page:

The author of this document proposes restricting user-defined exit codes to the range 64 - 113 (in addition to 0, for success), to conform with the C/C++ standard.

matschaffer commented 4 years ago

I ended up doing this as a workaround. Seems to do the trick, but having something baked into lego would definitely be handy.

LEGO_ARGS=(
  "${ACCEPT_TOS_ARG}"
  --path ./lego
  --email "${INPUT_EMAIL}"
  --server "${INPUT_SERVER}"
  "${DOMAIN_ARGS[@]}"
  --dns route53
)

lego "${LEGO_ARGS[@]}" renew || lego "${LEGO_ARGS[@]}" run
datafoo commented 4 years ago

I ended up doing this as a workaround. Seems to do the trick, but having something baked into lego would definitely be handy.

LEGO_ARGS=(
  "${ACCEPT_TOS_ARG}"
  --path ./lego
  --email "${INPUT_EMAIL}"
  --server "${INPUT_SERVER}"
  "${DOMAIN_ARGS[@]}"
  --dns route53
)

lego "${LEGO_ARGS[@]}" renew || lego "${LEGO_ARGS[@]}" run

That is a bad idea that we used to have in nixpkgs: https://github.com/NixOS/nixpkgs/issues/86184. Fixed in https://github.com/NixOS/nixpkgs/commit/cc37d7edd785e70a3a13db5268bd1439afbc1ab2

Related issue: https://github.com/go-acme/lego/issues/1192

erkexzcx commented 3 years ago

acme.sh app already does this...

+1 for merging renew+run into a single issue --days 45 (or set --days default to 45 or smth)...

matschaffer commented 3 years ago

Thanks @datafoo - Pretty sure I got the idea from that same file, so good to get the update. Yeah it'd definitely be nicer to check the expiration first.

Ultimately I ended up just sticking to AWS certs rather than introducing letsencrypt certs.

ambis commented 2 years ago

I hope you don't end up using exit codes. That would have a really bad smell to it, since I've never come across another tool that would report success (or no-op) with an error code.

The way I see this should work is have an option to get the status out as JSON which you could parse easily with jq.

lego --options --json renew | jq .status.was_renewed

As further clarification, you could write all other log output into stderr, and the json into stdout, so pipeing to jq would always work ok.

lingfish commented 3 months ago

I hope you don't end up using exit codes. That would have a really bad smell to it, since I've never come across another tool that would report success (or no-op) with an error code.

I hope they do, and I think you'll find they're more common than you think (remember, it's not an error code, it's an exit or return code):

grep(1):

Normally the exit status is 0 if a line is selected, 1 if no lines were selected, and 2 if an error occurred. However, if the -q or --quiet or --silent is used and a line is selected, the exit status is 0 even if an error occurred.

awk(1p):

The following exit values shall be returned:

 0    All input files were processed successfully.

>0    An error occurred.

The exit status can be altered within the program by using an exit expression.

borg backup:

Return code Meaning
0 success (logged as INFO)
1 generic warning (operation reached its normal end, but there were warnings --
you should check the log, logged as WARNING)
2 generic error (like a fatal error, a local or remote exception, the operation
did not reach its normal end, logged as ERROR)
3..99 specific error (enabled by BORG_EXIT_CODES=modern)
100..127 specific warning (enabled by BORG_EXIT_CODES=modern)
128+N killed by signal N (e.g. 137 == kill -9)

The way I see this should work is have an option to get the status out as JSON which you could parse easily with jq.

Disagree. You're proposing installation of another tool (jq) to handle situations easily handled with builtin behaviour?!?

As further clarification, you could write all other log output into stderr, and the json into stdout, so pipeing to jq would always work ok.

Ugh, no, also disagree.