Support secure transport and certificate-based authentication. (#42)
Signed-off-by: Tao He <linzhu.ht@alibaba-inc.com>
This commit is contained in:
parent
d2e35ceb47
commit
03baacf9ea
|
|
@ -39,6 +39,7 @@ jobs:
|
||||||
libssl-dev \
|
libssl-dev \
|
||||||
libz-dev \
|
libz-dev \
|
||||||
lsb-release \
|
lsb-release \
|
||||||
|
openssl \
|
||||||
screenfetch \
|
screenfetch \
|
||||||
wget
|
wget
|
||||||
|
|
||||||
|
|
@ -136,7 +137,7 @@ jobs:
|
||||||
-DBUILD_ETCD_TESTS=ON \
|
-DBUILD_ETCD_TESTS=ON \
|
||||||
-DCMAKE_C_COMPILER_LAUNCHER=ccache \
|
-DCMAKE_C_COMPILER_LAUNCHER=ccache \
|
||||||
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache
|
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache
|
||||||
make -j2
|
make -j`nproc`
|
||||||
sudo make install
|
sudo make install
|
||||||
|
|
||||||
- name: Setup tmate session
|
- name: Setup tmate session
|
||||||
|
|
@ -150,16 +151,31 @@ jobs:
|
||||||
# use etcd v3 api
|
# use etcd v3 api
|
||||||
export ETCDCTL_API="3"
|
export ETCDCTL_API="3"
|
||||||
|
|
||||||
cd build
|
rm -rf default.etcd
|
||||||
/usr/local/bin/etcd &
|
/usr/local/bin/etcd &
|
||||||
|
|
||||||
# tests without auth
|
sleep 5
|
||||||
./bin/EtcdSyncTest
|
|
||||||
./bin/EtcdTest
|
|
||||||
./bin/LockTest
|
|
||||||
./bin/WatcherTest
|
|
||||||
|
|
||||||
# tests with auth
|
# tests without auth
|
||||||
|
./build/bin/EtcdSyncTest
|
||||||
|
./build/bin/EtcdTest
|
||||||
|
./build/bin/LockTest
|
||||||
|
./build/bin/WatcherTest
|
||||||
|
|
||||||
|
killall -TERM etcd
|
||||||
|
sleep 5
|
||||||
|
|
||||||
|
- name: Authentication Test
|
||||||
|
run: |
|
||||||
|
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib
|
||||||
|
|
||||||
|
# use etcd v3 api
|
||||||
|
export ETCDCTL_API="3"
|
||||||
|
|
||||||
|
rm -rf default.etcd
|
||||||
|
/usr/local/bin/etcd &
|
||||||
|
|
||||||
|
sleep 5
|
||||||
|
|
||||||
# for etcd v3.2, v3.3
|
# for etcd v3.2, v3.3
|
||||||
if [[ "${{ matrix.etcd }}" == v3.2* ]] || [[ "${{ matrix.etcd }}" == v3.3* ]];
|
if [[ "${{ matrix.etcd }}" == v3.2* ]] || [[ "${{ matrix.etcd }}" == v3.3* ]];
|
||||||
|
|
@ -174,7 +190,7 @@ jobs:
|
||||||
|
|
||||||
/usr/local/bin/etcdctl auth enable || true
|
/usr/local/bin/etcdctl auth enable || true
|
||||||
|
|
||||||
./bin/AuthTest
|
./build/bin/AuthTest
|
||||||
|
|
||||||
# for etcd v3.2
|
# for etcd v3.2
|
||||||
if [[ "${{ matrix.etcd }}" == v3.2* ]] || [[ "${{ matrix.etcd }}" == v3.3* ]];
|
if [[ "${{ matrix.etcd }}" == v3.2* ]] || [[ "${{ matrix.etcd }}" == v3.3* ]];
|
||||||
|
|
@ -187,6 +203,36 @@ jobs:
|
||||||
/usr/local/bin/etcdctl auth disable --user="root" --password="root" || true
|
/usr/local/bin/etcdctl auth disable --user="root" --password="root" || true
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
killall -TERM etcd
|
||||||
|
sleep 5
|
||||||
|
|
||||||
|
- name: Transport Security and Authentication Test
|
||||||
|
run: |
|
||||||
|
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib
|
||||||
|
|
||||||
|
# use etcd v3 api
|
||||||
|
export ETCDCTL_API="3"
|
||||||
|
|
||||||
|
# generate CA certificates
|
||||||
|
./security-config/reset-ca.sh
|
||||||
|
./security-config/setup-ca.sh
|
||||||
|
|
||||||
|
rm -rf default.etcd
|
||||||
|
/usr/local/bin/etcd \
|
||||||
|
--cert-file security-config/certs/etcd0.example.com.crt \
|
||||||
|
--key-file security-config/private/etcd0.example.com.key \
|
||||||
|
--client-cert-auth \
|
||||||
|
--trusted-ca-file security-config/certs/ca.crt \
|
||||||
|
--advertise-client-urls=https://127.0.0.1:2379 \
|
||||||
|
--listen-client-urls=https://127.0.0.1:2379 &
|
||||||
|
|
||||||
|
sleep 5
|
||||||
|
|
||||||
|
./build/bin/SecurityChannelTest
|
||||||
|
|
||||||
|
killall -TERM etcd
|
||||||
|
sleep 5
|
||||||
|
|
||||||
- name: Check ccache
|
- name: Check ccache
|
||||||
run: |
|
run: |
|
||||||
ccache --show-stats
|
ccache --show-stats
|
||||||
|
|
|
||||||
48
README.md
48
README.md
|
|
@ -160,7 +160,9 @@ be used by default.
|
||||||
|
|
||||||
### Etcd authentication
|
### Etcd authentication
|
||||||
|
|
||||||
Etcd [v3's authentication](https://etcd.io/docs/v3.4.0/learning/design-auth-v3/) is currently
|
#### v3 authentication
|
||||||
|
|
||||||
|
Etcd [v3 authentication](https://etcd.io/docs/v3.4.0/learning/design-auth-v3/) has been
|
||||||
supported. The `Client::Client` could accept a `username` and `password` as arguments and handle
|
supported. The `Client::Client` could accept a `username` and `password` as arguments and handle
|
||||||
the authentication properly.
|
the authentication properly.
|
||||||
|
|
||||||
|
|
@ -168,6 +170,13 @@ the authentication properly.
|
||||||
etcd::Client etcd("http://127.0.0.1:2379", "root", "root");
|
etcd::Client etcd("http://127.0.0.1:2379", "root", "root");
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Or the etcd client can be constructed explictly:
|
||||||
|
|
||||||
|
```c++
|
||||||
|
etcd::Client *etcd = etcd::Client::WithUser(
|
||||||
|
"http://127.0.0.1:2379", "root", "root");
|
||||||
|
```
|
||||||
|
|
||||||
Enabling v3 authentication requires a bit more work for older versions etcd (etcd 3.2.x and etcd 3.3.x).
|
Enabling v3 authentication requires a bit more work for older versions etcd (etcd 3.2.x and etcd 3.3.x).
|
||||||
First you need to set the `ETCDCTL_API=3`, then
|
First you need to set the `ETCDCTL_API=3`, then
|
||||||
|
|
||||||
|
|
@ -189,6 +198,43 @@ printf 'root\nroot\n' | /usr/local/bin/etcdctl user add root
|
||||||
/usr/local/bin/etcdctl --user="root:root" auth disable
|
/usr/local/bin/etcdctl --user="root:root" auth disable
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### transport security
|
||||||
|
|
||||||
|
Etcd [transport security](https://etcd.io/docs/v3.4.0/op-guide/security/) and certificate based
|
||||||
|
authentication have been supported as well. The `Client::Client` could accept arguments `ca` ,
|
||||||
|
`cert` and `key` for CA cert, cert and private key files for the SSL/TLS transport and authentication.
|
||||||
|
Note that the later arguments `cert` and `key` could be empty strings or omitted if you just need
|
||||||
|
secure transport and don't enable certificate-based client authentication (using the `--client-cert-auth`
|
||||||
|
arguments when launching etcd server).
|
||||||
|
|
||||||
|
```c++
|
||||||
|
etcd::Client etcd("https://127.0.0.1:2379",
|
||||||
|
"example.rootca.cert", "example.cert", "example.key",
|
||||||
|
"round_robin");
|
||||||
|
```
|
||||||
|
|
||||||
|
Or the etcd client can be constructed explictly:
|
||||||
|
|
||||||
|
```c++
|
||||||
|
etcd::Client *etcd = etcd::Client::WithUser(
|
||||||
|
"https://127.0.0.1:2379",
|
||||||
|
"example.rootca.cert", "example.cert", "example.key");
|
||||||
|
```
|
||||||
|
|
||||||
|
Using secure transport but not certificated-based client authentication:
|
||||||
|
|
||||||
|
```c++
|
||||||
|
etcd::Client *etcd = etcd::Client::WithUser(
|
||||||
|
"https://127.0.0.1:2379", "example.rootca.cert");
|
||||||
|
```
|
||||||
|
|
||||||
|
For more details about setup about security communication between etcd server and client, please
|
||||||
|
refer to [transport security](https://etcd.io/docs/v3.4.0/op-guide/security/) in etcd documentation
|
||||||
|
and [an example](https://github.com/kelseyhightower/etcd-production-setup) about setup etcd with
|
||||||
|
transport security using openssl.
|
||||||
|
|
||||||
|
We also provide a tool [`setup-ca.sh`](./security-config/setup-ca.sh) as a helper for development and testing.
|
||||||
|
|
||||||
### Reading a value
|
### Reading a value
|
||||||
|
|
||||||
You can read a value with the ```get``` method of the clinent instance. The only parameter is the
|
You can read a value with the ```get``` method of the clinent instance. The only parameter is the
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,52 @@ namespace etcd
|
||||||
std::string const & password,
|
std::string const & password,
|
||||||
std::string const & load_balancer = "round_robin");
|
std::string const & load_balancer = "round_robin");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs an etcd client object.
|
||||||
|
*
|
||||||
|
* @param etcd_url is the url of the etcd server to connect to, like "http://127.0.0.1:2379",
|
||||||
|
* or multiple url, seperated by ',' or ';'.
|
||||||
|
* @param username username of etcd auth
|
||||||
|
* @param password password of etcd auth
|
||||||
|
* @param load_balancer is the load balance strategy, can be one of round_robin/pick_first/grpclb/xds.
|
||||||
|
*/
|
||||||
|
static etcd::Client *WithUser(std::string const & etcd_url,
|
||||||
|
std::string const & username,
|
||||||
|
std::string const & password,
|
||||||
|
std::string const & load_balancer = "round_robin");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs an etcd client object.
|
||||||
|
*
|
||||||
|
* @param etcd_url is the url of the etcd server to connect to, like "http://127.0.0.1:2379",
|
||||||
|
* or multiple url, seperated by ',' or ';'.
|
||||||
|
* @param ca root CA file for SSL/TLS connection.
|
||||||
|
* @param cert cert chain file for SSL/TLS authentication, could be empty string.
|
||||||
|
* @param key private key file for SSL/TLS authentication, could be empty string.
|
||||||
|
* @param load_balancer is the load balance strategy, can be one of round_robin/pick_first/grpclb/xds.
|
||||||
|
*/
|
||||||
|
Client(std::string const & etcd_url,
|
||||||
|
std::string const & ca,
|
||||||
|
std::string const & cert,
|
||||||
|
std::string const & key,
|
||||||
|
std::string const & load_balancer);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs an etcd client object.
|
||||||
|
*
|
||||||
|
* @param etcd_url is the url of the etcd server to connect to, like "http://127.0.0.1:2379",
|
||||||
|
* or multiple url, seperated by ',' or ';'.
|
||||||
|
* @param ca root CA file for SSL/TLS connection.
|
||||||
|
* @param cert cert chain file for SSL/TLS authentication, could be empty string.
|
||||||
|
* @param key private key file for SSL/TLS authentication, could be empty string.
|
||||||
|
* @param load_balancer is the load balance strategy, can be one of round_robin/pick_first/grpclb/xds.
|
||||||
|
*/
|
||||||
|
static etcd::Client *WithSSL(std::string const & etcd_url,
|
||||||
|
std::string const & ca,
|
||||||
|
std::string const & cert = "",
|
||||||
|
std::string const & key = "",
|
||||||
|
std::string const & load_balancer = "round_robin");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sends a get request to the etcd server
|
* Sends a get request to the etcd server
|
||||||
* @param key is the key to be read
|
* @param key is the key to be read
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
!.gitignore
|
||||||
|
!openssl.cnf
|
||||||
|
!setup-ca.sh
|
||||||
|
!reset-ca.sh
|
||||||
|
|
@ -0,0 +1,75 @@
|
||||||
|
# etcd OpenSSL configuration file.
|
||||||
|
#
|
||||||
|
# Referred from https://github.com/kelseyhightower/etcd-production-setup/blob/master/openssl.cnf
|
||||||
|
#
|
||||||
|
SAN = "IP:127.0.0.1"
|
||||||
|
dir = .
|
||||||
|
|
||||||
|
[ ca ]
|
||||||
|
default_ca = etcd_ca
|
||||||
|
|
||||||
|
[ etcd_ca ]
|
||||||
|
certs = $dir/certs
|
||||||
|
certificate = $dir/certs/etcd-ca.crt
|
||||||
|
crl = $dir/crl.pem
|
||||||
|
crl_dir = $dir/crl
|
||||||
|
crlnumber = $dir/crlnumber
|
||||||
|
database = $dir/index.txt
|
||||||
|
email_in_dn = no
|
||||||
|
new_certs_dir = $dir/newcerts
|
||||||
|
private_key = $dir/private/etcd-ca.key
|
||||||
|
serial = $dir/serial
|
||||||
|
RANDFILE = $dir/private/.rand
|
||||||
|
name_opt = ca_default
|
||||||
|
cert_opt = ca_default
|
||||||
|
default_days = 3650
|
||||||
|
default_crl_days = 30
|
||||||
|
default_md = sha512
|
||||||
|
preserve = no
|
||||||
|
policy = policy_etcd
|
||||||
|
|
||||||
|
[ policy_etcd ]
|
||||||
|
organizationName = optional
|
||||||
|
commonName = supplied
|
||||||
|
|
||||||
|
[ req ]
|
||||||
|
default_bits = 4096
|
||||||
|
default_keyfile = privkey.pem
|
||||||
|
distinguished_name = req_distinguished_name
|
||||||
|
attributes = req_attributes
|
||||||
|
x509_extensions = v3_ca
|
||||||
|
string_mask = utf8only
|
||||||
|
req_extensions = etcd_client
|
||||||
|
|
||||||
|
[ req_distinguished_name ]
|
||||||
|
countryName = Country Name (2 letter code)
|
||||||
|
countryName_default = US
|
||||||
|
countryName_min = 2
|
||||||
|
countryName_max = 2
|
||||||
|
commonName = Common Name (FQDN)
|
||||||
|
0.organizationName = Organization Name (eg, company)
|
||||||
|
0.organizationName_default = etcd-ca
|
||||||
|
|
||||||
|
[ req_attributes ]
|
||||||
|
|
||||||
|
[ v3_ca ]
|
||||||
|
basicConstraints = CA:true
|
||||||
|
keyUsage = keyCertSign,cRLSign
|
||||||
|
subjectKeyIdentifier = hash
|
||||||
|
|
||||||
|
[ etcd_client ]
|
||||||
|
basicConstraints = CA:FALSE
|
||||||
|
extendedKeyUsage = clientAuth
|
||||||
|
keyUsage = digitalSignature, keyEncipherment
|
||||||
|
|
||||||
|
[ etcd_peer ]
|
||||||
|
basicConstraints = CA:FALSE
|
||||||
|
extendedKeyUsage = clientAuth, serverAuth
|
||||||
|
keyUsage = digitalSignature, keyEncipherment
|
||||||
|
subjectAltName = ${ENV::SAN}
|
||||||
|
|
||||||
|
[ etcd_server ]
|
||||||
|
basicConstraints = CA:FALSE
|
||||||
|
extendedKeyUsage = clientAuth, serverAuth
|
||||||
|
keyUsage = digitalSignature, keyEncipherment
|
||||||
|
subjectAltName = ${ENV::SAN}
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
#!/bin/bash
|
||||||
|
#
|
||||||
|
# generate ca certificate for etcd
|
||||||
|
#
|
||||||
|
# referred from: https://github.com/kelseyhightower/etcd-production-setup
|
||||||
|
|
||||||
|
set -x
|
||||||
|
set -e
|
||||||
|
set -o pipefail
|
||||||
|
|
||||||
|
shopt -s extglob
|
||||||
|
|
||||||
|
ROOT=$(dirname "${BASH_SOURCE[0]}")
|
||||||
|
|
||||||
|
pushd $ROOT
|
||||||
|
|
||||||
|
rm -rf !(setup-ca.sh|reset-ca.sh|openssl.cnf)
|
||||||
|
|
||||||
|
popd # $ROOT
|
||||||
|
|
||||||
|
set +x
|
||||||
|
set +e
|
||||||
|
set +o pipefail
|
||||||
|
|
@ -0,0 +1,105 @@
|
||||||
|
#!/bin/bash
|
||||||
|
#
|
||||||
|
# generate ca certificate for etcd
|
||||||
|
#
|
||||||
|
# referred from: https://github.com/kelseyhightower/etcd-production-setup
|
||||||
|
|
||||||
|
set -x
|
||||||
|
set -e
|
||||||
|
set -o pipefail
|
||||||
|
|
||||||
|
ROOT=$(dirname "${BASH_SOURCE[0]}")
|
||||||
|
|
||||||
|
pushd $ROOT
|
||||||
|
|
||||||
|
touch index.txt
|
||||||
|
echo '01' > serial
|
||||||
|
|
||||||
|
mkdir -p private
|
||||||
|
mkdir -p certs
|
||||||
|
mkdir -p newcerts
|
||||||
|
|
||||||
|
# Create the CA Certificate and Key
|
||||||
|
openssl req -config ./openssl.cnf -new -x509 -extensions v3_ca \
|
||||||
|
-keyout private/ca.key -out certs/ca.crt \
|
||||||
|
-passin pass:etcd-ca -passout pass:etcd-ca \
|
||||||
|
-subj "/C=US/ST=CA/L=CA/O=etcd-ca/CN=ca.etcd.example.com/emailAddress=ca.etcd.example.com"
|
||||||
|
|
||||||
|
# Verify the CA Certificate
|
||||||
|
openssl x509 -in certs/ca.crt -noout -text
|
||||||
|
|
||||||
|
# Create an etcd server certificate
|
||||||
|
# If you want cert verification to work with IPs in addition to hostnames, be sure to set the SAN env var:
|
||||||
|
# export SAN="IP:127.0.0.1, IP:10.0.1.10"
|
||||||
|
export SAN="IP:127.0.0.1"
|
||||||
|
|
||||||
|
openssl req -config openssl.cnf -new -nodes \
|
||||||
|
-keyout private/etcd0.example.com.key -out etcd0.example.com.csr \
|
||||||
|
-subj "/C=US/ST=CA/L=CA/O=etcd-ca/CN=etcd0.example.com/emailAddress=ca.etcd.example.com"
|
||||||
|
|
||||||
|
# Sign the cert
|
||||||
|
openssl ca -batch -config openssl.cnf -extensions etcd_server \
|
||||||
|
-passin pass:etcd-ca \
|
||||||
|
-keyfile private/ca.key \
|
||||||
|
-cert certs/ca.crt \
|
||||||
|
-out certs/etcd0.example.com.crt -infiles etcd0.example.com.csr
|
||||||
|
|
||||||
|
# Verify the etcd Server Certificate
|
||||||
|
openssl x509 -in certs/etcd0.example.com.crt -noout -text
|
||||||
|
|
||||||
|
# Create an etcd client certificate
|
||||||
|
unset SAN
|
||||||
|
|
||||||
|
openssl req -config openssl.cnf -new -nodes \
|
||||||
|
-keyout private/etcd-client.key -out etcd-client.csr \
|
||||||
|
-subj "/C=US/ST=CA/L=CA/O=etcd-ca/CN=etcd_client/emailAddress=ca.etcd.example.com"
|
||||||
|
|
||||||
|
openssl ca -batch -config openssl.cnf -extensions etcd_client \
|
||||||
|
-passin pass:etcd-ca \
|
||||||
|
-keyfile private/ca.key \
|
||||||
|
-cert certs/ca.crt \
|
||||||
|
-out certs/etcd-client.crt -infiles etcd-client.csr
|
||||||
|
|
||||||
|
|
||||||
|
# Configuring etcd for SSL
|
||||||
|
|
||||||
|
# Configure etcd
|
||||||
|
|
||||||
|
# $ etcd --advertise-client-urls https://etcd0.example.com:2379 \
|
||||||
|
# --listen-client-urls https://10.0.1.10:2379 \
|
||||||
|
# --cert-file etcd0.example.com.crt \
|
||||||
|
# --key-file etcd0.example.com.key
|
||||||
|
|
||||||
|
# Configuring etcd clients for SSL
|
||||||
|
|
||||||
|
# cURL
|
||||||
|
# $ curl --cacert ca.crt -XPUT -v https://etcd0.example.com:2379/v2/keys/foo -d value=bar
|
||||||
|
# $ curl --cacert ca.crt -v https://etcd0.example.com:2379/v2/keys
|
||||||
|
|
||||||
|
# etcdctl
|
||||||
|
# $ etcdctl -C https://etcd0.example.com:2379 --ca-file ca.crt set foo bar
|
||||||
|
# $ etcdctl -C https://etcd0.example.com:2379 --ca-file ca.crt get foo
|
||||||
|
|
||||||
|
# Configuring etcd for client auth
|
||||||
|
# $ etcd --advertise-client-urls https://etcd0.example.com:2379 \
|
||||||
|
# --listen-client-urls https://10.0.1.10:2379 \
|
||||||
|
# --cert-file etcd0.example.com.crt \
|
||||||
|
# --key-file etcd0.example.com.key \
|
||||||
|
# --client-cert-auth --trusted-ca-file ca.crt \
|
||||||
|
#
|
||||||
|
# Notice the usage of the `--client-cert-auth` and `--trusted-ca-file` flag. This is what enables client auth.
|
||||||
|
|
||||||
|
# Configuring etcd clients for client auth
|
||||||
|
|
||||||
|
# etcdctl
|
||||||
|
# $ etcdctl -C https://etcd0.example.com:2379 \
|
||||||
|
# --cert etcd-client.crt \
|
||||||
|
# --key etcd-client.key \
|
||||||
|
# --cacert ca.crt \
|
||||||
|
# get foo
|
||||||
|
|
||||||
|
popd # $ROOT
|
||||||
|
|
||||||
|
set +x
|
||||||
|
set +e
|
||||||
|
set +o pipefail
|
||||||
|
|
@ -11,12 +11,14 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <fstream>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
#include <boost/algorithm/string.hpp>
|
#include <boost/algorithm/string.hpp>
|
||||||
|
|
||||||
#include <grpc++/grpc++.h>
|
#include <grpc++/grpc++.h>
|
||||||
|
#include <grpc++/security/credentials.h>
|
||||||
#include "proto/rpc.grpc.pb.h"
|
#include "proto/rpc.grpc.pb.h"
|
||||||
#include "proto/v3lock.grpc.pb.h"
|
#include "proto/v3lock.grpc.pb.h"
|
||||||
|
|
||||||
|
|
@ -130,6 +132,27 @@ const bool authenticate(std::shared_ptr<grpc::Channel> const &channel,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static std::string read_from_file(std::string const &filename) {
|
||||||
|
std::ifstream file(filename.c_str(), std::ios::in);
|
||||||
|
if (file.is_open()) {
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << file.rdbuf ();
|
||||||
|
file.close ();
|
||||||
|
return ss.str ();
|
||||||
|
}
|
||||||
|
return std::string{};
|
||||||
|
}
|
||||||
|
|
||||||
|
static grpc::SslCredentialsOptions make_ssl_credentials(std::string const &ca,
|
||||||
|
std::string const &cert,
|
||||||
|
std::string const &key) {
|
||||||
|
grpc::SslCredentialsOptions options;
|
||||||
|
options.pem_root_certs = read_from_file(ca);
|
||||||
|
options.pem_cert_chain = read_from_file(cert);
|
||||||
|
options.pem_private_key = read_from_file(key);
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -195,6 +218,47 @@ etcd::Client::Client(std::string const & address,
|
||||||
stubs->lockServiceStub = Lock::NewStub(this->channel);
|
stubs->lockServiceStub = Lock::NewStub(this->channel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
etcd::Client *etcd::Client::WithUser(std::string const & etcd_url,
|
||||||
|
std::string const & username,
|
||||||
|
std::string const & password,
|
||||||
|
std::string const & load_balancer) {
|
||||||
|
return new etcd::Client(etcd_url, username, password, load_balancer);
|
||||||
|
}
|
||||||
|
|
||||||
|
etcd::Client::Client(std::string const & address,
|
||||||
|
std::string const & ca,
|
||||||
|
std::string const & cert,
|
||||||
|
std::string const & key,
|
||||||
|
std::string const & load_balancer)
|
||||||
|
{
|
||||||
|
// create channels
|
||||||
|
std::string const addresses = etcd::detail::strip_and_resolve_addresses(address);
|
||||||
|
grpc::ChannelArguments grpc_args;
|
||||||
|
grpc_args.SetMaxSendMessageSize(std::numeric_limits<int>::max());
|
||||||
|
grpc_args.SetMaxReceiveMessageSize(std::numeric_limits<int>::max());
|
||||||
|
std::shared_ptr<grpc::ChannelCredentials> creds = grpc::SslCredentials(
|
||||||
|
etcd::detail::make_ssl_credentials(ca, cert, key));
|
||||||
|
grpc_args.SetLoadBalancingPolicyName(load_balancer);
|
||||||
|
this->channel = grpc::CreateCustomChannel(addresses, creds, grpc_args);
|
||||||
|
|
||||||
|
std::cout << "this->channel : " << this->channel;
|
||||||
|
|
||||||
|
// setup stubs
|
||||||
|
stubs.reset(new EtcdServerStubs{});
|
||||||
|
stubs->kvServiceStub = KV::NewStub(this->channel);
|
||||||
|
stubs->watchServiceStub= Watch::NewStub(this->channel);
|
||||||
|
stubs->leaseServiceStub= Lease::NewStub(this->channel);
|
||||||
|
stubs->lockServiceStub = Lock::NewStub(this->channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
etcd::Client *etcd::Client::WithSSL(std::string const & etcd_url,
|
||||||
|
std::string const & ca,
|
||||||
|
std::string const & cert,
|
||||||
|
std::string const & key,
|
||||||
|
std::string const & load_balancer) {
|
||||||
|
return new etcd::Client(etcd_url, ca, cert, key, load_balancer);
|
||||||
|
}
|
||||||
|
|
||||||
pplx::task<etcd::Response> etcd::Client::get(std::string const & key)
|
pplx::task<etcd::Response> etcd::Client::get(std::string const & key)
|
||||||
{
|
{
|
||||||
etcdv3::ActionParameters params;
|
etcdv3::ActionParameters params;
|
||||||
|
|
|
||||||
|
|
@ -8,15 +8,15 @@
|
||||||
|
|
||||||
TEST_CASE("setup with auth")
|
TEST_CASE("setup with auth")
|
||||||
{
|
{
|
||||||
etcd::Client etcd("http://127.0.0.1:2379", "root", "root");
|
etcd::Client *etcd = etcd::Client::WithUser("http://127.0.0.1:2379", "root", "root");
|
||||||
etcd.rmdir("/test", true).wait();
|
etcd->rmdir("/test", true).wait();
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("add a new key after authenticate")
|
TEST_CASE("add a new key after authenticate")
|
||||||
{
|
{
|
||||||
etcd::Client etcd("http://127.0.0.1:2379", "root", "root");
|
etcd::Client *etcd = etcd::Client::WithUser("http://127.0.0.1:2379", "root", "root");
|
||||||
etcd.rmdir("/test", true).wait();
|
etcd->rmdir("/test", true).wait();
|
||||||
etcd::Response resp = etcd.add("/test/key1", "42").get();
|
etcd::Response resp = etcd->add("/test/key1", "42").get();
|
||||||
REQUIRE(0 == resp.error_code());
|
REQUIRE(0 == resp.error_code());
|
||||||
CHECK("create" == resp.action());
|
CHECK("create" == resp.action());
|
||||||
etcd::Value const & val = resp.value();
|
etcd::Value const & val = resp.value();
|
||||||
|
|
@ -26,24 +26,24 @@ TEST_CASE("add a new key after authenticate")
|
||||||
CHECK(0 < val.created_index());
|
CHECK(0 < val.created_index());
|
||||||
CHECK(0 < val.modified_index());
|
CHECK(0 < val.modified_index());
|
||||||
CHECK(0 < resp.index());
|
CHECK(0 < resp.index());
|
||||||
CHECK(105 == etcd.add("/test/key1", "43").get().error_code()); // Key already exists
|
CHECK(105 == etcd->add("/test/key1", "43").get().error_code()); // Key already exists
|
||||||
CHECK(105 == etcd.add("/test/key1", "42").get().error_code()); // Key already exists
|
CHECK(105 == etcd->add("/test/key1", "42").get().error_code()); // Key already exists
|
||||||
CHECK("Key already exists" == etcd.add("/test/key1", "42").get().error_message());
|
CHECK("Key already exists" == etcd->add("/test/key1", "42").get().error_message());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("read a value from etcd")
|
TEST_CASE("read a value from etcd")
|
||||||
{
|
{
|
||||||
etcd::Client etcd("http://127.0.0.1:2379", "root", "root");
|
etcd::Client *etcd = etcd::Client::WithUser("http://127.0.0.1:2379", "root", "root");
|
||||||
etcd::Response resp = etcd.get("/test/key1").get();
|
etcd::Response resp = etcd->get("/test/key1").get();
|
||||||
CHECK("get" == resp.action());
|
CHECK("get" == resp.action());
|
||||||
REQUIRE(resp.is_ok());
|
REQUIRE(resp.is_ok());
|
||||||
REQUIRE(0 == resp.error_code());
|
REQUIRE(0 == resp.error_code());
|
||||||
CHECK("42" == resp.value().as_string());
|
CHECK("42" == resp.value().as_string());
|
||||||
CHECK("" == etcd.get("/test").get().value().as_string()); // key points to a directory
|
CHECK("" == etcd->get("/test").get().value().as_string()); // key points to a directory
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("cleanup")
|
TEST_CASE("cleanup")
|
||||||
{
|
{
|
||||||
etcd::Client etcd("http://127.0.0.1:2379", "root", "root");
|
etcd::Client *etcd = etcd::Client::WithUser("http://127.0.0.1:2379", "root", "root");
|
||||||
REQUIRE(0 == etcd.rmdir("/test", true).get().error_code());
|
REQUIRE(0 == etcd->rmdir("/test", true).get().error_code());
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,6 @@ TEST_CASE("sync operations")
|
||||||
etcd::SyncClient etcd(etcd_uri);
|
etcd::SyncClient etcd(etcd_uri);
|
||||||
etcd.rmdir("/test", true);
|
etcd.rmdir("/test", true);
|
||||||
|
|
||||||
|
|
||||||
// add
|
// add
|
||||||
CHECK(0 == etcd.add("/test/key1", "42").error_code());
|
CHECK(0 == etcd.add("/test/key1", "42").error_code());
|
||||||
CHECK(105 == etcd.add("/test/key1", "42").error_code()); // Key already exists
|
CHECK(105 == etcd.add("/test/key1", "42").error_code()); // Key already exists
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,52 @@
|
||||||
|
#define CATCH_CONFIG_MAIN
|
||||||
|
#include <catch.hpp>
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#include "etcd/Client.hpp"
|
||||||
|
|
||||||
|
static std::string ca = "security-config/certs/ca.crt";
|
||||||
|
static std::string cert = "security-config/certs/etcd0.example.com.crt";
|
||||||
|
static std::string key = "security-config/private/etcd0.example.com.key";
|
||||||
|
|
||||||
|
TEST_CASE("setup with auth")
|
||||||
|
{
|
||||||
|
etcd::Client *etcd = etcd::Client::WithSSL("https://127.0.0.1:2379", ca, cert, key);
|
||||||
|
etcd->rmdir("/test", true).wait();
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("add a new key after authenticate")
|
||||||
|
{
|
||||||
|
etcd::Client *etcd = etcd::Client::WithSSL("https://127.0.0.1:2379", ca, cert, key);
|
||||||
|
etcd->rmdir("/test", true).wait();
|
||||||
|
etcd::Response resp = etcd->add("/test/key1", "42").get();
|
||||||
|
REQUIRE(0 == resp.error_code());
|
||||||
|
CHECK("create" == resp.action());
|
||||||
|
etcd::Value const & val = resp.value();
|
||||||
|
CHECK("42" == val.as_string());
|
||||||
|
CHECK("/test/key1" == val.key());
|
||||||
|
CHECK(!val.is_dir());
|
||||||
|
CHECK(0 < val.created_index());
|
||||||
|
CHECK(0 < val.modified_index());
|
||||||
|
CHECK(0 < resp.index());
|
||||||
|
CHECK(105 == etcd->add("/test/key1", "43").get().error_code()); // Key already exists
|
||||||
|
CHECK(105 == etcd->add("/test/key1", "42").get().error_code()); // Key already exists
|
||||||
|
CHECK("Key already exists" == etcd->add("/test/key1", "42").get().error_message());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("read a value from etcd")
|
||||||
|
{
|
||||||
|
etcd::Client *etcd = etcd::Client::WithSSL("https://127.0.0.1:2379", ca, cert, key);
|
||||||
|
etcd::Response resp = etcd->get("/test/key1").get();
|
||||||
|
CHECK("get" == resp.action());
|
||||||
|
REQUIRE(resp.is_ok());
|
||||||
|
REQUIRE(0 == resp.error_code());
|
||||||
|
CHECK("42" == resp.value().as_string());
|
||||||
|
CHECK("" == etcd->get("/test").get().value().as_string()); // key points to a directory
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("cleanup")
|
||||||
|
{
|
||||||
|
etcd::Client *etcd = etcd::Client::WithSSL("https://127.0.0.1:2379", ca, cert, key);
|
||||||
|
REQUIRE(0 == etcd->rmdir("/test", true).get().error_code());
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue