Class: LittleGhost::Network::EnvoyGateway
- Defined in:
- lib/little_ghost/network/envoy_gateway.rb
Overview
Manages Envoy as a native process or pinned Docker sidecar for one Sandbox. CONNECT policy sees destinations, not encrypted request details. Optional HTTP inspection changes the child trust configuration and may not work for clients with certificate pinning or custom trust stores. The Sandbox must block direct sockets for either mode to be an enforcement boundary.
Constant Summary collapse
- ENVOY_IMAGE =
:nodoc:
"envoyproxy/envoy:v1.39.0@sha256:d59f7f5fa10cff6d5892b6c5e7df5c9297ddfb2c3683e33fbfb82da24de4fa66"- RUNTIMES =
:nodoc:
%i[auto native docker].freeze
- TRANSPORTS =
:nodoc:
%i[unix docker].freeze
Instance Attribute Summary collapse
-
#client_network ⇒ Object
readonly
Internal Docker network exposed only to sandbox clients, when used.
-
#proxy_socket ⇒ Object
readonly
Host path of the explicit proxy's Unix socket, when used.
-
#runtime ⇒ Object
readonly
Configured runtime selector:
:auto,:native, or:docker.
Attributes inherited from Gateway
Instance Method Summary collapse
-
#close ⇒ Object
Removes the process, containers, networks, sockets, and trust material.
-
#environment ⇒ Object
Returns proxy variables and, for inspection, child-scoped trust paths.
-
#initialize(policy:, runtime: :auto, transport: :unix, envoy: "envoy", docker: "docker", image: ENVOY_IMAGE, pull: :if_missing, dns: []) ⇒ EnvoyGateway
constructor
Builds a run-scoped Envoy gateway.
-
#mounts ⇒ Object
Returns the gateway files that must be mounted into the sandbox.
-
#open(run: nil) ⇒ Object
Creates configuration, trust material, and the Envoy process.
-
#proxy_mount_path ⇒ Object
Returns the proxy socket's stable path inside a mounted sandbox.
Methods inherited from Gateway
Constructor Details
#initialize(policy:, runtime: :auto, transport: :unix, envoy: "envoy", docker: "docker", image: ENVOY_IMAGE, pull: :if_missing, dns: []) ⇒ EnvoyGateway
Builds a run-scoped Envoy gateway. Envoy remains an optional external dependency and the Docker image is pinned by digest by default.
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
# File 'lib/little_ghost/network/envoy_gateway.rb', line 27 def initialize(policy:, runtime: :auto, transport: :unix, envoy: "envoy", docker: "docker", image: ENVOY_IMAGE, pull: :if_missing, dns: []) super(policy:) @runtime = runtime.to_sym @transport = transport.to_sym raise PolicyError, "Envoy runtime must be :auto, :native, or :docker" unless RUNTIMES.include?(@runtime) raise PolicyError, "Envoy transport must be :unix or :docker" unless TRANSPORTS.include?(@transport) @envoy = String(envoy) @docker = String(docker) @image = String(image) @pull = pull.to_sym raise PolicyError, "Envoy image must be a non-option image reference" if @image.empty? || @image.start_with?("-") raise PolicyError, "Envoy pull must be :if_missing, :never, or :always" unless %i[if_missing never always].include?(@pull) @dns = Array(dns).map do |address| address = String(address) IPAddr.new(address) address.freeze rescue IPAddr::InvalidAddressError raise PolicyError, "Envoy DNS resolvers must be IP addresses" end.freeze @gateway_id = SecureRandom.uuid @opened = false end |
Instance Attribute Details
#client_network ⇒ Object (readonly)
Internal Docker network exposed only to sandbox clients, when used.
56 57 58 |
# File 'lib/little_ghost/network/envoy_gateway.rb', line 56 def client_network @client_network end |
#proxy_socket ⇒ Object (readonly)
Host path of the explicit proxy's Unix socket, when used.
54 55 56 |
# File 'lib/little_ghost/network/envoy_gateway.rb', line 54 def proxy_socket @proxy_socket end |
#runtime ⇒ Object (readonly)
Configured runtime selector: :auto, :native, or :docker.
58 59 60 |
# File 'lib/little_ghost/network/envoy_gateway.rb', line 58 def runtime @runtime end |
Instance Method Details
#close ⇒ Object
Removes the process, containers, networks, sockets, and trust material.
92 93 94 95 96 97 98 99 100 |
# File 'lib/little_ghost/network/envoy_gateway.rb', line 92 def close stop_native stop_docker @authorizer_server&.close FileUtils.remove_entry_secure(@root) if @root && File.exist?(@root) @root = nil @opened = false nil end |
#environment ⇒ Object
Returns proxy variables and, for inspection, child-scoped trust paths.
103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 |
# File 'lib/little_ghost/network/envoy_gateway.rb', line 103 def environment endpoint = if @transport == :docker "http://#{@container_name}:3128" else "http://127.0.0.1:3128" end values = { "HTTP_PROXY" => endpoint, "HTTPS_PROXY" => endpoint, "http_proxy" => endpoint, "https_proxy" => endpoint, "NO_PROXY" => "localhost,127.0.0.1", "no_proxy" => "localhost,127.0.0.1" } values.merge!(trust_environment) if @trust_paths values.freeze end |
#mounts ⇒ Object
Returns the gateway files that must be mounted into the sandbox.
122 123 124 125 126 |
# File 'lib/little_ghost/network/envoy_gateway.rb', line 122 def mounts return [] unless @transport == :unix || @trust_paths [{source: @client_root, target: "/run/little-ghost-egress", access: :read_only}].freeze end |
#open(run: nil) ⇒ Object
Creates configuration, trust material, and the Envoy process.
61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 |
# File 'lib/little_ghost/network/envoy_gateway.rb', line 61 def open(run: nil) return self if @opened @root = Dir.mktmpdir("little-ghost-egress-", "/tmp") File.chmod(0o700, @root) @client_root = File.join(@root, "client") Dir.mkdir(@client_root, 0o700) @proxy_socket = File.join(@client_root, "proxy.sock") if @transport == :unix @interceptor_socket = File.join(@root, "interceptor.sock") @authorizer_socket = File.join(@root, "authorizer.sock") @access_log = File.join(@root, "access.log") @envoy_log = File.join(@root, "envoy.log") File.write(@access_log, "") File.chmod(0o600, @access_log) @resolved_runtime = resolve_runtime validate_runtime! prepare_http_inspection(run) @config_path = File.join(@root, "envoy.json") File.open(@config_path, File::WRONLY | File::CREAT | File::EXCL, 0o600) do |file| file.write("#{JSON.pretty_generate(configuration)}\n") end (@resolved_runtime == :native) ? start_native : start_docker wait_until_ready! @opened = true self rescue close raise end |
#proxy_mount_path ⇒ Object
Returns the proxy socket's stable path inside a mounted sandbox.
129 |
# File 'lib/little_ghost/network/envoy_gateway.rb', line 129 def proxy_mount_path = "/run/little-ghost-egress/proxy.sock" |