Skip to content

Commit

Permalink
Add SSL options to configuration (#114)
Browse files Browse the repository at this point in the history
* Add development guide for schema registry
* Bump version to 0.28.0
  • Loading branch information
Strech committed Apr 18, 2024
1 parent 5fe9b71 commit 7617648
Show file tree
Hide file tree
Showing 10 changed files with 407 additions and 99 deletions.
47 changes: 22 additions & 25 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,45 +1,42 @@
# Changelog

## [0.27.0] - 2023-07-14 <sup>([notes][0.27.0-n])</sup>
## [0.28.0] - 2024-04-18

- Add new `Avrora.Config` SSL options `registry_ssl_cacerts` and `registry_ssl_cacert_path` (#114 @strech)

## [0.27.0] - 2023-07-14

- Replace `Logger.warn/1` with `Logger.warning/2` (#107 @trbngr, @strech)
- Drop support for Elixir lower than 1.12 (#107 @strech)

## [0.26.0] - 2023-01-11 <sup>([notes][0.26.0-n])</sup>
## [0.26.0] - 2023-01-11

- Add `--appconfig` argument to schema registration mix task (#102 @emilianobovetti, @strech)

## [0.25.0] - 2023-01-03 <sup>([notes][0.25.0-n])</sup>
## [0.25.0] - 2023-01-03

- Add User-Agent header when communicating with Schema Registry (#100 @azeemchauhan, @strech)
- Add `User-Agent` header when communicating with Schema Registry (#100 @azeemchauhan, @strech)

## [0.24.2] - 2022-09-13 <sup>([notes][0.24.2-n])</sup>
## [0.24.2] - 2022-09-13

- Fix Avrora.Config.registry_schemas_autoreg/0 to return configured `false` value (#99 @ankhers)
- Fix `Avrora.Config.registry_schemas_autoreg/0` to return configured `false` value (#99 @ankhers)

## [0.24.1] - 2022-09-12 <sup>([notes][0.24.1-n])</sup>
## [0.24.1] - 2022-09-12

- Add SSL option `[verify: :verify_none]` to Avrora.HttpClient (#97, @goozzik)
- Add SSL option `[verify: :verify_none]` to `Avrora.HttpClient` (#97, @goozzik)

## [0.24.0] - 2022-03-16 <sup>([notes][0.24.0-n])</sup>
## [0.24.0] - 2022-03-16

- Add new Avrora.Config option decoder_hook (#94, @strech)
- Add new `Avrora.Config` option decoder_hook (#94, @strech)

## [0.23.0] - 2021-07-06 <sup>([notes][0.23.0-n])</sup>
## [0.23.0] - 2021-07-06

- Add runtime config resolution for Avrora.Client (#92, @strech)

[0.27.0]: https://github.com/Strech/avrora/compare/v0.26.0...v0.27.0
[0.27.0-n]: https://github.com/Strech/avrora/releases/tag/v0.27.0
[0.26.0]: https://github.com/Strech/avrora/compare/v0.25.0...v0.26.0
[0.26.0-n]: https://github.com/Strech/avrora/releases/tag/v0.26.0
[0.25.0]: https://github.com/Strech/avrora/compare/v0.24.2...v0.25.0
[0.25.0-n]: https://github.com/Strech/avrora/releases/tag/v0.25.0
[0.24.2]: https://github.com/Strech/avrora/compare/v0.24.1...v0.24.2
[0.24.2-n]: https://github.com/Strech/avrora/releases/tag/v0.24.2
[0.24.1]: https://github.com/Strech/avrora/compare/v0.24.0...v0.24.1
[0.24.1-n]: https://github.com/Strech/avrora/releases/tag/v0.24.1
[0.24.0]: https://github.com/Strech/avrora/compare/v0.23.0...v0.24.0
[0.24.0-n]: https://github.com/Strech/avrora/releases/tag/v0.24.0
[0.23.0]: https://github.com/Strech/avrora/compare/v0.22.0...v0.23.0
[0.23.0-n]: https://github.com/Strech/avrora/releases/tag/v0.23.0
[0.27.0]: https://github.com/Strech/avrora/releases/tag/v0.27.0
[0.26.0]: https://github.com/Strech/avrora/releases/tag/v0.26.0
[0.25.0]: https://github.com/Strech/avrora/releases/tag/v0.25.0
[0.24.2]: https://github.com/Strech/avrora/releases/tag/v0.24.2
[0.24.1]: https://github.com/Strech/avrora/releases/tag/v0.24.1
[0.24.0]: https://github.com/Strech/avrora/releases/tag/v0.24.0
[0.23.0]: https://github.com/Strech/avrora/releases/tag/v0.23.0
208 changes: 208 additions & 0 deletions DEVELOPMENT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
# Development

To test `Avrora` with real Confluent Schema Registry it is recommended to use
official demo project.

## 1. Setup Confluent demo project

```console
$ git clone git clone git@github.com:confluentinc/cp-demo.git
$ cd cp-demo
$ git checkout -b 7.5.1-post origin/7.5.1-post
```

Apply the following patch to minimize the setup and keep the bare-minimum.

> [!NOTE]
> You may see some errors and warnings, but you could ignore them, for example:
>
> Error response from daemon: No such container: connect
> WARNING: Expected to find at least 6 subjects in Schema Registry but found...
```diff
diff --git a/scripts/start.sh b/scripts/start.sh
index 50b5b41a..535b7a0e 100755
--- a/scripts/start.sh
+++ b/scripts/start.sh
@@ -5,6 +5,8 @@ source ${DIR}/helper/functions.sh
source ${DIR}/env.sh

#-------------------------------------------------------------------------------
+# Disable visualization by default
+VIZ=false

# Do preflight checks
preflight_checks || exit
@@ -15,7 +17,7 @@ ${DIR}/stop.sh
CLEAN=${CLEAN:-false}

# Build Kafka Connect image with connector plugins
-build_connect_image
+# build_connect_image

# Set the CLEAN variable to true if cert doesn't exist
if ! [[ -f "${DIR}/security/controlCenterAndKsqlDBServer-ca1-signed.crt" ]] || ! check_num_certs; then
@@ -86,75 +88,82 @@ docker-compose exec kafka1 kafka-configs \


# Bring up more containers
-docker-compose up --no-recreate -d schemaregistry connect control-center
+# FIXME
+# docker-compose up --no-recreate -d schemaregistry connect control-center
+docker-compose up --no-recreate -d schemaregistry

echo
echo -e "Create topics in Kafka cluster:"
docker-compose exec tools bash -c "/tmp/helper/create-topics.sh" || exit 1

# Verify Kafka Connect Worker has started
-MAX_WAIT=240
-echo -e "\nWaiting up to $MAX_WAIT seconds for Connect to start"
-retry $MAX_WAIT host_check_up connect || exit 1
+# FIXME
+# MAX_WAIT=240
+# echo -e "\nWaiting up to $MAX_WAIT seconds for Connect to start"
+# retry $MAX_WAIT host_check_up connect || exit 1

#-------------------------------------------------------------------------------

-echo -e "\nStart streaming from the Wikipedia SSE source connector:"
-${DIR}/connectors/submit_wikipedia_sse_config.sh || exit 1
+# FIXME
+# echo -e "\nStart streaming from the Wikipedia SSE source connector:"
+# ${DIR}/connectors/submit_wikipedia_sse_config.sh || exit 1

-# Verify connector is running
-MAX_WAIT=120
-echo
-echo "Waiting up to $MAX_WAIT seconds for connector to be in RUNNING state"
-retry $MAX_WAIT check_connector_status_running "wikipedia-sse" || exit 1
+# # Verify connector is running
+# MAX_WAIT=120
+# echo
+# echo "Waiting up to $MAX_WAIT seconds for connector to be in RUNNING state"
+# retry $MAX_WAIT check_connector_status_running "wikipedia-sse" || exit 1

-# Verify wikipedia.parsed topic is populated and schema is registered
-MAX_WAIT=120
-echo
-echo -e "Waiting up to $MAX_WAIT seconds for subject wikipedia.parsed-value (for topic wikipedia.parsed) to be registered in Schema Registry"
-retry $MAX_WAIT host_check_schema_registered || exit 1
+# # Verify wikipedia.parsed topic is populated and schema is registered
+# MAX_WAIT=120
+# echo
+# echo -e "Waiting up to $MAX_WAIT seconds for subject wikipedia.parsed-value (for topic wikipedia.parsed) to be registered in Schema Registry"
+# retry $MAX_WAIT host_check_schema_registered || exit 1

#-------------------------------------------------------------------------------

-# Verify Confluent Control Center has started
-MAX_WAIT=300
-echo
-echo "Waiting up to $MAX_WAIT seconds for Confluent Control Center to start"
-retry $MAX_WAIT host_check_up control-center || exit 1
+# # Verify Confluent Control Center has started
+# FIXME
+# MAX_WAIT=300
+# echo
+# echo "Waiting up to $MAX_WAIT seconds for Confluent Control Center to start"
+# retry $MAX_WAIT host_check_up control-center || exit 1

-echo -e "\nConfluent Control Center modifications:"
-${DIR}/helper/control-center-modifications.sh
-echo
+# echo -e "\nConfluent Control Center modifications:"
+# ${DIR}/helper/control-center-modifications.sh
+# echo


#-------------------------------------------------------------------------------

-# Start more containers
-docker-compose up --no-recreate -d ksqldb-server ksqldb-cli restproxy
+# FIXME
+# # Start more containers
+# docker-compose up --no-recreate -d ksqldb-server ksqldb-cli restproxy

-# Verify ksqlDB server has started
-echo
-echo
-MAX_WAIT=120
-echo -e "\nWaiting up to $MAX_WAIT seconds for ksqlDB server to start"
-retry $MAX_WAIT host_check_up ksqldb-server || exit 1
+# # Verify ksqlDB server has started
+# echo
+# echo
+# MAX_WAIT=120
+# echo -e "\nWaiting up to $MAX_WAIT seconds for ksqlDB server to start"
+# retry $MAX_WAIT host_check_up ksqldb-server || exit 1

-echo -e "\nRun ksqlDB queries:"
-${DIR}/ksqlDB/run_ksqlDB.sh
+# echo -e "\nRun ksqlDB queries:"
+# ${DIR}/ksqlDB/run_ksqlDB.sh

if [[ "$VIZ" == "true" ]]; then
build_viz || exit 1
fi

-echo -e "\nStart additional consumers to read from topics WIKIPEDIANOBOT, WIKIPEDIA_COUNT_GT_1"
-${DIR}/consumers/listen_WIKIPEDIANOBOT.sh
-${DIR}/consumers/listen_WIKIPEDIA_COUNT_GT_1.sh
+# FIXME
+# echo -e "\nStart additional consumers to read from topics WIKIPEDIANOBOT, WIKIPEDIA_COUNT_GT_1"
+# ${DIR}/consumers/listen_WIKIPEDIANOBOT.sh
+# ${DIR}/consumers/listen_WIKIPEDIA_COUNT_GT_1.sh

-echo
-echo
-echo "Start the Kafka Streams application wikipedia-activity-monitor"
-docker-compose up --no-recreate -d streams-demo
-echo "..."
+# echo
+# echo
+# echo "Start the Kafka Streams application wikipedia-activity-monitor"
+# docker-compose up --no-recreate -d streams-demo
+# echo "..."


#-------------------------------------------------------------------------------
```

Save it with name `cp-demo.patch` and run the following command.

```console
$ git apply cp-demo.patch
```

To ensure HTTP(S) connectivity with the generated certificate,
add the schema registry to your system's hosts file:

```console
$ sudo sh -c 'echo "0.0.0.0 schemaregistry" >> /etc/hosts'
```

## 2. Get certificates

Copy the certificate from the Docker container to your local machine and
convert the PEM certificate to a DER-encoded format.

```console
$ docker cp schemaregistry:/etc/kafka/secrets/snakeoil-ca-1.crt .
$ openssl x509 -in snakeoil-ca-1.crt -outform DER -out snakeoil-ca-1.der
```

## 3. Check Schema Registry connectivity

Test the connection to the Schema Registry using the `Avrora.HTTPClient`
with the converted certificate. Run the following code in your console.

```elixir
# Setup the URL and read the certificate
url = "https://superUser:superUser@schemaregistry:8085/subjects"
cert = File.read!(Path.expand("./snakeoil-ca-1.der"))

# Make a get request to the Schema Registry it should output `{:ok, []}`
# (because no data was populated in patched `start.sh` script)
Avrora.HTTPClient.get(url, [ssl_options: [verify: :verify_peer, cacerts: [cert]]])
```
19 changes: 15 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
[v0.24]: https://github.com/Strech/avrora/releases/tag/v0.24.0
[v0.25]: https://github.com/Strech/avrora/releases/tag/v0.25.0
[v0.26]: https://github.com/Strech/avrora/releases/tag/v0.26.0
[v0.28]: https://github.com/Strech/avrora/releases/tag/v0.28.0
[1]: https://avro.apache.org/
[2]: https://www.confluent.io/confluent-schema-registry
[3]: https://docs.confluent.io/current/schema-registry/serializer-formatter.html#wire-format
Expand All @@ -34,6 +35,7 @@
[9]: https://github.com/Strech/avrora/wiki/Schema-name-resolution
[10]: https://github.com/Strech/avrora/pull/70
[11]: https://github.com/klarna/erlavro#decoder-hooks
[12]: https://www.erlang.org/docs/26/man/ssl#type-client_cacerts

# Getting Started

Expand Down Expand Up @@ -62,7 +64,7 @@ Add Avrora to `mix.exs` as a dependency
```elixir
def deps do
[
{:avrora, "~> 0.21"}
{:avrora, "~> 0.27"}
]
end
```
Expand All @@ -83,10 +85,12 @@ Create your private Avrora client module
defmodule MyClient do
use Avrora.Client,
otp_app: :my_application,
schemas_path: "./priv/schemas",
registry_url: "http://localhost:8081",
registry_auth: {:basic, ["username", "password"]},
registry_user_agent: "Avrora/0.25.0 Elixir",
schemas_path: "./priv/schemas",
registry_ssl_cacerts: File.read!("./priv/trusted.der"),
registry_ssl_cacert_path: "./priv/trusted.crt",
registry_schemas_autoreg: false,
convert_null_values: false,
convert_map_to_proplist: false,
Expand All @@ -104,10 +108,12 @@ Configure the `Avrora` shared client in `config/config.exs`
```elixir
config :avrora,
otp_app: :my_application, # optional, if you want to use it as a root folder for `schemas_path`
schemas_path: "./priv/schemas",
registry_url: "http://localhost:8081",
registry_auth: {:basic, ["username", "password"]}, # optional
registry_user_agent: "Avrora/0.24.2 Elixir", # optional: if you want to return previous behaviour, set it to `nil`
schemas_path: "./priv/schemas",
registry_ssl_cacerts: File.read!("./priv/trusted.der"), # optional: if you have DER-encoded certificate
registry_ssl_cacert_path: "./priv/trusted.crt", # optional: if you have PEM-encoded certificate file
registry_schemas_autoreg: false, # optional: if you want manually register schemas
convert_null_values: false, # optional: if you want to keep decoded `:null` values as is
convert_map_to_proplist: false, # optional: if you want to restore the old behavior for decoding map-type
Expand All @@ -116,10 +122,12 @@ config :avrora,
```

- `otp_app`<sup>[v0.22]</sup> - Name of the OTP application to use for runtime configuration via env, default `nil`
- `schemas_path` - Base path for locally stored schema files, default `./priv/schemas`
- `registry_url` - URL for the Schema Registry, default `nil`
- `registry_auth` – Credentials to authenticate in the Schema Registry, default `nil`
- `registry_user_agent`<sup>[v0.25]</sup> - HTTP `User-Agent` header for Schema Registry requests, default `Avrora/<version> Elixir`
- `schemas_path` - Base path for locally stored schema files, default `./priv/schemas`
- `registry_ssl_cacerts`<sup>[v0.28]</sup> - DER-encoded certificates, but [without combined support][12], default `nil`
- `registry_ssl_cacert_path`<sup>[v0.28]</sup> - Path to a file containing PEM-encoded CA certificates, default `nil`
- `registry_schemas_autoreg`<sup>[v0.13]</sup> - Flag for automatic schemas registration in the Schema Registry, default `true`
- `convert_null_values`<sup>[v0.14]</sup> - Flag for automatic conversion of decoded `:null` values into `nil`, default `true`
- `convert_map_to_proplist`<sup>[v0.15]</sup> restore old behaviour and confiugre decoding map-type to proplist, default `false`
Expand All @@ -146,6 +154,9 @@ recommended to set `otp_app` which will point to your OTP applications. This wil
to have a per-client runtime resolution for all configuration options (i.e. `schemas_path`)
with a fallback to staticly defined in a client itself.<sup>[v0.23]</sup>

:bulb: If both `registry_ssl_cacerts` and `registry_ssl_cacert_path` given, then
`registry_ssl_cacerts` has a priority.

## Start cache process

Avrora uses an in-memory cache to speed up schema lookup.
Expand Down
7 changes: 7 additions & 0 deletions lib/avrora/client.ex
Original file line number Diff line number Diff line change
Expand Up @@ -109,9 +109,16 @@ defmodule Avrora.Client do
if is_nil(@otp_app), do: Path.expand(path), else: Application.app_dir(@otp_app, path)
end

def registry_ssl_cacert_path do
path = get(@opts, :registry_ssl_cacert_path, nil)

if is_nil(path), do: nil, else: Path.expand(path)
end

def registry_url, do: get(@opts, :registry_url, nil)
def registry_auth, do: get(@opts, :registry_auth, nil)
def registry_user_agent, do: get(@opts, :registry_user_agent, "Avrora/#{version()} Elixir")
def registry_ssl_cacerts, do: get(@opts, :registry_ssl_cacerts, nil)
def registry_schemas_autoreg, do: get(@opts, :registry_schemas_autoreg, true)
def convert_null_values, do: get(@opts, :convert_null_values, true)
def convert_map_to_proplist, do: get(@opts, :convert_map_to_proplist, false)
Expand Down
Loading

0 comments on commit 7617648

Please sign in to comment.