empeira 0.1.0.pre.alpha.4 → 0.1.0.pre.alpha.7
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/config/defaults.yaml +11 -4
- data/docs/addons.md +10 -6
- data/docs/architecture.md +18 -0
- data/docs/configuration.md +50 -23
- data/docs/hiera-and-eyaml.md +2 -2
- data/docs/networking.md +16 -7
- data/docs/nodes.md +9 -3
- data/docs/proxy.md +114 -19
- data/docs/troubleshooting.md +2 -2
- data/lib/empeira/build.json +3 -3
- data/lib/empeira/cli/proxy.rb +2 -2
- data/lib/empeira/configuration/schema.rb +28 -2
- data/lib/empeira/control_plane/controller.rb +16 -4
- data/lib/empeira/control_plane/files.rb +1 -8
- data/lib/empeira/control_plane/health.rb +11 -0
- data/lib/empeira/control_plane/plan.rb +17 -15
- data/lib/empeira/control_plane/startup_failure.rb +43 -0
- data/lib/empeira/infrastructure/cleanup.rb +6 -0
- data/lib/empeira/infrastructure/session_lock.rb +60 -0
- data/lib/empeira/network/direct_egress.rb +0 -7
- data/lib/empeira/network/gateway.rb +9 -1
- data/lib/empeira/network/host_policy.rb +1 -1
- data/lib/empeira/network/peer/layout.rb +8 -0
- data/lib/empeira/network/proxy_bindings.rb +27 -10
- data/lib/empeira/network/proxy_policy.rb +1 -1
- data/lib/empeira/node/container_lifecycle.rb +41 -23
- data/lib/empeira/node/runtime_proxy.rb +2 -2
- data/lib/empeira/node/session_lock.rb +19 -0
- data/lib/empeira/runtime/service_arguments.rb +7 -1
- data/lib/empeira/runtime/service_inspection.rb +5 -1
- data/lib/empeira/vm/session_lock.rb +2 -46
- data/lib/empeira.rb +3 -0
- data/resources/gateway/configure.rb +21 -11
- data/resources/nodes/management/guest_agent.py +4 -0
- metadata +4 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 3f0b0c2558d0799e27a8985ab84de156e8de49741ec078965338d0c1638ab0d3
|
|
4
|
+
data.tar.gz: 5607b7c52a5fb130b7a8534318aecb89c0093709ea7d9b65fcccef86da745fab
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 5a0eaa2c61a33c12d49632cfa4690f9e369bbae26a1d4cf4f9b1c5ae0d3178d6aed0f14f28721d1a83e1332319b02cf5a899f8f31287b88816b2e2e7ba3e8366
|
|
7
|
+
data.tar.gz: ed8ab5306f5948ed1f81c8e1b00121cb6360490e35daf915b95c441a42174552320b08fcc37a193eb2e1011b694eb1f85932739df3ee0d2274b182fded0b6957
|
data/config/defaults.yaml
CHANGED
|
@@ -50,12 +50,19 @@ puppetdb:
|
|
|
50
50
|
bootstrap_config: /etc/puppetlabs/puppetdb/bootstrap.cfg
|
|
51
51
|
data: /opt/puppetlabs/server/data/puppetdb
|
|
52
52
|
network:
|
|
53
|
-
egress:
|
|
53
|
+
egress:
|
|
54
|
+
nodes: []
|
|
55
|
+
infrastructure: []
|
|
54
56
|
redirects: []
|
|
55
57
|
proxy:
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
58
|
+
memory: 512
|
|
59
|
+
nodes:
|
|
60
|
+
enabled: false
|
|
61
|
+
global: []
|
|
62
|
+
rules: []
|
|
63
|
+
infrastructure:
|
|
64
|
+
enabled: false
|
|
65
|
+
allow: []
|
|
59
66
|
vm:
|
|
60
67
|
disk: 30
|
|
61
68
|
interfaces: []
|
data/docs/addons.md
CHANGED
|
@@ -208,8 +208,12 @@ Array elements can be mappings, arrays or scalars. For example:
|
|
|
208
208
|
|
|
209
209
|
```yaml
|
|
210
210
|
proxy:
|
|
211
|
-
|
|
212
|
-
|
|
211
|
+
nodes:
|
|
212
|
+
enabled: true
|
|
213
|
+
global: [forge.puppet.com, github.com]
|
|
214
|
+
infrastructure:
|
|
215
|
+
enabled: false
|
|
216
|
+
allow: []
|
|
213
217
|
server:
|
|
214
218
|
mounts:
|
|
215
219
|
- source: /home/user/data
|
|
@@ -217,10 +221,10 @@ server:
|
|
|
217
221
|
readonly: true
|
|
218
222
|
```
|
|
219
223
|
|
|
220
|
-
produces `
|
|
221
|
-
`
|
|
222
|
-
`
|
|
223
|
-
`
|
|
224
|
+
produces `EMPEIRA_CONFIG_PROXY_NODES_ENABLED=true`,
|
|
225
|
+
`EMPEIRA_CONFIG_PROXY_NODES_GLOBAL_COUNT=2`,
|
|
226
|
+
`EMPEIRA_CONFIG_PROXY_NODES_GLOBAL_0=forge.puppet.com`,
|
|
227
|
+
`EMPEIRA_CONFIG_PROXY_NODES_GLOBAL_1=github.com`,
|
|
224
228
|
`EMPEIRA_CONFIG_SERVER_MOUNTS_COUNT=1`, and the variables
|
|
225
229
|
`EMPEIRA_CONFIG_SERVER_MOUNTS_0_SOURCE`, `_TARGET`, `_READONLY`.
|
|
226
230
|
|
data/docs/architecture.md
CHANGED
|
@@ -93,6 +93,24 @@ system SSH. Guest commands use argument arrays and owned process groups; stdout
|
|
|
93
93
|
and stderr stream in bounded frames without truncation. Timeouts, interrupts and
|
|
94
94
|
host disconnects terminate those groups. Binary uploads are SHA-256 verified,
|
|
95
95
|
staged privately and atomically installed in the real guest filesystem.
|
|
96
|
+
The management supervisor and workers retain `umask 077`, private 0700 staging and
|
|
97
|
+
explicit upload modes. Only executed commands (including Puppet and its children)
|
|
98
|
+
and PTY shells start with `umask 022`. Previously the worker's private mask leaked
|
|
99
|
+
into these children, causing ordinary file/directory creation to yield 0600/0700;
|
|
100
|
+
APT could not read public keys created by commands that omitted an explicit mode.
|
|
101
|
+
Commands still run as numeric root, and explicit private modes remain private.
|
|
102
|
+
Empeira's native APT acquisition already copies its own signing key with mode 0644
|
|
103
|
+
under `/var/tmp`; it does not create project repository keys under
|
|
104
|
+
`/usr/share/keyrings` or `/etc/apt/keyrings`.
|
|
105
|
+
|
|
106
|
+
This correction applies to newly seeded VMs. Recreate disposable existing VMs to
|
|
107
|
+
receive the updated adapter; `up` does not replace seeded management code or repair
|
|
108
|
+
existing files. No directory-wide chmod or post-Puppet keyring rewrite is performed.
|
|
109
|
+
A catalog explicitly setting mode 0600, or a tool using private temporary files,
|
|
110
|
+
still needs correction at its owning resource. Project-managed public APT keys
|
|
111
|
+
should specify root:root and 0644, with searchable parent directories. The reported
|
|
112
|
+
Docker/Elastic/project key names are absent from Empeira's source; their exact
|
|
113
|
+
catalog resources require inspection in the affected project.
|
|
96
114
|
|
|
97
115
|
The QEMU monitor verifies workspace/node/instance identity before management.
|
|
98
116
|
A 0700 host directory and 0600 Unix socket restrict access; recorded device/inode
|
data/docs/configuration.md
CHANGED
|
@@ -107,9 +107,15 @@ runtime:
|
|
|
107
107
|
server:
|
|
108
108
|
environment: production
|
|
109
109
|
network:
|
|
110
|
-
egress:
|
|
110
|
+
egress:
|
|
111
|
+
nodes: []
|
|
112
|
+
infrastructure: []
|
|
111
113
|
proxy:
|
|
112
|
-
|
|
114
|
+
nodes:
|
|
115
|
+
enabled: false
|
|
116
|
+
infrastructure:
|
|
117
|
+
enabled: false
|
|
118
|
+
allow: []
|
|
113
119
|
node_defaults:
|
|
114
120
|
os: ubuntu
|
|
115
121
|
version: "24.04"
|
|
@@ -150,11 +156,15 @@ release version. A partial project mapping may omit it and inherit the default.
|
|
|
150
156
|
| `server.timeout` | Positive readiness timeout in seconds per component, `240` |
|
|
151
157
|
| `server.mounts` | Additional server-only host bind mounts, `[]`; `source`, `target`, optional boolean `readonly` (default `true`) |
|
|
152
158
|
| `puppetdb.memory` | Positive integer MiB, `768` |
|
|
153
|
-
| `network.egress` |
|
|
159
|
+
| `network.egress.nodes` | Node-only direct TCP host/port grants, `[]`; arrays replace |
|
|
160
|
+
| `network.egress.infrastructure` | Infrastructure-only direct TCP host/port grants, `[]`; arrays replace |
|
|
154
161
|
| `network.redirects` | Exact IPv4/protocol/port endpoints redirected to enabled internal services, `[]` |
|
|
155
|
-
| `proxy.
|
|
156
|
-
| `proxy.
|
|
157
|
-
| `proxy.
|
|
162
|
+
| `proxy.memory` | Positive integer MiB, `512` per Squid container; shared regular proxy and separate temporary bootstrap proxy each use this limit; does not enable either proxy |
|
|
163
|
+
| `proxy.nodes.enabled` | Boolean, `false`; enables normal node HTTP/HTTPS proxy access |
|
|
164
|
+
| `proxy.nodes.global` | DNS destination array, `[]` |
|
|
165
|
+
| `proxy.nodes.rules` | Additive node hostname-glob rules with `hosts` and `allow` arrays, `[]` |
|
|
166
|
+
| `proxy.infrastructure.enabled` | Boolean, `false`; enables infrastructure HTTP/HTTPS proxy access |
|
|
167
|
+
| `proxy.infrastructure.allow` | Independent infrastructure DNS destination array, `[]` |
|
|
158
168
|
| `vm.disk` | New VM virtual disk capacity in GiB, integer `1`–`2048`, default `30`; must accommodate the base image |
|
|
159
169
|
| `vm.interfaces` | VM-only hostname rules defining dummy/VLAN devices with explicit IPv4 CIDR host addresses, `[]` |
|
|
160
170
|
| `vm.console.root_password` | Initial console password, `empeira`; a string overrides it and `null` disables it |
|
|
@@ -488,14 +498,20 @@ The numeric gem packaging fallback is never used to bypass this check.
|
|
|
488
498
|
```yaml
|
|
489
499
|
network:
|
|
490
500
|
egress:
|
|
491
|
-
|
|
492
|
-
|
|
501
|
+
nodes:
|
|
502
|
+
- host: api.example.com
|
|
503
|
+
ports: [443]
|
|
504
|
+
infrastructure: []
|
|
493
505
|
proxy:
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
506
|
+
nodes:
|
|
507
|
+
enabled: true
|
|
508
|
+
global: [forgeapi.puppet.com]
|
|
509
|
+
rules:
|
|
510
|
+
- hosts: ["*-web-*"]
|
|
511
|
+
allow: [github.com, "*.github.com"]
|
|
512
|
+
infrastructure:
|
|
513
|
+
enabled: false
|
|
514
|
+
allow: []
|
|
499
515
|
hiera:
|
|
500
516
|
mounts:
|
|
501
517
|
- source: ../hieradata
|
|
@@ -507,7 +523,12 @@ vm:
|
|
|
507
523
|
root_password: null
|
|
508
524
|
```
|
|
509
525
|
|
|
510
|
-
When `proxy.enabled` is true, every node may use its matching destination policy.
|
|
526
|
+
When `proxy.nodes.enabled` is true, every node may use its matching destination policy.
|
|
527
|
+
Infrastructure access is separately controlled by `proxy.infrastructure.enabled`
|
|
528
|
+
and `proxy.infrastructure.allow`; both scopes default to disabled. Disabled
|
|
529
|
+
infrastructure has no configured proxy endpoint even when nodes use the proxy.
|
|
530
|
+
Keep its direct-egress list empty for internal-only infrastructure. See the
|
|
531
|
+
[migration instructions](proxy.md#migrating-shared-settings) for former shared keys.
|
|
511
532
|
After bootstrap cleanup, Empeira reconciles normal proxy settings on running,
|
|
512
533
|
provisioned nodes during `up` and `node start`. Repeated Puppet runs use the
|
|
513
534
|
existing guest configuration. No
|
|
@@ -719,21 +740,24 @@ desired state; Puppet owns package state after the first catalog.
|
|
|
719
740
|
|
|
720
741
|
## Direct egress
|
|
721
742
|
|
|
722
|
-
`network.egress`
|
|
723
|
-
|
|
743
|
+
`network.egress.nodes` and `network.egress.infrastructure` contain separate
|
|
744
|
+
destination TCP grants, independent of HTTP/HTTPS proxy policy. A node grant never
|
|
745
|
+
applies to infrastructure or conversely. Entries have exactly one `host` or IPv4 `ip` and a nonempty unique `ports`
|
|
724
746
|
array (1–65535). `up` resolves all A records and replaces the complete policy;
|
|
725
747
|
resolution/apply failure blocks egress. IPv6 and external UDP grants are unsupported.
|
|
726
748
|
|
|
727
749
|
```yaml
|
|
728
750
|
network:
|
|
729
751
|
egress:
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
752
|
+
nodes:
|
|
753
|
+
- host: repository.example.net
|
|
754
|
+
ports: [443]
|
|
755
|
+
- ip: 203.0.113.10
|
|
756
|
+
ports: [443, 8443]
|
|
757
|
+
infrastructure: []
|
|
734
758
|
```
|
|
735
759
|
|
|
736
|
-
|
|
760
|
+
Direct destinations enter only their own scope's generated `NO_PROXY`; applications choose DIRECT
|
|
737
761
|
or proxy themselves. See [networking](networking.md#one-gateway-one-direct-egress-policy)
|
|
738
762
|
for routing, revocation and privilege boundaries.
|
|
739
763
|
|
|
@@ -765,7 +789,7 @@ vm:
|
|
|
765
789
|
network: 192.0.2.20/32
|
|
766
790
|
```
|
|
767
791
|
|
|
768
|
-
Patterns use the same `*`/`?` glob matcher as `proxy.rules`, against the full stored
|
|
792
|
+
Patterns use the same `*`/`?` glob matcher as `proxy.nodes.rules`, against the full stored
|
|
769
793
|
node hostname, case-insensitively. Patterns within a rule are alternatives. All
|
|
770
794
|
matching rules contribute devices; identical definitions merge once. A conflicting
|
|
771
795
|
definition names the hostname, device and both configuration paths. No match creates
|
|
@@ -947,7 +971,10 @@ The default control plane has six containers, or seven with the proxy, plus the
|
|
|
947
971
|
runtime's peer-network adapter. Requested limits
|
|
948
972
|
are 1536 MiB for the server (768 MiB JVM heap), 768 MiB for PuppetDB (256 MiB heap),
|
|
949
973
|
256 MiB for PostgreSQL, 64 MiB for DNS, 96 MiB for the database relay, 64 MiB
|
|
950
|
-
for the gateway and
|
|
974
|
+
for the gateway and 512 MiB for the regular Squid proxy (`proxy.memory`). A temporary
|
|
975
|
+
bootstrap Squid container has its own additional limit of the same size while
|
|
976
|
+
running. These are per-container limits, not reservations or measured peaks.
|
|
977
|
+
The control-plane memory ceiling is about
|
|
951
978
|
2.7 GiB without the proxy, excluding the peer adapter and nodes. The server gets two CPUs and each
|
|
952
979
|
other service one. Actual idle use is lower and varies by provider and platform.
|
|
953
980
|
`up` automatically pulls missing remote images and builds missing recipe images.
|
data/docs/hiera-and-eyaml.md
CHANGED
|
@@ -207,8 +207,8 @@ The obsolete `images.modules` key is rejected.
|
|
|
207
207
|
The disposable installer belongs to the update plane. It uses the selected
|
|
208
208
|
runtime's normal bridge network (`bridge` on Docker, `podman` on Podman), with
|
|
209
209
|
normal runtime DNS and external connectivity. It never joins an Empeira workspace
|
|
210
|
-
network and needs neither CoreDNS nor Squid. Workspace `network.egress`, `proxy.enabled`,
|
|
211
|
-
`proxy.global` and `proxy.rules` do not affect
|
|
210
|
+
network and needs neither CoreDNS nor Squid. Workspace `network.egress`, `proxy.nodes.enabled`,
|
|
211
|
+
`proxy.nodes.global` and `proxy.nodes.rules` do not affect
|
|
212
212
|
Forge or Git acquisition. Supplied `HTTP_PROXY`, `HTTPS_PROXY`, `NO_PROXY` and their
|
|
213
213
|
lowercase equivalents are inherited unchanged; Empeira generates no proxy URL.
|
|
214
214
|
The host/runtime's normal network and corporate-proxy configuration must permit
|
data/docs/networking.md
CHANGED
|
@@ -102,17 +102,26 @@ Every peer uses the owned gateway as its IPv4 default route. Only the gateway ha
|
|
|
102
102
|
an external uplink; DNS, proxies, server, database and nodes stay isolated.
|
|
103
103
|
The fixed browser UI relay has a separate loopback access path with forwarding off.
|
|
104
104
|
|
|
105
|
-
`network.egress`
|
|
105
|
+
`network.egress.nodes` and `network.egress.infrastructure` grant destination
|
|
106
|
+
IPv4/TCP-port pairs independently:
|
|
106
107
|
|
|
107
108
|
```yaml
|
|
108
109
|
network:
|
|
109
110
|
egress:
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
111
|
+
nodes:
|
|
112
|
+
- host: repository.example.net
|
|
113
|
+
ports: [443]
|
|
114
|
+
- ip: 203.0.113.10
|
|
115
|
+
ports: [443, 8443]
|
|
116
|
+
infrastructure: []
|
|
114
117
|
```
|
|
115
118
|
|
|
119
|
+
The gateway restricts node grants to the reserved VM/container node address ranges
|
|
120
|
+
and infrastructure grants to its separate runtime allocation pool. Both directions
|
|
121
|
+
of direct connections are source-bound. The shared workspace subnet is never used
|
|
122
|
+
as a blanket source for direct grants. Custom gateway images must advertise
|
|
123
|
+
`scoped-egress-v1`; an older image fails closed before receiving external access.
|
|
124
|
+
|
|
116
125
|
Entries have exactly one `host` or `ip`. Hostnames match exactly, without wildcards;
|
|
117
126
|
`up` resolves every current A record and replaces the complete policy. IP selectors
|
|
118
127
|
bypass DNS and may intentionally permit private LAN/VPN destinations. A different
|
|
@@ -128,9 +137,9 @@ CoreDNS alone receives TCP/UDP 53 to its selected resolvers. Proxies alone recei
|
|
|
128
137
|
TCP 80/443 and enforce their destination allowlists. Normal proxy allow rules apply
|
|
129
138
|
regardless of target IP range; the temporary authenticated bootstrap proxy retains
|
|
130
139
|
its separate private/reserved-address restrictions.
|
|
131
|
-
`proxy.global` does not grant direct egress. Applications choose DIRECT/proxy using
|
|
140
|
+
`proxy.nodes.global` does not grant direct egress. Applications choose DIRECT/proxy using
|
|
132
141
|
their own settings and `NO_PROXY`; direct/proxy selection has no automatic fallback.
|
|
133
|
-
Both explicit hostnames and IPv4 selectors
|
|
142
|
+
Both explicit hostnames and IPv4 selectors are included only in their own scope's
|
|
134
143
|
normal proxy bypass settings. The gateway still permits only their configured TCP ports.
|
|
135
144
|
See [proxy policy](proxy.md) for hostname rules and bootstrap destinations.
|
|
136
145
|
|
data/docs/nodes.md
CHANGED
|
@@ -296,12 +296,18 @@ Empeira uses the private privileged VirtIO channel internally to install the VM
|
|
|
296
296
|
enroll certificates and run Puppet. User-facing SSH opens an ordinary interactive
|
|
297
297
|
login without the internal provisioning command or automatic sudo.
|
|
298
298
|
|
|
299
|
-
VM exec, shell, SSH and serial sessions release the workspace
|
|
300
|
-
running-state checks. Parallel sessions and read-only status remain available. A
|
|
299
|
+
Container SSH and VM exec, shell, SSH and serial sessions release the workspace
|
|
300
|
+
mutation lock after ownership and running-state checks. Parallel sessions and read-only status remain available. A
|
|
301
301
|
shared instance guard prevents `node stop`, `node destroy` or restart from replacing
|
|
302
|
-
|
|
302
|
+
that node instance while a session is attached; detach first and retry the conflicting operation.
|
|
303
303
|
Other nodes and workspace commands can proceed. Instance lock files stay stable;
|
|
304
304
|
their open descriptors are released on detach, disconnect, errors and signals.
|
|
305
|
+
Container SSH preparation remains inside the workspace transaction and pins the
|
|
306
|
+
observed container ID before releasing that lock. Concurrent SSH sessions to the
|
|
307
|
+
same or different nodes are allowed. Container `exec` and `shell` retain their
|
|
308
|
+
existing workspace transaction. Mutations still use the exclusive workspace lock;
|
|
309
|
+
`down` continues to require node removal, and destroy retains attached instances
|
|
310
|
+
for a retry after sessions detach.
|
|
305
311
|
|
|
306
312
|
## Root diagnostic commands and interactive shell
|
|
307
313
|
|
data/docs/proxy.md
CHANGED
|
@@ -1,19 +1,24 @@
|
|
|
1
1
|
# Proxy policy
|
|
2
2
|
|
|
3
|
-
Normal external HTTP/HTTPS access stays behind Squid.
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
Normal external HTTP/HTTPS access stays behind Squid. Nodes and infrastructure
|
|
4
|
+
have independent switches and destination lists, both disabled by default. Enabling
|
|
5
|
+
the node scope makes its global and hostname rules available to nodes:
|
|
6
6
|
|
|
7
7
|
```yaml
|
|
8
8
|
proxy:
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
9
|
+
memory: 512 # MiB per regular or temporary bootstrap Squid container
|
|
10
|
+
nodes:
|
|
11
|
+
enabled: true
|
|
12
|
+
global:
|
|
13
|
+
- forgeapi.puppet.com
|
|
14
|
+
rules:
|
|
15
|
+
- hosts: ["lab-*"]
|
|
16
|
+
allow: ["*.github.com"]
|
|
17
|
+
- hosts: ["*-web-*"]
|
|
18
|
+
allow: [github.com, nginx.org]
|
|
19
|
+
infrastructure:
|
|
20
|
+
enabled: false
|
|
21
|
+
allow: []
|
|
17
22
|
```
|
|
18
23
|
|
|
19
24
|
```console
|
|
@@ -44,9 +49,60 @@ The diagnostic resolves desired policy even for a hostname that does not exist.
|
|
|
44
49
|
It reports when the normal proxy is disabled and does not claim that live
|
|
45
50
|
infrastructure has already been reconciled.
|
|
46
51
|
|
|
52
|
+
## Memory budget and startup diagnostics
|
|
53
|
+
|
|
54
|
+
`proxy.memory` is a positive integer MiB limit per managed Squid container,
|
|
55
|
+
defaulting to 512. Node access and infrastructure access share one regular proxy;
|
|
56
|
+
the same limit applies when either or both scopes are enabled. The temporary,
|
|
57
|
+
authenticated bootstrap proxy inherits this value as its own container limit.
|
|
58
|
+
If both containers run simultaneously, each has a separate 512 MiB limit by
|
|
59
|
+
default (1024 MiB combined), not one shared 512 MiB budget. These limits are
|
|
60
|
+
neither memory reservations nor measured peak consumption.
|
|
61
|
+
|
|
62
|
+
Existing configurations inherit the default. Setting `proxy.memory` alone does
|
|
63
|
+
not enable a regular proxy or start a bootstrap proxy. Node and infrastructure
|
|
64
|
+
access keep independent activation switches and destination grants. Bootstrap
|
|
65
|
+
keeps its separate temporary activation, authentication and destination grants.
|
|
66
|
+
Docker and Podman use the same native
|
|
67
|
+
`--memory` argument, including rootless Podman. The engine host (or its Linux VM on
|
|
68
|
+
macOS) still needs enough available memory and working cgroup memory controls.
|
|
69
|
+
|
|
70
|
+
The former fixed 128 MiB limit was below the reported killed process's approximately
|
|
71
|
+
139 MiB resident footprint (anonymous plus file RSS), before accounting for other
|
|
72
|
+
cgroup charges. The 512 MiB default provides headroom for startup, concurrent
|
|
73
|
+
requests, DNS and allocator overhead. It is a conservative budget, not a measured
|
|
74
|
+
maximum or a guarantee for arbitrary workloads. Validate actual peak usage with
|
|
75
|
+
native container stats and host cgroup/OOM diagnostics in the affected environment.
|
|
76
|
+
|
|
77
|
+
Generated policy keeps `cache deny all`, sets `cache_mem 16 MB`, and explicitly uses
|
|
78
|
+
`workers 1` together with `-N` for one foreground main process. An explicit
|
|
79
|
+
`workers 0` causes Squid 7.7 to exit during startup with
|
|
80
|
+
`FATAL: check failed: theProcessCount > 0`; `-N` already selects foreground mode.
|
|
81
|
+
No disk cache is configured. Squid's [cache_mem documentation](https://www.squid-cache.org/Doc/config/cache_mem/)
|
|
82
|
+
describes an object-memory target, not a cap on process RSS; in-flight requests can
|
|
83
|
+
exceed it. The [workers setting](https://www.squid-cache.org/Doc/config/workers/)
|
|
84
|
+
avoids multiplying main-process memory. These settings preserve DNS, source-bound
|
|
85
|
+
client rules and the HTTP/HTTPS destination policy.
|
|
86
|
+
|
|
87
|
+
If the regular proxy exits during startup or its readiness check, diagnostics
|
|
88
|
+
include the native exit code, `OOMKilled` observation and actual inspected memory
|
|
89
|
+
limit in bytes/MiB when available. Only `OOMKilled=true` confirms an OOM kill. Exit 137 alone does not:
|
|
90
|
+
external SIGKILL can produce the same exit. Missing observations are explicitly
|
|
91
|
+
reported as unavailable, and a runtime-reported zero limit is shown as unlimited.
|
|
92
|
+
For confirmed proxy OOM, increase `proxy.memory`, check engine-host capacity, and
|
|
93
|
+
run `empeira up`. Native logs remain available via the displayed runtime command.
|
|
94
|
+
The temporary bootstrap proxy uses its own endpoint verification and readiness
|
|
95
|
+
probe; the new regular-proxy OOM diagnostics do not apply to that separate path.
|
|
96
|
+
|
|
97
|
+
A memory change replaces only the regular proxy on the next `up`; CA/database
|
|
98
|
+
storage, nodes and unrelated services retain their identities. Unchanged runs
|
|
99
|
+
reuse the proxy. Generated policy changes use the existing readiness/reload checkpoint.
|
|
100
|
+
Replacing or reloading the proxy may interrupt active proxy connections.
|
|
101
|
+
The next temporary bootstrap proxy receives the configured limit when created.
|
|
102
|
+
|
|
47
103
|
## Matching and destinations
|
|
48
104
|
|
|
49
|
-
Defaults are `proxy.global: []` and `proxy.rules: []`. Every node receives
|
|
105
|
+
Defaults are `proxy.nodes.global: []` and `proxy.nodes.rules: []`. Every node receives
|
|
50
106
|
all global destinations plus `allow` from **every** matching rule. Duplicate entries
|
|
51
107
|
are removed while preserving configuration order. A rule matches when any of its
|
|
52
108
|
`hosts` patterns matches the complete lowercase hostname. Patterns must be lowercase.
|
|
@@ -66,14 +122,14 @@ Destination rules are separate from hostname globs:
|
|
|
66
122
|
Only HTTP port 80 and HTTPS CONNECT port 443 are allowed. Allowed DNS destinations
|
|
67
123
|
are reachable regardless of their target IP range, including private, loopback,
|
|
68
124
|
link-local and reserved addresses. Domains without a matching allow rule remain
|
|
69
|
-
denied. There is no TLS interception.
|
|
70
|
-
|
|
125
|
+
denied. There is no TLS interception. Node destinations never grant infrastructure
|
|
126
|
+
access, and infrastructure destinations never grant node access.
|
|
71
127
|
|
|
72
128
|
## Node policy and reconciliation
|
|
73
129
|
|
|
74
|
-
When `proxy.enabled` is true, all nodes are authorized as proxy clients. Nodes get
|
|
130
|
+
When `proxy.nodes.enabled` is true, all nodes are authorized as proxy clients. Nodes get
|
|
75
131
|
upper/lowercase proxy variables and internal `NO_PROXY` entries, including explicitly
|
|
76
|
-
configured `network.egress` hosts. Those named hosts use the common workspace
|
|
132
|
+
configured `network.egress.nodes` hosts. Those named hosts use the common workspace
|
|
77
133
|
gateway for their configured ports. VM access uses the same destination policy bound
|
|
78
134
|
directly to the VM instance's reserved peer address. The proxy is reached at
|
|
79
135
|
`proxy.empeira.internal:3128` on the isolated network. A request without a matching
|
|
@@ -90,17 +146,56 @@ Container and VM nodes consume the new policy without a restart; open proxy
|
|
|
90
146
|
connections can end during reconciliation. Enabling/disabling proxy mode is a
|
|
91
147
|
separate topology change and may require explicit node cleanup as reported by `up`.
|
|
92
148
|
|
|
93
|
-
Changing
|
|
94
|
-
environment. `up` replaces a server whose environment definition changes while
|
|
149
|
+
Changing `network.egress.infrastructure` destinations also changes infrastructure
|
|
150
|
+
`NO_PROXY`. Changing node destinations does not change the server environment. `up` replaces a server whose environment definition changes while
|
|
95
151
|
retaining CA storage and node identities. Changing only destination ports or
|
|
96
152
|
transparent redirect rules preserves the server when its definition is unchanged.
|
|
97
153
|
|
|
154
|
+
## Infrastructure policy
|
|
155
|
+
|
|
156
|
+
`proxy.infrastructure.enabled` controls the Puppet/OpenVox server, PostgreSQL,
|
|
157
|
+
PuppetDB backend/relay and `containers.additional` services. Its independent
|
|
158
|
+
`allow` list uses the same exact-domain and domain-family syntax as node grants.
|
|
159
|
+
The browser, DNS, Squid and gateway transports do not receive these application
|
|
160
|
+
proxy settings.
|
|
161
|
+
|
|
162
|
+
```yaml
|
|
163
|
+
proxy:
|
|
164
|
+
infrastructure:
|
|
165
|
+
enabled: true
|
|
166
|
+
allow: [api.example.net]
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
When disabled, infrastructure definitions contain empty proxy values, overriding
|
|
170
|
+
any image defaults. They contain no configured proxy endpoint and receive no Squid
|
|
171
|
+
client grants, even if the node proxy is enabled. No startup wrapper removes an
|
|
172
|
+
otherwise configured proxy. Internal names and mocks remain directly reachable.
|
|
173
|
+
When enabled, only infrastructure's own direct destinations enter its `NO_PROXY`.
|
|
174
|
+
Explicit environment settings in additional-service definitions remain application
|
|
175
|
+
configuration; they do not add Squid permissions.
|
|
176
|
+
|
|
177
|
+
`up` creates the shared normal proxy when either scope is enabled and removes it
|
|
178
|
+
when both are disabled. Changing infrastructure's switch updates affected service
|
|
179
|
+
definitions while retaining storage and node identities. Allowlist-only edits reload
|
|
180
|
+
Squid without replacing unrelated services. Direct grants remain independent of
|
|
181
|
+
both proxy switches; keep `network.egress.infrastructure: []` for internal-only
|
|
182
|
+
infrastructure.
|
|
183
|
+
|
|
184
|
+
## Migrating shared settings
|
|
185
|
+
|
|
186
|
+
The former `proxy.enabled`, `proxy.global` and `proxy.rules` keys are rejected with
|
|
187
|
+
a migration diagnostic. Move them under `proxy.nodes`; configure infrastructure
|
|
188
|
+
explicitly with its own switch and `allow` list. Old shared `network.egress` arrays
|
|
189
|
+
and legacy `mode` values are also rejected. Move grants to `network.egress.nodes`
|
|
190
|
+
or `network.egress.infrastructure` according to the intended clients. No existing
|
|
191
|
+
grant is silently copied to infrastructure.
|
|
192
|
+
|
|
98
193
|
## Managed bootstrap
|
|
99
194
|
|
|
100
195
|
Managed bootstrap destinations are separate from normal user policy. Reviewed
|
|
101
196
|
agent and distribution endpoints in `agent.install` and `bootstrap.guests` allow
|
|
102
197
|
Puppet/OpenVox and required guest packages to be installed without duplicating them
|
|
103
|
-
in `proxy.global`. The temporary authenticated bootstrap proxy uses workspace DNS
|
|
198
|
+
in `proxy.nodes.global`. The temporary authenticated bootstrap proxy uses workspace DNS
|
|
104
199
|
and retains its private, loopback, link-local and reserved destination-address
|
|
105
200
|
restrictions independently of normal proxy policy. Its credential is not stored in
|
|
106
201
|
the guest seed, JSON inventory or logs. A /32 source ACL admits only the provisioning
|
data/docs/troubleshooting.md
CHANGED
|
@@ -132,7 +132,7 @@ or restart of that same VM conflicts with the shared instance guard: detach with
|
|
|
132
132
|
## Proxy destination denied
|
|
133
133
|
|
|
134
134
|
Run `empeira proxy show HOSTNAME` and check every matching rule. Verify the node
|
|
135
|
-
exists in the workspace, `proxy.enabled` is true, and `up` has reconciled the
|
|
135
|
+
exists in the workspace, `proxy.nodes.enabled` is true, and `up` has reconciled the
|
|
136
136
|
configuration and current node addresses. Allowed DNS destinations may resolve to
|
|
137
137
|
private or reserved IP addresses; only HTTP port 80 and HTTPS CONNECT port 443 are
|
|
138
138
|
permitted. After updating Empeira, run `empeira up` to replace a proxy still using
|
|
@@ -367,6 +367,6 @@ external runtime network as a workaround. Docker workspace bridge attachment
|
|
|
367
367
|
errors require an accessible Linux-engine host namespace and an identifiable
|
|
368
368
|
iptables backend. The helper changes only owned bridge rules. Do not disable the
|
|
369
369
|
host firewall globally. Check Docker Desktop host-network restrictions separately.
|
|
370
|
-
Proxy errors require checking Squid source bindings and `proxy.global`/`proxy.rules`,
|
|
370
|
+
Proxy errors require checking Squid source bindings and `proxy.nodes.global`/`proxy.nodes.rules`,
|
|
371
371
|
not adding unrelated direct grants. Corporate DNS still follows host discovery;
|
|
372
372
|
IPv6-only upstreams must be replaced with reachable IPv4 resolvers for this backend.
|
data/lib/empeira/build.json
CHANGED
data/lib/empeira/cli/proxy.rb
CHANGED
|
@@ -5,7 +5,7 @@ module Empeira
|
|
|
5
5
|
class Proxy < Base
|
|
6
6
|
desc 'show HOSTNAME', 'Explain the normal proxy destination policy for a hostname'
|
|
7
7
|
def show(hostname)
|
|
8
|
-
config = application.context.configuration.
|
|
8
|
+
config = application.context.configuration.dig('proxy', 'nodes')
|
|
9
9
|
resolver = Network::HostPolicy.new(config)
|
|
10
10
|
say "Effective proxy policy for #{hostname.downcase}"
|
|
11
11
|
say access_message(config)
|
|
@@ -19,7 +19,7 @@ module Empeira
|
|
|
19
19
|
return "All workspace nodes may use this policy after 'empeira up' reconciles the proxy." if
|
|
20
20
|
config.fetch('enabled')
|
|
21
21
|
|
|
22
|
-
'The normal
|
|
22
|
+
'The normal node proxy is disabled.'
|
|
23
23
|
end
|
|
24
24
|
|
|
25
25
|
def display_matches(rules)
|
|
@@ -20,8 +20,11 @@ module Empeira
|
|
|
20
20
|
'environment' => :environment,
|
|
21
21
|
'memory' => :positive_integer, 'cpus' => :positive_integer, 'timeout' => :positive_integer },
|
|
22
22
|
'puppetdb' => { 'enabled' => :boolean, 'memory' => :positive_integer, 'runtime' => :puppetdb_runtime },
|
|
23
|
-
'network' => { 'egress' => :network_egress, '
|
|
24
|
-
|
|
23
|
+
'network' => { 'egress' => { 'nodes' => :network_egress, 'infrastructure' => :network_egress },
|
|
24
|
+
'redirects' => :network_redirects },
|
|
25
|
+
'proxy' => { 'memory' => :positive_integer,
|
|
26
|
+
'nodes' => { 'enabled' => :boolean, 'global' => :allowlist, 'rules' => :proxy_rules },
|
|
27
|
+
'infrastructure' => { 'enabled' => :boolean, 'allow' => :allowlist } },
|
|
25
28
|
'vm' => { 'disk' => :vm_disk, 'interfaces' => :vm_interfaces,
|
|
26
29
|
'console' => { 'root_password' => :optional_string } },
|
|
27
30
|
'dns' => { 'upstream' => { 'mode' => :dns_mode, 'servers' => :dns_servers },
|
|
@@ -116,12 +119,35 @@ module Empeira
|
|
|
116
119
|
end
|
|
117
120
|
|
|
118
121
|
def validate_fragment!(data)
|
|
122
|
+
validate_scope_migration!(data)
|
|
119
123
|
validate_mapping!(data, RULES, [])
|
|
120
124
|
data
|
|
121
125
|
end
|
|
122
126
|
|
|
123
127
|
private
|
|
124
128
|
|
|
129
|
+
def validate_scope_migration!(data)
|
|
130
|
+
return unless data.is_a?(Hash)
|
|
131
|
+
|
|
132
|
+
validate_proxy_scope_migration!(data['proxy'])
|
|
133
|
+
network = data['network']
|
|
134
|
+
validate_egress_scope_migration!(network['egress']) if network.is_a?(Hash)
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def validate_proxy_scope_migration!(proxy)
|
|
138
|
+
return unless proxy.is_a?(Hash) && proxy.keys.intersect?(%w[enabled global rules])
|
|
139
|
+
|
|
140
|
+
raise ConfigurationError, 'Shared proxy settings have moved to proxy.nodes; configure ' \
|
|
141
|
+
'proxy.infrastructure separately'
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def validate_egress_scope_migration!(egress)
|
|
145
|
+
return unless egress.is_a?(Array) || (egress.is_a?(Hash) && egress.key?('mode'))
|
|
146
|
+
|
|
147
|
+
raise ConfigurationError, 'Shared network.egress grants must be moved explicitly to ' \
|
|
148
|
+
'network.egress.nodes or network.egress.infrastructure'
|
|
149
|
+
end
|
|
150
|
+
|
|
125
151
|
def validate_operational!(data)
|
|
126
152
|
validate_images!(data.fetch('images'))
|
|
127
153
|
NodeImages.effective!(data.fetch('images'))
|
|
@@ -254,8 +254,10 @@ module Empeira
|
|
|
254
254
|
end
|
|
255
255
|
|
|
256
256
|
def resolve_direct_egress
|
|
257
|
-
|
|
258
|
-
|
|
257
|
+
entries = @plan.config.dig('network', 'egress').flat_map do |scope, grants|
|
|
258
|
+
@direct_resolver.resolve(grants).entries.map { |entry| entry.merge('scope' => scope) }
|
|
259
|
+
end
|
|
260
|
+
Network::DirectEgress::Resolved.new(entries)
|
|
259
261
|
end
|
|
260
262
|
|
|
261
263
|
def bootstrap_dns?(upstreams:, routes:)
|
|
@@ -526,8 +528,7 @@ module Empeira
|
|
|
526
528
|
def require_started!(key, resource)
|
|
527
529
|
return if resource.fetch('state') == 'running'
|
|
528
530
|
|
|
529
|
-
raise
|
|
530
|
-
"inspect #{@context.container_engine} logs #{resource.fetch('id')}"
|
|
531
|
+
raise StartupFailure.new(key: key, resource: resource, engine: @context.container_engine)
|
|
531
532
|
end
|
|
532
533
|
|
|
533
534
|
def update_hosts(reload_proxy: false)
|
|
@@ -636,6 +637,7 @@ module Empeira
|
|
|
636
637
|
end
|
|
637
638
|
reconcile('gateway')
|
|
638
639
|
lockdown_gateway(@observed.fetch('gateway'))
|
|
640
|
+
require_scoped_egress_capability
|
|
639
641
|
require_redirect_capability
|
|
640
642
|
attach_gateway('gateway')
|
|
641
643
|
apply_gateway_policy
|
|
@@ -668,6 +670,16 @@ module Empeira
|
|
|
668
670
|
'images.direct_egress image'
|
|
669
671
|
end
|
|
670
672
|
|
|
673
|
+
def require_scoped_egress_capability
|
|
674
|
+
return if @gateway_plan.fetch('entries').empty?
|
|
675
|
+
|
|
676
|
+
result = @runtime.service_exec(@observed.fetch('gateway'),
|
|
677
|
+
[Network::Gateway::EXECUTABLE, 'scoped-egress-capability'])
|
|
678
|
+
return if result.success? && result.stdout.strip == 'scoped-egress-v1'
|
|
679
|
+
|
|
680
|
+
raise Error, 'Gateway does not support scoped direct egress; configure a compatible images.direct_egress image'
|
|
681
|
+
end
|
|
682
|
+
|
|
671
683
|
def apply_gateway_policy
|
|
672
684
|
# An uncertain apply must force fresh lockdown during failure recovery.
|
|
673
685
|
@gateway_locked_id = nil
|