Permalink
Browse files

Improve errors related to TLS certs, fix docs

  • Loading branch information...
dunglas committed Jan 23, 2019
1 parent d7b778e commit f82ddd91696b1b6c59222b9e5732ed7959a0c50b
Showing with 85 additions and 7 deletions.
  1. +2 −2 .env
  2. +1 −1 README.md
  3. +20 −0 fixtures/tls/server.crt
  4. +27 −0 fixtures/tls/server.key
  5. +7 −1 hub/options.go
  6. +16 −0 hub/options_test.go
  7. +12 −3 hub/server_test.go
4 .env
@@ -2,8 +2,8 @@ ACME_CERT_DIR=
ACME_HOSTS=
ADDR=:3001
ALLOW_ANONYMOUS=1
CERT_FILE=
CERT_KEY=
#CERT_FILE=fixtures/tls/server.crt
#KEY_FILE=fixtures/tls/server.key
CORS_ALLOWED_ORIGINS=http://localhost:3000,http://localhost:8000
DB_PATH=
DEBUG=1
@@ -211,7 +211,7 @@ Be sure to update the value of `ACME_HOSTS` to match your domain name(s), a Let'
* `ADDR`: the address to listen on (example: `127.0.0.1:3000`, default to `:http` or `:https` depending if HTTPS is enabled or not)
* `ALLOW_ANONYMOUS`: set to `1` to allow subscribers with no valid JWT to connect
* `CERT_FILE`: a cert file (to use a custom certificate)
* `CERT_KEY`: a cert key (to use a custom certificate)
* `KEY_FILE`: a key file (to use a custom certificate)
* `CORS_ALLOWED_ORIGINS`: a comma separated list of allowed CORS origins, can be `*` for all
* `DB_PATH`: the path of the [bbolt](https://github.com/etcd-io/bbolt) database (default to `updates.db` in the current directory)
* `DEBUG`: set to `1` to enable the debug mode (prints recovery stack traces)
@@ -0,0 +1,20 @@
-----BEGIN CERTIFICATE-----
MIIDPjCCAiYCCQDpVvfmCZt2GzANBgkqhkiG9w0BAQsFADBhMQswCQYDVQQGEwJV
UzEUMBIGA1UEBwwLR290aGFtIENpdHkxEjAQBgNVBAMMCWxvY2FsaG9zdDEoMCYG
CSqGSIb3DQEJARYZZHVuZ2xhcyttZXJjdXJlQGdtYWlsLmNvbTAeFw0xOTAxMjMx
NTUzMzlaFw0yOTAxMjAxNTUzMzlaMGExCzAJBgNVBAYTAlVTMRQwEgYDVQQHDAtH
b3RoYW0gQ2l0eTESMBAGA1UEAwwJbG9jYWxob3N0MSgwJgYJKoZIhvcNAQkBFhlk
dW5nbGFzK21lcmN1cmVAZ21haWwuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
MIIBCgKCAQEAuKnXkBSJwOwkKfR58wP/yLYW9QFX2THoqN8iffangRmZwc5KLE6F
1S8jYMv3JGiJ95Ij3MezAfuBCdgPqqP8JrR1XwjR1RFZMOL/4U9R9OuMVng04PLw
L6TzKoEtZuExHUWFP0+5AYblgno2hoN/HVuox8m6zQrBNcbhTgDIjP5Hn491d9od
MtS3OxksDLr1UIOUGUWF7MQMN7lsN7rgT5qxoCkcAGAB4GPOA23HMt2zt4afDiI7
lAmuv8MKkTmBCcFe+q+U7o6wMxkjGstzAWRibtwzR4ejPwdO7se23MXCWGPvF16Z
tu1ip+e+waRus9o5UnyGaVPFAw8iCTC/KwIDAQABMA0GCSqGSIb3DQEBCwUAA4IB
AQB42AW7E57yOky8GpsKLoa9u7okwvvg8CQJ117X8a2MElBGnmMd9tjLa/pXAx2I
bN7jSTSadXiPNYCx4ueiJa4Dwy+C8YkwUbhRf3+mc7Chnz0SXouTjh7OUeeA06jS
W2VAR2pKB0pdJtAkXxIy21Juu8KF5uZqVq1oimgKw2lRUIMdKaqsrVwESk6u5Ojj
3DS40q9DzFnwKGCuZpspvMdWYLscotzLrCbnHp+guWDigEHS3CKzKbNo327nVg6X
7UjqqtPZ2mCsnUx3QTDJsr3gcSqhzmB+Q6I/0Q2Nx/aMmbsNegu+LC3GjFtL59Bv
B8pB/MxID0j47SwPKQghZvb3
-----END CERTIFICATE-----
@@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAuKnXkBSJwOwkKfR58wP/yLYW9QFX2THoqN8iffangRmZwc5K
LE6F1S8jYMv3JGiJ95Ij3MezAfuBCdgPqqP8JrR1XwjR1RFZMOL/4U9R9OuMVng0
4PLwL6TzKoEtZuExHUWFP0+5AYblgno2hoN/HVuox8m6zQrBNcbhTgDIjP5Hn491
d9odMtS3OxksDLr1UIOUGUWF7MQMN7lsN7rgT5qxoCkcAGAB4GPOA23HMt2zt4af
DiI7lAmuv8MKkTmBCcFe+q+U7o6wMxkjGstzAWRibtwzR4ejPwdO7se23MXCWGPv
F16Ztu1ip+e+waRus9o5UnyGaVPFAw8iCTC/KwIDAQABAoIBAQCczVNGe7oRADMh
EP/wM4ghhUTvHAndWrzFkFs4fJX1UKi34ZQoFTEdOZ6f1fHwj3f/qa8cDNJar5X9
puJ+siotL3Suks2iT83dbhN63SCpiM2sqvuzu3Xp7vWwNOo5fqR2x46CmQ5uVn5S
EbZ09/mbEza5FvmwnB49rLepxY6F8P+vK5ZnCZYS2SHpOxv3U9wG8gmcHRI9ejbC
X9rwuu3oT23bfbJ0tn6Qh8O3R1kXZUUXqnxsn554cZZrXg5+ygbt4HfDVWMLpqy/
5wG0FCpU8QvjF4L8qErP7TZRrWGFtti1RtACbu9LrWvO/74v54td5V28U6kqlDJR
ff4Mi4whAoGBAOBzReQIxGwoYApPyhF+ohvF39JEEXYfkzk94t6hbgyBFBFvqdFY
shT59im2P9LyDvTd5DnCIo52Sj7vM9H80tRjAA0A8okGOczk31ABbH8aZ2orU/0G
EJe4PV4r3bpLO6DKTYsicgRpXI3aHHLvYFXOVNrQKfrKCQ+GFMVuhDdRAoGBANKe
3Dn3XOq7EW42GZey1xUxrfQRJp491KXHvjYt7z7zSiUzqN+mqIqz6ngCjJWbyQsl
Ud9N9U+4rNfYYLHQ0resjxGQRtmooOHlLhT6pEplXDgQb2SmCg2u22SKkkXA7zOV
OFbNryXgkYThsA6ih8LiKM8aFn7zttRSEeTpfye7AoGBALhIzRyiuiuXpuswgdeF
YrJs8A1jB/c1i5qXHlvurT2lCYYbaZHSQj0I0r2CvrqDNhaEzStDIz5XDzTHD4Qd
EjmBo3wJyBkLPI/nZxb4ZE2jrz8znf0EasE3a2OTnrSjqqylDa/sMzM+EtkBORSB
SFaLV45lFeKs2W2eiBVmXTZRAoGAJoA7qaz6Iz6G9SqWixB6GLm4HsFz2cFbueJF
dwn2jf9TMnG7EQcaECDLX5y3rjGIEq2DxdouWaBcmChJpLeTjVfR31gMW4Vjw2dt
gRBAMAlPTkBS3Ictl0q7eCmMi4u1Liy828FFnxrp/uxyjnpPbuSAqTsPma1bYnyO
INY+FDkCgYAe9e39/vXe7Un3ysjqDUW+0OMM+kg4ulhiopzKY+QbHiSWmUUDtvcN
asqrYiX1d59e2ZNiqrlBn86I8549St81bWSrRMNf7R+WVb79RApsABeUaEoyo3lq
0UgOBM8Nt558kaja/YfJf/jwNC1DPuu5x5t38ZcqAkqrZ/HEPkFdGQ==
-----END RSA PRIVATE KEY-----
@@ -56,13 +56,19 @@ func NewOptionsFromEnv() (*Options, error) {
os.Getenv("DEMO") == "1" || os.Getenv("DEBUG") == "1",
}

missingEnv := make([]string, 0, 2)
missingEnv := make([]string, 0, 4)
if len(options.PublisherJWTKey) == 0 {
missingEnv = append(missingEnv, "PUBLISHER_JWT_KEY")
}
if len(options.SubscriberJWTKey) == 0 {
missingEnv = append(missingEnv, "SUBSCRIBER_JWT_KEY")
}
if len(options.CertFile) != 0 && len(options.KeyFile) == 0 {
missingEnv = append(missingEnv, "KEY_FILE")
}
if len(options.KeyFile) != 0 && len(options.CertFile) == 0 {
missingEnv = append(missingEnv, "CERT_FILE")
}

if len(missingEnv) > 0 {
return nil, fmt.Errorf("The following environment variable must be defined: %s", missingEnv)
@@ -51,3 +51,19 @@ func TestMissingEnv(t *testing.T) {
_, err := NewOptionsFromEnv()
assert.EqualError(t, err, "The following environment variable must be defined: [PUBLISHER_JWT_KEY SUBSCRIBER_JWT_KEY]")
}

func TestMissingKeyFile(t *testing.T) {
os.Setenv("CERT_FILE", "foo")
defer os.Unsetenv("CERT_FILE")

_, err := NewOptionsFromEnv()
assert.EqualError(t, err, "The following environment variable must be defined: [PUBLISHER_JWT_KEY SUBSCRIBER_JWT_KEY KEY_FILE]")
}

func TestMissingCertFile(t *testing.T) {
os.Setenv("KEY_FILE", "foo")
defer os.Unsetenv("KEY_FILE")

_, err := NewOptionsFromEnv()
assert.EqualError(t, err, "The following environment variable must be defined: [PUBLISHER_JWT_KEY SUBSCRIBER_JWT_KEY CERT_FILE]")
}
@@ -2,6 +2,7 @@ package hub

import (
"context"
"crypto/tls"
"io/ioutil"
"net/http"
"net/url"
@@ -14,22 +15,30 @@ import (
)

const testURL = "http://" + testAddr + "/hub"
const testSecureURL = "https://" + testAddr + "/hub"

func TestSecurityOptions(t *testing.T) {
h := createAnonymousDummy()
h.options.Demo = true
h.options.CorsAllowedOrigins = []string{"*"}
h.options.CertFile = "../fixtures/tls/server.crt"
h.options.KeyFile = "../fixtures/tls/server.key"

h.Start()
go func() {
h.Serve()
}()

// This is a self-signed certificate
transport := &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
client := http.Client{Transport: transport, Timeout: time.Duration(100 * time.Millisecond)}

// loop until the web server is ready
var resp *http.Response
client := http.Client{Timeout: time.Duration(100 * time.Millisecond)}
for resp == nil {
resp, _ = client.Get("http://" + testAddr + "/hub")
resp, _ = client.Get(testSecureURL)
}

assert.Equal(t, "default-src 'self'", resp.Header.Get("Content-Security-Policy"))
@@ -38,7 +47,7 @@ func TestSecurityOptions(t *testing.T) {
assert.Equal(t, "1; mode=block", resp.Header.Get("X-Xss-Protection"))

// Preflight request
req, _ := http.NewRequest("OPTIONS", "http://"+testAddr+"/hub", nil)
req, _ := http.NewRequest("OPTIONS", testSecureURL, nil)
req.Header.Add("Origin", "https://example.com")
req.Header.Add("Access-Control-Request-Headers", "authorization")
req.Header.Add("Access-Control-Request-Method", "GET")

0 comments on commit f82ddd9

Please sign in to comment.