Huawei Cloud ECS Passwordless Login
Overview
Configure passwordless SSH login to Huawei Cloud ECS using COC (Cloud Operations Center) with a 7-step automated workflow:
- IAM Authorization — Create the
ServiceAgencyForCOC agency for COC service and bind 4 required roles; skip entirely if agency already exists (HTTP 409)
- Key Generation — Generate a local RSA 4096-bit SSH key pair
- Script Creation — Create or reuse a parameterized COC script that deploys the public key
- Script Execution — Execute the script on the target ECS via COC
- SSH Test — Verify passwordless SSH connection to the target ECS
- Persistent Connection — Establish SSH ControlMaster so the agent can continue SSH access after keys are cleaned up
- Security Cleanup — After 60 seconds, automatically remove keys from remote
authorized_keys, delete the local key pair, and clean up the COC script
Tool chain: hcloud CLI (KooCLI) + local SSH tools. Deployment is handled through COC script execution only.
Prerequisites
- hcloud CLI (KooCLI) installed and authenticated with AK/SK — see CLI Installation Guide
- IAM user with sufficient permissions to create agencies and operate COC — see IAM Policies
- SSH client and
ssh-keygen available locally
- Target ECS instance ID or elastic IP (the ECS must be in
ACTIVE state)
Security
- Never expose private key content in conversation or output
- Never log or persist private keys beyond the 60s window
- Cleanup is mandatory — if SSH test succeeds, keys MUST be removed within
cleanup_delay seconds
- Removing the public key from
authorized_keys only blocks new SSH connections; existing sessions are NOT interrupted
- If SSH test fails, preserve keys for debugging and do NOT trigger cleanup
Workflow
Execute the numbered steps below in order. See Core Commands section for the exact command syntax to use at each step.
1. IAM Authorization
One-time per-account setup. Authorize COC to operate on your ECS instances.
- Get domain ID — Call
KeystoneListAuthDomains and extract the id.
- Create agency — Call
CreateAgency with name ServiceAgencyForCOC and trust domain op_svc_coc.
- HTTP 200 — New agency created. Record
agency.id. Proceed to step 3.
- HTTP 409 — Agency already exists. Skip the entire IAM phase (steps 3–4) and jump to Step 2 (Key Generation).
- Find role IDs — Call
KeystoneListPermissions for each of the 4 roles: IAM ReadOnlyAccess, RMS ReadOnlyAccess, DCS UserAccess, COCServiceAgencyPolicy. If any role is not found, stop — the account may lack access.
- Bind roles — Call
AssociateAgencyWithAllProjectsPermission for each of the 4 role IDs.
- HTTP 200 — Bound successfully.
- HTTP 409 — Already bound, skip and continue.
2. Generate Local SSH Key Pair
Generate an RSA 4096-bit key pair. The comment coc-temp-key is the cleanup marker.
Record the key fingerprint. Never display the private key content.
3. Create or Reuse COC Script
- Check existing — Call
ListScripts with --name_like="coc_ssh_key_setup". If found, record script_uuid and skip step 2.
- Create new — Write a JSON file with the script content, then call
CreateScript --cli-jsonInput=<file>. Use --cli-jsonInput (not inline --content) to avoid shell quoting issues with special characters in the script body. Record the returned script_uuid.
4. Execute Script on Target ECS
- Resolve instance — If only an ECS IP was provided, call
NovaListServersDetails to find the instance ID.
- Execute — Write a JSON file with the public key embedded, then call
ExecuteScript --cli-jsonInput=<file>. Same pattern as Step 3 to avoid shell quoting issues with the public key content. Record execute_uuid.
- Poll — Call
GetScriptJobInfo every 5 seconds until terminal status. Note: the API redacts param_value in the response for security; verify deployment by testing SSH in Step 5.
| Status |
Action |
RUNNING |
Wait 5s, poll again |
SUCCESS |
Proceed to Step 5 |
FAILED / TIMEOUT |
Report error, stop |
Max 2 minutes (24 polls).
5. Test SSH Connection
Test passwordless SSH using the generated key.
SSH_OK returned — Proceed to Step 6.
- Connection fails — Report error. Stop here. Preserve keys for debugging. Do NOT trigger cleanup.
6. Establish Persistent SSH Connection
Set up SSH ControlMaster so the agent can continue accessing the ECS after keys are removed in Step 7.
- Append SSH config — Add a
Host <EIP> block to ~/.ssh/config with ControlMaster auto, ControlPath /tmp/coc_ssh_%r@%h:%p, and ControlPersist <persist_timeout>.
- Start master — Run
ssh -N -f <EIP> -i <key> to background a persistent master connection.
- Verify — Run
ssh <EIP> "echo SSH_MUX_OK" without a key file. If SSH_MUX_OK is returned, multiplexing works.
SSH config and socket do not need cleanup — config entries are harmless, sockets auto-expire with ControlPersist.
7. Security Cleanup
Mandatory. Start a background timer that fires after cleanup_delay seconds (default: 60):
- Remove
coc-temp-key line from remote /root/.ssh/authorized_keys
- Delete the COC script via
DeleteScript
- Delete local key files from
<temp_dir>/
After cleanup, the agent can still connect via ssh <EIP> — ControlMaster bypasses key authentication.
Fallback: If remote key removal via SSH fails, create and execute a one-shot COC script:
#!/bin/bash
set -e
sed -i '/coc-temp-key/d' /root/.ssh/authorized_keys
echo "KEY_REMOVED"
Create and execute this script with no parameters. Delete it immediately after execution completes.
Core Commands
Placeholder values (see Parameters for per-OS resolution):
| Placeholder |
Linux / macOS |
Windows |
<hcloud> |
hcloud |
hcloud |
<temp_dir> |
/tmp |
$env:TEMP |
# 1. Check/setup COC IAM authorization
<hcloud> IAM KeystoneListAuthDomains/v3
<hcloud> IAM CreateAgency/v3 \
--agency.domain_id="<domain_id>" \
--agency.name="ServiceAgencyForCOC" \
--agency.trust_domain_name="op_svc_coc" \
--agency.duration="FOREVER"
<hcloud> IAM KeystoneListPermissions/v3 \
--display_name="<role_name>"
<hcloud> IAM AssociateAgencyWithAllProjectsPermission/v3 \
--agency_id="<agency_id>" --domain_id="<domain_id>" --role_id="<role_id>"
# 2. Generate local SSH key pair
ssh-keygen -t rsa -b 4096 -f <temp_dir>/coc_ssh_key -N "" -C "coc-temp-key"
ssh-keygen -lf <temp_dir>/coc_ssh_key # record fingerprint
# 3. Check for existing COC script
<hcloud> COC ListScripts --limit=100 --name_like="coc_ssh_key_setup"
# If not found, create a JSON file and use --cli-jsonInput:
cat > <temp_dir>/coc_create.json << 'JSONEOF'
{
"body": {
"name": "coc_ssh_key_setup",
"type": "SHELL",
"description": "Deploy SSH public key for passwordless login",
"content": "#!/bin/bash\nset -e\nmkdir -p /root/.ssh && chmod 700 /root/.ssh\necho $PUBLIC_KEY >> /root/.ssh/authorized_keys\nchmod 600 /root/.ssh/authorized_keys\necho KEY_DEPLOYED_SUCCESSFULLY",
"properties": {
"risk_level": "LOW",
"version": "1.0.0"
},
"script_params": [
{
"param_name": "PUBLIC_KEY",
"param_description": "SSH public key to deploy",
"param_value": "",
"sensitive": false
}
]
}
}
JSONEOF
<hcloud> COC CreateScript --cli-jsonInput=<temp_dir>/coc_create.json
# 4. Execute script on target ECS, replace with actual value
cat > <temp_dir>/coc_execute.json << 'JSONEOF'
{
"path": {"script_uuid": "<script_uuid>"},
"body": {
"execute_batches": [{
"batch_index": 1,
"rotation_strategy": "CONTINUE",
"target_instances": [{
"region_id": "<region>",
"resource_id": "<instance_id>"
}]
}],
"execute_param": {
"execute_user": "root",
"success_rate": 100,
"timeout": 120,
"script_params": [{
"param_name": "PUBLIC_KEY",
"param_value": pubkey
}]
}
}
}
JSONEOF
<hcloud> COC ExecuteScript --cli-jsonInput=<temp_dir>/coc_execute.json
# Poll execution status (note: GetScriptJobInfo redacts param_value for security; check SSH directly to verify)
<hcloud> COC GetScriptJobInfo --execute_uuid=<execute_uuid>
# 5. Test SSH connection
ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
-o ConnectTimeout=10 -i <temp_dir>/coc_ssh_key root@<EIP> "echo SSH_OK"
# 6. Establish persistent SSH connection (ControlMaster multiplexing)
cat >> ~/.ssh/config << 'EOF'
Host <EIP>
User <ssh_user>
ControlMaster auto
ControlPath /tmp/coc_ssh_%r@%h:%p
ControlPersist <persist_timeout>
StrictHostKeyChecking no
UserKnownHostsFile /dev/null
EOF
ssh -N -f <EIP> -i <temp_dir>/coc_ssh_key && echo "MASTER_CONNECTED"
ssh <EIP> "echo SSH_MUX_OK" # verify multiplexing works
# 7. Security cleanup (background, survives parent shell exit via nohup + disown)
nohup bash -c '
sleep <cleanup_delay>
# Remove public key from remote
ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
-o ConnectTimeout=5 -i <temp_dir>/coc_ssh_key root@<EIP> \
"sed -i \"/coc-temp-key/d\" /root/.ssh/authorized_keys" 2>/dev/null || true
# Delete COC script
<hcloud> COC DeleteScript --script_uuid="<script_uuid>" 2>/dev/null || true
# Delete local keys
rm -f <temp_dir>/coc_ssh_key <temp_dir>/coc_ssh_key.pub
echo "COC SSH keys cleaned up. Existing SSH sessions remain unaffected."
' > <temp_dir>/coc_cleanup.log 2>&1 &
disown
echo "Cleanup scheduled in <cleanup_delay>s (PID: $!, log: <temp_dir>/coc_cleanup.log)"
Parameters
| Parameter |
Required |
Default |
Constraint |
ecs_instance_id |
Conditional |
None |
ECS instance UUID; required if ecs_ip is not provided |
ecs_ip |
Conditional |
None |
ECS elastic IPv4 address; required if ecs_instance_id is not provided |
region |
Yes |
cn-north-4 |
Region where the ECS and COC reside. Must match the ECS's region |
ssh_user |
No |
root |
SSH username on the target ECS |
cleanup_delay |
No |
60 |
Seconds to wait before automatic key cleanup (min 10, max 300) |
persist_timeout |
No |
3600 |
Seconds to keep ControlMaster alive after all sessions close (min 60, max 86400) |
Output Format
At each step, report progress in a structured manner:
| Step |
Output |
| 1. Authorization |
Agency status (created / already exists), roles bound count (4/4) |
| 2. Key Generation |
Key fingerprint, key file paths |
| 3. Script |
Script name, action (created / reused), script_uuid |
| 4. Execution |
execute_uuid, polling status, final result |
| 5. SSH Test |
Connection result, SSH command string |
| 6. Persistent Connection |
ControlMaster status, multiplex verification, SSH alias <EIP> |
| 7. Cleanup |
Timer PID, countdown notification, cleanup confirmation |
Verification
Verify the workflow step by step:
- Authorization —
CreateAgency returns 200 → all 4 roles bound (200 or 409 each); returns 409 → entire IAM phase skipped (agency already authorized from prior run)
- Key Generation — Key pair files exist in
<temp_dir>/ with correct permissions
- Script —
coc_ssh_key_setup exists with PUBLIC_KEY parameter and valid script_uuid
- Execution —
GetScriptJobInfo shows SUCCESS within 2 minutes
- SSH Test —
ssh connects without password prompt; test command returns SSH_OK
- Persistent Connection — SSH config appended,
ssh -N -f <EIP> starts master, ssh <EIP> "echo SSH_MUX_OK" succeeds
- Cleanup — Remote key removed, COC script deleted, local key files deleted;
ssh <EIP> still connects via ControlMaster
See Verification Method and Acceptance Criteria for detailed checklists.
Best Practices
- IAM authorization is a one-time per-account setup — if
CreateAgency returns 409, the entire IAM phase (agency + role binding) is already complete and should be skipped entirely
- Use the
--name_like filter in ListScripts to avoid creating duplicate scripts
- Always test the SSH connection before starting the cleanup timer
- If SSH fails, keep keys on disk for debugging — do NOT clean up automatically
- The cleanup timer runs in a background subshell; killing the process before cleanup completes leaves keys in place
- After key cleanup, the agent can still SSH via
ssh <EIP> — ControlMaster bypasses key authentication
- Use
-o UserKnownHostsFile=/dev/null to avoid polluting the local known_hosts file
- The SSH key comment
coc-temp-key is the marker used by sed for cleanup — do not change it
- COC script execution is limited to 200 hosts per execution and 10 hosts per batch
- SSH config entries persist after cleanup as harmless dead entries; they can be removed later if desired
Reference Documents
| Document |
Description |
| CLI Installation Guide |
Install and configure hcloud CLI and SSH tools |
| IAM Policies |
Required IAM permissions, agency setup, and error handling |
| Verification Method |
Step-by-step verification per workflow step |
| Acceptance Criteria |
Full end-to-end acceptance checklist |
Notes
- All
hcloud commands use the default region from the CLI profile (no --cli-region). Ensure your profile is configured with the correct region where your ECS and COC reside.
- The COC script is created via
--cli-jsonInput with a JSON file, not inline --content="..." — inline quoting causes parsing errors with shell special characters in the script body
- The COC script is parameterized with
PUBLIC_KEY — it persists across invocations and can deploy different keys
- If the COC script already exists from a previous run, it is reused rather than recreated
- The cleanup uses
nohup bash -c '...' & + disown to survive parent shell exit; output is logged to <temp_dir>/coc_cleanup.log for verification. The old (sleep N && ...) & pattern loses stdout when the parent shell exits in non-interactive mode
- Private keys are stored in
<temp_dir> and should never be committed to VCS
- The
sed cleanup target coc-temp-key matches the key comment set during ssh-keygen
- After cleanup, use
ssh <EIP> (no key file needed) — ControlMaster socket handles authentication
- The SSH config entry is appended to
~/.ssh/config as a simple Host block
- ControlMaster socket is stored at
/tmp/coc_ssh_%r@%h:%p and auto-cleaned when the master process exits
1---
2name: huawei-cloud-ecs-passwordless-login
3description: |
4 Configure passwordless SSH login to Huawei Cloud ECS instances using COC (Cloud Operations Center). Automates IAM agency authorization, SSH key pair generation, COC script deployment to the target ECS, SSH connection testing, and automatic security cleanup after 60 seconds (removing keys from both local and remote).
5 触发词: 免密登录, COC SSH, ECS key login, SSH key deployment, passwordless SSH
6---
7
8# Huawei Cloud ECS Passwordless Login
9
10## Overview
11
12Configure passwordless SSH login to Huawei Cloud ECS using COC (Cloud Operations Center) with a 7-step automated workflow:
13
141. **IAM Authorization** — Create the `ServiceAgencyForCOC` agency for COC service and bind 4 required roles; skip entirely if agency already exists (HTTP 409)
152. **Key Generation** — Generate a local RSA 4096-bit SSH key pair
163. **Script Creation** — Create or reuse a parameterized COC script that deploys the public key
174. **Script Execution** — Execute the script on the target ECS via COC
185. **SSH Test** — Verify passwordless SSH connection to the target ECS
196. **Persistent Connection** — Establish SSH ControlMaster so the agent can continue SSH access after keys are cleaned up
207. **Security Cleanup** — After 60 seconds, automatically remove keys from remote `authorized_keys`, delete the local key pair, and clean up the COC script
21
22**Tool chain:** hcloud CLI (KooCLI) + local SSH tools. Deployment is handled through COC script execution only.
23
24## Prerequisites
25
26- hcloud CLI (KooCLI) installed and authenticated with AK/SK — see [CLI Installation Guide](references/cli-installation-guide.md)
27- IAM user with sufficient permissions to create agencies and operate COC — see [IAM Policies](references/iam-policies.md)
28- SSH client and `ssh-keygen` available locally
29- Target ECS instance ID or elastic IP (the ECS must be in `ACTIVE` state)
30
31### Security
32
33- Never expose private key content in conversation or output
34- Never log or persist private keys beyond the 60s window
35- Cleanup is mandatory — if SSH test succeeds, keys MUST be removed within `cleanup_delay` seconds
36- Removing the public key from `authorized_keys` only blocks **new** SSH connections; existing sessions are NOT interrupted
37- If SSH test fails, preserve keys for debugging and do NOT trigger cleanup
38
39## Workflow
40
41Execute the numbered steps below in order. See **Core Commands** section for the exact command syntax to use at each step.
42
43### 1. IAM Authorization
44
45**One-time per-account setup.** Authorize COC to operate on your ECS instances.
46
471. **Get domain ID** — Call `KeystoneListAuthDomains` and extract the `id`.
482. **Create agency** — Call `CreateAgency` with name `ServiceAgencyForCOC` and trust domain `op_svc_coc`.
49 - **HTTP 200** — New agency created. Record `agency.id`. Proceed to step 3.
50 - **HTTP 409** — Agency already exists. **Skip the entire IAM phase (steps 3–4) and jump to Step 2 (Key Generation).**
513. **Find role IDs** — Call `KeystoneListPermissions` for each of the 4 roles: `IAM ReadOnlyAccess`, `RMS ReadOnlyAccess`, `DCS UserAccess`, `COCServiceAgencyPolicy`. If any role is not found, stop — the account may lack access.
524. **Bind roles** — Call `AssociateAgencyWithAllProjectsPermission` for each of the 4 role IDs.
53 - **HTTP 200** — Bound successfully.
54 - **HTTP 409** — Already bound, skip and continue.
55
56### 2. Generate Local SSH Key Pair
57
58Generate an RSA 4096-bit key pair. The comment `coc-temp-key` is the cleanup marker.
59
60Record the key fingerprint. **Never display the private key content.**
61
62### 3. Create or Reuse COC Script
63
641. **Check existing** — Call `ListScripts` with `--name_like="coc_ssh_key_setup"`. If found, record `script_uuid` and skip step 2.
652. **Create new** — Write a JSON file with the script content, then call `CreateScript --cli-jsonInput=<file>`. Use `--cli-jsonInput` (not inline `--content`) to avoid shell quoting issues with special characters in the script body. Record the returned `script_uuid`.
66
67### 4. Execute Script on Target ECS
68
691. **Resolve instance** — If only an ECS IP was provided, call `NovaListServersDetails` to find the instance ID.
702. **Execute** — Write a JSON file with the public key embedded, then call `ExecuteScript --cli-jsonInput=<file>`. Same pattern as Step 3 to avoid shell quoting issues with the public key content. Record `execute_uuid`.
713. **Poll** — Call `GetScriptJobInfo` every 5 seconds until terminal status. Note: the API redacts `param_value` in the response for security; verify deployment by testing SSH in Step 5.
72 | Status | Action |
73 |--------|--------|
74 | `RUNNING` | Wait 5s, poll again |
75 | `SUCCESS` | Proceed to Step 5 |
76 | `FAILED` / `TIMEOUT` | Report error, stop |
77
78 Max 2 minutes (24 polls).
79
80### 5. Test SSH Connection
81
82Test passwordless SSH using the generated key.
83
84- **`SSH_OK` returned** — Proceed to Step 6.
85- **Connection fails** — Report error. **Stop here.** Preserve keys for debugging. Do NOT trigger cleanup.
86
87### 6. Establish Persistent SSH Connection
88
89Set up SSH ControlMaster so the agent can continue accessing the ECS after keys are removed in Step 7.
90
911. **Append SSH config** — Add a `Host <EIP>` block to `~/.ssh/config` with `ControlMaster auto`, `ControlPath /tmp/coc_ssh_%r@%h:%p`, and `ControlPersist <persist_timeout>`.
922. **Start master** — Run `ssh -N -f <EIP> -i <key>` to background a persistent master connection.
933. **Verify** — Run `ssh <EIP> "echo SSH_MUX_OK"` without a key file. If `SSH_MUX_OK` is returned, multiplexing works.
94
95SSH config and socket **do not need cleanup** — config entries are harmless, sockets auto-expire with `ControlPersist`.
96
97### 7. Security Cleanup
98
99**Mandatory.** Start a background timer that fires after `cleanup_delay` seconds (default: 60):
100
1011. Remove `coc-temp-key` line from remote `/root/.ssh/authorized_keys`
1022. Delete the COC script via `DeleteScript`
1033. Delete local key files from `<temp_dir>/`
104
105**After cleanup**, the agent can still connect via `ssh <EIP>` — ControlMaster bypasses key authentication.
106
107**Fallback**: If remote key removal via SSH fails, create and execute a one-shot COC script:
108
109```bash
110#!/bin/bash
111set -e
112sed -i '/coc-temp-key/d' /root/.ssh/authorized_keys
113echo "KEY_REMOVED"
114```
115
116Create and execute this script with no parameters. Delete it immediately after execution completes.
117
118## Core Commands
119
120Placeholder values (see Parameters for per-OS resolution):
121
122| Placeholder | Linux / macOS | Windows |
123|-------------|---------------|---------|
124| `<hcloud>` | `hcloud` | `hcloud` |
125| `<temp_dir>` | `/tmp` | `$env:TEMP` |
126
127```bash
128# 1. Check/setup COC IAM authorization
129<hcloud> IAM KeystoneListAuthDomains/v3
130<hcloud> IAM CreateAgency/v3 \
131 --agency.domain_id="<domain_id>" \
132 --agency.name="ServiceAgencyForCOC" \
133 --agency.trust_domain_name="op_svc_coc" \
134 --agency.duration="FOREVER"
135<hcloud> IAM KeystoneListPermissions/v3 \
136 --display_name="<role_name>"
137<hcloud> IAM AssociateAgencyWithAllProjectsPermission/v3 \
138 --agency_id="<agency_id>" --domain_id="<domain_id>" --role_id="<role_id>"
139
140# 2. Generate local SSH key pair
141ssh-keygen -t rsa -b 4096 -f <temp_dir>/coc_ssh_key -N "" -C "coc-temp-key"
142ssh-keygen -lf <temp_dir>/coc_ssh_key # record fingerprint
143
144# 3. Check for existing COC script
145<hcloud> COC ListScripts --limit=100 --name_like="coc_ssh_key_setup"
146# If not found, create a JSON file and use --cli-jsonInput:
147cat > <temp_dir>/coc_create.json << 'JSONEOF'
148{
149 "body": {
150 "name": "coc_ssh_key_setup",
151 "type": "SHELL",
152 "description": "Deploy SSH public key for passwordless login",
153 "content": "#!/bin/bash\nset -e\nmkdir -p /root/.ssh && chmod 700 /root/.ssh\necho $PUBLIC_KEY >> /root/.ssh/authorized_keys\nchmod 600 /root/.ssh/authorized_keys\necho KEY_DEPLOYED_SUCCESSFULLY",
154 "properties": {
155 "risk_level": "LOW",
156 "version": "1.0.0"
157 },
158 "script_params": [
159 {
160 "param_name": "PUBLIC_KEY",
161 "param_description": "SSH public key to deploy",
162 "param_value": "",
163 "sensitive": false
164 }
165 ]
166 }
167}
168JSONEOF
169<hcloud> COC CreateScript --cli-jsonInput=<temp_dir>/coc_create.json
170
171# 4. Execute script on target ECS, replace with actual value
172cat > <temp_dir>/coc_execute.json << 'JSONEOF'
173{
174 "path": {"script_uuid": "<script_uuid>"},
175 "body": {
176 "execute_batches": [{
177 "batch_index": 1,
178 "rotation_strategy": "CONTINUE",
179 "target_instances": [{
180 "region_id": "<region>",
181 "resource_id": "<instance_id>"
182 }]
183 }],
184 "execute_param": {
185 "execute_user": "root",
186 "success_rate": 100,
187 "timeout": 120,
188 "script_params": [{
189 "param_name": "PUBLIC_KEY",
190 "param_value": pubkey
191 }]
192 }
193 }
194}
195JSONEOF
196<hcloud> COC ExecuteScript --cli-jsonInput=<temp_dir>/coc_execute.json
197
198# Poll execution status (note: GetScriptJobInfo redacts param_value for security; check SSH directly to verify)
199<hcloud> COC GetScriptJobInfo --execute_uuid=<execute_uuid>
200
201# 5. Test SSH connection
202ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
203 -o ConnectTimeout=10 -i <temp_dir>/coc_ssh_key root@<EIP> "echo SSH_OK"
204
205# 6. Establish persistent SSH connection (ControlMaster multiplexing)
206cat >> ~/.ssh/config << 'EOF'
207
208Host <EIP>
209 User <ssh_user>
210 ControlMaster auto
211 ControlPath /tmp/coc_ssh_%r@%h:%p
212 ControlPersist <persist_timeout>
213 StrictHostKeyChecking no
214 UserKnownHostsFile /dev/null
215EOF
216ssh -N -f <EIP> -i <temp_dir>/coc_ssh_key && echo "MASTER_CONNECTED"
217ssh <EIP> "echo SSH_MUX_OK" # verify multiplexing works
218
219# 7. Security cleanup (background, survives parent shell exit via nohup + disown)
220nohup bash -c '
221sleep <cleanup_delay>
222# Remove public key from remote
223ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
224 -o ConnectTimeout=5 -i <temp_dir>/coc_ssh_key root@<EIP> \
225 "sed -i \"/coc-temp-key/d\" /root/.ssh/authorized_keys" 2>/dev/null || true
226# Delete COC script
227<hcloud> COC DeleteScript --script_uuid="<script_uuid>" 2>/dev/null || true
228# Delete local keys
229rm -f <temp_dir>/coc_ssh_key <temp_dir>/coc_ssh_key.pub
230echo "COC SSH keys cleaned up. Existing SSH sessions remain unaffected."
231' > <temp_dir>/coc_cleanup.log 2>&1 &
232disown
233echo "Cleanup scheduled in <cleanup_delay>s (PID: $!, log: <temp_dir>/coc_cleanup.log)"
234```
235
236## Parameters
237
238| Parameter | Required | Default | Constraint |
239|-----------|----------|---------|------------|
240| `ecs_instance_id` | Conditional | None | ECS instance UUID; required if `ecs_ip` is not provided |
241| `ecs_ip` | Conditional | None | ECS elastic IPv4 address; required if `ecs_instance_id` is not provided |
242| `region` | Yes | `cn-north-4` | Region where the ECS and COC reside. Must match the ECS's region |
243| `ssh_user` | No | `root` | SSH username on the target ECS |
244| `cleanup_delay` | No | `60` | Seconds to wait before automatic key cleanup (min 10, max 300) |
245| `persist_timeout` | No | `3600` | Seconds to keep ControlMaster alive after all sessions close (min 60, max 86400) |
246
247## Output Format
248
249At each step, report progress in a structured manner:
250
251| Step | Output |
252|------|--------|
253| 1. Authorization | Agency status (created / already exists), roles bound count (4/4) |
254| 2. Key Generation | Key fingerprint, key file paths |
255| 3. Script | Script name, action (created / reused), script_uuid |
256| 4. Execution | execute_uuid, polling status, final result |
257| 5. SSH Test | Connection result, SSH command string |
258| 6. Persistent Connection | ControlMaster status, multiplex verification, SSH alias `<EIP>` |
259| 7. Cleanup | Timer PID, countdown notification, cleanup confirmation |
260
261## Verification
262
263Verify the workflow step by step:
264
2651. **Authorization** — `CreateAgency` returns 200 → all 4 roles bound (200 or 409 each); returns 409 → entire IAM phase skipped (agency already authorized from prior run)
2662. **Key Generation** — Key pair files exist in `<temp_dir>/` with correct permissions
2673. **Script** — `coc_ssh_key_setup` exists with `PUBLIC_KEY` parameter and valid `script_uuid`
2684. **Execution** — `GetScriptJobInfo` shows `SUCCESS` within 2 minutes
2695. **SSH Test** — `ssh` connects without password prompt; test command returns `SSH_OK`
2706. **Persistent Connection** — SSH config appended, `ssh -N -f <EIP>` starts master, `ssh <EIP> "echo SSH_MUX_OK"` succeeds
2717. **Cleanup** — Remote key removed, COC script deleted, local key files deleted; `ssh <EIP>` still connects via ControlMaster
272
273See [Verification Method](references/verification-method.md) and [Acceptance Criteria](references/acceptance-criteria.md) for detailed checklists.
274
275## Best Practices
276
277- IAM authorization is a **one-time per-account** setup — if `CreateAgency` returns 409, the entire IAM phase (agency + role binding) is already complete and should be skipped entirely
278- Use the `--name_like` filter in `ListScripts` to avoid creating duplicate scripts
279- Always test the SSH connection before starting the cleanup timer
280- If SSH fails, keep keys on disk for debugging — do NOT clean up automatically
281- The cleanup timer runs in a background subshell; killing the process before cleanup completes leaves keys in place
282- After key cleanup, the agent can still SSH via `ssh <EIP>` — ControlMaster bypasses key authentication
283- Use `-o UserKnownHostsFile=/dev/null` to avoid polluting the local `known_hosts` file
284- The SSH key comment `coc-temp-key` is the marker used by `sed` for cleanup — do not change it
285- COC script execution is limited to 200 hosts per execution and 10 hosts per batch
286- SSH config entries persist after cleanup as harmless dead entries; they can be removed later if desired
287
288## Reference Documents
289
290| Document | Description |
291|----------|-------------|
292| [CLI Installation Guide](references/cli-installation-guide.md) | Install and configure hcloud CLI and SSH tools |
293| [IAM Policies](references/iam-policies.md) | Required IAM permissions, agency setup, and error handling |
294| [Verification Method](references/verification-method.md) | Step-by-step verification per workflow step |
295| [Acceptance Criteria](references/acceptance-criteria.md) | Full end-to-end acceptance checklist |
296
297## Notes
298
299- All `hcloud` commands use the default region from the CLI profile (no `--cli-region`). Ensure your profile is configured with the correct region where your ECS and COC reside.
300- The COC script is created via `--cli-jsonInput` with a JSON file, not inline `--content="..."` — inline quoting causes parsing errors with shell special characters in the script body
301- The COC script is **parameterized** with `PUBLIC_KEY` — it persists across invocations and can deploy different keys
302- If the COC script already exists from a previous run, it is **reused** rather than recreated
303- The cleanup uses `nohup bash -c '...' &` + `disown` to survive parent shell exit; output is logged to `<temp_dir>/coc_cleanup.log` for verification. The old `(sleep N && ...) &` pattern loses stdout when the parent shell exits in non-interactive mode
304- Private keys are stored in `<temp_dir>` and should never be committed to VCS
305- The `sed` cleanup target `coc-temp-key` matches the key comment set during `ssh-keygen`
306- After cleanup, use `ssh <EIP>` (no key file needed) — ControlMaster socket handles authentication
307- The SSH config entry is appended to `~/.ssh/config` as a simple `Host` block
308- ControlMaster socket is stored at `/tmp/coc_ssh_%r@%h:%p` and auto-cleaned when the master process exits