mirror of
https://github.com/Av0idD4rk/firegex.git
synced 2026-10-23 21:42:05 +00:00
120 lines
5.6 KiB
Python
120 lines
5.6 KiB
Python
"""Editing a service, and what a refused edit leaves behind: nothing.
|
|
|
|
An edit changes what the datapath *is*, so it is checked the way a start would check it
|
|
and written all at once. Each test here is a way it used to leave the service somewhere
|
|
between the old definition and the new one.
|
|
"""
|
|
|
|
import time
|
|
|
|
import pytest
|
|
|
|
from integration.conftest import RELOAD, start_and_settle
|
|
from helpers.certs import UNPARSEABLE_CERT, UNPARSEABLE_KEY
|
|
from helpers.traffic import Channel
|
|
|
|
pytestmark = pytest.mark.instance
|
|
|
|
|
|
def test_a_stopped_service_is_refused_what_it_could_not_start_with(api, service,
|
|
stand_in, certificate):
|
|
"""The layer is asked whether or not the service is running.
|
|
|
|
It used to be asked only by starting the service, so a stopped one took any
|
|
combination — TLS on the queued layer, here — and failed on its next start instead,
|
|
long after the operator had moved on.
|
|
"""
|
|
cert, key = certificate()
|
|
server = stand_in()
|
|
service_id = service(f"edit-nfq-{server.port}", "127.0.0.1", server.port, "nfqueue")
|
|
|
|
why = api.services_edit_error(service_id, proto="tls", tls_cert=cert, tls_key=key)
|
|
assert why is not None, "a stopped queued service was switched to TLS"
|
|
assert "proxy layer" in why, why
|
|
assert api.services_get(service_id)["proto"] == "tcp", "the refused edit was written"
|
|
|
|
|
|
def test_an_edit_that_collides_leaves_the_service_as_it_was(api, service, stand_in):
|
|
"""The service and its addresses are one write.
|
|
|
|
Moving a UDP service to TCP rewrites its addresses too, and one of them is another
|
|
service's TCP address. The service row used to be written first, on its own, so the
|
|
collision left a TCP service whose addresses still said UDP.
|
|
"""
|
|
server = stand_in()
|
|
service(f"edit-tcp-{server.port}", "127.0.0.1", server.port, "proxy")
|
|
udp = service(f"edit-udp-{server.port}", "127.0.0.1", server.port, "proxy", proto="udp")
|
|
|
|
why = api.services_edit_error(udp, proto="tcp")
|
|
assert why is not None, "two services were allowed the same TCP address"
|
|
assert api.services_get(udp)["proto"] == "udp", "the service was rewritten anyway"
|
|
assert [a["proto"] for a in api.services_addresses(udp)] == ["udp"]
|
|
|
|
|
|
def test_saving_a_service_as_it_was_or_renaming_it_drops_nothing(api, protected, proxy_layer):
|
|
"""The form sends every setting on every save, so most saves change nothing.
|
|
|
|
Each of them was taken for a change of definition anyway: a running service was
|
|
stopped and started around it, and every connection it carried was reset — for opening
|
|
the dialog and pressing save, or for a new name, which nothing running reads.
|
|
"""
|
|
service_id, server, _ = protected(proxy_layer, name="edit-same")
|
|
start_and_settle(api, service_id)
|
|
was = api.services_get(service_id)
|
|
|
|
server.connect_client(timeout=3)
|
|
try:
|
|
server.send_packet(b"before")
|
|
assert server.recv_packet() == b"before", "the connection did not work to begin with"
|
|
assert api.services_edit(service_id, **{key: was[key] for key in (
|
|
"name", "proto", "transport", "fail_open", "max_connections",
|
|
"over_limit_forwards", "first_byte_timeout")})
|
|
assert api.services_edit(service_id, name=f"{was['name']}-renamed")
|
|
time.sleep(RELOAD)
|
|
server.send_packet(b"after")
|
|
assert server.recv_packet() == b"after", "saving the service dropped its connections"
|
|
finally:
|
|
server.close_client()
|
|
assert api.services_get(service_id)["name"] == f"{was['name']}-renamed"
|
|
|
|
|
|
def test_a_running_service_whose_edit_will_not_start_is_put_back(api, protected,
|
|
proxy_layer):
|
|
"""Refused by the engine, not by the form: nothing short of starting it can tell.
|
|
|
|
The material is shaped like a certificate and is not one. The service used to be left
|
|
stopped, on the definition that failed, with a 500 — so a typo in a certificate took
|
|
a working service off the air.
|
|
"""
|
|
service_id, server, port = protected(proxy_layer, name="edit-back")
|
|
start_and_settle(api, service_id)
|
|
channel = Channel(server, port, proxy_layer.ipv6)
|
|
assert channel.gets_through(b"before"), "the service was not working to begin with"
|
|
|
|
why = api.services_edit_error(service_id, proto="tls", tls_cert=UNPARSEABLE_CERT,
|
|
tls_key=UNPARSEABLE_KEY)
|
|
assert why is not None, "the engine accepted material that is not a certificate"
|
|
assert "nothing was changed" in why, why
|
|
|
|
now = api.services_get(service_id)
|
|
assert (now["status"], now["proto"]) == ("active", "tcp"), str(now)
|
|
assert channel.gets_through(b"after"), "the service was not started again as it was"
|
|
|
|
|
|
def test_leaving_the_hand_off_layer_gives_the_proxy_endpoint_back(api, service):
|
|
"""The endpoint of the operator's own proxy is the hand-off layer's alone. It stayed
|
|
on the rows when a service moved off that layer — read by nothing, and still in the
|
|
unique index, refusing another service's hand-off the endpoint."""
|
|
from helpers.net import free_port
|
|
|
|
endpoint = free_port()
|
|
first = service(f"handoff-a-{endpoint}", "127.0.0.1", free_port(), "external",
|
|
proxy_ip="127.0.0.1", proxy_port=endpoint)
|
|
assert api.services_edit(first, transport="proxy")
|
|
address = api.services_addresses(first)[0]
|
|
assert address["proxy_port"] is None and address["proxy_ip"] is None, address
|
|
|
|
second = service(f"handoff-b-{endpoint}", "127.0.0.1", free_port(), "external",
|
|
proxy_ip="127.0.0.1", proxy_port=endpoint)
|
|
assert second, "the endpoint was still held by a service no longer handing off"
|