mattcasters opened a new issue, #9099:
URL: https://github.com/apache/hop/issues/9099

   ### What would you like to happen?
   
   Add a keystore plugin that keeps one master key in the operating-system 
secret store and exposes named secrets through a variable resolver. Logging 
into the operating-system account that stored the key makes those secrets 
available to every Hop process running as that user.
   
   This gives Hop GUI, `hop-run`, and pipelines one place to resolve a secret, 
and it gives Git a credential that survives a restart. Today the token typed in 
**Add project → From version control** is used only for that clone 
(`UsernamePasswordCredentialsProvider` with username `x-oauth-basic`) and is 
then discarded. Push and pull start with no credentials and open a 
username/password dialog. The answer is kept in memory on that Git session 
until Hop exits. Nothing is written to a store Hop can read the next time.
   
   #### Architecture
   
   The operating-system store holds a single master key. Named secrets are AES2 
ciphertext in a file under the Hop config folder. `hop-config.json` stays the 
registry of projects and environment files and does not hold the key or the 
secrets. Project files do not hold them either.
   
   ```mermaid
   flowchart TB
     subgraph osAccount [Operating-system account]
       Store["OS secret store<br/>Windows Credential Manager<br/>macOS 
Keychain<br/>Linux Secret Service"]
     end
   
     subgraph hopProcess [Hop process running as that account]
       Plugin[Keystore plugin]
       File["Named secrets<br/>HOP_CONFIG_FOLDER/keystore.json<br/>AES2 
ciphertext"]
       Resolver["Variable resolver<br/>#{hop-keystore:name}"]
       Encoder[AES2 password encoder]
       Dialog[Configuration perspective]
       Git[Git clone, push and pull]
       Runtime[Pipelines, workflows, metadata]
     end
   
     Store -->|master key| Plugin
     Plugin --> File
     Plugin --> Encoder
     Dialog --> Plugin
     Resolver --> Plugin
     Git --> Resolver
     Runtime --> Resolver
     Runtime --> Encoder
   
     KeyVar["HOP_AES_ENCODER_KEY"] -->|wins| Encoder
     KeyFile["HOP_AES_ENCODER_KEY_FILE"] -->|wins| Encoder
   ```
   
   Master-key lookup, first hit wins:
   
   1. `HOP_AES_ENCODER_KEY`
   2. `HOP_AES_ENCODER_KEY_FILE`
   3. The operating-system store, service name `Apache Hop`
   
   `AesEncoderKeyUtil` already implements the first two. The plugin adds the 
third, so password fields and environment values that are already stored as 
`AES2` ciphertext unlock with the same login. The default `Encrypted ` encoder 
is obfuscation and is not a fallback for this store.
   
   ```mermaid
   sequenceDiagram
     participant User
     participant Git as Git clone or push
     participant Resolver as hop-keystore resolver
     participant File as keystore.json
     participant OS as OS secret store
   
     User->>Git: https://github.com/org/repo.git
     Git->>Resolver: #{hop-keystore:git/github.com}
     Resolver->>OS: read master key
     OS-->>Resolver: master key of the logged-in account
     Resolver->>File: read AES2 ciphertext for git/github.com
     File-->>Resolver: ciphertext
     Resolver-->>Git: token
     Git->>User: clone or push, no password dialog
   ```
   
   When the name is missing, Git keeps the token field and the 
username/password dialog it has today. A **Remember for this host** action 
writes `git/<host>` through the keystore dialog's save path.
   
   #### Requirements
   
   **Plugin and resolver**
   
   * New plugin, with a `@VariableResolverPlugin`. Expression: 
`#{hop-keystore:<name>}`, for example `#{hop-keystore:git/github.com}`.
   * Names are chosen by the user. `git/<host>` is the convention the Git 
plugin uses. It is not a special case inside the resolver.
   * A missing name, a locked store, or no store leaves the expression 
unresolved and logs one line. The line does not contain the secret or the 
master key. An empty string is not a successful resolution.
   * The same resolver works in Hop GUI, `hop-run`, Hop Server, and Hop Web. 
There is no separate Hop login that unlocks it.
   
   **Master key**
   
   * Generate a random key the first time Hop GUI needs one and the OS store is 
available. Store it under the service name `Apache Hop`.
   * Windows Credential Manager opens with the Windows login. No second prompt.
   * macOS may ask once whether this Hop binary may read the keychain item. 
After the user allows it, later launches in that account succeed.
   * Linux uses the secret service unlocked by a graphical login. An SSH 
session, a service account, or a container usually has no secret service. 
Resolution then depends on `HOP_AES_ENCODER_KEY` or `HOP_AES_ENCODER_KEY_FILE`, 
which is the path Hop Server, Hop Web, and containers already use.
   * The account that matters is the operating-system user that owns the Hop 
process. Another OS user on the same machine cannot read the key. A person 
signing into Hop Web in a browser does not unlock the store. The account that 
started the JVM does.
   * Any other process running as that same user can read the entry. That is 
the boundary of Credential Manager, Keychain, and the secret service.
   * Copying `hop-config.json` or a project to another machine does not copy 
the master key.
   
   **Named secrets**
   
   * Store them in `${HOP_CONFIG_FOLDER}/keystore.json`, readable and writable 
by the user only. Each entry has a name, an optional username, and an AES2 
ciphertext. The username is not secret. The master key is not in this file.
   * Edit the list from a dialog in the configuration perspective: add, 
replace, and delete. The feature is not file-only.
   * The value field accepts a token. Saving encrypts it with the master key. 
The dialog never shows the stored ciphertext as something to edit by hand.
   
   **Git**
   
   * Clone, push, and pull look up `git/<host>` from the remote URL before 
asking for a password. The same entry is used at checkout, before any project 
or lifecycle environment exists.
   * Remembering a token from the clone dialog or the username/password dialog 
creates or replaces that entry.
   * The username stored on the entry is sent with the token. For GitHub the 
existing `x-oauth-basic` form remains valid when the username is empty.
   * The token is not written into the remote URL, `.git/config`, 
`project-config.json`, or `hop-config.json`.
   * A lifecycle environment can override the user entry for the enabled 
session, for a deploy token on a build agent. The environment file holds a 
reference such as `#{hop-keystore:...}` or `#{vault:...}`, or an `AES2` value. 
Checkout does not require an environment.
   
   **Existing secrets**
   
   * With no explicit AES key variable or key file, the AES2 encoder uses the 
OS master key. Database passwords and environment secrets already saved as 
`AES2` then open for the logged-in account.
   * A server that sets `HOP_AES_ENCODER_KEY_FILE` keeps using that file and 
does not need the OS store.
   
   #### Implementation notes
   
   * `java.security.KeyStore` is a file format such as PKCS12. It is not the 
operating-system store.
   * Hop already depends on JNA. Put a small Hop interface in front of the 
native calls so the backend can be replaced. 
[`com.github.javakeyring:java-keyring`](https://central.sonatype.com/artifact/com.github.javakeyring/java-keyring)
 1.0.4 (BSD-3, August 2023) talks to Credential Manager, Keychain, and the 
secret service and is a candidate behind that interface. Microsoft 
`credential-secure-storage` was archived in March 2025 and should not be added.
   * Document the desktop first-run and the server key-file fallback in the 
user manual.
   
   ### Issue Priority
   
   Priority: 2
   
   ### Issue Component
   
   Component: Hop Gui
   Component: API
   Component: Hop Config
   Component: Hop Server
   Component: Hop Web
   Component: Documentation
   


-- 
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]

Reply via email to