Skip to content

Commit

Permalink
Get all certs API
Browse files Browse the repository at this point in the history
  • Loading branch information
vfarcic committed Nov 11, 2016
1 parent c911c96 commit 0dac191
Show file tree
Hide file tree
Showing 25 changed files with 925 additions and 347 deletions.
19 changes: 13 additions & 6 deletions TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,22 @@

## Certs

* Read cert names from env
* Distribute certs request
* Distribute cert files
* Get cert files from other instances on init
* Get cert names from other instances on init
* Explain certs inside Docker (files + env)
* Explain certs API
* Fix TODOs
* Remove cert API (file and name)
* Fix TODOs
* Load existing certs from labels on init
* Document

* API: PUT /v1/docker-flow-proxy/cert

* Test

```bash
curl -i -XPUT \
-d 'Content of my certificate PEM file' \
$(docker-machine ip docker-flow-proxy-tests):8080/v1/docker-flow-proxy/cert?certName=viktor.pem
```

## Content

Expand Down
10 changes: 9 additions & 1 deletion args_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@
package main

import (
"./proxy"
"fmt"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/suite"
"net/http"
"os"
"os/exec"
"testing"
"./proxy"
)

type ArgsTestSuite struct {
Expand Down Expand Up @@ -401,6 +401,11 @@ func (m *ProxyMock) AddCert(certName string) {
m.Called(certName)
}

func (m *ProxyMock) GetCerts() map[string]string {
params := m.Called()
return params.Get(0).(map[string]string)
}

func getProxyMock(skipMethod string) *ProxyMock {
mockObj := new(ProxyMock)
if skipMethod != "RunCmd" {
Expand All @@ -418,5 +423,8 @@ func getProxyMock(skipMethod string) *ProxyMock {
if skipMethod != "AddCert" {
mockObj.On("AddCert", mock.Anything).Return(nil)
}
if skipMethod != "GetCerts" {
mockObj.On("GetCerts").Return(map[string]string{})
}
return mockObj
}
10 changes: 10 additions & 0 deletions articles/certificates.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ docker service create --name swarm-listener \
docker service create --name proxy \
-p 80:80 \
-p 443:443 \
--replicas 3 \
--network proxy \
-e MODE=swarm \
-e LISTENER_ADDRESS=swarm-listener \
Expand Down Expand Up @@ -89,5 +90,14 @@ docker service update \

docker service inspect proxy --pretty

curl -i -XPUT \
--data-binary @tmp/xip.io/xip.io.pem \
$(docker-machine ip docker-flow-proxy-tests):8080/v1/docker-flow-proxy/cert?certName=xip.io.pem

curl -i \
$(docker-machine ip docker-flow-proxy-tests):8080/v1/docker-flow-proxy/certs

# TODO: Show that certs are distributed

# TODO: Show that certs are recuperated from existing instances on scale
```
Empty file removed certs/empty
Empty file.
16 changes: 16 additions & 0 deletions certs/xip.io.crt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
-----BEGIN CERTIFICATE-----
MIICmTCCAgICCQDNz17kqs0wgjANBgkqhkiG9w0BAQUFADCBkDELMAkGA1UEBhMC
RVMxEjAQBgNVBAgTCUJhcmNlbG9uYTESMBAGA1UEBxMJQmFyY2Vsb25hMSQwIgYD
VQQKExtUZWNobm9sb2d5Q29udmVyc2F0aW9ucy5jb20xETAPBgNVBAMUCCoueGlw
LmlvMSAwHgYJKoZIhvcNAQkBFhF2aWt0b3JAZmFyY2ljLmNvbTAeFw0xNjExMDky
MzIzMjhaFw0xNzExMDkyMzIzMjhaMIGQMQswCQYDVQQGEwJFUzESMBAGA1UECBMJ
QmFyY2Vsb25hMRIwEAYDVQQHEwlCYXJjZWxvbmExJDAiBgNVBAoTG1RlY2hub2xv
Z3lDb252ZXJzYXRpb25zLmNvbTERMA8GA1UEAxQIKi54aXAuaW8xIDAeBgkqhkiG
9w0BCQEWEXZpa3RvckBmYXJjaWMuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCB
iQKBgQDlJvQZ/e3+JxWLWkqWokyHH/1VtFj2+XgqrZ3kbPYVnFV4fJ9VtCTgHES7
kTHRB34e7EBnsVjnG9GTHwKnAnK0ToAYzdzg9ElTHhCA1EM9WzTXQpJR97uADVNS
dFg6M/iXl3Fnun444pojX2+dAVFpwSvzMBbU8JuJefTZhrebYwIDAQABMA0GCSqG
SIb3DQEBBQUAA4GBAHUyWkXuMgrtlsWhMjI5lZLlj48wwbNYFWfWtQcteKYmI2XE
8FKAwKBmGHqWJSogrXaaafxZlrtJ+NlK+5PNCGmhUOjt8QP88taSWW6zhJ9+AImT
LBiCwxAvkf58k/GrTuodDGyjlFK8t1agi/hrF1/2Djd5b2NIMwRHlQfaHFzL
-----END CERTIFICATE-----
12 changes: 12 additions & 0 deletions certs/xip.io.csr
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
-----BEGIN CERTIFICATE REQUEST-----
MIIB0TCCAToCAQAwgZAxCzAJBgNVBAYTAkVTMRIwEAYDVQQIEwlCYXJjZWxvbmEx
EjAQBgNVBAcTCUJhcmNlbG9uYTEkMCIGA1UEChMbVGVjaG5vbG9neUNvbnZlcnNh
dGlvbnMuY29tMREwDwYDVQQDFAgqLnhpcC5pbzEgMB4GCSqGSIb3DQEJARYRdmlr
dG9yQGZhcmNpYy5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAOUm9Bn9
7f4nFYtaSpaiTIcf/VW0WPb5eCqtneRs9hWcVXh8n1W0JOAcRLuRMdEHfh7sQGex
WOcb0ZMfAqcCcrROgBjN3OD0SVMeEIDUQz1bNNdCklH3u4ANU1J0WDoz+JeXcWe6
fjjimiNfb50BUWnBK/MwFtTwm4l59NmGt5tjAgMBAAGgADANBgkqhkiG9w0BAQUF
AAOBgQBK6Tv5YRcgJnhjPxZX+5YkBqF15G+Ma8nGbtkJWBn0bG1Y0XDvsVLzKKQ4
d0J6+2GeP+3BhAWG6KR7AmVkG1Cmfgroi55YdUAiB/MBoH49t5WaEa9JqMpw3vPG
tTtlFTkM0NQp6iqKRpXnt7JriyAo1z9PXWrLgdi3RF5YeV4mzg==
-----END CERTIFICATE REQUEST-----
15 changes: 15 additions & 0 deletions certs/xip.io.key
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
-----BEGIN RSA PRIVATE KEY-----
MIICXAIBAAKBgQDlJvQZ/e3+JxWLWkqWokyHH/1VtFj2+XgqrZ3kbPYVnFV4fJ9V
tCTgHES7kTHRB34e7EBnsVjnG9GTHwKnAnK0ToAYzdzg9ElTHhCA1EM9WzTXQpJR
97uADVNSdFg6M/iXl3Fnun444pojX2+dAVFpwSvzMBbU8JuJefTZhrebYwIDAQAB
AoGAE6k28m/2FoNV9uHlWm1McZ+OBed2ZqJa7BZ/YJT0vD/wmWjf5uOQ8mWahBle
E9g3uwF8OSlfPl0Fq0DS4m90Vdh4KKoV/4KduI9WbnDALFtrn8Zp0QY1jy+ga4lH
NMOPc7iP3OqR59hAg1LubTlVFtsas/Slav0DWjpciezfmzkCQQD9FskvaeJgg1FY
9ialvlgXhwPUGhrbGLoLDLpH/BIplU8oHUOgBdFEgq1H/orFXEJF1W4tSZpvrEHW
NRycsj39AkEA58mvq584RyP3nVumBZuQMT3GOhYVgvRiy+N5XXM8oSoWfaSoNDOM
cSPRiSJ76f6GNxzVcRjpWrjFzw5ZvKbM3wJAaCnhoacFqc1CRMgCfgLKRNynwbsA
xJ33rCaP4KKpYv31tz/cHldmmrjh0pi7yqC/EfjWwMLjUgTmj+AKJ5/gjQJAKwR4
cO6c4D8hnne66UGE0k63smIU3fgDOfnetWREtVh9UhY0DXQftYPDSKM4wiW1vRbB
2jqYXHj8ZUAxkVyXmwJBAI/9D2+XBBVePjGq18DFAEALx6KH4nkO6IDKXUREqKWV
jCuYIfrupr1o+JXmWbQUfs562f2VCy+Kd6HTUJkj+nQ=
-----END RSA PRIVATE KEY-----
31 changes: 31 additions & 0 deletions certs/xip.io.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
-----BEGIN CERTIFICATE-----
MIICmTCCAgICCQDNz17kqs0wgjANBgkqhkiG9w0BAQUFADCBkDELMAkGA1UEBhMC
RVMxEjAQBgNVBAgTCUJhcmNlbG9uYTESMBAGA1UEBxMJQmFyY2Vsb25hMSQwIgYD
VQQKExtUZWNobm9sb2d5Q29udmVyc2F0aW9ucy5jb20xETAPBgNVBAMUCCoueGlw
LmlvMSAwHgYJKoZIhvcNAQkBFhF2aWt0b3JAZmFyY2ljLmNvbTAeFw0xNjExMDky
MzIzMjhaFw0xNzExMDkyMzIzMjhaMIGQMQswCQYDVQQGEwJFUzESMBAGA1UECBMJ
QmFyY2Vsb25hMRIwEAYDVQQHEwlCYXJjZWxvbmExJDAiBgNVBAoTG1RlY2hub2xv
Z3lDb252ZXJzYXRpb25zLmNvbTERMA8GA1UEAxQIKi54aXAuaW8xIDAeBgkqhkiG
9w0BCQEWEXZpa3RvckBmYXJjaWMuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCB
iQKBgQDlJvQZ/e3+JxWLWkqWokyHH/1VtFj2+XgqrZ3kbPYVnFV4fJ9VtCTgHES7
kTHRB34e7EBnsVjnG9GTHwKnAnK0ToAYzdzg9ElTHhCA1EM9WzTXQpJR97uADVNS
dFg6M/iXl3Fnun444pojX2+dAVFpwSvzMBbU8JuJefTZhrebYwIDAQABMA0GCSqG
SIb3DQEBBQUAA4GBAHUyWkXuMgrtlsWhMjI5lZLlj48wwbNYFWfWtQcteKYmI2XE
8FKAwKBmGHqWJSogrXaaafxZlrtJ+NlK+5PNCGmhUOjt8QP88taSWW6zhJ9+AImT
LBiCwxAvkf58k/GrTuodDGyjlFK8t1agi/hrF1/2Djd5b2NIMwRHlQfaHFzL
-----END CERTIFICATE-----
-----BEGIN RSA PRIVATE KEY-----
MIICXAIBAAKBgQDlJvQZ/e3+JxWLWkqWokyHH/1VtFj2+XgqrZ3kbPYVnFV4fJ9V
tCTgHES7kTHRB34e7EBnsVjnG9GTHwKnAnK0ToAYzdzg9ElTHhCA1EM9WzTXQpJR
97uADVNSdFg6M/iXl3Fnun444pojX2+dAVFpwSvzMBbU8JuJefTZhrebYwIDAQAB
AoGAE6k28m/2FoNV9uHlWm1McZ+OBed2ZqJa7BZ/YJT0vD/wmWjf5uOQ8mWahBle
E9g3uwF8OSlfPl0Fq0DS4m90Vdh4KKoV/4KduI9WbnDALFtrn8Zp0QY1jy+ga4lH
NMOPc7iP3OqR59hAg1LubTlVFtsas/Slav0DWjpciezfmzkCQQD9FskvaeJgg1FY
9ialvlgXhwPUGhrbGLoLDLpH/BIplU8oHUOgBdFEgq1H/orFXEJF1W4tSZpvrEHW
NRycsj39AkEA58mvq584RyP3nVumBZuQMT3GOhYVgvRiy+N5XXM8oSoWfaSoNDOM
cSPRiSJ76f6GNxzVcRjpWrjFzw5ZvKbM3wJAaCnhoacFqc1CRMgCfgLKRNynwbsA
xJ33rCaP4KKpYv31tz/cHldmmrjh0pi7yqC/EfjWwMLjUgTmj+AKJ5/gjQJAKwR4
cO6c4D8hnne66UGE0k63smIU3fgDOfnetWREtVh9UhY0DXQftYPDSKM4wiW1vRbB
2jqYXHj8ZUAxkVyXmwJBAI/9D2+XBBVePjGq18DFAEALx6KH4nkO6IDKXUREqKWV
jCuYIfrupr1o+JXmWbQUfs562f2VCy+Kd6HTUJkj+nQ=
-----END RSA PRIVATE KEY-----
1 change: 1 addition & 0 deletions docker-compose-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ services:
- PROXY_INSTANCE_NAME=proxy-test-instance
ports:
- 80:80
- 443:443
- 8080:8080
volumes:
- ./test_configs/:/test_configs/
Expand Down
59 changes: 56 additions & 3 deletions integration_tests/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,10 @@ $ docker-compose -f docker-compose-test.yml run --rm production
Manual tests
$ docker-compose -f docker-compose-test.yml up -d staging-dep
$ curl -i "localhost:8080/v1/docker-flow-proxy/reconfigure?serviceName=test-service&servicePath=/v1/test"
$ curl -i localhost/v1/test
$ curl -i "localhost:8080/v1/docker-flow-proxy/reconfigure?serviceName=test-service&servicePath=^/v1/.*es.*&pathType=path_reg"
$ curl -i "$(docker-machine ip docker-flow-proxy-tests):8080/v1/docker-flow-proxy/reconfigure?serviceName=test-service&servicePath=/v1/test"
$ curl -i $(docker-machine ip docker-flow-proxy-tests)/v1/test
$ curl -i -XPUT --data-binary @tmp/xip.io/xip.io.pem "$(docker-machine ip docker-flow-proxy-tests):8080/v1/docker-flow-proxy/cert?certName=my-cert.pem"
$ curl -i "$(docker-machine ip docker-flow-proxy-tests):8080/v1/docker-flow-proxy/reconfigure?serviceName=test-service&servicePath=^/v1/.*es.*&pathType=path_reg"
$ docker-compose -f docker-compose-test.yml down
Cleanup
Expand Down Expand Up @@ -141,6 +142,58 @@ func (s IntegrationTestSuite) Test_Config() {
s.Equal(string(expected[:]), string(body))
}

func (s IntegrationTestSuite) Test_Certs() {
// Body is mandatory
url := fmt.Sprintf("http://%s:8080/v1/docker-flow-proxy/cert?certName=my-cert.pem", os.Getenv("DOCKER_IP"))
req, _ := http.NewRequest("PUT", url, nil)
client := &http.Client{}

resp, _ := client.Do(req)

s.Equal(400, resp.StatusCode)

// certName is mandatory
url = fmt.Sprintf("http://%s:8080/v1/docker-flow-proxy/cert", os.Getenv("DOCKER_IP"))
req, _ = http.NewRequest("PUT", url, strings.NewReader("THIS IS A CERTIFICATE"))
client = &http.Client{}

resp, _ = client.Do(req)

s.Equal(400, resp.StatusCode)

// Stores certs
url = fmt.Sprintf("http://%s:8080/v1/docker-flow-proxy/cert?certName=my-cert.pem", os.Getenv("DOCKER_IP"))
certContent, _ := ioutil.ReadFile("../certs/xip.io.pem")
req, _ = http.NewRequest("PUT", url, strings.NewReader(string(certContent)))
client = &http.Client{}

resp, _ = client.Do(req)

s.Equal(200, resp.StatusCode)

// // HTTPS works
// url = fmt.Sprintf("https://%s:8080/v2/test", os.Getenv("DOCKER_IP"))
// req, _ = http.NewRequest("GET", url, nil)
// client = &http.Client{}
//
// resp, err := client.Do(req)
//
// s.NoError(err)
// s.Equal(200, resp.StatusCode)

// Can retrieve certs
url = fmt.Sprintf("http://%s:8080/v1/docker-flow-proxy/certs", os.Getenv("DOCKER_IP"))
req, _ = http.NewRequest("GET", url, nil)
client = &http.Client{}

resp, _ = client.Do(req)
defer resp.Body.Close()
body, _ := ioutil.ReadAll(resp.Body)

s.Equal(200, resp.StatusCode)
s.Contains(strings.Replace(string(body), "\\n", "\n", -1), string(certContent))
}

// Util

func (s IntegrationTestSuite) verifyReconfigure(version int) {
Expand Down
9 changes: 9 additions & 0 deletions proxy/ha_proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,15 @@ func (m HaProxy) AddCert(certName string) {
data.Certs[certName] = true
}

func (m HaProxy) GetCerts() map[string]string {
certs := map[string]string{}
for cert, _ := range data.Certs {
content, _ := ReadFile(fmt.Sprintf("/certs/%s", cert))
certs[cert] = string(content)
}
return certs
}

func (m HaProxy) RunCmd(extraArgs []string) error {
args := []string{
"-f",
Expand Down
48 changes: 25 additions & 23 deletions proxy/ha_proxy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,31 @@ func (s HaProxyTestSuite) Test_AddCert_DoesNotStoreDuplicates() {
s.Equal(expected, data.Certs)
}

// GetCerts

func (s HaProxyTestSuite) Test_GetCerts_ReturnsAllCerts() {
dataOrig := data
defer func() { data = dataOrig }()
p := HaProxy{}
data.Certs = map[string]bool{}
expected := map[string]string{}
for i := 1; i <= 3; i++ {
certName := fmt.Sprintf("my-cert-%d", i)
data.Certs[certName] = true
expected[certName] = fmt.Sprintf("content of the certificate /certs/%s", certName)
}
readFileOrig := ReadFile
defer func() { ReadFile = readFileOrig }()
ReadFile = func(filename string) ([]byte, error) {
content := fmt.Sprintf("content of the certificate %s", filename)
return []byte(content), nil
}

actual := p.GetCerts()

s.EqualValues(expected, actual)
}

// CreateConfigFromTemplates

func (s HaProxyTestSuite) Test_CreateConfigFromTemplates_ReturnsError_WhenReadDirFails() {
Expand Down Expand Up @@ -119,28 +144,6 @@ config2 content`
s.Equal(expectedData, actualData)
}

func (s HaProxyTestSuite) Test_CreateConfigFromTemplates_AddsMultipleCerts() {
var actualFilename string
expectedFilename := fmt.Sprintf("%s/haproxy.cfg", s.ConfigsPath)
var actualData string
expectedData := `template content ssl crt /certs/my-cert.pem crt /certs/my-other-cert.pem
config1 content
config2 content`
writeFile = func(filename string, data []byte, perm os.FileMode) error {
actualFilename = filename
actualData = string(data)
return nil
}

p := NewHaProxy(s.TemplatesPath, s.ConfigsPath, map[string]bool{"my-cert.pem": true, "my-other-cert.pem": true})
p.CreateConfigFromTemplates()

s.Equal(expectedFilename, actualFilename)
s.Equal(expectedData, actualData)
}

func (s HaProxyTestSuite) Test_CreateConfigFromTemplates_WritesMockDataIfConfigsAreNotPresent() {
var actualData string
readConfigsDirOrig := readConfigsDir
Expand Down Expand Up @@ -284,4 +287,3 @@ func (s HaProxyTestSuite) mockHaExecCmd() *[]string {
}
return &actualCommand
}

50 changes: 1 addition & 49 deletions proxy/proxy.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
package proxy
import "github.com/stretchr/testify/mock"

var ProxyInstance Proxy = HaProxy{}

Expand All @@ -15,54 +14,7 @@ type Proxy interface {
ReadConfig() (string, error)
Reload() error
AddCert(certName string)
GetCerts() map[string]string
}

// Mock

type ProxyMock struct {
mock.Mock
}

func (m *ProxyMock) RunCmd(extraArgs []string) error {
params := m.Called(extraArgs)
return params.Error(0)
}

func (m *ProxyMock) CreateConfigFromTemplates() error {
params := m.Called()
return params.Error(0)
}

func (m *ProxyMock) ReadConfig() (string, error) {
params := m.Called()
return params.String(0), params.Error(1)
}

func (m *ProxyMock) Reload() error {
params := m.Called()
return params.Error(0)
}

func (m *ProxyMock) AddCert(certName string) {
m.Called(certName)
}

func GetProxyMock(skipMethod string) *ProxyMock {
mockObj := new(ProxyMock)
if skipMethod != "RunCmd" {
mockObj.On("RunCmd", mock.Anything).Return(nil)
}
if skipMethod != "CreateConfigFromTemplates" {
mockObj.On("CreateConfigFromTemplates").Return(nil)
}
if skipMethod != "ReadConfig" {
mockObj.On("ReadConfig").Return("", nil)
}
if skipMethod != "Reload" {
mockObj.On("Reload").Return(nil)
}
if skipMethod != "AddCert" {
mockObj.On("AddCert", mock.Anything).Return(nil)
}
return mockObj
}
Loading

0 comments on commit 0dac191

Please sign in to comment.