asf-tooling commented on issue #437:
URL:
https://github.com/apache/tooling-trusted-releases/issues/437#issuecomment-4410319158
<!-- gofannon-issue-triage-bot v2 -->
**Automated triage** — analyzed at `main@2da7807a`
**Type:** `discussion` • **Classification:** `no_action` •
**Confidence:** `high`
**Application domain(s):** `cryptographic_keys`, `automated_checks`,
`authentication_authorization`
### Summary
This is an open-ended RFC exploring alternatives to local OpenPGP signing
for ASF releases. The issue discusses Sigstore adoption (as Python SF did),
server-side signing with password-protected private keys on ATR, and WebAuthn
requirements. @sbp noted in a follow-up that the tooling-releases-client (issue
#3) could add a signing command to ease the process client-side. No concrete
implementation decision has been made, and no specific code change is proposed
— this remains a design discussion about the overall signing architecture.
### Where this lives in the code today
#### `atr/tasks/checks/signature.py` — `check` (lines 38-50)
_currently does this_
The existing signature check is tightly coupled to OpenPGP .asc detached
signatures; any alternative signing scheme would need a parallel check or this
would need abstraction.
```python
async def check(args: checks.FunctionArguments) -> results.Results | None:
"""Check a signature file."""
recorder = await args.recorder(CHECK_VERSION)
if not (primary_abs_path := await recorder.abs_path()):
return None
if not (primary_rel_path := args.primary_rel_path):
await recorder.exception("Primary relative path is required",
{"primary_rel_path": primary_rel_path})
return None
artifact_rel_path = str(primary_rel_path).removesuffix(".asc")
if not (artifact_abs_path := await recorder.abs_path(artifact_rel_path)):
return None
```
#### `atr/tasks/checks/signature.py` — `_check_core_logic` (lines 82-93)
_currently does this_
Core verification logic fetches OpenPGP public keys from the database. A
server-side signing approach would store private keys here too, requiring new
DB models and storage.
```python
async def _check_core_logic(committee_key: str, artifact_path: str,
signature_path: str) -> dict[str, Any]:
"""Verify a signature file using the committee's public signing keys."""
log.info(f"Attempting to fetch keys for committee_key:
'{committee_key}'")
async with db.session() as session:
statement = (
sqlmodel.select(sql.PublicSigningKey)
.join(sql.KeyLink)
.join(sql.Committee)
.where(sql.validate_instrumented_attribute(sql.Committee.key) ==
committee_key)
)
result = await session.execute(statement)
db_public_keys = result.scalars().all()
```
#### `atr/storage/writers/keys.py` — `FoundationCommitter` (lines 196-208)
_extension point_
The key storage writer handles only public keys. Server-side signing would
require extending this (or creating a new writer) to store encrypted private
keys and perform signing operations.
```python
class FoundationCommitter(GeneralPublic):
def __init__(self, write: storage.Write, write_as:
storage.WriteAsFoundationCommitter, data: db.Session):
super().__init__(write, write_as, data)
self.__write = write
self.__write_as = write_as
self.__data = data
asf_uid = write.authorisation.asf_uid
if asf_uid is None:
raise storage.AccessError("Not authorized", status=403)
self.__asf_uid = asf_uid
# Specific to this module
self.__key_block_models_cache = {}
```
#### `atr/post/keys.py` — `PrivateKeyUploadError` (lines 53-54)
_currently does this_
ATR currently REJECTS private key uploads as a security measure. Server-side
signing would require a deliberate reversal of this stance with new encrypted
storage.
```python
class PrivateKeyUploadError(Exception):
pass
```
#### `atr/post/keys.py` — `_add_key_text_resolve` (lines 238-247)
_currently does this_
Private key detection with immediate memory cleanup - shows current security
stance against server-side private key storage.
```python
async def _add_key_text_resolve(session: web.Committer, add_form:
shared.keys.AddOpenPGPKeyForm) -> str:
if (file := add_form.public_key_file) is None:
key_text = add_form.public_key
if util.contains_private_key_text(key_text):
vars(add_form)["public_key"] = ""
session.form_data_discard(["public_key", "public_key_file"])
del key_text
gc.collect()
raise PrivateKeyUploadError
return key_text
```
#### `atr/storage/writers/policy.py` — `CommitteeMember` (lines 95-108)
_extension point_
Policy management is where a 'signing_mode' option
(openpgp/sigstore/server-side) might be configured per project, but this is
speculative until a design decision is made.
```python
class CommitteeMember(CommitteeParticipant):
def __init__(
self,
write: storage.Write,
write_as: storage.WriteAsCommitteeMember,
data: db.Session,
committee_key: str,
):
super().__init__(write, write_as, data, committee_key)
...
async def edit_compose(self, form: shared.projects.ComposePolicyForm) ->
None:
project_key = form.project_key
_, release_policy = await self.__get_or_create_policy(project_key)
```
### Where new code would go
- `atr/storage/writers/signing.py` — new file
If server-side signing is implemented, a new writer module would handle
encrypted private key storage and signing operations.
- `atr/tasks/checks/sigstore.py` — new file
If Sigstore support is added, a parallel check module to signature.py
would handle Sigstore bundle verification.
### Proposed approach
This is a design discussion with no actionable code change at this time. The
issue explores three directions: (1) Sigstore support alongside or replacing
OpenPGP, (2) server-side signing where ATR generates and stores
password-protected private keys, and (3) enhanced authentication (WebAuthn) to
protect the signing operation. Each would require significant architectural
work across multiple subsystems — new database models for private key or
Sigstore metadata storage, new verification checks, policy configuration
changes, and potentially new authentication flows.
The only concrete follow-up mentioned is the tooling-releases-client issue
#3 (client-side signing command), which is external to this repository. No
implementation decision has been reached, and the tradeoffs remain under
consideration. This issue should remain open as a tracking discussion for the
signing architecture direction.
### Open questions
- Has the ASF Security Team provided formal guidance on whether server-side
signing with password-protected keys meets the 'automated release
infrastructure' requirements in release policy?
- Would Sigstore be supported only as an additional signature format
alongside OpenPGP, or could it replace OpenPGP for some projects?
- What is the status of the tooling-releases-client issue #3 (client-side
signing command) — does it reduce the urgency of server-side signing?
- Would WebAuthn enforcement be a prerequisite for server-side signing, and
does ATR currently support WebAuthn authentication at all?
- How would encrypted private key storage interact with the existing
security controls that explicitly reject private key uploads
(PrivateKeyUploadError)?
_The agent reviewed this issue and is not proposing patches in this run.
Review the existing-code citations and open questions above before deciding
next steps._
### Files examined
- `atr/tasks/checks/signature.py`
- `atr/post/keys.py`
- `atr/shared/keys.py`
- `atr/storage/writers/keys.py`
- `atr/get/keys.py`
- `atr/storage/writers/policy.py`
- `atr/post/tokens.py`
- `atr/shared/tokens.py`
---
*Draft from a triage agent. A human reviewer should validate before merging
any change. The agent did not run tests or verify diffs apply.*
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]