예제 #1
0
파일: dialer2.go 프로젝트: phuslu/goagent
func (d *MultiDialer) pickupTLSAddrs(addrs []string, n int) []string {
	if len(addrs) <= n {
		return addrs
	}

	type racer struct {
		addr     string
		duration time.Duration
	}

	goodAddrs := make([]racer, 0)
	unknownAddrs := make([]string, 0)
	badAddrs := make([]string, 0)

	for _, addr := range addrs {
		if duration, ok := d.TLSConnDuration.GetNotStale(addr); ok {
			if d, ok := duration.(time.Duration); !ok {
				glog.Errorf("%#v for %#v is not a time.Duration", duration, addr)
			} else {
				goodAddrs = append(goodAddrs, racer{addr, d})
			}
		} else if e, ok := d.TLSConnError.GetNotStale(addr); ok {
			if _, ok := e.(error); !ok {
				glog.Errorf("%#v for %#v is not a error", e, addr)
			} else {
				badAddrs = append(badAddrs, addr)
			}
		} else {
			unknownAddrs = append(unknownAddrs, addr)
		}
	}

	addrs1 := make([]string, 0, n)

	sort.Slice(goodAddrs, func(i, j int) bool { return goodAddrs[i].duration < goodAddrs[j].duration })
	if len(goodAddrs) > n/2 {
		goodAddrs = goodAddrs[:n/2]
	}
	for _, r := range goodAddrs {
		addrs1 = append(addrs1, r.addr)
	}

	for _, addrs2 := range [][]string{unknownAddrs, badAddrs} {
		if len(addrs1) < n && len(addrs2) > 0 {
			m := n - len(addrs1)
			if len(addrs2) > m {
				ShuffleStringsN(addrs2, m)
				addrs2 = addrs2[:m]
			}
			addrs1 = append(addrs1, addrs2...)
		}
	}

	return addrs1
}
예제 #2
0
func pickupAddrs(addrs []string, n int, connDuration lrucache.Cache, connError lrucache.Cache) []string {
	if len(addrs) <= n {
		return addrs
	}

	goodAddrs := make([]racer, 0)
	unknownAddrs := make([]string, 0)
	badAddrs := make([]string, 0)

	for _, addr := range addrs {
		if d, ok := connDuration.GetQuiet(addr); ok {
			if d1, ok := d.(time.Duration); !ok {
				glog.Errorf("%#v for %#v is not a time.Duration", d, addr)
			} else {
				goodAddrs = append(goodAddrs, racer{addr, d1})
			}
		} else if e, ok := connError.GetQuiet(addr); ok {
			if _, ok := e.(error); !ok {
				glog.Errorf("%#v for %#v is not a error", e, addr)
			} else {
				badAddrs = append(badAddrs, addr)
			}
		} else {
			unknownAddrs = append(unknownAddrs, addr)
		}
	}

	addrs1 := make([]string, 0, n)

	sort.Sort(racers(goodAddrs))
	if len(goodAddrs) > n/2 {
		goodAddrs = goodAddrs[:n/2]
	}
	for _, r := range goodAddrs {
		addrs1 = append(addrs1, r.addr)
	}

	for _, addrs2 := range [][]string{unknownAddrs, badAddrs} {
		if len(addrs1) < n && len(addrs2) > 0 {
			m := n - len(addrs1)
			if len(addrs2) > m {
				helpers.ShuffleStringsN(addrs2, m)
				addrs2 = addrs2[:m]
			}
			addrs1 = append(addrs1, addrs2...)
		}
	}

	return addrs1
}
예제 #3
0
파일: rdpproxy.go 프로젝트: phuslu/cmdhere
func forward(lconn net.Conn, raddr string) {
	if err := exec.Command("wakeonlan", "18:66:DA:17:A2:95").Run(); err != nil {
		glog.Warningf("exec wakeonlan error: %+v", err)
	}

	glog.Infof("try connect to %+v for %+v forwarding", raddr, lconn.RemoteAddr())
	rconn, err := net.Dial("tcp", raddr)
	if err != nil {
		glog.Errorf("net.Dial(%#v) error: %v", raddr, err)
		return
	}

	glog.Infof("forward %+v to %+v", lconn.RemoteAddr(), rconn.RemoteAddr())
	go func() {
		buf := bufpool.Get().([]byte)
		defer bufpool.Put(buf)
		defer rconn.Close()
		defer lconn.Close()
		io.CopyBuffer(rconn, lconn, buf)
	}()
	go func() {
		buf := bufpool.Get().([]byte)
		defer bufpool.Put(buf)
		defer lconn.Close()
		defer rconn.Close()
		io.CopyBuffer(lconn, rconn, buf)
	}()
}
예제 #4
0
func (f *Filter) RoundTrip(ctx context.Context, req *http.Request) (context.Context, *http.Response, error) {
	switch req.Method {
	case "CONNECT":
		glog.V(2).Infof("%s \"DIRECT %s %s %s\" - -", req.RemoteAddr, req.Method, req.Host, req.Proto)
		rconn, err := f.transport.Dial("tcp", req.Host)
		if err != nil {
			return ctx, nil, err
		}

		rw := filters.GetResponseWriter(ctx)

		hijacker, ok := rw.(http.Hijacker)
		if !ok {
			return ctx, nil, fmt.Errorf("http.ResponseWriter(%#v) does not implments http.Hijacker", rw)
		}

		flusher, ok := rw.(http.Flusher)
		if !ok {
			return ctx, nil, fmt.Errorf("http.ResponseWriter(%#v) does not implments http.Flusher", rw)
		}

		rw.WriteHeader(http.StatusOK)
		flusher.Flush()

		lconn, _, err := hijacker.Hijack()
		if err != nil {
			return ctx, nil, fmt.Errorf("%#v.Hijack() error: %v", hijacker, err)
		}
		defer lconn.Close()

		go helpers.IoCopy(rconn, lconn)
		helpers.IoCopy(lconn, rconn)

		filters.SetHijacked(ctx, true)
		return ctx, nil, nil
	default:
		helpers.FixRequestURL(req)
		resp, err := f.transport.RoundTrip(req)

		if err != nil {
			glog.Errorf("%s \"DIRECT %s %s %s\" error: %s", req.RemoteAddr, req.Method, req.URL.String(), req.Proto, err)
			data := err.Error()
			resp = &http.Response{
				StatusCode:    http.StatusBadGateway,
				Header:        http.Header{},
				Request:       req,
				Close:         true,
				ContentLength: int64(len(data)),
				Body:          ioutil.NopCloser(bytes.NewReader([]byte(data))),
			}
			err = nil
		} else {
			if req.RemoteAddr != "" {
				glog.V(2).Infof("%s \"DIRECT %s %s %s\" %d %s", req.RemoteAddr, req.Method, req.URL.String(), req.Proto, resp.StatusCode, resp.Header.Get("Content-Length"))
			}
		}
		return ctx, resp, err
	}
}
예제 #5
0
func (d *MultiDialer) LookupAlias(alias string) (addrs []string, err error) {
	names, ok := d.HostMap[alias]
	if !ok {
		return nil, fmt.Errorf("alias %#v not exists", alias)
	}

	seen := make(map[string]struct{}, 0)
	expiry := time.Now().Add(d.DNSCacheExpiry)
	for _, name := range names {
		var addrs0 []string
		if net.ParseIP(name) != nil {
			addrs0 = []string{name}
			expiry = time.Time{}
		} else if addrs1, ok := d.DNSCache.Get(name); ok {
			addrs0 = addrs1.([]string)
		} else {
			if d.ForceIPv6 {
				addrs0, err = d.LookupHost2(name, d.DNSServers[0])
				if err != nil {
					glog.Warningf("LookupHost2(%#v, %#v) error: %s", name, d.DNSServers[0], err)
					addrs0 = []string{}
				}
			} else {
				addrs0, err = d.LookupHost(name)
				if err != nil {
					glog.Warningf("LookupHost(%#v) error: %s", name, err)
					addrs0 = []string{}
				}
			}

			glog.V(2).Infof("LookupHost(%#v) return %v", name, addrs0)
			d.DNSCache.Set(name, addrs0, expiry)
		}
		for _, addr := range addrs0 {
			seen[addr] = struct{}{}
		}
	}

	if len(seen) == 0 {
		return nil, err
	}

	addrs = make([]string, 0)
	for addr, _ := range seen {
		if _, ok := d.IPBlackList.GetQuiet(addr); ok {
			continue
		}
		addrs = append(addrs, addr)
	}

	if len(addrs) == 0 {
		glog.Errorf("MULTIDIALER: LookupAlias(%#v) have no good ip addrs", alias)
		return nil, fmt.Errorf("MULTIDIALER: LookupAlias(%#v) have no good ip addrs", alias)
	}

	return addrs, nil
}
예제 #6
0
파일: auth.go 프로젝트: taiping-z/goproxy
func (f *Filter) RoundTrip(ctx context.Context, req *http.Request) (context.Context, *http.Response, error) {

	if ip, _, err := net.SplitHostPort(req.RemoteAddr); err == nil {
		if _, ok := f.WhiteList[ip]; ok {
			return ctx, nil, nil
		}
	}

	if auth := filters.String(ctx, authHeader); auth != "" {
		if _, ok := f.ByPassHeaders.Get(auth); ok {
			glog.V(3).Infof("auth filter hit bypass cache %#v", auth)
			return ctx, nil, nil
		}
		parts := strings.SplitN(auth, " ", 2)
		if len(parts) == 2 {
			switch parts[0] {
			case "Basic":
				if userpass, err := base64.StdEncoding.DecodeString(parts[1]); err == nil {
					parts := strings.Split(string(userpass), ":")
					user := parts[0]
					pass := parts[1]
					pass1, ok := f.Basic[user]
					if ok && pass == pass1 {
						f.ByPassHeaders.Set(auth, struct{}{}, time.Now().Add(time.Hour))
						return ctx, nil, nil
					}
				}
			default:
				glog.Errorf("Unrecognized auth type: %#v", parts[0])
				break
			}
		}
	}

	glog.V(1).Infof("UnAuthenticated URL %v from %#v", req.URL.String(), req.RemoteAddr)

	noAuthResponse := &http.Response{
		Status:        "407 Proxy Authentication Required",
		StatusCode:    http.StatusProxyAuthRequired,
		Proto:         "HTTP/1.1",
		ProtoMajor:    1,
		ProtoMinor:    1,
		Header:        http.Header{},
		Request:       req,
		Close:         true,
		ContentLength: -1,
	}

	return ctx, noAuthResponse, nil
}
예제 #7
0
파일: dialer2.go 프로젝트: phuslu/goagent
func (d *MultiDialer) LookupAlias(alias string) (addrs []string, err error) {
	names, ok := d.HostMap[alias]
	if !ok {
		return nil, fmt.Errorf("alias %#v not exists", alias)
	}

	seen := make(map[string]struct{}, 0)
	for _, name := range names {
		var addrs0 []string
		if net.ParseIP(name) != nil {
			addrs0 = []string{name}
		} else {
			addrs0, err = d.Resolver.LookupHost(name)
			if err != nil {
				glog.Warningf("LookupHost(%#v) error: %s", name, err)
				addrs0 = []string{}
			}
		}
		for _, addr := range addrs0 {
			seen[addr] = struct{}{}
		}
	}

	if len(seen) == 0 {
		return nil, err
	}

	addrs = make([]string, 0)
	for addr := range seen {
		if _, ok := d.IPBlackList.GetQuiet(addr); ok {
			continue
		}
		addrs = append(addrs, addr)
	}

	if len(addrs) == 0 {
		glog.Errorf("MULTIDIALER: LookupAlias(%#v) have no good ip addrs", alias)
		return nil, fmt.Errorf("MULTIDIALER: LookupAlias(%#v) have no good ip addrs", alias)
	}

	return addrs, nil
}
예제 #8
0
func (f *Filter) ProxyPacRoundTrip(ctx context.Context, req *http.Request) (context.Context, *http.Response, error) {
	_, port, err := net.SplitHostPort(req.Host)
	if err != nil {
		port = "80"
	}

	if v, ok := f.ProxyPacCache.Get(req.RequestURI); ok {
		if s, ok := v.(string); ok {
			s = fixProxyPac(s, req)
			return ctx, &http.Response{
				StatusCode:    http.StatusOK,
				Header:        http.Header{},
				Request:       req,
				Close:         true,
				ContentLength: int64(len(s)),
				Body:          ioutil.NopCloser(strings.NewReader(s)),
			}, nil
		}
	}

	filename := req.URL.Path[1:]

	buf := new(bytes.Buffer)

	resp, err := f.Store.Get(filename, -1, -1)
	switch {
	case os.IsNotExist(err), resp.StatusCode == http.StatusNotFound:
		glog.V(2).Infof("AUTOPROXY ProxyPac: generate %#v", filename)
		s := fmt.Sprintf(`// User-defined FindProxyForURL
function FindProxyForURL(url, host) {
    if (isPlainHostName(host) ||
        host.indexOf('127.') == 0 ||
        host.indexOf('192.168.') == 0 ||
        host.indexOf('10.') == 0 ||
        shExpMatch(host, 'localhost.*')) {
        return 'DIRECT';
    }

    if (shExpMatch(host, '*.google*.*')) {
        return 'PROXY localhost:%s';
    }

    return 'DIRECT';
}
`, port)
		f.Store.Put(filename, http.Header{}, ioutil.NopCloser(bytes.NewBufferString(s)))
	case err != nil:
		return ctx, nil, err
	case resp.Body != nil:
		resp.Body.Close()
	}

	if resp, err := f.Store.Get(filename, -1, -1); err == nil {
		defer resp.Body.Close()
		if b, err := ioutil.ReadAll(resp.Body); err == nil {
			if f.GFWListEnabled {
				b = []byte(strings.Replace(string(b), "function FindProxyForURL(", "function MyFindProxyForURL(", 1))
			}
			buf.Write(b)
		}
	}

	if f.GFWListEnabled {
		resp, err := f.Store.Get(f.GFWList.Filename, -1, -1)
		if err != nil {
			glog.Errorf("GetObject(%#v) error: %v", f.GFWList.Filename, err)
			return ctx, nil, err
		}
		defer resp.Body.Close()

		sites, err := parseAutoProxy(resp.Body)
		if err != nil {
			glog.Errorf("parseAutoProxy(%#v) error: %v", f.GFWList.Filename, err)
			return ctx, nil, err
		}

		sort.Strings(sites)

		io.WriteString(buf, "\nvar sites = {\n")
		for _, site := range sites {
			io.WriteString(buf, "\""+site+"\":1,\n")
		}
		io.WriteString(buf, "\"google.com\":1\n")
		io.WriteString(buf, "}\n")

		io.WriteString(buf, `
function FindProxyForURL(url, host) {
    if ((p = MyFindProxyForURL(url, host)) != "DIRECT") {
        return p
    }

    var lastPos;
    do {
        if (sites.hasOwnProperty(host)) {
            return 'PROXY GOPROXY_ADDRESS';
        }
        lastPos = host.indexOf('.') + 1;
        host = host.slice(lastPos);
    } while (lastPos >= 1);
    return 'DIRECT';
}`)
	}

	s := buf.String()
	f.ProxyPacCache.Set(req.RequestURI, s, time.Now().Add(15*time.Minute))

	s = fixProxyPac(s, req)
	resp = &http.Response{
		StatusCode:    http.StatusOK,
		Header:        http.Header{},
		Request:       req,
		Close:         true,
		ContentLength: int64(len(s)),
		Body:          ioutil.NopCloser(strings.NewReader(s)),
	}

	return ctx, resp, nil
}
예제 #9
0
파일: rootca.go 프로젝트: phuslu/goagent
func NewRootCA(name string, vaildFor time.Duration, rsaBits int, certDir string, portable bool) (*RootCA, error) {
	keyFile := name + ".key"
	certFile := name + ".crt"

	var store storage.Store
	if portable {
		exe, err := os.Executable()
		if err != nil {
			glog.Fatalf("os.Executable() error: %+v", err)
		}
		store = &storage.FileStore{filepath.Dir(exe)}
	} else {
		store = &storage.FileStore{"."}
	}

	rootCA := &RootCA{
		store:    store,
		name:     name,
		keyFile:  keyFile,
		certFile: certFile,
		rsaBits:  rsaBits,
		certDir:  certDir,
		mu:       new(sync.Mutex),
	}

	if storage.NotExist(store, certFile) {
		glog.Infof("Generating RootCA for %s/%s", keyFile, certFile)
		template := x509.Certificate{
			IsCA:         true,
			SerialNumber: big.NewInt(1),
			Subject: pkix.Name{
				CommonName:   name,
				Country:      []string{"US"},
				Province:     []string{"California"},
				Locality:     []string{"Los Angeles"},
				Organization: []string{name},
				ExtraNames: []pkix.AttributeTypeAndValue{
					{
						Type:  []int{2, 5, 4, 42},
						Value: name,
					},
				},
			},
			DNSNames: []string{name},

			NotBefore: time.Now().Add(-time.Duration(30 * 24 * time.Hour)),
			NotAfter:  time.Now().Add(vaildFor),

			KeyUsage:              x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
			ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth},
			BasicConstraintsValid: true,
			// AuthorityKeyId:        sha1.New().Sum([]byte("phuslu")),
			// SubjectKeyId:          sha1.New().Sum([]byte("phuslu")),
		}

		priv, err := rsa.GenerateKey(rand.Reader, rsaBits)
		if err != nil {
			return nil, err
		}

		derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv)
		if err != nil {
			return nil, err
		}

		ca, err := x509.ParseCertificate(derBytes)
		if err != nil {
			return nil, err
		}

		rootCA.ca = ca
		rootCA.priv = priv
		rootCA.derBytes = derBytes

		keypem := &pem.Block{Type: "PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(rootCA.priv)}
		rc := ioutil.NopCloser(bytes.NewReader(pem.EncodeToMemory(keypem)))
		if _, err = store.Put(keyFile, http.Header{}, rc); err != nil {
			return nil, err
		}

		certpem := &pem.Block{Type: "CERTIFICATE", Bytes: rootCA.derBytes}
		rc = ioutil.NopCloser(bytes.NewReader(pem.EncodeToMemory(certpem)))
		if _, err = store.Put(certFile, http.Header{}, rc); err != nil {
			return nil, err
		}
	} else {
		for _, name := range []string{keyFile, certFile} {
			resp, err := store.Get(name)
			if err != nil {
				return nil, err
			}

			data, err := ioutil.ReadAll(resp.Body)
			resp.Body.Close()
			if err != nil {
				return nil, err
			}

			var b *pem.Block
			for {
				b, data = pem.Decode(data)
				if b == nil {
					break
				}
				switch b.Type {
				case "CERTIFICATE":
					rootCA.derBytes = b.Bytes
					ca, err := x509.ParseCertificate(rootCA.derBytes)
					if err != nil {
						return nil, err
					}
					rootCA.ca = ca
				case "PRIVATE KEY", "PRIVATE RSA KEY":
					priv, err := x509.ParsePKCS1PrivateKey(b.Bytes)
					if err != nil {
						return nil, err
					}
					rootCA.priv = priv
				}
			}
		}
	}

	switch runtime.GOOS {
	case "windows", "darwin":
		if _, err := rootCA.ca.Verify(x509.VerifyOptions{}); err != nil {
			glog.Warningf("Verify RootCA(%#v) error: %v, try import to system root", name, err)
			if err = helpers.RemoveCAFromSystemRoot(rootCA.name); err != nil {
				glog.Errorf("Remove Old RootCA(%#v) error: %v", name, err)
			}
			if err = helpers.ImportCAToSystemRoot(rootCA.ca); err != nil {
				glog.Errorf("Import RootCA(%#v) error: %v", name, err)
			} else {
				glog.Infof("Import RootCA(%s) OK", certFile)
			}

			if fs, err := store.List(certDir); err == nil {
				for _, f := range fs {
					if _, err = store.Delete(f); err != nil {
						glog.Errorf("%T.Delete(%#v) error: %v", store, f, err)
					}
				}
			}
		}
	}

	if fs, ok := store.(*storage.FileStore); ok {
		if storage.NotExist(store, certDir) {
			if err := os.Mkdir(filepath.Join(fs.Dirname, certDir), 0777); err != nil {
				return nil, err
			}
		}
	}

	return rootCA, nil
}
예제 #10
0
파일: handler.go 프로젝트: johnsonz/goproxy
func (h Handler) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
	var err error

	remoteAddr := req.RemoteAddr

	// Prepare filter.Context
	ctx := filters.NewContext(req.Context(), h, h.Listener, rw, h.Branding)
	req = req.WithContext(ctx)

	// Enable transport http proxy
	if req.Method != "CONNECT" && !req.URL.IsAbs() {
		if req.URL.Scheme == "" {
			if req.TLS != nil && req.ProtoMajor == 1 {
				req.URL.Scheme = "https"
			} else {
				req.URL.Scheme = "http"
			}
		}

		if req.TLS != nil {
			if req.Host == "" {
				if req.URL.Host != "" {
					req.Host = req.URL.Host
				} else {
					req.Host = req.TLS.ServerName
				}
			}
			if req.URL.Host == "" {
				if req.Host != "" {
					req.URL.Host = req.Host
				} else {
					req.URL.Host = req.TLS.ServerName
				}
			}
		}
	}

	// Filter Request
	for _, f := range h.RequestFilters {
		ctx, req, err = f.Request(ctx, req)
		if req == filters.DummyRequest {
			return
		}
		if err != nil {
			if err != io.EOF {
				glog.Errorf("%s Filter Request %T error: %+v", remoteAddr, f, err)
			}
			return
		}
		// Update context for request
		req = req.WithContext(ctx)
	}

	if req.Body != nil {
		defer req.Body.Close()
	}

	// Filter Request -> Response
	var resp *http.Response
	for _, f := range h.RoundTripFilters {
		ctx, resp, err = f.RoundTrip(ctx, req)
		if resp == filters.DummyResponse {
			return
		}
		// Unexcepted errors
		if err != nil {
			filters.SetRoundTripFilter(ctx, f)
			glog.Errorf("%s Filter RoundTrip %T error: %+v", remoteAddr, f, err)
			http.Error(rw, h.FormatError(ctx, err), http.StatusBadGateway)
			return
		}
		// Update context for request
		req = req.WithContext(ctx)
		// A roundtrip filter give a response
		if resp != nil {
			resp.Request = req
			filters.SetRoundTripFilter(ctx, f)
			break
		}
	}

	// Filter Response
	for _, f := range h.ResponseFilters {
		if resp == nil || resp == filters.DummyResponse {
			return
		}
		ctx, resp, err = f.Response(ctx, resp)
		if err != nil {
			glog.Errorln("%s Filter %T Response error: %+v", remoteAddr, f, err)
			http.Error(rw, h.FormatError(ctx, err), http.StatusBadGateway)
			return
		}
		// Update context for request
		req = req.WithContext(ctx)
	}

	if resp == nil {
		glog.Errorln("%s Handler %#v Response empty response", remoteAddr, h)
		http.Error(rw, h.FormatError(ctx, fmt.Errorf("empty response")), http.StatusBadGateway)
		return
	}

	if resp.Header.Get("Content-Length") == "" && resp.ContentLength >= 0 {
		resp.Header.Set("Content-Length", strconv.FormatInt(resp.ContentLength, 10))
	}
	for key, values := range resp.Header {
		for _, value := range values {
			rw.Header().Add(key, value)
		}
	}
	rw.WriteHeader(resp.StatusCode)
	if resp.Body != nil {
		defer resp.Body.Close()
		n, err := helpers.IOCopy(rw, resp.Body)
		if err != nil {
			if isClosedConnError(err) {
				glog.Infof("IOCopy %#v return %#v %T(%v)", resp.Body, n, err, err)
			} else {
				glog.Warningf("IOCopy %#v return %#v %T(%v)", resp.Body, n, err, err)
			}
		}
	}
}
예제 #11
0
func NewRootCA(name string, vaildFor time.Duration, rsaBits int, certDir string, portable bool) (*RootCA, error) {
	keyFile := name + ".key"
	certFile := name + ".crt"

	if portable {
		rootdir := filepath.Dir(os.Args[0])
		keyFile = filepath.Join(rootdir, keyFile)
		certFile = filepath.Join(rootdir, certFile)
		certDir = filepath.Join(rootdir, certDir)
	}

	rootCA := &RootCA{
		name:     name,
		keyFile:  keyFile,
		certFile: certFile,
		rsaBits:  rsaBits,
		certDir:  certDir,
		mu:       new(sync.Mutex),
	}

	if _, err := os.Stat(certFile); os.IsNotExist(err) {
		glog.Infof("Generating RootCA for %s", certFile)
		template := x509.Certificate{
			IsCA:         true,
			SerialNumber: big.NewInt(1),
			Subject: pkix.Name{
				CommonName:   name,
				Organization: []string{name},
			},
			NotBefore: time.Now().Add(-time.Duration(30 * 24 * time.Hour)),
			NotAfter:  time.Now().Add(vaildFor),

			KeyUsage:              x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
			ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth},
			BasicConstraintsValid: true,
		}

		priv, err := rsa.GenerateKey(rand.Reader, rsaBits)
		if err != nil {
			return nil, err
		}

		derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv)
		if err != nil {
			return nil, err
		}

		ca, err := x509.ParseCertificate(derBytes)
		if err != nil {
			return nil, err
		}

		rootCA.ca = ca
		rootCA.priv = priv
		rootCA.derBytes = derBytes

		keypem := &pem.Block{Type: "PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(rootCA.priv)}
		if err = ioutil.WriteFile(keyFile, pem.EncodeToMemory(keypem), 0755); err != nil {
			return nil, err
		}

		certpem := &pem.Block{Type: "CERTIFICATE", Bytes: rootCA.derBytes}
		if err = ioutil.WriteFile(certFile, pem.EncodeToMemory(certpem), 0755); err != nil {
			return nil, err
		}
	} else {
		data, err := ioutil.ReadFile(keyFile)
		if err != nil {
			return nil, err
		}

		var b *pem.Block
		for {
			b, data = pem.Decode(data)
			if b == nil {
				break
			}
			if b.Type == "CERTIFICATE" {
				rootCA.derBytes = b.Bytes
				ca, err := x509.ParseCertificate(rootCA.derBytes)
				if err != nil {
					return nil, err
				}
				rootCA.ca = ca
			} else if b.Type == "PRIVATE KEY" {
				priv, err := x509.ParsePKCS1PrivateKey(b.Bytes)
				if err != nil {
					return nil, err
				}
				rootCA.priv = priv
			}
		}

		data, err = ioutil.ReadFile(certFile)
		if err != nil {
			return nil, err
		}

		for {
			b, data = pem.Decode(data)
			if b == nil {
				break
			}
			if b.Type == "CERTIFICATE" {
				rootCA.derBytes = b.Bytes
				ca, err := x509.ParseCertificate(rootCA.derBytes)
				if err != nil {
					return nil, err
				}
				rootCA.ca = ca
			} else if b.Type == "PRIVATE KEY" {
				priv, err := x509.ParsePKCS1PrivateKey(b.Bytes)
				if err != nil {
					return nil, err
				}
				rootCA.priv = priv
			}
		}
	}

	switch runtime.GOOS {
	case "windows", "darwin":
		if _, err := rootCA.ca.Verify(x509.VerifyOptions{}); err != nil {
			glog.Warningf("Verify RootCA(%#v) error: %v, try import to system root", name, err)
			if err = helpers.RemoveCAFromSystemRoot(rootCA.name); err != nil {
				glog.Errorf("Remove Old RootCA(%#v) error: %v", name, err)
			}
			if err = helpers.ImportCAToSystemRoot(rootCA.ca); err != nil {
				glog.Errorf("Import RootCA(%#v) error: %v", name, err)
			} else {
				glog.Infof("Import RootCA(%s) OK", certFile)
			}

			if fis, err := ioutil.ReadDir(certDir); err == nil {
				for _, fi := range fis {
					if err = os.Remove(certDir + "/" + fi.Name()); err != nil {
						glog.Errorf("Remove(%#v) error: %v", fi.Name(), err)
					}
				}
			}
		}
	}

	if _, err := os.Stat(certDir); os.IsNotExist(err) {
		if err = os.Mkdir(certDir, 0755); err != nil {
			return nil, err
		}
	}

	return rootCA, nil
}