From f0e0d526315c433131f4ae2f811ae831716be3d3 Mon Sep 17 00:00:00 2001 From: Jim Wang Date: Mon, 8 Jun 2020 18:31:11 -0700 Subject: [PATCH] Documentation of How To Guide on SSH Tunneling of remote device services Closes: #134 References: - https://wiki.edgexfoundry.org/download/attachments/37912800/EdgeX%20Security%20WG%20Options%20for%20Remote%20Device%20Services.pptx Signed-off-by: Jim Wang --- ...SSH-Tunneling-HowToSecureDeviceServices.md | 185 ++++++ .../security/Dockerfile-primary-ds-proxy | 33 + .../security/Dockerfile-remote-sshd | 15 + docs_src/microservices/security/Vagrantfile | 76 +++ .../security/ds-proxy-entrypoint.sh | 54 ++ .../security/edgex-core-ssh-proxy.yml | 585 ++++++++++++++++++ .../security/edgex-device-sshd-remote.yml | 507 +++++++++++++++ .../security/ssh-tunneling device.png | Bin 0 -> 75904 bytes 8 files changed, 1455 insertions(+) create mode 100644 docs_src/microservices/security/Ch-SSH-Tunneling-HowToSecureDeviceServices.md create mode 100644 docs_src/microservices/security/Dockerfile-primary-ds-proxy create mode 100644 docs_src/microservices/security/Dockerfile-remote-sshd create mode 100644 docs_src/microservices/security/Vagrantfile create mode 100644 docs_src/microservices/security/ds-proxy-entrypoint.sh create mode 100644 docs_src/microservices/security/edgex-core-ssh-proxy.yml create mode 100644 docs_src/microservices/security/edgex-device-sshd-remote.yml create mode 100644 docs_src/microservices/security/ssh-tunneling device.png diff --git a/docs_src/microservices/security/Ch-SSH-Tunneling-HowToSecureDeviceServices.md b/docs_src/microservices/security/Ch-SSH-Tunneling-HowToSecureDeviceServices.md new file mode 100644 index 0000000000..a04c21eb3f --- /dev/null +++ b/docs_src/microservices/security/Ch-SSH-Tunneling-HowToSecureDeviceServices.md @@ -0,0 +1,185 @@ +# Security for EdgeX Stack + +This page describes one of options to secure the EdgeX software stack with running remote device services like device-virtual, device-rest, device-mqtt, and so on, via secure two-way SSH-tunnelings. + +## Basic SSH-Tunneling + +In this option to secure the EdgeX software stack, SSH tunneling is utilized. The basic idea is to create a secure SSH connection between a local machine, the primary host, and a remote machine, the secondary, in which some micro-services or applications can be relayed. In this particular example, the primary host is running the whole EdgeX core services including core services and security services but **without** any device service. The device services are running in the secondary or the remote machine. + +The communication is secure because SSH port forwarding connection is encrypted by default. + +The SSH communication is established by introducing some extra SSH-related services: + +1) device-ssh-proxy: this is the service with ssh client opening up the SSH communication between the primary and the secondary + +2) device-ssh-remote: this is actually the SSH server or daemon service together with device services running on the remote machine + +The high-level diagram is shown as follows: + +![image](ssh-tunneling device.png) "Top level diagram for SSH tunneling for device services" + + more description needed + +## How to- Reference implementation example + +### Setup remote running Virtual Machine + +In the example setup, `vagrant` is used on the top of `Virtual Box` to set up as the secondary/remote VM. The network port for ssh standard port 22 is mapped into the vm port 2223 and host port also 2223. + +Once you have downloaded the vagrant from Hashicorp website, typical vagrant setup for the first time can be done via command `./vagrant init` and it will generate the Vagrant configuration file. + +Here is the Vagrant file used to create the remote machine: + +[Vagrantfile]: Vagrantfile "remote VM Vagrant file with docker and docker-compose installed" + +### SSH Tunneling: Setup the SSH server on the remote machine + +Use the example from Docker hub, an example of ssh damenon or ssh server can be setup pretty easily as a Docker container: https://docs.docker.com/engine/examples/running_ssh_service/ + +Note that this one is the ssh server and it is set up using password authentication by default. In order to authenticate to this ssh server without password prompt, we injected the generated public SSH key from the primary machine via simple login into the ssh server machine first and then created the `authorized_keys` under `~/.ssh` directory. Without loss of generality, the following command shows how this is accomplished: + +```sh +root@sshd-remote: mkdir -p ~/.ssh +root@sshd-remote: chmod 700 ~/.ssh +root@sshd-remote: echo "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDKvsFf5HocBOBWXdVJKfQzkhf0K8lSLjZn9PX84VdhHyP8n1mzfpZywA4vsz8+A3OsGHAr2xpkyzOS0YkwD7nrI3q1x0A0+ANhQNOaKbnfQRepTAES3FPm5n0AbNVfgOre3RR2NLOt6M5m3mA/MERNer1fEp6BM96sdU0o3KjqwFGkPufoQrVkpz2691MZ6/ACDc+lk7uQrinsB4YxM7ctiLNl4I1A3TJgVv0jkJImUCHaThYj3XoaqUqUjQFTS7SlFfkXuk13EjNfRzqPwKFnVvGTUaYzaBV5S4wt5XCxhLfs497M2k5zmNx3HFY/GEyeoroCpjsiXkm+HcgdIYb7 root" >> ~/.ssh/authorized_keys +``` + +The ssh key pairs can be generated using `ssh-keygen` command from the primary machine and the contents of ssh public key usually is stored as ~/.ssh/id_rsa.pub file like this: + +```sh +ssh-keygen -q -t rsa -C root -N '' -f ~/.ssh/id_rsa 2>/dev/null +``` + + +### SSH Tunneling: Local Port Forwarding + +from primary to secondary /remote +it is achieved by -L flags of ssh command. + +```sh +ssh -vv -o StrictHostKeyChecking=no -N $TUNNEL_HOST \ + -L *:$LOCAL_PORT:$REMOTE_HOST:$REMOTE_PORT -p $SSH_PROT +``` + +where environment variables are: + +- TUNNEL_HOST is the remote host name or IP address that SSH daemon or server is running on; + +- LOCAL_PORT is the port number from the local or the primary to be forwared to the remote machine; + +- REMOTE_HOST is the host name or IP address of the Docker containers that are running on the remote machine; + +- REMOTE_PORT is the port number to be used on the remote machine that is forwarded from the primary machine in the SSH tunneling + +### SSH Reverse Tunneling: Remote Port Forwarding + +the reverse direction: from the secondary /remote back to the primary + +The reverse SSH tunneling is also needed because the device services depends on the core services like `data`, `metadata`, `command`, ... and so on. These core services are running on the primray machine and should be **reversely** tunneling back to the device services on the remote side through the SSH remote port forwarding connection. This can be achieved by using `-R` flag of ssh command. + +```sh +ssh -vv -o StrictHostKeyChecking=no -N $TUNNEL_HOST \ + -R 48080:$REVERSE_HOST:48080 \ + -R 48081:$REVERSE_HOST:48081 \ + -R 48082:$REVERSE_HOST:48082 \ + -R 5563:$REVERSE_HOST:5563 \ + -p $SSH_PORT +``` + +where environment variables are: + +- TUNNEL_HOST is the remote host name or IP address that SSH daemon or server is running on; + +- REVERSE_HOST is the host name or IP address of the Docker containers that are running on the primary; it is basically the gateway host name or IP address of the ssh-device-proxy container; + +### Put it all together + +- Launch the remote machine or VM if it is not yet: + +```sh +~/vm/vagrant up +``` + +- In the primary machine, generate ssh key pairs using ssh-keygen: + +```sh +ssh-keygen -q -t rsa -C root -N '' -f ~/.ssh/id_rsa 2>/dev/null +``` + +This produces two files under directory ~/.ssh: one for private key (id_rsa) and one for public key (id_rsa.pub) + +- Build ssh-device-proxy Docker file and entrypoint.sh: + +[Dockerfile]: Dockerfile-primary-ds-proxy "primary device service proxy Dockerfile" + +[docker-entrypoint]: ds-proxy-entrypoint.sh "Docker entrypoint shell script for primary device service proxy" + +and build it with the following command: + +```sh +docker build -f Dockerfile-primary-ds-proxy --build-arg SSH_PORT=2223 -t device-ssh-proxy:test . +``` + +- Build the remote sshd server / daemon image with Dockerfile: + +[Dockerfile]: Dockerfile-remote-sshd "remote sshd Dockerfile" + +to build: + +```sh +docker build -t eg_sshd . +``` + +- Run the remote EdgeX device services with the following docker-compose file: + +[composefile]: edgex-device-sshd-remote.yml "docker-compose file for remote device services with SSH server/daemon" + +Note that the following ssh server service is added in the docker-compose file: + +```yaml +################################################################ +# SSH Daemon +################################################################ + sshd-remote: + image: eg_sshd + ports: + - "2223:22" + container_name: edgex-sshd-remote + hostname: edgex-sshd-remote + networks: + - edgex-network +``` + +- Copy the contents of the public key into the ~/.ssh/ directory as `~/.ssh/authorized_keys` in the `sshd-remote` container so that authentication between primary machine and remote container `sshd-remote` can be authenticated automatically. + +- In the primary machine, include `device-ssh-proxy:test` ssh proxy docker image together with EdgeX core services in the docker-compose file like this: + +```yaml +########################################################## +# ssh tunneling proxy service +########################################################## + device-ssh-proxy: + image: device-ssh-proxy:test + volumes: + - $HOME/.ssh:/root/ssh:ro + environment: + TUNNEL_HOST: 192.168.1.190 + LOCAL_HOST: 172.17.0.1 + REMOTE_HOST: 192.168.64.1 + LOCAL_PORT: 49986 + REMOTE_PORT: 49986 + SSH_PORT: 2223 +``` + +The full docker-compose file is included here: +[composefile]: edgex-core-ssh-proxy.yml "docker-compose file for the primary core services and ssh tunneling proxy service without any device services" + +Note that: + +1. The values of environment variables depend on your environment settings of the primary and the remote machine. In this particular case, we are ssh tunneling to the remote device-rest service. + +2. The docker-compose file in the primary machine does not include any device services at all. This is to ensure that we are actually using the device services in the remote machine. + +#### Test with the device-rest API + + mainly run curl or postman directly from the primary machine to the device-rest APIs to verify rest device service can be accessible via two-way SSH tunneling. \ No newline at end of file diff --git a/docs_src/microservices/security/Dockerfile-primary-ds-proxy b/docs_src/microservices/security/Dockerfile-primary-ds-proxy new file mode 100644 index 0000000000..b4939ffdab --- /dev/null +++ b/docs_src/microservices/security/Dockerfile-primary-ds-proxy @@ -0,0 +1,33 @@ +FROM alpine:latest + +ARG SSH_VERSION + +# tunneling host name or ip +ARG TUNNEL_HOST + +# the local hostname / IP of container for Remote port forwarding +ARG LOCAL_HOST + +# local port +ARG LOCAL_PORT + +# remote sshd host name or ip address +ARG REMOTE_HOST + +# remote sshd container port +ARG REMOTE_PORT + +# ssh port in use, set this number if it is not the usually port 22 +# or there is a different ssh port mapping between local and remote +ARG SSH_PORT + +RUN apk add --update dumb-init openssh-client && rm -rf /var/cache/apk/* + +COPY entrypoint.sh /usr/local/bin/ +RUN chmod +x /usr/local/bin/entrypoint.sh \ + && ln -s /usr/local/bin/entrypoint.sh / + +ENV APP_PORT=49990 +EXPOSE $APP_PORT $LOCAL_PORT $REMOTE_PORT $SSH_PORT + +ENTRYPOINT ["entrypoint.sh"] diff --git a/docs_src/microservices/security/Dockerfile-remote-sshd b/docs_src/microservices/security/Dockerfile-remote-sshd new file mode 100644 index 0000000000..3a5b186ab6 --- /dev/null +++ b/docs_src/microservices/security/Dockerfile-remote-sshd @@ -0,0 +1,15 @@ +FROM ubuntu:16.04 + +RUN apt-get update && apt-get install -y openssh-server +RUN mkdir /var/run/sshd +RUN echo 'root:THEPASSWORDYOUCREATED' | chpasswd +RUN sed -i 's/PermitRootLogin prohibit-password/PermitRootLogin yes/' /etc/ssh/sshd_config + +# SSH login fix. Otherwise user is kicked off after login +RUN sed 's@session\s*required\s*pam_loginuid.so@session optional pam_loginuid.so@g' -i /etc/pam.d/sshd + +ENV NOTVISIBLE "in users profile" +RUN echo "export VISIBLE=now" >> /etc/profile + +EXPOSE 22 +CMD ["/usr/sbin/sshd", "-D"] diff --git a/docs_src/microservices/security/Vagrantfile b/docs_src/microservices/security/Vagrantfile new file mode 100644 index 0000000000..380fca974a --- /dev/null +++ b/docs_src/microservices/security/Vagrantfile @@ -0,0 +1,76 @@ +# -*- mode: ruby -*- +# vi: set ft=ruby : + +# All Vagrant configuration is done below. The "2" in Vagrant.configure +# configures the configuration version (we support older styles for +# backwards compatibility). Please don't change it unless you know what +# you're doing. +Vagrant.configure("2") do |config| + # The most common configuration options are documented and commented below. + # For a complete reference, please see the online documentation at + # https://docs.vagrantup.com. + + # Every Vagrant development environment requires a box. You can search for + # boxes at https://vagrantcloud.com/search. + config.vm.box = "ubuntu/bionic64" + + # Disable automatic box update checking. If you disable this, then + # boxes will only be checked for updates when the user runs + # `vagrant box outdated`. This is not recommended. + # config.vm.box_check_update = false + + # Create a forwarded port mapping which allows access to a specific port + # within the machine from a port on the host machine. In the example below, + # accessing "localhost:8080" will access port 80 on the guest machine. + # NOTE: This will enable public access to the opened port + # config.vm.network "forwarded_port", guest: 80, host: 8080 + + # Create a forwarded port mapping which allows access to a specific port + # within the machine from a port on the host machine and only allow access + # via 127.0.0.1 to disable public access + # config.vm.network "forwarded_port", guest: 80, host: 8080, host_ip: "127.0.0.1" + + # Create a private network, which allows host-only access to the machine + # using a specific IP. + # config.vm.network "private_network", ip: "192.168.33.10" + + # Create a public network, which generally matched to bridged network. + # Bridged networks make the machine appear as another physical device on + # your network. + config.vm.network "public_network" + + # Share an additional folder to the guest VM. The first argument is + # the path on the host to the actual folder. The second argument is + # the path on the guest to mount the folder. And the optional third + # argument is a set of non-required options. + # config.vm.synced_folder "../data", "/vagrant_data" + + # Provider-specific configuration so you can fine-tune various + # backing providers for Vagrant. These expose provider-specific options. + # Example for VirtualBox: + # + config.vm.provider "virtualbox" do |vb| + # # Display the VirtualBox GUI when booting the machine + vb.gui = true + # + # # Customize the amount of memory on the VM: + # vb.memory = "1024" + end + # + # View the documentation for the provider you are using for more + # information on available options. + + # Enable provisioning with a shell script. Additional provisioners such as + # Ansible, Chef, Docker, Puppet and Salt are also available. Please see the + # documentation for more information about their specific syntax and use. + config.vm.provision "shell", inline: <<-SHELL + # Install last version of Docker + curl -fsSL https://test.docker.com -o test-docker.sh + sh test-docker.sh # helper script installs the beta package # Add default user in docker group + usermod -aG docker vagrant + # Install docker-compose package + curl -L https://github.com/docker/compose/releases/download/1.25.5/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose + chmod +x /usr/local/bin/docker-compose + docker-compose --version + SHELL +end diff --git a/docs_src/microservices/security/ds-proxy-entrypoint.sh b/docs_src/microservices/security/ds-proxy-entrypoint.sh new file mode 100644 index 0000000000..d2aa1f9c9a --- /dev/null +++ b/docs_src/microservices/security/ds-proxy-entrypoint.sh @@ -0,0 +1,54 @@ +#!/usr/bin/dumb-init /bin/sh +# ---------------------------------------------------------------------------------- +# Copyright (c) 2020 Intel Corporation +# +# 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. +# +# SPDX-License-Identifier: Apache-2.0' +# ---------------------------------------------------------------------------------- + +set -e + +# Use dumb-init as PID 1 in order to reap zombie processes and forward system signals to +# all processes in its session. This can alleviate the chance of leaking zombies, +# thus more graceful termination of all sub-processes if any. + +# runtime directory is set per user: +XDG_RUNTIME_DIR=${XDG_RUNTIME_DIR:-/run/user/$(echo $(id -u))} +export XDG_RUNTIME_DIR + +# debug output: +echo XDG_RUNTIME_DIR $XDG_RUNTIME_DIR + +# use static ssh key +rm -rf /root/.ssh && mkdir /root/.ssh \ +&& cp -R /root/ssh/* /root/.ssh/ \ +&& chmod -R 600 /root/.ssh/* \ +&& ls -al /root/.ssh/* \ +&& cat /root/.ssh/id_rsa.pub + +posthook="ssh -vv -o StrictHostKeyChecking=no -N $TUNNEL_HOST \ + -L *:$LOCAL_PORT:$REMOTE_HOST:$REMOTE_PORT \ + -R 48080:$LOCAL_HOST:48080 \ + -R 48082:$LOCAL_HOST:48082 \ + -R 5563:$LOCAL_HOST:5563 \ + -p $SSH_PORT && while true; do sleep 60; done" + +echo "Executing $@" +"$@" + +#sleep for some time to before running posthook +sleep 3 + +echo "Executing hook=$posthook" +eval $posthook diff --git a/docs_src/microservices/security/edgex-core-ssh-proxy.yml b/docs_src/microservices/security/edgex-core-ssh-proxy.yml new file mode 100644 index 0000000000..5487ba2623 --- /dev/null +++ b/docs_src/microservices/security/edgex-core-ssh-proxy.yml @@ -0,0 +1,585 @@ +# /******************************************************************************* +# * Copyright 2020 Redis Labs +# * Copyright 2020 Intel Corporation. +# * +# * 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. +# * +# * @author: Andre Srinivasan, Redis Labs +# * @author: Leonard Goodell, Intel +# * EdgeX Foundry, Geneva, version 1.2.0 +# * added: May 14, 2020 +# *******************************************************************************/ + +version: '3.4' + +# all common shared environment variables defined here: +x-common-env-variables: &common-variables + Registry_Host: edgex-core-consul + Clients_CoreData_Host: edgex-core-data + Clients_Notifications_Host: edgex-support-notifications + Clients_Metadata_Host: edgex-core-metadata + Clients_Command_Host: edgex-core-command + Clients_Scheduler_Host: edgex-support-scheduler + Clients_RulesEngine_Host: edgex-kuiper + Clients_VirtualDevice_Host: edgex-device-virtual + Databases_Primary_Type: redisdb + Databases_Primary_Host: edgex-redis + Databases_Primary_Port: 6379 + SecretStore_Host: edgex-vault + SecretStore_ServerName: edgex-vault + SecretStore_RootCaCertPath: /tmp/edgex/secrets/ca/ca.pem + # Required in case old configuration from previous release used. + # Change to "true" if re-enabling logging service for remote logging + Logging_EnableRemote: "false" + # Clients_Logging_Host: edgex-support-logging # un-comment if re-enabling logging service for remote logging + +# REDIS5_PASSWORD_PATHNAME must have the same value as +# security-secretstore-read/res/configuration.toml SecretStore.Passwordfile. Note edgex-go issue +# #2503 that will address this. +x-redis5-env-variables: &redis5-variables + REDIS5_PASSWORD_PATHNAME: /tmp/edgex/secrets/edgex-redis/redis5-password + +volumes: + db-data: + log-data: + consul-config: + consul-data: + consul-scripts: + vault-init: + vault-config: + vault-file: + vault-logs: + # non-shared volumes + secrets-setup-cache: + +services: + consul: + image: edgexfoundry/docker-edgex-consul:1.2.0 + ports: + - "8400:8400" + - "8500:8500" + container_name: edgex-core-consul + hostname: edgex-core-consul + networks: + edgex-network: + aliases: + - edgex-core-consul + volumes: + - consul-config:/consul/config:z + - consul-data:/consul/data:z + - consul-scripts:/consul/scripts:z + - /tmp/edgex/secrets/ca:/tmp/edgex/secrets/ca:ro,z + - /tmp/edgex/secrets/edgex-consul:/tmp/edgex/secrets/edgex-consul:ro,z + - /tmp/edgex/secrets/edgex-vault:/tmp/edgex/secrets/edgex-vault:ro,z + - /tmp/edgex/secrets/edgex-kong:/tmp/edgex/secrets/edgex-kong:ro,z + environment: + - "SECRETSTORE_SETUP_DONE_FLAG=/tmp/edgex/secrets/edgex-consul/.secretstore-setup-done" + - EDGEX_DB=redis + - EDGEX_SECURE=true + depends_on: + - security-secrets-setup + + vault: + image: vault:1.3.1 + container_name: edgex-vault + hostname: edgex-vault + networks: + edgex-network: + aliases: + - edgex-vault + ports: + - "127.0.0.1:8200:8200" + cap_add: + - "IPC_LOCK" + tmpfs: + - /vault/config + entrypoint: ["/vault/init/start_vault.sh"] + environment: + - VAULT_ADDR=https://edgex-vault:8200 + - VAULT_CONFIG_DIR=/vault/config + - VAULT_UI=true + volumes: + - vault-file:/vault/file:z + - vault-logs:/vault/logs:z + - vault-init:/vault/init:ro,z + - /tmp/edgex/secrets/edgex-vault:/tmp/edgex/secrets/edgex-vault:ro,z + depends_on: + - consul + - security-secrets-setup + + security-secrets-setup: + image: edgexfoundry/docker-edgex-secrets-setup-go:1.2.0 + container_name: edgex-secrets-setup + hostname: edgex-secrets-setup + environment: + <<: *redis5-variables + tmpfs: + - /tmp + - /run + command: "generate" + volumes: + - secrets-setup-cache:/etc/edgex/pki + - vault-init:/vault/init:z + - /tmp/edgex/secrets:/tmp/edgex/secrets:z + + vault-worker: + image: edgexfoundry/docker-edgex-security-secretstore-setup-go:1.2.0 + container_name: edgex-vault-worker + hostname: edgex-vault-worker + environment: + <<: *redis5-variables + SECRETSTORE_SETUP_DONE_FLAG: /tmp/edgex/secrets/edgex-consul/.secretstore-setup-done + networks: + edgex-network: + aliases: + - edgex-vault-worker + tmpfs: + - /run + volumes: + - vault-config:/vault/config:z + - consul-scripts:/consul/scripts:ro,z + - /tmp/edgex/secrets:/tmp/edgex/secrets:z + depends_on: + - security-secrets-setup + - consul + - vault + +# containers for reverse proxy + kong-db: + image: postgres:12.1-alpine + container_name: kong-db + hostname: kong-db + networks: + edgex-network: + aliases: + - kong-db + ports: + - "127.0.0.1:5432:5432" + environment: + - 'POSTGRES_DB=kong' + - 'POSTGRES_USER=kong' + - 'POSTGRES_PASSWORD=${KONG_POSTGRES_PASSWORD:-kong}' + depends_on: + - security-secrets-setup + + kong-migrations: + image: kong:${KONG_VERSION:-2.0.1} + container_name: kong-migrations + networks: + edgex-network: + aliases: + - kong-migrations + environment: + - 'KONG_DATABASE=postgres' + - 'KONG_PG_HOST=kong-db' + - 'KONG_PG_PASSWORD=${KONG_POSTGRES_PASSWORD:-kong}' + command: > + /bin/sh -cx + 'until /consul/scripts/consul-svc-healthy.sh kong-db; + do sleep 1; + done && kong migrations bootstrap; + kong migrations list; + code=$$?; + if [ $$code -eq 5 ]; then + kong migrations up && kong migrations finish; + fi' + volumes: + - consul-scripts:/consul/scripts:ro,z + depends_on: + - consul + - kong-db + + kong: + image: kong:${KONG_VERSION:-2.0.1} + container_name: kong + hostname: kong + networks: + edgex-network: + aliases: + - kong + ports: + - "8000:8000" + - "127.0.0.1:8001:8001" + - "8443:8443" + - "127.0.0.1:8444:8444" + tty: true + environment: + - 'KONG_DATABASE=postgres' + - 'KONG_PG_HOST=kong-db' + - 'KONG_PG_PASSWORD=${KONG_POSTGRES_PASSWORD:-kong}' + - 'KONG_PROXY_ACCESS_LOG=/dev/stdout' + - 'KONG_ADMIN_ACCESS_LOG=/dev/stdout' + - 'KONG_PROXY_ERROR_LOG=/dev/stderr' + - 'KONG_ADMIN_ERROR_LOG=/dev/stderr' + - 'KONG_ADMIN_LISTEN=0.0.0.0:8001, 0.0.0.0:8444 ssl' + restart: on-failure + command: > + /bin/sh -c + "until /consul/scripts/consul-svc-healthy.sh kong-migrations; do sleep 1; done; + /docker-entrypoint.sh kong docker-start" + volumes: + - consul-scripts:/consul/scripts:ro,z + depends_on: + - consul + - kong-db + - kong-migrations + + edgex-proxy: + image: edgexfoundry/docker-edgex-security-proxy-setup-go:1.2.0 + container_name: edgex-proxy + hostname: edgex-proxy + entrypoint: > + /bin/sh -c + "until /consul/scripts/consul-svc-healthy.sh kong; do sleep 1; done; + until /consul/scripts/consul-svc-healthy.sh security-secretstore-setup; do sleep 1; done; + /edgex/security-proxy-setup --init=true" + networks: + edgex-network: + aliases: + - edgex-proxy + environment: + <<: *common-variables + KongURL_Server: kong + SecretService_Server: edgex-vault + SecretService_TokenPath: /tmp/edgex/secrets/edgex-security-proxy-setup/secrets-token.json + SecretService_CACertPath: /tmp/edgex/secrets/ca/ca.pem + SecretService_SNIS: "edgex-kong" + volumes: + - consul-scripts:/consul/scripts:ro,z + - /tmp/edgex/secrets/ca:/tmp/edgex/secrets/ca:ro,z + - /tmp/edgex/secrets/edgex-security-proxy-setup:/tmp/edgex/secrets/edgex-security-proxy-setup:ro,z + depends_on: + - consul + - vault-worker + - kong + +# end of containers for reverse proxy + + redis: + image: redis:5.0.8-alpine + ports: + - "127.0.0.1:6379:6379" + container_name: edgex-redis + hostname: edgex-redis + environment: + <<: *redis5-variables + command: | + /bin/sh -c " + until [ -r $${REDIS5_PASSWORD_PATHNAME} ] && [ -s $${REDIS5_PASSWORD_PATHNAME} ]; do sleep 1; done + exec /usr/local/bin/docker-entrypoint.sh --requirepass `cat $${REDIS5_PASSWORD_PATHNAME}` \ + --dir /data \ + --save 900 1 \ + --save 300 10 \ + --save 60 10000 + " + networks: + - edgex-network + volumes: + - db-data:/data:z + - /tmp/edgex/secrets/edgex-redis:/tmp/edgex/secrets/edgex-redis:z + depends_on: + - vault-worker + +# The logging service has been deprecated in Geneva release and will be removed in the Hanoi release. +# All services are configure to send logging to STDOUT, i.e. not remote which requires this logging service +# If you still must use remote logging, un-comment the block below, all the related depends that have been commented out +# and the related global override that are commented out at the top. +# +# logging: +# image: edgexfoundry/docker-support-logging-go:1.2.0 +# ports: +# - "127.0.0.1:48061:48061" +# container_name: edgex-support-logging +# hostname: edgex-support-logging +# networks: +# - edgex-network +# environment: +# <<: *common-variables +# SecretStore_TokenFile: /tmp/edgex/secrets/edgex-support-logging/secrets-token.json +# Service_Host: edgex-support-logging +# Writable_Persistence: file +# Databases_Primary_Type: file +# Logging_EnableRemote: "false" +# volumes: +# - /tmp/edgex/secrets/ca:/tmp/edgex/secrets/ca:ro,z +# - /tmp/edgex/secrets/edgex-support-logging:/tmp/edgex/secrets/edgex-support-logging:ro,z +# depends_on: +# - consul +# - vault-worker + + system: + image: edgexfoundry/docker-sys-mgmt-agent-go:1.2.0 + ports: + - "127.0.0.1:48090:48090" + container_name: edgex-sys-mgmt-agent + hostname: edgex-sys-mgmt-agent + networks: + - edgex-network + environment: + <<: *common-variables + Service_Host: edgex-sys-mgmt-agent + ExecutorPath: /sys-mgmt-executor + MetricsMechanism: executor + volumes: + - /var/run/docker.sock:/var/run/docker.sock:z + depends_on: + - consul +# - logging # uncomment if re-enabled remote logging + - scheduler + - notifications + - metadata + - data + - command + + notifications: + image: edgexfoundry/docker-support-notifications-go:1.2.0 + ports: + - "127.0.0.1:48060:48060" + container_name: edgex-support-notifications + hostname: edgex-support-notifications + networks: + - edgex-network + environment: + <<: *common-variables + Service_Host: edgex-support-notifications + SecretStore_TokenFile: /tmp/edgex/secrets/edgex-support-notifications/secrets-token.json + volumes: + - /tmp/edgex/secrets/ca:/tmp/edgex/secrets/ca:ro,z + - /tmp/edgex/secrets/edgex-support-notifications:/tmp/edgex/secrets/edgex-support-notifications:ro,z + depends_on: + - consul +# - logging # uncomment if re-enabled remote logging + - redis + - vault-worker + + metadata: + image: edgexfoundry/docker-core-metadata-go:1.2.0 + ports: + - "48081:48081" + container_name: edgex-core-metadata + hostname: edgex-core-metadata + networks: + - edgex-network + environment: + <<: *common-variables + Service_Host: edgex-core-metadata + Notifications_Sender: edgex-core-metadata + SecretStore_TokenFile: /tmp/edgex/secrets/edgex-core-metadata/secrets-token.json + volumes: + - /tmp/edgex/secrets/ca:/tmp/edgex/secrets/ca:ro,z + - /tmp/edgex/secrets/edgex-core-metadata:/tmp/edgex/secrets/edgex-core-metadata:ro,z + depends_on: + - consul +# - logging # uncomment if re-enabled remote logging + - redis + - notifications + - vault-worker + + data: + image: edgexfoundry/docker-core-data-go:1.2.0 + ports: + - "48080:48080" + - "5563:5563" + container_name: edgex-core-data + hostname: edgex-core-data + networks: + - edgex-network + environment: + <<: *common-variables + Service_Host: edgex-core-data + SecretStore_TokenFile: /tmp/edgex/secrets/edgex-core-data/secrets-token.json + volumes: + - /tmp/edgex/secrets/ca:/tmp/edgex/secrets/ca:ro,z + - /tmp/edgex/secrets/edgex-core-data:/tmp/edgex/secrets/edgex-core-data:ro,z + depends_on: + - consul +# - logging # uncomment if re-enabled remote logging + - redis + - metadata + - vault-worker + + command: + image: edgexfoundry/docker-core-command-go:1.2.0 + ports: + - "48082:48082" + container_name: edgex-core-command + hostname: edgex-core-command + networks: + - edgex-network + environment: + <<: *common-variables + Service_Host: edgex-core-command + SecretStore_TokenFile: /tmp/edgex/secrets/edgex-core-command/secrets-token.json + volumes: + - /tmp/edgex/secrets/ca:/tmp/edgex/secrets/ca:ro,z + - /tmp/edgex/secrets/edgex-core-command:/tmp/edgex/secrets/edgex-core-command:ro,z + depends_on: + - consul +# - logging # uncomment if re-enabled remote logging + - redis + - metadata + - vault-worker + + scheduler: + image: edgexfoundry/docker-support-scheduler-go:1.2.0 + ports: + - "48085:48085" + container_name: edgex-support-scheduler + hostname: edgex-support-scheduler + networks: + - edgex-network + environment: + <<: *common-variables + Service_Host: edgex-support-scheduler + IntervalActions_ScrubPushed_Host: edgex-core-data + IntervalActions_ScrubAged_Host: edgex-core-data + SecretStore_TokenFile: /tmp/edgex/secrets/edgex-support-scheduler/secrets-token.json + volumes: + - /tmp/edgex/secrets/ca:/tmp/edgex/secrets/ca:ro,z + - /tmp/edgex/secrets/edgex-support-scheduler:/tmp/edgex/secrets/edgex-support-scheduler:ro,z + depends_on: + - consul +# - logging # uncomment if re-enabled remote logging + - redis + - vault-worker + +################################################################# +# Device Services +################################################################# + +# NOTE: all device micro services are commented out on the primary machine +# or host to demonstrate that the SSH tunneling is actually utilizing the +# secondary or remote running device services + +# device-virtual: +# image: edgexfoundry/docker-device-virtual-go:1.2.0 +# ports: +# - "127.0.0.1:49990:49990" +# container_name: edgex-device-virtual +# hostname: edgex-device-virtual +# networks: +# edgex-network: +# aliases: +# - edgex-device-virtual +# environment: +# <<: *common-variables +# Service_Host: edgex-device-virtual +# depends_on: +# - consul +# # - logging # uncomment if re-enabled remote logging +# - data +# - metadata + + # device-rest: + # image: edgexfoundry/docker-device-rest-go:1.1.0 + # ports: + # - "127.0.0.1:49986:49986" + # container_name: edgex-device-rest + # hostname: edgex-device-rest + # networks: + # edgex-network: + # aliases: + # - edgex-device-rest + # environment: + # <<: *common-variables + # Service_Host: edgex-device-rest + # depends_on: + # - data + # - command + # - logging # uncomment if re-enabled remote logging + + # device-random: + # image: edgexfoundry/docker-device-random-go:1.2.0 + # ports: + # - "127.0.0.1:49988:49988" + # container_name: edgex-device-random + # hostname: edgex-device-random + # networks: + # - edgex-network + # aliases: + # - edgex-device-random + # environment: + # <<: *common-variables + # Service_Host: edgex-device-random + # depends_on: + # - data + # - command + # + # device-mqtt: + # image: edgexfoundry/docker-device-mqtt-go:1.2.0 + # ports: + # - "127.0.0.1:49982:49982" + # container_name: edgex-device-mqtt + # hostname: edgex-device-mqtt + # networks: + # - edgex-network + # aliases: + # - edgex-device-mqtt + # environment: + # <<: *common-variables + # Service_Host: edgex-device-mqtt + # depends_on: + # - data + # - command + # + # device-modbus: + # image: edgexfoundry/docker-device-modbus-go:1.2.0 + # ports: + # - "127.0.0.1:49991:49991" + # container_name: edgex-device-modbus + # hostname: edgex-device-modbus + # networks: + # - edgex-network + # aliases: + # - edgex-device-modbus + # environment: + # <<: *common-variables + # Service_Host: edgex-device-modbus + # depends_on: + # - data + # - command + # + # device-snmp: + # image: edgexfoundry/docker-device-snmp-go:1.2.0 + # ports: + # - "127.0.0.1:49993:49993" + # container_name: edgex-device-snmp + # hostname: edgex-device-snmp + # networks: + # - edgex-network + # aliases: + # - edgex-device-snmp + # environment: + # <<: *common-variables + # Service_Host: edgex-device-snmp + # depends_on: + # - data + # - command + +########################################################## +# ssh tunneling proxy service +########################################################## + device-ssh-proxy: + image: device-ssh-proxy:test + volumes: + - $HOME/.ssh:/root/ssh:ro + environment: + TUNNEL_HOST: 192.168.1.190 + LOCAL_HOST: 172.17.0.1 + REMOTE_HOST: 192.168.64.1 + LOCAL_PORT: 49986 + REMOTE_PORT: 49986 + SSH_PORT: 2223 + + +networks: + edgex-network: + driver: "bridge" diff --git a/docs_src/microservices/security/edgex-device-sshd-remote.yml b/docs_src/microservices/security/edgex-device-sshd-remote.yml new file mode 100644 index 0000000000..7ec4e750e6 --- /dev/null +++ b/docs_src/microservices/security/edgex-device-sshd-remote.yml @@ -0,0 +1,507 @@ +# /******************************************************************************* +# * Copyright 2020 Redis Labs +# * +# * 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. +# * +# * Andre Srinivasan +# * added: April, 2020 +# *******************************************************************************/ + +version: '3.4' + +# all common shared environment variables defined here: +x-common-env-variables: &common-variables + Registry_Host: edgex-core-consul + Clients_CoreData_Host: edgex-core-data + Clients_Notifications_Host: edgex-support-notifications + Clients_Metadata_Host: edgex-core-metadata + Clients_Command_Host: edgex-core-command + Clients_Scheduler_Host: edgex-support-scheduler + Clients_RulesEngine_Host: edgex-kuiper + Clients_VirtualDevice_Host: edgex-device-virtual + Databases_Primary_Type: redisdb + Databases_Primary_Host: edgex-redis + Databases_Primary_Port: 6379 + SecretStore_Host: edgex-vault + SecretStore_ServerName: edgex-vault + SecretStore_RootCaCertPath: /tmp/edgex/secrets/ca/ca.pem + # Require in case old configuration from previous release used. + # Change to "true" if re-enabling logging service for remote logging + Logging_EnableRemote: "false" + # Clients_Logging_Host: edgex-support-logging # un-comment if re-enabling logging service for remote logging + +# REDIS5_PASSWORD_PATHNAME must have the same value as +# security-secretstore-read/res/configuration.toml SecretStore.Passwordfile. Note edgex-go issue +# #2503 that will address this. +x-redis5-env-variables: &redis5-variables + REDIS5_PASSWORD_PATHNAME: /tmp/edgex/secrets/edgex-redis/redis5-password + +volumes: + db-data: + log-data: + consul-config: + consul-data: + consul-scripts: + vault-init: + vault-config: + vault-file: + vault-logs: + # non-shared volumes + secrets-setup-cache: + +services: + consul: + image: nexus3.edgexfoundry.org:10004/docker-edgex-consul:master + ports: + - "8400:8400" + - "8500:8500" + container_name: edgex-core-consul + hostname: edgex-core-consul + networks: + edgex-network: + aliases: + - edgex-core-consul + volumes: + - consul-config:/consul/config:z + - consul-data:/consul/data:z + - consul-scripts:/consul/scripts:z + - /tmp/edgex/secrets/ca:/tmp/edgex/secrets/ca:ro,z + - /tmp/edgex/secrets/edgex-consul:/tmp/edgex/secrets/edgex-consul:ro,z + - /tmp/edgex/secrets/edgex-vault:/tmp/edgex/secrets/edgex-vault:ro,z + - /tmp/edgex/secrets/edgex-kong:/tmp/edgex/secrets/edgex-kong:ro,z + environment: + - "SECRETSTORE_SETUP_DONE_FLAG=/tmp/edgex/secrets/edgex-consul/.secretstore-setup-done" + - EDGEX_DB=redis + - EDGEX_SECURE=true + depends_on: + - security-secrets-setup + + vault: + image: vault:1.3.1 + container_name: edgex-vault + hostname: edgex-vault + networks: + edgex-network: + aliases: + - edgex-vault + ports: + - "8200:8200" + cap_add: + - "IPC_LOCK" + tmpfs: + - /vault/config + entrypoint: ["/vault/init/start_vault.sh"] + environment: + - VAULT_ADDR=https://edgex-vault:8200 + - VAULT_CONFIG_DIR=/vault/config + - VAULT_UI=true + volumes: + - vault-file:/vault/file:z + - vault-logs:/vault/logs:z + - vault-init:/vault/init:ro,z + - /tmp/edgex/secrets/edgex-vault:/tmp/edgex/secrets/edgex-vault:ro,z + depends_on: + - consul + - security-secrets-setup + + security-secrets-setup: + image: nexus3.edgexfoundry.org:10004/docker-edgex-secrets-setup-go:master + container_name: edgex-secrets-setup + hostname: edgex-secrets-setup + environment: + <<: *redis5-variables + tmpfs: + - /tmp + - /run + command: "generate" + volumes: + - secrets-setup-cache:/etc/edgex/pki + - vault-init:/vault/init:z + - /tmp/edgex/secrets:/tmp/edgex/secrets:z + + vault-worker: + image: nexus3.edgexfoundry.org:10004/docker-edgex-security-secretstore-setup-go:master + container_name: edgex-vault-worker + hostname: edgex-vault-worker + environment: + <<: *redis5-variables + SECRETSTORE_SETUP_DONE_FLAG: /tmp/edgex/secrets/edgex-consul/.secretstore-setup-done + networks: + edgex-network: + aliases: + - edgex-vault-worker + tmpfs: + - /run + volumes: + - vault-config:/vault/config:z + - consul-scripts:/consul/scripts:ro,z + - /tmp/edgex/secrets:/tmp/edgex/secrets:z + depends_on: + - security-secrets-setup + - consul + - vault + +# containers for reverse proxy + kong-db: + image: postgres:12.1-alpine + container_name: kong-db + hostname: kong-db + networks: + edgex-network: + aliases: + - kong-db + ports: + - "5432:5432" + environment: + - 'POSTGRES_DB=kong' + - 'POSTGRES_USER=kong' + - 'POSTGRES_PASSWORD=${KONG_POSTGRES_PASSWORD:-kong}' + depends_on: + - security-secrets-setup + + kong-migrations: + image: kong:${KONG_VERSION:-2.0.1} + container_name: kong-migrations + networks: + edgex-network: + aliases: + - kong-migrations + environment: + - 'KONG_DATABASE=postgres' + - 'KONG_PG_HOST=kong-db' + - 'KONG_PG_PASSWORD=${KONG_POSTGRES_PASSWORD:-kong}' + command: > + /bin/sh -cx + 'until /consul/scripts/consul-svc-healthy.sh kong-db; + do sleep 1; + done && kong migrations bootstrap; + kong migrations list; + code=$$?; + if [ $$code -eq 5 ]; then + kong migrations up && kong migrations finish; + fi' + volumes: + - consul-scripts:/consul/scripts:ro,z + depends_on: + - consul + - kong-db + + kong: + image: kong:${KONG_VERSION:-2.0.1} + container_name: kong + hostname: kong + networks: + edgex-network: + aliases: + - kong + ports: + - "8000:8000" + - "127.0.0.1:8001:8001" + - "8443:8443" + - "127.0.0.1:8444:8444" + tty: true + environment: + - 'KONG_DATABASE=postgres' + - 'KONG_PG_HOST=kong-db' + - 'KONG_PG_PASSWORD=${KONG_POSTGRES_PASSWORD:-kong}' + - 'KONG_PROXY_ACCESS_LOG=/dev/stdout' + - 'KONG_ADMIN_ACCESS_LOG=/dev/stdout' + - 'KONG_PROXY_ERROR_LOG=/dev/stderr' + - 'KONG_ADMIN_ERROR_LOG=/dev/stderr' + - 'KONG_ADMIN_LISTEN=0.0.0.0:8001, 0.0.0.0:8444 ssl' + restart: on-failure + command: > + /bin/sh -c + "until /consul/scripts/consul-svc-healthy.sh kong-migrations; do sleep 1; done; + /docker-entrypoint.sh kong docker-start" + volumes: + - consul-scripts:/consul/scripts:ro,z + depends_on: + - consul + - kong-db + - kong-migrations + + edgex-proxy: + image: nexus3.edgexfoundry.org:10004/docker-edgex-security-proxy-setup-go:master + container_name: edgex-proxy + hostname: edgex-proxy + entrypoint: > + /bin/sh -c + "until /consul/scripts/consul-svc-healthy.sh kong; do sleep 1; done; + until /consul/scripts/consul-svc-healthy.sh security-secretstore-setup; do sleep 1; done; + /edgex/security-proxy-setup --init=true" + networks: + edgex-network: + aliases: + - edgex-proxy + environment: + <<: *common-variables + KongURL_Server: kong + SecretService_Server: edgex-vault + SecretService_TokenPath: /tmp/edgex/secrets/edgex-security-proxy-setup/secrets-token.json + SecretService_CACertPath: /tmp/edgex/secrets/ca/ca.pem + SecretService_SNIS: "edgex-kong" + volumes: + - consul-scripts:/consul/scripts:ro,z + - /tmp/edgex/secrets/ca:/tmp/edgex/secrets/ca:ro,z + - /tmp/edgex/secrets/edgex-security-proxy-setup:/tmp/edgex/secrets/edgex-security-proxy-setup:ro,z + depends_on: + - consul + - vault-worker + - kong + +# end of containers for reverse proxy + + redis: + image: redis:5.0.8-alpine + ports: + - "127.0.0.1:6379:6379" + container_name: edgex-redis + hostname: edgex-redis + environment: + <<: *redis5-variables + command: | + /bin/sh -c " + until [ -r $${REDIS5_PASSWORD_PATHNAME} ] && [ -s $${REDIS5_PASSWORD_PATHNAME} ]; do sleep 1; done + exec /usr/local/bin/docker-entrypoint.sh --requirepass `cat $${REDIS5_PASSWORD_PATHNAME}` \ + --dir /data \ + --save 900 1 \ + --save 300 10 \ + --save 60 10000 + " + networks: + - edgex-network + volumes: + - db-data:/data:z + - /tmp/edgex/secrets/edgex-redis:/tmp/edgex/secrets/edgex-redis:z + depends_on: + - vault-worker + +# The logging service has been deprecated in Geneva release and will be removed in the Hanoi release. +# All services are configure to send logging to STDOUT, i.e. not remote which requires this logging service +# If you still must use remote logging, un-comment the block below, all the related depends that have been commented out +# and the related global override that are commented out at the top. +# +# logging: +# image: nexus3.edgexfoundry.org:10004/docker-support-logging-go:master +# ports: +# - "127.0.0.1:48061:48061" +# container_name: edgex-support-logging +# hostname: edgex-support-logging +# networks: +# - edgex-network +# environment: +# <<: *common-variables +# SecretStore_TokenFile: /tmp/edgex/secrets/edgex-support-logging/secrets-token.json +# Service_Host: edgex-support-logging +# Writable_Persistence: file +# Databases_Primary_Type: file +# Logging_EnableRemote: "false" +# volumes: +# - /tmp/edgex/secrets/ca:/tmp/edgex/secrets/ca:ro,z +# - /tmp/edgex/secrets/edgex-support-logging:/tmp/edgex/secrets/edgex-support-logging:ro,z +# depends_on: +# - consul +# - vault-worker + + system: + image: nexus3.edgexfoundry.org:10004/docker-sys-mgmt-agent-go:master + ports: + - "48090:48090" + container_name: edgex-sys-mgmt-agent + hostname: edgex-sys-mgmt-agent + networks: + - edgex-network + environment: + <<: *common-variables + Service_Host: edgex-sys-mgmt-agent + ExecutorPath: /sys-mgmt-executor + MetricsMechanism: executor + volumes: + - /var/run/docker.sock:/var/run/docker.sock:z + depends_on: + - consul +# - logging # uncomment is re-enabled remote logging + - scheduler + - notifications + - metadata + - data + - command + + notifications: + image: nexus3.edgexfoundry.org:10004/docker-support-notifications-go:master + ports: + - "48060:48060" + container_name: edgex-support-notifications + hostname: edgex-support-notifications + networks: + - edgex-network + environment: + <<: *common-variables + Service_Host: edgex-support-notifications + SecretStore_TokenFile: /tmp/edgex/secrets/edgex-support-notifications/secrets-token.json + volumes: + - /tmp/edgex/secrets/ca:/tmp/edgex/secrets/ca:ro,z + - /tmp/edgex/secrets/edgex-support-notifications:/tmp/edgex/secrets/edgex-support-notifications:ro,z + depends_on: + - consul +# - logging # uncomment is re-enabled remote logging + - redis + - vault-worker + + metadata: + image: nexus3.edgexfoundry.org:10004/docker-core-metadata-go:master + ports: + - "48081:48081" + container_name: edgex-core-metadata + hostname: edgex-core-metadata + networks: + - edgex-network + environment: + <<: *common-variables + Service_Host: edgex-core-metadata + Notifications_Sender: edgex-core-metadata + SecretStore_TokenFile: /tmp/edgex/secrets/edgex-core-metadata/secrets-token.json + volumes: + - /tmp/edgex/secrets/ca:/tmp/edgex/secrets/ca:ro,z + - /tmp/edgex/secrets/edgex-core-metadata:/tmp/edgex/secrets/edgex-core-metadata:ro,z + depends_on: + - consul +# - logging # uncomment is re-enabled remote logging + - redis + - notifications + - vault-worker + + data: + image: nexus3.edgexfoundry.org:10004/docker-core-data-go:master + ports: + - "48080:48080" + - "5563:5563" + container_name: edgex-core-data + hostname: edgex-core-data + networks: + - edgex-network + environment: + <<: *common-variables + Service_Host: edgex-core-data + SecretStore_TokenFile: /tmp/edgex/secrets/edgex-core-data/secrets-token.json + volumes: + - /tmp/edgex/secrets/ca:/tmp/edgex/secrets/ca:ro,z + - /tmp/edgex/secrets/edgex-core-data:/tmp/edgex/secrets/edgex-core-data:ro,z + depends_on: + - consul +# - logging # uncomment is re-enabled remote logging + - redis + - metadata + - vault-worker + + command: + image: nexus3.edgexfoundry.org:10004/docker-core-command-go:master + ports: + - "48082:48082" + container_name: edgex-core-command + hostname: edgex-core-command + networks: + - edgex-network + environment: + <<: *common-variables + Service_Host: edgex-core-command + SecretStore_TokenFile: /tmp/edgex/secrets/edgex-core-command/secrets-token.json + volumes: + - /tmp/edgex/secrets/ca:/tmp/edgex/secrets/ca:ro,z + - /tmp/edgex/secrets/edgex-core-command:/tmp/edgex/secrets/edgex-core-command:ro,z + depends_on: + - consul +# - logging # uncomment is re-enabled remote logging + - redis + - metadata + - vault-worker + + scheduler: + image: nexus3.edgexfoundry.org:10004/docker-support-scheduler-go:master + ports: + - "48085:48085" + container_name: edgex-support-scheduler + hostname: edgex-support-scheduler + networks: + - edgex-network + environment: + <<: *common-variables + Service_Host: edgex-support-scheduler + IntervalActions_ScrubPushed_Host: edgex-core-data + IntervalActions_ScrubAged_Host: edgex-core-data + SecretStore_TokenFile: /tmp/edgex/secrets/edgex-support-scheduler/secrets-token.json + volumes: + - /tmp/edgex/secrets/ca:/tmp/edgex/secrets/ca:ro,z + - /tmp/edgex/secrets/edgex-support-scheduler:/tmp/edgex/secrets/edgex-support-scheduler:ro,z + depends_on: + - consul +# - logging # uncomment is re-enabled remote logging + - redis + - vault-worker + +################################################################# +# Device Services +################################################################# + + device-virtual: + image: nexus3.edgexfoundry.org:10004/docker-device-virtual-go:master + ports: + - "49990:49990" + container_name: edgex-device-virtual + hostname: edgex-device-virtual + networks: + edgex-network: + aliases: + - edgex-device-virtual + environment: + <<: *common-variables + Service_Host: edgex-device-virtual + depends_on: + - consul +# - logging # uncomment is re-enabled remote logging + - data + - metadata + + device-rest: + image: nexus3.edgexfoundry.org:10004/docker-device-rest-go:master + ports: + - "49986:49986" + container_name: edgex-device-rest + hostname: edgex-device-rest + networks: + edgex-network: + aliases: + - edgex-device-rest + environment: + <<: *common-variables + Service_Host: edgex-device-rest + depends_on: + - data + - command + # - logging # uncomment if re-enabled remote logging + + +################################################################ +# SSH Daemon +################################################################ + sshd-remote: + image: eg_sshd + ports: + - "2223:22" + container_name: edgex-sshd-remote + hostname: edgex-sshd-remote + networks: + - edgex-network + +networks: + edgex-network: + driver: "bridge" diff --git a/docs_src/microservices/security/ssh-tunneling device.png b/docs_src/microservices/security/ssh-tunneling device.png new file mode 100644 index 0000000000000000000000000000000000000000..e31afa75de8e3d7aa6f5013341c719b56a52c6af GIT binary patch literal 75904 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/wjd!n;Hd2&D z9EO?DwAq*E8=}g0houJ%^u3}z$p#jMbeJ{nFh8i$tr^pGt;TNL=4nGQWHDAg58Tn- z&bbHb6SM!)LB3|tyg_Ux9Kc~}d2S~=axY4#RP*o0AROS9Zv)I#Kpu=R3^_%0)N5YtQ0b)vV zcOE{xvKmlS0Oc~(D>LuZ*$jY<#Y*X=|FEUw;=Z}Yy!%il2%dB|Vw|N~J_XiqFlLOK z3$Z||5g!|FZ2=7B7evzJ0OG!n3yw480*nsksnnOV3dSMESe|U6?_`gG@k^SQNS`Md zf*c-i^*4m{6vR3=sJZ#x0p=F*@CeOyn27}yD3r4u$*Ly`u~A!)1Xe5NNM-1 zV;!tWW;PT}LIm7?kwQl(QB*-O>YG`q!JN_jZtH&p zPF-F06#2T4?hWAe**yl9U~G#zI;!O=G=2|n!b6vDZ^R}ljVxq?g}i98dp}iy|F59( z1velZlFPY+aNeZ8<9xSJt#OXeW^OYFJ#Jfm0{{+R2>I0pysm5I3C(M$XP9@8zwUuY zW9&)mOE3#zmbhaEAdu@>yMj+w*%9&n>wG6h*wF3y8ua z96vb)=Z{<{0hfcsEw0DUHmq^Zq=Mhfyxmzn}{Ug zRsq1JyW2k&G?6n^wnqXL>h?=I(54_!CI2}K?AJLod}6FPbMq?scdj3m0eGhWaG(26 z2h~(p_F!N`Vw~(;jK9tg#^A`#%_5j2K5dHZoNM%Lv{x{ZRrUA7 zRQo2IU!1}oTeIkw)_BG{fym)fAB5H(umkeqlZB#UA42I!@`_;duUVQdx%O4#o^wm| z)qxfbl(dGKH@1L3rRdY~S_o6>Wod=N`T7(mdDzn1`P~YdX9sQep~D&YXq5~i@`aeo zRVgx~DL1rEgI`JK3=!zOaJ5}NFO(#v^s+xu5??uscp23TZxn3s-r-f1XqiU`tFZF2 ze#6n=roz0luFkfoUFpMdw%Cr~=hQ>#QNc?j)@5O?mF?jMpG&rUOr}Isc*17jTo^u9K6wzb~^x{zC zz>sd3OzYOh3Fp)FK!QUhtn|o^_KOe1jM#?oBJI!JfW0h_ChKbhhLfd?ZJBtNHbvJk zFs=Gr^fc2dgo<`k@<`Ck@~y)!5F~NFIw$>B*uu`O!DEeQMCf@zUnVmcTzl01z>u^O zWxCW%?L5w_e7Zt%R80Y`xrz^H6b)^7UY$3-C$TYzQ?6bXbDNftu;QS^S{!al9oBhf zdtiSJ>=}#98v9DG1E9h_72}(F6Hbh9i8Ha}~*?*%O!5yX);AWcXye z7%7h8m)<~zgIO=RR>w_=(?ydcsq~8Wi4L5IVJ$osXX=*F+NSY0&~jc6kG?1#AnVED z6-Be-?oVDH1o&S$<3p*J;-qKrg;z3GE!EP#gt&vzqjR4^R@n&hj6N&6 z(Eg^{e>lLSR^N!ACkI$JW`T(6eZuC6ACIosIjDP}>`MXH9;1Eq2)0v1R#58`DyHHf z#s0NB{8!LB-#Stp3`Tj#WLGf`;O>}H9}5mV$DNx6m?djj##ori;+pC*d#iB1;er0a z+S?Ua3Se*nWpp4t&?%)YIwneuZU2eLX5mGQ@P>HF3T|kO5?Z3@whT75bE~jZc7t`X zdV)Fj=63UwMWzf@!D5*nF-8hO3_NOeUvR7H1IG}(rzaLiH8KxBNxBOTMKA$P*72%L%Qy@p0GAQm4SbfTa*f#)HADR5O&<;!bUNR zIR5KuxrgTxy{9nRLe4g(!g}@M74p^EafVUzyt|OzY3YT;G&w)meZxu!(6HT1W4dvQ zv41F69Y_*psQ}310SERyL`8lN4b$C9!veHo(!95GeY%toa+@zfCFlHXm+yq7qry=0 z+xC|}#ouUm2p6W{?*;+4bSI9)sqY9*V#bASSTp+zuJ8t5Ai)O?C)}&hE&CHirw>E~ z?+|DyM=ML&&M&gb02d6&L3q~u=y>1?jU1iqxhXajS5>l4JloNyWl#ax&gsuhj+~3* z1lyS17=i5dm0R-wcyu#b(;-EAwAJU7l@QnhAigm(N=NY@XLvHh!YhAWS5Jum{@Nuk zE^b)F6dEq1IuQA-^TjzFS0v|}00DQcZ;!wXwBj9v>6jo=nmLX*LDPx~iWFWpc0;J# z*La7y)d9(P4+e%bZA&*C=hTJC@?D8D6Q0@fgNX2vSGiIaj*0K8KNy9xjO7E;ZAczQ z2dtRvcxQ|3BBj}r71!1lsphO#RV1o6?x&*S?r|x|c&l;6*T%gr+icE-IQsg^eUeRu z;u_b%_4VusAjOIGof!+U#VF2-`06EI#R4dL*OU`=dZ&0Lar!VcN4$rJ!Rw%Fw!*bD z-SVY>B5SZWq!W1s$j)%TPx%9Kmy0t;r36%w)kkM0`i`Ghik*j4`jw-B;|a(#vTEN* zTy}z{1%HTU}UkFEWm}|cw@Sg z0gg7RQ}4|_D4FOZ*qCXQFD=-_iDJ8&5zA4}vY}d3YktF*Jyj&LFNkb!?F9y^jRxRL ziSwhv^Z^}V{gA>N>*C2N!c`sjt_K{SPZvd^`Ou$5)*U1xI*!6je+eC^I)0B;F*!GQ zD3f5zwpk6hqSaW(v6wW~DLbSSv&9V&G+s=OT& zPUXJRZXUM&KD=?KZQ)5&BT zF3Dybb_qBY@&x}G9j(go8%s}q)lF~oQV9$2eq|19P;A+55-D{f%G+=Hj_xz{af?L2 z$3#kfvL)eBn)&IT094s^VqH#;eq__CIe>0@WBMv-=3(uWB_P2t`Zj!ktEiW1B1k!7MIx{oO+X8F9&Gq|I zb7p3qA<;U_-` fmTAfszL{Seb_%+aIT-MEW$fE?Adt2z;p~3{Cfsfc literal 0 HcmV?d00001