empeira 0.1.0.pre.alpha.1
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 +7 -0
- data/LICENSE +661 -0
- data/README.md +150 -0
- data/bin/empeira +18 -0
- data/config/defaults.yaml +229 -0
- data/docs/addons.md +331 -0
- data/docs/architecture.md +277 -0
- data/docs/configuration.md +1204 -0
- data/docs/development.md +472 -0
- data/docs/hiera-and-eyaml.md +361 -0
- data/docs/installation.md +280 -0
- data/docs/networking.md +362 -0
- data/docs/nodes.md +642 -0
- data/docs/proxy.md +136 -0
- data/docs/troubleshooting.md +361 -0
- data/lib/empeira/addons/environment.rb +82 -0
- data/lib/empeira/addons/error.rb +14 -0
- data/lib/empeira/addons/installation.rb +119 -0
- data/lib/empeira/addons/manifest.rb +112 -0
- data/lib/empeira/addons/service.rb +50 -0
- data/lib/empeira/addons/storage.rb +94 -0
- data/lib/empeira/agent/acquisition.rb +120 -0
- data/lib/empeira/agent/artifact.rb +41 -0
- data/lib/empeira/agent/authentication.rb +122 -0
- data/lib/empeira/agent/cache.rb +122 -0
- data/lib/empeira/agent/cache_storage.rb +82 -0
- data/lib/empeira/agent/download.rb +84 -0
- data/lib/empeira/agent/native_commands.rb +84 -0
- data/lib/empeira/agent/release_sources.rb +64 -0
- data/lib/empeira/agent/target.rb +66 -0
- data/lib/empeira/agent/versions.rb +29 -0
- data/lib/empeira/application.rb +86 -0
- data/lib/empeira/build.json +5 -0
- data/lib/empeira/build_info.rb +46 -0
- data/lib/empeira/cli/addons.rb +45 -0
- data/lib/empeira/cli/base.rb +73 -0
- data/lib/empeira/cli/completion.rb +42 -0
- data/lib/empeira/cli/config.rb +22 -0
- data/lib/empeira/cli/main.rb +114 -0
- data/lib/empeira/cli/node.rb +87 -0
- data/lib/empeira/cli/progress.rb +169 -0
- data/lib/empeira/cli/proxy.rb +36 -0
- data/lib/empeira/cli/update.rb +23 -0
- data/lib/empeira/completion/addons.rb +93 -0
- data/lib/empeira/completion/bash.rb +62 -0
- data/lib/empeira/completion/catalog.rb +170 -0
- data/lib/empeira/configuration/additional_services.rb +74 -0
- data/lib/empeira/configuration/addons.rb +85 -0
- data/lib/empeira/configuration/agent_schema.rb +163 -0
- data/lib/empeira/configuration/agent_values.rb +54 -0
- data/lib/empeira/configuration/bootstrap_guests.rb +42 -0
- data/lib/empeira/configuration/command_mocks.rb +65 -0
- data/lib/empeira/configuration/display.rb +20 -0
- data/lib/empeira/configuration/dns_rewrites.rb +58 -0
- data/lib/empeira/configuration/document.rb +67 -0
- data/lib/empeira/configuration/eyaml.rb +46 -0
- data/lib/empeira/configuration/hiera_modulepath.rb +40 -0
- data/lib/empeira/configuration/hiera_mount_schema.rb +62 -0
- data/lib/empeira/configuration/hiera_mounts.rb +83 -0
- data/lib/empeira/configuration/image_schema.rb +68 -0
- data/lib/empeira/configuration/loader.rb +70 -0
- data/lib/empeira/configuration/merge.rb +48 -0
- data/lib/empeira/configuration/network_redirects.rb +73 -0
- data/lib/empeira/configuration/node_images.rb +64 -0
- data/lib/empeira/configuration/schema.rb +215 -0
- data/lib/empeira/configuration/server_mounts.rb +106 -0
- data/lib/empeira/configuration/service_configuration.rb +40 -0
- data/lib/empeira/configuration/ssh_preferences.rb +83 -0
- data/lib/empeira/configuration/user.rb +46 -0
- data/lib/empeira/configuration/vm_interfaces.rb +147 -0
- data/lib/empeira/control_plane/additional_configurations.rb +106 -0
- data/lib/empeira/control_plane/browser.rb +73 -0
- data/lib/empeira/control_plane/controller.rb +763 -0
- data/lib/empeira/control_plane/discovery.rb +69 -0
- data/lib/empeira/control_plane/environment.rb +101 -0
- data/lib/empeira/control_plane/files.rb +263 -0
- data/lib/empeira/control_plane/health.rb +104 -0
- data/lib/empeira/control_plane/plan.rb +356 -0
- data/lib/empeira/control_plane/storage.rb +48 -0
- data/lib/empeira/errors.rb +9 -0
- data/lib/empeira/execution/console.rb +45 -0
- data/lib/empeira/execution/diagnostics.rb +210 -0
- data/lib/empeira/execution/launch_error.rb +14 -0
- data/lib/empeira/execution/result.rb +11 -0
- data/lib/empeira/execution/runner.rb +235 -0
- data/lib/empeira/execution/terminal.rb +35 -0
- data/lib/empeira/hostname_pattern.rb +14 -0
- data/lib/empeira/images/configuration.rb +44 -0
- data/lib/empeira/images/identity.rb +30 -0
- data/lib/empeira/images/recipe.rb +22 -0
- data/lib/empeira/images/reference.rb +43 -0
- data/lib/empeira/images/source.rb +19 -0
- data/lib/empeira/immutable.rb +19 -0
- data/lib/empeira/infrastructure/cleanup.rb +328 -0
- data/lib/empeira/infrastructure/definition.rb +59 -0
- data/lib/empeira/infrastructure/owned_files.rb +63 -0
- data/lib/empeira/infrastructure/recovery_inventory.rb +53 -0
- data/lib/empeira/infrastructure/service.rb +237 -0
- data/lib/empeira/infrastructure/status.rb +73 -0
- data/lib/empeira/infrastructure/store.rb +274 -0
- data/lib/empeira/modules/events.rb +67 -0
- data/lib/empeira/modules/git_safety.rb +60 -0
- data/lib/empeira/modules/host_git.rb +91 -0
- data/lib/empeira/modules/image.rb +27 -0
- data/lib/empeira/modules/installer.rb +67 -0
- data/lib/empeira/modules/request.rb +68 -0
- data/lib/empeira/modules/state.rb +87 -0
- data/lib/empeira/modules/storage.rb +35 -0
- data/lib/empeira/network/bootstrap_proxy.rb +157 -0
- data/lib/empeira/network/definition.rb +61 -0
- data/lib/empeira/network/direct_egress.rb +133 -0
- data/lib/empeira/network/egress.rb +27 -0
- data/lib/empeira/network/gateway.rb +79 -0
- data/lib/empeira/network/host_policy.rb +53 -0
- data/lib/empeira/network/naming.rb +28 -0
- data/lib/empeira/network/peer/adapter_image.rb +32 -0
- data/lib/empeira/network/peer/allocation.rb +45 -0
- data/lib/empeira/network/peer/attachment.rb +27 -0
- data/lib/empeira/network/peer/backend.rb +108 -0
- data/lib/empeira/network/peer/channel.rb +133 -0
- data/lib/empeira/network/peer/channel_server.rb +71 -0
- data/lib/empeira/network/peer/docker_adapter.rb +165 -0
- data/lib/empeira/network/peer/helper_security.rb +40 -0
- data/lib/empeira/network/peer/layout.rb +55 -0
- data/lib/empeira/network/peer/linux_podman.rb +114 -0
- data/lib/empeira/network/peer/podman_machine.rb +112 -0
- data/lib/empeira/network/policy.rb +39 -0
- data/lib/empeira/network/proxy_bindings.rb +52 -0
- data/lib/empeira/network/proxy_policy.rb +64 -0
- data/lib/empeira/network/redirects.rb +89 -0
- data/lib/empeira/network/resource.rb +50 -0
- data/lib/empeira/node/agent_installer.rb +31 -0
- data/lib/empeira/node/agent_package.rb +125 -0
- data/lib/empeira/node/agent_repository.rb +160 -0
- data/lib/empeira/node/apt_configuration.rb +85 -0
- data/lib/empeira/node/bootstrap.rb +17 -0
- data/lib/empeira/node/certificates.rb +98 -0
- data/lib/empeira/node/command_mock.rb +38 -0
- data/lib/empeira/node/command_mocks.rb +114 -0
- data/lib/empeira/node/container.rb +134 -0
- data/lib/empeira/node/container_agent_bootstrap.rb +43 -0
- data/lib/empeira/node/container_bootstrap.rb +52 -0
- data/lib/empeira/node/container_guest.rb +42 -0
- data/lib/empeira/node/container_lifecycle.rb +145 -0
- data/lib/empeira/node/container_preparation.rb +144 -0
- data/lib/empeira/node/container_state.rb +129 -0
- data/lib/empeira/node/definition.rb +20 -0
- data/lib/empeira/node/diagnostic_command.rb +19 -0
- data/lib/empeira/node/dnf_agent_repository.rb +202 -0
- data/lib/empeira/node/external_fact.rb +21 -0
- data/lib/empeira/node/image.rb +38 -0
- data/lib/empeira/node/interactive_tools.rb +33 -0
- data/lib/empeira/node/interface.rb +18 -0
- data/lib/empeira/node/inventory.rb +143 -0
- data/lib/empeira/node/package_bootstrap.rb +129 -0
- data/lib/empeira/node/package_proxy.rb +102 -0
- data/lib/empeira/node/package_sources.rb +19 -0
- data/lib/empeira/node/puppet_command.rb +12 -0
- data/lib/empeira/node/rpm_configuration.rb +62 -0
- data/lib/empeira/node/run_request.rb +52 -0
- data/lib/empeira/node/runtime_proxy.rb +86 -0
- data/lib/empeira/node/service.rb +72 -0
- data/lib/empeira/node/ssh_client.rb +25 -0
- data/lib/empeira/node/ssh_credentials.rb +78 -0
- data/lib/empeira/node/ssh_endpoint.rb +58 -0
- data/lib/empeira/node/ssh_identity.rb +37 -0
- data/lib/empeira/node/user_ssh.rb +60 -0
- data/lib/empeira/node/vm.rb +89 -0
- data/lib/empeira/node/vm_bootstrap.rb +53 -0
- data/lib/empeira/node/vm_lifecycle.rb +212 -0
- data/lib/empeira/node/vm_preflight.rb +37 -0
- data/lib/empeira/node/vm_preparation.rb +161 -0
- data/lib/empeira/node/vm_state.rb +161 -0
- data/lib/empeira/platform/facts.rb +66 -0
- data/lib/empeira/platform/kvm.rb +91 -0
- data/lib/empeira/platform/locations.rb +51 -0
- data/lib/empeira/platform/resolvers.rb +85 -0
- data/lib/empeira/platform/routes.rb +50 -0
- data/lib/empeira/progress.rb +70 -0
- data/lib/empeira/project_root.rb +47 -0
- data/lib/empeira/providers/lifecycle.rb +96 -0
- data/lib/empeira/providers/registry.rb +21 -0
- data/lib/empeira/providers/resource.rb +32 -0
- data/lib/empeira/providers.rb +55 -0
- data/lib/empeira/runtime/agent_helper.rb +34 -0
- data/lib/empeira/runtime/container.rb +184 -0
- data/lib/empeira/runtime/diagnostic_execution.rb +52 -0
- data/lib/empeira/runtime/docker.rb +87 -0
- data/lib/empeira/runtime/image_metadata.rb +83 -0
- data/lib/empeira/runtime/interface.rb +8 -0
- data/lib/empeira/runtime/mount_probe.rb +57 -0
- data/lib/empeira/runtime/podman.rb +122 -0
- data/lib/empeira/runtime/recipe_build.rb +103 -0
- data/lib/empeira/runtime/registry_diagnostics.rb +23 -0
- data/lib/empeira/runtime/service_arguments.rb +55 -0
- data/lib/empeira/runtime/service_images.rb +118 -0
- data/lib/empeira/runtime/service_inspection.rb +159 -0
- data/lib/empeira/runtime/services.rb +120 -0
- data/lib/empeira/runtime/update_helper.rb +97 -0
- data/lib/empeira/runtime/workspace_network.rb +69 -0
- data/lib/empeira/server/environment_configuration.rb +53 -0
- data/lib/empeira/server/http.rb +38 -0
- data/lib/empeira/server/puppetdb_runtime.rb +74 -0
- data/lib/empeira/server/relay_certificate.rb +76 -0
- data/lib/empeira/server/runtime.rb +90 -0
- data/lib/empeira/services/definition.rb +42 -0
- data/lib/empeira/updates/images.rb +104 -0
- data/lib/empeira/updates/modules.rb +45 -0
- data/lib/empeira/updates/service.rb +45 -0
- data/lib/empeira/version_requirement.rb +23 -0
- data/lib/empeira/vm/agent.rb +66 -0
- data/lib/empeira/vm/bootstrap_requirements.rb +69 -0
- data/lib/empeira/vm/certificate_runtime.rb +30 -0
- data/lib/empeira/vm/cloud_init.rb +171 -0
- data/lib/empeira/vm/disk.rb +136 -0
- data/lib/empeira/vm/guest.rb +123 -0
- data/lib/empeira/vm/guest_channel.rb +110 -0
- data/lib/empeira/vm/guest_network.rb +94 -0
- data/lib/empeira/vm/guest_output.rb +52 -0
- data/lib/empeira/vm/guest_shell.rb +79 -0
- data/lib/empeira/vm/guest_upload.rb +43 -0
- data/lib/empeira/vm/image_cache.rb +77 -0
- data/lib/empeira/vm/image_source.rb +91 -0
- data/lib/empeira/vm/interface.rb +8 -0
- data/lib/empeira/vm/interface_ownership.rb +124 -0
- data/lib/empeira/vm/interface_routes.rb +101 -0
- data/lib/empeira/vm/interfaces.rb +240 -0
- data/lib/empeira/vm/management.rb +73 -0
- data/lib/empeira/vm/prerequisites.rb +42 -0
- data/lib/empeira/vm/qemu.rb +83 -0
- data/lib/empeira/vm/qemu_runtime.rb +404 -0
- data/lib/empeira/vm/removal.rb +30 -0
- data/lib/empeira/vm/root_disk.rb +92 -0
- data/lib/empeira/vm/session_lock.rb +56 -0
- data/lib/empeira/vm/system_ssh.rb +29 -0
- data/lib/empeira/workspace.rb +42 -0
- data/lib/empeira.rb +209 -0
- data/resources/browser/relay.rb +22 -0
- data/resources/gateway/Containerfile +8 -0
- data/resources/gateway/bridge.rb +66 -0
- data/resources/gateway/configure.rb +323 -0
- data/resources/modules/Containerfile +15 -0
- data/resources/modules/Gemfile +5 -0
- data/resources/modules/Gemfile.lock +105 -0
- data/resources/modules/worker.rb +161 -0
- data/resources/network/adapter/Containerfile +13 -0
- data/resources/network/adapter/bridge_linux.go +51 -0
- data/resources/network/adapter/framing.go +45 -0
- data/resources/network/adapter/framing_test.go +53 -0
- data/resources/network/adapter/go.mod +3 -0
- data/resources/network/adapter/main_linux.go +273 -0
- data/resources/network/channel.rb +18 -0
- data/resources/network/ssh_connect.rb +32 -0
- data/resources/nodes/apt_sources.rb +66 -0
- data/resources/nodes/debian/Containerfile +10 -0
- data/resources/nodes/diagnostic_exec.rb +109 -0
- data/resources/nodes/interactive_tools.rb +139 -0
- data/resources/nodes/managed_file.rb +91 -0
- data/resources/nodes/management/guest_agent.py +415 -0
- data/resources/nodes/management/setup.sh +20 -0
- data/resources/nodes/management/shell_rc.sh +12 -0
- data/resources/nodes/management/system_account.sh +24 -0
- data/resources/nodes/management/system_home.sh +15 -0
- data/resources/nodes/rpm/Containerfile +10 -0
- data/resources/nodes/runtime_proxy.rb +244 -0
- data/resources/nodes/ssh/proxy.rb +16 -0
- data/resources/nodes/ssh/sshd_config +15 -0
- data/resources/nodes/ssh/start +6 -0
- data/resources/proxy/Containerfile +10 -0
- data/resources/relay/Containerfile +5 -0
- data/resources/relay/puppetdb.rb +81 -0
- data/resources/relay/server_environment.rb +10 -0
- data/resources/relay/server_start.sh +41 -0
- metadata +326 -0
data/docs/networking.md
ADDED
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
# Networking
|
|
2
|
+
|
|
3
|
+
Container and VM nodes share one isolated IPv4 peer network with logical DNS names
|
|
4
|
+
and arbitrary internal TCP/UDP. They have no unrestricted host-network or Internet
|
|
5
|
+
attachment. External traffic requires an explicit TCP grant or allowlisted proxy
|
|
6
|
+
policy. IPv6 is disabled; external UDP grants are unsupported.
|
|
7
|
+
|
|
8
|
+
VM-only [additional dummy/VLAN interfaces](configuration.md#additional-vm-interfaces)
|
|
9
|
+
exist entirely inside the guest. They provide ordinary Facter bindings without an
|
|
10
|
+
external VLAN attachment or changes to the peer NIC. Their connected
|
|
11
|
+
routes must avoid existing guest routes, the workspace subnet,
|
|
12
|
+
and transparent redirect source addresses. `/32` is preferred for isolated fact
|
|
13
|
+
tests. Empeira verifies and preserves default routes, and refuses foreign device
|
|
14
|
+
ownership or dependencies instead of removing them.
|
|
15
|
+
|
|
16
|
+
```mermaid
|
|
17
|
+
flowchart LR
|
|
18
|
+
subgraph Lab[Isolated Empeira workspace]
|
|
19
|
+
C[Container nodes] <--> V[VM nodes]
|
|
20
|
+
C --> D[CoreDNS]
|
|
21
|
+
V --> D
|
|
22
|
+
C --> S[OpenVox Server]
|
|
23
|
+
V --> S
|
|
24
|
+
S --> DB[OpenVoxDB relay and backend] --> PG[PostgreSQL]
|
|
25
|
+
B[Chromium / additional services] <--> C
|
|
26
|
+
B <--> V
|
|
27
|
+
C --> P[Normal / bootstrap proxy]
|
|
28
|
+
V --> P
|
|
29
|
+
end
|
|
30
|
+
D --> G[Workspace gateway: default DROP]
|
|
31
|
+
P --> G
|
|
32
|
+
C -. explicit TCP grant .-> G
|
|
33
|
+
V -. explicit TCP grant .-> G
|
|
34
|
+
G --> External[Permitted DNS / LAN / Internet destinations]
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
The workspace is a cooperative lab, not an adversarial tenant boundary. Privileged
|
|
38
|
+
peers can spoof shared-network identities. Engine administrators can inspect
|
|
39
|
+
containers and credentials. Use disposable test data and trusted guests.
|
|
40
|
+
|
|
41
|
+
## Logical network and backend resources
|
|
42
|
+
|
|
43
|
+
Docker uses an internal bridge with
|
|
44
|
+
[isolated gateway mode](https://docs.docker.com/engine/network/port-publishing/#gateway-modes).
|
|
45
|
+
Podman uses an internal Netavark bridge with isolation and runtime DNS disabled.
|
|
46
|
+
Empeira verifies ownership, driver and isolation before reuse; unsupported or
|
|
47
|
+
unverifiable capabilities fail closed.
|
|
48
|
+
|
|
49
|
+
| CLI host/runtime | VM peer attachment |
|
|
50
|
+
| --- | --- |
|
|
51
|
+
| Linux/WSL2, rootless Podman | Nonpersistent TAP in the rootless Netavark namespace; capless peer TCP client |
|
|
52
|
+
| Linux/WSL2, Docker | Private QEMU Ethernet channel and owned bridge adapter |
|
|
53
|
+
| macOS, Podman Machine | QEMU channel through machine SSH and rootless TAP |
|
|
54
|
+
| macOS, Docker Desktop | QEMU channel through runtime exec and owned bridge adapter |
|
|
55
|
+
|
|
56
|
+
One backend factory owns this selection. No host route to container IPs is assumed
|
|
57
|
+
on macOS. KVM/HVF and platform-specific wiring require the corresponding
|
|
58
|
+
[manual gates](development.md#vm-and-shared-network-prerequisites).
|
|
59
|
+
Podman Machine needs one unambiguous active rootless connection and shared canonical
|
|
60
|
+
state paths; Empeira never resets the machine or falls back to rootful execution.
|
|
61
|
+
|
|
62
|
+
The static Go adapter is built from packaged source in an isolated, content-hashed
|
|
63
|
+
context by the selected runtime. Go is not a host prerequisite. Packet channels
|
|
64
|
+
use private directories/sockets outside the control repository. Adapter helpers
|
|
65
|
+
have no control-code mount or credentials; identity, definition and checksum are
|
|
66
|
+
verified. VM management uses a separate private VirtIO-Serial channel and no
|
|
67
|
+
network NIC, SLIRP forward or TCP listener. Its Unix socket is private to the host
|
|
68
|
+
user and bound to the owned QEMU instance. It adds no route or egress exception.
|
|
69
|
+
|
|
70
|
+
System SSH connects directly through the owned peer attachment to the VM's
|
|
71
|
+
verified peer IPv4 and selected guest port (22 by default). Native Podman uses a
|
|
72
|
+
capless owned peer client on the existing isolated network;
|
|
73
|
+
Docker keeps its existing adapter IP on its local bridge with forwarding disabled.
|
|
74
|
+
Podman machine uses its capless staging adapter. TCP binds the observed internal
|
|
75
|
+
client IP, with no host route or additional VM NIC. Ownership, attachment and subnet
|
|
76
|
+
are verified
|
|
77
|
+
before connecting. User SSH needs no management check or authentication and adds
|
|
78
|
+
no host publication or additional VM NIC. OpenSSH uses private per-node known-host
|
|
79
|
+
state and preserves native connection/authentication errors. See
|
|
80
|
+
[SSH and recovery](nodes.md#shell-and-ssh).
|
|
81
|
+
|
|
82
|
+
The CLI host and engine may have different filesystems. Control/Hiera directories,
|
|
83
|
+
server mounts and state must be visible at their canonical paths. Mount probes
|
|
84
|
+
inspect visibility without writing markers. Remote-engine path translation is not
|
|
85
|
+
implemented. On WSL2, keep source checkouts on the Linux filesystem.
|
|
86
|
+
|
|
87
|
+
### Address lifetime and recovery
|
|
88
|
+
|
|
89
|
+
Under the workspace lock, Empeira selects an unused private /24 after checking
|
|
90
|
+
host routes and runtime networks. Gateway/DNS/proxy addresses, VM/container leases
|
|
91
|
+
and runtime infrastructure allocation use separate pools. There is no fixed global
|
|
92
|
+
subnet or user IP/MAC setting. Unknown overlap or ownership fails closed.
|
|
93
|
+
|
|
94
|
+
A node's IP, MAC and DNS record survive stop/start. Destroy releases its lease and
|
|
95
|
+
removes its record, transport and credentials; recreating a name need not reuse its
|
|
96
|
+
address. A later VPN route can require workspace recreation. Unsupported inventory
|
|
97
|
+
or definition revisions fail closed without migration or automatic adoption.
|
|
98
|
+
|
|
99
|
+
## One gateway, one direct-egress policy
|
|
100
|
+
|
|
101
|
+
Every peer uses the owned gateway as its IPv4 default route. Only the gateway has
|
|
102
|
+
an external uplink; DNS, proxies, server, database and nodes stay isolated.
|
|
103
|
+
The fixed browser UI relay has a separate loopback access path with forwarding off.
|
|
104
|
+
|
|
105
|
+
`network.egress` grants destination IPv4/TCP-port pairs:
|
|
106
|
+
|
|
107
|
+
```yaml
|
|
108
|
+
network:
|
|
109
|
+
egress:
|
|
110
|
+
- host: repository.example.net
|
|
111
|
+
ports: [443]
|
|
112
|
+
- ip: 203.0.113.10
|
|
113
|
+
ports: [443, 8443]
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Entries have exactly one `host` or `ip`. Hostnames match exactly, without wildcards;
|
|
117
|
+
`up` resolves every current A record and replaces the complete policy. IP selectors
|
|
118
|
+
bypass DNS and may intentionally permit private LAN/VPN destinations. A different
|
|
119
|
+
name sharing an allowed IP is reachable on that port: the firewall cannot distinguish
|
|
120
|
+
HTTP Host or TLS SNI. DNS changes require another `up`.
|
|
121
|
+
|
|
122
|
+
Reconciliation installs DROP before applying complete rules. Removed grants also
|
|
123
|
+
revoke existing connections. DNS or firewall failure leaves egress blocked/stopped,
|
|
124
|
+
never on unrestricted provider NAT. Policy edits preserve node/gateway identities;
|
|
125
|
+
`status` checks actual rules and forwarding, and `up` repairs drift.
|
|
126
|
+
|
|
127
|
+
CoreDNS alone receives TCP/UDP 53 to its selected resolvers. Proxies alone receive
|
|
128
|
+
TCP 80/443 and enforce their destination allowlists. Normal proxy allow rules apply
|
|
129
|
+
regardless of target IP range; the temporary authenticated bootstrap proxy retains
|
|
130
|
+
its separate private/reserved-address restrictions.
|
|
131
|
+
`proxy.global` does not grant direct egress. Applications choose DIRECT/proxy using
|
|
132
|
+
their own settings and `NO_PROXY`; direct/proxy selection has no automatic fallback.
|
|
133
|
+
Both explicit hostnames and IPv4 selectors from `network.egress` are included in
|
|
134
|
+
normal proxy bypass settings. The gateway still permits only their configured TCP ports.
|
|
135
|
+
See [proxy policy](proxy.md) for hostname rules and bootstrap destinations.
|
|
136
|
+
|
|
137
|
+
During bootstrap, a node is excluded from direct egress and normal proxy bindings.
|
|
138
|
+
A separate authenticated /32-source-bound proxy installs packages. Cleanup restores
|
|
139
|
+
package configuration, removes temporary access/credentials and activates final
|
|
140
|
+
runtime policy before enrollment and Puppet. Failed bootstrap stays incomplete.
|
|
141
|
+
|
|
142
|
+
Normal proxy settings are distinct from that temporary bootstrap transaction.
|
|
143
|
+
Managed Puppet processes receive matching uppercase/lowercase HTTP/HTTPS and
|
|
144
|
+
NO_PROXY variables inside the privileged guest command, including direct-root VM
|
|
145
|
+
management.
|
|
146
|
+
APT uses `/etc/apt/apt.conf.d/90-empeira-proxy`; DNF receives an owned block in
|
|
147
|
+
the existing `/etc/dnf/dnf.conf` `[main]` section. Direct-egress hosts get native
|
|
148
|
+
APT DIRECT entries unless an explicit host setting already exists. Foreign
|
|
149
|
+
host-specific APT settings and DNF repository overrides remain unchanged.
|
|
150
|
+
Conflicting global settings, modified owned files or unsafe paths fail with a
|
|
151
|
+
diagnosis. APT configurations using `#include`/`#clear` require explicit review.
|
|
152
|
+
DNF repository `proxy=` disables the inherited proxy; `_none_` retains DNF's
|
|
153
|
+
native curl environment semantics (see the [DNF reference](https://dnf.readthedocs.io/en/stable/conf_ref.html#proxy)).
|
|
154
|
+
These settings change client routing only; destination grants and isolation stay
|
|
155
|
+
under the existing gateway and proxy policies.
|
|
156
|
+
|
|
157
|
+
## Transparent TCP redirects
|
|
158
|
+
|
|
159
|
+
`network.redirects` maps exact original IPv4/protocol/port destination tuples to
|
|
160
|
+
the current address/port of an owned internal application service. Multiple pairs may share a
|
|
161
|
+
service. This supports fixed production endpoints such as PowerDNS APIs without
|
|
162
|
+
changing Puppet/Hiera or implementing application logic in Empeira. Configuration
|
|
163
|
+
and an example are in [configuration](configuration.md#transparent-tcp-redirects).
|
|
164
|
+
|
|
165
|
+
Container nodes, Puppet/OpenVox Server and internal services already route external
|
|
166
|
+
destinations through the workspace gateway. VM cloud-init installs the same default
|
|
167
|
+
route, and peer adapters transport Ethernet without introducing another IP router.
|
|
168
|
+
The browser UI relay and the gateway itself are infrastructure endpoints, outside
|
|
169
|
+
this source/target contract. Source addresses inside the workspace subnet or in
|
|
170
|
+
loopback, link-local, unspecified or multicast/reserved ranges are rejected because
|
|
171
|
+
those paths would bypass normal gateway routing.
|
|
172
|
+
|
|
173
|
+
The optional source protocol defaults to TCP. UDP DNS uses the same translation
|
|
174
|
+
path on port 53 to the built-in `coredns`/`dns` service. A fixed virtual endpoint such
|
|
175
|
+
as `198.18.0.53` remains independent of the dynamic workspace subnet, without an
|
|
176
|
+
interface alias or resolver-file rewrite. Benchmark addresses are restricted to
|
|
177
|
+
validated redirect sources; overlapping host/VPN/engine routes fail before mutation.
|
|
178
|
+
|
|
179
|
+
The gateway applies exact DNAT in its private namespace. Since the destination and
|
|
180
|
+
client share a subnet, it also SNATs redirected flows to its internal address so
|
|
181
|
+
replies return through the same conntrack translation. The test service sees the
|
|
182
|
+
gateway as its client; the original client sees the configured external endpoint.
|
|
183
|
+
TCP bytes (including HTTP headers/bodies) and UDP DNS messages are unchanged.
|
|
184
|
+
TLS remains end to end
|
|
185
|
+
and the application must validate the original endpoint's certificate as usual.
|
|
186
|
+
This follows Netfilter's [same-network NAT guidance](https://www.netfilter.org/documentation/HOWTO/NAT-HOWTO-6.html).
|
|
187
|
+
|
|
188
|
+
Filtering accepts only the configured original pair and the observed internal
|
|
189
|
+
target pair in the appropriate direction. It drops other packets for that original
|
|
190
|
+
pair before DNS/proxy/direct-egress grants, so a missing target never falls back to
|
|
191
|
+
the external IP. Bootstrap nodes remain excluded. Target addresses come from the
|
|
192
|
+
existing ownership-checked runtime discovery, with no additional inventory mapping.
|
|
193
|
+
CoreDNS retains the peer layout reservation at offset 3 within each dynamic workspace
|
|
194
|
+
subnet, keeping existing guest resolver addresses stable. A same-workspace recreation
|
|
195
|
+
normally keeps that address. A new workspace
|
|
196
|
+
with a different subnet has a different actual CoreDNS IP; the virtual endpoint
|
|
197
|
+
remains unchanged. Reconciliation never stores that actual IP in redirect inventory.
|
|
198
|
+
Before target recreation, the redirect is blocked; after services start, `up`
|
|
199
|
+
refreshes the policy using their current addresses. A target that exits or refuses
|
|
200
|
+
connections causes a connection failure. Removing a service still referenced by
|
|
201
|
+
configuration fails validation; remove its redirect as part of the same edit.
|
|
202
|
+
|
|
203
|
+
Reconciliation first installs DROP, installs changed NAT while forwarding is still
|
|
204
|
+
blocked, then clears conntrack only inside the owned gateway namespace and activates
|
|
205
|
+
the complete filter. Mapping changes therefore revoke old connections, including
|
|
206
|
+
previous connections to the original external destination. This also interrupts
|
|
207
|
+
other connections passing through that gateway; clients must reconnect. Identical
|
|
208
|
+
NAT on repeated `up` preserves connection state. Any apply/capability failure leaves
|
|
209
|
+
the gateway blocked/stopped. `status` checks complete filter/NAT rules and forwarding.
|
|
210
|
+
No host conntrack, foreign firewall or target container configuration is changed.
|
|
211
|
+
|
|
212
|
+
The same helper runs in the Linux engine for Docker, rootless Podman, Docker Desktop
|
|
213
|
+
and Podman Machine; QEMU peers use the existing adapters. Runtime isolation and
|
|
214
|
+
attachment requirements still apply. Custom `images.direct_egress` images must
|
|
215
|
+
implement `redirects-capability` (`tcp-udp-redirects-v2` for UDP DNS; legacy
|
|
216
|
+
`tcp-redirects-v1` remains valid for TCP-only configuration), the redirect plan and
|
|
217
|
+
conntrack invalidation; incompatible images fail explicitly. Native macOS requires
|
|
218
|
+
execution of the platform gates before claiming validation.
|
|
219
|
+
|
|
220
|
+
Redirects do not change DNS, proxy allowlists/environment, `NO_PROXY`, or bootstrap
|
|
221
|
+
grants. An application explicitly using a proxy initially connects to that proxy;
|
|
222
|
+
it still needs a proxy allowlist entry and receives no automatic direct exception.
|
|
223
|
+
Use application-specific DIRECT settings or `curl --noproxy '*'` for direct tests.
|
|
224
|
+
|
|
225
|
+
## Runtime attachment and privilege boundary
|
|
226
|
+
|
|
227
|
+
Normal Empeira operation requires no interactive sudo/root. One-time runtime,
|
|
228
|
+
TUN and accelerator setup can need administrator privileges.
|
|
229
|
+
|
|
230
|
+
Containers receive managed routes through short-lived helpers in their owned
|
|
231
|
+
network namespace and do not retain `NET_ADMIN`. Docker peer helpers have narrowly
|
|
232
|
+
scoped `NET_ADMIN` and `/dev/net/tun`, read-only filesystems and no new privileges.
|
|
233
|
+
|
|
234
|
+
Docker also requires a short-lived administrative helper in the engine Linux host
|
|
235
|
+
network namespace. It has `NET_ADMIN`/`NET_RAW` and installs only two exact owned
|
|
236
|
+
bridge/subnet rules: raw acceptance and same-bridge forwarding. It verifies network
|
|
237
|
+
labels/ID, bridge name and subnet first, changes no host routes/default policies,
|
|
238
|
+
never flushes firewall tables, and removes its exact rules before network deletion.
|
|
239
|
+
It mounts no host files, engine socket or PID namespace. This is a real privilege
|
|
240
|
+
boundary relying on Docker daemon authorization. Rootless Podman does not use it.
|
|
241
|
+
Docker iptables-nft/legacy interfaces are supported; unknown/native nftables-only
|
|
242
|
+
engines fail closed. Docker Desktop host-network helper restrictions require
|
|
243
|
+
actual platform validation.
|
|
244
|
+
|
|
245
|
+
Do not attach extra interfaces or restart gateways outside the managed lifecycle.
|
|
246
|
+
Cross-workspace subnets, namespaces and bridge rules are distinct, but this shared
|
|
247
|
+
network does not protect against a malicious privileged peer.
|
|
248
|
+
|
|
249
|
+
The gateway recipe uses official Ruby/Alpine and distribution iproute2/iptables
|
|
250
|
+
and [conntrack-tools](https://www.netfilter.org/projects/conntrack-tools/)
|
|
251
|
+
(Netfilter, GPL-2.0-or-later) packages. The connection tool is a separate executable
|
|
252
|
+
in the owned helper, with no host installation or new Ruby dependency. Go/Ruby,
|
|
253
|
+
image and package notices are preserved in built images; only
|
|
254
|
+
project-owned source/recipes are shipped in Empeira. See
|
|
255
|
+
[provenance](configuration.md#control-plane-images-and-provenance).
|
|
256
|
+
|
|
257
|
+
## DNS and service communication
|
|
258
|
+
|
|
259
|
+
CoreDNS owns `empeira.internal` service names and node hostnames. Unknown names in
|
|
260
|
+
that internal zone never forward externally. Other names use host-derived resolvers:
|
|
261
|
+
|
|
262
|
+
```yaml
|
|
263
|
+
dns:
|
|
264
|
+
upstream:
|
|
265
|
+
mode: host
|
|
266
|
+
servers: []
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
Linux/WSL2 reads the host resolver configuration, including systemd-resolved's
|
|
270
|
+
upstream file when needed. macOS reads `scutil --dns` and keeps per-domain routes.
|
|
271
|
+
Unusable loopback/link-local resolvers fail with instructions for an explicit
|
|
272
|
+
reachable IPv4 override. There is no public fallback resolver.
|
|
273
|
+
|
|
274
|
+
```yaml
|
|
275
|
+
dns:
|
|
276
|
+
upstream:
|
|
277
|
+
mode: explicit
|
|
278
|
+
servers: [10.20.30.53]
|
|
279
|
+
additional_resolver: resolver.empeira.internal
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
The optional additional resolver is queried first for non-Empeira names; NXDOMAIN
|
|
283
|
+
or NODATA falls through to the existing upstream. It may be a DNS name or address
|
|
284
|
+
and need not be an Empeira service. `up` activates it after additional services
|
|
285
|
+
start. VPN/engine routing remains the host's responsibility; flat Linux resolver
|
|
286
|
+
files cannot represent every split-DNS route. AAAA queries return NODATA.
|
|
287
|
+
|
|
288
|
+
### Exact DNS rewrites
|
|
289
|
+
|
|
290
|
+
`dns.rewrites` redirects an exact external hostname to an enabled internal service:
|
|
291
|
+
|
|
292
|
+
```yaml
|
|
293
|
+
dns:
|
|
294
|
+
rewrites:
|
|
295
|
+
- from: ipam.example.net
|
|
296
|
+
to: api-layer.empeira.internal
|
|
297
|
+
- from: inventory.example.net
|
|
298
|
+
to: api-layer.empeira.internal
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
Both container and VM nodes use the same CoreDNS policy. Sources are normalized
|
|
302
|
+
case-insensitively, with an optional trailing root dot. A rule matches only its
|
|
303
|
+
source: `child.ipam.example.net` still follows the normal resolver policy, including
|
|
304
|
+
host split-DNS routes and the optional additional resolver. Unknown internal names
|
|
305
|
+
and configured rewrite lookups never fall back to an external resolver.
|
|
306
|
+
|
|
307
|
+
CoreDNS uses an exact `rewrite name` rule and the existing authoritative hosts-file
|
|
308
|
+
discovery for `empeira.internal`. An A query returns the target service's current
|
|
309
|
+
IPv4 address under the original source name, with a one-second TTL. No CNAME record
|
|
310
|
+
is created: CNAME queries return NOERROR with no answers for a discovered target.
|
|
311
|
+
AAAA queries retain IPv4-only behavior and return NOERROR with no answers. If a
|
|
312
|
+
target temporarily has no discovery record, A/CNAME queries return SERVFAIL locally;
|
|
313
|
+
AAAA remains empty. `up` rejects targets absent from the enabled service plan.
|
|
314
|
+
|
|
315
|
+
Adding, changing or removing rules takes effect through `empeira up`, using the
|
|
316
|
+
existing Corefile reload and SIGUSR1. CoreDNS and existing nodes keep their
|
|
317
|
+
identities and resolver bindings. Hosts-file updates are polled every second, so
|
|
318
|
+
service-address changes do not require a CoreDNS restart. Client DNS caches may
|
|
319
|
+
delay observations until their cached records expire. Unchanged `up` is idempotent.
|
|
320
|
+
Changes to the DNS image or container definition still retain the existing
|
|
321
|
+
protection against DNS replacement while nodes exist.
|
|
322
|
+
|
|
323
|
+
An API Compatibility Layer can therefore simulate several production API names
|
|
324
|
+
through one additional service; see the [configuration example](configuration.md#dns-rewrites-for-internal-services).
|
|
325
|
+
Only DNS changes. The original HTTP Host, TLS SNI, protocol, port and certificate
|
|
326
|
+
checks remain the application's responsibility. Gateway grants and proxy allowlists
|
|
327
|
+
are unchanged; a client using an HTTP proxy remains subject to that proxy's policy.
|
|
328
|
+
|
|
329
|
+
The server is `server.empeira.internal:8140`. PostgreSQL and OpenVoxDB are internal,
|
|
330
|
+
with retained volumes and random private database credentials. Readiness verifies
|
|
331
|
+
real SQL and database-backed PuppetDB queries.
|
|
332
|
+
|
|
333
|
+
Puppet/OpenVox Server uses `https://puppetdb.empeira.internal:8081`; internal tools
|
|
334
|
+
use `http://puppetdb.empeira.internal:8080`. Both fixed relay listeners forward to
|
|
335
|
+
`puppetdb-backend.empeira.internal:8080`. The HTTPS relay has a workspace CA-issued
|
|
336
|
+
certificate for terminus compatibility; it does not authenticate clients.
|
|
337
|
+
**PuppetDB HTTP is deliberately unauthenticated within the isolated lab.** It is
|
|
338
|
+
never host-published; this is not a production deployment recommendation.
|
|
339
|
+
PostgreSQL transport TLS is not configured. Normal agent/server/CA TLS and disabled
|
|
340
|
+
global autosigning remain intact.
|
|
341
|
+
|
|
342
|
+
`down` retains CA/database volumes and generated credentials; `destroy` removes
|
|
343
|
+
owned persistent data. Missing recorded storage or credentials fails closed.
|
|
344
|
+
Preserve state and volumes together for recovery.
|
|
345
|
+
|
|
346
|
+
## Internal browser and additional services
|
|
347
|
+
|
|
348
|
+
After `up`, `browser` starts/reuses only Chromium and its fixed UI relay and prints
|
|
349
|
+
`https://127.0.0.1:<dynamic-port>/`. It does not start stopped infrastructure.
|
|
350
|
+
The relay carries TLS only to the observed browser's port 3001, accepts no arbitrary
|
|
351
|
+
forwarding target and disables IP forwarding. Chromium stays isolated and has no
|
|
352
|
+
Internet grant from enabling the node proxy; its profile is disposable.
|
|
353
|
+
|
|
354
|
+
Additional services receive `<name>.empeira.internal` and publish no host ports.
|
|
355
|
+
Use internal URLs in Chromium, for example
|
|
356
|
+
`http://openvoxview.empeira.internal:5000` or `http://host1:8080`. VM/container peers
|
|
357
|
+
are reachable directly at their logical names, with no application exposure matrix.
|
|
358
|
+
The UI is accessible to local users who can reach its loopback endpoint.
|
|
359
|
+
|
|
360
|
+
Image/module acquisition uses the separate [update plane](architecture.md#runtime-and-update-planes),
|
|
361
|
+
with native runtime networking and user proxy/registry settings. Those helpers
|
|
362
|
+
never join the workspace or start its DNS/proxy/services.
|