Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

gRPC server reflection #224

Merged
merged 4 commits into from
Jan 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ port, that implements the Envoy Rate Limit protocol (v3).

- [**Getting started**](#getting-started)
- [**How it works**](/doc/how-it-works.md)
- [**Configuration**](/doc/server/configuration.md)
- [**Development**](#development)
- [**Testing Environment**](limitador-server/sandbox/README.md)
- [**Kubernetes**](limitador-server/kubernetes/)
Expand Down
2 changes: 2 additions & 0 deletions doc/server/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ Options:
Validates the LIMITS_FILE and exits
-H, --rate-limit-headers <rate_limit_headers>
Enables rate limit response headers [default: NONE] [possible values: NONE, DRAFT_VERSION_03]
--grpc-reflection-service
Enables gRPC server reflection service
-h, --help
Print help
-V, --version
Expand Down
1 change: 1 addition & 0 deletions limitador-server/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ limitador = { path = "../limitador", features = ['lenient_conditions'] }
tokio = { version = "1", features = ["full"] }
thiserror = "1"
tonic = "0.10"
tonic-reflection = "0.10.2"
prost = "0.12"
prost-types = "0.12"
serde_yaml = "0.9"
Expand Down
22 changes: 14 additions & 8 deletions limitador-server/build.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
use std::env;
use std::error::Error;
use std::path::PathBuf;
use std::process::Command;

fn main() -> Result<(), Box<dyn Error>> {
Expand All @@ -9,14 +11,18 @@ fn main() -> Result<(), Box<dyn Error>> {
}

fn generate_protobuf() -> Result<(), Box<dyn Error>> {
tonic_build::configure().build_server(true).compile(
&["envoy/service/ratelimit/v3/rls.proto"],
&[
"vendor/protobufs/data-plane-api",
"vendor/protobufs/protoc-gen-validate",
"vendor/protobufs/xds",
],
)?;
let original_out_dir = PathBuf::from(env::var("OUT_DIR")?);
tonic_build::configure()
.build_server(true)
.file_descriptor_set_path(original_out_dir.join("rls.bin"))
.compile(
&["envoy/service/ratelimit/v3/rls.proto"],
&[
"vendor/protobufs/data-plane-api",
"vendor/protobufs/protoc-gen-validate",
"vendor/protobufs/xds",
],
)?;
Ok(())
}

Expand Down
1 change: 1 addition & 0 deletions limitador-server/sandbox/.gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/bin/
*.crt
*.key
*.pem
Expand Down
21 changes: 21 additions & 0 deletions limitador-server/sandbox/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,29 @@ clean-containers: ## clean containers
- $(DOCKER) compose -f docker-compose-envoy.yaml -f docker-compose-limitador-redis.yaml down --volumes --remove-orphans
- $(DOCKER)_compose -f docker-compose-envoy.yaml -f docker-compose-limitador-redis-cached.yaml down --volumes --remove-orphans
- $(DOCKER) compose -f docker-compose-envoy.yaml -f docker-compose-limitador-infinispan.yaml down --volumes --remove-orphans
- $(DOCKER) compose -f docker-compose-envoy.yaml -f docker-compose-limitador-disk.yaml down --volumes --remove-orphans
- $(MAKE) cleancerts

clean: ## clean all
- $(MAKE) clean-containers
- $(MAKE) redis-clean-certs

GRPCURL=$(PROJECT_PATH)/bin/grpcurl
$(GRPCURL):
$(call go-install-tool,$(GRPCURL),github.com/fullstorydev/grpcurl/cmd/[email protected])

.PHONY: grpcurl
grpcurl: $(GRPCURL) ## Download grpcurl locally if necessary.

# go-install-tool will 'go install' any package $2 and install it to $1.
define go-install-tool
@[ -f $(1) ] || { \
set -e ;\
TMP_DIR=$$(mktemp -d) ;\
cd $$TMP_DIR ;\
go mod init tmp ;\
echo "Downloading $(2)" ;\
GOBIN=$(PROJECT_PATH)/bin go install $(2) ;\
rm -rf $$TMP_DIR ;\
}
endef
74 changes: 74 additions & 0 deletions limitador-server/sandbox/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,87 @@ Check out `make help` for all the targets.
| Redis Secured | `make deploy-redis-tls` | Uses Redis with TLS and password protected to store counters |
| Redis Cached | `make deploy-redis-cached` | Uses Redis to store counters, with an in-memory cache |
| Infinispan | `make deploy-infinispan` | Uses Infinispan to store counters |
| Disk | `make deploy-disk` | Uses disk to store counters |

### Limitador's admin HTTP endpoint

Limits

```bash
curl -i http://127.0.0.1:18080/limits/test_namespace
```

Counters

```bash
curl -i http://127.0.0.1:18080/counters/test_namespace
```

Metrics

```bash
curl -i http://127.0.0.1:18080/metrics
```

### Limitador's GRPC RateLimitService endpoint

Get `grpcurl`. You need [Go SDK](https://golang.org/doc/install) installed.

Golang version >= 1.18 (from [fullstorydev/grpcurl](https://github.com/fullstorydev/grpcurl/blob/v1.8.9/go.mod#L3))

```bash
make grpcurl
```

Inspect `RateLimitService` GRPC service

```bash
bin/grpcurl -plaintext 127.0.0.1:18081 describe envoy.service.ratelimit.v3.RateLimitService
```

Make a custom request

```bash
bin/grpcurl -plaintext -d @ 127.0.0.1:18081 envoy.service.ratelimit.v3.RateLimitService.ShouldRateLimit <<EOM
{
"domain": "test_namespace",
"hits_addend": 1,
"descriptors": [
{
"entries": [
{
"key": "req.method",
"value": "POST"
}
]
}
]
}
EOM
```

Do repeated requests. As the limit is set to max 5 request for 60 seconds,
you should see `OVER_LIMIT` response after 5 requests.

```bash
while :; do bin/grpcurl -plaintext -d @ 127.0.0.1:18081 envoy.service.ratelimit.v3.RateLimitService.ShouldRateLimit <<EOM; sleep 1; done
{
"domain": "test_namespace",
"hits_addend": 1,
"descriptors": [
{
"entries": [
{
"key": "req.method",
"value": "POST"
}
]
}
]
}
EOM
```

### Downstream traffic

**Upstream** service implemented by [httpbin.org](https://httpbin.org/)
Expand Down
2 changes: 2 additions & 0 deletions limitador-server/sandbox/docker-compose-limitador-disk.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ services:
- --http-port
- "8080"
- -vvv
- --grpc-reflection-service
- /opt/kuadrant/limits/limits.yaml
- disk
- "/tmp/data"
Expand All @@ -24,5 +25,6 @@ services:
- "8081"
ports:
- "18080:8080"
- "18081:8081"
volumes:
- ./limits.yaml:/opt/kuadrant/limits/limits.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ services:
- --http-port
- "8080"
- -vvvv
- --grpc-reflection-service
- /opt/kuadrant/limits/limits.yaml
- infinispan
- --cache-name
Expand All @@ -29,6 +30,7 @@ services:
- "8081"
ports:
- "18080:8080"
- "18081:8081"
volumes:
- ./limits.yaml:/opt/kuadrant/limits/limits.yaml
infinispan:
Expand Down
2 changes: 2 additions & 0 deletions limitador-server/sandbox/docker-compose-limitador-memory.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,14 @@ services:
- --http-port
- "8080"
- -vvv
- --grpc-reflection-service
- /opt/kuadrant/limits/limits.yaml
- memory
expose:
- "8080"
- "8081"
ports:
- "18080:8080"
- "18081:8081"
volumes:
- ./limits.yaml:/opt/kuadrant/limits/limits.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ services:
- --http-port
- "8080"
- -vvv
- --grpc-reflection-service
- /opt/kuadrant/limits/limits.yaml
- redis_cached
- --ttl
Expand All @@ -33,6 +34,7 @@ services:
- "8081"
ports:
- "18080:8080"
- "18081:8081"
volumes:
- ./limits.yaml:/opt/kuadrant/limits/limits.yaml
redis:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ services:
- --http-port
- "8080"
- -vvv
- --grpc-reflection-service
- /opt/kuadrant/limits/limits.yaml
- redis
- rediss://:foobared@redis:6379/#insecure
Expand Down
2 changes: 2 additions & 0 deletions limitador-server/sandbox/docker-compose-limitador-redis.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ services:
- --http-port
- "8080"
- -vvv
- --grpc-reflection-service
- /opt/kuadrant/limits/limits.yaml
- redis
- redis://redis:6379
Expand All @@ -25,6 +26,7 @@ services:
- "8081"
ports:
- "18080:8080"
- "18081:8081"
volumes:
- ./limits.yaml:/opt/kuadrant/limits/limits.yaml
redis:
Expand Down
4 changes: 4 additions & 0 deletions limitador-server/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ pub struct Configuration {
pub limit_name_in_labels: bool,
pub log_level: Option<LevelFilter>,
pub rate_limit_headers: RateLimitHeaders,
pub grpc_reflection_service: bool,
}

pub mod env {
Expand Down Expand Up @@ -83,6 +84,7 @@ impl Configuration {
http_port: u16,
limit_name_in_labels: bool,
rate_limit_headers: RateLimitHeaders,
grpc_reflection_service: bool,
) -> Self {
Self {
limits_file,
Expand All @@ -94,6 +96,7 @@ impl Configuration {
limit_name_in_labels,
log_level: None,
rate_limit_headers,
grpc_reflection_service,
}
}

Expand Down Expand Up @@ -121,6 +124,7 @@ impl Default for Configuration {
limit_name_in_labels: false,
log_level: None,
rate_limit_headers: RateLimitHeaders::None,
grpc_reflection_service: false,
}
}
}
Expand Down
16 changes: 16 additions & 0 deletions limitador-server/src/envoy_rls/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -194,16 +194,32 @@ pub fn to_response_header(
headers
}

mod rls_proto {
pub(crate) const RLS_DESCRIPTOR_SET: &[u8] = tonic::include_file_descriptor_set!("rls");
}

pub async fn run_envoy_rls_server(
address: String,
limiter: Arc<Limiter>,
rate_limit_headers: RateLimitHeaders,
grpc_reflection_service: bool,
) -> Result<(), transport::Error> {
let rate_limiter = MyRateLimiter::new(limiter, rate_limit_headers);
let svc = RateLimitServiceServer::new(rate_limiter);

let reflection_service = match grpc_reflection_service {
false => None,
true => Some(
tonic_reflection::server::Builder::configure()
.register_encoded_file_descriptor_set(rls_proto::RLS_DESCRIPTOR_SET)
.build()
.unwrap(),
),
};

Server::builder()
.add_service(svc)
.add_optional_service(reflection_service)
.serve(address.parse().unwrap())
.await
}
Expand Down
10 changes: 10 additions & 0 deletions limitador-server/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
let envoy_rls_address = config.rlp_address();
let http_api_address = config.http_address();
let rate_limit_headers = config.rate_limit_headers.clone();
let grpc_reflection_service = config.grpc_reflection_service;

let rate_limiter: Arc<Limiter> = match Limiter::new(config).await {
Ok(limiter) => Arc::new(limiter),
Expand Down Expand Up @@ -390,6 +391,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
envoy_rls_address.to_string(),
rate_limiter.clone(),
rate_limit_headers,
grpc_reflection_service,
));

info!("HTTP server starting on {}", http_api_address);
Expand Down Expand Up @@ -515,6 +517,13 @@ fn create_config() -> (Configuration, &'static str) {
]))
.help("Enables rate limit response headers"),
)
.arg(
Arg::new("grpc_reflection_service")
.long("grpc-reflection-service")
.action(ArgAction::SetTrue)
.display_order(9)
.help("Enables gRPC server reflection service"),
)
.subcommand(
Command::new("memory")
.display_order(1)
Expand Down Expand Up @@ -745,6 +754,7 @@ fn create_config() -> (Configuration, &'static str) {
matches.get_flag("limit_name_in_labels")
|| env_option_is_enabled("LIMIT_NAME_IN_PROMETHEUS_LABELS"),
rate_limit_headers,
matches.get_flag("grpc_reflection_service"),
);

config.log_level = match matches.get_count("v") {
Expand Down
Loading