func (s *svcCertService) GetSvcCertProvider() SvcCertProvider {
	return s.getSvcCert
}
GetSvcCertProvider returns a function pointer to get the svccert.
package service
import (
	"bytes"
	"context"
	"crypto"
	"crypto/rand"
	"crypto/tls"
	"crypto/x509"
	"crypto/x509/pkix"
	"encoding/pem"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"regexp"
	"strings"
	"sync/atomic"
	"time"
	"github.com/kpango/fastime"
	"github.com/kpango/glg"
	"github.com/kpango/ntokend"
	"github.com/pkg/errors"
	"github.com/AthenZ/athenz/clients/go/zts"
	"github.com/yahoojapan/athenz-client-sidecar/v2/config"
	"golang.org/x/sync/singleflight"
)
var (
	defaultSvcCertRefreshPeriod = time.Hour * 24
	defaultSvcCertExpiryMargin = time.Hour * 24 * 10
	defaultSvcCertExpiry int32
	domainReg = regexp.MustCompile(`^([a-zA-Z0-9_][a-zA-Z0-9_-]*\.)*[a-zA-Z0-9_][a-zA-Z0-9_-]*$`)
	ErrCertNotFound = errors.New("Failed to fetch service cert")
	ErrInvalidCert = errors.New("Failed to parse service cert")
	ErrLoadPrivateKey = errors.New("PrivateKey does not exist")
	ErrFailedToInitialize = errors.New("Failed to initialize a service")
	ErrInvalidParameter = errors.New("Invalid parameter")
)
type signer struct {
	key       crypto.Signer
	algorithm x509.SignatureAlgorithm
}
type requestTemplate struct {
	req          *zts.InstanceRefreshRequest
	compoundName zts.CompoundName
	simpleName   zts.SimpleName
}
type SvcCertService interface {
	StartSvcCertUpdater(context.Context) SvcCertService
	GetSvcCertProvider() SvcCertProvider
	RefreshSvcCert() ([]byte, error)
}
type certCache struct {
	cert []byte
	exp  time.Time
}
type svcCertService struct {
	cfg             config.ServiceCert
	token           ntokend.TokenProvider
	certCache       *atomic.Value
	group           singleflight.Group
	refreshDuration time.Duration
	expireMargin    time.Duration
	client          *zts.ZTSClient
	refreshRequest  *requestTemplate
}
type SvcCertProvider func() ([]byte, error)
func NewSvcCertService(cfg config.Config, token ntokend.TokenProvider) (SvcCertService, error) {
	if !cfg.ServiceCert.Enable {
		return nil, ErrDisabled
	}
	dur, err := time.ParseDuration(cfg.ServiceCert.RefreshPeriod)
	if err != nil {
		glg.Warnf("Failed to parse configuration value of refreshPeriod, err: %s. Using default value: %d", err.Error(), defaultSvcCertRefreshPeriod)
		dur = defaultSvcCertRefreshPeriod
	}
	beforeDur, err := time.ParseDuration(cfg.ServiceCert.ExpiryMargin)
	if err != nil {
		glg.Warnf("Failed to parse configuration value of expiryMargin, err: %s. Using default value: %d", err.Error(), defaultSvcCertExpiryMargin)
		beforeDur = defaultSvcCertExpiryMargin
	}
	expireDur, err := time.ParseDuration(cfg.ServiceCert.Expiry)
	var expireInt int32
	if err != nil {
		glg.Warnf("Failed to parse configuration value of expiry, err: %s. Using default value: %d", err.Error(), defaultSvcCertExpiry)
		expireInt = defaultSvcCertExpiry
	} else {
		expireInt = int32(expireDur.Minutes())
	}
	reqTemp, client, err := setup(cfg, expireInt)
	if err != nil {
		return nil, err
	}
	cache := &atomic.Value{}
	cache.Store(
		certCache{
			cert: nil,
			exp:  fastime.Now(),
		},
	)
	return &svcCertService{
		cfg:             cfg.ServiceCert,
		certCache:       cache,
		token:           token,
		refreshDuration: dur,
		expireMargin:    beforeDur,
		client:          client,
		refreshRequest:  reqTemp,
	}, nil
}
func isValidDomain(domain string) bool {
	return domainReg.MatchString(domain)
}
func setup(cfg config.Config, expiry int32) (*requestTemplate, *zts.ZTSClient, error) {
	keyBytes, err := ioutil.ReadFile(cfg.NToken.PrivateKeyPath)
	if err != nil {
		return nil, nil, ErrLoadPrivateKey
	}
	pkSigner, err := newSigner(keyBytes)
	if err != nil {
		return nil, nil, ErrFailedToInitialize
	}
	if !isValidDomain(cfg.NToken.AthenzDomain) {
		return nil, nil, ErrInvalidParameter
	}
	hyphenDomain := strings.Replace(cfg.NToken.AthenzDomain, ".", "-", -1)
	host := fmt.Sprintf("%s.%s.%s", cfg.NToken.ServiceName, hyphenDomain, cfg.ServiceCert.DNSSuffix)
	commonName := fmt.Sprintf("%s.%s", cfg.NToken.AthenzDomain, cfg.NToken.ServiceName)
	subj := pkix.Name{
		CommonName:         commonName,
		OrganizationalUnit: []string{cfg.ServiceCert.Subject.OrganizationalUnit},
		Organization:       []string{cfg.ServiceCert.Subject.Organization},
		Province:           []string{cfg.ServiceCert.Subject.Province},
		Country:            []string{cfg.ServiceCert.Subject.Country},
	}
	uri := ""
	if cfg.ServiceCert.Spiffe {
		uri = fmt.Sprintf("spiffe://%s/sa/%s", cfg.NToken.AthenzDomain, cfg.NToken.ServiceName)
	}
	csrData, err := generateCSR(pkSigner, subj, host, uri)
	if err != nil {
		return nil, nil, ErrFailedToInitialize
	}
	client, err := ztsClient(cfg.ServiceCert)
	if err != nil {
		return nil, nil, ErrFailedToInitialize
	}
	req := &zts.InstanceRefreshRequest{
		Csr:        csrData,
		ExpiryTime: &expiry,
	}
	return &requestTemplate{
		req:          req,
		compoundName: zts.CompoundName(cfg.NToken.AthenzDomain),
		simpleName:   zts.SimpleName(cfg.NToken.ServiceName),
	}, client, nil
}
func newSigner(privateKeyPEM []byte) (*signer, error) {
	block, _ := pem.Decode(privateKeyPEM)
	if block == nil {
		return nil, fmt.Errorf("Unable to load private key")
	}
	switch block.Type {
	case "EC PRIVATE KEY":
		key, err := x509.ParseECPrivateKey(block.Bytes)
		if err != nil {
			return nil, err
		}
		return &signer{key: key, algorithm: x509.ECDSAWithSHA256}, nil
	case "RSA PRIVATE KEY":
		key, err := x509.ParsePKCS1PrivateKey(block.Bytes)
		if err != nil {
			return nil, err
		}
		return &signer{key: key, algorithm: x509.SHA256WithRSA}, nil
	default:
		return nil, fmt.Errorf("Unsupported private key type: %s", block.Type)
	}
}
func generateCSR(keySigner *signer, subj pkix.Name, host, uri string) (string, error) {
	template := x509.CertificateRequest{
		Subject:            subj,
		SignatureAlgorithm: keySigner.algorithm,
	}
	if host != "" {
		template.DNSNames = []string{host}
	}
	if uri != "" {
		uriptr, err := url.Parse(uri)
		if err == nil {
			template.URIs = []*url.URL{uriptr}
		}
	}
	csr, err := x509.CreateCertificateRequest(rand.Reader, &template, keySigner.key)
	if err != nil {
		return "", fmt.Errorf("Cannot create CSR: %v", err)
	}
	block := &pem.Block{
		Type:  "CERTIFICATE REQUEST",
		Bytes: csr,
	}
	var buf bytes.Buffer
	err = pem.Encode(&buf, block)
	if err != nil {
		return "", fmt.Errorf("Cannot encode CSR to PEM: %v", err)
	}
	return buf.String(), nil
}
func ztsClient(cfg config.ServiceCert) (*zts.ZTSClient, error) {
	_, err := url.Parse(cfg.AthenzURL)
	if err != nil {
		return nil, ErrInvalidParameter
	}
	transport := &http.Transport{
		Proxy:                 http.ProxyFromEnvironment,
		ResponseHeaderTimeout: 30 * time.Second,
	}
	if cfg.AthenzCAPath != "" {
		config := &tls.Config{}
		certPool := x509.NewCertPool()
		caCert, err := ioutil.ReadFile(cfg.AthenzCAPath)
		if err != nil {
			return nil, err
		}
		certPool.AppendCertsFromPEM(caCert)
		config.RootCAs = certPool
		transport.TLSClientConfig = config
	}
	client := zts.NewClient(cfg.AthenzURL, transport)
	return &client, nil
}
func (s *svcCertService) StartSvcCertUpdater(ctx context.Context) SvcCertService {
	go func() {
		var err error
		fch := make(chan struct{}, 1)
		ticker := time.NewTicker(s.refreshDuration)
		for {
			select {
			case <-ctx.Done():
				ticker.Stop()
				return
			case <-fch:
				_, err = s.RefreshSvcCert()
				if err != nil {
					glg.Error(err)
					time.Sleep(time.Minute * 10)
					fch <- struct{}{}
				}
			case <-ticker.C:
				_, err = s.RefreshSvcCert()
				if err != nil {
					glg.Error(err)
					fch <- struct{}{}
				}
			}
		}
	}()
	return s
}