Federation tutorial (#29)

* Added example apps code, SPIRE configurations, docker files, and shell scripts.

Signed-off-by: martincapello <m.a.capello@gmail.com>

* Removed commented out config

Signed-off-by: martincapello <m.a.capello@gmail.com>

* Added readme.

Signed-off-by: martincapello <m.a.capello@gmail.com>

* Added how to use federation with WebPKI authentication.

Signed-off-by: martincapello <m.a.capello@gmail.com>

* Addressed PR comments

Signed-off-by: martincapello <m.a.capello@gmail.com>

* Added Federation to the main README.md

Signed-off-by: martincapello <m.a.capello@gmail.com>

* Addressed PR comments.

Signed-off-by: martincapello <m.a.capello@gmail.com>

* Address PR comments.

Signed-off-by: martincapello <m.a.capello@gmail.com>

* Addressed PR comments.

Signed-off-by: martincapello <m.a.capello@gmail.com>

* Added newline to the end of the file.

Signed-off-by: martincapello <m.a.capello@gmail.com>

* Removed blank spaces in last line

Signed-off-by: martincapello <m.a.capello@gmail.com>

* Addressed  PR comments.

Signed-off-by: martincapello <m.a.capello@gmail.com>

* Addressed PR comments.

Signed-off-by: martincapello <m.a.capello@gmail.com>
This commit is contained in:
martincapello 2020-08-19 17:35:42 -03:00 committed by GitHub
parent 44be01528e
commit 28be9cb9c3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 1386 additions and 0 deletions

View File

@ -9,6 +9,7 @@ The tutorials in this repo describe how to install SPIRE and integrate it with s
| [Integrating with Envoy using X.509 certs](k8s/envoy-x509) | Kubernetes | | [Integrating with Envoy using X.509 certs](k8s/envoy-x509) | Kubernetes |
| [Integrating with Envoy using JWT](k8s/envoy-jwt) | Kubernetes | | [Integrating with Envoy using JWT](k8s/envoy-jwt) | Kubernetes |
| [Nested SPIRE](nested-spire) | Docker Compose | | [Nested SPIRE](nested-spire) | Docker Compose |
| [Federation](federation) | Docker Compose |
Additional examples of how to install and deploy SPIRE are available. The spiffe.io [Try SPIRE](https://spiffe.io/spire/try/) page includes a [Quickstart for Linux and MacOS X](https://spiffe.io/spire/try/getting-started-linux-macos-x/) and [SPIFFE Library Usage Examples](https://spiffe.io/spire/try/spiffe-library-usage-examples/). The [SPIRE Examples](https://github.com/spiffe/spire-examples) repo on GitHub includes more usage examples for Kubernetes deployments, including Postgres integration, and a Docker-based Envoy example. Additional examples of how to install and deploy SPIRE are available. The spiffe.io [Try SPIRE](https://spiffe.io/spire/try/) page includes a [Quickstart for Linux and MacOS X](https://spiffe.io/spire/try/getting-started-linux-macos-x/) and [SPIFFE Library Usage Examples](https://spiffe.io/spire/try/spiffe-library-usage-examples/). The [SPIRE Examples](https://github.com/spiffe/spire-examples) repo on GitHub includes more usage examples for Kubernetes deployments, including Postgres integration, and a Docker-based Envoy example.

View File

@ -0,0 +1,23 @@
#!/bin/bash
set -e
bb=$(tput bold)
nn=$(tput sgr0)
# Bootstrap trust to the SPIRE server for each agent by copying over the
# trust bundle into each agent container.
echo "${bb}Bootstrapping trust between SPIRE agents and SPIRE servers...${nn}"
docker-compose exec -T spire-server-broker bin/spire-server bundle show |
docker-compose exec -T broker-webapp tee conf/agent/bootstrap.crt > /dev/null
docker-compose exec -T spire-server-stock bin/spire-server bundle show |
docker-compose exec -T stock-quotes-service tee conf/agent/bootstrap.crt > /dev/null
# Start up the broker-webapp SPIRE agent.
echo "${bb}Starting broker-webapp SPIRE agent...${nn}"
docker-compose exec -d broker-webapp bin/spire-agent run
# Start up the stock-quotes-service SPIRE agent.
echo "${bb}Starting stock-quotes-service SPIRE agent...${nn}"
docker-compose exec -d stock-quotes-service bin/spire-agent run

View File

@ -0,0 +1,18 @@
#/bin/bash
set -e
bb=$(tput bold)
nn=$(tput sgr0)
echo "${bb}bootstrapping bundle from broker to quotes-service server...${nn}"
docker-compose exec -T spire-server-broker \
/opt/spire/bin/spire-server bundle show -format spiffe > docker/spire-server-stockmarket.example/conf/broker.example.bundle
docker-compose exec -T spire-server-stock \
/opt/spire/bin/spire-server bundle set -format spiffe -id spiffe://broker.example -path /opt/spire/conf/server/broker.example.bundle
echo "${bb}bootstrapping bundle from quotes-service to broker server...${nn}"
docker-compose exec -T spire-server-stock \
/opt/spire/bin/spire-server bundle show -format spiffe > docker/spire-server-broker.example/conf/stockmarket.example.bundle
docker-compose exec -T spire-server-broker \
/opt/spire/bin/spire-server bundle set -format spiffe -id spiffe://stockmarket.example -path /opt/spire/conf/server/stockmarket.example.bundle

View File

@ -0,0 +1,30 @@
#/bin/bash
set -e
bb=$(tput bold)
nn=$(tput sgr0)
fingerprint() {
# calculate the SHA1 digest of the DER bytes of the certificate using the
# "coreutils" output format (`-r`) to provide uniform output from
# `openssl sha1` on macOS and linux.
cat $1 | openssl x509 -outform DER | openssl sha1 -r | awk '{print $1}'
}
BROKER_WEBAPP_AGENT_FINGERPRINT=$(fingerprint docker/broker-webapp/conf/agent.crt.pem)
QUOTES_SERVICE_AGENT_FINGERPRINT=$(fingerprint docker/stock-quotes-service/conf/agent.crt.pem)
echo "${bb}Creating registration entry for the broker-webapp...${nn}"
docker-compose exec spire-server-broker bin/spire-server entry create \
-parentID spiffe://broker.example/spire/agent/x509pop/${BROKER_WEBAPP_AGENT_FINGERPRINT} \
-spiffeID spiffe://broker.example/webapp \
-selector unix:user:root \
-federatesWith "spiffe://stockmarket.example"
echo "${bb}Creating registration entry for the stock-quotes-service...${nn}"
docker-compose exec spire-server-stock bin/spire-server entry create \
-parentID spiffe://stockmarket.example/spire/agent/x509pop/${QUOTES_SERVICE_AGENT_FINGERPRINT} \
-spiffeID spiffe://stockmarket.example/quotes-service \
-selector unix:user:root \
-federatesWith "spiffe://broker.example"

501
federation/README.md Normal file
View File

@ -0,0 +1,501 @@
# Using SPIFFE Federation to Authenticate Workloads from Different SPIRE Servers
This tutorial shows how to authenticate two SPIFFE-identified workloads that are identified by two different SPIRE Servers.
The first part of this document demonstrates how to configure SPIFFE federation by showing the SPIRE configuration file changes and `spire-server` commands used to set up a stock quote webapp frontend and service backend. The second part of this document lists the steps you run to show the scenario in action using the Docker Compose files included in this tutorial's directory.
In this tutorial you will learn how to:
* Configure each SPIRE Server to expose its SPIFFE Federation bundle endpoint using SPIFFE authentication and Web PKI authentication.
* Configure the SPIRE Servers to retrieve trust bundles from each other.
* Bootstrap federation between two SPIRE Servers using different trust domains.
* Create registration entries for the workloads so that they can federate with other trust domain.
# Prerequisites
The baseline components for SPIFFE federation are:
* Two SPIRE Server instances
* Two SPIRE Agents, one connected to one SPIRE Server, and the second connected to the other SPIRE Server
* Two workloads that needs to communicate each other via mTLS, and use the Workload API to get SVIDs and trust bundles
# Scenario
Let's say we have a stock broker's webapp that wants to display stock quotes fetched from a stock market web service provider. The scenario goes as follows:
1. The user enters the broker's webapp stock quotes URL in a browser.
2. The webapp workload receives the request and makes an HTTP request for quotes to the stock market service using mTLS.
3. The stock market service receives the request and sends the quotes in the response.
4. The webapp renders the stock quotes page using the returned quotes and sends it to the browser.
5. The browser displays the quotes to the user. The webapp includes some JavaScript to refresh the page every 1 second, so every second these steps are executed again.
In addition to the above and for the rest of this tutorial, we are going to assume the following [trust domain](https://spiffe.io/docs/latest/spiffe/concepts/#trust-domain) names for these sample SPIRE installations: `broker.example` and `stockmarket.example`. Keep in mind that trust domains do not need to correspond to actual DNS domain names. Also, the applications access the WorkloadAPI directly to get SVIDs and trust bundles, meaning there are no proxies in the scenario described.
# Configure SPIFFE Federation Endpoints
To make federation work, and because the webapp and the quotes service are going to use `mTLS`, both SPIRE Servers need each other's trust bundle. This is done, in part, by configuring a so-called federation endpoint on each SPIRE Server, which provides the API used by SPIRE Servers in other trust domains to get the trust bundle for the trust domain they want to federate with.
The federation endpoint exposed by a SPIRE Server can be configured to use one of two authentication methods: SPIFFE auth or Web PKI auth.
## Configure a Federation Endpoint Using SPIFFE Authentication
To configure the broker's SPIRE Server bundle endpoint, we use the `federation` section in the broker's SPIRE Server configuration file, by default `server.conf`:
```hcl
server {
.
.
trust_domain = "broker.example"
.
.
federation {
bundle_endpoint {
address = "0.0.0.0"
port = 8443
}
}
}
```
This will publish the federation endpoint in any IP address at port 8443 in the host where the SPIRE Server is running.
On the other side, the stock market service provider's SPIRE Server is configured in a similar fashion:
```hcl
server {
.
.
trust_domain = "stockmarket.example"
.
.
federation {
bundle_endpoint {
address = "0.0.0.0"
port = 8443
}
}
}
```
At this point, both SPIRE Servers have exposed their federation endpoints to provide their trust bundles, but none of them knows how to reach each other's federation endpoint.
## Configure a Federation Endpoint Using Web PKI Authentication
We are going to assume that only the broker's SPIRE Server will use Web PKI authentication for its federation endpoint. The stock market SPIRE Server will still use SPIFFE authentication. Hence, the stock market SPIRE Server configuration remains the same as seen in the previous section.
Then, to configure the broker's SPIRE Server bundle endpoint, we configure the `federation` section as follows:
```hcl
server {
.
.
trust_domain = "broker.example"
.
.
federation {
bundle_endpoint {
address = "0.0.0.0"
port = 443
acme {
domain_name = "broker.example"
email = "some@email.com"
tos_accepted = true
}
}
}
}
```
This will publish the federation endpoint in any IP address at port 443. We use port 443 because we're demonstrating the use of Let's Encrypt as our ACME provider (this is used by default, if you want to use a different one then you must set the `directory_url` configurable). Note that `tos_accepted` was set to `true`, meaning that we accept the terms of service of our ACME provider, which in turn is needed when using Let's Encrypt.
For SPIFFE Federation using Web PKI to work, you must own the DNS domain specified for `domain_name` (`broker.example` in our example) and the domain must resolve to the SPIRE Server exposing the federation bundle endpoint.
# Configure SPIRE Servers to Retrieve Trust Bundles From Each Other
After configuring federation endpoints, the next step to enable SPIFFE federation is to configure the SPIRE Servers to find the trust bundles for other trust domains. The `federates_with` configuration option in `server.conf` is where you specify the endpoint of the other trust domain. The configuration of this section has some slight differences when using the different methods of authentication.
## Configure Trust Bundle Location Using SPIFFE Authentication
As we saw previously, the SPIRE Server of the stock market service provider has its federation endpoint listening on port `8443` at any IP address. We will also assume that `spire-server-stock` is a DNS name that resolves to the stock market service's SPIRE Server IP address. (The Docker Compose demo here uses the hostname `spire-server-stock`, but in typical usage you would specify a FQDN.) Then, the broker's SPIRE Server must be configured with the following `federates_with` section:
```hcl
server {
.
.
trust_domain = "broker.example"
.
.
federation {
bundle_endpoint {
address = "0.0.0.0"
port = 8443
}
federates_with "stockmarket.example" {
bundle_endpoint {
address = "spire-server-stock"
port = 8443
}
}
}
}
```
Now the broker's SPIRE Server knows where to find a trust bundle that can be used to validate SVIDs containing identities from the `stockmarket.example` trust domain.
On the other side, the stock market service provider's SPIRE Server must be configured in a similar fashion:
```hcl
server {
.
.
trust_domain = "stockmarket.example"
.
.
federation {
bundle_endpoint {
address = "0.0.0.0"
port = 8443
}
federates_with "broker.example" {
bundle_endpoint {
address = "spire-server-broker"
port = 8443
}
}
}
}
```
That is it. Specifying the `federation` section and `federates_with` subsection of `server.conf` is all that's needed configure SPIFFE federation. To finish enabling SPIFFE federation, we need to bootstrap the trust bundles and register the workloads using `spire-server` commands as described below.
## Configure Trust Bundle Location Using Web PKI authentication
As mentioned, in this alternate scenario we are assuming that only the broker's SPIRE Server will use Web PKI authentication for its federation endpoint, so the `federates_with` configuration for the broker server is the same as seen in the previous section. However, the SPIRE Server of the stock market service provider needs a different configuration:
```hcl
server {
.
.
trust_domain = "stockmarket.example"
.
.
federation {
bundle_endpoint {
address = "0.0.0.0"
port = 8443
}
federates_with "broker.example" {
bundle_endpoint {
address = "broker.example"
use_web_pki = true
}
}
}
}
```
The differences are:
- `port` was removed. This is because by default it is set to `443`, which is the port where the broker's federation bundle endpoint is listening.
- `address` now is set to the broker's domain `broker.example`.
- `use_web_pki` was added and set to `true`. This is mandatory when the bundle endpoint to which we want to federate is using Web PKI authentication.
# Bootstrap Federation
We have configured the SPIRE Servers with the address of the federation endpoints, but this is not enough to make federation work. To enable the SPIRE Servers to fetch the trust bundles from each other they need each other's trust bundle first, because they have to authenticate the SPIFFE identity of the federated server that is trying to access the federation endpoint. Once federation is bootstrapped, the trust bundle updates are fetched trough the federation endpoint API using the current trust bundle.
The bootstrapping is done using a couple of SPIRE Server commands: `bundle show` and `bundle set`.
## Get the Bootstrap Trust Bundle
Let's say we want to get the broker's SPIRE Server trust bundle. On the node where the broker's SPIRE Server is running we run:
```
broker> spire-server bundle show -format spiffe > broker.example.bundle
```
This saves the trust bundle in the `broker.example.bundle` file. Then the broker must give a copy of this file to the stock market service folks, so they can store this trust bundle on their SPIRE Server and associate it with the `broker.example` trust domain. To achieve this, the stock market service folks must run the following on the node where they have SPIRE Server running:
```
stock-market> spire-server bundle set -format spiffe -id spiffe://broker.example -path /some/path/broker.example.bundle
```
At this point the stock market service's SPIRE Server is able to validate SVIDs having SPIFFE IDs with a `broker.example` trust domain. However, the broker's SPIRE Server is not yet able to validate SVIDs having SPIFFE IDs with a `stockmarket.example` trust domain. To make this possible, the stock market folks must run the following on the node where they have SPIRE Server running:
```
stock-market> spire-server bundle show -format spiffe > stockmarket.example.bundle
```
Then the stock market folks must give a copy of this file to the broker folks, so they can store this trust bundle on their SPIRE Server and associate it with the `stockmarket.example` trust domain. To achieve this, the broker folks must run the following on the node where they have SPIRE Server running:
```
broker> spire-server bundle set -format spiffe -id spiffe://stockmarket.example -path /some/path/stockmarket.example.bundle
```
Now both SPIRE Servers can validate SVIDs having SPIFFE IDs with each other's trust domain, thus both can start fetching trust bundle updates from each other's federation endpoints. Also, as of now they can create registration entries for federating as shown in the next section.
Note that the creation of the `broker.example.bundle` file (and later importing by the stock market service) is not needed when the broker's SPIRE Server is using Web PKI authentication for its federation bundle endpoint.
# Create Registration Entries for Federation
Now that the SPIRE Servers have each other's trust bundle, let's see how they can create registration entries to federate with each other.
To simplify things, we are going to suppose that the stock market webapp and the quotes service are both running on Linux boxes, one owned by the stock market organization and the other owned by the broker. Since they are using SPIRE, each Linux box also has a SPIRE Agent installed. In addition to this, the webapp is run using the `webapp` user, and the quotes service is run using the `quotes-service` user.
With those assumptions, in the SPIRE Server node of the broker, the broker folks must create a registration entry. The `-federatesWith` flag is required to enable SPIFFE federation:
```
broker> spire-server entry create \
-parentID <SPIRE Agent's SPIFFE ID> \
-spiffeID spiffe://broker.example/webapp \
-selector unix:user:webapp \
-federatesWith "spiffe://stockmarket.example"
```
By specifying the `-federatesWith` flag, once this registration entry is created, when the webapp's SPIRE Server asks for an SVID it will get one from the broker's SPIRE Server with the `spiffe://broker.example/webapp` identity, along with the trust bundle associated to the `stockmarket.example` trust domain.
On the stock market service side, they must create a registration entry as follows:
```
stock-market> spire-server entry create \
-parentID <SPIRE Agent's SPIFFE ID> \
-spiffeID spiffe://stockmarket.example/quotes-service \
-selector unix:user:quotes-service \
-federatesWith "spiffe://broker.example"
```
Similarly, once this registration entry is created, when the quotes service asks for an SVID it will get one having the `spiffe://stockmarket.example/quotes-service` identity, along with the trust bundle associated to the `broker.example` trust domain.
That is about it. Now all the pieces are in place to make federation work and demonstrate how the webapp is able to communicate with the quotes service despite having identities with different trust domains.
# Federation Example Using SPIFFE Authentication with SPIRE 0.11.0
This section explains how to use Docker Compose to try an example implementation of the SPIFFE auth scenario described in this tutorial.
Although not shown here, you could make the changes shown in the Web PKI authentication sections to try the Web PKI scenario. Remember that to configure Web PKI, the FQDN specified for `domain_name` must be owned by you and resolvable over the internet via DNS.
## Requirements
Required files for this tutorial can be found in the `federation` directory in https://github.com/spiffe/spire-tutorials. If you didn't already clone the repository please do so now.
Before proceeding, review the following system requirements:
- A 64-bit Linux or macOS environment
- [Docker](https://docs.docker.com/get-docker/) and [Docker Compose](https://docs.docker.com/compose/install/) installed (Docker Compose is included in macOS Docker Desktop)
- [Go](https://golang.org/dl/) 1.14.4 or higher installed
## Build
Ensure that the current working directory is `.../spire-tutorials/federation` and run the following command to create the files needed for Docker Compose:
```
$ ./build.sh
```
## Run
Run the following command to start the SPIRE Servers and the applications:
```
$ docker-compose up -d
```
## Start SPIRE Agents
Run the following command to start the SPIRE Agents:
```
$ ./1-start-spire-agents.sh
```
## Bootstrap Federation
Run the following command to [bootstrap the federation](#bootstrap-federation):
```
$ ./2-bootstrap-federation.sh
```
## Create Workload Registration Entries
Run the following command to create [workload registration entries](#create-registration-entries-for-federation):
```
$ ./3-create-registration-entries.sh
```
After running this script, it may take some seconds for the applications to receive their SVIDs and trust bundles.
## See the Scenario Working In a Browser
Open up a browser to http://localhost:8080/quotes and you should see a grid of randomly generated phony stock quotes that are updated every 1 second.
## See the Configuration
To see the broker's SPIRE Server configuration you can run:
```
$ docker-compose exec spire-server-broker cat conf/server/server.conf
```
You should see:
```
server {
bind_address = "0.0.0.0"
bind_port = "8081"
registration_uds_path = "/tmp/spire-registration.sock"
trust_domain = "broker.example"
data_dir = "/opt/spire/data/server"
log_level = "DEBUG"
log_file = "/opt/spire/server.log"
default_svid_ttl = "1h"
ca_subject = {
country = ["US"],
organization = ["SPIFFE"],
common_name = "",
}
federation {
bundle_endpoint {
address = "0.0.0.0"
port = 8443
}
federates_with "stockmarket.example" {
bundle_endpoint {
address = "spire-server-stock"
port = 8443
}
}
}
}
plugins {
DataStore "sql" {
plugin_data {
database_type = "sqlite3"
connection_string = "/opt/spire/data/server/datastore.sqlite3"
}
}
NodeAttestor "x509pop" {
plugin_data {
ca_bundle_path = "/opt/spire/conf/server/agent-cacert.pem"
}
}
NodeResolver "noop" {
plugin_data {}
}
KeyManager "memory" {
plugin_data = {}
}
}
```
To see the stock market's SPIRE Server configuration you can run:
```
$ docker-compose exec spire-server-stock cat conf/server/server.conf
```
You should see:
```
server {
bind_address = "0.0.0.0"
bind_port = "8081"
registration_uds_path = "/tmp/spire-registration.sock"
trust_domain = "stockmarket.example"
data_dir = "/opt/spire/data/server"
log_level = "DEBUG"
log_file = "/opt/spire/server.log"
default_svid_ttl = "1h"
ca_subject = {
country = ["US"],
organization = ["SPIFFE"],
common_name = "",
}
federation {
bundle_endpoint {
address = "0.0.0.0"
port = 8443
}
federates_with "broker.example" {
bundle_endpoint {
address = "spire-server-broker"
port = 8443
}
}
}
}
plugins {
DataStore "sql" {
plugin_data {
database_type = "sqlite3"
connection_string = "/opt/spire/data/server/datastore.sqlite3"
}
}
NodeAttestor "x509pop" {
plugin_data {
ca_bundle_path = "/opt/spire/conf/server/agent-cacert.pem"
}
}
NodeResolver "noop" {
plugin_data {}
}
KeyManager "memory" {
plugin_data = {}
}
}
```
## See the Registration Entries
To see the broker's SPIRE Server registration entries you can run:
```
$ docker-compose exec spire-server-broker bin/spire-server entry show
```
You should see something like this:
```
Found 1 entry
Entry ID : 2d799235-ddca-4088-ba6f-bf54d2af918f
SPIFFE ID : spiffe://broker.example/webapp
Parent ID : spiffe://broker.example/spire/agent/x509pop/4f9238aaa7a93cf96ca3d6060abe27bc51a267e7
Revision : 0
TTL : 3600
Selector : unix:user:root
FederatesWith : spiffe://stockmarket.example
```
To see the stock martket's SPIRE Server registration entries you can run:
```
$ docker-compose exec spire-server-stock bin/spire-server entry show
```
You should see something like this:
```
Found 1 entry
Entry ID : e42e8d6b-0a0a-4e38-b544-08510c35cbbe
SPIFFE ID : spiffe://stockmarket.example/quotes-service
Parent ID : spiffe://stockmarket.example/spire/agent/x509pop/50686366996ece3ca8e528765af685fe81f81435
Revision : 0
TTL : 3600
Selector : unix:user:root
FederatesWith : spiffe://broker.example
```
## Cleanup
```
$ docker-compose down
```

10
federation/build.sh Executable file
View File

@ -0,0 +1,10 @@
#!/bin/bash
set -e
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
(cd src/broker-webapp && GOOS=linux go build -v -o $DIR/docker/broker-webapp/broker-webapp)
(cd src/stock-quotes-service && GOOS=linux go build -v -o $DIR/docker/stock-quotes-service/stock-quotes-service)
docker-compose -f docker-compose.yml build

View File

@ -0,0 +1,37 @@
version: '3'
services:
spire-server-stock:
build: ./docker/spire-server-stockmarket.example
hostname: spire-server-stock
tty: true
privileged: true
volumes:
- ./docker/spire-server-stockmarket.example/conf:/opt/spire/conf/server
spire-server-broker:
build: ./docker/spire-server-broker.example
hostname: spire-server-broker
tty: true
privileged: true
volumes:
- ./docker/spire-server-broker.example/conf:/opt/spire/conf/server
stock-quotes-service:
build: ./docker/stock-quotes-service
hostname: stock-quotes-service
tty: true
privileged: true
links:
- spire-server-stock
broker-webapp:
build: ./docker/broker-webapp
hostname: broker-webapp
tty: true
privileged: true
links:
- spire-server-broker
- stock-quotes-service
ports:
- 8080:8080

View File

@ -0,0 +1,11 @@
FROM gcr.io/spiffe-io/spire-agent:unstable
COPY conf/agent.conf /opt/spire/conf/agent/agent.conf
COPY conf/agent.key.pem /opt/spire/conf/agent/agent.key.pem
COPY conf/agent.crt.pem /opt/spire/conf/agent/agent.crt.pem
COPY broker-webapp /usr/local/bin/broker-webapp
WORKDIR /opt/spire
ENTRYPOINT []
CMD broker-webapp

View File

@ -0,0 +1,28 @@
agent {
data_dir = "/opt/spire/data/agent"
log_level = "DEBUG"
log_file = "/opt/spire/agent.log"
server_address = "spire-server-broker"
server_port = "8081"
socket_path ="/tmp/agent.sock"
trust_bundle_path = "/opt/spire/conf/agent/bootstrap.crt"
trust_domain = "broker.example"
}
plugins {
NodeAttestor "x509pop" {
plugin_data {
private_key_path = "/opt/spire/conf/agent/agent.key.pem"
certificate_path = "/opt/spire/conf/agent/agent.crt.pem"
}
}
KeyManager "disk" {
plugin_data {
directory = "/opt/spire/data/agent"
}
}
WorkloadAttestor "unix" {
plugin_data {
}
}
}

View File

@ -0,0 +1,10 @@
-----BEGIN CERTIFICATE-----
MIIBcTCB/KADAgECAgEBMA0GCSqGSIb3DQEBCwUAMBQxEjAQBgNVBAMTCWJyb2tl
ciBDQTAiGA8wMDAxMDEwMTAwMDAwMFoYDzk5OTkxMjMxMjM1OTU5WjAXMRUwEwYD
VQQDEwx3ZWJhcHAgYWdlbnQwfDANBgkqhkiG9w0BAQEFAANrADBoAmEA2t2N2LyK
jb5A32zI5ChQmnnCSacjERlIedTkW2URnriW/IirLUHJSmSNoybqq0ubbEbV2LTX
IKU7dbvabF9TI3M9N3J+eL2yvkabqoDj6srN5QZavuTWb/gMB7J6AskrAgMBAAGj
EjAQMA4GA1UdDwEB/wQEAwIHgDANBgkqhkiG9w0BAQsFAANhAHXsRyWbt+adfKty
a2fPwwJyBiq836cF2PI0F6KIztXSNBxAkRE+Ky4vE21b52N56KpuopbIf/ibaapW
7k/o5PdjUp3iAXIJXsP7d0qJ5By83/swsOkDY9g2/XYpnr2YBQ==
-----END CERTIFICATE-----

View File

@ -0,0 +1,13 @@
-----BEGIN PRIVATE KEY-----
MIIB5gIBADANBgkqhkiG9w0BAQEFAASCAdAwggHMAgEAAmEA2t2N2LyKjb5A32zI
5ChQmnnCSacjERlIedTkW2URnriW/IirLUHJSmSNoybqq0ubbEbV2LTXIKU7dbva
bF9TI3M9N3J+eL2yvkabqoDj6srN5QZavuTWb/gMB7J6AskrAgMBAAECYHXF9wiE
HIK9uCcCYO/1ibo2fwgnOkV/N3LnzqIntt2UMxtdZ8+IsQqpJVaAIJNgsRjL/pTK
akSbwXXQ8RrmjXEifHVl+XqNibqgLgIuFLJR3C2fixWzzIihlbYcxw5WQQIxAPvO
vlvTocP0PfT92FrYkIVwVNQH9SKXkQf9I5GhII5BFA5fBguOENj5avAwziwBOwIx
AN6CZ3pCW1WXfugyO/9SsgK5kIoTN6rp3U0Lg+XbOiHZ68jzUGxS6IR/X90U5GTY
0QIxALyBy5Qm3MU7hV5w4pUv5xFeRMLuqh8ZZGOcqBIPk7WrFn6juHzR/97O6bWi
c9YRnQIxAIgsbn+YFKVxLa2U8Lr1NRQN1LNrx2nF7jW0kmgdnpoQ8AfvQIzKwJo2
CckXfB9rwQIxAK8yIMZZdGcwb6rHVZTkleorHwT6JZCV01oEBy6/KWHiJI11QdMt
4vLzQVDYWlYXwQ==
-----END PRIVATE KEY-----

View File

@ -0,0 +1,7 @@
FROM gcr.io/spiffe-io/spire-server:unstable
# Override spire configurations
COPY conf/server.conf /opt/spire/conf/server/server.conf
COPY conf/agent-cacert.pem /opt/spire/conf/server/agent-cacert.pem
WORKDIR /opt/spire

View File

@ -0,0 +1,10 @@
-----BEGIN CERTIFICATE-----
MIIBbzCB+qADAgECAgEBMA0GCSqGSIb3DQEBCwUAMBQxEjAQBgNVBAMTCWJyb2tl
ciBDQTAiGA8wMDAxMDEwMTAwMDAwMFoYDzk5OTkxMjMxMjM1OTU5WjAUMRIwEAYD
VQQDEwlicm9rZXIgQ0EwfDANBgkqhkiG9w0BAQEFAANrADBoAmEArbgurz3YC9Me
VlNPuk3yXPvSUfWelptozX3EZzCqL5yLuokSKgMHrZEjwcjYz5+OvwndjkpbZ6hp
atBOqv9ETjji0G1RnIWapq2iLYBQ45whPfyRFQYXCqad33CShiE3AgMBAAGjEzAR
MA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADYQAVX3ZQm6sY/ZxIf3HL
O81sPL1rMdxie/r+JUHgmg2hRAFDsagsP7GEd7MRVX8N7bltLsI0vAs33cMF++RS
1oadKVRaK6+UU1ouelfF4ESXOPxREeQc4kCRLueqoVwraxU=
-----END CERTIFICATE-----

View File

@ -0,0 +1,51 @@
server {
bind_address = "0.0.0.0"
bind_port = "8081"
registration_uds_path = "/tmp/spire-registration.sock"
trust_domain = "broker.example"
data_dir = "/opt/spire/data/server"
log_level = "DEBUG"
log_file = "/opt/spire/server.log"
default_svid_ttl = "1h"
ca_subject = {
country = ["US"],
organization = ["SPIFFE"],
common_name = "",
}
federation {
bundle_endpoint {
address = "0.0.0.0"
port = 8443
}
federates_with "stockmarket.example" {
bundle_endpoint {
address = "spire-server-stock"
port = 8443
}
}
}
}
plugins {
DataStore "sql" {
plugin_data {
database_type = "sqlite3"
connection_string = "/opt/spire/data/server/datastore.sqlite3"
}
}
NodeAttestor "x509pop" {
plugin_data {
ca_bundle_path = "/opt/spire/conf/server/agent-cacert.pem"
}
}
NodeResolver "noop" {
plugin_data {}
}
KeyManager "memory" {
plugin_data = {}
}
}

View File

@ -0,0 +1,7 @@
FROM gcr.io/spiffe-io/spire-server:unstable
# Override spire configurations
COPY conf/server.conf /opt/spire/conf/server/server.conf
COPY conf/agent-cacert.pem /opt/spire/conf/server/agent-cacert.pem
WORKDIR /opt/spire

View File

@ -0,0 +1,10 @@
-----BEGIN CERTIFICATE-----
MIIBfDCCAQagAwIBAgIBATANBgkqhkiG9w0BAQsFADAaMRgwFgYDVQQDEw9zdG9j
ayBtYXJrZXQgQ0EwIhgPMDAwMTAxMDEwMDAwMDBaGA85OTk5MTIzMTIzNTk1OVow
GjEYMBYGA1UEAxMPc3RvY2sgbWFya2V0IENBMHwwDQYJKoZIhvcNAQEBBQADawAw
aAJhAOOiuSYdyfFhep+OJBkMy5RMbOa5aMEICur7euGWfclyco9enF5DEfd/wAs/
TGmx5a/cYfIbI/+LKGk51l6gKxlI7W7PLwm++chC9XDbKXNvGUW6Ljr0qPFgORHW
sWfTuwIDAQABoxMwETAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA2EA
BGIufwHpf45XAFlhtycAPTQJnwWYXfGAJ236q1/YinyY/PrcW3qXx+98mEBQ1G88
rD3gMy9vgUIooimHvpWbs3XkRXjW6GOcWgNgccYsT2PivOP3Tg2dqwKxrM+Mj+AZ
-----END CERTIFICATE-----

View File

@ -0,0 +1,51 @@
server {
bind_address = "0.0.0.0"
bind_port = "8081"
registration_uds_path = "/tmp/spire-registration.sock"
trust_domain = "stockmarket.example"
data_dir = "/opt/spire/data/server"
log_level = "DEBUG"
log_file = "/opt/spire/server.log"
default_svid_ttl = "1h"
ca_subject = {
country = ["US"],
organization = ["SPIFFE"],
common_name = "",
}
federation {
bundle_endpoint {
address = "0.0.0.0"
port = 8443
}
federates_with "broker.example" {
bundle_endpoint {
address = "spire-server-broker"
port = 8443
}
}
}
}
plugins {
DataStore "sql" {
plugin_data {
database_type = "sqlite3"
connection_string = "/opt/spire/data/server/datastore.sqlite3"
}
}
NodeAttestor "x509pop" {
plugin_data {
ca_bundle_path = "/opt/spire/conf/server/agent-cacert.pem"
}
}
NodeResolver "noop" {
plugin_data {}
}
KeyManager "memory" {
plugin_data = {}
}
}

View File

@ -0,0 +1,10 @@
FROM gcr.io/spiffe-io/spire-agent:unstable as spire
COPY conf/agent.conf /opt/spire/conf/agent/agent.conf
COPY conf/agent.key.pem /opt/spire/conf/agent/agent.key.pem
COPY conf/agent.crt.pem /opt/spire/conf/agent/agent.crt.pem
COPY stock-quotes-service /usr/local/bin/stock-quotes-service
WORKDIR /opt/spire
ENTRYPOINT []
CMD stock-quotes-service

View File

@ -0,0 +1,28 @@
agent {
data_dir = "/opt/spire/data/agent"
log_level = "DEBUG"
log_file = "/opt/spire/agent.log"
server_address = "spire-server-stock"
server_port = "8081"
socket_path ="/tmp/agent.sock"
trust_bundle_path = "/opt/spire/conf/agent/bootstrap.crt"
trust_domain = "stockmarket.example"
}
plugins {
NodeAttestor "x509pop" {
plugin_data {
private_key_path = "/opt/spire/conf/agent/agent.key.pem"
certificate_path = "/opt/spire/conf/agent/agent.crt.pem"
}
}
KeyManager "disk" {
plugin_data {
directory = "/opt/spire/data/agent"
}
}
WorkloadAttestor "unix" {
plugin_data {
}
}
}

View File

@ -0,0 +1,11 @@
-----BEGIN CERTIFICATE-----
MIIBgDCCAQqgAwIBAgIBATANBgkqhkiG9w0BAQsFADAaMRgwFgYDVQQDEw9zdG9j
ayBtYXJrZXQgQ0EwIhgPMDAwMTAxMDEwMDAwMDBaGA85OTk5MTIzMTIzNTk1OVow
HzEdMBsGA1UEAxMUcXVvdGVzLXNlcnZpY2UgYWdlbnQwfDANBgkqhkiG9w0BAQEF
AANrADBoAmEAtmHF8/92WExZWZpM0qMf/K07jZIGJYhksDFBsfP96vybCoLaqJqa
S2kF/ZHNci1/JZmQj88+PBSsw1cgU9t+hGRiO4Q1gMETAoTTVKjwrZFPH9qxu2+A
ziiBxLHNr3YNAgMBAAGjEjAQMA4GA1UdDwEB/wQEAwIHgDANBgkqhkiG9w0BAQsF
AANhALtDWHNK2KbJlBC8MTzP/CtswmpIySVM9fKq2FQIgy7Sljrz5gGEUFSL1TFz
bn3XN50dovVCSauXd8w5PO3QQ6J/SLa0z8gHH+wJvlWvAF4FTMTJkGqK/ZKDIwbK
pZ4H6g==
-----END CERTIFICATE-----

View File

@ -0,0 +1,13 @@
-----BEGIN PRIVATE KEY-----
MIIB5AIBADANBgkqhkiG9w0BAQEFAASCAc4wggHKAgEAAmEAtmHF8/92WExZWZpM
0qMf/K07jZIGJYhksDFBsfP96vybCoLaqJqaS2kF/ZHNci1/JZmQj88+PBSsw1cg
U9t+hGRiO4Q1gMETAoTTVKjwrZFPH9qxu2+AziiBxLHNr3YNAgMBAAECYCb62Ksg
o3OVxdb/woGWecSwZbUJS6UD9LkvneHhyxhJKv3hH8i/WlDZvn0Gh4lqrZCnk6a5
lC75lVsW/xMHHEra45UUcaHA/COw5acuykh+62YAkAYrhXhw5SJBa0McZQIxAM0q
MeItgVnk3eHd3/yhMhuayJw6S1jgV7kard8PL0UjevBFLfBI+0tUA3BzydSHiwIx
AOOSb975bjYGkfPfKJSrkiKVd6DcK0o1UUne8hUcRz34BR0bbUNv2o16hpGtkNlr
xwIwCVEBMuQeG5bo/Hi20yH+xIIi2fVLtp15Xk531sk5vEoAKyj5DRBDWQhXn6Oi
ZqRBAjEA24bDjCCphExKNyqqhuALFHmC8RXyXJ+aTtxWQq8Iumqq5C009bzM43Wy
oo0AEfy5AjAhRc0I+wZDnHqJUE+JQzDpeuKhrAf0gY1zjDhFh6Bax+oGqZVCXttP
S0UyEhdses0=
-----END PRIVATE KEY-----

View File

@ -0,0 +1,5 @@
module broker-webapp
go 1.14
require github.com/spiffe/go-spiffe/v2 v2.0.0-alpha.4

View File

@ -0,0 +1,67 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/spiffe/go-spiffe/v2 v2.0.0-alpha.4 h1:S/TtS3UiP69IvrWjtjSF/qv+GiIkP2jkYfV9Yl712hs=
github.com/spiffe/go-spiffe/v2 v2.0.0-alpha.4/go.mod h1:Z6jOEo3L49OpNaK5JTIOig6K9HJhwH6cb78MF5mothQ=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/zeebo/errs v1.2.2 h1:5NFypMTuSdoySVTqlNs1dEoU21QVamMQJxW/Fii5O7g=
github.com/zeebo/errs v1.2.2/go.mod h1:sgbWHsvVuTPHcqJJGQ1WhI5KbWlHYz+2+2C/LSEtCw4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 h1:gSJIx1SDwno+2ElGhA4+qG2zF97qiUzTM+rQ0klBOcE=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.27.1 h1:zvIju4sqAGvwKspUQOhwnpcqSbzi7/H6QomNNjTL4sk=
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/square/go-jose.v2 v2.4.1 h1:H0TmLt7/KmzlrDOpa1F+zr0Tk90PbJYBfsVUmRLrf9Y=
gopkg.in/square/go-jose.v2 v2.4.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

View File

@ -0,0 +1,128 @@
package main
import (
"context"
"encoding/json"
"fmt"
"io/ioutil"
"log"
"net/http"
"time"
"broker-webapp/quotes"
"github.com/spiffe/go-spiffe/v2/spiffeid"
"github.com/spiffe/go-spiffe/v2/spiffetls/tlsconfig"
"github.com/spiffe/go-spiffe/v2/workloadapi"
)
const (
port = 8080
quotesURL = "https://stock-quotes-service:8090/quotes"
socketPath = "unix:///tmp/agent.sock"
)
var (
latestQuotes = []*quotes.Quote(nil)
latestUpdate = time.Now()
// Stock quotes provider SPIFFE ID
quotesProviderSpiffeID = spiffeid.Must("stockmarket.example", "quotes-service")
x509Src *workloadapi.X509Source
bundleSrc *workloadapi.BundleSource
)
func main() {
log.Print("Webapp waiting for an X.509 SVID...")
ctx := context.Background()
var err error
x509Src, err = workloadapi.NewX509Source(ctx,
workloadapi.WithClientOptions(
workloadapi.WithAddr(socketPath),
//workloadapi.WithLogger(logger.Std),
),
)
if err != nil {
log.Fatal(err)
}
log.Print("Webapp waiting for a trust bundle...")
bundleSrc, err = workloadapi.NewBundleSource(ctx,
workloadapi.WithClientOptions(
workloadapi.WithAddr(socketPath),
),
)
if err != nil {
log.Fatal(err)
}
server := &http.Server{
Addr: fmt.Sprintf(":%d", port),
}
http.HandleFunc("/quotes", quotesHandler)
log.Printf("Webapp listening on port %d...", port)
err = server.ListenAndServe()
if err != nil {
log.Fatal(err)
}
}
func quotesHandler(resp http.ResponseWriter, req *http.Request) {
if req.Method != http.MethodGet {
resp.WriteHeader(http.StatusMethodNotAllowed)
return
}
data, err := getQuotesData()
if data != nil {
latestQuotes = data
latestUpdate = time.Now()
} else {
data = latestQuotes
}
quotes.Page.Execute(resp, map[string]interface{}{
"Data": data,
"Err": err,
"LastUpdated": latestUpdate,
})
}
func getQuotesData() ([]*quotes.Quote, error) {
client := http.Client{
Transport: &http.Transport{
TLSClientConfig: tlsconfig.MTLSClientConfig(x509Src, bundleSrc, tlsconfig.AuthorizeID(quotesProviderSpiffeID)),
},
}
resp, err := client.Get(quotesURL)
if err != nil {
log.Printf("Error getting quotes: %v", err)
return nil, err
}
if resp.StatusCode != http.StatusOK {
log.Printf("Quotes unavailables: %s", resp.Status)
return nil, err
}
jsonData, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Printf("Error reading response body: %v", err)
return nil, err
}
data := []*quotes.Quote{}
err = json.Unmarshal(jsonData, &data)
if err != nil {
log.Printf("Error unmarshaling json quotes: %v", err)
return nil, err
}
return data, nil
}

View File

@ -0,0 +1,107 @@
package quotes
import (
"html/template"
"log"
"time"
)
const markup = `
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<style>
body {
font-family: Arial, Helvetica, sans-serif;
margin: 0 20%;
}
.right {
text-align: right;
}
.center {
text-align: center;
}
.quotes {
width: 100%;
}
.quotes, .quotes td, .quotes th {
border-spacing: 0;
border: 1px solid black;
}
.error {
color: red;
}
</style>
{{if .Err}}
<div class="error">Quotes service unavailable</div>
{{end}}
<table class="quotes">
<caption class="right">Last Updated: {{.LastUpdated.Format "Jan 2 15:04:05"}}</caption>
<thead>
<tr>
<th scope="col">Symbol</th>
<th scope="col">Price</th>
<th scope="col">Open</th>
<th scope="col">Low</th>
<th scope="col">High</th>
<th scope="col">Close</th>
<th scope="col">Time</th>
</tr>
</thead>
<tbody>
{{range .Data}}
<tr>
<th scope="row">{{.Symbol}}</th>
{{if .Time}}
<td class="right">{{.Price | printf "%.2f"}}</td>
<td class="right">{{.Open | printf "%.2f"}}</td>
<td class="right">{{.Low | printf "%.2f"}}</td>
<td class="right">{{.High | printf "%.2f"}}</td>
<td class="right">{{.Close | printf "%.2f"}}</td>
<td class="center">{{.Time.Format "15:04:05"}}</td>
{{else}}
<td class="right">-</td>
<td class="right">-</td>
<td class="right">-</td>
<td class="right">-</td>
<td class="right">-</td>
<td class="center">-</td>
{{end}}
</tr>
{{end}}
</tbody>
</table>
<script>
function refresh() {
window.location.reload(true);
}
setTimeout(refresh, 1000);
</script>
</body>
</html>
`
// Page is the quotes page template already parsed.
var Page *template.Template
// Quote represent a quote for a specific symbol in a specific time.
type Quote struct {
Symbol string
Price float64
Open float64
Low float64
High float64
Close float64
Time *time.Time
}
func init() {
var err error
Page, err = template.New("quotes").Parse(markup)
if err != nil {
log.Fatal(err)
}
}

View File

@ -0,0 +1,5 @@
module stock-quotes-service
go 1.14
require github.com/spiffe/go-spiffe/v2 v2.0.0-alpha.4

View File

@ -0,0 +1,67 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/spiffe/go-spiffe/v2 v2.0.0-alpha.4 h1:S/TtS3UiP69IvrWjtjSF/qv+GiIkP2jkYfV9Yl712hs=
github.com/spiffe/go-spiffe/v2 v2.0.0-alpha.4/go.mod h1:Z6jOEo3L49OpNaK5JTIOig6K9HJhwH6cb78MF5mothQ=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/zeebo/errs v1.2.2 h1:5NFypMTuSdoySVTqlNs1dEoU21QVamMQJxW/Fii5O7g=
github.com/zeebo/errs v1.2.2/go.mod h1:sgbWHsvVuTPHcqJJGQ1WhI5KbWlHYz+2+2C/LSEtCw4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 h1:gSJIx1SDwno+2ElGhA4+qG2zF97qiUzTM+rQ0klBOcE=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.27.1 h1:zvIju4sqAGvwKspUQOhwnpcqSbzi7/H6QomNNjTL4sk=
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/square/go-jose.v2 v2.4.1 h1:H0TmLt7/KmzlrDOpa1F+zr0Tk90PbJYBfsVUmRLrf9Y=
gopkg.in/square/go-jose.v2 v2.4.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

View File

@ -0,0 +1,127 @@
package main
import (
"context"
"encoding/json"
"fmt"
"log"
"math"
"math/rand"
"net/http"
"sync"
"time"
"github.com/spiffe/go-spiffe/v2/spiffeid"
"github.com/spiffe/go-spiffe/v2/spiffetls/tlsconfig"
"github.com/spiffe/go-spiffe/v2/workloadapi"
)
const (
port = 8090
socketPath = "unix:///tmp/agent.sock"
)
var (
quotes = []*Quote{
{Symbol: "AAAA"},
{Symbol: "BBBB"},
{Symbol: "CCCC"},
{Symbol: "DDDD"},
{Symbol: "EEEE"},
{Symbol: "FFFF"},
{Symbol: "GGGG"},
{Symbol: "HHHH"},
{Symbol: "IIII"},
{Symbol: "JJJJ"},
{Symbol: "KKKK"},
}
quotesMtx = sync.RWMutex{}
brokerSpiffeID = spiffeid.Must("broker.example", "webapp")
)
func main() {
log.Print("Service waiting for an X.509 SVID...")
ctx := context.Background()
x509Src, err := workloadapi.NewX509Source(ctx,
workloadapi.WithClientOptions(
workloadapi.WithAddr(socketPath),
//workloadapi.WithLogger(logger.Std),
),
)
if err != nil {
log.Fatal(err)
}
log.Print("Service waiting for a trust bundle...")
bundleSrc, err := workloadapi.NewBundleSource(ctx,
workloadapi.WithClientOptions(
workloadapi.WithAddr(socketPath),
),
)
if err != nil {
log.Fatal(err)
}
server := &http.Server{
Addr: fmt.Sprintf(":%d", port),
TLSConfig: tlsconfig.MTLSServerConfig(x509Src, bundleSrc, tlsconfig.AuthorizeID(brokerSpiffeID)),
}
http.HandleFunc("/quotes", quotesHandler)
log.Printf("Stock quotes service listening on port %d...", port)
err = server.ListenAndServeTLS("", "")
if err != nil {
log.Fatal(err)
}
}
// Quote represent a quote for a specific symbol in a specific time.
type Quote struct {
Symbol string
Price float64
Open float64
Low float64
High float64
Close float64
Time *time.Time
}
func quotesHandler(resp http.ResponseWriter, req *http.Request) {
randomizeQuotes()
encoder := json.NewEncoder(resp)
quotesMtx.RLock()
err := encoder.Encode(quotes)
quotesMtx.RUnlock()
if err != nil {
log.Printf("Error encoding data: %v", err)
resp.WriteHeader(http.StatusInternalServerError)
return
}
}
func randomizeQuotes() {
quotesMtx.Lock()
for _, quote := range quotes {
if rand.Int()%4 == 0 {
priceDelta := rand.NormFloat64() * 1.5
now := time.Now()
if quote.Time == nil {
quote.Open = priceDelta + 10 + 100*rand.Float64()
quote.Low = quote.Open
quote.High = quote.Open
quote.Close = quote.Open - rand.NormFloat64()*1.5
quote.Price = quote.Open
} else {
quote.Price += priceDelta
}
quote.Time = &now
quote.Low = math.Min(quote.Price, quote.Low)
quote.High = math.Max(quote.Price, quote.High)
}
}
quotesMtx.Unlock()
}