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.
Files changed (274) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +661 -0
  3. data/README.md +150 -0
  4. data/bin/empeira +18 -0
  5. data/config/defaults.yaml +229 -0
  6. data/docs/addons.md +331 -0
  7. data/docs/architecture.md +277 -0
  8. data/docs/configuration.md +1204 -0
  9. data/docs/development.md +472 -0
  10. data/docs/hiera-and-eyaml.md +361 -0
  11. data/docs/installation.md +280 -0
  12. data/docs/networking.md +362 -0
  13. data/docs/nodes.md +642 -0
  14. data/docs/proxy.md +136 -0
  15. data/docs/troubleshooting.md +361 -0
  16. data/lib/empeira/addons/environment.rb +82 -0
  17. data/lib/empeira/addons/error.rb +14 -0
  18. data/lib/empeira/addons/installation.rb +119 -0
  19. data/lib/empeira/addons/manifest.rb +112 -0
  20. data/lib/empeira/addons/service.rb +50 -0
  21. data/lib/empeira/addons/storage.rb +94 -0
  22. data/lib/empeira/agent/acquisition.rb +120 -0
  23. data/lib/empeira/agent/artifact.rb +41 -0
  24. data/lib/empeira/agent/authentication.rb +122 -0
  25. data/lib/empeira/agent/cache.rb +122 -0
  26. data/lib/empeira/agent/cache_storage.rb +82 -0
  27. data/lib/empeira/agent/download.rb +84 -0
  28. data/lib/empeira/agent/native_commands.rb +84 -0
  29. data/lib/empeira/agent/release_sources.rb +64 -0
  30. data/lib/empeira/agent/target.rb +66 -0
  31. data/lib/empeira/agent/versions.rb +29 -0
  32. data/lib/empeira/application.rb +86 -0
  33. data/lib/empeira/build.json +5 -0
  34. data/lib/empeira/build_info.rb +46 -0
  35. data/lib/empeira/cli/addons.rb +45 -0
  36. data/lib/empeira/cli/base.rb +73 -0
  37. data/lib/empeira/cli/completion.rb +42 -0
  38. data/lib/empeira/cli/config.rb +22 -0
  39. data/lib/empeira/cli/main.rb +114 -0
  40. data/lib/empeira/cli/node.rb +87 -0
  41. data/lib/empeira/cli/progress.rb +169 -0
  42. data/lib/empeira/cli/proxy.rb +36 -0
  43. data/lib/empeira/cli/update.rb +23 -0
  44. data/lib/empeira/completion/addons.rb +93 -0
  45. data/lib/empeira/completion/bash.rb +62 -0
  46. data/lib/empeira/completion/catalog.rb +170 -0
  47. data/lib/empeira/configuration/additional_services.rb +74 -0
  48. data/lib/empeira/configuration/addons.rb +85 -0
  49. data/lib/empeira/configuration/agent_schema.rb +163 -0
  50. data/lib/empeira/configuration/agent_values.rb +54 -0
  51. data/lib/empeira/configuration/bootstrap_guests.rb +42 -0
  52. data/lib/empeira/configuration/command_mocks.rb +65 -0
  53. data/lib/empeira/configuration/display.rb +20 -0
  54. data/lib/empeira/configuration/dns_rewrites.rb +58 -0
  55. data/lib/empeira/configuration/document.rb +67 -0
  56. data/lib/empeira/configuration/eyaml.rb +46 -0
  57. data/lib/empeira/configuration/hiera_modulepath.rb +40 -0
  58. data/lib/empeira/configuration/hiera_mount_schema.rb +62 -0
  59. data/lib/empeira/configuration/hiera_mounts.rb +83 -0
  60. data/lib/empeira/configuration/image_schema.rb +68 -0
  61. data/lib/empeira/configuration/loader.rb +70 -0
  62. data/lib/empeira/configuration/merge.rb +48 -0
  63. data/lib/empeira/configuration/network_redirects.rb +73 -0
  64. data/lib/empeira/configuration/node_images.rb +64 -0
  65. data/lib/empeira/configuration/schema.rb +215 -0
  66. data/lib/empeira/configuration/server_mounts.rb +106 -0
  67. data/lib/empeira/configuration/service_configuration.rb +40 -0
  68. data/lib/empeira/configuration/ssh_preferences.rb +83 -0
  69. data/lib/empeira/configuration/user.rb +46 -0
  70. data/lib/empeira/configuration/vm_interfaces.rb +147 -0
  71. data/lib/empeira/control_plane/additional_configurations.rb +106 -0
  72. data/lib/empeira/control_plane/browser.rb +73 -0
  73. data/lib/empeira/control_plane/controller.rb +763 -0
  74. data/lib/empeira/control_plane/discovery.rb +69 -0
  75. data/lib/empeira/control_plane/environment.rb +101 -0
  76. data/lib/empeira/control_plane/files.rb +263 -0
  77. data/lib/empeira/control_plane/health.rb +104 -0
  78. data/lib/empeira/control_plane/plan.rb +356 -0
  79. data/lib/empeira/control_plane/storage.rb +48 -0
  80. data/lib/empeira/errors.rb +9 -0
  81. data/lib/empeira/execution/console.rb +45 -0
  82. data/lib/empeira/execution/diagnostics.rb +210 -0
  83. data/lib/empeira/execution/launch_error.rb +14 -0
  84. data/lib/empeira/execution/result.rb +11 -0
  85. data/lib/empeira/execution/runner.rb +235 -0
  86. data/lib/empeira/execution/terminal.rb +35 -0
  87. data/lib/empeira/hostname_pattern.rb +14 -0
  88. data/lib/empeira/images/configuration.rb +44 -0
  89. data/lib/empeira/images/identity.rb +30 -0
  90. data/lib/empeira/images/recipe.rb +22 -0
  91. data/lib/empeira/images/reference.rb +43 -0
  92. data/lib/empeira/images/source.rb +19 -0
  93. data/lib/empeira/immutable.rb +19 -0
  94. data/lib/empeira/infrastructure/cleanup.rb +328 -0
  95. data/lib/empeira/infrastructure/definition.rb +59 -0
  96. data/lib/empeira/infrastructure/owned_files.rb +63 -0
  97. data/lib/empeira/infrastructure/recovery_inventory.rb +53 -0
  98. data/lib/empeira/infrastructure/service.rb +237 -0
  99. data/lib/empeira/infrastructure/status.rb +73 -0
  100. data/lib/empeira/infrastructure/store.rb +274 -0
  101. data/lib/empeira/modules/events.rb +67 -0
  102. data/lib/empeira/modules/git_safety.rb +60 -0
  103. data/lib/empeira/modules/host_git.rb +91 -0
  104. data/lib/empeira/modules/image.rb +27 -0
  105. data/lib/empeira/modules/installer.rb +67 -0
  106. data/lib/empeira/modules/request.rb +68 -0
  107. data/lib/empeira/modules/state.rb +87 -0
  108. data/lib/empeira/modules/storage.rb +35 -0
  109. data/lib/empeira/network/bootstrap_proxy.rb +157 -0
  110. data/lib/empeira/network/definition.rb +61 -0
  111. data/lib/empeira/network/direct_egress.rb +133 -0
  112. data/lib/empeira/network/egress.rb +27 -0
  113. data/lib/empeira/network/gateway.rb +79 -0
  114. data/lib/empeira/network/host_policy.rb +53 -0
  115. data/lib/empeira/network/naming.rb +28 -0
  116. data/lib/empeira/network/peer/adapter_image.rb +32 -0
  117. data/lib/empeira/network/peer/allocation.rb +45 -0
  118. data/lib/empeira/network/peer/attachment.rb +27 -0
  119. data/lib/empeira/network/peer/backend.rb +108 -0
  120. data/lib/empeira/network/peer/channel.rb +133 -0
  121. data/lib/empeira/network/peer/channel_server.rb +71 -0
  122. data/lib/empeira/network/peer/docker_adapter.rb +165 -0
  123. data/lib/empeira/network/peer/helper_security.rb +40 -0
  124. data/lib/empeira/network/peer/layout.rb +55 -0
  125. data/lib/empeira/network/peer/linux_podman.rb +114 -0
  126. data/lib/empeira/network/peer/podman_machine.rb +112 -0
  127. data/lib/empeira/network/policy.rb +39 -0
  128. data/lib/empeira/network/proxy_bindings.rb +52 -0
  129. data/lib/empeira/network/proxy_policy.rb +64 -0
  130. data/lib/empeira/network/redirects.rb +89 -0
  131. data/lib/empeira/network/resource.rb +50 -0
  132. data/lib/empeira/node/agent_installer.rb +31 -0
  133. data/lib/empeira/node/agent_package.rb +125 -0
  134. data/lib/empeira/node/agent_repository.rb +160 -0
  135. data/lib/empeira/node/apt_configuration.rb +85 -0
  136. data/lib/empeira/node/bootstrap.rb +17 -0
  137. data/lib/empeira/node/certificates.rb +98 -0
  138. data/lib/empeira/node/command_mock.rb +38 -0
  139. data/lib/empeira/node/command_mocks.rb +114 -0
  140. data/lib/empeira/node/container.rb +134 -0
  141. data/lib/empeira/node/container_agent_bootstrap.rb +43 -0
  142. data/lib/empeira/node/container_bootstrap.rb +52 -0
  143. data/lib/empeira/node/container_guest.rb +42 -0
  144. data/lib/empeira/node/container_lifecycle.rb +145 -0
  145. data/lib/empeira/node/container_preparation.rb +144 -0
  146. data/lib/empeira/node/container_state.rb +129 -0
  147. data/lib/empeira/node/definition.rb +20 -0
  148. data/lib/empeira/node/diagnostic_command.rb +19 -0
  149. data/lib/empeira/node/dnf_agent_repository.rb +202 -0
  150. data/lib/empeira/node/external_fact.rb +21 -0
  151. data/lib/empeira/node/image.rb +38 -0
  152. data/lib/empeira/node/interactive_tools.rb +33 -0
  153. data/lib/empeira/node/interface.rb +18 -0
  154. data/lib/empeira/node/inventory.rb +143 -0
  155. data/lib/empeira/node/package_bootstrap.rb +129 -0
  156. data/lib/empeira/node/package_proxy.rb +102 -0
  157. data/lib/empeira/node/package_sources.rb +19 -0
  158. data/lib/empeira/node/puppet_command.rb +12 -0
  159. data/lib/empeira/node/rpm_configuration.rb +62 -0
  160. data/lib/empeira/node/run_request.rb +52 -0
  161. data/lib/empeira/node/runtime_proxy.rb +86 -0
  162. data/lib/empeira/node/service.rb +72 -0
  163. data/lib/empeira/node/ssh_client.rb +25 -0
  164. data/lib/empeira/node/ssh_credentials.rb +78 -0
  165. data/lib/empeira/node/ssh_endpoint.rb +58 -0
  166. data/lib/empeira/node/ssh_identity.rb +37 -0
  167. data/lib/empeira/node/user_ssh.rb +60 -0
  168. data/lib/empeira/node/vm.rb +89 -0
  169. data/lib/empeira/node/vm_bootstrap.rb +53 -0
  170. data/lib/empeira/node/vm_lifecycle.rb +212 -0
  171. data/lib/empeira/node/vm_preflight.rb +37 -0
  172. data/lib/empeira/node/vm_preparation.rb +161 -0
  173. data/lib/empeira/node/vm_state.rb +161 -0
  174. data/lib/empeira/platform/facts.rb +66 -0
  175. data/lib/empeira/platform/kvm.rb +91 -0
  176. data/lib/empeira/platform/locations.rb +51 -0
  177. data/lib/empeira/platform/resolvers.rb +85 -0
  178. data/lib/empeira/platform/routes.rb +50 -0
  179. data/lib/empeira/progress.rb +70 -0
  180. data/lib/empeira/project_root.rb +47 -0
  181. data/lib/empeira/providers/lifecycle.rb +96 -0
  182. data/lib/empeira/providers/registry.rb +21 -0
  183. data/lib/empeira/providers/resource.rb +32 -0
  184. data/lib/empeira/providers.rb +55 -0
  185. data/lib/empeira/runtime/agent_helper.rb +34 -0
  186. data/lib/empeira/runtime/container.rb +184 -0
  187. data/lib/empeira/runtime/diagnostic_execution.rb +52 -0
  188. data/lib/empeira/runtime/docker.rb +87 -0
  189. data/lib/empeira/runtime/image_metadata.rb +83 -0
  190. data/lib/empeira/runtime/interface.rb +8 -0
  191. data/lib/empeira/runtime/mount_probe.rb +57 -0
  192. data/lib/empeira/runtime/podman.rb +122 -0
  193. data/lib/empeira/runtime/recipe_build.rb +103 -0
  194. data/lib/empeira/runtime/registry_diagnostics.rb +23 -0
  195. data/lib/empeira/runtime/service_arguments.rb +55 -0
  196. data/lib/empeira/runtime/service_images.rb +118 -0
  197. data/lib/empeira/runtime/service_inspection.rb +159 -0
  198. data/lib/empeira/runtime/services.rb +120 -0
  199. data/lib/empeira/runtime/update_helper.rb +97 -0
  200. data/lib/empeira/runtime/workspace_network.rb +69 -0
  201. data/lib/empeira/server/environment_configuration.rb +53 -0
  202. data/lib/empeira/server/http.rb +38 -0
  203. data/lib/empeira/server/puppetdb_runtime.rb +74 -0
  204. data/lib/empeira/server/relay_certificate.rb +76 -0
  205. data/lib/empeira/server/runtime.rb +90 -0
  206. data/lib/empeira/services/definition.rb +42 -0
  207. data/lib/empeira/updates/images.rb +104 -0
  208. data/lib/empeira/updates/modules.rb +45 -0
  209. data/lib/empeira/updates/service.rb +45 -0
  210. data/lib/empeira/version_requirement.rb +23 -0
  211. data/lib/empeira/vm/agent.rb +66 -0
  212. data/lib/empeira/vm/bootstrap_requirements.rb +69 -0
  213. data/lib/empeira/vm/certificate_runtime.rb +30 -0
  214. data/lib/empeira/vm/cloud_init.rb +171 -0
  215. data/lib/empeira/vm/disk.rb +136 -0
  216. data/lib/empeira/vm/guest.rb +123 -0
  217. data/lib/empeira/vm/guest_channel.rb +110 -0
  218. data/lib/empeira/vm/guest_network.rb +94 -0
  219. data/lib/empeira/vm/guest_output.rb +52 -0
  220. data/lib/empeira/vm/guest_shell.rb +79 -0
  221. data/lib/empeira/vm/guest_upload.rb +43 -0
  222. data/lib/empeira/vm/image_cache.rb +77 -0
  223. data/lib/empeira/vm/image_source.rb +91 -0
  224. data/lib/empeira/vm/interface.rb +8 -0
  225. data/lib/empeira/vm/interface_ownership.rb +124 -0
  226. data/lib/empeira/vm/interface_routes.rb +101 -0
  227. data/lib/empeira/vm/interfaces.rb +240 -0
  228. data/lib/empeira/vm/management.rb +73 -0
  229. data/lib/empeira/vm/prerequisites.rb +42 -0
  230. data/lib/empeira/vm/qemu.rb +83 -0
  231. data/lib/empeira/vm/qemu_runtime.rb +404 -0
  232. data/lib/empeira/vm/removal.rb +30 -0
  233. data/lib/empeira/vm/root_disk.rb +92 -0
  234. data/lib/empeira/vm/session_lock.rb +56 -0
  235. data/lib/empeira/vm/system_ssh.rb +29 -0
  236. data/lib/empeira/workspace.rb +42 -0
  237. data/lib/empeira.rb +209 -0
  238. data/resources/browser/relay.rb +22 -0
  239. data/resources/gateway/Containerfile +8 -0
  240. data/resources/gateway/bridge.rb +66 -0
  241. data/resources/gateway/configure.rb +323 -0
  242. data/resources/modules/Containerfile +15 -0
  243. data/resources/modules/Gemfile +5 -0
  244. data/resources/modules/Gemfile.lock +105 -0
  245. data/resources/modules/worker.rb +161 -0
  246. data/resources/network/adapter/Containerfile +13 -0
  247. data/resources/network/adapter/bridge_linux.go +51 -0
  248. data/resources/network/adapter/framing.go +45 -0
  249. data/resources/network/adapter/framing_test.go +53 -0
  250. data/resources/network/adapter/go.mod +3 -0
  251. data/resources/network/adapter/main_linux.go +273 -0
  252. data/resources/network/channel.rb +18 -0
  253. data/resources/network/ssh_connect.rb +32 -0
  254. data/resources/nodes/apt_sources.rb +66 -0
  255. data/resources/nodes/debian/Containerfile +10 -0
  256. data/resources/nodes/diagnostic_exec.rb +109 -0
  257. data/resources/nodes/interactive_tools.rb +139 -0
  258. data/resources/nodes/managed_file.rb +91 -0
  259. data/resources/nodes/management/guest_agent.py +415 -0
  260. data/resources/nodes/management/setup.sh +20 -0
  261. data/resources/nodes/management/shell_rc.sh +12 -0
  262. data/resources/nodes/management/system_account.sh +24 -0
  263. data/resources/nodes/management/system_home.sh +15 -0
  264. data/resources/nodes/rpm/Containerfile +10 -0
  265. data/resources/nodes/runtime_proxy.rb +244 -0
  266. data/resources/nodes/ssh/proxy.rb +16 -0
  267. data/resources/nodes/ssh/sshd_config +15 -0
  268. data/resources/nodes/ssh/start +6 -0
  269. data/resources/proxy/Containerfile +10 -0
  270. data/resources/relay/Containerfile +5 -0
  271. data/resources/relay/puppetdb.rb +81 -0
  272. data/resources/relay/server_environment.rb +10 -0
  273. data/resources/relay/server_start.sh +41 -0
  274. metadata +326 -0
data/lib/empeira.rb ADDED
@@ -0,0 +1,209 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'empeira/errors'
4
+ require_relative 'empeira/progress'
5
+ require_relative 'empeira/build_info'
6
+ require_relative 'empeira/immutable'
7
+ require_relative 'empeira/hostname_pattern'
8
+ require_relative 'empeira/project_root'
9
+ require_relative 'empeira/providers/registry'
10
+ require_relative 'empeira/providers/resource'
11
+ require_relative 'empeira/providers/lifecycle'
12
+ require_relative 'empeira/runtime/interface'
13
+ require_relative 'empeira/vm/interface'
14
+ require_relative 'empeira/platform/kvm'
15
+ require_relative 'empeira/vm/prerequisites'
16
+ require_relative 'empeira/vm/qemu'
17
+ require_relative 'empeira/node/interface'
18
+ require_relative 'empeira/node/diagnostic_command'
19
+ require_relative 'empeira/images/source'
20
+ require_relative 'empeira/runtime/container'
21
+ require_relative 'empeira/runtime/service_inspection'
22
+ require_relative 'empeira/runtime/service_arguments'
23
+ require_relative 'empeira/runtime/service_images'
24
+ require_relative 'empeira/runtime/image_metadata'
25
+ require_relative 'empeira/runtime/registry_diagnostics'
26
+ require_relative 'empeira/runtime/recipe_build'
27
+ require_relative 'empeira/runtime/services'
28
+ require_relative 'empeira/runtime/diagnostic_execution'
29
+ require_relative 'empeira/runtime/workspace_network'
30
+ require_relative 'empeira/runtime/update_helper'
31
+ require_relative 'empeira/runtime/agent_helper'
32
+ require_relative 'empeira/runtime/mount_probe'
33
+ require_relative 'empeira/runtime/docker'
34
+ require_relative 'empeira/runtime/podman'
35
+ require_relative 'empeira/providers'
36
+ require_relative 'empeira/network/policy'
37
+ require_relative 'empeira/network/resource'
38
+ require_relative 'empeira/network/definition'
39
+ require_relative 'empeira/network/egress'
40
+ require_relative 'empeira/network/peer/layout'
41
+ require_relative 'empeira/network/direct_egress'
42
+ require_relative 'empeira/network/gateway'
43
+ require_relative 'empeira/network/redirects'
44
+ require_relative 'empeira/images/configuration'
45
+ require_relative 'empeira/images/reference'
46
+ require_relative 'empeira/images/recipe'
47
+ require_relative 'empeira/server/runtime'
48
+ require_relative 'empeira/server/environment_configuration'
49
+ require_relative 'empeira/server/puppetdb_runtime'
50
+ require_relative 'empeira/server/http'
51
+ require_relative 'empeira/server/relay_certificate'
52
+ require_relative 'empeira/node/external_fact'
53
+ require_relative 'empeira/node/bootstrap'
54
+ require_relative 'empeira/node/package_proxy'
55
+ require_relative 'empeira/node/package_bootstrap'
56
+ require_relative 'empeira/network/naming'
57
+ require_relative 'empeira/network/proxy_policy'
58
+ require_relative 'empeira/network/host_policy'
59
+ require_relative 'empeira/network/proxy_bindings'
60
+ require_relative 'empeira/network/bootstrap_proxy'
61
+ require_relative 'empeira/services/definition'
62
+ require_relative 'empeira/configuration/document'
63
+ require_relative 'empeira/configuration/merge'
64
+ require_relative 'empeira/configuration/image_schema'
65
+ require_relative 'empeira/configuration/node_images'
66
+ require_relative 'empeira/configuration/agent_values'
67
+ require_relative 'empeira/configuration/agent_schema'
68
+ require_relative 'empeira/configuration/hiera_mount_schema'
69
+ require_relative 'empeira/configuration/hiera_modulepath'
70
+ require_relative 'empeira/configuration/hiera_mounts'
71
+ require_relative 'empeira/configuration/server_mounts'
72
+ require_relative 'empeira/configuration/eyaml'
73
+ require_relative 'empeira/configuration/bootstrap_guests'
74
+ require_relative 'empeira/configuration/additional_services'
75
+ require_relative 'empeira/configuration/service_configuration'
76
+ require_relative 'empeira/configuration/command_mocks'
77
+ require_relative 'empeira/configuration/dns_rewrites'
78
+ require_relative 'empeira/configuration/network_redirects'
79
+ require_relative 'empeira/configuration/vm_interfaces'
80
+ require_relative 'empeira/configuration/addons'
81
+ require_relative 'empeira/configuration/schema'
82
+ require_relative 'empeira/configuration/ssh_preferences'
83
+ require_relative 'empeira/configuration/user'
84
+ require_relative 'empeira/configuration/loader'
85
+ require_relative 'empeira/configuration/display'
86
+ require_relative 'empeira/platform/facts'
87
+ require_relative 'empeira/platform/resolvers'
88
+ require_relative 'empeira/platform/locations'
89
+ require_relative 'empeira/workspace'
90
+ require_relative 'empeira/images/identity'
91
+ require_relative 'empeira/vm/image_source'
92
+ require_relative 'empeira/vm/image_cache'
93
+ require_relative 'empeira/vm/disk'
94
+ require_relative 'empeira/vm/root_disk'
95
+ require_relative 'empeira/vm/qemu_runtime'
96
+ require_relative 'empeira/vm/session_lock'
97
+ require_relative 'empeira/vm/removal'
98
+ require_relative 'empeira/node/ssh_credentials'
99
+ require_relative 'empeira/node/ssh_client'
100
+ require_relative 'empeira/node/user_ssh'
101
+ require_relative 'empeira/node/ssh_identity'
102
+ require_relative 'empeira/node/ssh_endpoint'
103
+ require_relative 'empeira/vm/cloud_init'
104
+ require_relative 'empeira/vm/guest_channel'
105
+ require_relative 'empeira/vm/guest_output'
106
+ require_relative 'empeira/vm/guest_shell'
107
+ require_relative 'empeira/vm/guest_upload'
108
+ require_relative 'empeira/vm/guest'
109
+ require_relative 'empeira/vm/guest_network'
110
+ require_relative 'empeira/vm/interface_ownership'
111
+ require_relative 'empeira/vm/interface_routes'
112
+ require_relative 'empeira/vm/interfaces'
113
+ require_relative 'empeira/vm/certificate_runtime'
114
+ require_relative 'empeira/execution/result'
115
+ require_relative 'empeira/execution/console'
116
+ require_relative 'empeira/execution/runner'
117
+ require_relative 'empeira/execution/terminal'
118
+ require_relative 'empeira/node/run_request'
119
+ require_relative 'empeira/node/image'
120
+ require_relative 'empeira/node/definition'
121
+ require_relative 'empeira/node/certificates'
122
+ require_relative 'empeira/node/puppet_command'
123
+ require_relative 'empeira/node/apt_configuration'
124
+ require_relative 'empeira/agent/target'
125
+ require_relative 'empeira/agent/versions'
126
+ require_relative 'empeira/agent/authentication'
127
+ require_relative 'empeira/agent/download'
128
+ require_relative 'empeira/agent/artifact'
129
+ require_relative 'empeira/agent/cache_storage'
130
+ require_relative 'empeira/agent/cache'
131
+ require_relative 'empeira/agent/acquisition'
132
+ require_relative 'empeira/agent/release_sources'
133
+ require_relative 'empeira/agent/native_commands'
134
+ require_relative 'empeira/node/agent_repository'
135
+ require_relative 'empeira/node/dnf_agent_repository'
136
+ require_relative 'empeira/node/agent_package'
137
+ require_relative 'empeira/node/agent_installer'
138
+ require_relative 'empeira/node/rpm_configuration'
139
+ require_relative 'empeira/node/package_sources'
140
+ require_relative 'empeira/node/runtime_proxy'
141
+ require_relative 'empeira/node/interactive_tools'
142
+ require_relative 'empeira/node/command_mock'
143
+ require_relative 'empeira/node/command_mocks'
144
+ require_relative 'empeira/node/container_bootstrap'
145
+ require_relative 'empeira/node/vm_bootstrap'
146
+ require_relative 'empeira/vm/bootstrap_requirements'
147
+ require_relative 'empeira/vm/agent'
148
+ require_relative 'empeira/node/inventory'
149
+ require_relative 'empeira/node/container_lifecycle'
150
+ require_relative 'empeira/node/container_preparation'
151
+ require_relative 'empeira/node/container_agent_bootstrap'
152
+ require_relative 'empeira/node/container_state'
153
+ require_relative 'empeira/node/container_guest'
154
+ require_relative 'empeira/node/container'
155
+ require_relative 'empeira/node/vm_state'
156
+ require_relative 'empeira/vm/management'
157
+ require_relative 'empeira/vm/system_ssh'
158
+ require_relative 'empeira/node/vm_preflight'
159
+ require_relative 'empeira/node/vm_preparation'
160
+ require_relative 'empeira/node/vm_lifecycle'
161
+ require_relative 'empeira/node/vm'
162
+ require_relative 'empeira/node/service'
163
+ require_relative 'empeira/control_plane/files'
164
+ require_relative 'empeira/control_plane/additional_configurations'
165
+ require_relative 'empeira/modules/image'
166
+ require_relative 'empeira/execution/diagnostics'
167
+ require_relative 'empeira/modules/state'
168
+ require_relative 'empeira/modules/events'
169
+ require_relative 'empeira/modules/git_safety'
170
+ require_relative 'empeira/modules/storage'
171
+ require_relative 'empeira/modules/request'
172
+ require_relative 'empeira/modules/installer'
173
+ require_relative 'empeira/modules/host_git'
174
+ require_relative 'empeira/addons/error'
175
+ require_relative 'empeira/addons/manifest'
176
+ require_relative 'empeira/addons/storage'
177
+ require_relative 'empeira/addons/installation'
178
+ require_relative 'empeira/addons/environment'
179
+ require_relative 'empeira/addons/service'
180
+ require_relative 'empeira/control_plane/environment'
181
+ require_relative 'empeira/control_plane/browser'
182
+ require_relative 'empeira/control_plane/plan'
183
+ require_relative 'empeira/infrastructure/definition'
184
+ require_relative 'empeira/infrastructure/store'
185
+ require_relative 'empeira/infrastructure/recovery_inventory'
186
+ require_relative 'empeira/infrastructure/owned_files'
187
+ require_relative 'empeira/infrastructure/cleanup'
188
+ require_relative 'empeira/infrastructure/status'
189
+ require_relative 'empeira/control_plane/health'
190
+ require_relative 'empeira/control_plane/storage'
191
+ require_relative 'empeira/control_plane/discovery'
192
+ require_relative 'empeira/control_plane/controller'
193
+ require_relative 'empeira/infrastructure/service'
194
+ require_relative 'empeira/updates/images'
195
+ require_relative 'empeira/updates/modules'
196
+ require_relative 'empeira/updates/service'
197
+ require_relative 'empeira/application'
198
+
199
+ require_relative 'empeira/platform/routes'
200
+ require_relative 'empeira/network/peer/allocation'
201
+ require_relative 'empeira/network/peer/backend'
202
+ require_relative 'empeira/network/peer/channel'
203
+ require_relative 'empeira/network/peer/channel_server'
204
+ require_relative 'empeira/network/peer/adapter_image'
205
+ require_relative 'empeira/network/peer/helper_security'
206
+ require_relative 'empeira/network/peer/docker_adapter'
207
+ require_relative 'empeira/network/peer/linux_podman'
208
+ require_relative 'empeira/network/peer/podman_machine'
209
+ require_relative 'empeira/network/peer/attachment'
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'socket'
4
+
5
+ # Fixed destination, opaque TLS bytes: no HTTP proxy, CONNECT or routing interface.
6
+ listener = TCPServer.new('0.0.0.0', 3001)
7
+ loop do
8
+ client = listener.accept
9
+ Thread.new(client) do |connection|
10
+ address = File.read('/empeira-browser/address').strip
11
+ Socket.tcp(address, 3001, connect_timeout: 5) do |upstream|
12
+ sender = Thread.new { IO.copy_stream(connection, upstream) }
13
+ IO.copy_stream(upstream, connection)
14
+ ensure
15
+ sender&.kill&.join
16
+ end
17
+ rescue IOError, SystemCallError, SocketError
18
+ nil
19
+ ensure
20
+ connection.close
21
+ end
22
+ end
@@ -0,0 +1,8 @@
1
+ ARG EMPEIRA_BASE_RUBY=library/ruby:3.4.11-alpine3.23
2
+ FROM ${EMPEIRA_BASE_RUBY}
3
+ RUN apk upgrade --no-cache && apk add --no-cache iproute2 iptables iptables-legacy conntrack-tools
4
+ COPY configure.rb /usr/local/libexec/empeira-gateway
5
+ COPY bridge.rb /usr/local/libexec/empeira-bridge
6
+ RUN chmod 0755 /usr/local/libexec/empeira-gateway /usr/local/libexec/empeira-bridge
7
+ ENTRYPOINT ["/usr/local/libexec/empeira-gateway"]
8
+ CMD ["hold"]
@@ -0,0 +1,66 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ load File.join(__dir__, 'empeira-gateway') unless defined?(EmpeiraGateway)
5
+
6
+ # Docker's internal bridge filtering otherwise discards off-subnet IP packets
7
+ # even when their Ethernet destination is another peer (the workspace gateway).
8
+ # This helper changes only two owned bridge rules, never engine chains/policies.
9
+ module EmpeiraBridge
10
+ module_function
11
+
12
+ def backend
13
+ candidates = %w[iptables iptables-legacy].select do |binary|
14
+ _output, _error, status = Open3.capture3(binary, '-t', 'filter', '-S', 'DOCKER-USER')
15
+ status.success?
16
+ rescue Errno::ENOENT
17
+ false
18
+ end
19
+ raise 'Cannot identify Docker iptables backend; workspace remains isolated' if candidates.empty?
20
+
21
+ candidates.last
22
+ end
23
+
24
+ def rules(bridge, subnet, identity)
25
+ comment = ['-m', 'comment', '--comment', "empeira:#{identity}", '-j', 'ACCEPT']
26
+ [['raw', 'PREROUTING', ['-i', bridge, '-s', subnet, *comment]],
27
+ ['filter', 'FORWARD', ['-i', bridge, '-o', bridge, *comment]]]
28
+ end
29
+
30
+ def validate(action, bridge, subnet, identity)
31
+ raise 'Invalid workspace bridge operation' unless %w[apply remove check].include?(action)
32
+ unless bridge.match?(/\Aep[a-f0-9]{10}\z/) && identity.match?(/\A[a-f0-9]{64}\z/)
33
+ raise 'Invalid workspace bridge identity'
34
+ end
35
+ raise 'Invalid workspace subnet' unless IPAddr.new(subnet).ipv4? && IPAddr.new(subnet).prefix == 24
36
+ raise 'Workspace bridge is unavailable' unless File.directory?("/sys/class/net/#{bridge}/bridge")
37
+ end
38
+
39
+ def run(action, bridge, subnet, identity)
40
+ validate(action, bridge, subnet, identity)
41
+ binary = backend
42
+ rules(bridge, subnet, identity).each do |table, chain, arguments|
43
+ reconcile(binary, action, table, chain, arguments)
44
+ end
45
+ end
46
+
47
+ def reconcile(binary, action, table, chain, arguments)
48
+ _output, _error, status = Open3.capture3(binary, '--wait', '5', '-t', table, '-C', chain, *arguments)
49
+ raise 'Workspace bridge attachment rules missing; run empeira up' if action == 'check' && !status.success?
50
+
51
+ if action == 'apply' && !status.success?
52
+ EmpeiraGateway.command(binary, '--wait', '5', '-t', table, '-I', chain, '1', *arguments)
53
+ elsif action == 'remove' && status.success?
54
+ EmpeiraGateway.command(binary, '--wait', '5', '-t', table, '-D', chain, *arguments)
55
+ end
56
+ end
57
+ end
58
+
59
+ if $PROGRAM_NAME == __FILE__
60
+ begin
61
+ EmpeiraBridge.run(*ARGV)
62
+ rescue StandardError => e
63
+ warn e.message
64
+ exit 1
65
+ end
66
+ end
@@ -0,0 +1,323 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'ipaddr'
5
+ require 'json'
6
+ require 'open3'
7
+ require 'socket'
8
+
9
+ # Runs only inside an owned gateway or an ephemeral helper sharing one owned
10
+ # container's network namespace. Never modifies host firewall tables or routes.
11
+ # rubocop:disable-next Metrics/ModuleLength -- Self-contained, reviewed helper shipped in the gateway image.
12
+ module EmpeiraGateway
13
+ module_function
14
+
15
+ def command(*arguments, input: nil)
16
+ output, error, status = Open3.capture3(*arguments, **(input ? { stdin_data: input } : {}))
17
+ raise "Gateway operation failed: #{arguments.first}: #{error.lines.last.to_s.strip}" unless status.success?
18
+
19
+ output
20
+ end
21
+
22
+ def lockdown
23
+ "*filter\n:INPUT DROP [0:0]\n:FORWARD DROP [0:0]\n:OUTPUT DROP [0:0]\n:BOOTSTRAP - [0:0]\nCOMMIT\n"
24
+ end
25
+
26
+ def restore(content)
27
+ command('iptables-restore', '--wait', '5', input: content)
28
+ end
29
+
30
+ # rubocop:disable-next Metrics/AbcSize -- Compose only explicit DNS, proxy, egress and redirect grants.
31
+ def rules(plan, internal:, external:)
32
+ filter = [':INPUT DROP [0:0]', ':FORWARD DROP [0:0]', ':OUTPUT DROP [0:0]', ':BOOTSTRAP - [0:0]',
33
+ "-A FORWARD -i #{internal} -o #{external} -j BOOTSTRAP"]
34
+ unless plan.fetch('redirects', []).empty?
35
+ filter << "-A FORWARD -i #{internal} -o #{internal} -m conntrack --ctstate DNAT -j BOOTSTRAP"
36
+ end
37
+ plan.fetch('blocked', []).sort.each { |ip| filter << "-A BOOTSTRAP -s #{ip}/32 -j DROP" }
38
+ filter.concat(redirect_filter(plan, internal))
39
+ filter.concat(dns_rules(plan, internal, external))
40
+ filter.concat(proxy_rules(plan, internal, external))
41
+ filter.concat(direct_rules(plan, internal, external))
42
+ nat = [':PREROUTING ACCEPT [0:0]', ':INPUT ACCEPT [0:0]', ':OUTPUT ACCEPT [0:0]',
43
+ ':POSTROUTING ACCEPT [0:0]',
44
+ "-A POSTROUTING -s #{plan.fetch('subnet')} -o #{external} -j MASQUERADE"]
45
+ nat.concat(redirect_nat(plan, internal))
46
+ "*filter\n#{filter.join("\n")}\nCOMMIT\n*nat\n#{nat.join("\n")}\nCOMMIT\n"
47
+ end
48
+
49
+ def redirect_selector(entry)
50
+ source = entry.fetch('from')
51
+ "-m conntrack --ctorigdst #{source.fetch('ip')} --ctorigdstport #{source.fetch('port')} " \
52
+ "--ctproto #{source.fetch('protocol', 'tcp')}"
53
+ end
54
+
55
+ # rubocop:disable-next Metrics/MethodLength -- Keep both conntrack directions and the final original-tuple drop together.
56
+ def redirect_filter(plan, internal)
57
+ plan.fetch('redirects', []).flat_map do |entry|
58
+ target = entry['to']
59
+ protocol = entry.fetch('from').fetch('protocol', 'tcp')
60
+ selector = redirect_selector(entry)
61
+ allowed = if target
62
+ ["-A FORWARD -i #{internal} -o #{internal} -s #{plan.fetch('subnet')} " \
63
+ "-d #{target.fetch('ip')}/32 -p #{protocol} --dport #{target.fetch('port')} #{selector} " \
64
+ '--ctstate DNAT --ctdir ORIGINAL -j ACCEPT',
65
+ "-A FORWARD -i #{internal} -o #{internal} -s #{target.fetch('ip')}/32 " \
66
+ "-d #{plan.fetch('subnet')} -p #{protocol} --sport #{target.fetch('port')} #{selector} " \
67
+ '--ctstate ESTABLISHED --ctdir REPLY -j ACCEPT']
68
+ else
69
+ []
70
+ end
71
+ [*allowed, "-A FORWARD -p #{protocol} #{selector} -j DROP"]
72
+ end
73
+ end
74
+
75
+ def redirect_nat(plan, internal)
76
+ plan.fetch('redirects', []).filter_map do |entry|
77
+ target = entry['to']
78
+ next unless target
79
+
80
+ source = entry.fetch('from')
81
+ protocol = source.fetch('protocol', 'tcp')
82
+ ["-A PREROUTING -i #{internal} -s #{plan.fetch('subnet')} -d #{source.fetch('ip')}/32 " \
83
+ "-p #{protocol} --dport #{source.fetch('port')} -j DNAT " \
84
+ "--to-destination #{target.fetch('ip')}:#{target.fetch('port')}",
85
+ "-A POSTROUTING -o #{internal} -s #{plan.fetch('subnet')} -d #{target.fetch('ip')}/32 " \
86
+ "-p #{protocol} --dport #{target.fetch('port')} #{redirect_selector(entry)} --ctstate DNAT --ctdir ORIGINAL " \
87
+ "-j SNAT --to-source #{plan.fetch('gateway')}"]
88
+ end.flatten
89
+ end
90
+
91
+ def dns_rules(plan, internal, external)
92
+ filter = []
93
+ plan.fetch('resolvers').each do |ip|
94
+ %w[tcp udp].each do |protocol|
95
+ filter << "-A FORWARD -i #{internal} -o #{external} -s #{plan.fetch('dns')}/32 " \
96
+ "-d #{ip}/32 -p #{protocol} --dport 53 -j ACCEPT"
97
+ filter << reply(internal, external, source: plan.fetch('dns'), destination: ip, port: 53, protocol: protocol)
98
+ end
99
+ end
100
+ filter
101
+ end
102
+
103
+ def proxy_rules(plan, internal, external)
104
+ filter = []
105
+ plan.fetch('proxies').each do |ip|
106
+ filter << "-A FORWARD -i #{internal} -o #{external} -s #{ip}/32 -p tcp " \
107
+ '-m multiport --dports 80,443 -j ACCEPT'
108
+ [80, 443].each { |port| filter << reply(internal, external, source: ip, port: port) }
109
+ end
110
+ filter
111
+ end
112
+
113
+ def direct_rules(plan, internal, external)
114
+ filter = []
115
+ plan.fetch('entries').each do |entry|
116
+ entry.fetch('addresses').each do |ip|
117
+ entry.fetch('ports').each do |port|
118
+ filter << "-A FORWARD -i #{internal} -o #{external} -s #{plan.fetch('subnet')} " \
119
+ "-d #{ip}/32 -p tcp --dport #{port} -j ACCEPT"
120
+ filter << reply(internal, external, destination: ip, port: port)
121
+ end
122
+ end
123
+ end
124
+ filter
125
+ end
126
+
127
+ def reply(internal, external, port:, source: nil, destination: nil, protocol: 'tcp')
128
+ selector = "--ctproto #{protocol} --ctorigdstport #{port}"
129
+ selector += " --ctorigsrc #{source}" if source
130
+ selector += " --ctorigdst #{destination}" if destination
131
+ "-A FORWARD -i #{external} -o #{internal} -m conntrack --ctstate ESTABLISHED,RELATED #{selector} -j ACCEPT"
132
+ end
133
+
134
+ def ipv4(value)
135
+ ip = IPAddr.new(value)
136
+ raise 'Gateway requires explicit IPv4 addresses' unless ip.ipv4? && ip.to_s == value
137
+
138
+ value
139
+ end
140
+
141
+ def validate(plan)
142
+ raise 'Invalid gateway plan version' unless plan.fetch('version') == 1
143
+
144
+ subnet = IPAddr.new(plan.fetch('subnet'))
145
+ raise 'Invalid workspace subnet' unless subnet.ipv4? && subnet.private? && subnet.prefix == 24
146
+
147
+ validate_addresses(plan, subnet)
148
+ validate_entries(plan.fetch('entries'))
149
+ validate_redirects(plan.fetch('redirects', []), subnet)
150
+ end
151
+
152
+ def validate_redirects(entries, subnet)
153
+ sources = entries.map { |entry| validate_redirect(entry, subnet) }
154
+ raise 'Duplicate gateway redirect sources' unless sources.uniq == sources
155
+ end
156
+
157
+ def validate_redirect(entry, subnet)
158
+ raise 'Invalid gateway redirect' unless entry.is_a?(Hash) && entry.keys.sort == %w[from to]
159
+
160
+ source = entry.fetch('from')
161
+ validate_endpoint(source, source: true)
162
+ raise 'Redirect source collides with workspace subnet' if subnet.include?(source.fetch('ip'))
163
+
164
+ target = entry['to']
165
+ validate_redirect_target(target, source, subnet)
166
+ source.values_at('ip', 'port') + [source.fetch('protocol', 'tcp')]
167
+ end
168
+
169
+ def validate_redirect_target(target, source, subnet)
170
+ if target
171
+ validate_endpoint(target)
172
+ raise 'Redirect target is outside workspace subnet' unless subnet.include?(target.fetch('ip'))
173
+ end
174
+ if source.fetch('protocol', 'tcp') == 'udp' &&
175
+ (source.fetch('port') != 53 || (target && target.fetch('port') != 53))
176
+ raise 'UDP redirects require DNS port 53'
177
+ end
178
+ end
179
+
180
+ def validate_endpoint(value, source: false)
181
+ raise 'Invalid redirect endpoint' unless value.is_a?(Hash)
182
+
183
+ keys = source ? value.keys - ['protocol'] : value.keys
184
+ raise 'Invalid redirect endpoint' unless keys.sort == %w[ip port]
185
+ raise 'Invalid redirect protocol' if source && !%w[tcp udp].include?(value.fetch('protocol', 'tcp'))
186
+
187
+ ipv4(value.fetch('ip'))
188
+ validate_redirect_port(value.fetch('port'))
189
+ end
190
+
191
+ def validate_redirect_port(port)
192
+ raise 'Invalid redirect port' unless port.is_a?(Integer) && port.between?(1, 65_535)
193
+ end
194
+
195
+ def validate_addresses(plan, subnet)
196
+ %w[gateway dns].each { |key| raise 'Invalid gateway address' unless subnet.include?(ipv4(plan.fetch(key))) }
197
+ %w[resolvers proxies blocked].each { |key| plan.fetch(key, []).each { |ip| ipv4(ip) } }
198
+ end
199
+
200
+ def validate_entries(entries)
201
+ entries.each do |entry|
202
+ entry.fetch('addresses').each { |ip| ipv4(ip) }
203
+ valid = entry.fetch('ports').all? { |port| port.is_a?(Integer) && port.between?(1, 65_535) }
204
+ raise 'Invalid gateway ports' unless valid
205
+ end
206
+ end
207
+
208
+ def ipv4_interfaces
209
+ Socket.getifaddrs.select { |entry| entry.addr&.ipv4? && !entry.addr.ipv4_loopback? }
210
+ end
211
+
212
+ def interfaces(plan)
213
+ addresses = ipv4_interfaces
214
+ internal = addresses.find { |entry| entry.addr.ip_address == plan.fetch('gateway') }&.name
215
+ external = addresses.map(&:name).uniq.reject { |name| name == internal }
216
+ raise 'Gateway NAT/routing interfaces are unavailable' unless internal && external.size == 1
217
+
218
+ [internal, external.first]
219
+ end
220
+
221
+ def apply(path)
222
+ restore(lockdown)
223
+ plan = JSON.parse(File.binread(path))
224
+ validate(plan)
225
+ internal, external = interfaces(plan)
226
+ expected = rules(plan, internal: internal, external: external)
227
+ nat = expected.split('*nat', 2).last
228
+ actual = command('iptables-save')
229
+ if nat_configuration(actual) != nat_configuration(expected)
230
+ # Keep forwarding blocked until new NAT and namespace-local connection state agree.
231
+ restore("*nat#{nat}")
232
+ command('conntrack', '--flush')
233
+ end
234
+ restore(expected)
235
+ end
236
+
237
+ def nat_configuration(content)
238
+ normalized(content).select { |(table, _chain), _value| table == '*nat' }
239
+ end
240
+
241
+ def route(gateway)
242
+ ipv4(gateway)
243
+ interface = ipv4_interfaces.find do |entry|
244
+ IPAddr.new("#{entry.addr.ip_address}/24").include?(gateway)
245
+ end&.name
246
+ raise 'Workspace gateway is not on the attached subnet' unless interface
247
+
248
+ command('ip', '-4', 'route', 'flush', 'default')
249
+ command('ip', '-4', 'route', 'replace', 'default', 'via', gateway, 'dev', interface)
250
+ end
251
+
252
+ def phase(action, ip)
253
+ ipv4(ip)
254
+ arguments = ['BOOTSTRAP', '-s', "#{ip}/32", '-j', 'DROP']
255
+ _output, _error, exists = Open3.capture3('iptables', '--wait', '5', '-C', *arguments)
256
+ command('iptables', '--wait', '5', action == 'block' ? '-A' : '-D', *arguments) if
257
+ (action == 'block') != exists.success?
258
+ end
259
+
260
+ # rubocop:disable-next Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
261
+ def normalized(content)
262
+ table = nil
263
+ content.lines.each_with_object({}) do |line, result|
264
+ line = line.strip.gsub(/\[\d+:\d+\]/, '[0:0]')
265
+ next if line.empty? || line.start_with?('#') || line == 'COMMIT'
266
+
267
+ if line.start_with?('*')
268
+ table = line
269
+ elsif line.start_with?(':')
270
+ result[[table, line.split.first]] = line
271
+ else
272
+ (result[[table, line.split[1]]] ||= []) << normalized_rule(line)
273
+ end
274
+ end
275
+ end
276
+
277
+ def normalized_rule(line)
278
+ line.split.each_slice(2).filter_map do |flag, value|
279
+ next if flag == '-m' && %w[tcp udp].include?(value)
280
+
281
+ value = value.split(',').sort.join(',') if flag == '--ctstate'
282
+ value = { 'tcp' => '6', 'udp' => '17' }.fetch(value, value) if flag == '--ctproto'
283
+ [flag, value]
284
+ end.sort
285
+ end
286
+
287
+ def check(path)
288
+ plan = JSON.parse(File.binread(path))
289
+ validate(plan)
290
+ internal, external = interfaces(plan)
291
+ expected = rules(plan, internal: internal, external: external)
292
+ actual = command('iptables-save')
293
+ raise 'Gateway firewall drift; run empeira up' unless normalized(actual) == normalized(expected)
294
+ raise 'Gateway routing disabled; run empeira up' unless File.read('/proc/sys/net/ipv4/ip_forward').strip == '1'
295
+ end
296
+
297
+ # rubocop:disable-next Metrics/CyclomaticComplexity -- Explicit operation dispatch for the owned helper contract.
298
+ def run(arguments)
299
+ case arguments.first
300
+ when 'hold' then sleep
301
+ when 'redirects-capability'
302
+ command('conntrack', '--version')
303
+ puts 'tcp-udp-redirects-v2'
304
+ when 'lockdown'
305
+ restore(lockdown)
306
+ when 'apply' then apply(arguments.fetch(1))
307
+ when 'route' then route(arguments.fetch(1))
308
+ when 'block', 'unblock' then phase(*arguments)
309
+ when 'check'
310
+ check(arguments.fetch(1))
311
+ else raise 'Unknown gateway operation'
312
+ end
313
+ end
314
+ end
315
+
316
+ if $PROGRAM_NAME == __FILE__
317
+ begin
318
+ EmpeiraGateway.run(ARGV)
319
+ rescue StandardError => e
320
+ warn e.message
321
+ exit 1
322
+ end
323
+ end
@@ -0,0 +1,15 @@
1
+ # SPDX-License-Identifier: AGPL-3.0-only
2
+ ARG EMPEIRA_BASE_RUBY=library/ruby:3.4.11-alpine3.23
3
+ FROM ${EMPEIRA_BASE_RUBY}
4
+ RUN apk add --no-cache git ca-certificates
5
+ WORKDIR /opt/empeira-modules
6
+ COPY Gemfile Gemfile.lock ./
7
+ RUN apk add --no-cache --virtual .build-deps build-base libffi-dev \
8
+ && gem install bundler -v 4.0.22 --no-document \
9
+ && bundle config set deployment true && bundle install --jobs 2 \
10
+ && apk del .build-deps
11
+ COPY worker.rb ./
12
+ RUN mkdir /work && chmod 1777 /work
13
+ USER 65534:65534
14
+ ENTRYPOINT ["ruby"]
15
+ CMD ["-e", "sleep"]
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ gem 'r10k', '5.0.3'