Compare commits

..

3 Commits
dev ... master

Author SHA1 Message Date
neil
261653ebf4 Merge pull request #7194 from acmesh-official/dev
sync
2026-08-13 17:01:02 +08:00
neil
2feb392bd0 Merge pull request #7133 from acmesh-official/dev
sync
2026-07-17 15:05:00 +08:00
neil
3661fd86b6 Merge pull request #7132 from acmesh-official/dev
sync
2026-07-17 10:01:37 +08:00
5 changed files with 27 additions and 137 deletions

View File

@@ -23,22 +23,10 @@ jobs:
- name: Create the v-prefixed tag
env:
GH_TOKEN: ${{ github.token }}
REPO: ${{ github.repository }}
TAG: ${{ github.ref_name }}
run: |
if gh api "repos/$REPO/git/ref/tags/v$TAG" >/dev/null 2>&1; then
echo "Tag v$TAG already exists, nothing to do."
if gh api "repos/${{ github.repository }}/git/ref/tags/v${{ github.ref_name }}" >/dev/null 2>&1; then
echo "Tag v${{ github.ref_name }} already exists, nothing to do."
exit 0
fi
# Mirror the object the pushed tag actually points at: the commit
# for a lightweight tag, the tag object itself for an annotated or
# signed one. Pointing the mirror at the commit would strip the
# signature, so "git verify-tag v3.1.3" would fail while
# "git verify-tag 3.1.3" succeeds.
sha="$(gh api "repos/$REPO/git/ref/tags/$TAG" --jq .object.sha)"
if [ -z "$sha" ] || [ "$sha" = "null" ]; then
echo "Could not resolve refs/tags/$TAG"
exit 1
fi
gh api "repos/$REPO/git/refs" -f ref="refs/tags/v$TAG" -f sha="$sha"
echo "Created tag v$TAG -> $sha"
gh api "repos/${{ github.repository }}/git/refs" -f ref="refs/tags/v${{ github.ref_name }}" -f sha="${{ github.sha }}"
echo "Created tag v${{ github.ref_name }} -> ${{ github.sha }}"

View File

@@ -227,31 +227,6 @@ Cron entry example:
acme.sh -h
```
#### 🔏 Verify a Release
Release tags from `3.1.5` on are signed with the maintainer's SSH key. The
signing happens on the maintainer's machine, so the private key is never
available to CI. The public half is [`allowed_signers`](allowed_signers) in
this repository. From a clone:
```bash
git config gpg.ssh.allowedSignersFile allowed_signers
```
```bash
git verify-tag 3.1.5
```
The signature covers the tag object, which pins the commit and therefore the
whole tree, so a good signature verifies every file at that release and no
separate tarball checksum is needed. Build a tarball from the verified tag:
```bash
git archive --format=tar.gz --prefix=acme.sh-3.1.5/ 3.1.5 > acme.sh-3.1.5.tar.gz
```
> ⚠️ Tags up to `3.1.4` predate the signing key and are unsigned.
---
### 2⃣ Issue a Certificate

49
acme.sh
View File

@@ -7072,30 +7072,6 @@ _uninstall_win_taskscheduler() {
fi
}
#binpath
#Reads a crontab listing from stdin, prints it without the acme.sh cron
#entries that call binpath.
_filter_cron_bin() {
_fcb_bin="$1"
if [ -z "$_fcb_bin" ]; then
cat
return
fi
#a case pattern with a quoted variable matches binpath literally, which
#grep cannot do portably: Solaris /usr/bin/grep has no -F, and as a regex
#the dot of ~/.acme.sh would stand for any character
while IFS= read -r _fcb_line || [ -n "$_fcb_line" ]; do
case "$_fcb_line" in
*"$_fcb_bin --cron"*)
_debug3 "Dropping cron entry" "$_fcb_line"
;;
*)
echo "$_fcb_line"
;;
esac
done
}
#confighome
installcronjob() {
_c_home="$1"
@@ -7165,26 +7141,7 @@ installcronjob() {
return 1
fi
fi
#An entry that calls LE_WORKING_DIR/PROJECT_ENTRY is dead once that copy is
#gone: ACME_PACKAGED installs never write it, and the package manager
#removes it when it takes over. The entry below would then keep the install
#from adding a working one and cron would fail silently every day, so drop
#the stale entries first.
_cron_stale=""
if [ ! -f "$LE_WORKING_DIR/$PROJECT_ENTRY" ] && [ "$_cron_entries" ]; then
_cron_kept="$(echo "$_cron_entries" | _filter_cron_bin "\"$LE_WORKING_DIR\"/$PROJECT_ENTRY")"
if [ "$_cron_kept" != "$_cron_entries" ]; then
_info "Removing the cron job that calls the missing $LE_WORKING_DIR/$PROJECT_ENTRY"
_cron_entries="$_cron_kept"
_cron_stale=1
fi
fi
#>/dev/null: grep would print the matching crontab line to the console
_cron_add=""
if ! echo "$_cron_entries" | grep "$PROJECT_ENTRY --cron" >/dev/null; then
_cron_add=1
fi
if [ "$_cron_add" ] || [ "$_cron_stale" ]; then
if ! echo "$_cron_entries" | grep "$PROJECT_ENTRY --cron"; then
if _exists uname && uname -a | grep SunOS >/dev/null; then
_CRONTAB_STDIN="$_CRONTAB --"
else
@@ -7194,9 +7151,7 @@ installcronjob() {
if [ "$_cron_entries" ]; then
echo "$_cron_entries"
fi
if [ "$_cron_add" ]; then
echo "$_cron_entry"
fi
echo "$_cron_entry"
} | $_CRONTAB_STDIN
fi
if [ "$?" != "0" ]; then

View File

@@ -1,20 +0,0 @@
# acme.sh release signing key.
#
# Release tags are signed with this key. Its private half is held by the
# maintainer and is never available to CI, so a compromise of the build
# pipeline cannot produce a tag that verifies against this file.
#
# Fingerprint: SHA256:M60qVafm/NUywQHXAkoQcj2v6KgkfrdSXv6mPejUUeE
#
# To verify a release tag, from a clone of this repository:
#
# git config gpg.ssh.allowedSignersFile allowed_signers
# git verify-tag 3.1.5
#
# A good signature covers the tag object, which pins the commit, which pins
# the whole tree -- so verifying the tag verifies every file at that
# release. Build a tarball from the verified tag with:
#
# git archive --format=tar.gz --prefix=acme.sh-3.1.5/ 3.1.5 > acme.sh-3.1.5.tar.gz
#
github@neilpang.com namespaces="git" ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBTjI0HBJn3uhfT2DsNcFybfAZi3ADbIacMpz1BItKdB

View File

@@ -1,29 +1,24 @@
#!/usr/bin/env sh
# Deploy hook for UniFi OS, via the certificate REST API.
# Deploy hook for UniFi OS Server (self-hosted).
#
# Works against any UniFi OS whose management UI exposes
# /api/userCertificates. Confirmed on:
# - UniFi OS Server (the separately-installed, self-hosted application)
# on macOS and on Linux. Windows should also work (it runs under
# WSL2), but has not been tested.
# Tested on: Ubuntu 26.04 (remote) and macOS 26.6 (local).
# - UniFi OS hardware: UDM Pro on UniFi OS 5.1.26, UCG Fiber on
# UniFi OS 5.0.16 (user reports, see issues 7184 and 6916).
# No lower version bound is claimed -- if the UI has a certificate
# manager, this hook should work.
# Supports:
# - UniFi OS Server on macOS
# - UniFi OS Server on Linux
# - UniFi OS Server on Windows should also work (runs under WSL2), but
# has not been tested.
#
# `unifios` vs `unifi`: the split is the access method, not the product
# line. `unifi` writes files / a Java keystore and needs local or SSH
# access on the device; this hook drives the same REST API the web UI
# uses and works remotely. Use `unifi` where acme.sh runs on the device
# itself, this hook where it does not.
# Tested on: Ubuntu 26.04 (remote) and macOS 26.6 (local).
#
# The API is served on the management port, which differs per install:
# UniFi OS Server listens on 11443 (hence the default below), while
# UniFi OS hardware serves it on 443 -- set DEPLOY_UNIFIOS_HOST to
# "https://<host>" there.
# This is a different product from the Cloud Key / UDM hardware and
# self-hosted Unifi Controller covered by the `unifi` deploy hook above
# (that hook already covers Cloud Key running UnifiOS v2.0.0+/Gen2/2+) --
# this hook targets the separately-installed, self-hosted "UniFi OS Server"
# application instead, which stores certificates in its own Postgres
# database via a REST API rather than a Java keystore, so the `unifi`
# hook's approach does not apply here.
#
# Endpoints used, all as the web UI itself calls them:
# UniFi OS Server exposes a REST API on its management port (default
# 11443) that its own web UI uses for certificate management:
# POST /api/auth/login - session login (cookie + JWT)
# GET /api/userCertificates - list uploaded certificates
# POST /api/userCertificates - upload a new certificate
@@ -46,9 +41,8 @@
# Uses core acme.sh helpers throughout (_post/_get, _json_encode,
# _durl_replace_base64, _dbase64, _egrep_o) rather than raw curl -k or
# python3, so the wget fallback, --debug tracing, and CA_BUNDLE are all
# honored the same as every other hook. The management API's cert may be
# self-signed -- it always is on a fresh install, and there is no reliable
# way to tell in advance whether an earlier run has already replaced it --
# honored the same as every other hook. The management API's cert is
# self-signed (it's a management-only port, not meant for public exposure),
# so this hook sets HTTPS_INSECURE=1 itself, scoped to its own subshell (see
# acme.sh's per-hook sourcing in _deploy) -- it does not weaken TLS
# verification for the rest of the acme.sh run, e.g. the connection to the
@@ -69,11 +63,9 @@
#
# Settings:
# DEPLOY_UNIFIOS_HOST - base URL of the management API
# (default: "https://localhost:11443", i.e. a UniFi OS Server on the
# same machine as acme.sh; set it to "https://<host>" for UniFi OS
# hardware or any remote target)
# DEPLOY_UNIFIOS_USERNAME - UniFi OS admin username (required)
# DEPLOY_UNIFIOS_PASSWORD - UniFi OS admin password (required)
# (default: "https://localhost:11443")
# DEPLOY_UNIFIOS_USERNAME - UniFi OS Server admin username (required)
# DEPLOY_UNIFIOS_PASSWORD - UniFi OS Server admin password (required)
#
# Example:
# export DEPLOY_UNIFIOS_USERNAME="acmeuser"