From b1aa9622336d1dd6660f3ce314ce3a67a783b7c3 Mon Sep 17 00:00:00 2001 From: Anthony Romano Date: Wed, 19 Jul 2017 12:41:43 -0700 Subject: [PATCH 1/3] transport: use reverse lookup to match wildcard DNS SAN Fixes #8268 --- pkg/transport/listener_tls.go | 63 +++++++++++++++++++++++++++++------ 1 file changed, 53 insertions(+), 10 deletions(-) diff --git a/pkg/transport/listener_tls.go b/pkg/transport/listener_tls.go index 545e0c43db3..6f1600945cc 100644 --- a/pkg/transport/listener_tls.go +++ b/pkg/transport/listener_tls.go @@ -21,6 +21,7 @@ import ( "fmt" "io/ioutil" "net" + "strings" "sync" ) @@ -206,20 +207,62 @@ func checkCertSAN(ctx context.Context, cert *x509.Certificate, remoteAddr string } } if len(cert.DNSNames) > 0 { - for _, dns := range cert.DNSNames { - addrs, lerr := net.DefaultResolver.LookupHost(ctx, dns) - if lerr != nil { - continue + ok, err := isHostInDNS(ctx, h, cert.DNSNames) + if ok { + return nil + } + errStr := "" + if err != nil { + errStr = " (" + err.Error() + ")" + } + return fmt.Errorf("tls: %q does not match any of DNSNames %q"+errStr, h, cert.DNSNames) + } + return nil +} + +func isHostInDNS(ctx context.Context, host string, dnsNames []string) (ok bool, err error) { + // reverse lookup + wildcards, names := []string{}, []string{} + for _, dns := range dnsNames { + if strings.HasPrefix(dns, "*.") { + wildcards = append(wildcards, dns[1:]) + } else { + names = append(names, dns) + } + } + lnames, lerr := net.DefaultResolver.LookupAddr(ctx, host) + for _, name := range lnames { + // strip trailing '.' from PTR record + if name[len(name)-1] == '.' { + name = name[:len(name)-1] + } + for _, wc := range wildcards { + if strings.HasSuffix(name, wc) { + return true, nil } - for _, addr := range addrs { - if addr == h { - return nil - } + } + for _, n := range names { + if n == name { + return true, nil } } - return fmt.Errorf("tls: %q does not match any of DNSNames %q", h, cert.DNSNames) } - return nil + err = lerr + + // forward lookup + for _, dns := range names { + addrs, lerr := net.DefaultResolver.LookupHost(ctx, dns) + if lerr != nil { + err = lerr + continue + } + for _, addr := range addrs { + if addr == host { + return true, nil + } + } + } + return false, err } func (l *tlsListener) Close() error { From 52dd13fa355be5e5c388e7517608f298bbff56a1 Mon Sep 17 00:00:00 2001 From: Anthony Romano Date: Thu, 20 Jul 2017 15:13:22 -0700 Subject: [PATCH 2/3] fixtures: generate wildcard DNS SAN cert DNS: *.etcd.local --- integration/fixtures/ca.crt | 52 +++++----- integration/fixtures/gencerts.sh | 14 ++- integration/fixtures/revoke.crl | Bin 778 -> 782 bytes integration/fixtures/server-revoked.crt | 54 +++++----- .../fixtures/server-revoked.key.insecure | 98 +++++++++--------- integration/fixtures/server-wildcard.crt | 30 ++++++ .../fixtures/server-wildcard.key.insecure | 27 +++++ integration/fixtures/server.crt | 54 +++++----- integration/fixtures/server.key.insecure | 98 +++++++++--------- 9 files changed, 248 insertions(+), 179 deletions(-) create mode 100644 integration/fixtures/server-wildcard.crt create mode 100644 integration/fixtures/server-wildcard.key.insecure diff --git a/integration/fixtures/ca.crt b/integration/fixtures/ca.crt index c042b29c51a..3ccaadbfe67 100644 --- a/integration/fixtures/ca.crt +++ b/integration/fixtures/ca.crt @@ -1,33 +1,33 @@ -----BEGIN CERTIFICATE----- -MIIFrjCCA5agAwIBAgIUCwleGnPMSwoODcFBty/IC/L6CUIwDQYJKoZIhvcNAQEN +MIIFrjCCA5agAwIBAgIUXWXsuLEZuHtKgeQSIVthb14+9EQwDQYJKoZIhvcNAQEN BQAwbzEMMAoGA1UEBhMDVVNBMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQH Ew1TYW4gRnJhbmNpc2NvMQ0wCwYDVQQKEwRldGNkMRYwFAYDVQQLEw1ldGNkIFNl -Y3VyaXR5MQswCQYDVQQDEwJjYTAeFw0xNzA2MTYyMDMzMDBaFw0yNzA2MTQyMDMz +Y3VyaXR5MQswCQYDVQQDEwJjYTAeFw0xNzA3MjAyMjA1MDBaFw0yNzA3MTgyMjA1 MDBaMG8xDDAKBgNVBAYTA1VTQTETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UE BxMNU2FuIEZyYW5jaXNjbzENMAsGA1UEChMEZXRjZDEWMBQGA1UECxMNZXRjZCBT ZWN1cml0eTELMAkGA1UEAxMCY2EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK -AoICAQDhp9t3WUGpaRtbM52hudffXT0V9dbl1ac4DD37MdIit2yDFsut1IxSgZ40 -9FliVStAWzDhZL6nX4rpInXOEI1WV1xKXu+T8i2LcxnW4QjvKTLMpBdF6q0KzsiZ -CV5uNTQvIuR/hQN4ij03j75nnj/ds5TUCQfz/Mh6T/xwbHp1XUimcVnh38+q+ZE2 -eCmEvcdAEQ9DXj7WTDD4dN0xaJz8rvZSVWVBwuP7dtN54FJmJyRXcCuus5pUd/Lm -n4mEEZ3DLceUM13AK/gwAS3SNHOwuH4pl6IKJ10qSUdzrB+Lt0rx2iqyodN/EMnh -kYJRWG8mv5spN/s695A3MLKk0hZ/bkys91n0hycaPFg8TwxmdXP8P/AOFQXyK4x9 -YhvtF6mGhD/RHqdaujF/tCH34DpMVY9ObTu59R/6qG4Zr3KfqpDp5iM1LjggT4QU -2JBn9zc5rAd/j3clcgfJfW5CZ8ek31HLIKPm5pa8q5l4qL7qWu0FjZTpSgUps29O -ekRhtSCFI3R8TZkWOAV5DM+FkXJACsOJT/Ds4/BFgia05dglNEkFTuSDAT6BfQjy -bghuxYkFP3bPj8rflM9AhXsfHM5qEcSkZcSdjHqn4u2uvRnpc1/T8MVADqkpMukf -IUabqJ0Iy5SHXmqouO2ZkPG8C4ytkUuQW3WKrLNBSXRJVQ3pAQIDAQABo0IwQDAO -BgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUiZ/XuFgs -FCGDhz0eMvNuB/aMvSgwDQYJKoZIhvcNAQENBQADggIBAHHsADO+SiUi51IibgF0 -gdKMurtJq2cdC8YNjkkDeI8jgIljrEi7HgYs9l3IbfRmBd5/5DRdVn8NLkjEVXSL -fcKfGHqJSsA7qLylfXoBUAwcwObdo0fTMBn+NEfK3zb5BndClTaQRs2XiHmEwntR -HUcSruOsWOJs9dxYHe89odMLIZv8rhbEH1vUIKC2vTnxF8vysJfx/ob3kpWiGClO -pwpt5sc/BkWM+zo8gVnypqZzhWkYMJj5xrz0/1Wk9I8NwJnsjCcyFB+GMwX6b0ei -TUU2MgS3krmG8A43JwUzPs8DVkQeWvsZejZzRCqDwlTwXM9pP8zGJFV0MYpyszc6 -Fx+qM2Xso5Gyja8RgHDlgJKAtnZe/vu6ocgnRXeLzLsWYVN3on2PLwL3dXxjciL0 -y4uCuLBb9ckbG3jJd4uvc6OdKVV47xsL6qgm4knHijclhkG4DXojAmdY2g0S0ptX -ingwbLw5YHARLrOeXCgRp23SzXdvtnzbfgbI+9YQrxet8vFWg2Y+7NP2iF2/JufU -HcPpuVGjsLkZBj4j9tOhUMDFk8esy6dBVpJ9+4d9slY0Eg5s5+XmnnVb6+QOCEii -Gcq4nDgM8VEJxYFX9pxpjtiwiy3KVOP5QU+H0fjYfKIAi3IUdW03vzIu/H0vPk5h -zceob2+4yKU2W+OQNeVChUzc +AoICAQCmtwjSg7gQBcVaoMycpePT0qoM0SKJVuvQRXIjL53/Bae5zuWiBdDVTElf +6OOFkjqPAxU7t28jmn/EqNcKkaVuFcFtVbgyD+vXWQITGSGfE1hmqVUcpbSpzLim +UIFNy6slMeUdFGiLG7/4P6mCHePgoW9r1+J2oAHSooCzJDqLNAGkgHhFQPhBC62G +3QrY2gwKlJ6Yl+2Ilb+bdT4PJq8sSlyAynPFTp07hnciEG6Ef6IQxc9pZb+UCa2A +Cyn9RU83AWj/aIcdlB8iNf86np4wFe8VEkgBdih91vfEzvoMhJZYBb0b0CnrRo1e +jVXAJkqTbajQM+yxlvlhB2PNCZusJa69eDCtnnO29MbTjOTqElTxlvU9c3huZycc +VMDgzyzm87F+Me3vh/6l6VC4Pm0zkA3XdwydncxreFoD/G+fQK2m6wXWzIsSGwqG +gzgAq8neJFfkcgzRu6WU1S8S/idqK9AoQAFIEPXYyIk3+K6JzHxhYZIBFE3OrZ58 +oEo2PCP4snzTysZk7eWCe/WTZvReKtytzKAIS/CcjxsmgaviHee5tlV/rIghAxq8 +QFnldJ1J9AtqPriRv0+EDFwOL8eyA+cVbWgX9UR0gWLe5lUqooowpq2ioWHG5F1m +cyi0u8cUtf5YZN6SVktQUdddsOCFfxvCU1NigxVxqs1ZWhSSrwIDAQABo0IwQDAO +BgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUp//gP4sC +l0dWZOXJWaPOYED2YdQwDQYJKoZIhvcNAQENBQADggIBABXyQox/T4kD+sLuTAp9 +IP6Hr/XaHmKj3Zkrp8DdWt62R13ugCdWA8hu2yYzu92mSHBGbssaSaLzsNeb+LqE +/gSNQBvbfV0btQN2h+B3+BmEUuiv4ZTMPNArGfG7L1p35kH0NL46Bcssu59XSFLe +RIc7M5yT/C5+f/muhIxsAT6AdnwwkcxjQvQj9257S1gonOjLmmsVXW+Z+G9Y3YIf +hp84yvrJh86QVGsDC5Cu5i9kC/0CodCouIlBjWdELZDWV5KvbLAuWoQ5Jp1Y6+Jo +Dhx+2HB9mKmDWJfS8rWd//EiX/JH8iSMSaltmrzk6PYlWFAuM8jycDyyQI4mCe6J +wPMRyism7cowcGqHb+Nn2OiPvJtX6bGcVb8DbaGDmfgPdACqjdguzLHnaFyLmDe/ +la0y1FAfW7jOyQrXEzqB4tJ8ZhI+HxRiXAh8ahBcKnMQFpjsEse03d2t65ZPDgev +NjIcoqhbANpYXdygux4hJNCT8KB194frC+eK0XqyO8BJYvid1Qp7SlnpFdEo1vMK +whLje6QkrgIyqoTP1+SiB3R79rtg+41bTb8paPJs9AqNaxS/l2bSnWnRvdkiJv89 +YWgQGNO21XW+VbNV7Z0tMglmTvJc0ubbV5zZpVsuSOAQjdRXKieAxWAePrzDx5AM +ZiQgL5b9icqHm0aV7bcfp8H+ -----END CERTIFICATE----- diff --git a/integration/fixtures/gencerts.sh b/integration/fixtures/gencerts.sh index 6ec4fb377c9..912e04903e7 100755 --- a/integration/fixtures/gencerts.sh +++ b/integration/fixtures/gencerts.sh @@ -12,7 +12,9 @@ fi cfssl gencert --initca=true ./ca-csr.json | cfssljson --bare ./ca mv ca.pem ca.crt +openssl x509 -in ca.crt -noout -text +# generate DNS: localhost, IP: 127.0.0.1, CN: example.com certificates cfssl gencert \ --ca ./ca.crt \ --ca-key ./ca-key.pem \ @@ -21,14 +23,24 @@ cfssl gencert \ mv server.pem server.crt mv server-key.pem server.key.insecure +# generate revoked certificates and crl cfssl gencert --ca ./ca.crt \ --ca-key ./ca-key.pem \ --config ./gencert.json \ ./server-ca-csr.json 2>revoked.stderr | cfssljson --bare ./server-revoked mv server-revoked.pem server-revoked.crt mv server-revoked-key.pem server-revoked.key.insecure - grep serial revoked.stderr | awk ' { print $9 } ' >revoke.txt cfssl gencrl revoke.txt ca.crt ca-key.pem | base64 -d >revoke.crl +# generate wildcard certificates DNS: *.etcd.local +cfssl gencert \ + --ca ./ca.crt \ + --ca-key ./ca-key.pem \ + --config ./gencert.json \ + ./server-ca-csr-wildcard.json | cfssljson --bare ./server-wildcard +mv server-wildcard.pem server-wildcard.crt +mv server-wildcard-key.pem server-wildcard.key.insecure + + rm -f *.csr *.pem *.stderr *.txt diff --git a/integration/fixtures/revoke.crl b/integration/fixtures/revoke.crl index aa3a71c2a46274b2c10dee777977c1b94fbb4ee9..dd378e0926590e97a81e2400bb1b8a5a624add57 100644 GIT binary patch delta 660 zcmV;F0&D$>295>?FoFXLFoE-t2Z(<+GB7eSF)%VXS{Ds5H!wFcH!?CYFfuq=Fe@-A z0u%;hIDYiK|J>)v+~fpOdJ_g7pe+{>F*h(bGB7bUF)%VXEigAQFrXtaAut~X163U( z1Q;+DfE1_y;6IB3mq%7)<;hv2&R{_HVbm}U1_>&LNQUs* zCnc5`MMh+z9tSpsG-y;TXzGS>nowdR1GL~#o;`t_?8OQy5m>^55@Mt{{mWoFTXU~C z^nmwt`YO5VDb83MBCH?I@yoDyuW1Wg{vG>(+jXQdh_V~T%x%CPz3H7=W}`WA+n2eW zD5v*+TUzrMK-*40zfvq$Q#gMKZyO%LquJ1s3|T%;m~rq6T~yGW$VliR=eaH2e~U^d zb$Oh0#hNdJMPCa)D_*Qdvy(Amzh2m7V7NC>(RyLN1>{!$&C`8|wrHGaEbj8S+`QB} zy=1nkwGDruk_o-axJ`diX%=cyV(7dU%voad?q!5H3Q?7ckTZGkF&}Jv0I*{0;G7E4;sUO7`M@)7Zq*(_U5=e4g!1`4|kN#1}g-cD)}2e%_unl delta 655 zcmV;A0&x9~28sp;FoFXHFoExp2Z(<*F*Y(VGdM9bS{Ds5H!wCbGcqtUI59I?Fefl2 z0u%!x$QSSLA=Jt#{cy&`J_+^y(lmZ1_>&LNQUwre)a*XEI*fjvg1WAcj*b$=>#`7AtA1=4P{o6 zt98&I922p8W?r5N#yS?6NojxlqTnRcNY!CJGz#YCJ(5Oy2sB?WHHetl8OflXV!ZL@ zD*Y*_VigE$x+0tN)|fvGrx*R3mWMqiR{NJ-LjJj1?7nM~w$QwXU|SKgu2l-+2p?e> zaF_%!FCD1yRFE87j6EIN(bGF`Kt5^Bj)P5Mt(eU9YwB?fFriM{pF zoYLeyudi^Fin^lm@PKvfHXP9&A6|Y?3A%*R2Q53eKi-=I{#_I}Ne@GWnv%kVdHbt4 zKHJ*rQ=ZPUD8sETvIu|A!OEr>?{+@w+a=fjp6qfNEj-yf?e^j;VHr+1$>V1N2@-ny zTbRchNhoBP4>amSk`vF^pK)UH6H0pBXJ_=(Sl27-NEgtrvRrZ-WgR%n_sGXFqYv|< pL%B-5{{zm31Bcc+(N7WaosXk9O^Rg7{poR Date: Fri, 21 Jul 2017 16:24:17 -0700 Subject: [PATCH 3/3] e2e/docker: docker image for testing wildcard DNS --- e2e/docker/Dockerfile | 12 ++++++++++++ e2e/docker/Makefile | 7 +++++++ e2e/docker/Procfile.tls | 6 ++++++ e2e/docker/etcd.zone | 14 ++++++++++++++ e2e/docker/named.conf | 23 +++++++++++++++++++++++ e2e/docker/rdns.zone | 13 +++++++++++++ e2e/docker/resolv.conf | 1 + e2e/docker/run.sh | 8 ++++++++ 8 files changed, 84 insertions(+) create mode 100644 e2e/docker/Dockerfile create mode 100644 e2e/docker/Makefile create mode 100644 e2e/docker/Procfile.tls create mode 100644 e2e/docker/etcd.zone create mode 100644 e2e/docker/named.conf create mode 100644 e2e/docker/rdns.zone create mode 100644 e2e/docker/resolv.conf create mode 100755 e2e/docker/run.sh diff --git a/e2e/docker/Dockerfile b/e2e/docker/Dockerfile new file mode 100644 index 00000000000..c94e1612a2a --- /dev/null +++ b/e2e/docker/Dockerfile @@ -0,0 +1,12 @@ +FROM golang:1.8.3-stretch +LABEL Description="Image for etcd DNS testing" +RUN apt update -y +RUN go get github.com/mattn/goreman +RUN apt install -y bind9 +RUN mkdir /var/bind +RUN chown bind /var/bind +ADD Procfile.tls /Procfile.tls +ADD run.sh /run.sh +ADD named.conf etcd.zone rdns.zone /etc/bind/ +ADD resolv.conf /etc/resolv.conf +CMD ["/run.sh"] \ No newline at end of file diff --git a/e2e/docker/Makefile b/e2e/docker/Makefile new file mode 100644 index 00000000000..7ec14d42cb1 --- /dev/null +++ b/e2e/docker/Makefile @@ -0,0 +1,7 @@ +# run makefile from repo root + +docker-dns-build: + docker build -t etcd-dns e2e/docker/ + +docker-dns-test: docker-dns-build + docker run --dns 127.0.0.1 --rm -v `pwd`/bin/:/etcd -v `pwd`/integration/fixtures:/certs -w /etcd -t etcd-dns diff --git a/e2e/docker/Procfile.tls b/e2e/docker/Procfile.tls new file mode 100644 index 00000000000..d8b79020567 --- /dev/null +++ b/e2e/docker/Procfile.tls @@ -0,0 +1,6 @@ +# Use goreman to run `go get github.com/mattn/goreman` +etcd1: ./etcd --name infra1 --listen-client-urls https://127.0.0.1:2379 --advertise-client-urls https://m1.etcd.local:2379 --listen-peer-urls https://127.0.0.1:12380 --initial-advertise-peer-urls=https://m1.etcd.local:12380 --initial-cluster-token etcd-cluster-1 --initial-cluster=infra1=https://m1.etcd.local:12380,infra2=https://m2.etcd.local:22380,infra3=https://m3.etcd.local:32380 --initial-cluster-state new --enable-pprof --peer-cert-file=/certs/server-wildcard.crt --peer-key-file=/certs/server-wildcard.key.insecure --peer-client-cert-auth --cert-file=/certs/server-wildcard.crt --key-file=/certs/server-wildcard.key.insecure --peer-trusted-ca-file=/certs/ca.crt --trusted-ca-file=/certs/ca.crt + +etcd2: ./etcd --name infra2 --listen-client-urls https://127.0.0.1:22379 --advertise-client-urls https://m2.etcd.local:22379 --listen-peer-urls https://127.0.0.1:22380 --initial-advertise-peer-urls=https://m2.etcd.local:22380 --initial-cluster-token etcd-cluster-1 --initial-cluster=infra1=https://m1.etcd.local:12380,infra2=https://m2.etcd.local:22380,infra3=https://m3.etcd.local:32380 --initial-cluster-state new --enable-pprof --peer-cert-file=/certs/server-wildcard.crt -peer-key-file=/certs/server-wildcard.key.insecure --peer-client-cert-auth --cert-file=/certs/server-wildcard.crt --key-file=/certs/server-wildcard.key.insecure --peer-trusted-ca-file=/certs/ca.crt --trusted-ca-file=/certs/ca.crt + +etcd3: ./etcd --name infra3 --listen-client-urls https://127.0.0.1:32379 --advertise-client-urls https://m3.etcd.local:32379 --listen-peer-urls https://127.0.0.1:32380 --initial-advertise-peer-urls=https://m3.etcd.local:32380 --initial-cluster-token etcd-cluster-1 --initial-cluster=infra1=https://m1.etcd.local:12380,infra2=https://m2.etcd.local:22380,infra3=https://m3.etcd.local:32380 --initial-cluster-state new --enable-pprof --peer-cert-file=/certs/server-wildcard.crt --peer-key-file=/certs/server-wildcard.key.insecure --peer-client-cert-auth --cert-file=/certs/server-wildcard.crt --key-file=/certs/server-wildcard.key.insecure --peer-trusted-ca-file=/certs/ca.crt --trusted-ca-file=/certs/ca.crt diff --git a/e2e/docker/etcd.zone b/e2e/docker/etcd.zone new file mode 100644 index 00000000000..03c15fe8e66 --- /dev/null +++ b/e2e/docker/etcd.zone @@ -0,0 +1,14 @@ +$TTL 86400 +@ IN SOA etcdns.local. root.etcdns.local. ( + 100500 ; Serial + 604800 ; Refresh + 86400 ; Retry + 2419200 ; Expire + 86400 ) ; Negative Cache TTL + IN NS ns.etcdns.local. + IN A 127.0.0.1 + +ns IN A 127.0.0.1 +m1 IN A 127.0.0.1 +m2 IN A 127.0.0.1 +m3 IN A 127.0.0.1 diff --git a/e2e/docker/named.conf b/e2e/docker/named.conf new file mode 100644 index 00000000000..83549305c34 --- /dev/null +++ b/e2e/docker/named.conf @@ -0,0 +1,23 @@ +options { + directory "/var/bind"; + listen-on { 127.0.0.1; }; + listen-on-v6 { none; }; + allow-transfer { + none; + }; + // If you have problems and are behind a firewall: + query-source address * port 53; + pid-file "/var/run/named/named.pid"; + allow-recursion { none; }; + recursion no; +}; + +zone "etcd.local" IN { + type master; + file "/etc/bind/etcd.zone"; +}; + +zone "0.0.127.in-addr.arpa" { + type master; + file "/etc/bind/rdns.zone"; +}; diff --git a/e2e/docker/rdns.zone b/e2e/docker/rdns.zone new file mode 100644 index 00000000000..fb71b30b1fa --- /dev/null +++ b/e2e/docker/rdns.zone @@ -0,0 +1,13 @@ +$TTL 86400 +@ IN SOA etcdns.local. root.etcdns.local. ( + 100500 ; Serial + 604800 ; Refresh + 86400 ; Retry + 2419200 ; Expire + 86400 ) ; Negative Cache TTL + IN NS ns.etcdns.local. + IN A 127.0.0.1 + +1 IN PTR m1.etcd.local. +1 IN PTR m2.etcd.local. +1 IN PTR m3.etcd.local. diff --git a/e2e/docker/resolv.conf b/e2e/docker/resolv.conf new file mode 100644 index 00000000000..bbc8559cd54 --- /dev/null +++ b/e2e/docker/resolv.conf @@ -0,0 +1 @@ +nameserver 127.0.0.1 diff --git a/e2e/docker/run.sh b/e2e/docker/run.sh new file mode 100755 index 00000000000..e020bcbecab --- /dev/null +++ b/e2e/docker/run.sh @@ -0,0 +1,8 @@ +#!/bin/sh + +/etc/init.d/bind9 start +# get rid of hosts so go lookup won't resolve 127.0.0.1 to localhost +cat /dev/null >/etc/hosts +goreman -f /Procfile.tls start & +sleep 5s +ETCDCTL_API=3 ./etcdctl --cacert=/certs/ca.crt --endpoints=https://m1.etcd.local:2379 put abc def