ec2patcher 1.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of ec2patcher might be problematic. Click here for more details.
- ec2patcher-1.0.0/LICENSE +21 -0
- ec2patcher-1.0.0/PKG-INFO +417 -0
- ec2patcher-1.0.0/README.md +376 -0
- ec2patcher-1.0.0/pyproject.toml +82 -0
- ec2patcher-1.0.0/setup.cfg +4 -0
- ec2patcher-1.0.0/src/ec2patcher/__init__.py +8 -0
- ec2patcher-1.0.0/src/ec2patcher/__main__.py +4 -0
- ec2patcher-1.0.0/src/ec2patcher/app.py +1022 -0
- ec2patcher-1.0.0/src/ec2patcher/config.py +100 -0
- ec2patcher-1.0.0/src/ec2patcher/database.py +1570 -0
- ec2patcher-1.0.0/src/ec2patcher/formatting.py +22 -0
- ec2patcher-1.0.0/src/ec2patcher/logging_setup.py +103 -0
- ec2patcher-1.0.0/src/ec2patcher/main.py +96 -0
- ec2patcher-1.0.0/src/ec2patcher/models.py +395 -0
- ec2patcher-1.0.0/src/ec2patcher/services/__init__.py +0 -0
- ec2patcher-1.0.0/src/ec2patcher/services/amazon_state.py +205 -0
- ec2patcher-1.0.0/src/ec2patcher/services/amazon_updateinfo.py +456 -0
- ec2patcher-1.0.0/src/ec2patcher/services/analysis_service.py +819 -0
- ec2patcher-1.0.0/src/ec2patcher/services/apt_planner.py +323 -0
- ec2patcher-1.0.0/src/ec2patcher/services/cache_settings.py +240 -0
- ec2patcher-1.0.0/src/ec2patcher/services/cve_resolver.py +642 -0
- ec2patcher-1.0.0/src/ec2patcher/services/debversion.py +94 -0
- ec2patcher-1.0.0/src/ec2patcher/services/downloader.py +117 -0
- ec2patcher-1.0.0/src/ec2patcher/services/excel_export.py +307 -0
- ec2patcher-1.0.0/src/ec2patcher/services/local_apt.py +353 -0
- ec2patcher-1.0.0/src/ec2patcher/services/nvd.py +635 -0
- ec2patcher-1.0.0/src/ec2patcher/services/os_adapters/__init__.py +92 -0
- ec2patcher-1.0.0/src/ec2patcher/services/os_adapters/amazon_linux.py +374 -0
- ec2patcher-1.0.0/src/ec2patcher/services/os_adapters/base.py +146 -0
- ec2patcher-1.0.0/src/ec2patcher/services/os_adapters/ubuntu.py +246 -0
- ec2patcher-1.0.0/src/ec2patcher/services/patch_remote.py +432 -0
- ec2patcher-1.0.0/src/ec2patcher/services/patch_service.py +1457 -0
- ec2patcher-1.0.0/src/ec2patcher/services/patch_state.py +176 -0
- ec2patcher-1.0.0/src/ec2patcher/services/report_service.py +152 -0
- ec2patcher-1.0.0/src/ec2patcher/services/rpmversion.py +138 -0
- ec2patcher-1.0.0/src/ec2patcher/services/secret_store.py +144 -0
- ec2patcher-1.0.0/src/ec2patcher/services/security_metadata.py +542 -0
- ec2patcher-1.0.0/src/ec2patcher/services/server_state.py +255 -0
- ec2patcher-1.0.0/src/ec2patcher/services/severity.py +39 -0
- ec2patcher-1.0.0/src/ec2patcher/services/ssh_service.py +431 -0
- ec2patcher-1.0.0/src/ec2patcher/services/staging.py +222 -0
- ec2patcher-1.0.0/src/ec2patcher/static/app.js +113 -0
- ec2patcher-1.0.0/src/ec2patcher/static/style.css +395 -0
- ec2patcher-1.0.0/src/ec2patcher/templates/_cache_badges.html +2 -0
- ec2patcher-1.0.0/src/ec2patcher/templates/_cache_stats.html +3 -0
- ec2patcher-1.0.0/src/ec2patcher/templates/_metadata_status.html +8 -0
- ec2patcher-1.0.0/src/ec2patcher/templates/_nvd_key_badge.html +13 -0
- ec2patcher-1.0.0/src/ec2patcher/templates/_run_status.html +5 -0
- ec2patcher-1.0.0/src/ec2patcher/templates/_ssh_result.html +19 -0
- ec2patcher-1.0.0/src/ec2patcher/templates/analysis_run.html +97 -0
- ec2patcher-1.0.0/src/ec2patcher/templates/base.html +42 -0
- ec2patcher-1.0.0/src/ec2patcher/templates/dashboard.html +36 -0
- ec2patcher-1.0.0/src/ec2patcher/templates/error.html +9 -0
- ec2patcher-1.0.0/src/ec2patcher/templates/history.html +35 -0
- ec2patcher-1.0.0/src/ec2patcher/templates/patch_all.html +48 -0
- ec2patcher-1.0.0/src/ec2patcher/templates/patch_all_confirm.html +52 -0
- ec2patcher-1.0.0/src/ec2patcher/templates/patch_confirm.html +40 -0
- ec2patcher-1.0.0/src/ec2patcher/templates/patch_execution.html +169 -0
- ec2patcher-1.0.0/src/ec2patcher/templates/reports.html +142 -0
- ec2patcher-1.0.0/src/ec2patcher/templates/server_form.html +100 -0
- ec2patcher-1.0.0/src/ec2patcher/templates/server_report.html +283 -0
- ec2patcher-1.0.0/src/ec2patcher/templates/servers.html +79 -0
- ec2patcher-1.0.0/src/ec2patcher/templates/settings.html +137 -0
- ec2patcher-1.0.0/src/ec2patcher/templates/shutdown_confirm.html +16 -0
- ec2patcher-1.0.0/src/ec2patcher/templates/shutdown_done.html +33 -0
- ec2patcher-1.0.0/src/ec2patcher/validation.py +222 -0
- ec2patcher-1.0.0/src/ec2patcher.egg-info/PKG-INFO +417 -0
- ec2patcher-1.0.0/src/ec2patcher.egg-info/SOURCES.txt +106 -0
- ec2patcher-1.0.0/src/ec2patcher.egg-info/dependency_links.txt +1 -0
- ec2patcher-1.0.0/src/ec2patcher.egg-info/entry_points.txt +2 -0
- ec2patcher-1.0.0/src/ec2patcher.egg-info/requires.txt +14 -0
- ec2patcher-1.0.0/src/ec2patcher.egg-info/top_level.txt +1 -0
- ec2patcher-1.0.0/tests/test_amazon_linux.py +549 -0
- ec2patcher-1.0.0/tests/test_amazon_updateinfo.py +402 -0
- ec2patcher-1.0.0/tests/test_analysis.py +901 -0
- ec2patcher-1.0.0/tests/test_apt_planner.py +242 -0
- ec2patcher-1.0.0/tests/test_cache_ttl.py +690 -0
- ec2patcher-1.0.0/tests/test_cve_resolver.py +335 -0
- ec2patcher-1.0.0/tests/test_database.py +183 -0
- ec2patcher-1.0.0/tests/test_debversion.py +109 -0
- ec2patcher-1.0.0/tests/test_integration_e2e.py +423 -0
- ec2patcher-1.0.0/tests/test_integration_targets.py +152 -0
- ec2patcher-1.0.0/tests/test_local_apt.py +320 -0
- ec2patcher-1.0.0/tests/test_nvd_cache.py +242 -0
- ec2patcher-1.0.0/tests/test_nvd_key_check.py +350 -0
- ec2patcher-1.0.0/tests/test_os_adapters.py +259 -0
- ec2patcher-1.0.0/tests/test_patch_all.py +467 -0
- ec2patcher-1.0.0/tests/test_patch_execution.py +826 -0
- ec2patcher-1.0.0/tests/test_patch_resilience.py +205 -0
- ec2patcher-1.0.0/tests/test_patch_web.py +493 -0
- ec2patcher-1.0.0/tests/test_perl_report.py +150 -0
- ec2patcher-1.0.0/tests/test_phase21.py +555 -0
- ec2patcher-1.0.0/tests/test_phase22.py +702 -0
- ec2patcher-1.0.0/tests/test_phase4.py +128 -0
- ec2patcher-1.0.0/tests/test_planner_guards.py +148 -0
- ec2patcher-1.0.0/tests/test_remediation_groups.py +340 -0
- ec2patcher-1.0.0/tests/test_report_service.py +127 -0
- ec2patcher-1.0.0/tests/test_rpmversion.py +125 -0
- ec2patcher-1.0.0/tests/test_security_metadata.py +826 -0
- ec2patcher-1.0.0/tests/test_server_state.py +134 -0
- ec2patcher-1.0.0/tests/test_ssh_service.py +142 -0
- ec2patcher-1.0.0/tests/test_ssh_user.py +204 -0
- ec2patcher-1.0.0/tests/test_stale_state.py +356 -0
- ec2patcher-1.0.0/tests/test_stored_credentials.py +609 -0
- ec2patcher-1.0.0/tests/test_tags.py +376 -0
- ec2patcher-1.0.0/tests/test_ui_polish.py +378 -0
- ec2patcher-1.0.0/tests/test_validation.py +91 -0
- ec2patcher-1.0.0/tests/test_web.py +409 -0
ec2patcher-1.0.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Amir Amiri
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,417 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ec2patcher
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Local web GUI that analyzes Ubuntu and Amazon Linux 2023 EC2 servers for CVE fixes and patches Ubuntu servers over SSH from an approved .deb plan
|
|
5
|
+
Author: Amir Amiri
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/Amiri83/EC2Patcher
|
|
8
|
+
Project-URL: Source, https://github.com/Amiri83/EC2Patcher
|
|
9
|
+
Project-URL: Issues, https://github.com/Amiri83/EC2Patcher/issues
|
|
10
|
+
Keywords: ec2,aws,ubuntu,amazon-linux,cve,security,patching,ssh,apt
|
|
11
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
12
|
+
Classifier: Environment :: Web Environment
|
|
13
|
+
Classifier: Framework :: FastAPI
|
|
14
|
+
Classifier: Intended Audience :: System Administrators
|
|
15
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
22
|
+
Classifier: Topic :: Security
|
|
23
|
+
Classifier: Topic :: System :: Systems Administration
|
|
24
|
+
Requires-Python: >=3.10
|
|
25
|
+
Description-Content-Type: text/markdown
|
|
26
|
+
License-File: LICENSE
|
|
27
|
+
Requires-Dist: fastapi>=0.110
|
|
28
|
+
Requires-Dist: uvicorn>=0.27
|
|
29
|
+
Requires-Dist: jinja2>=3.1
|
|
30
|
+
Requires-Dist: python-multipart>=0.0.9
|
|
31
|
+
Requires-Dist: platformdirs>=4.0
|
|
32
|
+
Requires-Dist: openpyxl>=3.1
|
|
33
|
+
Requires-Dist: cryptography>=42
|
|
34
|
+
Provides-Extra: dev
|
|
35
|
+
Requires-Dist: pytest>=8; extra == "dev"
|
|
36
|
+
Requires-Dist: httpx2; extra == "dev"
|
|
37
|
+
Requires-Dist: ruff>=0.5; extra == "dev"
|
|
38
|
+
Requires-Dist: build>=1.2; extra == "dev"
|
|
39
|
+
Requires-Dist: twine>=6.1; extra == "dev"
|
|
40
|
+
Dynamic: license-file
|
|
41
|
+
|
|
42
|
+
# EC2 Patcher
|
|
43
|
+
|
|
44
|
+
A small, local, single-user web GUI (FastAPI + Jinja2 + SQLite) for recurring security
|
|
45
|
+
patching of EC2 servers. You keep an inventory of servers, upload the security team's CVE
|
|
46
|
+
report, get a **read-only** per-server analysis, and then patch an approved plan on one server
|
|
47
|
+
or on all eligible servers of an analysis (**Patch All**), with an optional reboot afterwards.
|
|
48
|
+
|
|
49
|
+
- **Ubuntu**: analysis (Canonical security data) with an exact package / `.deb` plan, and
|
|
50
|
+
patching of the approved plan.
|
|
51
|
+
- **Amazon Linux 2023**: read-only analysis (Amazon's ALAS advisories); patching is not
|
|
52
|
+
supported yet.
|
|
53
|
+
- The OS is **detected automatically** from `/etc/os-release`; any other OS is listed as
|
|
54
|
+
*OS not supported yet*.
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
pipx install ec2patcher
|
|
58
|
+
ec2patcher
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
EC2Patcher never runs `apt upgrade` / `apt dist-upgrade`: it installs only the exact `.deb`
|
|
62
|
+
files of a plan you approved.
|
|
63
|
+
|
|
64
|
+
## Workflow at a glance
|
|
65
|
+
|
|
66
|
+
1. **Servers**: add each server (name, IP, SSH user, login type: PEM key or password,
|
|
67
|
+
optional tags) and run the SSH test.
|
|
68
|
+
2. **Reports**: pick or drop the CVE report JSON; it is uploaded and validated automatically.
|
|
69
|
+
3. **Analyze Report**: read-only analysis of every server in the report.
|
|
70
|
+
4. Review each **server report** (Severity, CVSS, Ubuntu priority, package plan, reboot
|
|
71
|
+
expectation); export it to Excel if needed. **Re-analyze** or **Retry** where needed.
|
|
72
|
+
5. **Approve & Patch** one server, or **Patch All** for the whole analysis. Optionally tick
|
|
73
|
+
**Skip reboot**.
|
|
74
|
+
6. **History** keeps every decision, execution, verification and reboot result.
|
|
75
|
+
|
|
76
|
+
## Target server requirements
|
|
77
|
+
|
|
78
|
+
- Ubuntu (analysis + patching) or Amazon Linux 2023 (analysis only), reachable over SSH as
|
|
79
|
+
the server's **SSH user** (per server, default `ubuntu`; `ec2-user` on Amazon Linux) with a
|
|
80
|
+
PEM key (default; the PEM *path* is stored, its contents are never read, stored or logged)
|
|
81
|
+
or with username + password (see below). Other operating systems (including end-of-life
|
|
82
|
+
Amazon Linux 2) are detected and listed as *OS not supported yet* / *OS not supported*.
|
|
83
|
+
- **Passwordless sudo** for that user for patching: every privileged command uses `sudo -n`,
|
|
84
|
+
and patching aborts if `sudo -n true` fails. Analysis needs no sudo at all.
|
|
85
|
+
- Ubuntu: `dpkg`, `apt-get`, `sha256sum` and write access to `/tmp` (the standard Ubuntu image
|
|
86
|
+
has them). Amazon Linux 2023: `rpm` (read-only queries; `needs-restarting` from
|
|
87
|
+
`dnf-utils` is optional and used only for the reboot status).
|
|
88
|
+
|
|
89
|
+
## Features
|
|
90
|
+
|
|
91
|
+
### Servers and tags
|
|
92
|
+
|
|
93
|
+
- Add, edit, delete and SSH-test servers. **Clear All Servers** requires typing
|
|
94
|
+
`DELETE SERVERS`.
|
|
95
|
+
- Names may contain letters, digits, `.`, `_` and `-` and are unique regardless of case. CVE
|
|
96
|
+
reports are always matched by **name**.
|
|
97
|
+
- Optional key/value **tags** per server (e.g. `display_name = Billing API`, `env = prod`):
|
|
98
|
+
up to 50 per server, keys unique per server (case-insensitive). `display_name` is shown in
|
|
99
|
+
reports but never used for matching.
|
|
100
|
+
- Each server has an **SSH user** (default `ubuntu`, e.g. `ec2-user` on Amazon Linux) used for
|
|
101
|
+
every ssh/scp to it. It must be a plain POSIX user name (`[a-z_][a-z0-9_.-]*`, max 32).
|
|
102
|
+
- **SSH test** runs `ssh -i <pem> <user>@<ip>` with `BatchMode=yes`, `ConnectTimeout=10` and a
|
|
103
|
+
30 s overall limit, and shows hostname, OS release and architecture or a short error. New host
|
|
104
|
+
keys are accepted on first connect (`accept-new`); a changed host key is an error.
|
|
105
|
+
- **Login type** per server (dropdown on the server form): **PEM key** (default) or
|
|
106
|
+
**username + password**. The password is **stored per server, encrypted** (Fernet, see
|
|
107
|
+
[Secrets at rest](#secrets-at-rest)); it is never shown, logged or exported, and the form
|
|
108
|
+
field is always empty: when editing, leave it empty to keep the stored password. Switching a
|
|
109
|
+
server to PEM, deleting it, Clear All Servers or Reset Database removes its password.
|
|
110
|
+
Password login runs `sshpass -e ssh|scp ...`; the password reaches sshpass only through the
|
|
111
|
+
`SSHPASS` environment variable of the child process, never the command line. Install
|
|
112
|
+
`sshpass` on the workstation (`sudo apt install sshpass`); without it the connection fails
|
|
113
|
+
with a clear message. Analysis or patching of a password server without a usable stored
|
|
114
|
+
password is refused with a clear message. `sudo` on the server must still be passwordless
|
|
115
|
+
(`sudo -n`).
|
|
116
|
+
|
|
117
|
+
### Report upload
|
|
118
|
+
|
|
119
|
+
On **Reports**, choosing or dropping a `.json` file uploads it immediately (no extra click; a
|
|
120
|
+
plain *Upload Report* button is shown without JavaScript). Max 1 MiB; structural validation
|
|
121
|
+
only. The latest accepted report is kept; a failed upload does not replace it.
|
|
122
|
+
|
|
123
|
+
```json
|
|
124
|
+
{
|
|
125
|
+
"app-prod-01": ["CVE-2026-12345", "CVE-2026-67890"],
|
|
126
|
+
"database-prod-01": ["CVE-2026-22222"]
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Each key must be a configured server name (an unknown server rejects the whole report); each
|
|
131
|
+
value is an array of `CVE-YYYY-NNNN…` strings (case-insensitive, normalized and de-duplicated).
|
|
132
|
+
|
|
133
|
+
### Analysis (read-only)
|
|
134
|
+
|
|
135
|
+
**Analyze Report** starts a background run; the run page refreshes itself and shows each
|
|
136
|
+
server as *Waiting*, *Analyzing*, *Complete*, *Failed* (with the reason) or *Not supported*.
|
|
137
|
+
Servers are analyzed one after another; one failure never affects the others.
|
|
138
|
+
|
|
139
|
+
- **On the server**: one fixed read-only command as the server's SSH user, **without sudo**:
|
|
140
|
+
hostname, `/etc/os-release`, architecture, running kernel, `/run/reboot-required(.pkgs)`,
|
|
141
|
+
`dpkg-query` (binary → source package and versions) and `dpkg --audit`. Nothing is
|
|
142
|
+
downloaded, copied, installed or restarted.
|
|
143
|
+
- **OS detection**: the OS is taken from `/etc/os-release`; everything OS-specific (inventory,
|
|
144
|
+
security data lookups, planning, install, reboot check) sits behind an `OsAdapter`
|
|
145
|
+
(`services/os_adapters/`). Adapters: **Ubuntu** (analysis + patching) and **Amazon Linux
|
|
146
|
+
2023** (analysis only). For Amazon Linux a second fixed read-only command collects the `rpm`
|
|
147
|
+
inventory and the dnf releasever. Any other OS is shown as
|
|
148
|
+
`OS not supported yet: <name> <version>` (not a failure, never patchable).
|
|
149
|
+
- **On the workstation**: APT candidates and the `.deb` plan are resolved against a private
|
|
150
|
+
APT state per release and architecture (`<data dir>/apt/<codename>-<arch>/`, pockets
|
|
151
|
+
`<codename>`, `-updates`, `-security`). Every `apt-get` / `apt-cache` call overrides
|
|
152
|
+
`Dir::State`, `Dir::Cache` and `Dir::Etc`, so the workstation's own APT is never used or
|
|
153
|
+
changed. `apt-get -s` and `--print-uris` give exact versions, URIs, sizes and SHA256 without
|
|
154
|
+
downloading anything.
|
|
155
|
+
- **Canonical** (`https://ubuntu.com/security/cves/<CVE>.json`) decides applicability, fixed
|
|
156
|
+
version and status, per source package and Ubuntu release, with Debian version comparison.
|
|
157
|
+
Kernel CVEs are checked against the *running* kernel.
|
|
158
|
+
- **Amazon Linux 2023**: the repository `updateinfo.xml` (ALAS advisories) of the server's
|
|
159
|
+
releasever and of the latest release is fetched on the workstation (via
|
|
160
|
+
`cdn.amazonlinux.com` mirror lists) and compared with the installed rpm versions (EVR
|
|
161
|
+
comparison). Statuses: patch available, fix only in a newer releasever, already fixed,
|
|
162
|
+
package not installed, or no advisory (investigate). No plan is built and the server is
|
|
163
|
+
never patchable (*Patching not supported yet for Amazon Linux 2023*).
|
|
164
|
+
- **NVD** (CVE API 2.0) supplies only the CVSS **Severity** (Critical/High/Medium/Low/Unknown)
|
|
165
|
+
and score; it never changes a finding's status or plan. Canonical's priority is shown as
|
|
166
|
+
**Ubuntu Priority**.
|
|
167
|
+
- A server with unconfigured/half-installed packages (`dpkg --audit`) gets a blocker
|
|
168
|
+
("run sudo dpkg --configure -a") instead of a plan. Packages already at or above their
|
|
169
|
+
target version are never planned (shown as an "already at target" warning).
|
|
170
|
+
- Findings are grouped into **Action required**, **Investigate** and **No action**. Anything
|
|
171
|
+
that cannot be decided is shown as such, never as safe.
|
|
172
|
+
- Every run is stored as a snapshot (report, server facts, findings, plan); older runs stay
|
|
173
|
+
viewable. **Export to Excel** builds an `.xlsx` (Summary, CVE Findings, Package Plan) from
|
|
174
|
+
that snapshot only, without any network or SSH access.
|
|
175
|
+
|
|
176
|
+
**Re-analyze and Retry**
|
|
177
|
+
|
|
178
|
+
- **Retry failed lookups** (run page): re-runs only the Canonical lookups that failed in that
|
|
179
|
+
run.
|
|
180
|
+
- **Retry these CVEs** (server report, Investigate bucket): fetches those CVEs again and
|
|
181
|
+
re-analyzes the server.
|
|
182
|
+
- **Re-analyze** (server report): analyzes the server again, fetching all of its CVEs from
|
|
183
|
+
Canonical again.
|
|
184
|
+
|
|
185
|
+
### Patching one server
|
|
186
|
+
|
|
187
|
+
Each server report has **Reject** and **Approve & Patch** (after a confirmation page with a
|
|
188
|
+
**Skip reboot** checkbox, unchecked by default). Only the latest analysis of a server can be
|
|
189
|
+
executed, and each approved analysis only once. The pipeline stops at the first failing step:
|
|
190
|
+
|
|
191
|
+
1. **Revalidate** hostname, release, architecture and installed versions against the analysis
|
|
192
|
+
(drift → *PATCH ABORTED — SERVER STATE CHANGED*); check `sudo -n true`. Packages already
|
|
193
|
+
at target are dropped; if nothing is left the result is **ALREADY PATCHED**.
|
|
194
|
+
2. **Download** each approved `.deb` from its recorded URI; keep it only if size and SHA256
|
|
195
|
+
match the plan.
|
|
196
|
+
3. **Transfer** with `scp` to `/tmp/<server name>` and verify size + `sha256sum` (one retry).
|
|
197
|
+
4. **Simulate** `apt-get -s install <explicit .deb paths>`: must install exactly the approved
|
|
198
|
+
packages/versions, with no removal or downgrade.
|
|
199
|
+
5. **Install** `sudo -n apt-get install -y <explicit .deb paths>` with no remote APT sources,
|
|
200
|
+
`--no-remove`, `DEBIAN_FRONTEND=noninteractive`, `NEEDRESTART_MODE=l`.
|
|
201
|
+
6. **Verify** installed versions, `dpkg --audit`, each CVE against Canonical's fixed version,
|
|
202
|
+
and `/run/reboot-required`.
|
|
203
|
+
7. **Clean up** local and remote staging.
|
|
204
|
+
8. **Reboot**, only after a verified patch, only if `/run/reboot-required` exists and **Skip
|
|
205
|
+
reboot** is unchecked: `sudo -n reboot`, wait up to 10 minutes for a new boot id, record
|
|
206
|
+
uptime and kernel. The reboot result is recorded separately from the patch result.
|
|
207
|
+
|
|
208
|
+
A failed or interrupted install is never retried or rolled back; if the connection drops the
|
|
209
|
+
server is inspected once more and the result is proven or recorded as *EXECUTION STATE
|
|
210
|
+
UNKNOWN*. A new analysis is required before trying again.
|
|
211
|
+
|
|
212
|
+
### Patch All
|
|
213
|
+
|
|
214
|
+
The analysis run page has **Patch All** with a **Skip reboot** checkbox. The confirmation page
|
|
215
|
+
lists the eligible servers in order and the **SKIPPED** ones with their reasons. A server
|
|
216
|
+
analyzed again after this run is patched from its latest analysis. Servers are patched **one
|
|
217
|
+
at a time** with the pipeline above; the queue **stops at the first failure** and the rest are
|
|
218
|
+
shown as **NOT RUN**. Only one patch execution or queue runs at a time.
|
|
219
|
+
|
|
220
|
+
### Settings
|
|
221
|
+
|
|
222
|
+
- **Caches** (top of the page): one badge per cache (*NVD cache: <N> CVEs, oldest <age>, TTL
|
|
223
|
+
<ttl>*, same for Canonical and Amazon updateinfo; blue when the latest analysis answered
|
|
224
|
+
lookups from it, gray when unused or empty), the **Cache TTL** of each cache and **Clear
|
|
225
|
+
Security Cache**, which empties all three caches (`nvd_cache`, `cve_metadata_cache`,
|
|
226
|
+
`amazon_updateinfo_cache`) and the in-memory lookup state. TTLs are entered as hours (`24h`)
|
|
227
|
+
or days (`30d`), between 1 hour and 365 days, and stored in the database. Changing a TTL
|
|
228
|
+
deletes nothing; expired entries are refreshed on their next lookup. The same badges appear
|
|
229
|
+
next to the NVD API key badge on the Pre-Patch Analysis pages and link to this section.
|
|
230
|
+
- **Local Patch Download Directory**: template, default `/tmp/${server_name}`; must contain
|
|
231
|
+
`${server_name}` and resolve to a safe absolute path.
|
|
232
|
+
- **Logging**: the **Log directory** (default: the per-user log directory from platformdirs, or
|
|
233
|
+
`$EC2PATCHER_LOG_DIR`) and the current log file path. The directory is created if needed and
|
|
234
|
+
must be writable, or it is not saved. Logs rotate at 5 MB, keeping 5 old files.
|
|
235
|
+
- **Security Data**: Canonical request settings (timeout, pacing, retries, proxy).
|
|
236
|
+
- **Reset Database**: type `RESET` to remove all stored data (refused while an analysis or
|
|
237
|
+
patch is running).
|
|
238
|
+
|
|
239
|
+
### Caching and NVD API key
|
|
240
|
+
|
|
241
|
+
All caches live in the application SQLite database; failed lookups are never cached and a
|
|
242
|
+
cache error never fails a lookup. The TTLs below are the defaults (**Settings → Caches**; a
|
|
243
|
+
saved Canonical TTL also overrides `EC2PATCHER_CANONICAL_CACHE_TTL_HOURS`). Each analysis run
|
|
244
|
+
records, per cache, how many lookups came from the cache and how many were live (*NVD: x from
|
|
245
|
+
cache / y live*, shown on the run page and the server report; re-analyses and retries add to
|
|
246
|
+
it).
|
|
247
|
+
|
|
248
|
+
- **Canonical** (`cve_metadata_cache`): reused for 24 h (1 h while a release is under
|
|
249
|
+
investigation); older entries are refreshed and used as a marked fallback when ubuntu.com is
|
|
250
|
+
unreachable.
|
|
251
|
+
- **Amazon updateinfo** (`amazon_updateinfo_cache`): per repository, reused for 24 h.
|
|
252
|
+
- **NVD** (`nvd_cache`): raw CVSS metrics per CVE, reused for **30 days**; older entries are
|
|
253
|
+
refreshed and used as a *stale cache* fallback when NVD is unreachable. Without any data the
|
|
254
|
+
severity is **Unknown**. (The old on-disk NVD cache under `~/.cache/ec2patcher/nvd/` is no
|
|
255
|
+
longer read or written and can be deleted.)
|
|
256
|
+
|
|
257
|
+
NVD requests are spaced 6 s apart (public limit). With an API key they are spaced 0.6 s apart.
|
|
258
|
+
Enter the key on **Settings → NVD API Key** (stored encrypted; shown only as its last 4
|
|
259
|
+
characters, with **Replace** / **Clear**), or provide it through the environment — never in a
|
|
260
|
+
file in this repository:
|
|
261
|
+
|
|
262
|
+
```bash
|
|
263
|
+
export NVD_API_KEY=... # your own key; it is sent only in the apiKey request header
|
|
264
|
+
```
|
|
265
|
+
Request a free key at https://nvd.nist.gov/developers/request-an-api-key.
|
|
266
|
+
|
|
267
|
+
A key saved in Settings **overrides** `NVD_API_KEY`; Clear falls back to the environment. The
|
|
268
|
+
key is never logged, exported or shown in full. Saving a key, and the **Test key** button, send
|
|
269
|
+
one keyed request to NVD right away (cached CVSS lookups send none). The result and its time are
|
|
270
|
+
kept in the settings table (never the key itself) and updated by every keyed lookup, so they
|
|
271
|
+
survive restarts. The Pre-Patch Analysis pages show only the state and source: *NVD API key: not
|
|
272
|
+
set*, *unknown* (not checked yet, or NVD could not be reached), *valid (checked <time>)* (green,
|
|
273
|
+
HTTP 200) or *NVD API key rejected* (red, HTTP 403, or HTTP 404 with NVD's invalid-apiKey
|
|
274
|
+
message), each followed by *from Settings (DB)* or *from NVD_API_KEY env var* whenever a key is
|
|
275
|
+
configured. For *unknown* the reason (HTTP status or network error, or *not checked yet*) is in
|
|
276
|
+
the badge tooltip and the Settings notice; HTTP 429 / 5xx is retried once (after Retry-After)
|
|
277
|
+
before a check ends as unknown. At start the app checks the key again in the background if its
|
|
278
|
+
last check is unknown or older than 24 h. A rejected key never
|
|
279
|
+
marks a CVE as unknown to NVD and is never cached. If the saved key cannot be decrypted the badge
|
|
280
|
+
is red and asks to enter it again in Settings (no key is sent until then).
|
|
281
|
+
|
|
282
|
+
### Secrets at rest
|
|
283
|
+
|
|
284
|
+
Server passwords and the Settings NVD API key are encrypted with Fernet (`cryptography`). The
|
|
285
|
+
key file is `~/.config/ec2patcher/secret.key` (or `$EC2PATCHER_CONFIG_DIR/secret.key`), created
|
|
286
|
+
with mode `0600` on first use and never stored in the database or the repository. If it is
|
|
287
|
+
missing or does not match, nothing fails silently: the Servers / Settings pages, Test SSH,
|
|
288
|
+
analysis and patching show a clear error asking you to enter the secret again (it is then
|
|
289
|
+
encrypted with the current key). Back up the key file together with the database if you move
|
|
290
|
+
them to another machine.
|
|
291
|
+
|
|
292
|
+
## Requirements (workstation)
|
|
293
|
+
|
|
294
|
+
- Python 3.10+
|
|
295
|
+
- OpenSSH client (`ssh`, `scp`) on `PATH`
|
|
296
|
+
- APT (`apt-get`, `apt-cache`) and `/usr/share/keyrings/ubuntu-archive-keyring.gpg`
|
|
297
|
+
- `sshpass` only for servers with password login (`sudo apt install sshpass`)
|
|
298
|
+
- Internet access to the Ubuntu archive (`archive.ubuntu.com` / `security.ubuntu.com`, or
|
|
299
|
+
`ports.ubuntu.com` for arm64), `ubuntu.com`, `cdn.amazonlinux.com` (Amazon Linux servers)
|
|
300
|
+
and `services.nvd.nist.gov` (optional: without it severities are Unknown).
|
|
301
|
+
`HTTPS_PROXY` / `NO_PROXY` are honoured.
|
|
302
|
+
|
|
303
|
+
## Install
|
|
304
|
+
|
|
305
|
+
With [pipx](https://pipx.pypa.io/) (recommended; installs the `ec2patcher` command in its own
|
|
306
|
+
virtual environment):
|
|
307
|
+
|
|
308
|
+
```bash
|
|
309
|
+
pipx install ec2patcher # from PyPI
|
|
310
|
+
pipx install git+https://github.com/Amiri83/EC2Patcher.git # latest main from GitHub
|
|
311
|
+
pipx upgrade ec2patcher # later updates
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
From a checkout, for development:
|
|
315
|
+
|
|
316
|
+
```bash
|
|
317
|
+
python3 -m venv .venv
|
|
318
|
+
.venv/bin/pip install -e ".[dev]" # or without [dev] if you don't run tests
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
## Run
|
|
322
|
+
|
|
323
|
+
```bash
|
|
324
|
+
ec2patcher # from a checkout: .venv/bin/ec2patcher or .venv/bin/python -m ec2patcher
|
|
325
|
+
ec2patcher --version
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
Open <http://127.0.0.1:8080/> (a browser opens automatically when a desktop session is
|
|
329
|
+
available). Stop with **Shutdown App** in the sidebar or Ctrl+C.
|
|
330
|
+
|
|
331
|
+
| Option | Default | Description |
|
|
332
|
+
|---|---|---|
|
|
333
|
+
| `--host` | `127.0.0.1` | Bind address (no authentication: keep it local) |
|
|
334
|
+
| `--port` | `8080` | Port |
|
|
335
|
+
| `--data-dir` | per-user data dir | Database and log location (also `$EC2PATCHER_DATA_DIR`) |
|
|
336
|
+
| `--apt-state-dir` | `<data dir>/apt` | Private APT state (also `$EC2PATCHER_APT_STATE_DIR`) |
|
|
337
|
+
| `--apt-max-age-hours` | `6` | Refresh the private APT lists when older; `0` = every run (also `$EC2PATCHER_APT_MAX_AGE_HOURS`) |
|
|
338
|
+
| `--no-browser` | off | Do not open a browser |
|
|
339
|
+
| `--version` | | Print the version and exit |
|
|
340
|
+
|
|
341
|
+
Other environment variables: `NVD_API_KEY` (a key saved in Settings overrides it),
|
|
342
|
+
`EC2PATCHER_CONFIG_DIR` (location of the secret key file), `EC2PATCHER_LOG_DIR` (default log directory),
|
|
343
|
+
`EC2PATCHER_CANONICAL_TIMEOUT_SECONDS` (20),
|
|
344
|
+
`EC2PATCHER_CANONICAL_CACHE_TTL_HOURS` (24), `EC2PATCHER_CANONICAL_BREAKER_THRESHOLD` (3).
|
|
345
|
+
|
|
346
|
+
## Data location (Linux defaults)
|
|
347
|
+
|
|
348
|
+
| What | Path |
|
|
349
|
+
|---|---|
|
|
350
|
+
| SQLite database (incl. Canonical and NVD caches) | `~/.local/share/ec2patcher/ec2patcher.db` |
|
|
351
|
+
| Secret key file (encrypts stored passwords / NVD key; mode 0600) | `~/.config/ec2patcher/secret.key` |
|
|
352
|
+
| Log file (rotating, 5 × 5 MB; directory configurable in Settings) | `~/.local/state/ec2patcher/log/ec2patcher.log` |
|
|
353
|
+
| Private APT state | `~/.local/share/ec2patcher/apt/<codename>-<arch>/` |
|
|
354
|
+
|
|
355
|
+
The schema is created and migrated automatically on startup (`PRAGMA user_version`); existing
|
|
356
|
+
data is kept. Run one EC2Patcher process per database.
|
|
357
|
+
|
|
358
|
+
## Test and lint
|
|
359
|
+
|
|
360
|
+
```bash
|
|
361
|
+
.venv/bin/python -m pytest -q
|
|
362
|
+
.venv/bin/ruff check .
|
|
363
|
+
.venv/bin/ruff format --check .
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
The tests mock `ssh`, `scp`, downloads, Canonical, NVD and the local APT backend; they never
|
|
367
|
+
need a real server, PEM key, internet access or package installs.
|
|
368
|
+
|
|
369
|
+
### Integration tests (local containers, no AWS)
|
|
370
|
+
|
|
371
|
+
`scripts/test-targets/` holds two SSH targets with key-only auth on localhost: Ubuntu 24.04
|
|
372
|
+
(user `ubuntu`, port 2201) and Amazon Linux 2023 (user `ec2-user`, port 2202). The key pair is
|
|
373
|
+
generated at runtime into `scripts/test-targets/.keys/` (git-ignored). Docker runs via `sudo`
|
|
374
|
+
(set `DOCKER=docker` to change that).
|
|
375
|
+
|
|
376
|
+
```bash
|
|
377
|
+
sh scripts/test-targets/up.sh # build + start, generate the key on first use
|
|
378
|
+
.venv/bin/python -m pytest -q -m integration # deselected by default
|
|
379
|
+
sh scripts/test-targets/down.sh
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
Overrides: `EC2P_IT_HOST`, `EC2P_IT_UBUNTU_PORT`, `EC2P_IT_AMAZON_PORT`, `EC2P_IT_KEY`.
|
|
383
|
+
|
|
384
|
+
GitHub Actions (`.github/workflows/tests.yml`) runs ruff, the test suite (without integration
|
|
385
|
+
tests) and a package build check on every pull request.
|
|
386
|
+
|
|
387
|
+
## Release
|
|
388
|
+
|
|
389
|
+
The version lives only in `pyproject.toml` (`ec2patcher --version` reads the installed
|
|
390
|
+
metadata). To release:
|
|
391
|
+
|
|
392
|
+
1. Bump `version` in `pyproject.toml` and merge it to `main`.
|
|
393
|
+
2. Check locally: `.venv/bin/python -m build && .venv/bin/twine check dist/*`.
|
|
394
|
+
3. Push a tag `v<version>` (e.g. `v1.0.0`). `.github/workflows/publish.yml` checks that the
|
|
395
|
+
tag matches the version, runs the tests, builds the sdist + wheel and publishes them to
|
|
396
|
+
PyPI with **Trusted Publishing** (OIDC, environment `pypi`): no API token is stored in the
|
|
397
|
+
repository or in GitHub secrets. The trusted publisher must be configured once on PyPI for
|
|
398
|
+
this repository and workflow.
|
|
399
|
+
|
|
400
|
+
## Security notes
|
|
401
|
+
|
|
402
|
+
- Binds to `127.0.0.1` by default; requests with a non-local `Host` header and cross-site POSTs
|
|
403
|
+
are rejected (CSRF / DNS rebinding).
|
|
404
|
+
- Subprocesses never use `shell=True`; the remote analysis command is fixed; package names,
|
|
405
|
+
versions and paths are validated against strict patterns.
|
|
406
|
+
- Analysis uses no sudo and changes nothing. Patching uses `sudo -n` only for the sudo check,
|
|
407
|
+
the `apt-get` simulation/install of the explicit `.deb` files and the optional reboot.
|
|
408
|
+
- PEM contents are never read; the NVD API key and SSH passwords are stored only encrypted
|
|
409
|
+
(key file outside the database, mode 0600) and never appear in HTML, logs, exports, error
|
|
410
|
+
messages or command lines (passwords reach sshpass via `SSHPASS` only). Unexpected errors
|
|
411
|
+
show a generic message in the GUI; details go to the log.
|
|
412
|
+
- The Fernet key file lives in the config directory, never in the package, the database or
|
|
413
|
+
this repository; the built wheel and sdist contain no secrets, keys or server data.
|
|
414
|
+
|
|
415
|
+
## License
|
|
416
|
+
|
|
417
|
+
MIT, see [LICENSE](https://github.com/Amiri83/EC2Patcher/blob/main/LICENSE).
|