From 4eeb56930489a2e56096f4c11de8c9a6a835a78b Mon Sep 17 00:00:00 2001 From: GLYASAI Date: Mon, 3 Dec 2018 13:52:48 +0800 Subject: [PATCH] [ADD] create cert automaticlly --- gateway/controller/openresty/rbd_endpoints.go | 6 +- gateway/controller/openresty/service.go | 79 ++++++-- util/cert/certutil.go | 172 ++++++++++++++++++ util/cert/certutil_test.go | 57 ++++++ 4 files changed, 295 insertions(+), 19 deletions(-) create mode 100644 util/cert/certutil.go create mode 100644 util/cert/certutil_test.go diff --git a/gateway/controller/openresty/rbd_endpoints.go b/gateway/controller/openresty/rbd_endpoints.go index 1b5e7bd8a..52e646441 100644 --- a/gateway/controller/openresty/rbd_endpoints.go +++ b/gateway/controller/openresty/rbd_endpoints.go @@ -87,12 +87,12 @@ func mavenGoodrainMe() (*model.Server, *model.Upstream) { return svr, us } -func goodrainMe() (*model.Server, *model.Upstream) { +func goodrainMe(cfgPath string) (*model.Server, *model.Upstream) { svr := &model.Server{ Listen: fmt.Sprintf("%s:%d %s", "0.0.0.0", 443, "ssl"), ServerName: "goodrain.me", - SSLCertificate: "cert/server.crt", - SSLCertificateKey: "cert/server.key", + SSLCertificate: fmt.Sprintf("%s/%s", cfgPath, "ssl/server.crt"), + SSLCertificateKey: fmt.Sprintf("%s/%s", cfgPath, "ssl/server.key"), ClientMaxBodySize: model.Size{Num:0, Unit:"k"}, ChunkedTransferEncoding: true, Locations: []*model.Location{ diff --git a/gateway/controller/openresty/service.go b/gateway/controller/openresty/service.go index e58d51080..fb5e79d09 100644 --- a/gateway/controller/openresty/service.go +++ b/gateway/controller/openresty/service.go @@ -2,8 +2,11 @@ package openresty import ( "bytes" + "crypto/x509/pkix" + "encoding/asn1" "encoding/json" "fmt" + "github.com/goodrain/rainbond/util/cert" "net/http" "os" "path" @@ -76,11 +79,11 @@ func (osvc *OrService) Start(errCh chan error) { errCh <- fmt.Errorf("Can't not new nginx config: %s", err.Error()) return } - // if osvc.config.EnableRbdEndpoints { - // if err := osvc.newRbdServers(); err != nil { - // errCh <- err // TODO: consider if it is right - // } - // } + if osvc.config.EnableRbdEndpoints { + if err := osvc.newRbdServers(); err != nil { + errCh <- err // TODO: consider if it is right + } + } cmd := nginxExecCommand() cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr @@ -94,7 +97,7 @@ func (osvc *OrService) Start(errCh chan error) { if err := cmd.Wait(); err != nil { errCh <- err } - errCh <- fmt.Errorf("nginx process is exit") + //errCh <- fmt.Errorf("nginx process is exit") }() } @@ -280,9 +283,15 @@ func (osvc *OrService) newRbdServers() error { return err } - lesrv, leus := langGoodrainMe() - mesrv, meus := mavenGoodrainMe() - gesrv, geus := goodrainMe() + // create cert + err := createCert(cfgPath, "goodrain.me") + if err != nil { + return err + } + + lesrv, _ := langGoodrainMe() + mesrv, _ := mavenGoodrainMe() + gesrv, _ := goodrainMe(cfgPath) if err := template.NewServerTemplateWithCfgPath([]*model.Server{ lesrv, mesrv, @@ -292,12 +301,50 @@ func (osvc *OrService) newRbdServers() error { } // upstreams - if err := template.NewUpstreamTemplateWithCfgPath([]*model.Upstream{ - leus, - meus, - geus, - }, "upstreams-http-rbd.tmpl", cfgPath, "upstreams.default.http.conf"); err != nil { - return err - } + //if err := template.NewUpstreamTemplateWithCfgPath([]*model.Upstream{ + // leus, + // meus, + // geus, + //}, "upstreams-http-rbd.tmpl", cfgPath, "upstreams.default.http.conf"); err != nil { + // return err + //} + return nil +} + +func createCert(cfgPath string, cn string) error { + if e := os.MkdirAll(fmt.Sprintf("%s/%s", cfgPath, "ssl"), 0777); e != nil { + return e + } + baseinfo := cert.CertInformation{Country: []string{"CN"}, Organization: []string{"Goodrain"}, IsCA: true, + OrganizationalUnit: []string{"Rainbond"}, EmailAddress: []string{"zengqg@goodrain.com"}, + Locality: []string{"BeiJing"}, Province: []string{"BeiJing"}, CommonName: cn, + Domains: []string{"goodrain.me"}, + CrtName: fmt.Sprintf("%s/%s", cfgPath, "ssl/ca.pem"), + KeyName: fmt.Sprintf("%s/%s", cfgPath, "ssl/ca.key")} + + err := cert.CreateCRT(nil, nil, baseinfo) + if err != nil { + logrus.Errorf("Create crt error: ", err) + return err + } + crtInfo := baseinfo + crtInfo.IsCA = false + crtInfo.CrtName = fmt.Sprintf("%s/%s", cfgPath, "ssl/server.crt") + crtInfo.KeyName = fmt.Sprintf("%s/%s", cfgPath, "ssl/server.key") + crtInfo.Names = []pkix.AttributeTypeAndValue{{asn1.ObjectIdentifier{2, 1, 3}, "MAC_ADDR"}} + + crt, pri, err := cert.Parse(baseinfo.CrtName, baseinfo.KeyName) + if err != nil { + logrus.Errorf("Parse crt error,Error info:", err) + return err + } + err = cert.CreateCRT(crt, pri, crtInfo) + if err != nil { + logrus.Errorf("Create crt error,Error info:", err) + return err + } + + logrus.Info("Create certificate for goodrain.me successfully") + return nil } diff --git a/util/cert/certutil.go b/util/cert/certutil.go new file mode 100644 index 000000000..c42aead30 --- /dev/null +++ b/util/cert/certutil.go @@ -0,0 +1,172 @@ +// RAINBOND, Application Management Platform +// Copyright (C) 2014-2017 Goodrain Co., Ltd. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. For any non-GPL usage of Rainbond, +// one or multiple Commercial Licenses authorized by Goodrain Co., Ltd. +// must be obtained first. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +package cert + +import ( + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "crypto/x509/pkix" + "encoding/pem" + "io/ioutil" + "math/big" + rd "math/rand" + "net" + "os" + "time" +) + +func init() { + rd.Seed(time.Now().UnixNano()) +} + +type CertInformation struct { + Country []string + Organization []string + OrganizationalUnit []string + EmailAddress []string + Province []string + Locality []string + CommonName string + CrtName, KeyName string + IsCA bool + Names []pkix.AttributeTypeAndValue + IPAddresses []net.IP + Domains []string +} + +//CreateCRT create crt +func CreateCRT(RootCa *x509.Certificate, RootKey *rsa.PrivateKey, info CertInformation) error { + Crt := newCertificate(info) + Key, err := rsa.GenerateKey(rand.Reader, 2048) + if err != nil { + return err + } + + var buf []byte + if RootCa == nil || RootKey == nil { + //create ca cert + buf, err = x509.CreateCertificate(rand.Reader, Crt, Crt, &Key.PublicKey, Key) + if err != nil { + return err + } + keybuf := x509.MarshalPKCS1PrivateKey(Key) + err = write(info.KeyName, "PRIVATE KEY", keybuf) + } else { + //create cert by ca + buf, err = x509.CreateCertificate(rand.Reader, Crt, RootCa, &Key.PublicKey, RootKey) + if err != nil { + return err + } + keybuf := x509.MarshalPKCS1PrivateKey(Key) + err = write(info.KeyName, "RSA PRIVATE KEY", keybuf) + } + if err != nil { + return err + } + err = write(info.CrtName, "CERTIFICATE", buf) + if err != nil { + return err + } + return nil +} + +//编码写入文件 +func write(filename, Type string, p []byte) error { + File, err := os.Create(filename) + defer File.Close() + if err != nil { + return err + } + var b = &pem.Block{Bytes: p, Type: Type} + return pem.Encode(File, b) +} + +//Parse Parse +func Parse(crtPath, keyPath string) (rootcertificate *x509.Certificate, rootPrivateKey *rsa.PrivateKey, err error) { + rootcertificate, err = ParseCrt(crtPath) + if err != nil { + return + } + rootPrivateKey, err = ParseKey(keyPath) + return +} + +//ParseCrt ParseCrt +func ParseCrt(path string) (*x509.Certificate, error) { + buf, err := ioutil.ReadFile(path) + if err != nil { + return nil, err + } + p := &pem.Block{} + p, buf = pem.Decode(buf) + return x509.ParseCertificate(p.Bytes) +} + +//ParseKey ParseKey +func ParseKey(path string) (*rsa.PrivateKey, error) { + buf, err := ioutil.ReadFile(path) + if err != nil { + return nil, err + } + p, buf := pem.Decode(buf) + return x509.ParsePKCS1PrivateKey(p.Bytes) +} + +func newCertificate(info CertInformation) *x509.Certificate { + return &x509.Certificate{ + SerialNumber: big.NewInt(rd.Int63()), + Subject: pkix.Name{ + Country: info.Country, + Organization: info.Organization, + OrganizationalUnit: info.OrganizationalUnit, + Province: info.Province, + CommonName: info.CommonName, + Locality: info.Locality, + ExtraNames: info.Names, + }, + NotBefore: time.Now(), //start time + NotAfter: time.Now().AddDate(20, 0, 0), //end time + BasicConstraintsValid: true, //basic + IsCA: info.IsCA, //是否是根证书 + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth}, //证书用途 + KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign, + EmailAddresses: info.EmailAddress, + IPAddresses: info.IPAddresses, + DNSNames: info.Domains, + } +} + +//CreateCertInformation CreateCertInformation +func CreateCertInformation() CertInformation { + baseinfo := CertInformation{ + Country: []string{"CN"}, + Organization: []string{"Goodrain"}, + OrganizationalUnit: []string{"goodrain rainbond"}, + EmailAddress: []string{"zengqg@goodrain.com"}, + Locality: []string{"BeiJing"}, + Province: []string{"BeiJing"}, + CommonName: "rainbond", + CrtName: "", + KeyName: "", + Domains: []string{"goodrain.me"}, + } + baseinfo.IPAddresses = []net.IP{net.ParseIP("127.0.0.1")} + return baseinfo +} diff --git a/util/cert/certutil_test.go b/util/cert/certutil_test.go new file mode 100644 index 000000000..852d6b088 --- /dev/null +++ b/util/cert/certutil_test.go @@ -0,0 +1,57 @@ +// RAINBOND, Application Management Platform +// Copyright (C) 2014-2017 Goodrain Co., Ltd. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. For any non-GPL usage of Rainbond, +// one or multiple Commercial Licenses authorized by Goodrain Co., Ltd. +// must be obtained first. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +package cert + +import ( + "crypto/x509/pkix" + "encoding/asn1" + "testing" +) + +func Test_crt(t *testing.T) { + baseinfo := CertInformation{Country: []string{"CN"}, Organization: []string{"Goodrain"}, IsCA: true, + OrganizationalUnit: []string{"work-stacks"}, EmailAddress: []string{"zengqg@goodrain.com"}, + Locality: []string{"BeiJing"}, Province: []string{"BeiJing"}, CommonName: "Work-Stacks", + Domains: []string{"goodrain.me"},CrtName: "../../test/ssl/ca.pem", KeyName: "../../test/ssl/ca.key"} + + err := CreateCRT(nil, nil, baseinfo) + if err != nil { + t.Log("Create crt error,Error info:", err) + return + } + crtinfo := baseinfo + crtinfo.IsCA = false + crtinfo.CrtName = "../../test/ssl/api_server.pem" + crtinfo.KeyName = "../../test/ssl/api_server.key" + crtinfo.Names = []pkix.AttributeTypeAndValue{{asn1.ObjectIdentifier{2, 1, 3}, "MAC_ADDR"}} + + crt, pri, err := Parse(baseinfo.CrtName, baseinfo.KeyName) + if err != nil { + t.Log("Parse crt error,Error info:", err) + return + } + err = CreateCRT(crt, pri, crtinfo) + if err != nil { + t.Log("Create crt error,Error info:", err) + } + //os.Remove(baseinfo.CrtName) + //os.Remove(baseinfo.KeyName) + //os.Remove(crtinfo.CrtName) + //os.Remove(crtinfo.KeyName) +}