kitsune-kit 0.4.0 → 0.5.0
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +117 -2
- data/CONTRIBUTING.md +83 -0
- data/README.md +108 -136
- data/SECURITY.md +25 -0
- data/bin/kit +4 -1
- data/docs/architecture/decisions/0001-product-boundary.md +23 -0
- data/docs/architecture/decisions/0002-core-and-interfaces.md +23 -0
- data/docs/architecture/decisions/0003-configuration-state-and-secrets.md +23 -0
- data/docs/architecture/decisions/0004-supported-platforms.md +19 -0
- data/docs/architecture/decisions/0005-operation-semantics.md +19 -0
- data/docs/architecture/decisions/0006-tui-backend.md +21 -0
- data/docs/architecture/provider-adapters.md +49 -0
- data/docs/architecture.md +89 -0
- data/docs/commands.md +210 -0
- data/docs/configuration.md +210 -0
- data/docs/getting-started.md +133 -0
- data/docs/providers/digitalocean.md +52 -0
- data/docs/releasing.md +43 -0
- data/docs/roadmap.md +46 -0
- data/docs/security-audit.md +130 -0
- data/docs/security.md +116 -0
- data/docs/services/postgres.md +78 -0
- data/docs/services/redis.md +53 -0
- data/docs/testing.md +200 -0
- data/docs/troubleshooting.md +123 -0
- data/docs/tui.md +57 -0
- data/lib/kitsune/kit/adapters/confirming_host_key_verifier.rb +42 -0
- data/lib/kitsune/kit/adapters/digitalocean_provider.rb +217 -0
- data/lib/kitsune/kit/adapters/fake_clock.rb +34 -0
- data/lib/kitsune/kit/adapters/fake_provider.rb +107 -0
- data/lib/kitsune/kit/adapters/fake_reporter.rb +34 -0
- data/lib/kitsune/kit/adapters/fake_secret_store.rb +35 -0
- data/lib/kitsune/kit/adapters/fake_state_store.rb +91 -0
- data/lib/kitsune/kit/adapters/fake_transport.rb +56 -0
- data/lib/kitsune/kit/adapters/net_ssh_transport.rb +155 -0
- data/lib/kitsune/kit/adapters/provider.rb +34 -0
- data/lib/kitsune/kit/adapters/transport.rb +22 -0
- data/lib/kitsune/kit/adapters/transport_factory.rb +116 -0
- data/lib/kitsune/kit/application.rb +170 -0
- data/lib/kitsune/kit/cancellation.rb +21 -0
- data/lib/kitsune/kit/cli.rb +802 -64
- data/lib/kitsune/kit/clock.rb +11 -0
- data/lib/kitsune/kit/configuration.rb +493 -0
- data/lib/kitsune/kit/errors.rb +98 -0
- data/lib/kitsune/kit/events.rb +55 -0
- data/lib/kitsune/kit/operations/ensure_dns_records.rb +165 -0
- data/lib/kitsune/kit/operations/ensure_server.rb +135 -0
- data/lib/kitsune/kit/operations/ensure_service.rb +285 -0
- data/lib/kitsune/kit/operations/remote_script.rb +214 -0
- data/lib/kitsune/kit/operations/service_backup.rb +71 -0
- data/lib/kitsune/kit/operations/service_files.rb +127 -0
- data/lib/kitsune/kit/operations/service_firewall.rb +190 -0
- data/lib/kitsune/kit/operations/service_state.rb +59 -0
- data/lib/kitsune/kit/plan.rb +72 -0
- data/lib/kitsune/kit/reporters/human.rb +89 -0
- data/lib/kitsune/kit/reporters/json.rb +65 -0
- data/lib/kitsune/kit/reporters/reporter.rb +11 -0
- data/lib/kitsune/kit/result.rb +28 -0
- data/lib/kitsune/kit/run_journal.rb +102 -0
- data/lib/kitsune/kit/run_logger.rb +37 -0
- data/lib/kitsune/kit/scripts/docker.sh +137 -0
- data/lib/kitsune/kit/scripts/firewall.sh +205 -0
- data/lib/kitsune/kit/scripts/metrics.sh +54 -0
- data/lib/kitsune/kit/scripts/ssh.sh +95 -0
- data/lib/kitsune/kit/scripts/swap.sh +86 -0
- data/lib/kitsune/kit/scripts/unattended.sh +81 -0
- data/lib/kitsune/kit/scripts/user.sh +114 -0
- data/lib/kitsune/kit/secret_filter.rb +54 -0
- data/lib/kitsune/kit/secret_store.rb +32 -0
- data/lib/kitsune/kit/secret_stores/store.rb +12 -0
- data/lib/kitsune/kit/service_compose.rb +72 -0
- data/lib/kitsune/kit/state_store.rb +158 -0
- data/lib/kitsune/kit/state_stores/store.rb +15 -0
- data/lib/kitsune/kit/tui/actions.rb +72 -0
- data/lib/kitsune/kit/tui/application.rb +35 -0
- data/lib/kitsune/kit/tui/controller.rb +162 -0
- data/lib/kitsune/kit/tui/renderer.rb +134 -0
- data/lib/kitsune/kit/tui/state.rb +18 -0
- data/lib/kitsune/kit/tui/store.rb +88 -0
- data/lib/kitsune/kit/tui/terminal.rb +95 -0
- data/lib/kitsune/kit/version.rb +1 -1
- data/lib/kitsune/kit/workflows/apply_plan.rb +145 -0
- data/lib/kitsune/kit/workflows/base.rb +31 -0
- data/lib/kitsune/kit/workflows/build_plan.rb +33 -0
- data/lib/kitsune/kit/workflows/destroy_server.rb +84 -0
- data/lib/kitsune/kit/workflows/doctor.rb +225 -0
- data/lib/kitsune/kit/workflows/environment_selection.rb +70 -0
- data/lib/kitsune/kit/workflows/import_server.rb +100 -0
- data/lib/kitsune/kit/workflows/initialize_project.rb +121 -0
- data/lib/kitsune/kit/workflows/inspect_environment.rb +44 -0
- data/lib/kitsune/kit/workflows/rollback.rb +54 -0
- data/lib/kitsune/kit/workflows/support_bundle.rb +82 -0
- data/lib/kitsune/kit.rb +41 -2
- metadata +122 -79
- data/.rspec +0 -3
- data/Rakefile +0 -8
- data/kitsune-kit-logo.jpg +0 -0
- data/lib/kitsune/blueprints/.env.template +0 -31
- data/lib/kitsune/blueprints/docker/postgres.yml +0 -27
- data/lib/kitsune/blueprints/docker/redis.yml +0 -23
- data/lib/kitsune/blueprints/kit.env.template +0 -1
- data/lib/kitsune/kit/ansi_color.rb +0 -78
- data/lib/kitsune/kit/commands/bootstrap.rb +0 -134
- data/lib/kitsune/kit/commands/bootstrap_docker.rb +0 -66
- data/lib/kitsune/kit/commands/dns.rb +0 -112
- data/lib/kitsune/kit/commands/init.rb +0 -148
- data/lib/kitsune/kit/commands/install_docker_engine.rb +0 -146
- data/lib/kitsune/kit/commands/postinstall_docker.rb +0 -142
- data/lib/kitsune/kit/commands/provision.rb +0 -43
- data/lib/kitsune/kit/commands/setup_do_metrics.rb +0 -123
- data/lib/kitsune/kit/commands/setup_docker_prereqs.rb +0 -151
- data/lib/kitsune/kit/commands/setup_firewall.rb +0 -132
- data/lib/kitsune/kit/commands/setup_postgres_docker.rb +0 -246
- data/lib/kitsune/kit/commands/setup_redis_docker.rb +0 -241
- data/lib/kitsune/kit/commands/setup_swap.rb +0 -151
- data/lib/kitsune/kit/commands/setup_unattended.rb +0 -132
- data/lib/kitsune/kit/commands/setup_user.rb +0 -189
- data/lib/kitsune/kit/commands/ssh.rb +0 -46
- data/lib/kitsune/kit/commands/switch_env.rb +0 -42
- data/lib/kitsune/kit/defaults.rb +0 -91
- data/lib/kitsune/kit/env_loader.rb +0 -41
- data/lib/kitsune/kit/options_builder.rb +0 -26
- data/lib/kitsune/kit/provisioner.rb +0 -107
- data/sig/kitsune/kit.rbs +0 -6
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
# Getting started
|
|
2
|
+
|
|
3
|
+
This guide creates a new DigitalOcean server from an empty project. Read the plan before applying it; a real run creates billable infrastructure.
|
|
4
|
+
|
|
5
|
+
## 1. Prepare DigitalOcean
|
|
6
|
+
|
|
7
|
+
Create a DigitalOcean API token with the minimum access needed to inspect/create/delete Droplets and, only if configured, domain records. Keep development/CI infrastructure in a separate project or account where practical.
|
|
8
|
+
|
|
9
|
+
Upload your public SSH key to DigitalOcean and note its numeric key ID. Kitsune Kit needs the provider key ID at creation time and the corresponding private key locally.
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
chmod 600 ~/.ssh/id_ed25519
|
|
13
|
+
export DO_API_TOKEN="your-token"
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Do not put the token or a private key in `.kitsune/config.yml`.
|
|
17
|
+
|
|
18
|
+
## 2. Initialize the project
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
mkdir myapp-infrastructure
|
|
22
|
+
cd myapp-infrastructure
|
|
23
|
+
kit init
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Generated files:
|
|
27
|
+
|
|
28
|
+
```text
|
|
29
|
+
.kitsune/config.yml
|
|
30
|
+
.kitsune/environments/development.yml
|
|
31
|
+
.kitsune/environment
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Kitsune Kit adds state, logs, support bundles, known hosts and the selected environment to `.gitignore`. Commit the non-secret configuration files; back up `.kitsune/state/` securely outside Git once resources exist.
|
|
35
|
+
|
|
36
|
+
`kit init` does not overwrite generated files. `kit init --force` replaces them and should only be used intentionally.
|
|
37
|
+
|
|
38
|
+
## 3. Edit configuration
|
|
39
|
+
|
|
40
|
+
At minimum, replace:
|
|
41
|
+
|
|
42
|
+
```yaml
|
|
43
|
+
server:
|
|
44
|
+
name: myapp-development
|
|
45
|
+
region: sfo3
|
|
46
|
+
size: s-1vcpu-1gb
|
|
47
|
+
image: ubuntu-24-04-x64
|
|
48
|
+
ssh_key_id: "12345678"
|
|
49
|
+
|
|
50
|
+
ssh:
|
|
51
|
+
key_path: ~/.ssh/id_ed25519
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Keep `services.postgres.publish` and `services.redis.publish` false unless remote TCP access is a deliberate requirement. See [Configuration](configuration.md).
|
|
55
|
+
|
|
56
|
+
## 4. Run preflight checks
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
kit doctor
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Before the Droplet exists, `doctor` reports it as a warning. Configuration, local key permissions, provider credentials and state should pass. `doctor` never creates or modifies resources.
|
|
63
|
+
|
|
64
|
+
## 5. Review the plan
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
kit plan
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Markers are:
|
|
71
|
+
|
|
72
|
+
```text
|
|
73
|
+
+ create
|
|
74
|
+
~ update
|
|
75
|
+
- delete
|
|
76
|
+
= no change
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
A planned immutable server change is destructive and `apply` refuses to replace it implicitly. Destroying a server is always a separate, explicitly confirmed action.
|
|
80
|
+
|
|
81
|
+
## 6. Apply
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
kit apply
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Kitsune Kit creates and records the provider ID before waiting, so a timeout can be resumed without creating a duplicate. Remote operations then configure the user, SSH, firewall, updates, swap, Docker, optional services and DNS.
|
|
88
|
+
|
|
89
|
+
On the first SSH connection, Kitsune Kit displays the key type and SHA256 host-key fingerprint. Verify it through an independent trusted channel before accepting it. The accepted key is stored in `.kitsune/known_hosts` with restricted permissions.
|
|
90
|
+
|
|
91
|
+
If a step fails:
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
kit status
|
|
95
|
+
kit resume
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Only unfinished steps from a compatible saved run are executed. Do not delete state to work around a failure.
|
|
99
|
+
|
|
100
|
+
## 7. Verify and reapply
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
kit doctor
|
|
104
|
+
kit plan
|
|
105
|
+
kit apply
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
After a successful run, `doctor` should pass and `plan` should contain zero changes. Repeated `apply` is expected to be idempotent.
|
|
109
|
+
|
|
110
|
+
## Non-interactive first run
|
|
111
|
+
|
|
112
|
+
CI cannot answer confirmation or host-key prompts. First obtain and independently verify the expected fingerprint. Then run:
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
kit plan --format json --no-input
|
|
116
|
+
kit apply \
|
|
117
|
+
--format json \
|
|
118
|
+
--no-input \
|
|
119
|
+
--yes \
|
|
120
|
+
--trust-host-key SHA256:verified-fingerprint
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
The fingerprint option must match exactly. It is not a switch that disables host-key verification.
|
|
124
|
+
|
|
125
|
+
## Production checklist
|
|
126
|
+
|
|
127
|
+
- Use a dedicated environment overlay and `kit env use production`.
|
|
128
|
+
- Restrict `ssh.allowed_cidrs` to operator/CI networks when practical.
|
|
129
|
+
- Keep data services private or specify the smallest possible allowed CIDRs.
|
|
130
|
+
- Use generated, unique PostgreSQL and Redis passwords.
|
|
131
|
+
- Run `doctor`, archive the reviewed JSON plan and back up local state.
|
|
132
|
+
- Test service backup and restore procedures before relying on them.
|
|
133
|
+
- Retain provider-console access while changing SSH policy.
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# DigitalOcean provider
|
|
2
|
+
|
|
3
|
+
DigitalOcean is the only provider supported in the 0.5 release. Provider calls are isolated behind the provider contract so the core and presentation layers do not depend on DropletKit directly.
|
|
4
|
+
|
|
5
|
+
## Credentials and permissions
|
|
6
|
+
|
|
7
|
+
Set `provider.token_env` to the name of a process environment variable (default `DO_API_TOKEN`) and export the token before running Kitsune Kit.
|
|
8
|
+
|
|
9
|
+
The token needs Droplet read/write access. DNS read/write access is required only when `dns.domains` is non-empty. Prefer a dedicated account/project for CI and test resources, and grant the minimum scopes available for your workflow.
|
|
10
|
+
|
|
11
|
+
## SSH key IDs
|
|
12
|
+
|
|
13
|
+
`server.ssh_key_id` is the DigitalOcean key ID, not a filesystem path or public-key text. `ssh.key_path` is the corresponding local private key. Kitsune Kit passes the ID only during Droplet creation and never displays it in plan details.
|
|
14
|
+
|
|
15
|
+
For the exact commands to create a dedicated test key, upload its public half, retrieve the numeric ID and
|
|
16
|
+
configure local or GitHub E2E runs, see [DigitalOcean E2E](../testing.md#digitalocean-e2e).
|
|
17
|
+
|
|
18
|
+
## Identity and ownership
|
|
19
|
+
|
|
20
|
+
A server match requires both the exact configured name and all configured tags. Keep the default `kitsune-managed` tag. State stores the returned provider ID immediately after creation.
|
|
21
|
+
|
|
22
|
+
Deletion requires:
|
|
23
|
+
|
|
24
|
+
1. an ID present in local managed state;
|
|
25
|
+
2. the provider object still existing or being confirmed absent;
|
|
26
|
+
3. exact configured name and expected tags when it exists;
|
|
27
|
+
4. exact operator confirmation.
|
|
28
|
+
|
|
29
|
+
Kitsune Kit never deletes a Droplet by name alone and never adopts a same-name untagged Droplet.
|
|
30
|
+
|
|
31
|
+
## Creation and timeouts
|
|
32
|
+
|
|
33
|
+
Before server lookup or creation, Kitsune Kit queries DigitalOcean's region, size and distribution-image catalogs. The selected region must be active, the size must be available there, and the image must support it. An unavailable choice is an actionable configuration error; no Droplet is created.
|
|
34
|
+
|
|
35
|
+
The creation request includes name, region, size, supported Ubuntu image, SSH key ID and tags. Kitsune Kit polls until the Droplet is active and has a public IPv4 address. A timeout is retryable: the saved provider ID lets `kit resume` wait for the same Droplet instead of creating another.
|
|
36
|
+
|
|
37
|
+
Provider authentication, quota, validation and connectivity errors are mapped to stable domain error codes. Technical provider messages are not treated as safe user output.
|
|
38
|
+
|
|
39
|
+
## DNS
|
|
40
|
+
|
|
41
|
+
For each hostname, Public Suffix List rules determine the zone and relative record name. Kitsune Kit finds records by exact zone/name/type, persists returned record IDs and captures previous values before an update.
|
|
42
|
+
|
|
43
|
+
Rollback deletes records it created and restores records it updated. If a run fails between records, each completed record is already in state and resume does not duplicate it.
|
|
44
|
+
|
|
45
|
+
The zone must already exist in the DigitalOcean account; Kitsune Kit does not transfer domains or change nameservers.
|
|
46
|
+
|
|
47
|
+
## Real-infrastructure tests
|
|
48
|
+
|
|
49
|
+
The manually dispatched E2E suite uses unique names, tags every Droplet with `kitsune-ci` and
|
|
50
|
+
`kitsune-expires-EPOCH`, saves state immediately and destroys in `ensure`. A separate always-run cleanup job
|
|
51
|
+
deletes only expired resources carrying both conventions after an authorized workflow run. See
|
|
52
|
+
[Testing](../testing.md).
|
data/docs/releasing.md
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# Release process
|
|
2
|
+
|
|
3
|
+
Kitsune Kit follows Semantic Versioning. Before 1.0, minor releases may make incompatible changes, but every schema or command change must be documented in `CHANGELOG.md`.
|
|
4
|
+
|
|
5
|
+
## One-time repository setup
|
|
6
|
+
|
|
7
|
+
1. Protect `main` and version tags; require the CI jobs.
|
|
8
|
+
2. Create a GitHub environment named `release` with required reviewers.
|
|
9
|
+
3. Configure `release.yml` as a trusted publisher for `kitsune-kit` on RubyGems.org, scoped to the `release` environment.
|
|
10
|
+
4. Require MFA for gem owners and protect GitHub accounts with strong MFA.
|
|
11
|
+
5. Configure the separate DigitalOcean E2E secrets/account and budget alerts.
|
|
12
|
+
|
|
13
|
+
The workflow uses OIDC short-lived credentials through `rubygems/release-gem`; no `GEM_HOST_API_KEY` secret is stored.
|
|
14
|
+
|
|
15
|
+
## Release checklist
|
|
16
|
+
|
|
17
|
+
1. Ensure normal CI is green on every supported Ruby and Ubuntu combination.
|
|
18
|
+
2. Run/inspect the latest real DigitalOcean lifecycle; stable releases must not proceed without a green E2E.
|
|
19
|
+
3. Run locally:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
bundle exec rake ci
|
|
23
|
+
bundle exec rake integration
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
4. Move `Unreleased` entries into a dated version section and restore an empty `Unreleased` section.
|
|
27
|
+
5. Update `lib/kitsune/kit/version.rb` once. The version must match the intended `vVERSION` tag.
|
|
28
|
+
6. Build and inspect:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
bundle exec rake build
|
|
32
|
+
gem specification pkg/kitsune-kit-VERSION.gem
|
|
33
|
+
gem contents --show-install-dir kitsune-kit # after isolated install
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
7. Install the artifact in a clean gem directory and run `kit version`, `kit help`, and a temporary `kit init`.
|
|
37
|
+
8. Commit the version/changelog, obtain review, merge and create a signed or protected `vVERSION` tag.
|
|
38
|
+
9. Push only the tag. Approve the protected `release` environment after rechecking the commit/tag/version.
|
|
39
|
+
10. Confirm RubyGems version/checksum, generated GitHub Release and installation from RubyGems.
|
|
40
|
+
|
|
41
|
+
## Recovery
|
|
42
|
+
|
|
43
|
+
Do not overwrite/reuse a published version. If publishing succeeds but GitHub Release creation fails, create the release for the same immutable tag/artifact. If a package is defective, publish a new patch version and document the problem. Follow RubyGems owner/yank guidance only for a genuine security or legal incident.
|
data/docs/roadmap.md
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# Roadmap and extension decisions
|
|
2
|
+
|
|
3
|
+
Kitsune Kit remains deliberately narrow until the complete DigitalOcean lifecycle is repeatedly green. This file
|
|
4
|
+
records decisions for the priority-4 items in the professionalization plan so “deferred” does not mean
|
|
5
|
+
“forgotten.”
|
|
6
|
+
|
|
7
|
+
## Hetzner evaluation
|
|
8
|
+
|
|
9
|
+
The current provider boundary is sufficiently generic for a second server adapter: credential validation,
|
|
10
|
+
server-spec validation, exact lookup/ID lifecycle, readiness and normalized `ServerRecord`. A Hetzner adapter
|
|
11
|
+
would need to map its locations, server types, images, SSH keys, status/IP representation, errors and deadlines
|
|
12
|
+
to that contract and pass the same fake/real lifecycle tests.
|
|
13
|
+
|
|
14
|
+
DNS must not be silently assumed equivalent to server provisioning. A future adapter must either implement the
|
|
15
|
+
existing exact-ID DNS contract through the selected Hetzner DNS product or declare DNS unsupported before
|
|
16
|
+
planning; provider conditionals must not leak into CLI/TUI code.
|
|
17
|
+
|
|
18
|
+
Decision: do not add Hetzner before the DigitalOcean E2E is reliably green and the adapter API has survived a
|
|
19
|
+
released 0.5.x version. When that gate is met, begin with a contract-only spike and an opt-in create/wait/resume/delete
|
|
20
|
+
test in an isolated account. This is an evaluated sequencing decision, not a claim of current support.
|
|
21
|
+
|
|
22
|
+
## External services
|
|
23
|
+
|
|
24
|
+
Implemented in schema version 1 through `services.TYPE.mode: external`. External endpoints are validated and
|
|
25
|
+
visible through `status`, but they produce no VPS operation. Kitsune Kit refuses install, backup, remove and
|
|
26
|
+
destroy-data because ownership remains with the service provider. This avoids assuming every database is a
|
|
27
|
+
local Compose service without pretending to provision arbitrary vendors.
|
|
28
|
+
|
|
29
|
+
Provider-specific database creation, TLS certificate distribution, database/user setup and backup APIs remain
|
|
30
|
+
out of scope until a concrete integration is selected and can have ownership/rollback semantics.
|
|
31
|
+
|
|
32
|
+
## Hooks and plugins
|
|
33
|
+
|
|
34
|
+
Decision: no arbitrary pre/post shell hooks or dynamic plugin loading before real use cases exist. Such a system
|
|
35
|
+
would enlarge the command-injection, secret and rollback surface while weakening plan accuracy. Small,
|
|
36
|
+
versioned ports remain the extension mechanism. A future hook proposal must define:
|
|
37
|
+
|
|
38
|
+
- a concrete use case that cannot be represented as an operation;
|
|
39
|
+
- typed inputs/outputs and secret annotations;
|
|
40
|
+
- whether it is observable during plan;
|
|
41
|
+
- timeout, idempotence, retry and cancellation behavior;
|
|
42
|
+
- ownership and rollback semantics;
|
|
43
|
+
- human/JSON/TUI event behavior;
|
|
44
|
+
- hostile-input, contract and E2E tests.
|
|
45
|
+
|
|
46
|
+
Until those requirements are met, no hook API is promised.
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
# Manual security audit
|
|
2
|
+
|
|
3
|
+
- Review date: 2026-08-14
|
|
4
|
+
- Scope: the `0.5.0` direct rewrite in this repository
|
|
5
|
+
- Method: manual source review plus static, unit, contract, integration and real DigitalOcean E2E gates
|
|
6
|
+
- Result: no unresolved critical, high or medium findings were identified in the reviewed scope
|
|
7
|
+
|
|
8
|
+
This is a maintainer security review, not a third-party penetration test or certification. Version 0.5.0 remains
|
|
9
|
+
pre-1.0 while hosted CI and timed usability acceptance complement the green real-infrastructure result.
|
|
10
|
+
|
|
11
|
+
## Threat model and boundaries reviewed
|
|
12
|
+
|
|
13
|
+
The review assumes the local operator account, project configuration, selected first-use SSH fingerprint and
|
|
14
|
+
provider account are trusted. It treats configuration values, remote output, provider responses, existing
|
|
15
|
+
same-name resources, terminal state and interruption timing as potentially hostile or inconsistent.
|
|
16
|
+
|
|
17
|
+
The following boundaries were inspected:
|
|
18
|
+
|
|
19
|
+
- YAML loading, schema/type validation, path normalization and environment precedence;
|
|
20
|
+
- secret acquisition, redaction, remote `.env` generation, logs, state and support bundles;
|
|
21
|
+
- provider authentication/error translation, exact IDs/tags and destructive ownership checks;
|
|
22
|
+
- SSH host-key handling, command argument escaping, uploads, deadlines and preserved-session recovery;
|
|
23
|
+
- user/sudoers, SSH policy, UFW, swap, unattended upgrades, Docker and metrics scripts;
|
|
24
|
+
- Compose generation, database exposure, `DOCKER-USER` ownership and data lifecycle;
|
|
25
|
+
- DNS zone parsing, exact record IDs, partial persistence and rollback;
|
|
26
|
+
- atomic state, locks, resumable journals, cancellation and terminal restoration;
|
|
27
|
+
- E2E credentials, TTL tags, cleanup filtering and release supply-chain configuration.
|
|
28
|
+
|
|
29
|
+
## Security properties verified in code and tests
|
|
30
|
+
|
|
31
|
+
### Input and command execution
|
|
32
|
+
|
|
33
|
+
Configuration uses safe YAML parsing and a closed nested schema. Resource names, environment names, users,
|
|
34
|
+
ports, CIDRs, domains, images, paths and secret-variable names are validated before external adapters are
|
|
35
|
+
constructed. The SSH adapter sends an executable and separately Shellwords-escaped arguments; uploads send
|
|
36
|
+
base64 over stdin. Hostile tests cover shell metacharacters, command substitution, traversal and embedded
|
|
37
|
+
control characters.
|
|
38
|
+
|
|
39
|
+
### Secrets
|
|
40
|
+
|
|
41
|
+
Configuration/state store secret-variable names or hashes, never service passwords or provider tokens.
|
|
42
|
+
Human, JSON, TUI, file-log, error and support-bundle paths share the central filter. It redacts registered
|
|
43
|
+
values, sensitive hash keys, URL credentials and complete private-key blocks. Service `.env` files are mode
|
|
44
|
+
`0600`. Redis uses a literal runtime environment reference in its command and `REDISCLI_AUTH` healthcheck so
|
|
45
|
+
Compose does not resolve the password into container command arguments.
|
|
46
|
+
|
|
47
|
+
### SSH and firewall recovery
|
|
48
|
+
|
|
49
|
+
First-use SSH trust requires the exact displayed SHA256 fingerprint; subsequent access uses the isolated
|
|
50
|
+
known-hosts file. User creation validates sudoers with `visudo`. SSH policy validates with `sshd -t`, initially
|
|
51
|
+
keeps key-based root recovery, verifies a fresh deploy connection, disables root and verifies again. The
|
|
52
|
+
authenticated transition session remains open so a failed fresh verification can restore policy before the
|
|
53
|
+
session closes.
|
|
54
|
+
|
|
55
|
+
UFW adds the desired SSH route before removing stale rules marked by Kitsune Kit. Apply/finalize is verified with a
|
|
56
|
+
fresh connection. Rollback preserves pre-existing activation/package state and removes only recorded owned
|
|
57
|
+
rules. Docker-published database ports have separately owned `DOCKER-USER` allow/drop transactions.
|
|
58
|
+
|
|
59
|
+
### Ownership and destruction
|
|
60
|
+
|
|
61
|
+
Server deletion requires a recorded provider ID, exact-name confirmation and matching provider identity/tags.
|
|
62
|
+
Provider deletion happens before DNS cleanup, so a rejected provider deletion leaves DNS unchanged; cleanup is
|
|
63
|
+
retryable if DNS fails afterward. DNS updates store every exact ID and prior value immediately.
|
|
64
|
+
|
|
65
|
+
Service `remove` preserves volumes. `destroy-data` requires `TYPE@ENV`; `--yes` is insufficient. Interactive
|
|
66
|
+
use offers a data archive, and automation can request one with `--backup-before-destroy`. Destruction refuses
|
|
67
|
+
to proceed when managed state is absent.
|
|
68
|
+
|
|
69
|
+
### Files, state and supply chain
|
|
70
|
+
|
|
71
|
+
State, known-hosts, logs and support artifacts use restricted directories/files. State writes lock, validate,
|
|
72
|
+
back up, fsync and atomically rename. Unsupported schemas fail instead of being guessed. Logs retain at most 20
|
|
73
|
+
files. Temporary installer/key/config files are removed on failures; metrics downloads require an operator-set
|
|
74
|
+
SHA256. The internal backup container image is digest-pinned.
|
|
75
|
+
|
|
76
|
+
Bundler Audit was run with ruby-advisory-db commit
|
|
77
|
+
`0c1a72a61f08ac6758c5124c083bf01db4638456` and reported no known vulnerabilities. CI also runs RuboCop,
|
|
78
|
+
ShellCheck, branch-coverage gates, adapter contracts, gem build/install smoke tests and the supported Ruby/Ubuntu
|
|
79
|
+
matrices.
|
|
80
|
+
|
|
81
|
+
The local normal suite passed 243 examples with 87.05% line, 66.34% overall branch and 88.99% domain-core
|
|
82
|
+
branch coverage. The Docker-backed SSH/Bash/TUI integration passed 8 examples on Ubuntu 22.04 and the same 8
|
|
83
|
+
examples on Ubuntu 24.04. Pseudo-terminal cases verify restoration on normal exit and `SIGINT`. These local
|
|
84
|
+
results are complemented by the real-provider evidence below.
|
|
85
|
+
|
|
86
|
+
The baseline suite and core coverage gate passed under the supported Ruby 3.2.2, 3.3 and 3.4 runtimes. Six
|
|
87
|
+
additional bounded SSH-bootstrap retry, server-state drift and real SDK-interface examples passed under the
|
|
88
|
+
local Ruby 3.2.2 gate.
|
|
89
|
+
Standard-library extractions needed by Kitsune Kit and the current DigitalOcean SDK are explicit runtime dependencies,
|
|
90
|
+
so warnings cannot corrupt structured output on newer Ruby releases.
|
|
91
|
+
|
|
92
|
+
## Findings corrected during this review
|
|
93
|
+
|
|
94
|
+
1. The real E2E CLI checks did not explicitly select the provisioned `e2e` environment. They now do and assert
|
|
95
|
+
that rollback leaves only the server state.
|
|
96
|
+
2. Redis authentication was represented in a Compose command form that could resolve the secret into command
|
|
97
|
+
arguments. It now uses a literal runtime environment reference and `REDISCLI_AUTH`.
|
|
98
|
+
3. Service data destruction lacked an interactive backup offer and exact-name prompt. Both are now present,
|
|
99
|
+
with an explicit automation flag and tests.
|
|
100
|
+
4. Docker repository key/config and sudoers/SSH-policy temporary files could remain partial after interruption.
|
|
101
|
+
They now use guarded temporary files followed by restrictive installation and cleanup traps.
|
|
102
|
+
5. The TTL cleanup script had no isolated proof around its destructive filter. It is now dependency-injectable,
|
|
103
|
+
dry-run by default and tested to delete only expired `kitsune-ci` IDs.
|
|
104
|
+
|
|
105
|
+
## Residual risks and required operational validation
|
|
106
|
+
|
|
107
|
+
- The real DigitalOcean E2E passed locally on 2026-08-14 in 7 minutes 12 seconds (seed 12020; 1 example, 0
|
|
108
|
+
failures). It exercised provider behavior, Ubuntu 24.04, systemd, UFW, Docker networking, SSH hardening,
|
|
109
|
+
PostgreSQL/Redis isolation, zero-change reapply, rollback and exact Droplet cleanup. The operator confirmed the
|
|
110
|
+
Droplet was removed.
|
|
111
|
+
- The same credentialed workflow has not yet been observed on a GitHub-hosted runner, so runner permissions,
|
|
112
|
+
secret wiring and cleanup-job orchestration remain release gates.
|
|
113
|
+
- Ruby 4 is outside the 0.5.0 support contract because the current DigitalOcean SDK constrains
|
|
114
|
+
`faraday-retry` to a release series that requires Ruby `< 4`; CI and gem metadata enforce this boundary.
|
|
115
|
+
- User-selected PostgreSQL/Redis image tags are mutable unless pinned by digest. Production configurations
|
|
116
|
+
should pin and deliberately update reviewed multi-platform digests.
|
|
117
|
+
- Data archives are created on the same Droplet. They are not disaster recovery until copied to independent,
|
|
118
|
+
access-controlled storage and restoration is tested.
|
|
119
|
+
- Local state is not encrypted or centrally shared. It contains no intended secrets, but its integrity and
|
|
120
|
+
availability establish ownership; secure backups are required for team operation.
|
|
121
|
+
- Provider token least privilege and budget controls depend on DigitalOcean account capabilities and operator
|
|
122
|
+
configuration. Use a separate CI project/account, alerts and short-lived or narrowly scoped credentials where
|
|
123
|
+
available.
|
|
124
|
+
- Docker-group and passwordless-sudo membership make the deploy key root-equivalent by design.
|
|
125
|
+
|
|
126
|
+
## Release decision
|
|
127
|
+
|
|
128
|
+
The reviewed implementation passed its real-provider lifecycle, including confirmed cleanup and closed ports
|
|
129
|
+
5432/6379. The review supports the 0.5.0 release; hosted CI and timed new-user acceptance remain follow-up
|
|
130
|
+
evidence before 1.0 rather than unresolved security findings.
|
data/docs/security.md
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# Security model
|
|
2
|
+
|
|
3
|
+
Kitsune Kit changes remote security policy and can destroy infrastructure. Its design favors explicit ownership, verified transitions and refusal over implicit adoption.
|
|
4
|
+
|
|
5
|
+
## Trust boundaries
|
|
6
|
+
|
|
7
|
+
Kitsune Kit trusts:
|
|
8
|
+
|
|
9
|
+
- the local machine/user running the gem;
|
|
10
|
+
- configuration committed by the project;
|
|
11
|
+
- secrets supplied through the process environment;
|
|
12
|
+
- the independently verified first SSH host-key fingerprint;
|
|
13
|
+
- provider and package repositories over TLS;
|
|
14
|
+
- local state as the record of which resources Kitsune Kit owns.
|
|
15
|
+
|
|
16
|
+
Compromise of the local account, repository, environment, private key or state can invalidate these assumptions. Kitsune Kit is not a secrets vault and does not encrypt local files.
|
|
17
|
+
|
|
18
|
+
## Secrets
|
|
19
|
+
|
|
20
|
+
Never place API tokens or service passwords in YAML, command arguments, state or Git. Configuration stores only environment-variable names. Secret values are registered with a central redactor before event reporting/logging.
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
export DO_API_TOKEN="..."
|
|
24
|
+
export POSTGRES_PASSWORD="..."
|
|
25
|
+
export REDIS_PASSWORD="..."
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Generated service `.env` files exist only on the remote server with mode `0600`. Compose output, fingerprints and local state do not contain plaintext passwords. Newlines, carriage returns and NUL bytes are rejected.
|
|
29
|
+
|
|
30
|
+
Process environments may be observable by privileged local processes and CI administrators. Use the secret store of your CI platform, limit token scope, rotate credentials and avoid shell tracing.
|
|
31
|
+
|
|
32
|
+
## SSH
|
|
33
|
+
|
|
34
|
+
Kitsune Kit never uses `verify_host_key: :never` in production adapters. A new key raises a prompt containing the exact SHA256 fingerprint. Acceptance records it in `.kitsune/known_hosts`; later sessions use strict matching.
|
|
35
|
+
|
|
36
|
+
`--trust-host-key` accepts only an exact fingerprint and is intended for independently verified non-interactive bootstrap. A mismatch fails. Key changes are not automatically accepted.
|
|
37
|
+
|
|
38
|
+
SSH hardening preserves a recovery route:
|
|
39
|
+
|
|
40
|
+
1. bootstrap using deploy or root access;
|
|
41
|
+
2. create/configure the deploy user;
|
|
42
|
+
3. verify a second deploy-user connection;
|
|
43
|
+
4. validate generated `sshd_config` with `sshd -t`;
|
|
44
|
+
5. reload first with key-based root recovery still enabled;
|
|
45
|
+
6. verify a new deploy-user connection, then disable root and verify again;
|
|
46
|
+
7. add the new firewall route before removing a stale route owned by Kitsune Kit and verify deploy access once more.
|
|
47
|
+
|
|
48
|
+
The transport keeps the already authenticated session open across each safety-critical transition. Fresh verification uses a separate connection; if it fails before or after finalization, the preserved session runs the script rollback before it closes. A recovery failure is surfaced as a verification error with provider-console guidance. Kitsune Kit does not modify unrelated SSH/firewall configuration during rollback.
|
|
49
|
+
|
|
50
|
+
## Command and file safety
|
|
51
|
+
|
|
52
|
+
Remote commands are a validated executable plus separately Shellwords-escaped arguments. File uploads are base64 data over stdin, not interpolated shell source. Absolute remote paths and modes are validated before connecting.
|
|
53
|
+
|
|
54
|
+
Generated Compose documents are built as Ruby hashes and serialized with YAML. Resource names, users, ports, CIDRs, images, domains and environment names have allow-list validation. Tests include metacharacters, substitutions, traversal and embedded newline attacks.
|
|
55
|
+
|
|
56
|
+
Versioned shell scripts use `set -Eeuo pipefail`, explicit action dispatch and verification. ShellCheck runs in CI.
|
|
57
|
+
|
|
58
|
+
## Firewall and data services
|
|
59
|
+
|
|
60
|
+
PostgreSQL and Redis do not publish host ports by default. When publishing is enabled:
|
|
61
|
+
|
|
62
|
+
- at least one valid allowed CIDR is required;
|
|
63
|
+
- Compose binds only the configured IPv4 address/port;
|
|
64
|
+
- matching allow and terminal drop rules are installed in Docker's `DOCKER-USER` chain because Docker-published ports can bypass UFW forwarding policy;
|
|
65
|
+
- only rules recorded as created by Kitsune Kit are reconciled or removed.
|
|
66
|
+
|
|
67
|
+
Use private networking or an application-local Docker network whenever possible. Never publish a database to `0.0.0.0` with a broad CIDR unless the exposure is deliberate and independently audited.
|
|
68
|
+
|
|
69
|
+
## Ownership and destructive actions
|
|
70
|
+
|
|
71
|
+
Kitsune Kit records provider IDs, DNS record IDs, remote fingerprints, file backups and firewall ownership. Same-name resources without the expected tags/state are not adopted or deleted.
|
|
72
|
+
|
|
73
|
+
Semantics:
|
|
74
|
+
|
|
75
|
+
| Operation | Server | Containers | Data volume | Configuration |
|
|
76
|
+
| --- | --- | --- | --- | --- |
|
|
77
|
+
| `rollback` | Keep | Restore/remove managed | Keep | Restore captured state |
|
|
78
|
+
| service `remove` | Keep | Remove | Keep | Keep ownership record |
|
|
79
|
+
| service `destroy-data` | Keep | Remove | Delete | Delete managed service files/state |
|
|
80
|
+
| server `destroy` | Delete | Delete with server | Delete with server | Restore/remove managed DNS after provider deletion |
|
|
81
|
+
|
|
82
|
+
`--yes` never authorizes permanent service data or server destruction. Exact confirmation strings are required.
|
|
83
|
+
Interactive service-data destruction offers a restricted archive first. In automation,
|
|
84
|
+
`--backup-before-destroy` requests it explicitly; copy that same-server archive to independent storage.
|
|
85
|
+
|
|
86
|
+
The deploy user belongs to the `docker` group and has passwordless sudo, so it is effectively a privileged
|
|
87
|
+
administrator. Protect its private key as a root credential, restrict SSH CIDRs where possible and rotate the
|
|
88
|
+
key after suspected exposure.
|
|
89
|
+
|
|
90
|
+
## State, logs and diagnostics
|
|
91
|
+
|
|
92
|
+
State/log/support directories and files use restrictive permissions. State writes use file locks, fsync, atomic rename and backups. Logs are structured, redacted and rotated. Support bundles are local JSON and never transmitted.
|
|
93
|
+
|
|
94
|
+
Back up `.kitsune/state/` securely. If state is lost, recover it rather than recreating ownership from names. Inspect support bundles before sharing; redaction is defense in depth, not proof that arbitrary user-provided text contains no sensitive information.
|
|
95
|
+
|
|
96
|
+
When both state copies are lost, `kit server import` is the limited recovery path. It requires an exact numeric provider ID and exact-name confirmation, verifies the complete configured server identity, and imports only server metadata. It deliberately does not claim remote resources or DNS records.
|
|
97
|
+
|
|
98
|
+
## Supply chain
|
|
99
|
+
|
|
100
|
+
- Docker is installed from Docker's official signed apt repository.
|
|
101
|
+
- Metrics installation is off by default. Enabling it requires an explicitly configured SHA256 for the downloaded script; no `curl | sh` pipeline is used.
|
|
102
|
+
- Ruby dependencies are locked for development, audited with Bundler Audit and updated through Dependabot.
|
|
103
|
+
- Release metadata requires MFA on RubyGems. Releases should use trusted publishing and protected tags when configured.
|
|
104
|
+
|
|
105
|
+
Container service tags are mutable unless pinned by digest. For production reproducibility, use an image reference ending in `@sha256:...`, test it in staging and update deliberately.
|
|
106
|
+
|
|
107
|
+
The internal Alpine image used only to archive service volumes is multi-platform and pinned to the published Docker Official Image manifest digest. Its source is the [Docker Hub `alpine:3.20` manifest](https://hub.docker.com/layers/library/alpine/3.20/images/sha256-ac77ebc035f69184acb2660028580c9053f6d0f892de7933e1456d8b5e0ac085). Updating it requires reviewing the new manifest and running backup/restore tests.
|
|
108
|
+
|
|
109
|
+
Kitsune Kit contains no telemetry or automatic upload path. Logs and support bundles remain local unless the
|
|
110
|
+
operator deliberately shares them.
|
|
111
|
+
|
|
112
|
+
The latest manual review and its limitations are recorded in [Security audit](security-audit.md).
|
|
113
|
+
|
|
114
|
+
## Reporting vulnerabilities
|
|
115
|
+
|
|
116
|
+
Follow [SECURITY.md](../SECURITY.md). Do not include real credentials, addresses or domains in a public issue or reproduction.
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# PostgreSQL service
|
|
2
|
+
|
|
3
|
+
PostgreSQL is an optional Docker Compose service managed by Kitsune Kit on the target server. It is disabled and private by default.
|
|
4
|
+
|
|
5
|
+
## Enable
|
|
6
|
+
|
|
7
|
+
```yaml
|
|
8
|
+
services:
|
|
9
|
+
postgres:
|
|
10
|
+
enabled: true
|
|
11
|
+
image: postgres:17
|
|
12
|
+
publish: false
|
|
13
|
+
bind: 127.0.0.1
|
|
14
|
+
allowed_cidrs: []
|
|
15
|
+
port: 5432
|
|
16
|
+
password_env: POSTGRES_PASSWORD
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
export POSTGRES_PASSWORD="$(ruby -rsecurerandom -e 'print SecureRandom.base64(36)')"
|
|
21
|
+
kit plan
|
|
22
|
+
kit service postgres install
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
The Compose project is `kitsune-ENV-postgres`, attached to the external `kitsune-private` network. Data is stored in its named `data` volume. The database name is `app_ENV` with `-` converted to `_`; the user is `postgres`.
|
|
26
|
+
|
|
27
|
+
## Connectivity
|
|
28
|
+
|
|
29
|
+
With `publish: false`, there is no host port mapping. Applications on `kitsune-private` can connect using Docker service/network addressing and the configured credentials.
|
|
30
|
+
|
|
31
|
+
Publishing is an explicit exception:
|
|
32
|
+
|
|
33
|
+
```yaml
|
|
34
|
+
publish: true
|
|
35
|
+
bind: 0.0.0.0
|
|
36
|
+
allowed_cidrs:
|
|
37
|
+
- 203.0.113.10/32
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Kitsune Kit requires at least one CIDR and installs owned `DOCKER-USER` allow/drop rules. Validate the result externally; UFW alone does not reliably restrict Docker-published ports.
|
|
41
|
+
|
|
42
|
+
## Status, backup and removal
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
kit service postgres status
|
|
46
|
+
kit service postgres backup
|
|
47
|
+
kit service postgres remove --yes
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Backup pauses the service, archives the named volume into a restricted remote backup directory and unpauses in an `ensure` path. The returned path is on the server. Copy it to independent storage and test restoration; creating an archive on the same Droplet is not disaster recovery.
|
|
51
|
+
|
|
52
|
+
`remove` runs Compose down without `--volumes`, removes only firewall rules owned by Kitsune Kit and marks the service removed. Data remains.
|
|
53
|
+
|
|
54
|
+
## Permanent destruction
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
kit service postgres backup
|
|
58
|
+
kit service postgres destroy-data --backup-before-destroy --confirm-destroy postgres@production
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
In an interactive terminal, `destroy-data` offers to create the archive and then requires typing
|
|
62
|
+
`postgres@ENV`. Automation uses `--backup-before-destroy` when desired and must always provide the exact
|
|
63
|
+
`--confirm-destroy` value. The data archive is retained, while Compose volumes, managed configuration
|
|
64
|
+
backups/files, markers and service state are removed. Copy the archive off the server before relying on it;
|
|
65
|
+
`--yes` cannot replace the exact confirmation.
|
|
66
|
+
|
|
67
|
+
## Updates and rollback
|
|
68
|
+
|
|
69
|
+
Changing the image/configuration/secret changes the service fingerprint. Before an update, Kitsune Kit backs up managed Compose/env files. It validates Compose, waits for health, reconciles firewall rules and only then records the new state. Failed updates restore previous files and restart the prior service where possible.
|
|
70
|
+
|
|
71
|
+
`kit rollback` restores captured managed configuration and preserves the volume. It does not promise database-level downgrade compatibility; test image upgrades and rollback in staging.
|
|
72
|
+
|
|
73
|
+
## External PostgreSQL
|
|
74
|
+
|
|
75
|
+
Set `enabled: true`, `mode: external`, `host` and `port` to represent a provider-managed database without
|
|
76
|
+
installing it on the VPS. `kit service postgres status` reports the endpoint; install, backup, remove and
|
|
77
|
+
destroy-data are intentionally unavailable. Kitsune Kit does not test provider TLS, create users/databases or
|
|
78
|
+
manage external backups. Supply those through the database provider and application deployment system.
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Redis service
|
|
2
|
+
|
|
3
|
+
Redis is an optional Docker Compose service managed by Kitsune Kit on the target server. It is disabled, password-protected and private by default.
|
|
4
|
+
|
|
5
|
+
## Enable
|
|
6
|
+
|
|
7
|
+
```yaml
|
|
8
|
+
services:
|
|
9
|
+
redis:
|
|
10
|
+
enabled: true
|
|
11
|
+
image: redis:7.2
|
|
12
|
+
publish: false
|
|
13
|
+
bind: 127.0.0.1
|
|
14
|
+
allowed_cidrs: []
|
|
15
|
+
port: 6379
|
|
16
|
+
password_env: REDIS_PASSWORD
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
export REDIS_PASSWORD="$(ruby -rsecurerandom -e 'print SecureRandom.base64(36)')"
|
|
21
|
+
kit plan
|
|
22
|
+
kit service redis install
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
The generated service enables append-only persistence, requires the password and attaches to `kitsune-private`. Data lives in the Compose named `data` volume.
|
|
26
|
+
|
|
27
|
+
## Connectivity and publishing
|
|
28
|
+
|
|
29
|
+
With `publish: false`, only containers on the private Docker network can connect. If TCP access from outside the host is unavoidable, set `publish: true`, choose a bind address and provide narrow `allowed_cidrs`. Kitsune Kit installs matching `DOCKER-USER` rules and a final drop rule it owns.
|
|
30
|
+
|
|
31
|
+
Never treat the Redis password as an adequate substitute for network isolation. Validate port 6379 from an untrusted external network after any firewall/Docker change.
|
|
32
|
+
|
|
33
|
+
## Lifecycle
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
kit service redis status
|
|
37
|
+
kit service redis backup
|
|
38
|
+
kit service redis remove --yes
|
|
39
|
+
kit service redis destroy-data --backup-before-destroy --confirm-destroy redis@production
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
- `backup` pauses Redis, archives its volume remotely, then unpauses even if archiving fails.
|
|
43
|
+
- `remove` removes the container and owned firewall rules but preserves the data volume.
|
|
44
|
+
- `destroy-data` removes the volume, service files, backups and state after exact confirmation.
|
|
45
|
+
- `kit rollback` restores captured managed files/settings while preserving data.
|
|
46
|
+
|
|
47
|
+
Copy backups away from the Droplet, control access to them and regularly test restoration. Image downgrades may not understand data written by a newer Redis version; operational rollback and data-format compatibility are separate concerns.
|
|
48
|
+
|
|
49
|
+
## External Redis
|
|
50
|
+
|
|
51
|
+
Set `enabled: true`, `mode: external`, `host` and `port` for a provider-managed endpoint. Status is available,
|
|
52
|
+
but Kitsune Kit refuses install, backup, remove and destroy-data because those belong to the external provider. TLS,
|
|
53
|
+
availability and backup policy remain the operator/provider responsibility.
|