forked from Silverfish/proton-bridge
feat: switch to proxy when need be
This commit is contained in:
@ -97,7 +97,7 @@ func New(
|
||||
// Allow DoH before starting bridge if the user has previously set this setting.
|
||||
// This allows us to start even if protonmail is blocked.
|
||||
if pref.GetBool(preferences.AllowProxyKey) {
|
||||
AllowDoH()
|
||||
b.AllowProxy()
|
||||
}
|
||||
|
||||
go func() {
|
||||
@ -178,15 +178,16 @@ func (b *Bridge) watchBridgeOutdated() {
|
||||
}
|
||||
}
|
||||
|
||||
// watchUserAuths receives auths from the client manager and sends them to the appropriate user.
|
||||
func (b *Bridge) watchUserAuths() {
|
||||
for auth := range b.clientManager.GetBridgeAuthChannel() {
|
||||
user, ok := b.hasUser(auth.UserID)
|
||||
logrus.WithField("token", auth.Auth.GenToken()).WithField("userID", auth.UserID).Info("Received auth from bridge auth channel")
|
||||
|
||||
if !ok {
|
||||
continue
|
||||
if user, ok := b.hasUser(auth.UserID); ok {
|
||||
user.ReceiveAPIAuth(auth.Auth)
|
||||
} else {
|
||||
logrus.Info("User is not added to bridge yet")
|
||||
}
|
||||
|
||||
user.ReceiveAPIAuth(auth.Auth)
|
||||
}
|
||||
}
|
||||
|
||||
@ -274,7 +275,7 @@ func (b *Bridge) FinishLogin(loginClient PMAPIProvider, auth *pmapi.Auth, mbPass
|
||||
apiClient := b.clientManager.GetClient(apiUser.ID)
|
||||
auth, err = apiClient.AuthRefresh(auth.GenToken())
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Could refresh token in new client")
|
||||
log.WithError(err).Error("Could not refresh token in new client")
|
||||
return
|
||||
}
|
||||
|
||||
@ -298,6 +299,7 @@ func (b *Bridge) FinishLogin(loginClient PMAPIProvider, auth *pmapi.Auth, mbPass
|
||||
log.WithField("user", apiUser.ID).WithError(err).Error("Could not create user")
|
||||
return
|
||||
}
|
||||
b.users = append(b.users, user)
|
||||
}
|
||||
|
||||
// Set up the user auth and store (which we do for both new and existing users).
|
||||
@ -307,7 +309,6 @@ func (b *Bridge) FinishLogin(loginClient PMAPIProvider, auth *pmapi.Auth, mbPass
|
||||
}
|
||||
|
||||
if !hasUser {
|
||||
b.users = append(b.users, user)
|
||||
b.SendMetric(m.New(m.Setup, m.NewUser, m.NoLabel))
|
||||
}
|
||||
|
||||
@ -475,16 +476,16 @@ func (b *Bridge) GetIMAPUpdatesChannel() chan interface{} {
|
||||
return b.idleUpdates
|
||||
}
|
||||
|
||||
// AllowDoH instructs bridge to use DoH to access an API proxy if necessary.
|
||||
// AllowProxy instructs bridge to use DoH to access an API proxy if necessary.
|
||||
// It also needs to work before bridge is initialised (because we may need to use the proxy at startup).
|
||||
func AllowDoH() {
|
||||
pmapi.GlobalAllowDoH()
|
||||
func (b *Bridge) AllowProxy() {
|
||||
b.clientManager.AllowProxy()
|
||||
}
|
||||
|
||||
// DisallowDoH instructs bridge to not use DoH to access an API proxy if necessary.
|
||||
// DisallowProxy instructs bridge to not use DoH to access an API proxy if necessary.
|
||||
// It also needs to work before bridge is initialised (because we may need to use the proxy at startup).
|
||||
func DisallowDoH() {
|
||||
pmapi.GlobalDisallowDoH()
|
||||
func (b *Bridge) DisallowProxy() {
|
||||
b.clientManager.DisallowProxy()
|
||||
}
|
||||
|
||||
func (b *Bridge) updateCurrentUserAgent() {
|
||||
@ -493,7 +494,11 @@ func (b *Bridge) updateCurrentUserAgent() {
|
||||
|
||||
// hasUser returns whether the bridge currently has a user with ID `id`.
|
||||
func (b *Bridge) hasUser(id string) (user *User, ok bool) {
|
||||
logrus.WithField("id", id).Info("Checking whether bridge has given user")
|
||||
|
||||
for _, u := range b.users {
|
||||
logrus.WithField("id", u.ID()).Info("Found potential user")
|
||||
|
||||
if u.ID() == id {
|
||||
user, ok = u, true
|
||||
return
|
||||
|
||||
@ -107,6 +107,8 @@ func (u *User) init(idleUpdates chan interface{}) (err error) {
|
||||
u.wasKeyringUnlocked = false
|
||||
u.unlockingKeyringLock.Unlock()
|
||||
|
||||
u.log.Info("Initialising user")
|
||||
|
||||
// Reload the user's credentials (if they log out and back in we need the new
|
||||
// version with the apitoken and mailbox password).
|
||||
creds, err := u.credStorer.Get(u.userID)
|
||||
@ -242,27 +244,19 @@ func (u *User) authorizeAndUnlock() (err error) {
|
||||
}
|
||||
|
||||
func (u *User) ReceiveAPIAuth(auth *pmapi.Auth) {
|
||||
u.lock.Lock()
|
||||
defer u.lock.Unlock()
|
||||
|
||||
if auth == nil {
|
||||
if err := u.logout(); err != nil {
|
||||
u.log.WithError(err).Error("Cannot logout user after receiving empty auth from API")
|
||||
u.log.WithError(err).Error("Failed to logout user after receiving empty auth from API")
|
||||
}
|
||||
u.isAuthorized = false
|
||||
return
|
||||
}
|
||||
|
||||
u.updateAPIToken(auth.GenToken())
|
||||
}
|
||||
|
||||
// updateAPIToken is helper for updating the token in keychain. It's not supposed to be
|
||||
// called directly from other parts of the code, only from `ReceiveAPIAuth`.
|
||||
func (u *User) updateAPIToken(newRefreshToken string) {
|
||||
u.lock.Lock()
|
||||
defer u.lock.Unlock()
|
||||
|
||||
u.log.WithField("token", newRefreshToken).Info("Saving token to credentials store")
|
||||
|
||||
if err := u.credStorer.UpdateToken(u.userID, newRefreshToken); err != nil {
|
||||
u.log.WithError(err).Error("Cannot update refresh token in credentials store")
|
||||
if err := u.credStorer.UpdateToken(u.userID, auth.GenToken()); err != nil {
|
||||
u.log.WithError(err).Error("Failed to update refresh token in credentials store")
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@ -22,7 +22,6 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/ProtonMail/proton-bridge/internal/bridge"
|
||||
"github.com/ProtonMail/proton-bridge/internal/preferences"
|
||||
"github.com/ProtonMail/proton-bridge/pkg/connection"
|
||||
"github.com/ProtonMail/proton-bridge/pkg/ports"
|
||||
@ -135,13 +134,13 @@ func (f *frontendCLI) toggleAllowProxy(c *ishell.Context) {
|
||||
f.Println("Bridge is currently set to use alternative routing to connect to Proton if it is being blocked.")
|
||||
if f.yesNoQuestion("Are you sure you want to stop bridge from doing this") {
|
||||
f.preferences.SetBool(preferences.AllowProxyKey, false)
|
||||
bridge.DisallowDoH()
|
||||
f.bridge.DisallowProxy()
|
||||
}
|
||||
} else {
|
||||
f.Println("Bridge is currently set to NOT use alternative routing to connect to Proton if it is being blocked.")
|
||||
if f.yesNoQuestion("Are you sure you want to allow bridge to do this") {
|
||||
f.preferences.SetBool(preferences.AllowProxyKey, true)
|
||||
bridge.AllowDoH()
|
||||
f.bridge.AllowProxy()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -52,6 +52,8 @@ type Bridger interface {
|
||||
DeleteUser(userID string, clearCache bool) error
|
||||
ReportBug(osType, osVersion, description, accountName, address, emailClient string) error
|
||||
ClearData() error
|
||||
AllowProxy()
|
||||
DisallowProxy()
|
||||
}
|
||||
|
||||
// BridgeUser is an interface of user needed by frontend.
|
||||
|
||||
@ -21,11 +21,14 @@
|
||||
package pmapifactory
|
||||
|
||||
import (
|
||||
"github.com/ProtonMail/proton-bridge/internal/bridge"
|
||||
"github.com/ProtonMail/proton-bridge/pkg/listener"
|
||||
"github.com/ProtonMail/proton-bridge/pkg/pmapi"
|
||||
)
|
||||
|
||||
func GetClientConfig(config bridge.Configer, _ listener.Listener) *pmapi.ClientConfig {
|
||||
return config.GetAPIConfig()
|
||||
func GetClientConfig(clientConfig *pmapi.ClientConfig) *pmapi.ClientConfig {
|
||||
return clientConfig
|
||||
}
|
||||
|
||||
func SetClientRoundTripper(_ *pmapi.ClientManager, _ *pmapi.ClientConfig, _ listener.Listener) {
|
||||
// Use the default roundtripper; do nothing.
|
||||
}
|
||||
|
||||
@ -23,26 +23,13 @@ package pmapifactory
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/ProtonMail/proton-bridge/internal/bridge"
|
||||
"github.com/ProtonMail/proton-bridge/internal/events"
|
||||
"github.com/ProtonMail/proton-bridge/pkg/listener"
|
||||
"github.com/ProtonMail/proton-bridge/pkg/pmapi"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func GetClientConfig(config bridge.Configer, listener listener.Listener) *pmapi.ClientConfig {
|
||||
clientConfig := config.GetAPIConfig()
|
||||
|
||||
pin := pmapi.NewPMAPIPinning(clientConfig.AppVersion)
|
||||
pin.ReportCertIssueLocal = func() {
|
||||
listener.Emit(events.TLSCertIssue, "")
|
||||
}
|
||||
|
||||
// This transport already has timeouts set governing the roundtrip:
|
||||
// - IdleConnTimeout: 5 * time.Minute,
|
||||
// - ExpectContinueTimeout: 500 * time.Millisecond,
|
||||
// - ResponseHeaderTimeout: 30 * time.Second,
|
||||
clientConfig.Transport = pin.TransportWithPinning()
|
||||
|
||||
func GetClientConfig(clientConfig *pmapi.ClientConfig) *pmapi.ClientConfig {
|
||||
// We set additional timeouts/thresholds for the request as a whole:
|
||||
clientConfig.Timeout = 10 * time.Minute // Overall request timeout (~25MB / 10 mins => ~40kB/s, should be reasonable).
|
||||
clientConfig.FirstReadTimeout = 30 * time.Second // 30s to match 30s response header timeout.
|
||||
@ -50,3 +37,15 @@ func GetClientConfig(config bridge.Configer, listener listener.Listener) *pmapi.
|
||||
|
||||
return clientConfig
|
||||
}
|
||||
|
||||
func SetClientRoundTripper(cm *pmapi.ClientManager, cfg *pmapi.ClientConfig, listener listener.Listener) {
|
||||
logrus.Info("Setting dialer with pinning")
|
||||
|
||||
pin := pmapi.NewDialerWithPinning(cm, cfg.AppVersion)
|
||||
|
||||
pin.ReportCertIssueLocal = func() {
|
||||
listener.Emit(events.TLSCertIssue, "")
|
||||
}
|
||||
|
||||
cm.SetClientRoundTripper(pin.TransportWithPinning())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user