|
1 | | -# SSH Key Sync Script |
| 1 | +# SSH Key Sync |
2 | 2 |
|
3 | | -This Bash script pulls `authorized_keys` files from remote URLs and updates SSH access for multiple local users. |
| 3 | +[](https://github.com/locus313/ssh-key-sync/actions) |
| 4 | +[](LICENSE) |
| 5 | +[](https://www.gnu.org/software/bash/) |
| 6 | +[](https://github.com/locus313/ssh-key-sync/releases) |
4 | 7 |
|
5 | | -## 🔧 Features |
| 8 | +⭐ If you like this project, star it on GitHub — it helps a lot! |
6 | 9 |
|
7 | | -- Pull-based (ideal for cron or systemd timer) |
8 | | -- Supports multiple users |
9 | | -- Works with: |
10 | | - - ✅ Public URLs (method: `raw`) |
11 | | - - ✅ Private GitHub repositories via GitHub API (method: `api`, requires token) |
12 | | - - ✅ GitHub user public keys (method: `ghuser`) |
13 | | -- Safe: Only updates keys if they’ve changed |
14 | | -- Logs activity per user |
15 | | -- Retries failed fetch operations up to 3 times with a delay |
16 | | -- **Self-update**: Automatically updates the script to the latest version from the GitHub repository |
| 10 | +[Features](#features) • [Getting Started](#getting-started) • [Configuration](#configuration) • [Usage](#usage) • [Examples](#examples) • [Automation](#automation) |
17 | 11 |
|
18 | | -## ⚙️ Configuration |
| 12 | +A robust Bash script for automating SSH `authorized_keys` synchronization across multiple users from various sources. Perfect for managing SSH access in development environments, CI/CD pipelines, and production systems. |
19 | 13 |
|
20 | | -User configuration is stored in a separate `users.conf` file in the same directory as the script. |
21 | | -Edit `users.conf` to define users and their key sources. |
22 | | -Each entry uses the format: |
23 | | -`["username"]="method:url"` |
| 14 | +## Features |
24 | 15 |
|
25 | | -- **raw:** Fetches directly from a public URL. |
26 | | -- **api:** Fetches from a private GitHub repo using the GitHub API (requires `GITHUB_TOKEN` environment variable). |
27 | | -- **ghuser:** Fetches public keys from a GitHub user's profile (provide the GitHub username after the colon). |
| 16 | +- **Multiple Key Sources**: Fetch SSH keys from public URLs, private GitHub repositories, or GitHub user profiles |
| 17 | +- **Retry Logic**: Built-in retry mechanism with configurable delays for handling network failures |
| 18 | +- **Safe Updates**: Only modifies `authorized_keys` when remote content has actually changed |
| 19 | +- **Comprehensive Logging**: Detailed timestamped logs for monitoring and debugging |
| 20 | +- **Self-Updating**: Automatically update to the latest version from the GitHub repository |
| 21 | +- **Configuration-Driven**: External configuration file for easy management |
| 22 | +- **Error Handling**: Robust error handling with graceful fallbacks |
| 23 | +- **Multi-User Support**: Manage SSH keys for multiple system users simultaneously |
28 | 24 |
|
29 | | -You can also set your GitHub token in the config file using `CONF_GITHUB_TOKEN`. |
30 | | -If `GITHUB_TOKEN` is not set in the environment, the script will use `CONF_GITHUB_TOKEN` from `users.conf`. |
| 25 | +## Getting Started |
| 26 | + |
| 27 | +### Prerequisites |
| 28 | + |
| 29 | +- Bash 4.0 or later |
| 30 | +- `curl` for HTTP requests |
| 31 | +- `getent` for user information (typically available on Linux systems) |
| 32 | +- GitHub token (only required for private repository access) |
| 33 | + |
| 34 | +### Quick Start |
| 35 | + |
| 36 | +1. **Download the script**: |
| 37 | + ```bash |
| 38 | + curl -fsSL https://raw.githubusercontent.com/locus313/ssh-key-sync/main/sync-ssh-keys.sh -o sync-ssh-keys.sh |
| 39 | + chmod +x sync-ssh-keys.sh |
| 40 | + ``` |
| 41 | + |
| 42 | +2. **Download the configuration template**: |
| 43 | + ```bash |
| 44 | + curl -fsSL https://raw.githubusercontent.com/locus313/ssh-key-sync/main/users.conf -o users.conf |
| 45 | + ``` |
| 46 | + |
| 47 | +3. **Configure your users** by editing `users.conf`: |
| 48 | + ```bash |
| 49 | + nano users.conf |
| 50 | + ``` |
| 51 | + |
| 52 | +4. **Run the script**: |
| 53 | + ```bash |
| 54 | + ./sync-ssh-keys.sh |
| 55 | + ``` |
| 56 | + |
| 57 | +## Configuration |
| 58 | + |
| 59 | +Configuration is managed through the `users.conf` file, which defines users and their SSH key sources. |
| 60 | + |
| 61 | +### Configuration Format |
31 | 62 |
|
32 | | -**Example `users.conf`:** |
33 | 63 | ```bash |
| 64 | +# Optional: GitHub token for private repository access |
34 | 65 | CONF_GITHUB_TOKEN="your_github_token_here" |
35 | 66 |
|
| 67 | +# User key mapping |
| 68 | +declare -A USER_KEYS=( |
| 69 | + ["username"]="method:target" |
| 70 | +) |
| 71 | +``` |
| 72 | + |
| 73 | +### Supported Methods |
| 74 | + |
| 75 | +| Method | Description | Example | |
| 76 | +|--------|-------------|---------| |
| 77 | +| `raw` | Fetch from public URL | `raw:https://example.com/keys.txt` | |
| 78 | +| `api` | Fetch from private GitHub repo via API | `api:https://api.github.com/repos/org/repo/contents/keys/user.keys?ref=main` | |
| 79 | +| `ghuser` | Fetch public keys from GitHub user | `ghuser:username` | |
| 80 | + |
| 81 | +### Example Configuration |
| 82 | + |
| 83 | +```bash |
| 84 | +#!/bin/bash |
| 85 | + |
| 86 | +# GitHub token for API access (optional) |
| 87 | +CONF_GITHUB_TOKEN="ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" |
| 88 | + |
| 89 | +# User key definitions |
36 | 90 | declare -A USER_KEYS=( |
| 91 | + # Fetch from public URL |
37 | 92 | ["ubuntu"]="raw:https://example.com/ssh-keys/ubuntu.authorized_keys" |
| 93 | + |
| 94 | + # Fetch from private GitHub repository |
38 | 95 | ["devuser"]="api:https://api.github.com/repos/yourorg/ssh-keys/contents/keys/devuser.authorized_keys?ref=main" |
| 96 | + |
| 97 | + # Fetch public keys from GitHub user |
39 | 98 | ["alice"]="ghuser:alice-github-username" |
| 99 | + ["bob"]="ghuser:bob-github-username" |
40 | 100 | ) |
41 | 101 | ``` |
42 | 102 |
|
43 | 103 | ## Usage |
44 | 104 |
|
45 | | -1. Edit the `users.conf` file to define users and their key URLs or GitHub usernames. |
46 | | -2. If using the `api` method, either export your GitHub token: |
| 105 | +### Command Line Options |
| 106 | + |
| 107 | +```bash |
| 108 | +./sync-ssh-keys.sh [OPTIONS] |
| 109 | + |
| 110 | +OPTIONS: |
| 111 | + --self-update Update the script to the latest version from GitHub |
| 112 | + --help, -h Show help message |
| 113 | + --version, -v Show version information |
| 114 | +``` |
| 115 | + |
| 116 | +### Environment Variables |
| 117 | + |
| 118 | +- `GITHUB_TOKEN`: GitHub personal access token (overrides `CONF_GITHUB_TOKEN`) |
| 119 | + |
| 120 | +### Basic Usage |
| 121 | + |
| 122 | +```bash |
| 123 | +# Run synchronization |
| 124 | +./sync-ssh-keys.sh |
| 125 | + |
| 126 | +# Update script to latest version |
| 127 | +./sync-ssh-keys.sh --self-update |
| 128 | + |
| 129 | +# Show help |
| 130 | +./sync-ssh-keys.sh --help |
| 131 | +``` |
| 132 | + |
| 133 | +## Examples |
| 134 | + |
| 135 | +### Adding New Users |
| 136 | + |
| 137 | +1. **Add a user with GitHub public keys**: |
47 | 138 | ```bash |
48 | | - export GITHUB_TOKEN=your_token_here |
| 139 | + # Edit users.conf |
| 140 | + ["newuser"]="ghuser:github-username" |
49 | 141 | ``` |
50 | | - or set `CONF_GITHUB_TOKEN` in `users.conf`. |
51 | | -3. Make sure the script is executable: |
| 142 | + |
| 143 | +2. **Add a user with keys from a private repository**: |
52 | 144 | ```bash |
53 | | - chmod +x sync-ssh-keys.sh |
| 145 | + ["secureuser"]="api:https://api.github.com/repos/company/ssh-keys/contents/users/secureuser.keys?ref=main" |
54 | 146 | ``` |
55 | | -4. Add to root's crontab: |
56 | | - ```cron |
57 | | - */15 * * * * /usr/local/bin/sync-ssh-keys.sh >> /var/log/ssh-key-sync.log 2>&1 |
| 147 | + |
| 148 | +3. **Add a user with keys from a public URL**: |
| 149 | + ```bash |
| 150 | + ["webuser"]="raw:https://company.com/keys/webuser.authorized_keys" |
58 | 151 | ``` |
59 | | -5. To update the script to the latest version, run: |
| 152 | + |
| 153 | +### GitHub Token Setup |
| 154 | + |
| 155 | +For private repositories, you need a GitHub personal access token: |
| 156 | + |
| 157 | +1. Go to GitHub Settings → Developer settings → Personal access tokens |
| 158 | +2. Generate a new token with `repo` scope |
| 159 | +3. Add it to your configuration: |
60 | 160 | ```bash |
61 | | - ./sync-ssh-keys.sh --self-update |
| 161 | + CONF_GITHUB_TOKEN="your_token_here" |
62 | 162 | ``` |
63 | 163 |
|
64 | | -## Implementation Notes |
| 164 | +### Multiple Key Sources |
65 | 165 |
|
66 | | -- The script sources `users.conf` for configuration. |
67 | | -- Uses a helper function `fetch_key_file` to fetch keys using the appropriate method. |
68 | | -- Includes a retry mechanism for failed fetch operations (3 attempts with a 2-second delay). |
69 | | -- Only updates a user's `authorized_keys` if the remote file has changed. |
70 | | -- Logs all actions with timestamps. |
71 | | -- The `--self-update` option fetches the latest version of the script from the GitHub repository and replaces the current version. |
| 166 | +You can configure users with different key sources: |
72 | 167 |
|
73 | | -## Examples |
| 168 | +```bash |
| 169 | +declare -A USER_KEYS=( |
| 170 | + ["prod-user"]="api:https://api.github.com/repos/company/prod-keys/contents/prod-user.keys?ref=main" |
| 171 | + ["dev-user"]="ghuser:dev-github-username" |
| 172 | + ["external-user"]="raw:https://partner.com/keys/external-user.keys" |
| 173 | +) |
| 174 | +``` |
74 | 175 |
|
75 | | -### Adding a New User |
76 | | -1. Add the user to `users.conf`: |
77 | | - ```bash |
78 | | - ["newuser"]="ghuser:newuser-github-username" |
| 176 | +## Automation |
| 177 | + |
| 178 | +### Cron Setup |
| 179 | + |
| 180 | +Add to root's crontab for automated synchronization: |
| 181 | + |
| 182 | +```bash |
| 183 | +# Edit crontab |
| 184 | +sudo crontab -e |
| 185 | + |
| 186 | +# Add entry (sync every 15 minutes) |
| 187 | +*/15 * * * * /path/to/sync-ssh-keys.sh >> /var/log/ssh-key-sync.log 2>&1 |
| 188 | +``` |
| 189 | + |
| 190 | +### Systemd Timer |
| 191 | + |
| 192 | +Create a systemd service and timer: |
| 193 | + |
| 194 | +1. **Create service file** `/etc/systemd/system/ssh-key-sync.service`: |
| 195 | + ```ini |
| 196 | + [Unit] |
| 197 | + Description=SSH Key Synchronization |
| 198 | + Wants=ssh-key-sync.timer |
| 199 | + |
| 200 | + [Service] |
| 201 | + Type=oneshot |
| 202 | + ExecStart=/path/to/sync-ssh-keys.sh |
| 203 | + User=root |
| 204 | + |
| 205 | + [Install] |
| 206 | + WantedBy=multi-user.target |
| 207 | + ``` |
| 208 | + |
| 209 | +2. **Create timer file** `/etc/systemd/system/ssh-key-sync.timer`: |
| 210 | + ```ini |
| 211 | + [Unit] |
| 212 | + Description=Run SSH Key Sync every 15 minutes |
| 213 | + Requires=ssh-key-sync.service |
| 214 | + |
| 215 | + [Timer] |
| 216 | + OnCalendar=*:0/15 |
| 217 | + Persistent=true |
| 218 | + |
| 219 | + [Install] |
| 220 | + WantedBy=timers.target |
79 | 221 | ``` |
80 | | -2. Run the script to sync keys: |
| 222 | + |
| 223 | +3. **Enable and start**: |
81 | 224 | ```bash |
82 | | - ./sync-ssh-keys.sh |
| 225 | + sudo systemctl daemon-reload |
| 226 | + sudo systemctl enable ssh-key-sync.timer |
| 227 | + sudo systemctl start ssh-key-sync.timer |
83 | 228 | ``` |
| 229 | + |
| 230 | +### CI/CD Integration |
| 231 | + |
| 232 | +Use in CI/CD pipelines for automated SSH access management: |
| 233 | + |
| 234 | +```yaml |
| 235 | +# GitHub Actions example |
| 236 | +- name: Sync SSH Keys |
| 237 | + run: | |
| 238 | + curl -fsSL https://raw.githubusercontent.com/locus313/ssh-key-sync/main/sync-ssh-keys.sh | bash -s |
| 239 | + env: |
| 240 | + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} |
| 241 | +``` |
| 242 | +
|
| 243 | +## Troubleshooting |
| 244 | +
|
| 245 | +### Common Issues |
| 246 | +
|
| 247 | +**Permission denied errors**: |
| 248 | +```bash |
| 249 | +# Ensure script is executable |
| 250 | +chmod +x sync-ssh-keys.sh |
| 251 | + |
| 252 | +# Run with appropriate privileges |
| 253 | +sudo ./sync-ssh-keys.sh |
| 254 | +``` |
| 255 | + |
| 256 | +**GitHub API rate limits**: |
| 257 | +- Use authenticated requests with a GitHub token |
| 258 | +- Consider reducing sync frequency |
| 259 | + |
| 260 | +**Network timeouts**: |
| 261 | +- Script includes automatic retry logic (3 attempts by default) |
| 262 | +- Check network connectivity and firewall settings |
| 263 | + |
| 264 | +### Logging |
| 265 | + |
| 266 | +The script provides detailed logging with timestamps: |
| 267 | + |
| 268 | +``` |
| 269 | +2025-08-29 12:00:00: Starting SSH key synchronization (version 0.1.0) |
| 270 | +2025-08-29 12:00:01: Fetching key file for user 'ubuntu' from https://example.com/keys.txt (method: raw) |
| 271 | +2025-08-29 12:00:02: Successfully processed user 'ubuntu' |
| 272 | +2025-08-29 12:00:02: Synchronization complete. Processed: 1, Failed: 0 |
| 273 | +``` |
| 274 | + |
| 275 | +## Security Considerations |
| 276 | + |
| 277 | +- Store GitHub tokens securely and rotate them regularly |
| 278 | +- Use least-privilege access for GitHub tokens (only `repo` scope if needed) |
| 279 | +- Monitor logs for failed authentication attempts |
| 280 | +- Validate SSH key sources before adding them to configuration |
| 281 | +- Consider using private repositories for sensitive key storage |
| 282 | + |
| 283 | +## FAQ |
| 284 | + |
| 285 | +**Q: Can I use this script with GitLab or other Git providers?** |
| 286 | +A: Currently, the script supports GitHub's API. For other providers, use the `raw` method with direct URLs to key files. |
| 287 | + |
| 288 | +**Q: What happens if a user doesn't exist on the system?** |
| 289 | +A: The script will log a warning and skip that user, continuing with the next user in the configuration. |
| 290 | + |
| 291 | +**Q: How often should I run the synchronization?** |
| 292 | +A: This depends on your security requirements. Common intervals are 15 minutes for development environments and 1 hour for production systems. |
0 commit comments