This is an automated email from the ASF dual-hosted git repository.

kou pushed a commit to branch main
in repository 
https://gitbox.apache.org/repos/asf/arrow-flight-sql-postgresql.git


The following commit(s) were added to refs/heads/main by this push:
     new c603719  Add support for TLS (#46)
c603719 is described below

commit c603719442b904a95b432a221d1f6cf90e22dfb1
Author: Sutou Kouhei <[email protected]>
AuthorDate: Mon Aug 14 15:32:26 2023 +0900

    Add support for TLS (#46)
    
    Closes GH-38
---
 .editorconfig                          | 10 +++-
 dev/prepare-tls.sh                     | 87 ++++++++++++++++++++++++++++++++++
 .editorconfig => dev/run-postgresql.sh | 38 ++++++++++++---
 src/afs.cc                             | 32 +++++++++++++
 test/helper/sandbox.rb                 | 55 +++++++++++++++++----
 test/test-flight-sql.rb                |  2 +-
 6 files changed, 207 insertions(+), 17 deletions(-)

diff --git a/.editorconfig b/.editorconfig
index 362c371..4075833 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -19,8 +19,16 @@ root = true
 
 [*.{c,cc,h}]
 
-indent_style = tab
+end_of_line = lf
 indent_size = 4
+indent_style = tab
+insert_final_newline = true
+trim_trailing_whitespace = true
+
+[*.sh]
+
 end_of_line = lf
+indent_size = 2
+indent_style = space
 insert_final_newline = true
 trim_trailing_whitespace = true
diff --git a/dev/prepare-tls.sh b/dev/prepare-tls.sh
new file mode 100755
index 0000000..c781ffb
--- /dev/null
+++ b/dev/prepare-tls.sh
@@ -0,0 +1,87 @@
+#!/bin/bash
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+set -eu
+
+if [ $# -ne 3 ]; then
+  echo "Usage: $0 CA_NAME SERVER_NAME CLIENT_NAME"
+  echo " e.g.: $0 root.example.com server.example.com client.example.com"
+  exit 1
+fi
+
+root_name=$1
+server_name=$2
+client_name=$3
+
+openssl req \
+        -new \
+        -nodes \
+        -text \
+        -out root.csr \
+        -keyout root.key \
+        -subj "/CN=${root_name}"
+chmod go-rwx root.key
+
+openssl x509 \
+        -req \
+        -in root.csr \
+        -text \
+        -days 3650 \
+        -extfile /etc/ssl/openssl.cnf \
+        -extensions v3_ca \
+        -signkey root.key \
+        -out root.crt
+
+openssl req \
+        -new \
+        -nodes \
+        -text \
+        -out server.csr \
+        -keyout server.key \
+        -subj "/CN=${server_name}"
+chmod og-rwx server.key
+
+openssl x509 \
+        -req \
+        -in server.csr \
+        -text \
+        -days 365 \
+        -CA root.crt \
+        -CAkey root.key \
+        -CAcreateserial \
+        -out server.crt
+
+openssl req \
+        -new \
+        -nodes \
+        -text \
+        -out client.csr \
+        -keyout client.key \
+        -subj "/CN=${client_name}"
+chmod og-rwx client.key
+
+openssl x509 \
+        -req \
+        -in client.csr \
+        -text \
+        -days 365 \
+        -CA root.crt \
+        -CAkey root.key \
+        -CAcreateserial \
+        -out client.crt
diff --git a/.editorconfig b/dev/run-postgresql.sh
old mode 100644
new mode 100755
similarity index 52%
copy from .editorconfig
copy to dev/run-postgresql.sh
index 362c371..209bfb9
--- a/.editorconfig
+++ b/dev/run-postgresql.sh
@@ -1,3 +1,5 @@
+#!/bin/bash
+#
 # Licensed to the Apache Software Foundation (ASF) under one
 # or more contributor license agreements.  See the NOTICE file
 # distributed with this work for additional information
@@ -15,12 +17,34 @@
 # specific language governing permissions and limitations
 # under the License.
 
-root = true
+set -eu
+
+if [ $# -ne 4 ]; then
+  echo "Usage: $0 DATA_DIRECTORY CA_NAME SERVER_NAME CLIENT_NAME"
+  echo " e.g.: $0 /tmp/afs root.example.com server.example.com 
client.example.com"
+  exit 1
+fi
+
+data_directory=$1
+root_name=$2
+server_name=$3
+client_name=$4
+
+base_directory="$(cd "$(dirname "$0")" && pwd)"
 
-[*.{c,cc,h}]
+rm -rf "${data_directory}"
 
-indent_style = tab
-indent_size = 4
-end_of_line = lf
-insert_final_newline = true
-trim_trailing_whitespace = true
+initdb \
+  --locale=C \
+  --set=arrow_flight_sql.uri=grpc+tls://${server_name}:15432 \
+  --set=shared_preload_libraries=arrow_flight_sql \
+  --set=ssl=on \
+  --set=ssl_ca_file=root.crt \
+  "${data_directory}"
+pushd "${data_directory}"
+"${base_directory}/prepare-tls.sh" \
+  "${root_name}" \
+  "${server_name}" \
+  "${client_name}"
+popd
+LANG=C postgres -D "${data_directory}"
diff --git a/src/afs.cc b/src/afs.cc
index 603eba0..d267a43 100644
--- a/src/afs.cc
+++ b/src/afs.cc
@@ -25,8 +25,10 @@ extern "C"
 #include <lib/dshash.h>
 #include <libpq/crypt.h>
 #include <libpq/libpq-be.h>
+#include <libpq/libpq.h>
 #include <miscadmin.h>
 #include <postmaster/bgworker.h>
+#include <postmaster/postmaster.h>
 #include <storage/ipc.h>
 #include <storage/latch.h>
 #include <storage/lwlock.h>
@@ -54,6 +56,8 @@ extern "C"
 
 #include <cinttypes>
 #include <condition_variable>
+#include <fstream>
+#include <iterator>
 #include <random>
 #include <sstream>
 
@@ -1654,12 +1658,40 @@ afs_server_internal(Proxy* proxy)
 {
        ARROW_ASSIGN_OR_RAISE(auto location, 
arrow::flight::Location::Parse(URI));
        arrow::flight::FlightServerOptions options(location);
+       if (EnableSSL)
+       {
+               arrow::flight::CertKeyPair certificate;
+               if (ssl_cert_file)
+               {
+                       std::ifstream input(ssl_cert_file);
+                       if (input)
+                       {
+                               certificate.pem_cert =
+                                       
std::string(std::istreambuf_iterator<char>{input}, {});
+                       }
+               }
+               if (ssl_key_file)
+               {
+                       std::ifstream input(ssl_key_file);
+                       if (input)
+                       {
+                               certificate.pem_key =
+                                       
std::string(std::istreambuf_iterator<char>{input}, {});
+                       }
+               }
+               if (!certificate.pem_cert.empty() && 
!certificate.pem_key.empty())
+               {
+                       
options.tls_certificates.push_back(std::move(certificate));
+               }
+       }
        options.auth_handler = 
std::make_unique<arrow::flight::NoOpAuthHandler>();
        options.middleware.push_back(
                {"header-auth", 
std::make_shared<HeaderAuthServerMiddlewareFactory>(proxy)});
        FlightSQLServer flightSQLServer(proxy);
        ARROW_RETURN_NOT_OK(flightSQLServer.Init(options));
 
+       ereport(LOG, (errmsg("listening on %s for Apache Arrow Flight SQL", 
URI)));
+
        while (!GotSIGTERM)
        {
                WaitLatch(MyLatch, WL_LATCH_SET | WL_EXIT_ON_PM_DEATH, -1, 
PG_WAIT_EXTENSION);
diff --git a/test/helper/sandbox.rb b/test/helper/sandbox.rb
index 6aec966..a320683 100644
--- a/test/helper/sandbox.rb
+++ b/test/helper/sandbox.rb
@@ -96,7 +96,7 @@ module Helper
     include CommandRunnable
 
     attr_reader :dir
-    attr_reader :host
+    attr_reader :address
     attr_reader :port
     attr_reader :flight_sql_port
     attr_reader :flight_sql_uri
@@ -107,7 +107,7 @@ module Helper
       @dir = nil
       @log_base_name = "postgresql.log"
       @log_path = nil
-      @host = "127.0.0.1"
+      @address = "127.0.0.1"
       @port = nil
       @flight_sql_port = nil
       @flight_sql_uri = nil
@@ -129,10 +129,14 @@ module Helper
       socket_dir = File.join(@dir, "socket")
       @port = port
       @pgpass = Tempfile.new("arrow-flight-sql-test-pgpass")
-      @pgpass.puts("#{@host}:#{@port}:*:#{@user}:#{@password}")
+      @pgpass.puts("#{@address}:#{@port}:*:#{@user}:#{@password}")
       @pgpass.close
       @flight_sql_port = flight_sql_port
-      @flight_sql_uri = "grpc://#{@host}:#{@flight_sql_port}"
+      if use_tls?
+        @flight_sql_uri = "grpc+tls://#{@address}:#{@flight_sql_port}"
+      else
+        @flight_sql_uri = "grpc://#{@address}:#{@flight_sql_port}"
+      end
       Tempfile.create("arrow-flight-sql-test-password") do |password|
         password.print(@password)
         password.close
@@ -143,19 +147,24 @@ module Helper
                     "--pwfile", password.path,
                     "-D", @dir)
       end
+      prepare_tls if use_tls?
       FileUtils.mkdir_p(socket_dir)
       postgresql_conf = File.join(@dir, "postgresql.conf")
       File.open(postgresql_conf, "a") do |conf|
-        conf.puts("listen_addresses = '#{@host}'")
+        conf.puts("listen_addresses = '#{@address}'")
         conf.puts("port = #{@port}")
         unless windows?
           conf.puts("unix_socket_directories = '#{socket_dir}'")
         end
+        if use_tls?
+          conf.puts("ssl = on")
+          conf.puts("ssl_ca_file = 'root.crt'")
+        end
         conf.puts("logging_collector = on")
         conf.puts("log_filename = '#{@log_base_name}'")
         conf.puts("shared_preload_libraries = " +
                   "'#{shared_preload_libraries.join(",")}'")
-        conf.puts("arrow_flight_sql.uri = #{@flight_sql_uri}")
+        conf.puts("arrow_flight_sql.uri = '#{@flight_sql_uri}'")
         yield(conf) if block_given?
       end
       pg_hba_conf = File.join(@dir, "pg_hba.conf")
@@ -197,7 +206,7 @@ module Helper
                                     "PGPASSFILE" => @pgpass.path,
                                   },
                                   "psql",
-                                  "--host", @host,
+                                  "--host", @address,
                                   "--port", @port.to_s,
                                   "--username", @user,
                                   "--dbname", db,
@@ -209,7 +218,12 @@ module Helper
     end
 
     def flight_client
-      @flight_client ||= ArrowFlight::Client.new(@flight_sql_uri)
+      @flight_client ||=
+        ArrowFlight::Client.new(@flight_sql_uri, flight_client_options)
+    end
+
+    def flight_client_options
+      @flight_client_options ||= create_flight_client_options
     end
 
     def flight_sql_client
@@ -225,6 +239,31 @@ module Helper
     def windows?
       /mingw|mswin|cygwin/.match?(RUBY_PLATFORM)
     end
+
+    def use_tls?
+      return false if windows?
+      ArrowFlight::ClientOptions.method_defined?(:tls_root_certificates=)
+    end
+
+    def create_flight_client_options
+      options = ArrowFlight::ClientOptions.new
+      if use_tls?
+        options.tls_root_certificates = File.read(File.join(@dir, "root.crt"))
+        options.override_host_name = "server.example.com"
+      end
+      options
+    end
+
+    def prepare_tls
+      prepare_tls_sh = File.join(__dir__, "..", "..", "dev", "prepare-tls.sh")
+      prepare_tls_sh = File.expand_path(prepare_tls_sh)
+      Dir.chdir(@dir) do
+        run_command(prepare_tls_sh,
+                    "root.example.com",
+                    "server.example.com",
+                    "client.example.com")
+      end
+    end
   end
 
   module Sandbox
diff --git a/test/test-flight-sql.rb b/test/test-flight-sql.rb
index 6997460..4237dd4 100644
--- a/test/test-flight-sql.rb
+++ b/test/test-flight-sql.rb
@@ -53,7 +53,7 @@ class FlightSQLTest < Test::Unit::TestCase
   end
 
   def test_isnert_int32
-    unless filght_sql_client.respond_to?(:execute_update)
+    unless flight_sql_client.respond_to?(:execute_update)
       omit("red-arrow-flight-sql 13.0.0 or later is required")
     end
 

Reply via email to