-
Notifications
You must be signed in to change notification settings - Fork 12
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
14 changed files
with
4,565 additions
and
42 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
import os | ||
from pathlib import Path | ||
from signal import SIGQUIT | ||
from subprocess import PIPE, STDOUT, Popen, run | ||
|
||
import pytest | ||
|
||
from utils import wait_for_lines_in_output | ||
|
||
|
||
@pytest.fixture(scope="session") | ||
def certificates(tmp_path_factory: pytest.TempPathFactory) -> dict[str, Path]: | ||
dir = tmp_path_factory.mktemp("aeon_cert") | ||
cmd = (Path(__file__).parent / "generate-keys.sh", dir) | ||
returncode = run(cmd).returncode | ||
assert returncode == 0, "Some error on generate certificates" | ||
cert = { | ||
"ca": dir / "ca.crt", | ||
"s_private": dir / "server.key", | ||
"s_public": dir / "server.crt", | ||
"c_private": dir / "client.key", | ||
"c_public": dir / "client.crt", | ||
} | ||
for k, v in cert.items(): | ||
assert v.exists(), f"Not found {k} certificate" | ||
return cert | ||
|
||
|
||
@pytest.fixture(scope="session") | ||
def mock_aeon(tmp_path_factory) -> Path: | ||
server_dir = Path(__file__).parent / "server" | ||
exec = tmp_path_factory.mktemp("aeon_mock") / "aeon" | ||
result = run(f"go build -C {server_dir} -o {exec}".split()) | ||
assert result.returncode == 0, "Failed build mock aeon server" | ||
return exec | ||
|
||
|
||
@pytest.fixture(params=[50052, "@aeon_unix_socket", "AEON"]) | ||
def aeon_plain(mock_aeon, tmp_path, request): | ||
cmd = [mock_aeon] | ||
param = request.param | ||
if isinstance(param, int): | ||
cmd.append(f"-port={param}") | ||
elif isinstance(param, str): | ||
if param[0] != "@": | ||
param = tmp_path / param | ||
cmd.append(f"-unix={param}") | ||
|
||
aeon = Popen( | ||
cmd, | ||
env=dict(os.environ, GRPC_GO_LOG_SEVERITY_LEVEL="info"), | ||
stderr=STDOUT, | ||
stdout=PIPE, | ||
text=True, | ||
) | ||
print(wait_for_lines_in_output(aeon.stdout, ["ListenSocket created"])) | ||
yield param | ||
aeon.send_signal(SIGQUIT) | ||
assert aeon.wait(5) == 0, "Mock aeon server didn't stopped properly" | ||
|
||
|
||
@pytest.fixture(params=["server-side", "mutual-tls"]) | ||
def aeon_ssl(mock_aeon, certificates, request): | ||
cmd = [ | ||
mock_aeon, | ||
"-ssl", | ||
f"-key={certificates['s_private']}", | ||
f"-cert={certificates['s_public']}", | ||
] | ||
mode = request.param | ||
if mode == "mutual-tls": | ||
cmd.append(f"-ca={certificates['ca']}") | ||
elif mode == "server-side": | ||
pass | ||
else: | ||
assert False, "Unsupported TLS mode" | ||
|
||
aeon = Popen( | ||
cmd, | ||
env=dict(os.environ, GRPC_GO_LOG_SEVERITY_LEVEL="info"), | ||
stderr=STDOUT, | ||
stdout=PIPE, | ||
text=True, | ||
) | ||
print(wait_for_lines_in_output(aeon.stdout, ["ListenSocket created"])) | ||
yield mode | ||
aeon.send_signal(SIGQUIT) | ||
assert aeon.wait(5) == 0, "Mock aeon ssl server didn't stopped properly" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
#!/usr/bin/env bash | ||
set -e | ||
|
||
### First argument is target directory where generate files. | ||
DIR=$(realpath ${1:-$(pwd)}) | ||
[ -d $DIR ] || mkdir -p $DIR | ||
cd $DIR | ||
|
||
CA_SUBJ="/C=RU/ST=State/L=TestCity/O=Integration test/OU=Aeon/CN=localhost" | ||
|
||
cat > ext.cnf << EOF | ||
subjectAltName = @alt_names | ||
[alt_names] | ||
DNS = localhost | ||
IP = 127.0.0.1 | ||
EOF | ||
|
||
### Server .key&.crt and ca.crt required for Server-Side TLS mode. | ||
|
||
# 1. Generate CA's private key and self-signed certificate | ||
openssl req -new -x509 -days 1 -noenc -keyout ca.key -out ca.crt -subj "${CA_SUBJ}" -quiet | ||
|
||
# 2. Generate web server's private key and certificate signing request (CSR) | ||
openssl req -new -noenc -keyout server.key -out server.csr -subj "${CA_SUBJ}" -quiet | ||
|
||
# 3. Use CA's private key to sign web server's CSR and get back the signed certificate | ||
openssl x509 -req -in server.csr -days 1 -CA ca.crt -CAkey ca.key -CAcreateserial \ | ||
-out server.crt -extfile ext.cnf | ||
|
||
### Client .key & .crt required for Mutual TSL mode. | ||
|
||
# 4. Generate client's private key and certificate signing request (CSR) | ||
openssl req -new -noenc -keyout client.key -out client.csr -subj "$CA_SUBJ" -quiet | ||
|
||
# 5. Use CA's private key to sign client's CSR and get back the signed certificate | ||
openssl x509 -req -in client.csr -days 1 -CA ca.crt -CAkey ca.key -CAcreateserial \ | ||
-out client.crt -extfile ext.cnf |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
*.proto | ||
aeon |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
# Mock `aeon` server | ||
|
||
## Update and generate from `.proto` file | ||
|
||
1. cd `test/integration/aeon/server`; | ||
2. Get a new [aeon_router.proto](https://github.com/tarantool/aeon/blob/master/proto/aeon_router.proto); | ||
3. Run: `protoc --go_out=. --go-grpc_out=. aeon_router.proto`; | ||
4. Directory `pb` will be created with files: | ||
``` | ||
pb | ||
├── aeon_router_grpc.pb.go | ||
└── aeon_router.pb.go | ||
``` | ||
5. Commit the new changes in the created files; | ||
6. Update the `service/server.go` file to comply with the new `*.pb.go` requirements. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
module mock/server/aeon | ||
|
||
go 1.22.8 | ||
|
||
require ( | ||
google.golang.org/grpc v1.68.1 | ||
google.golang.org/protobuf v1.35.2 | ||
) | ||
|
||
require ( | ||
golang.org/x/net v0.29.0 // indirect | ||
golang.org/x/sys v0.25.0 // indirect | ||
golang.org/x/text v0.18.0 // indirect | ||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 // indirect | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= | ||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= | ||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= | ||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= | ||
golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo= | ||
golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= | ||
golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= | ||
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= | ||
golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= | ||
golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= | ||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 h1:pPJltXNxVzT4pK9yD8vR9X75DaWYYmLGMsEvBfFQZzQ= | ||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= | ||
google.golang.org/grpc v1.68.1 h1:oI5oTa11+ng8r8XMMN7jAOmWfPZWbYpCFaMUTACxkM0= | ||
google.golang.org/grpc v1.68.1/go.mod h1:+q1XYFJjShcqn0QZHvCyeR4CXPA+llXIeUIfIe00waw= | ||
google.golang.org/protobuf v1.35.2 h1:8Ar7bF+apOIoThw1EdZl0p1oWvMqTHmpA2fRTyZO8io= | ||
google.golang.org/protobuf v1.35.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,139 @@ | ||
package main | ||
|
||
import ( | ||
"crypto/tls" | ||
"crypto/x509" | ||
"flag" | ||
"fmt" | ||
"log" | ||
"mock/server/aeon/pb" | ||
"mock/server/aeon/service" | ||
"net" | ||
"os" | ||
"os/signal" | ||
"strings" | ||
"sync" | ||
"syscall" | ||
|
||
"google.golang.org/grpc" | ||
"google.golang.org/grpc/credentials" | ||
) | ||
|
||
var args = struct { | ||
is_ssl *bool | ||
ca_file *string | ||
cert_file *string | ||
key_file *string | ||
port *int | ||
unix_socket *string | ||
}{ | ||
is_ssl: flag.Bool("ssl", false, "Connection uses SSL if set, (default plain TCP)"), | ||
ca_file: flag.String("ca", "", "The CA file"), | ||
cert_file: flag.String("cert", "", "The TLS cert file"), | ||
key_file: flag.String("key", "", "The TLS key file"), | ||
port: flag.Int("port", 50051, "The server port"), | ||
unix_socket: flag.String("unix", "", "The Unix socket name"), | ||
} | ||
|
||
func getCertificate() tls.Certificate { | ||
if *args.cert_file == "" || *args.key_file == "" { | ||
log.Fatalln("Both 'key_file' and 'cert_file' required") | ||
} | ||
tls_cert, err := tls.LoadX509KeyPair(*args.cert_file, *args.key_file) | ||
if err != nil { | ||
log.Fatalf("Could not load server key pair: %v", err) | ||
} | ||
return tls_cert | ||
} | ||
|
||
func getTlsConfig() *tls.Config { | ||
if *args.ca_file == "" { | ||
return &tls.Config{ | ||
Certificates: []tls.Certificate{getCertificate()}, | ||
ClientAuth: tls.NoClientCert, | ||
} | ||
} | ||
|
||
ca, err := os.ReadFile(*args.ca_file) | ||
if err != nil { | ||
log.Fatalf("Failed to read CA file: %v", err) | ||
} | ||
certPool := x509.NewCertPool() | ||
if !certPool.AppendCertsFromPEM(ca) { | ||
log.Fatalln("Failed to append CA data") | ||
} | ||
return &tls.Config{ | ||
Certificates: []tls.Certificate{getCertificate()}, | ||
ClientAuth: tls.RequireAndVerifyClientCert, | ||
ClientCAs: certPool, | ||
} | ||
} | ||
|
||
func getServerOpts() []grpc.ServerOption { | ||
if !*args.is_ssl { | ||
return []grpc.ServerOption{} | ||
} | ||
creds := credentials.NewTLS(getTlsConfig()) | ||
return []grpc.ServerOption{grpc.Creds(creds)} | ||
} | ||
|
||
func getListener() net.Listener { | ||
var protocol string | ||
var address string | ||
|
||
if *args.unix_socket != "" { | ||
protocol = "unix" | ||
address = *args.unix_socket | ||
if strings.HasPrefix(address, "@") { | ||
address = "\x00" + address[1:] | ||
} | ||
} else { | ||
protocol = "tcp" | ||
address = fmt.Sprintf("localhost:%d", *args.port) | ||
} | ||
lis, err := net.Listen(protocol, address) | ||
if err != nil { | ||
log.Fatalf("Failed to listen: %v", err) | ||
} | ||
return lis | ||
} | ||
|
||
func exit() { | ||
log.Println("Exit func") | ||
syscall.Kill(syscall.Getpid(), syscall.SIGTERM) | ||
os.Exit(0) | ||
} | ||
|
||
func main() { | ||
log.Println("Start aeon mock server:", os.Args) | ||
|
||
flag.Parse() | ||
|
||
srv := grpc.NewServer(getServerOpts()...) | ||
pb.RegisterAeonRouterServiceServer(srv, &service.Server{}) | ||
|
||
// Run gRPC server. | ||
wg := sync.WaitGroup{} | ||
wg.Add(1) | ||
go func() { | ||
if err := srv.Serve(getListener()); err != nil { | ||
log.Fatalf("Failed to serve: %v", err) | ||
} | ||
wg.Done() | ||
}() | ||
|
||
// Shutdown on signals. | ||
exit_sig := make(chan os.Signal, 1) | ||
signal.Notify(exit_sig, | ||
syscall.SIGTERM, | ||
syscall.SIGINT, | ||
syscall.SIGQUIT, | ||
syscall.SIGHUP, | ||
) | ||
s := <-exit_sig | ||
log.Println("Got terminate signal:", s) | ||
|
||
srv.GracefulStop() | ||
wg.Wait() | ||
log.Println("Exit aeon mock server.") | ||
} |
Oops, something went wrong.