Merge pull request #31 from syncthing/http
Allow plain HTTP serving behind a proxy
This commit is contained in:
commit
f83ae630c1
|
@ -25,6 +25,7 @@ var (
|
||||||
certFile = "cert.pem"
|
certFile = "cert.pem"
|
||||||
keyFile = "key.pem"
|
keyFile = "key.pem"
|
||||||
debug = false
|
debug = false
|
||||||
|
useHttp = false
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
@ -48,15 +49,20 @@ func main() {
|
||||||
flag.StringVar(&certFile, "cert", certFile, "Certificate file")
|
flag.StringVar(&certFile, "cert", certFile, "Certificate file")
|
||||||
flag.StringVar(&keyFile, "key", keyFile, "Key file")
|
flag.StringVar(&keyFile, "key", keyFile, "Key file")
|
||||||
flag.BoolVar(&debug, "debug", debug, "Debug")
|
flag.BoolVar(&debug, "debug", debug, "Debug")
|
||||||
|
flag.BoolVar(&useHttp, "http", useHttp, "Listen on HTTP (behind an HTTPS proxy)")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
cert, err := tls.LoadX509KeyPair(certFile, keyFile)
|
var cert tls.Certificate
|
||||||
if err != nil {
|
var err error
|
||||||
log.Fatalln("Failed to load X509 key pair:", err)
|
if !useHttp {
|
||||||
}
|
cert, err = tls.LoadX509KeyPair(certFile, keyFile)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln("Failed to load X509 key pair:", err)
|
||||||
|
}
|
||||||
|
|
||||||
devID := protocol.NewDeviceID(cert.Certificate[0])
|
devID := protocol.NewDeviceID(cert.Certificate[0])
|
||||||
log.Println("Server device ID is", devID)
|
log.Println("Server device ID is", devID)
|
||||||
|
}
|
||||||
|
|
||||||
db, err := sql.Open(backend, dsn)
|
db, err := sql.Open(backend, dsn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -3,9 +3,11 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"encoding/pem"
|
||||||
"log"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
@ -39,39 +41,47 @@ type annRelay struct {
|
||||||
func (s *querysrv) Serve() {
|
func (s *querysrv) Serve() {
|
||||||
s.limiter = lru.New(lruSize)
|
s.limiter = lru.New(lruSize)
|
||||||
|
|
||||||
tlsCfg := &tls.Config{
|
if useHttp {
|
||||||
Certificates: []tls.Certificate{s.cert},
|
listener, err := net.Listen("tcp", s.addr)
|
||||||
ClientAuth: tls.RequestClientCert,
|
if err != nil {
|
||||||
SessionTicketsDisabled: true,
|
log.Println("Listen:", err)
|
||||||
MinVersion: tls.VersionTLS12,
|
return
|
||||||
CipherSuites: []uint16{
|
}
|
||||||
tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
|
s.listener = listener
|
||||||
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
|
} else {
|
||||||
tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
|
tlsCfg := &tls.Config{
|
||||||
tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
|
Certificates: []tls.Certificate{s.cert},
|
||||||
tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
|
ClientAuth: tls.RequestClientCert,
|
||||||
tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
|
SessionTicketsDisabled: true,
|
||||||
},
|
MinVersion: tls.VersionTLS12,
|
||||||
|
CipherSuites: []uint16{
|
||||||
|
tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
|
||||||
|
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
|
||||||
|
tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
|
||||||
|
tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
|
||||||
|
tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
|
||||||
|
tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
tlsListener, err := tls.Listen("tcp", s.addr, tlsCfg)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("Listen:", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s.listener = tlsListener
|
||||||
}
|
}
|
||||||
|
|
||||||
http.HandleFunc("/", s.handler)
|
http.HandleFunc("/", s.handler)
|
||||||
http.HandleFunc("/ping", handlePing)
|
http.HandleFunc("/ping", handlePing)
|
||||||
|
|
||||||
tlsListener, err := tls.Listen("tcp", s.addr, tlsCfg)
|
|
||||||
if err != nil {
|
|
||||||
log.Println("Listen:", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
s.listener = tlsListener
|
|
||||||
|
|
||||||
srv := &http.Server{
|
srv := &http.Server{
|
||||||
ReadTimeout: 5 * time.Second,
|
ReadTimeout: 5 * time.Second,
|
||||||
WriteTimeout: 5 * time.Second,
|
WriteTimeout: 5 * time.Second,
|
||||||
MaxHeaderBytes: 1 << 10,
|
MaxHeaderBytes: 1 << 10,
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := srv.Serve(tlsListener); err != nil {
|
if err := srv.Serve(s.listener); err != nil {
|
||||||
log.Println("Serve:", err)
|
log.Println("Serve:", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -81,16 +91,22 @@ func (s *querysrv) handler(w http.ResponseWriter, req *http.Request) {
|
||||||
log.Println(req.Method, req.URL)
|
log.Println(req.Method, req.URL)
|
||||||
}
|
}
|
||||||
|
|
||||||
remoteAddr, err := net.ResolveTCPAddr("tcp", req.RemoteAddr)
|
var remoteIP net.IP
|
||||||
if err != nil {
|
if useHttp {
|
||||||
log.Println("remoteAddr:", err)
|
remoteIP = net.ParseIP(req.Header.Get("X-Forwarded-For"))
|
||||||
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
|
} else {
|
||||||
return
|
addr, err := net.ResolveTCPAddr("tcp", req.RemoteAddr)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("remoteAddr:", err)
|
||||||
|
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
remoteIP = addr.IP
|
||||||
}
|
}
|
||||||
|
|
||||||
if s.limit(remoteAddr.IP) {
|
if s.limit(remoteIP) {
|
||||||
if debug {
|
if debug {
|
||||||
log.Println(remoteAddr.IP, "is limited")
|
log.Println(remoteIP, "is limited")
|
||||||
}
|
}
|
||||||
w.Header().Set("Retry-After", "60")
|
w.Header().Set("Retry-After", "60")
|
||||||
http.Error(w, "Too Many Requests", 429)
|
http.Error(w, "Too Many Requests", 429)
|
||||||
|
@ -101,7 +117,7 @@ func (s *querysrv) handler(w http.ResponseWriter, req *http.Request) {
|
||||||
case "GET":
|
case "GET":
|
||||||
s.handleGET(w, req)
|
s.handleGET(w, req)
|
||||||
case "POST":
|
case "POST":
|
||||||
s.handlePOST(w, req)
|
s.handlePOST(remoteIP, w, req)
|
||||||
default:
|
default:
|
||||||
globalStats.Error()
|
globalStats.Error()
|
||||||
http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed)
|
http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed)
|
||||||
|
@ -109,15 +125,6 @@ func (s *querysrv) handler(w http.ResponseWriter, req *http.Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *querysrv) handleGET(w http.ResponseWriter, req *http.Request) {
|
func (s *querysrv) handleGET(w http.ResponseWriter, req *http.Request) {
|
||||||
if req.TLS == nil {
|
|
||||||
if debug {
|
|
||||||
log.Println(req.Method, req.URL, "not TLS")
|
|
||||||
}
|
|
||||||
globalStats.Error()
|
|
||||||
http.Error(w, "Forbidden", http.StatusForbidden)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
deviceID, err := protocol.DeviceIDFromString(req.URL.Query().Get("device"))
|
deviceID, err := protocol.DeviceIDFromString(req.URL.Query().Get("device"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if debug {
|
if debug {
|
||||||
|
@ -159,17 +166,9 @@ func (s *querysrv) handleGET(w http.ResponseWriter, req *http.Request) {
|
||||||
json.NewEncoder(w).Encode(ann)
|
json.NewEncoder(w).Encode(ann)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *querysrv) handlePOST(w http.ResponseWriter, req *http.Request) {
|
func (s *querysrv) handlePOST(remoteIP net.IP, w http.ResponseWriter, req *http.Request) {
|
||||||
if req.TLS == nil {
|
rawCert := certificateBytes(req)
|
||||||
if debug {
|
if rawCert == nil {
|
||||||
log.Println(req.Method, req.URL, "not TLS")
|
|
||||||
}
|
|
||||||
globalStats.Error()
|
|
||||||
http.Error(w, "Forbidden", http.StatusForbidden)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(req.TLS.PeerCertificates) == 0 {
|
|
||||||
if debug {
|
if debug {
|
||||||
log.Println(req.Method, req.URL, "no certificates")
|
log.Println(req.Method, req.URL, "no certificates")
|
||||||
}
|
}
|
||||||
|
@ -188,22 +187,14 @@ func (s *querysrv) handlePOST(w http.ResponseWriter, req *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
remoteAddr, err := net.ResolveTCPAddr("tcp", req.RemoteAddr)
|
deviceID := protocol.NewDeviceID(rawCert)
|
||||||
if err != nil {
|
|
||||||
log.Println("remoteAddr:", err)
|
|
||||||
globalStats.Error()
|
|
||||||
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
deviceID := protocol.NewDeviceID(req.TLS.PeerCertificates[0].Raw)
|
|
||||||
|
|
||||||
// handleAnnounce returns *two* errors. The first indicates a problem with
|
// handleAnnounce returns *two* errors. The first indicates a problem with
|
||||||
// something the client posted to us. We should return a 400 Bad Request
|
// something the client posted to us. We should return a 400 Bad Request
|
||||||
// and not worry about it. The second indicates that the request was fine,
|
// and not worry about it. The second indicates that the request was fine,
|
||||||
// but something internal fucked up. We should log it and respond with a
|
// but something internal fucked up. We should log it and respond with a
|
||||||
// more apologetic 500 Internal Server Error.
|
// more apologetic 500 Internal Server Error.
|
||||||
userErr, internalErr := s.handleAnnounce(remoteAddr.IP, deviceID, ann.Direct, ann.Relays)
|
userErr, internalErr := s.handleAnnounce(remoteIP, deviceID, ann.Direct, ann.Relays)
|
||||||
if userErr != nil {
|
if userErr != nil {
|
||||||
if debug {
|
if debug {
|
||||||
log.Println(req.Method, req.URL, userErr)
|
log.Println(req.Method, req.URL, userErr)
|
||||||
|
@ -396,3 +387,32 @@ func (s *querysrv) getRelays(device protocol.DeviceID) ([]annRelay, error) {
|
||||||
func handlePing(w http.ResponseWriter, r *http.Request) {
|
func handlePing(w http.ResponseWriter, r *http.Request) {
|
||||||
w.WriteHeader(204)
|
w.WriteHeader(204)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func certificateBytes(req *http.Request) []byte {
|
||||||
|
if req.TLS != nil && len(req.TLS.PeerCertificates) > 0 {
|
||||||
|
return req.TLS.PeerCertificates[0].Raw
|
||||||
|
}
|
||||||
|
|
||||||
|
if hdr := req.Header.Get("X-SSL-Cert"); hdr != "" {
|
||||||
|
bs := []byte(hdr)
|
||||||
|
// The certificate is in PEM format but with spaces for newlines. We
|
||||||
|
// need to reinstate the newlines for the PEM decoder. But we need to
|
||||||
|
// leave the spaces in the BEGIN and END lines - the first and last
|
||||||
|
// space - alone.
|
||||||
|
firstSpace := bytes.Index(bs, []byte(" "))
|
||||||
|
lastSpace := bytes.LastIndex(bs, []byte(" "))
|
||||||
|
for i := firstSpace + 1; i < lastSpace; i++ {
|
||||||
|
if bs[i] == ' ' {
|
||||||
|
bs[i] = '\n'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
block, _ := pem.Decode(bs)
|
||||||
|
if block == nil {
|
||||||
|
// Decoding failed
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return block.Bytes
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user