Implement HTTPS for GUI

This commit is contained in:
Jakob Borg 2014-05-21 14:04:16 +02:00
parent 30837a7d95
commit 7c8652b600
6 changed files with 43 additions and 14 deletions

File diff suppressed because one or more lines are too long

View File

@ -13,6 +13,7 @@ import (
"sync" "sync"
"time" "time"
"crypto/tls"
"code.google.com/p/go.crypto/bcrypt" "code.google.com/p/go.crypto/bcrypt"
"github.com/calmh/syncthing/config" "github.com/calmh/syncthing/config"
"github.com/calmh/syncthing/logger" "github.com/calmh/syncthing/logger"
@ -42,9 +43,30 @@ func init() {
} }
func startGUI(cfg config.GUIConfiguration, m *model.Model) error { func startGUI(cfg config.GUIConfiguration, m *model.Model) error {
listener, err := net.Listen("tcp", cfg.Address) var listener net.Listener
if err != nil { var err error
return err if cfg.UseTLS {
cert, err := loadCert(confDir, "https-")
if err != nil {
newCertificate(confDir, "https-")
cert, err = loadCert(confDir, "https-")
}
if err != nil {
return err
}
tlsCfg := &tls.Config{
Certificates: []tls.Certificate{cert},
ServerName: "syncthing",
}
listener, err = tls.Listen("tcp", cfg.Address, tlsCfg)
if err != nil {
return err
}
} else {
listener, err = net.Listen("tcp", cfg.Address)
if err != nil {
return err
}
} }
router := martini.NewRouter() router := martini.NewRouter()

View File

@ -144,10 +144,10 @@ func main() {
// Ensure that our home directory exists and that we have a certificate and key. // Ensure that our home directory exists and that we have a certificate and key.
ensureDir(confDir, 0700) ensureDir(confDir, 0700)
cert, err := loadCert(confDir) cert, err := loadCert(confDir, "")
if err != nil { if err != nil {
newCertificate(confDir) newCertificate(confDir, "")
cert, err = loadCert(confDir) cert, err = loadCert(confDir, "")
l.FatalErr(err) l.FatalErr(err)
} }
@ -272,13 +272,18 @@ func main() {
hostShow = hostOpen hostShow = hostOpen
} }
l.Infof("Starting web GUI on http://%s:%d/", hostShow, addr.Port) var proto = "http"
if cfg.GUI.UseTLS {
proto = "https"
}
l.Infof("Starting web GUI on %s://%s:%d/", proto, hostShow, addr.Port)
err := startGUI(cfg.GUI, m) err := startGUI(cfg.GUI, m)
if err != nil { if err != nil {
l.Fatalln("Cannot start GUI:", err) l.Fatalln("Cannot start GUI:", err)
} }
if cfg.Options.StartBrowser && len(os.Getenv("STRESTART")) == 0 { if cfg.Options.StartBrowser && len(os.Getenv("STRESTART")) == 0 {
openURL(fmt.Sprintf("http://%s:%d", hostOpen, addr.Port)) openURL(fmt.Sprintf("%s://%s:%d", proto, hostOpen, addr.Port))
} }
} }
} }

View File

@ -22,8 +22,8 @@ const (
tlsName = "syncthing" tlsName = "syncthing"
) )
func loadCert(dir string) (tls.Certificate, error) { func loadCert(dir string, prefix string) (tls.Certificate, error) {
return tls.LoadX509KeyPair(filepath.Join(dir, "cert.pem"), filepath.Join(dir, "key.pem")) return tls.LoadX509KeyPair(filepath.Join(dir, prefix+"cert.pem"), filepath.Join(dir, prefix+"key.pem"))
} }
func certID(bs []byte) string { func certID(bs []byte) string {
@ -40,7 +40,7 @@ func certSeed(bs []byte) int64 {
return int64(binary.BigEndian.Uint64(id)) return int64(binary.BigEndian.Uint64(id))
} }
func newCertificate(dir string) { func newCertificate(dir string, prefix string) {
l.Infoln("Generating RSA certificate and key...") l.Infoln("Generating RSA certificate and key...")
priv, err := rsa.GenerateKey(rand.Reader, tlsRSABits) priv, err := rsa.GenerateKey(rand.Reader, tlsRSABits)
@ -65,13 +65,13 @@ func newCertificate(dir string) {
derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv) derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv)
l.FatalErr(err) l.FatalErr(err)
certOut, err := os.Create(filepath.Join(dir, "cert.pem")) certOut, err := os.Create(filepath.Join(dir, prefix+"cert.pem"))
l.FatalErr(err) l.FatalErr(err)
pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes}) pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
certOut.Close() certOut.Close()
l.Okln("Created RSA certificate file") l.Okln("Created RSA certificate file")
keyOut, err := os.OpenFile(filepath.Join(dir, "key.pem"), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600) keyOut, err := os.OpenFile(filepath.Join(dir, prefix+"key.pem"), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
l.FatalErr(err) l.FatalErr(err)
pem.Encode(keyOut, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(priv)}) pem.Encode(keyOut, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(priv)})
keyOut.Close() keyOut.Close()

View File

@ -73,6 +73,7 @@ type GUIConfiguration struct {
Address string `xml:"address" default:"127.0.0.1:8080"` Address string `xml:"address" default:"127.0.0.1:8080"`
User string `xml:"user,omitempty"` User string `xml:"user,omitempty"`
Password string `xml:"password,omitempty"` Password string `xml:"password,omitempty"`
UseTLS bool `xml:"tls,attr"`
} }
func setDefaults(data interface{}) error { func setDefaults(data interface{}) error {

View File

@ -40,6 +40,7 @@ syncthing.controller('SyncthingCtrl', function ($scope, $http) {
{id: 'Address', descr: 'GUI Listen Addresses', type: 'text', restart: true}, {id: 'Address', descr: 'GUI Listen Addresses', type: 'text', restart: true},
{id: 'User', descr: 'GUI Authentication User', type: 'text', restart: true}, {id: 'User', descr: 'GUI Authentication User', type: 'text', restart: true},
{id: 'Password', descr: 'GUI Authentication Password', type: 'password', restart: true}, {id: 'Password', descr: 'GUI Authentication Password', type: 'password', restart: true},
{id: 'UseTLS', descr: 'Use HTTPS for GUI', type: 'bool', restart: true},
]; ];
function getSucceeded() { function getSucceeded() {