mirror of
https://github.com/nikdoof/hapz2m.git
synced 2026-01-30 10:28:21 +00:00
Use a more secure PIN config by default
The server used to use the hap default PIN, but using a fixed PIN is not secure. A random PIN is now generated on first run and displayed to the console (or journal), similar to how homebridge does it. It can also be specified explicitly by the user in the config file.
This commit is contained in:
@@ -47,6 +47,10 @@ Additionally, these options control networking aspects for the bridge:
|
|||||||
|
|
||||||
These settings are optional and can be left blank.
|
These settings are optional and can be left blank.
|
||||||
|
|
||||||
|
The pairing code for the server is generated dynamically on first startup
|
||||||
|
and printed to the console (or systemd journal). Alternatively, you can specify
|
||||||
|
it using `Pin` in the config file to have it fixed.
|
||||||
|
|
||||||
License
|
License
|
||||||
========
|
========
|
||||||
|
|
||||||
|
|||||||
60
bridge.go
60
bridge.go
@@ -12,9 +12,11 @@ import (
|
|||||||
"net/url"
|
"net/url"
|
||||||
|
|
||||||
"context"
|
"context"
|
||||||
|
"crypto/rand"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
|
"math/big"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"reflect"
|
"reflect"
|
||||||
@@ -36,6 +38,9 @@ const (
|
|||||||
// Store name for persisting zigbee2mqtt state
|
// Store name for persisting zigbee2mqtt state
|
||||||
Z2M_STATE_STORE = "z2m_state"
|
Z2M_STATE_STORE = "z2m_state"
|
||||||
|
|
||||||
|
// Store name for server PIN code
|
||||||
|
Z2M_PIN_STORE = "z2m_pin"
|
||||||
|
|
||||||
// timeout for UpdateZ2MState
|
// timeout for UpdateZ2MState
|
||||||
Z2M_UPDATE_TIMEOUT = 3 * time.Second
|
Z2M_UPDATE_TIMEOUT = 3 * time.Second
|
||||||
|
|
||||||
@@ -65,6 +70,7 @@ type Bridge struct {
|
|||||||
devices map[string]*BridgeDevice
|
devices map[string]*BridgeDevice
|
||||||
server *hap.Server
|
server *hap.Server
|
||||||
store hap.Store
|
store hap.Store
|
||||||
|
pin string
|
||||||
|
|
||||||
// RWMutex protects hap init variables below
|
// RWMutex protects hap init variables below
|
||||||
hapInitMutex sync.RWMutex
|
hapInitMutex sync.RWMutex
|
||||||
@@ -102,6 +108,51 @@ func NewBridge(ctx context.Context, storeDir string) *Bridge {
|
|||||||
return br
|
return br
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sets the PIN code for the HAP server.
|
||||||
|
// If the given pin is empty, it will be read from the store, or failing that,
|
||||||
|
// one will be generated
|
||||||
|
func (br *Bridge) SetPin(pin string) (string, error) {
|
||||||
|
// if PIN was not explicitly specified, we re-use the existing one from store
|
||||||
|
if pin == "" {
|
||||||
|
if storePin, err := br.store.Get(Z2M_PIN_STORE); err == nil {
|
||||||
|
pin = string(storePin)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
savePin := pin == ""
|
||||||
|
|
||||||
|
if pin == "" {
|
||||||
|
for {
|
||||||
|
rnd, err := rand.Int(rand.Reader, big.NewInt(99999999+1))
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("can't generate PIN: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// pad if necessary
|
||||||
|
pin = rnd.Text(10) + "00000000"
|
||||||
|
pin = pin[:8]
|
||||||
|
|
||||||
|
// ensure it's not an insecure PIN
|
||||||
|
if !hap.InvalidPins[pin] {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if hap.InvalidPins[pin] {
|
||||||
|
return "", fmt.Errorf("insecure pin %s", pin)
|
||||||
|
}
|
||||||
|
|
||||||
|
// persist the PIN
|
||||||
|
if savePin {
|
||||||
|
br.store.Set(Z2M_PIN_STORE, []byte(pin))
|
||||||
|
}
|
||||||
|
|
||||||
|
br.pin = pin
|
||||||
|
return pin, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the PIN
|
||||||
|
func (br *Bridge) GetPin() string { return br.pin }
|
||||||
|
|
||||||
// Initializes the hap.Server and calls ListenAndServe().
|
// Initializes the hap.Server and calls ListenAndServe().
|
||||||
// ListenAndServe() will block until the context is cancelled
|
// ListenAndServe() will block until the context is cancelled
|
||||||
func (br *Bridge) StartHAP() error {
|
func (br *Bridge) StartHAP() error {
|
||||||
@@ -109,6 +160,13 @@ func (br *Bridge) StartHAP() error {
|
|||||||
return fmt.Errorf("bridge accessory not created yet")
|
return fmt.Errorf("bridge accessory not created yet")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// initialize PIN, either from store or dynamically generated
|
||||||
|
if br.pin == "" {
|
||||||
|
if _, err := br.SetPin(""); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
br.hapInitMutex.RLock()
|
br.hapInitMutex.RLock()
|
||||||
if !br.hapInitDone {
|
if !br.hapInitDone {
|
||||||
br.hapInitMutex.RUnlock()
|
br.hapInitMutex.RUnlock()
|
||||||
@@ -124,6 +182,8 @@ func (br *Bridge) StartHAP() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
br.server.Pin = br.pin
|
||||||
|
|
||||||
br.server.Addr = br.ListenAddr
|
br.server.Addr = br.ListenAddr
|
||||||
br.server.Ifaces = br.Interfaces
|
br.server.Ifaces = br.Interfaces
|
||||||
|
|
||||||
|
|||||||
@@ -36,6 +36,8 @@ type config struct {
|
|||||||
ListenAddr string
|
ListenAddr string
|
||||||
Interfaces []string
|
Interfaces []string
|
||||||
|
|
||||||
|
Pin string
|
||||||
|
|
||||||
Server, Username, Password string
|
Server, Username, Password string
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,6 +91,10 @@ func main() {
|
|||||||
log.Fatalf("-quiet and -debug options are mutually-exclusive")
|
log.Fatalf("-quiet and -debug options are mutually-exclusive")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if _, err := br.SetPin(cfg.Pin); err != nil {
|
||||||
|
log.Fatalf("cannot set PIN code: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
// validate ListenAddr if specified
|
// validate ListenAddr if specified
|
||||||
if cfg.ListenAddr != "" {
|
if cfg.ListenAddr != "" {
|
||||||
_, _, err := net.SplitHostPort(cfg.ListenAddr)
|
_, _, err := net.SplitHostPort(cfg.ListenAddr)
|
||||||
@@ -120,6 +126,9 @@ func main() {
|
|||||||
|
|
||||||
log.Println("hapz2m configured. starting HAP server...")
|
log.Println("hapz2m configured. starting HAP server...")
|
||||||
|
|
||||||
|
pin := br.GetPin()
|
||||||
|
log.Printf("server PIN is %s-%s", pin[:4], pin[4:])
|
||||||
|
|
||||||
err = br.StartHAP()
|
err = br.StartHAP()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == http.ErrServerClosed {
|
if err == http.ErrServerClosed {
|
||||||
|
|||||||
Reference in New Issue
Block a user