wip(#1970): github-token injection path (priv wire + hive-priv handler + priv_client + hivectl github set-token)
This commit is contained in:
parent
5fb4b9f4b0
commit
80ef7d8151
4 changed files with 102 additions and 0 deletions
|
|
@ -70,6 +70,15 @@ enum Cmd {
|
||||||
#[command(subcommand)]
|
#[command(subcommand)]
|
||||||
cmd: MatrixCmd,
|
cmd: MatrixCmd,
|
||||||
},
|
},
|
||||||
|
/// GitHub account provisioning: write an operator-supplied personal
|
||||||
|
/// access token (PAT) into an agent's `hyperhive.githubAccount` token
|
||||||
|
/// file so its `gh` wrapper + git credential helper can authenticate.
|
||||||
|
/// Unlike forge/matrix there is no account creation — the operator
|
||||||
|
/// supplies a PAT for an existing GitHub account.
|
||||||
|
Github {
|
||||||
|
#[command(subcommand)]
|
||||||
|
cmd: GithubCmd,
|
||||||
|
},
|
||||||
/// Gateway htpasswd user management. Add, remove, or list users in
|
/// Gateway htpasswd user management. Add, remove, or list users in
|
||||||
/// an htpasswd file used by the gateway's HTTP Basic auth
|
/// an htpasswd file used by the gateway's HTTP Basic auth
|
||||||
/// (`services.hyperhive.gateway.auth`). Credentials are stored as
|
/// (`services.hyperhive.gateway.auth`). Credentials are stored as
|
||||||
|
|
@ -421,6 +430,28 @@ enum MatrixCmd {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Subcommand)]
|
||||||
|
enum GithubCmd {
|
||||||
|
/// Write a GitHub PAT into `<agent>`'s state dir (`github-token`, 0600,
|
||||||
|
/// agent-owned) via hive-priv. The agent must declare
|
||||||
|
/// `hyperhive.githubAccount` (its `tokenFile` pointing at this path) for
|
||||||
|
/// the `gh` wrapper + git credential helper to pick it up. The token is
|
||||||
|
/// read live at invocation, so no rebuild/restart is needed. Prefer
|
||||||
|
/// `--token-stdin`: an inline `--token` is visible in shell history +
|
||||||
|
/// process listings.
|
||||||
|
SetToken {
|
||||||
|
/// Logical agent name (the container/agent name).
|
||||||
|
agent: String,
|
||||||
|
/// The PAT value inline. Mutually exclusive with `--token-stdin`.
|
||||||
|
#[arg(long)]
|
||||||
|
token: Option<String>,
|
||||||
|
/// Read the PAT from stdin (trailing newline stripped). Mutually
|
||||||
|
/// exclusive with `--token`.
|
||||||
|
#[arg(long, conflicts_with = "token")]
|
||||||
|
token_stdin: bool,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
// Default htpasswd file path — the host-side location of the gateway's
|
// Default htpasswd file path — the host-side location of the gateway's
|
||||||
// credential store, pre-created by a tmpfiles rule when
|
// credential store, pre-created by a tmpfiles rule when
|
||||||
// `services.hyperhive.gateway.auth.enable = true`. Literal lives in
|
// `services.hyperhive.gateway.auth.enable = true`. Literal lives in
|
||||||
|
|
@ -618,6 +649,13 @@ async fn main() -> Result<()> {
|
||||||
MatrixCmd::ResetPassword { name } => matrix_reset_password(&name).await,
|
MatrixCmd::ResetPassword { name } => matrix_reset_password(&name).await,
|
||||||
MatrixCmd::Invite { user, room } => matrix_invite(&user, room.as_deref()).await,
|
MatrixCmd::Invite { user, room } => matrix_invite(&user, room.as_deref()).await,
|
||||||
},
|
},
|
||||||
|
Cmd::Github { cmd } => match cmd {
|
||||||
|
GithubCmd::SetToken {
|
||||||
|
agent,
|
||||||
|
token,
|
||||||
|
token_stdin,
|
||||||
|
} => github_set_token(&agent, token, token_stdin).await,
|
||||||
|
},
|
||||||
Cmd::Gateway { cmd } => match cmd {
|
Cmd::Gateway { cmd } => match cmd {
|
||||||
GatewayCmd::CreateUser {
|
GatewayCmd::CreateUser {
|
||||||
file,
|
file,
|
||||||
|
|
@ -1124,6 +1162,34 @@ fn choom(name: &str, resume_session: Option<&str>) -> Result<()> {
|
||||||
Err(anyhow::anyhow!("exec machinectl: {err}"))
|
Err(anyhow::anyhow!("exec machinectl: {err}"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// `hivectl github set-token <agent>`: write an operator-supplied GitHub PAT
|
||||||
|
/// into the agent's `github-token` state file (0600, agent-owned) via
|
||||||
|
/// hive-priv, so the agent's `gh` wrapper + git credential helper can
|
||||||
|
/// authenticate. Read live at invocation, so no rebuild/restart is needed.
|
||||||
|
async fn github_set_token(agent: &str, token: Option<String>, token_stdin: bool) -> Result<()> {
|
||||||
|
let token = match (token, token_stdin) {
|
||||||
|
(Some(t), _) => t,
|
||||||
|
(None, true) => {
|
||||||
|
let mut s = String::new();
|
||||||
|
std::io::Read::read_to_string(&mut std::io::stdin(), &mut s)?;
|
||||||
|
s.trim_end_matches(['\n', '\r']).to_owned()
|
||||||
|
}
|
||||||
|
(None, false) => bail!(
|
||||||
|
"provide the PAT via --token <pat> or --token-stdin (stdin preferred — \
|
||||||
|
an inline token is visible in shell history + process listings)"
|
||||||
|
),
|
||||||
|
};
|
||||||
|
if token.is_empty() {
|
||||||
|
bail!("refusing to write an empty GitHub token for agent '{agent}'");
|
||||||
|
}
|
||||||
|
hive_c0re::priv_client::write_agent_github_token(agent, &token).await?;
|
||||||
|
println!(
|
||||||
|
"wrote github-token for agent '{agent}' \
|
||||||
|
(read live by the gh wrapper / git credential helper — no rebuild needed)"
|
||||||
|
);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
async fn forge_create_user(name: &str, password: Option<&str>, password_stdin: bool) -> Result<()> {
|
async fn forge_create_user(name: &str, password: Option<&str>, password_stdin: bool) -> Result<()> {
|
||||||
if !hive_c0re::forge::is_present().await {
|
if !hive_c0re::forge::is_present().await {
|
||||||
bail!(
|
bail!(
|
||||||
|
|
|
||||||
|
|
@ -281,6 +281,19 @@ pub async fn write_agent_matrix_token(
|
||||||
.await?)
|
.await?)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Write a GitHub personal access token (PAT) for `agent_name` via hive-priv
|
||||||
|
/// (running as root). Writes `<state>/github-token` 0600, chowned to the agent
|
||||||
|
/// user so the `gh` wrapper / git credential helper can read it from inside the
|
||||||
|
/// container. Single account per agent — no account suffix. The token value is
|
||||||
|
/// operator-supplied (for the agent's `hyperhive.githubAccount`).
|
||||||
|
pub async fn write_agent_github_token(agent_name: &str, token: &str) -> Result<()> {
|
||||||
|
ok(call(&PrivRequest::WriteAgentGithubToken {
|
||||||
|
agent_name: agent_name.to_owned(),
|
||||||
|
token: token.to_owned(),
|
||||||
|
})
|
||||||
|
.await?)
|
||||||
|
}
|
||||||
|
|
||||||
/// Restart `hive-matrix-daemon.service` inside an agent container via
|
/// Restart `hive-matrix-daemon.service` inside an agent container via
|
||||||
/// `systemctl --machine=h-<agent_name> restart hive-matrix-daemon.service`.
|
/// `systemctl --machine=h-<agent_name> restart hive-matrix-daemon.service`.
|
||||||
/// Non-fatal: callers should handle errors gracefully — if the container is
|
/// Non-fatal: callers should handle errors gracefully — if the container is
|
||||||
|
|
|
||||||
|
|
@ -289,6 +289,14 @@ async fn exec(req: PrivRequest, writer: &mut OwnedWriteHalf) -> Result<(String,
|
||||||
Ok(res)
|
Ok(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PrivRequest::WriteAgentGithubToken {
|
||||||
|
ref agent_name,
|
||||||
|
ref token,
|
||||||
|
} => {
|
||||||
|
validate_agent_name(agent_name)?;
|
||||||
|
write_agent_state_file(agent_name, "github-token", &format!("{token}\n"))
|
||||||
|
}
|
||||||
|
|
||||||
PrivRequest::RestartMatrixDaemon { ref agent_name } => {
|
PrivRequest::RestartMatrixDaemon { ref agent_name } => {
|
||||||
restart_matrix_daemon(agent_name).await
|
restart_matrix_daemon(agent_name).await
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -411,6 +411,21 @@ pub enum PrivRequest {
|
||||||
homeserver: Option<String>,
|
homeserver: Option<String>,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/// Write `github-token` into `AGENT_STATE_ROOT/<agent_name>/state/github-token`.
|
||||||
|
///
|
||||||
|
/// The operator-supplied GitHub personal access token (PAT) for the
|
||||||
|
/// agent's `hyperhive.githubAccount`. Same write semantics as
|
||||||
|
/// `WriteAgentForgeToken` — validates `agent_name`, creates the state dir
|
||||||
|
/// if absent, writes the file 0600, and chowns it to the agent so the
|
||||||
|
/// `gh` wrapper / git credential helper can read it. No account suffix
|
||||||
|
/// (single GitHub account per agent).
|
||||||
|
WriteAgentGithubToken {
|
||||||
|
/// Logical agent name (validated by `validate_agent_name`).
|
||||||
|
agent_name: String,
|
||||||
|
/// PAT value. hive-priv appends a trailing newline before writing.
|
||||||
|
token: String,
|
||||||
|
},
|
||||||
|
|
||||||
/// Restart `hive-matrix-daemon.service` inside an agent container via
|
/// Restart `hive-matrix-daemon.service` inside an agent container via
|
||||||
/// `systemctl --machine=h-<agent_name> restart hive-matrix-daemon.service`.
|
/// `systemctl --machine=h-<agent_name> restart hive-matrix-daemon.service`.
|
||||||
/// Used by hive-c0re to kick the daemon after a successful token write
|
/// Used by hive-c0re to kick the daemon after a successful token write
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue