// MakeTLSConfig reduces configs into a single tls.Config. // If TLS is to be disabled, a nil tls.Config will be returned. func MakeTLSConfig(configs []*Config) (*tls.Config, error) { if configs == nil || len(configs) == 0 { return nil, nil } config := new(tls.Config) ciphersAdded := make(map[uint16]struct{}) configMap := make(configGroup) for i, cfg := range configs { if cfg == nil { // avoid nil pointer dereference below configs[i] = new(Config) continue } // Key this config by its hostname; this // overwrites configs with the same hostname configMap[cfg.Hostname] = cfg // Can't serve TLS and not-TLS on same port if i > 0 && cfg.Enabled != configs[i-1].Enabled { thisConfProto, lastConfProto := "not TLS", "not TLS" if cfg.Enabled { thisConfProto = "TLS" } if configs[i-1].Enabled { lastConfProto = "TLS" } return nil, fmt.Errorf("cannot multiplex %s (%s) and %s (%s) on same listener", configs[i-1].Hostname, lastConfProto, cfg.Hostname, thisConfProto) } // Union cipher suites for _, ciph := range cfg.Ciphers { if _, ok := ciphersAdded[ciph]; !ok { ciphersAdded[ciph] = struct{}{} config.CipherSuites = append(config.CipherSuites, ciph) } } // Can't resolve conflicting PreferServerCipherSuites settings if i > 0 && cfg.PreferServerCipherSuites != configs[i-1].PreferServerCipherSuites { return nil, fmt.Errorf("cannot both use PreferServerCipherSuites and not use it") } config.PreferServerCipherSuites = cfg.PreferServerCipherSuites // Go with the widest range of protocol versions if config.MinVersion == 0 || cfg.ProtocolMinVersion < config.MinVersion { config.MinVersion = cfg.ProtocolMinVersion } if cfg.ProtocolMaxVersion > config.MaxVersion { config.MaxVersion = cfg.ProtocolMaxVersion } // Go with the strictest ClientAuth type if cfg.ClientAuth > config.ClientAuth { config.ClientAuth = cfg.ClientAuth } } // Is TLS disabled? If so, we're done here. // By now, we know that all configs agree // whether it is or not, so we can just look // at the first one. if len(configs) == 0 || !configs[0].Enabled { return nil, nil } // Default cipher suites if len(config.CipherSuites) == 0 { config.CipherSuites = defaultCiphers } // For security, ensure TLS_FALLBACK_SCSV is always included if config.CipherSuites[0] != tls.TLS_FALLBACK_SCSV { config.CipherSuites = append([]uint16{tls.TLS_FALLBACK_SCSV}, config.CipherSuites...) } // Set up client authentication if enabled if config.ClientAuth != tls.NoClientCert { pool := x509.NewCertPool() clientCertsAdded := make(map[string]struct{}) for _, cfg := range configs { for _, caFile := range cfg.ClientCerts { // don't add cert to pool more than once if _, ok := clientCertsAdded[caFile]; ok { continue } clientCertsAdded[caFile] = struct{}{} // Any client with a certificate from this CA will be allowed to connect caCrt, err := ioutil.ReadFile(caFile) if err != nil { return nil, err } if !pool.AppendCertsFromPEM(caCrt) { return nil, fmt.Errorf("error loading client certificate '%s': no certificates were successfully parsed", caFile) } } } config.ClientCAs = pool } // Associate the GetCertificate callback, or almost nothing we just did will work config.GetCertificate = configMap.GetCertificate return config, nil }
// CreateConfig creates a tls.config from using ACME configuration func (a *ACME) CreateConfig(tlsConfig *tls.Config, CheckOnDemandDomain func(domain string) bool) error { acme.Logger = fmtlog.New(ioutil.Discard, "", 0) if len(a.StorageFile) == 0 { return errors.New("Empty StorageFile, please provide a filename for certs storage") } log.Debugf("Generating default certificate...") if len(tlsConfig.Certificates) == 0 { // no certificates in TLS config, so we add a default one cert, err := generateDefaultCertificate() if err != nil { return err } tlsConfig.Certificates = append(tlsConfig.Certificates, *cert) } var account *Account var needRegister bool // if certificates in storage, load them if fileInfo, err := os.Stat(a.StorageFile); err == nil && fileInfo.Size() != 0 { log.Infof("Loading ACME certificates...") // load account account, err = a.loadAccount(a) if err != nil { return err } } else { log.Infof("Generating ACME Account...") // Create a user. New accounts need an email and private key to start privateKey, err := rsa.GenerateKey(rand.Reader, 4096) if err != nil { return err } account = &Account{ Email: a.Email, PrivateKey: x509.MarshalPKCS1PrivateKey(privateKey), } account.DomainsCertificate = DomainsCertificates{Certs: []*DomainsCertificate{}, lock: &sync.RWMutex{}} needRegister = true } client, err := a.buildACMEClient(account) if err != nil { return err } client.ExcludeChallenges([]acme.Challenge{acme.HTTP01, acme.DNS01}) wrapperChallengeProvider := newWrapperChallengeProvider() client.SetChallengeProvider(acme.TLSSNI01, wrapperChallengeProvider) if needRegister { // New users will need to register; be sure to save it reg, err := client.Register() if err != nil { return err } account.Registration = reg } // The client has a URL to the current Let's Encrypt Subscriber // Agreement. The user will need to agree to it. err = client.AgreeToTOS() if err != nil { return err } safe.Go(func() { a.retrieveCertificates(client, account) }) tlsConfig.GetCertificate = func(clientHello *tls.ClientHelloInfo) (*tls.Certificate, error) { if challengeCert, ok := wrapperChallengeProvider.getCertificate(clientHello.ServerName); ok { return challengeCert, nil } if domainCert, ok := account.DomainsCertificate.getCertificateForDomain(clientHello.ServerName); ok { return domainCert.tlsCert, nil } if a.OnDemand { if CheckOnDemandDomain != nil && !CheckOnDemandDomain(clientHello.ServerName) { return nil, nil } return a.loadCertificateOnDemand(client, account, clientHello) } return nil, nil } ticker := time.NewTicker(24 * time.Hour) safe.Go(func() { for { select { case <-ticker.C: if err := a.renewCertificates(client, account); err != nil { log.Errorf("Error renewing ACME certificate %+v: %s", account, err.Error()) } } } }) return nil }
func main() { http.HandleFunc("/", newLoggingHandleFunc(rootHandler)) http.HandleFunc("/new", newLoggingHandleFunc(newHandler)) http.HandleFunc("/view/", newLoggingHandleFunc(newGzipHandleFunc(titleHandler(viewHandler)))) http.HandleFunc("/edit/", newLoggingHandleFunc(newGzipHandleFunc(titleHandler(editHandler)))) http.HandleFunc("/save/", newLoggingHandleFunc(titleHandler(saveHandler))) http.HandleFunc("/del/", newLoggingHandleFunc(titleHandler(delHandler))) http.HandleFunc("/front", newLoggingHandleFunc(newGzipHandleFunc(frontHandler))) http.HandleFunc("/hsts_hpkp", hsts_hpkp) log.SetPrefix("goWiki: ") log.Println("listening... on port", HTTPS_PORT) go func() { for { err := func() error { var OCSPC OCSPCert var err error cert, err := tls.LoadX509KeyPair(CERT, KEY) if err != nil { return err } OCSPC.cert = &cert if OCSPC.cert.Leaf, err = x509.ParseCertificate(OCSPC.cert.Certificate[0]); err != nil { return err } issuerRAW, err := ioutil.ReadFile(ISSUER) if err != nil { return err } for { var issuerPEM *pem.Block issuerPEM, issuerRAW = pem.Decode(issuerRAW) if issuerPEM == nil { break } if issuerPEM.Type == "CERTIFICATE" { OCSPC.issuer, err = x509.ParseCertificate(issuerPEM.Bytes) if err != nil { return err } } } if OCSPC.issuer == nil { return errors.New("no issuer") } OCSPC.req, err = ocsp.CreateRequest(OCSPC.cert.Leaf, OCSPC.issuer, nil) if err != nil { return err } err = OCSPC.updateStaple() if err != nil { return err } go OCSPC.stapleLoop() TLSConfig := new(tls.Config) TLSConfig.Certificates = []tls.Certificate{cert} TLSConfig.CipherSuites = []uint16{ tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, tls.TLS_RSA_WITH_AES_128_CBC_SHA, tls.TLS_RSA_WITH_AES_256_CBC_SHA, tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA} TLSConfig.PreferServerCipherSuites = true TLSConfig.MinVersion = tls.VersionTLS11 //MaxVersion needed because of bug with TLS_FALLBACK_SCSV gonna be fixed in go 1.5 TLSConfig.MaxVersion = tls.VersionTLS12 TLSConfig.NextProtos = []string{"http/1.1"} TLSConfig.GetCertificate = func(clientHello *tls.ClientHelloInfo) (*tls.Certificate, error) { OCSPC.RLock() defer OCSPC.RUnlock() return OCSPC.cert, nil } ln, err := net.Listen("tcp", HTTPS_PORT) if err != nil { return err } tlsListener := tls.NewListener(tcpKeepAliveListener{ln.(*net.TCPListener)}, TLSConfig) return new(http.Server).Serve(tlsListener) }() if err != nil { log.Println(err) } time.Sleep(time.Second * TIMEOUT) } }() for { log.Println("redirecting from port", HTTP_PORT, "to", HTTPS_PORT) err := http.ListenAndServe(HTTP_PORT, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("X-Frame-Options", "SAMEORIGIN") w.Header().Set("Server", "Jesus") log.Println("redirecting http", r.RemoteAddr, "to https", DOMAIN+HTTPS_PORT+r.URL.Path) http.Redirect(w, r, "https://"+DOMAIN+HTTPS_PORT+r.URL.Path, http.StatusMovedPermanently) })) if err != nil { log.Println(err) } time.Sleep(time.Second * TIMEOUT) } }
func main() { flag.Usage = usage port := flag.Uint("port", 443, "https port") certsPath := flag.String("letsencrypt-path", "/etc/letsencrypt/live", "path at which an 'xyz.example.com' containing 'fullchain.pem' and 'privkey.pem' can be found") defaultHost := flag.String("default-hostname", "localhost.daplie.com", "the default folder to find certificates to use when no matches are found") flag.Parse() host := strings.ToLower(*defaultHost) // See https://groups.google.com/a/letsencrypt.org/forum/#!topic/ca-dev/l1Dd6jzWeu8 /* if strings.HasPrefix("www.", host) { fmt.Println("TODO: 'www.' prefixed certs should be obtained for every 'example.com' domain.") } host = strings.TrimPrefix("www.", host) */ fmt.Printf("Loading Certificates %s/%s/{privkey.pem,fullchain.pem}\n", *certsPath, *defaultHost) privkeyPath := filepath.Join(*certsPath, *defaultHost, "privkey.pem") certPath := filepath.Join(*certsPath, *defaultHost, "fullchain.pem") defaultCert, err := tls.LoadX509KeyPair(certPath, privkeyPath) if err != nil { fmt.Fprintf(os.Stderr, "Couldn't load default certificates: %s\n", err) os.Exit(1) } addr := ":" + strconv.Itoa(int(*port)) conn, err := net.Listen("tcp", addr) if nil != err { fmt.Fprintf(os.Stderr, "Couldn't bind to TCP socket %q: %s\n", addr, err) os.Exit(1) } certMap := make(map[string]myCert) tlsConfig := new(tls.Config) tlsConfig.Certificates = []tls.Certificate{defaultCert} tlsConfig.GetCertificate = func(clientHello *tls.ClientHelloInfo) (*tls.Certificate, error) { // Load from memory // TODO unload untouched certificates every x minutes if myCert, ok := certMap[clientHello.ServerName]; ok { myCert.touchedAt = time.Now() return myCert.cert, nil } privkeyPath := filepath.Join(*certsPath, clientHello.ServerName, "privkey.pem") certPath := filepath.Join(*certsPath, clientHello.ServerName, "fullchain.pem") loadCert := func() *tls.Certificate { // TODO handle race condition (ask Matt) // the transaction is idempotent, however, so it shouldn't matter if _, err := os.Stat(privkeyPath); err == nil { fmt.Printf("Loading Certificates %s/%s/{privkey.pem,fullchain.pem}\n\n", *certsPath, clientHello.ServerName) cert, err := tls.LoadX509KeyPair(certPath, privkeyPath) if nil != err { return &cert } return nil } return nil } if cert := loadCert(); nil != cert { certMap[clientHello.ServerName] = myCert{ cert: cert, touchedAt: time.Now(), } return cert, nil } // TODO try to get cert via letsencrypt python client // TODO check for a hosting directory before attempting this /* cmd := exec.Command( "./venv/bin/letsencrypt", "--text", "--agree-eula", "--email", "*****@*****.**", "--authenticator", "standalone", "--domains", "www.example.com", "--domains", "example.com", "--dvsni-port", "65443", "auth", ) err := cmd.Run() if nil != err { if cert := loadCert(); nil != cert { return cert, nil } } */ fmt.Fprintf(os.Stderr, "Failed to load certificates for %q.\n", clientHello.ServerName) fmt.Fprintf(os.Stderr, "\tTried %s/{privkey.pem,fullchain.pem}\n", filepath.Join(*certsPath, clientHello.ServerName)) //fmt.Fprintf(os.Stderr, "\tand letsencrypt api\n") fmt.Fprintf(os.Stderr, "\n") // TODO how to prevent attack and still enable retry? // perhaps check DNS and hosting directory, wait 5 minutes? certMap[clientHello.ServerName] = myCert{ cert: &defaultCert, touchedAt: time.Now(), } return &defaultCert, nil } tlsListener := tls.NewListener(conn, tlsConfig) server := &http.Server{ Addr: addr, Handler: &myHandler{}, } fmt.Printf("Listening on https://%s:%d\n\n", host, *port) server.Serve(tlsListener) }
// CreateLocalConfig creates a tls.config using local ACME configuration func (a *ACME) CreateLocalConfig(tlsConfig *tls.Config, checkOnDemandDomain func(domain string) bool) error { err := a.init() if err != nil { return err } if len(a.Storage) == 0 { return errors.New("Empty Store, please provide a filename for certs storage") } a.checkOnDemandDomain = checkOnDemandDomain tlsConfig.Certificates = append(tlsConfig.Certificates, *a.defaultCertificate) tlsConfig.GetCertificate = a.getCertificate localStore := NewLocalStore(a.Storage) a.store = localStore a.challengeProvider = &challengeProvider{store: a.store} var needRegister bool var account *Account if fileInfo, fileErr := os.Stat(a.Storage); fileErr == nil && fileInfo.Size() != 0 { log.Infof("Loading ACME Account...") // load account object, err := localStore.Load() if err != nil { return err } account = object.(*Account) } else { log.Infof("Generating ACME Account...") account, err = NewAccount(a.Email) if err != nil { return err } needRegister = true } a.client, err = a.buildACMEClient(account) if err != nil { return err } if needRegister { // New users will need to register; be sure to save it log.Infof("Register...") reg, err := a.client.Register() if err != nil { return err } account.Registration = reg } // The client has a URL to the current Let's Encrypt Subscriber // Agreement. The user will need to agree to it. log.Debugf("AgreeToTOS...") err = a.client.AgreeToTOS() if err != nil { // Let's Encrypt Subscriber Agreement renew ? reg, err := a.client.QueryRegistration() if err != nil { return err } account.Registration = reg err = a.client.AgreeToTOS() if err != nil { log.Errorf("Error sending ACME agreement to TOS: %+v: %s", account, err.Error()) } } // save account transaction, _, err := a.store.Begin() if err != nil { return err } err = transaction.Commit(account) if err != nil { return err } safe.Go(func() { a.retrieveCertificates() if err := a.renewCertificates(); err != nil { log.Errorf("Error renewing ACME certificate %+v: %s", account, err.Error()) } }) ticker := time.NewTicker(24 * time.Hour) safe.Go(func() { for range ticker.C { if err := a.renewCertificates(); err != nil { log.Errorf("Error renewing ACME certificate %+v: %s", account, err.Error()) } } }) return nil }
// CreateClusterConfig creates a tls.config using ACME configuration in cluster mode func (a *ACME) CreateClusterConfig(leadership *cluster.Leadership, tlsConfig *tls.Config, checkOnDemandDomain func(domain string) bool) error { err := a.init() if err != nil { return err } if len(a.Storage) == 0 { return errors.New("Empty Store, please provide a key for certs storage") } a.checkOnDemandDomain = checkOnDemandDomain tlsConfig.Certificates = append(tlsConfig.Certificates, *a.defaultCertificate) tlsConfig.GetCertificate = a.getCertificate listener := func(object cluster.Object) error { account := object.(*Account) account.Init() if !leadership.IsLeader() { a.client, err = a.buildACMEClient(account) if err != nil { log.Errorf("Error building ACME client %+v: %s", object, err.Error()) } } return nil } datastore, err := cluster.NewDataStore( leadership.Pool.Ctx(), staert.KvSource{ Store: leadership.Store, Prefix: a.Storage, }, &Account{}, listener) if err != nil { return err } a.store = datastore a.challengeProvider = &challengeProvider{store: a.store} ticker := time.NewTicker(24 * time.Hour) leadership.Pool.AddGoCtx(func(ctx context.Context) { log.Infof("Starting ACME renew job...") defer log.Infof("Stopped ACME renew job...") for { select { case <-ctx.Done(): return case <-ticker.C: if err := a.renewCertificates(); err != nil { log.Errorf("Error renewing ACME certificate: %s", err.Error()) } } } }) leadership.AddListener(func(elected bool) error { if elected { object, err := a.store.Load() if err != nil { return err } transaction, object, err := a.store.Begin() if err != nil { return err } account := object.(*Account) account.Init() var needRegister bool if account == nil || len(account.Email) == 0 { account, err = NewAccount(a.Email) if err != nil { return err } needRegister = true } if err != nil { return err } a.client, err = a.buildACMEClient(account) if err != nil { return err } if needRegister { // New users will need to register; be sure to save it log.Debugf("Register...") reg, err := a.client.Register() if err != nil { return err } account.Registration = reg } // The client has a URL to the current Let's Encrypt Subscriber // Agreement. The user will need to agree to it. log.Debugf("AgreeToTOS...") err = a.client.AgreeToTOS() if err != nil { // Let's Encrypt Subscriber Agreement renew ? reg, err := a.client.QueryRegistration() if err != nil { return err } account.Registration = reg err = a.client.AgreeToTOS() if err != nil { log.Errorf("Error sending ACME agreement to TOS: %+v: %s", account, err.Error()) } } err = transaction.Commit(account) if err != nil { return err } safe.Go(func() { a.retrieveCertificates() if err := a.renewCertificates(); err != nil { log.Errorf("Error renewing ACME certificate %+v: %s", account, err.Error()) } }) } return nil }) return nil }