From 25215e160d46e857ee127f510755cfb0b877f405 Mon Sep 17 00:00:00 2001 From: Maksim Paskal Date: Wed, 9 Feb 2022 10:00:07 +0200 Subject: [PATCH] control egress traffic with iptables redirection (#76) Signed-off-by: Maksim Paskal --- .goreleaser.yml | 5 + Makefile | 16 +-- README.md | 11 +- .../envoy-control-plane/templates/envoy.yaml | 11 +- cmd/cli/main.go | 24 +++- cmd/gencerts/main.go | 30 +++-- envoy/Dockerfile | 8 +- envoy/envoy.defaults/envoy.yaml | 39 ++++++ envoy/scripts/prepare_proxy.sh | 76 +++++++++++ examples/.gitignore | 1 + examples/egress/Makefile | 23 ++++ examples/egress/docker-compose.yaml | 37 ++++++ examples/egress/envoy.yaml | 119 ++++++++++++++++++ examples/egress/test.yaml | 43 +++++++ pkg/certs/certs.go | 26 +++- pkg/certs/certs_test.go | 4 +- pkg/configstore/configStore.go | 6 +- pkg/controlplane/controlPlane.go | 2 +- pkg/utils/utils.go | 2 +- pkg/web/web.go | 4 +- scripts/test.yaml | 0 21 files changed, 442 insertions(+), 45 deletions(-) create mode 100755 envoy/scripts/prepare_proxy.sh create mode 100644 examples/.gitignore create mode 100644 examples/egress/Makefile create mode 100644 examples/egress/docker-compose.yaml create mode 100644 examples/egress/envoy.yaml create mode 100644 examples/egress/test.yaml mode change 100644 => 100755 scripts/test.yaml diff --git a/.goreleaser.yml b/.goreleaser.yml index d22715e..8a80f92 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -1,3 +1,8 @@ +release: + footer: | + ## Docker Images + - `paskalmaksim/envoy-control-plane:latest` + - `paskalmaksim/envoy-control-plane:{{ .Tag }}` dockers: - goos: linux goarch: amd64 diff --git a/Makefile b/Makefile index 2c7c1c1..e7cd8be 100644 --- a/Makefile +++ b/Makefile @@ -103,23 +103,23 @@ sslInit: go run ./cmd/gencerts -cert.path=certs sslTest: openssl rsa -in ./certs/CA.key -check -noout - openssl rsa -in ./certs/test.key -check -noout - openssl verify -CAfile ./certs/CA.crt ./certs/test.crt + openssl rsa -in ./certs/server.key -check -noout + openssl verify -CAfile ./certs/CA.crt ./certs/server.crt openssl verify -CAfile ./certs/CA.crt ./certs/envoy.crt - openssl x509 -in ./certs/test.crt -text + openssl x509 -in ./certs/server.crt -text openssl x509 -in ./certs/envoy.crt -text openssl x509 -pubkey -in ./certs/CA.crt -noout | openssl md5 openssl pkey -pubout -in ./certs/CA.key | openssl md5 - openssl x509 -pubkey -in ./certs/test.crt -noout | openssl md5 - openssl pkey -pubout -in ./certs/test.key | openssl md5 + openssl x509 -pubkey -in ./certs/server.crt -noout | openssl md5 + openssl pkey -pubout -in ./certs/server.key | openssl md5 sslTestClient: - curl -v --cacert ./certs/CA.crt --resolve "test2-id:8001:127.0.0.1" --key ./certs/test.key --cert ./certs/test.crt https://test2-id:8001 - curl -v --cacert ./certs/CA.crt --resolve "test3-id:8002:127.0.0.1" --key ./certs/test.key --cert ./certs/test.crt https://test3-id:8002 + curl -v --cacert ./certs/CA.crt --resolve "test2-id:8001:127.0.0.1" --key ./certs/server.key --cert ./certs/server.crt https://test2-id:8001 + curl -v --cacert ./certs/CA.crt --resolve "test3-id:8002:127.0.0.1" --key ./certs/server.key --cert ./certs/server.crt https://test3-id:8002 sslTestControlPlane: - curl -vk --http2 --cacert ./certs/CA.crt --resolve "envoy-control-plane:18080:127.0.0.1" --key ./certs/test.key --cert ./certs/test.crt https://envoy-control-plane:18080 + curl -vk --http2 --cacert ./certs/CA.crt --resolve "envoy-control-plane:18080:127.0.0.1" --key ./certs/server.key --cert ./certs/server.crt https://envoy-control-plane:18080 test-e2e: make clean k8sConfig kubectl scale deploy test-001 test-002 --replicas=${initialPodCount} diff --git a/README.md b/README.md index 06c734f..6b7b6d2 100644 --- a/README.md +++ b/README.md @@ -63,8 +63,11 @@ containers: lifecycle: preStop: exec: - # gracefully drain all connection - command: ["/bin/sh", "-c", "cli -drainEnvoy; sleep 5s; pkill -SIGTERM envoy"] + # gracefully drain all connection and shutdown + command: + - cli + - -drainEnvoy + - -timeout=10s image: paskalmaksim/envoy-docker-image:latest imagePullPolicy: Always args: @@ -98,13 +101,13 @@ containers: readinessProbe: httpGet: path: /ready - port: 18000 + port: 18001 initialDelaySeconds: 3 periodSeconds: 5 livenessProbe: httpGet: path: /server_info - port: 18000 + port: 18001 initialDelaySeconds: 60 periodSeconds: 10 diff --git a/charts/envoy-control-plane/templates/envoy.yaml b/charts/envoy-control-plane/templates/envoy.yaml index e3068eb..e70e3b2 100644 --- a/charts/envoy-control-plane/templates/envoy.yaml +++ b/charts/envoy-control-plane/templates/envoy.yaml @@ -44,8 +44,11 @@ spec: lifecycle: preStop: exec: - # gracefully drain all connection - command: ["/bin/sh", "-c", "cli -drainEnvoy; sleep 5s; pkill -SIGTERM envoy"] + # gracefully drain all connection and shutdown + command: + - cli + - -drainEnvoy + - -timeout=10s image: {{ tpl .Values.envoy.registry.image . | quote }} imagePullPolicy: {{ .Values.envoy.registry.imagePullPolicy | quote }} args: @@ -80,13 +83,13 @@ spec: readinessProbe: httpGet: path: /ready - port: 18000 + port: 18001 initialDelaySeconds: 3 periodSeconds: 5 livenessProbe: httpGet: path: /server_info - port: 18000 + port: 18001 initialDelaySeconds: 60 periodSeconds: 10 ports: diff --git a/cmd/cli/main.go b/cmd/cli/main.go index b5e8cef..265813b 100644 --- a/cmd/cli/main.go +++ b/cmd/cli/main.go @@ -30,6 +30,7 @@ import ( const ( serverPort = 18081 serverAdminPort = 18000 + defaultTimeout = 10 * time.Second ) var ( @@ -42,7 +43,9 @@ var ( port = flag.Int("port", serverPort, "controlplane port") wait = flag.Bool("wait", true, "wait controlplane") debug = flag.Bool("debug", false, "debug mode") + envoyLogLevel = flag.String("envoyLogLevel", "", "set envoy log level") drainEnvoy = flag.Bool("drainEnvoy", false, "drain envoy") + timeout = flag.Duration("timeout", defaultTimeout, "timeout to shutdown envoy") envoyAdminPort = flag.Int("envoyAdminPort", serverAdminPort, "envoy admin port") logFlags = flag.Int("logFlags", 0, "log flags") tlsInsecure = flag.Bool("tls.insecure", false, "use insecure TLS") @@ -86,7 +89,8 @@ func waitForAPI() { } } -func requestEnvoyAdmin(method string, path string) { +func requestEnvoyAdmin(path string) { + method := http.MethodPost url := fmt.Sprintf("http://127.0.0.1:%d%s", *envoyAdminPort, path) req, err := http.NewRequestWithContext(ctx, method, url, nil) @@ -146,9 +150,23 @@ func main() { }, } + if len(*envoyLogLevel) > 0 { + requestEnvoyAdmin(fmt.Sprintf("/logging?level=%s", *envoyLogLevel)) + + return + } + if *drainEnvoy { - requestEnvoyAdmin(http.MethodPost, "/drain_listeners?graceful") - requestEnvoyAdmin(http.MethodPost, "/healthcheck/fail") + // draining connections + requestEnvoyAdmin("/drain_listeners?graceful") + requestEnvoyAdmin("/healthcheck/fail") + + // wait some time + log.Printf("Waiting %s to Envoy quit", *timeout) + time.Sleep(*timeout) + + // shutdown envoy + requestEnvoyAdmin("/quitquitquit") return } diff --git a/cmd/gencerts/main.go b/cmd/gencerts/main.go index 06ad2d2..ccdd765 100644 --- a/cmd/gencerts/main.go +++ b/cmd/gencerts/main.go @@ -17,37 +17,49 @@ import ( "io/fs" "io/ioutil" "path" + "strings" "github.com/maksim-paskal/envoy-control-plane/pkg/certs" + "github.com/maksim-paskal/envoy-control-plane/pkg/config" log "github.com/sirupsen/logrus" ) func main() { certPath := flag.String("cert.path", "certs", "path to generate certificates") + dnsNames := flag.String("dns.names", "test", "dns names for server certificate") flag.Parse() files := make(map[string][]byte) - log.Info("generate certificates") + if err := certs.Init(); err != nil { + log.WithError(err).Fatal() + } + + rootCrt := certs.GetLoadedRootCert() + rootCrtBytes := certs.GetLoadedRootCertBytes() + + rootKey := certs.GetLoadedRootKey() - rootCrt, rootCrtBytes, rootKey, rootKeyBytes, err := certs.GenCARoot() + rootKeyBytes, err := certs.GetLoadedRootKeyBytes() if err != nil { - log.Fatal(err) + log.WithError(err).Fatal() } - files["CA.crt"] = rootCrtBytes - files["CA.key"] = rootKeyBytes + if len(*config.Get().SSLCrt) == 0 && len(*config.Get().SSLKey) == 0 { + files["CA.crt"] = rootCrtBytes + files["CA.key"] = rootKeyBytes + } - _, serverCrtBytes, _, serverKeyBytes, err := certs.GenServerCert("test", rootCrt, rootKey, certs.CertValidity) + _, serverCrtBytes, _, serverKeyBytes, err := certs.GenServerCert(strings.Split(*dnsNames, ","), rootCrt, rootKey, certs.CertValidityMax) //nolint:lll if err != nil { log.Fatal(err) } - files["test.crt"] = serverCrtBytes - files["test.key"] = serverKeyBytes + files["server.crt"] = serverCrtBytes + files["server.key"] = serverKeyBytes - _, envoyCrtBytes, _, envoyKeyBytes, err := certs.GenServerCert("envoy", rootCrt, rootKey, certs.CertValidityMax) + _, envoyCrtBytes, _, envoyKeyBytes, err := certs.GenServerCert([]string{"envoy"}, rootCrt, rootKey, certs.CertValidityMax) //nolint:lll if err != nil { log.Fatal(err) } diff --git a/envoy/Dockerfile b/envoy/Dockerfile index 69ac24e..662536f 100644 --- a/envoy/Dockerfile +++ b/envoy/Dockerfile @@ -4,6 +4,7 @@ ADD --chown=envoy:envoy https://github.com/maksim-paskal/go-template/releases/do ADD --chown=envoy:envoy https://github.com/maksim-paskal/jaeger-client-cpp/releases/download/v0.5.0/libjaegertracing_plugin.so /usr/local/lib/libjaegertracing_plugin.so COPY ./cli /usr/local/bin/cli +COPY ./envoy/scripts /scripts COPY ./envoy/entrypoint.sh /entrypoint.sh COPY ./envoy/envoy.defaults /envoy.defaults/ COPY ./envoy/certs /certs/ @@ -14,8 +15,11 @@ RUN touch /tmp/checksum \ && cat /tmp/checksum \ && sha256sum -c /tmp/checksum \ && rm /tmp/checksum \ -&& chmod +x /usr/local/bin/go-template /entrypoint.sh /usr/local/bin/cli \ -&& chown -R 101:101 /etc/envoy +&& chmod +x /usr/local/bin/go-template /entrypoint.sh /usr/local/bin/cli /scripts/* \ +&& chown -R 101:101 /etc/envoy \ +&& apt update; apt install -y iptables \ +&& apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false \ +&& rm -rf /var/lib/apt/lists/* USER 101 diff --git a/envoy/envoy.defaults/envoy.yaml b/envoy/envoy.defaults/envoy.yaml index 0da31e8..8cc04f2 100644 --- a/envoy/envoy.defaults/envoy.yaml +++ b/envoy/envoy.defaults/envoy.yaml @@ -34,6 +34,32 @@ dynamic_resources: cluster_name: xds_cluster static_resources: + listeners: + - name: envoy_healthcheck + address: + socket_address: + address: 0.0.0.0 + port_value: 18001 + traffic_direction: INBOUND + filter_chains: + - filters: + - name: envoy.filters.network.http_connection_manager + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + stat_prefix: envoy_healthcheck + route_config: + name: envoy_healthcheck + virtual_hosts: + - name: envoy_healthcheck + domains: + - "*" + routes: + - match: + prefix: "/" + route: + cluster: envoy_admin_cluster + http_filters: + - name: envoy.filters.http.router clusters: - name: {{ env "ENVOY_SERVICE_NAME" }} connect_timeout: 0.25s @@ -95,6 +121,19 @@ static_resources: socket_address: address: {{ env "XDS_CLUSTER_ADDRESS" }} port_value: 18080 + - name: envoy_admin_cluster + connect_timeout: 1s + type: STATIC + dns_lookup_family: V4_ONLY + load_assignment: + cluster_name: envoy_admin_cluster + endpoints: + - lb_endpoints: + - endpoint: + address: + socket_address: + address: 127.0.0.1 + port_value: 18000 admin: address: diff --git a/envoy/scripts/prepare_proxy.sh b/envoy/scripts/prepare_proxy.sh new file mode 100755 index 0000000..4caf568 --- /dev/null +++ b/envoy/scripts/prepare_proxy.sh @@ -0,0 +1,76 @@ +#!/bin/sh + +# Copyright paskal.maksim@gmail.com +# +# Licensed 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 -ex + +: ${KUBERNETES_SERVICE_HOST:='127.0.0.1'} +: ${ENVOY_PORT:='15001'} +: ${ENVOY_UID:='101'} + +iptables -t nat -N PROXY_INBOUND +iptables -t nat -N PROXY_OUTBOUND +iptables -t nat -N PROXY_REDIRECT +iptables -t nat -N PROXY_IN_REDIRECT +iptables -t nat -N PROXY_OUT_REDIRECT + +# Redirects outbound TCP traffic hitting PROXY_OUT_REDIRECT chain to Envoy's outbound listener port +iptables -t nat -A PROXY_OUT_REDIRECT -p tcp -j REDIRECT --to-port $ENVOY_PORT + +# Traffic to the Proxy Admin port flows to the Proxy -- not redirected +iptables -t nat -A PROXY_OUT_REDIRECT -p tcp --dport 18000 -j ACCEPT + +# For outbound TCP traffic jump from OUTPUT chain to PROXY_OUTBOUND chain +iptables -t nat -A OUTPUT -p tcp -j PROXY_OUTBOUND + +# Outbound traffic from Envoy to the local app over the loopback interface should jump to the inbound proxy redirect chain. +# So when an app directs traffic to itself via the k8s service, traffic flows as follows: +# app -> local envoy's outbound listener -> iptables -> local envoy's inbound listener -> app +iptables -t nat -A PROXY_OUTBOUND -o lo ! -d 127.0.0.1/32 -m owner --uid-owner $ENVOY_UID -j PROXY_IN_REDIRECT + +# Outbound traffic from the app to itself over the loopback interface is not be redirected via the proxy. +# E.g. when app sends traffic to itself via the pod IP. +iptables -t nat -A PROXY_OUTBOUND -o lo -m owner ! --uid-owner $ENVOY_UID -j RETURN + +# Don't redirect Envoy traffic back to itself, return it to the next chain for processing +iptables -t nat -A PROXY_OUTBOUND -m owner --uid-owner $ENVOY_UID -j RETURN + +# Skip localhost traffic, doesn't need to be routed via the proxy +iptables -t nat -A PROXY_OUTBOUND -d 127.0.0.1/32 -j RETURN + +# Skip traffic to kubernetes API +iptables -t nat -A PROXY_OUTBOUND -d $KUBERNETES_SERVICE_HOST/32 -j RETURN + +# Redirect remaining outbound traffic to Envoy +iptables -t nat -A PROXY_OUTBOUND -j PROXY_OUT_REDIRECT + +# Redirects inbound TCP traffic hitting the PROXY_IN_REDIRECT chain to Envoy's inbound listener port +iptables -t nat -A PROXY_IN_REDIRECT -p tcp -j REDIRECT --to-port $ENVOY_PORT + +# For inbound traffic jump from PREROUTING chain to PROXY_INBOUND chain +iptables -t nat -A PREROUTING -p tcp -j PROXY_INBOUND + +# Skip traffic to Envoy sidecar metrics being directed to Envoy +iptables -t nat -A PROXY_INBOUND -p tcp --dport 18001 -j RETURN + +# Skip traffic being directed to Envoy - this needed for POD livenessProbe, readinessProbe, startupProbe +# or application metrics for Prometheus scraping +iptables -t nat -A PROXY_INBOUND -p tcp --dport 18002 -j RETURN +iptables -t nat -A PROXY_INBOUND -p tcp --dport 18003 -j RETURN +iptables -t nat -A PROXY_INBOUND -p tcp --dport 18004 -j RETURN +iptables -t nat -A PROXY_INBOUND -p tcp --dport 18005 -j RETURN + +# Redirect remaining inbound traffic to Envoy +iptables -t nat -A PROXY_INBOUND -p tcp -j PROXY_IN_REDIRECT diff --git a/examples/.gitignore b/examples/.gitignore new file mode 100644 index 0000000..1503cc8 --- /dev/null +++ b/examples/.gitignore @@ -0,0 +1 @@ +certs \ No newline at end of file diff --git a/examples/egress/Makefile b/examples/egress/Makefile new file mode 100644 index 0000000..f5ac5ee --- /dev/null +++ b/examples/egress/Makefile @@ -0,0 +1,23 @@ +KUBECONFIG=$(HOME)/.kube/dev + +deploy: + make clean + kubectl create cm envoy-config \ + --from-file=envoy.yaml \ + --from-file=certs/server.key \ + --from-file=certs/server.crt + kubectl apply -f test.yaml +clean: + kubectl delete cm envoy-config || true + kubectl delete -f test.yaml || true +run: + docker-compose down --remove-orphans; docker-compose up +genCerts: + rm -rf certs + mkdir certs + go run ../../cmd/gencerts -dns.names=\ + get.paskal-dev.com,\ + google.com,\ + www.recaptcha.net + + openssl x509 -in ./certs/server.crt -text -noout \ No newline at end of file diff --git a/examples/egress/docker-compose.yaml b/examples/egress/docker-compose.yaml new file mode 100644 index 0000000..5811ca6 --- /dev/null +++ b/examples/egress/docker-compose.yaml @@ -0,0 +1,37 @@ +version: '3' +services: + init: + image: paskalmaksim/envoy-docker-image:dev + user: "0" + command: + - sh + - -c + - | + set -ex + + apt update; apt install -y curl + + /scripts/prepare_proxy.sh + + curl 127.0.0.1:18000/listeners + curl -v http://google.com + + curl -v --cacert /egress/CA.crt https://google.com + curl -v --cacert /egress/CA.crt https://get.paskal-dev.com + curl -v --cacert /egress/CA.crt https://www.recaptcha.net + + cli -drainEnvoy -timeout=0s + + cap_add: + - NET_ADMIN + volumes: + - ./certs:/egress:ro + envoy: + image: paskalmaksim/envoy-docker-image:dev + volumes: + - ./envoy.yaml:/envoy/envoy.yaml:ro + - ./certs/server.key:/envoy/server.key:ro + - ./certs/server.crt:/envoy/server.crt:ro + network_mode: "service:init" + depends_on: + - init diff --git a/examples/egress/envoy.yaml b/examples/egress/envoy.yaml new file mode 100644 index 0000000..9215bb9 --- /dev/null +++ b/examples/egress/envoy.yaml @@ -0,0 +1,119 @@ +layered_runtime: + layers: + - name: static_layer_0 + static_layer: + overload: + global_downstream_max_connections: 50000 + +admin: + address: + socket_address: + address: 0.0.0.0 + port_value: 18000 + +static_resources: + listeners: + - address: + socket_address: + address: 0.0.0.0 + port_value: 15001 + use_original_dst: true + traffic_direction: OUTBOUND + filter_chains: + - filters: + - name: envoy.filters.network.tcp_proxy + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy + stat_prefix: egress_blackbox + cluster: egress_blackbox + - address: + socket_address: + address: 0.0.0.0 + port_value: 80 + traffic_direction: OUTBOUND + bind_to_port: false + filter_chains: + - filters: + - name: envoy.filters.network.http_connection_manager + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + stat_prefix: egress_http + http_protocol_options: + accept_http_10: true + route_config: + name: egress_http + virtual_hosts: + - name: egress_http + domains: + - "*" + response_headers_to_add: + - header: + key: "x-test1" + value: "test1" + routes: + - match: + prefix: "/" + route: + cluster: egress_http + http_filters: + - name: envoy.filters.http.router + - address: + socket_address: + address: 0.0.0.0 + port_value: 443 + traffic_direction: OUTBOUND + bind_to_port: false + filter_chains: + - filters: + - name: envoy.filters.network.http_connection_manager + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + stat_prefix: egress_https + http_protocol_options: + accept_http_10: true + route_config: + name: egress_https + virtual_hosts: + - name: egress_https + domains: + - "*" + response_headers_to_add: + - header: + key: "x-test2" + value: "test2" + routes: + - match: + prefix: "/" + route: + cluster: egress_https + http_filters: + - name: envoy.filters.http.router + transport_socket: + name: envoy.transport_sockets.tls + typed_config: + "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext + common_tls_context: + tls_certificates: + - certificate_chain: + filename: /envoy/server.crt + private_key: + filename: /envoy/server.key + + clusters: + - name: egress_blackbox + type: ORIGINAL_DST + lb_policy: CLUSTER_PROVIDED + + - name: egress_http + type: ORIGINAL_DST + lb_policy: CLUSTER_PROVIDED + + - name: egress_https + type: ORIGINAL_DST + lb_policy: CLUSTER_PROVIDED + upstream_http_protocol_options: + auto_sni: true + transport_socket: + name: envoy.transport_sockets.tls + typed_config: + "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext \ No newline at end of file diff --git a/examples/egress/test.yaml b/examples/egress/test.yaml new file mode 100644 index 0000000..d34615c --- /dev/null +++ b/examples/egress/test.yaml @@ -0,0 +1,43 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: test +spec: + replicas: 1 + selector: + matchLabels: + app: test + template: + metadata: + labels: + app: test + spec: + volumes: + - name: envoy-config + configMap: + name: envoy-config + initContainers: + - name: envoy-init + image: paskalmaksim/envoy-docker-image:dev + imagePullPolicy: Always + securityContext: + runAsUser: 0 + capabilities: + add: + - NET_ADMIN + command: + - /scripts/prepare_proxy.sh + containers: + - name: envoy + lifecycle: + preStop: + exec: + command: + - cli + - -drainEnvoy + - -timeout=10s + image: paskalmaksim/envoy-docker-image:dev + imagePullPolicy: Always + volumeMounts: + - name: envoy-config + mountPath: /envoy \ No newline at end of file diff --git a/pkg/certs/certs.go b/pkg/certs/certs.go index e96e15e..944921f 100644 --- a/pkg/certs/certs.go +++ b/pkg/certs/certs.go @@ -88,8 +88,16 @@ func GetLoadedRootCertBytes() []byte { return caCertBytes } -func NewCertificate(dnsName string, certDuration time.Duration) (*x509.Certificate, []byte, *rsa.PrivateKey, []byte, error) { //nolint:lll - return GenServerCert(dnsName, caCert, caKey, certDuration) +func GetLoadedRootKey() *rsa.PrivateKey { + return caKey +} + +func GetLoadedRootKeyBytes() ([]byte, error) { + return exportPrivateKey(caKey) +} + +func NewCertificate(dnsNames []string, certDuration time.Duration) (*x509.Certificate, []byte, *rsa.PrivateKey, []byte, error) { //nolint:lll + return GenServerCert(dnsNames, caCert, caKey, certDuration) } func loadCAFromFiles() (*x509.Certificate, []byte, *rsa.PrivateKey, error) { @@ -117,7 +125,13 @@ func loadCAFromFiles() (*x509.Certificate, []byte, *rsa.PrivateKey, error) { return nil, nil, nil, errors.Wrap(err, "can not parse key") } - return cert, certBytes, key.(*rsa.PrivateKey), nil + privateKey, ok := key.(*rsa.PrivateKey) + + if !ok { + return nil, nil, nil, errors.New("assertion error") + } + + return cert, certBytes, privateKey, nil } func GenCARoot() (*x509.Certificate, []byte, *rsa.PrivateKey, []byte, error) { @@ -156,7 +170,7 @@ func GenCARoot() (*x509.Certificate, []byte, *rsa.PrivateKey, []byte, error) { return rootCert, rootCertBytes, priv, priBytes, nil } -func GenServerCert(dnsName string, rootCert *x509.Certificate, rootKey *rsa.PrivateKey, certDuration time.Duration) (*x509.Certificate, []byte, *rsa.PrivateKey, []byte, error) { //nolint: lll +func GenServerCert(dnsNames []string, rootCert *x509.Certificate, rootKey *rsa.PrivateKey, certDuration time.Duration) (*x509.Certificate, []byte, *rsa.PrivateKey, []byte, error) { //nolint: lll priv, err := rsa.GenerateKey(rand.Reader, keyBits) if err != nil { return nil, nil, nil, nil, errors.Wrap(err, "Failed to generate key") @@ -168,9 +182,9 @@ func GenServerCert(dnsName string, rootCert *x509.Certificate, rootKey *rsa.Priv Country: []string{"US"}, Organization: []string{config.AppName}, OrganizationalUnit: []string{"CLIENT"}, - CommonName: dnsName, + CommonName: dnsNames[0], }, - DNSNames: []string{dnsName}, + DNSNames: dnsNames, NotBefore: time.Now().Add(-10 * time.Second), NotAfter: time.Now().Add(certDuration), KeyUsage: x509.KeyUsageDigitalSignature, diff --git a/pkg/certs/certs_test.go b/pkg/certs/certs_test.go index c4289bc..328cac5 100644 --- a/pkg/certs/certs_test.go +++ b/pkg/certs/certs_test.go @@ -39,7 +39,7 @@ func TestCert(t *testing.T) { t.Fatal(err) } - serverCert, _, _, _, err := certs.GenServerCert("test", rootCert, rootKey, time.Minute) //nolint:dogsled + serverCert, _, _, _, err := certs.GenServerCert([]string{"test"}, rootCert, rootKey, time.Minute) //nolint:dogsled if err != nil { t.Fatal(err) } @@ -85,7 +85,7 @@ func TestLoadCert(t *testing.T) { t.Fatal(err) } - serverCert, _, _, _, err := certs.NewCertificate("test", time.Minute) //nolint:dogsled + serverCert, _, _, _, err := certs.NewCertificate([]string{"test"}, time.Minute) //nolint:dogsled if err != nil { t.Fatal(err) } diff --git a/pkg/configstore/configStore.go b/pkg/configstore/configStore.go index 67240ba..e45aed6 100644 --- a/pkg/configstore/configStore.go +++ b/pkg/configstore/configStore.go @@ -38,6 +38,8 @@ import ( v1 "k8s.io/api/core/v1" ) +var ctx = context.Background() + var StoreMap = new(sync.Map) type ConfigStore struct { @@ -48,7 +50,6 @@ type ConfigStore struct { lastEndpointsArray []string log *log.Entry mutex sync.Mutex - ctx context.Context secrets []tls.Secret isStoped *atomic.Bool } @@ -56,7 +57,6 @@ type ConfigStore struct { func New(config *appConfig.ConfigType) (*ConfigStore, error) { cs := ConfigStore{ Config: config, - ctx: context.Background(), isStoped: atomic.NewBool(false), log: log.WithFields(log.Fields{ "type": "ConfigStore", @@ -131,7 +131,7 @@ func (cs *ConfigStore) Push(reason string) { return } - err = controlplane.SnapshotCache.SetSnapshot(cs.ctx, cs.Config.ID, snap) + err = controlplane.SnapshotCache.SetSnapshot(ctx, cs.Config.ID, snap) if err != nil { cs.log.WithError(err).Error() diff --git a/pkg/controlplane/controlPlane.go b/pkg/controlplane/controlPlane.go index 171e27b..c9ac3cc 100644 --- a/pkg/controlplane/controlPlane.go +++ b/pkg/controlplane/controlPlane.go @@ -76,7 +76,7 @@ func Init(ctx context.Context) { } func createGrpcServer() { - serverCert, _, serverKey, _, err := certs.NewCertificate(config.AppName, certs.CertValidityMax) + serverCert, _, serverKey, _, err := certs.NewCertificate([]string{config.AppName}, certs.CertValidityMax) if err != nil { log.WithError(err).Fatal() } diff --git a/pkg/utils/utils.go b/pkg/utils/utils.go index 981344a..77711f0 100644 --- a/pkg/utils/utils.go +++ b/pkg/utils/utils.go @@ -170,7 +170,7 @@ func YamlToResources(yamlObj []interface{}, outType interface{}) ([]types.Resour } func NewSecrets(dnsName string, validation interface{}) ([]tls.Secret, error) { - _, serverCertBytes, _, serverKeyBytes, err := certs.NewCertificate(dnsName, certs.CertValidity) + _, serverCertBytes, _, serverKeyBytes, err := certs.NewCertificate([]string{dnsName}, certs.CertValidity) if err != nil { return nil, err } diff --git a/pkg/web/web.go b/pkg/web/web.go index 517cf4e..c4771e7 100644 --- a/pkg/web/web.go +++ b/pkg/web/web.go @@ -141,7 +141,7 @@ func Start() { func StartTLS() { log.Info("https.address=", *config.Get().WebHTTPSAddress) - _, serverCertBytes, _, serverKeyBytes, err := certs.NewCertificate(config.AppName, certs.CertValidityYear) + _, serverCertBytes, _, serverKeyBytes, err := certs.NewCertificate([]string{config.AppName}, certs.CertValidityYear) if err != nil { log.WithError(err).Fatal("failed to NewCertificate") } @@ -477,7 +477,7 @@ func handlerCerts(w http.ResponseWriter, r *http.Request) { certDuration = parsedDuration } - _, crtBytes, _, keyBytes, err := certs.NewCertificate(name, certDuration) + _, crtBytes, _, keyBytes, err := certs.NewCertificate([]string{name}, certDuration) if err != nil { log.WithFields(logrushooksentry.AddRequest(r)).WithError(err).Error() } diff --git a/scripts/test.yaml b/scripts/test.yaml old mode 100644 new mode 100755