package main
import (
"crypto/tls"
"crypto/x509"
"io/ioutil"
"log"
"net/http"
"net/http/httputil"
"net/url"
"sync"
"time"
)
type ClientInfo struct {
mu sync.Mutex
lastRequest map[string]time.Time
}
func main() {
serverCrt, err := tls.LoadX509KeyPair("server.crt", "server.key")
if err != nil {
log.Fatalf("server: loadkeys: %s", err)
}
caCert, err := ioutil.ReadFile("ca.crt")
if err != nil {
log.Fatal(err)
}
caCertPool := x509.NewCertPool()
caCertPool.AppendCertsFromPEM(caCert)
tlsConfig := &tls.Config{
Certificates: []tls.Certificate{serverCrt},
ClientAuth: tls.RequireAndVerifyClientCert,
ClientCAs: caCertPool,
GetCertificate: func(*tls.ClientHelloInfo) (*tls.Certificate, error) {
return &serverCrt, nil
},
}
clientInfo := &ClientInfo{
lastRequest: make(map[string]time.Time),
}
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
if r.TLS != nil && len(r.TLS.PeerCertificates) > 0 {
cert := r.TLS.PeerCertificates[0]
clientInfo.mu.Lock()
lastRequest, ok := clientInfo.lastRequest[cert.Subject.CommonName]
//log only requests unique after 10s
if !ok || time.Since(lastRequest) > 10*time.Second {
log.Printf("Client Cert Subject Common Name: %s\n", cert.Subject.CommonName)
clientInfo.lastRequest[cert.Subject.CommonName] = time.Now()
}
clientInfo.mu.Unlock()
var targetUrl *url.URL
switch cert.Subject.CommonName {
case "client04":
targetUrl, _ = url.Parse("http://localhost:8144")
case "client05":
targetUrl, _ = url.Parse("http://localhost:8145")
default:
http.Error(w, "Unauthorized", http.StatusUnauthorized)
return
}
reverseProxy := httputil.NewSingleHostReverseProxy(targetUrl)
reverseProxy.ServeHTTP(w, r)
}
})
server := &http.Server{
Addr: ":9140",
TLSConfig: tlsConfig,
}
log.Fatal(server.ListenAndServeTLS("", ""))
}
- A certeket egyelőre simán egy belső CA-ból generálom. Nyilván komolyabb felhasználásra igazi CA kell, vagy legalább a sajátot jobban megvédeni, intermediate certet használni stb.
- a server.key és server.crt a reverse proxy kulcspárja, a szervert hitelesíti és a kliens felé történő HTTPS kommunikációt titkosítja.
- kliens oldali hitelesítésként csak olyan certet fogad el, amit a kódban beimportált CA-k egyike állított ki.
- A CN subject mezőt használom usernév gyanánt
- A kliens certet is a szokásos módon állítjuk elő:
- kliens gépen generáljuk privát kulcsot
- létrehozunk egy certificate signing request-et (CSR)
- ebből a CA-n létrehozzuk a certet
# Create CA
[ca]$ openssl genrsa -out ca.key 4096
[ca]$ openssl req -x509 -new -nodes -key ca.key -sha256 -days 1024 -out ca.crt
# Generate private key + CSR for reverse proxy
[proxy]$ openssl genrsa -out server.key 4096
[proxy]$ openssl req -new -key server.key -out server.csr
# Generate private key + CSR for client
[client]$ openssl genrsa -out client.key 4096
[client]$ openssl req -new -key client.key -out client.csr
# Sign server cert
[ca]$ openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt -days 1024 -sha256
# Sign client cert
[ca]$ openssl x509 -req -in client.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out client.crt -days 1024 -sha256
Kliensoldalon szükség lehet még a TLS cert és privát kulcs összefűzésére, ahol csak egy file-t tud használni a TLS client cert paraméter.
Nem vagyok a téma szakértője, csak ismerkedem, de a fentiekből már sikerült összehegeszteni egy működő tesztrendszert.
- kruska blogja
- A hozzászóláshoz be kell jelentkezni
- 477 megtekintés
Hozzászólások
Haproxy -val megoldható a content routing bármilyen kb bármilyen TLS paraméter alapján.
- A hozzászóláshoz be kell jelentkezni
Az URL parszolast és a revproxy létrehozását kihozhatnad a handleren kívülre. Ezek drága dolgoknak tűnnek nekem, de csak a partvonalrol ugatok.
- A hozzászóláshoz be kell jelentkezni
de ha mar igy tweakelunk :) , en a lastRequest ido mokat is kitennem kulon fuggvenybe:
func (ci ClientInfo) NeedLog(CommonName string) bool {
ci.mu.Lock()
defer ci.mu.Unlock()
lastRequest, ok := ci.lastRequest[CommonName]
//log only requests unique after 10s
if !ok || time.Since(lastRequest) > 10*time.Second {
ci.lastRequest[CommonName] = time.Now()
return true
}
return false
}
aztan a kodban csak ennyi lenne: if clientInfo.NeedLog(cert.Subject.CommonName) { log.Printf("Client Cert Subject Common Name: %s\n", cert.Subject.CommonName) }
A vegtelen ciklus is vegeter egyszer, csak kelloen eros hardver kell hozza!
- A hozzászóláshoz be kell jelentkezni