mirror of
https://github.com/quexten/goldwarden.git
synced 2024-12-24 03:44:48 +03:00
Add more env variables
This commit is contained in:
parent
6e3859dd43
commit
6d21d10a9e
19
Readme.md
19
Readme.md
@ -170,9 +170,25 @@ You can bind this to a hotkey in your desktop environment (i.e i3/sway config fi
|
||||
|
||||
### Login with device
|
||||
Approving other devices works out of the box and is enabled by default. If the agent is unlocked, you will be prompted
|
||||
to approve the device.
|
||||
to approve the device. If you want to log into goldwarden using another device, add the "--passwordless" parameter to the login command.
|
||||
|
||||
|
||||
### Environment Variables
|
||||
```
|
||||
GOLDWARDEN_WEBSOCKET_DISABLED=true # disable websocket
|
||||
GOLDWARDEN_PIN_REQUIREMENT_DISABLED=true # disable pin requirement
|
||||
GOLDWARDEN_DO_NOT_PERSIST_CONFIG=true # do not persist config
|
||||
GOLDWARDEN_API_URI=https://my.bitwarden.domain/api # set api uri
|
||||
GOLDWARDEN_IDENTITY_URI=https://my.bitwarden.domain/identity # set identity uri
|
||||
GOLDWARDEN_SINGLE_PROCESS=true # run in single process mode, i.e no daemon
|
||||
GOLDWARDEN_DEVICE_UUID= # set device uuid
|
||||
GOLDWARDEN_AUTH_METHOD= # set auth method (password/passwordless)
|
||||
GOLDWARDEN_AUTH_USER= # set auth user
|
||||
GOLDWARDEN_AUTH_PASSWORD= # set auth password
|
||||
GOLDWARDEN_SILENT_LOGGING=true # disable logging
|
||||
GOLDWARDEN_SYSTEM_AUTH_DISABLED=true # disable system auth (biometrics / approval)
|
||||
```
|
||||
|
||||
### Building
|
||||
|
||||
To build, you will need libfido2-dev. And a go toolchain.
|
||||
@ -204,7 +220,6 @@ When accessing a vault entry, the daemon will authenticate against a polkit poli
|
||||
|
||||
### Future Plans
|
||||
Some things that I consider adding (depending on time and personal need):
|
||||
- Paswordless sign in
|
||||
- Regular cli managment (add, delete, update, of logins / secure notes)
|
||||
- Scripts to properly set up the policies/systemd/etc.
|
||||
|
||||
|
@ -69,6 +69,10 @@ func sync(ctx context.Context, vault *vault.Vault, cfg *config.Config) bool {
|
||||
|
||||
func ensureIsNotLocked(action Action) Action {
|
||||
return func(request ipc.IPCMessage, cfg *config.Config, vault *vault.Vault, ctx sockets.CallingContext) (interface{}, error) {
|
||||
if cfg.ConfigFile.RuntimeConfig.DisablePinRequirement {
|
||||
return action(request, cfg, vault, ctx)
|
||||
}
|
||||
|
||||
if cfg.IsLocked() {
|
||||
err := cfg.TryUnlock(vault)
|
||||
ctx1 := context.Background()
|
||||
|
@ -13,7 +13,7 @@ import (
|
||||
)
|
||||
|
||||
func handleLogin(msg ipc.IPCMessage, cfg *config.Config, vault *vault.Vault, callingContext sockets.CallingContext) (response interface{}, err error) {
|
||||
if !cfg.HasPin() {
|
||||
if !cfg.HasPin() && !cfg.ConfigFile.RuntimeConfig.DisablePinRequirement {
|
||||
response, err = ipc.IPCMessageFromPayload(ipc.ActionResponse{
|
||||
Success: false,
|
||||
Message: "No pin set. Set a pin first!",
|
||||
|
@ -4,7 +4,6 @@ import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"github.com/LlamaNite/llamalog"
|
||||
"github.com/quexten/goldwarden/agent/bitwarden"
|
||||
"github.com/quexten/goldwarden/agent/config"
|
||||
"github.com/quexten/goldwarden/agent/sockets"
|
||||
@ -12,9 +11,10 @@ import (
|
||||
"github.com/quexten/goldwarden/agent/systemauth"
|
||||
"github.com/quexten/goldwarden/agent/vault"
|
||||
"github.com/quexten/goldwarden/ipc"
|
||||
"github.com/quexten/goldwarden/logging"
|
||||
)
|
||||
|
||||
var actionsLog = llamalog.NewLogger("Goldwarden", "Actions")
|
||||
var actionsLog = logging.GetLogger("Goldwarden", "Actions")
|
||||
|
||||
func handleAddSSH(msg ipc.IPCMessage, cfg *config.Config, vault *vault.Vault, callingContext sockets.CallingContext) (response interface{}, err error) {
|
||||
req := msg.ParsedPayload().(ipc.CreateSSHKeyRequest)
|
||||
|
@ -14,17 +14,17 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/LlamaNite/llamalog"
|
||||
"github.com/awnumar/memguard"
|
||||
"github.com/quexten/goldwarden/agent/bitwarden/crypto"
|
||||
"github.com/quexten/goldwarden/agent/bitwarden/twofactor"
|
||||
"github.com/quexten/goldwarden/agent/config"
|
||||
"github.com/quexten/goldwarden/agent/systemauth"
|
||||
"github.com/quexten/goldwarden/agent/vault"
|
||||
"github.com/quexten/goldwarden/logging"
|
||||
"golang.org/x/crypto/pbkdf2"
|
||||
)
|
||||
|
||||
var authLog = llamalog.NewLogger("Goldwarden", "Auth")
|
||||
var authLog = logging.GetLogger("Goldwarden", "Auth")
|
||||
|
||||
type preLoginRequest struct {
|
||||
Email string `json:"email"`
|
||||
@ -103,22 +103,10 @@ func LoginWithMasterpassword(ctx context.Context, email string, cfg *config.Conf
|
||||
err = authenticatedHTTPPost(ctx, cfg.ConfigFile.IdentityUrl+"/connect/token", &loginResponseToken, values)
|
||||
errsc, ok := err.(*errStatusCode)
|
||||
if ok && bytes.Contains(errsc.body, []byte("TwoFactor")) {
|
||||
var twoFactor twofactor.TwoFactorResponse
|
||||
if err := json.Unmarshal(errsc.body, &twoFactor); err != nil {
|
||||
loginResponseToken, err = Perform2FA(values, errsc, cfg, ctx)
|
||||
if err != nil {
|
||||
return LoginResponseToken{}, crypto.MasterKey{}, "", err
|
||||
}
|
||||
provider, token, err := twofactor.PerformSecondFactor(&twoFactor, cfg)
|
||||
if err != nil {
|
||||
return LoginResponseToken{}, crypto.MasterKey{}, "", fmt.Errorf("could not obtain two-factor auth token: %v", err)
|
||||
}
|
||||
values.Set("twoFactorProvider", strconv.Itoa(int(provider)))
|
||||
values.Set("twoFactorToken", string(token))
|
||||
values.Set("twoFactorRemember", "1")
|
||||
loginResponseToken = LoginResponseToken{}
|
||||
if err := authenticatedHTTPPost(ctx, cfg.ConfigFile.IdentityUrl+"/connect/token", &loginResponseToken, values); err != nil {
|
||||
return LoginResponseToken{}, crypto.MasterKey{}, "", fmt.Errorf("could not login via two-factor: %v", err)
|
||||
}
|
||||
authLog.Info("2FA login successful")
|
||||
} else if err != nil && strings.Contains(err.Error(), "Captcha required.") {
|
||||
return LoginResponseToken{}, crypto.MasterKey{}, "", fmt.Errorf("captcha required, please login via the web interface")
|
||||
|
||||
@ -177,8 +165,17 @@ func LoginWithDevice(ctx context.Context, email string, cfg *config.Config, vaul
|
||||
|
||||
var loginResponseToken LoginResponseToken
|
||||
err = authenticatedHTTPPost(ctx, cfg.ConfigFile.IdentityUrl+"/connect/token", &loginResponseToken, values)
|
||||
if err != nil {
|
||||
return LoginResponseToken{}, crypto.MasterKey{}, "", err
|
||||
errsc, ok := err.(*errStatusCode)
|
||||
if ok && bytes.Contains(errsc.body, []byte("TwoFactor")) {
|
||||
loginResponseToken, err = Perform2FA(values, errsc, cfg, ctx)
|
||||
if err != nil {
|
||||
return LoginResponseToken{}, crypto.MasterKey{}, "", err
|
||||
}
|
||||
} else if err != nil && strings.Contains(err.Error(), "Captcha required.") {
|
||||
return LoginResponseToken{}, crypto.MasterKey{}, "", fmt.Errorf("captcha required, please login via the web interface")
|
||||
|
||||
} else if err != nil {
|
||||
return LoginResponseToken{}, crypto.MasterKey{}, "", fmt.Errorf("could not login via password: %v", err)
|
||||
}
|
||||
return loginResponseToken, crypto.MasterKeyFromBytes(masterKey), string(masterPasswordHash), nil
|
||||
}
|
||||
@ -218,6 +215,26 @@ func RefreshToken(ctx context.Context, cfg *config.Config) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func Perform2FA(values url.Values, errsc *errStatusCode, cfg *config.Config, ctx context.Context) (LoginResponseToken, error) {
|
||||
var twoFactor twofactor.TwoFactorResponse
|
||||
if err := json.Unmarshal(errsc.body, &twoFactor); err != nil {
|
||||
return LoginResponseToken{}, err
|
||||
}
|
||||
provider, token, err := twofactor.PerformSecondFactor(&twoFactor, cfg)
|
||||
if err != nil {
|
||||
return LoginResponseToken{}, fmt.Errorf("could not obtain two-factor auth token: %v", err)
|
||||
}
|
||||
values.Set("twoFactorProvider", strconv.Itoa(int(provider)))
|
||||
values.Set("twoFactorToken", string(token))
|
||||
values.Set("twoFactorRemember", "1")
|
||||
loginResponseToken := LoginResponseToken{}
|
||||
if err := authenticatedHTTPPost(ctx, cfg.ConfigFile.IdentityUrl+"/connect/token", &loginResponseToken, values); err != nil {
|
||||
return LoginResponseToken{}, fmt.Errorf("could not login via two-factor: %v", err)
|
||||
}
|
||||
authLog.Info("2FA login successful")
|
||||
return loginResponseToken, nil
|
||||
}
|
||||
|
||||
func urlValues(pairs ...string) url.Values {
|
||||
if len(pairs)%2 != 0 {
|
||||
panic("pairs must be of even length")
|
||||
|
@ -6,14 +6,14 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/LlamaNite/llamalog"
|
||||
"github.com/quexten/goldwarden/agent/bitwarden/crypto"
|
||||
"github.com/quexten/goldwarden/agent/bitwarden/models"
|
||||
"github.com/quexten/goldwarden/agent/config"
|
||||
"github.com/quexten/goldwarden/agent/vault"
|
||||
"github.com/quexten/goldwarden/logging"
|
||||
)
|
||||
|
||||
var log = llamalog.NewLogger("Goldwarden", "Bitwarden API")
|
||||
var log = logging.GetLogger("Goldwarden", "Bitwarden API")
|
||||
|
||||
const path = "/.cache/goldwarden-vault.json"
|
||||
|
||||
|
@ -5,12 +5,12 @@ import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/LlamaNite/llamalog"
|
||||
"github.com/quexten/goldwarden/agent/config"
|
||||
"github.com/quexten/goldwarden/agent/systemauth"
|
||||
"github.com/quexten/goldwarden/logging"
|
||||
)
|
||||
|
||||
var twofactorLog = llamalog.NewLogger("Goldwarden", "TwoFactor")
|
||||
var twofactorLog = logging.GetLogger("Goldwarden", "TwoFactor")
|
||||
|
||||
func PerformSecondFactor(resp *TwoFactorResponse, cfg *config.Config) (TwoFactorProvider, []byte, error) {
|
||||
if resp.TwoFactorProviders2[WebAuthn] != nil {
|
||||
|
@ -8,17 +8,17 @@ import (
|
||||
"os/signal"
|
||||
"time"
|
||||
|
||||
"github.com/LlamaNite/llamalog"
|
||||
"github.com/awnumar/memguard"
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/quexten/goldwarden/agent/bitwarden/models"
|
||||
"github.com/quexten/goldwarden/agent/config"
|
||||
"github.com/quexten/goldwarden/agent/systemauth"
|
||||
"github.com/quexten/goldwarden/agent/vault"
|
||||
"github.com/quexten/goldwarden/logging"
|
||||
"github.com/vmihailenco/msgpack/v5"
|
||||
)
|
||||
|
||||
var websocketLog = llamalog.NewLogger("Goldwarden", "Websocket")
|
||||
var websocketLog = logging.GetLogger("Goldwarden", "Websocket")
|
||||
|
||||
type NotificationMessageType int64
|
||||
|
||||
|
@ -21,12 +21,29 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
KDFIterations = 2
|
||||
KDFMemory = 2 * 1024 * 1024
|
||||
KDFThreads = 8
|
||||
ConfigPath = "/.config/goldwarden.json"
|
||||
KDFIterations = 2
|
||||
KDFMemory = 2 * 1024 * 1024
|
||||
KDFThreads = 8
|
||||
DefaultConfigPath = "~/.config/goldwarden.json"
|
||||
)
|
||||
|
||||
type RuntimeConfig struct {
|
||||
DisableAuth bool
|
||||
DisablePinRequirement bool
|
||||
AuthMethod string
|
||||
DoNotPersistConfig bool
|
||||
ConfigDirectory string
|
||||
DisableSSHAgent bool
|
||||
WebsocketDisabled bool
|
||||
ApiURI string
|
||||
IdentityURI string
|
||||
SingleProcess bool
|
||||
DeviceUUID string
|
||||
User string
|
||||
Password string
|
||||
Pin string
|
||||
}
|
||||
|
||||
type ConfigFile struct {
|
||||
IdentityUrl string
|
||||
ApiUrl string
|
||||
@ -36,6 +53,7 @@ type ConfigFile struct {
|
||||
EncryptedUserSymmetricKey string
|
||||
EncryptedMasterPasswordHash string
|
||||
EncryptedMasterKey string
|
||||
RuntimeConfig RuntimeConfig `json:"-"`
|
||||
}
|
||||
|
||||
type LoginToken struct {
|
||||
@ -65,13 +83,14 @@ func DefaultConfig() Config {
|
||||
EncryptedUserSymmetricKey: "",
|
||||
EncryptedMasterPasswordHash: "",
|
||||
EncryptedMasterKey: "",
|
||||
RuntimeConfig: RuntimeConfig{},
|
||||
},
|
||||
sync.Mutex{},
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Config) IsLocked() bool {
|
||||
return c.key == nil
|
||||
return c.key.EqualTo(make([]byte, 32)) && c.HasPin()
|
||||
}
|
||||
|
||||
func (c *Config) IsLoggedIn() bool {
|
||||
@ -105,8 +124,7 @@ func (c *Config) Lock() {
|
||||
if c.IsLocked() {
|
||||
return
|
||||
}
|
||||
c.key.Destroy()
|
||||
c.key = nil
|
||||
c.key.Wipe()
|
||||
}
|
||||
|
||||
func (c *Config) Purge() {
|
||||
@ -315,6 +333,10 @@ func (c *Config) decryptString(data string) (string, error) {
|
||||
}
|
||||
|
||||
func (config *Config) WriteConfig() error {
|
||||
if config.ConfigFile.RuntimeConfig.DoNotPersistConfig {
|
||||
return nil
|
||||
}
|
||||
|
||||
config.mu.Lock()
|
||||
defer config.mu.Unlock()
|
||||
|
||||
@ -323,13 +345,9 @@ func (config *Config) WriteConfig() error {
|
||||
return err
|
||||
}
|
||||
|
||||
home, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
// write to disk
|
||||
os.Remove(home + ConfigPath)
|
||||
file, err := os.OpenFile(home+ConfigPath, os.O_CREATE|os.O_WRONLY, 0600)
|
||||
os.Remove(config.ConfigFile.RuntimeConfig.ConfigDirectory)
|
||||
file, err := os.OpenFile(config.ConfigFile.RuntimeConfig.ConfigDirectory, os.O_CREATE|os.O_WRONLY, 0600)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -342,14 +360,13 @@ func (config *Config) WriteConfig() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func ReadConfig() (Config, error) {
|
||||
home, err := os.UserHomeDir()
|
||||
func ReadConfig(rtCfg RuntimeConfig) (Config, error) {
|
||||
file, err := os.Open(rtCfg.ConfigDirectory)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
file, err := os.Open(home + ConfigPath)
|
||||
if err != nil {
|
||||
return Config{ConfigFile: ConfigFile{}}, err
|
||||
return Config{
|
||||
key: memguard.NewBuffer(32),
|
||||
ConfigFile: ConfigFile{},
|
||||
}, err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
@ -357,12 +374,21 @@ func ReadConfig() (Config, error) {
|
||||
config := ConfigFile{}
|
||||
err = decoder.Decode(&config)
|
||||
if err != nil {
|
||||
return Config{ConfigFile: ConfigFile{}}, err
|
||||
return Config{
|
||||
key: memguard.NewBuffer(32),
|
||||
ConfigFile: ConfigFile{},
|
||||
}, err
|
||||
}
|
||||
if config.ConfigKeyHash == "" {
|
||||
return Config{ConfigFile: config, key: memguard.NewBuffer(32)}, nil
|
||||
return Config{
|
||||
key: memguard.NewBuffer(32),
|
||||
ConfigFile: config,
|
||||
}, nil
|
||||
}
|
||||
return Config{ConfigFile: config}, nil
|
||||
return Config{
|
||||
key: memguard.NewBuffer(32),
|
||||
ConfigFile: config,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (cfg *Config) TryUnlock(vault *vault.Vault) error {
|
||||
|
@ -8,15 +8,15 @@ import (
|
||||
"net"
|
||||
"os"
|
||||
|
||||
"github.com/LlamaNite/llamalog"
|
||||
"github.com/quexten/goldwarden/agent/sockets"
|
||||
"github.com/quexten/goldwarden/agent/systemauth"
|
||||
"github.com/quexten/goldwarden/agent/vault"
|
||||
"github.com/quexten/goldwarden/logging"
|
||||
"golang.org/x/crypto/ssh"
|
||||
"golang.org/x/crypto/ssh/agent"
|
||||
)
|
||||
|
||||
var log = llamalog.NewLogger("Goldwarden", "SSH")
|
||||
var log = logging.GetLogger("Goldwarden", "SSH")
|
||||
|
||||
type vaultAgent struct {
|
||||
vault *vault.Vault
|
||||
|
@ -2,13 +2,10 @@ package systemauth
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
|
||||
"github.com/twpayne/go-pinentry"
|
||||
)
|
||||
|
||||
var authDisabled = false
|
||||
|
||||
func GetPassword(title string, description string) (string, error) {
|
||||
client, err := pinentry.NewClient(
|
||||
pinentry.WithBinaryNameFromGnuPGAgentConf(),
|
||||
@ -40,8 +37,7 @@ func GetPassword(title string, description string) (string, error) {
|
||||
}
|
||||
|
||||
func GetApproval(title string, description string) (bool, error) {
|
||||
if authDisabled {
|
||||
log.Info("Skipping approval because system auth is disabled")
|
||||
if systemAuthDisabled {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
@ -70,10 +66,3 @@ func GetApproval(title string, description string) (bool, error) {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
envAuthDisabled := os.Getenv("GOLDWARDEN_SYSTEM_AUTH_DISABLED")
|
||||
if envAuthDisabled == "true" {
|
||||
authDisabled = true
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,11 @@
|
||||
package systemauth
|
||||
|
||||
import (
|
||||
"github.com/LlamaNite/llamalog"
|
||||
"github.com/amenzhinsky/go-polkit"
|
||||
"github.com/quexten/goldwarden/logging"
|
||||
)
|
||||
|
||||
var log = llamalog.NewLogger("Goldwarden", "Systemauth")
|
||||
var log = logging.GetLogger("Goldwarden", "Systemauth")
|
||||
|
||||
type Approval string
|
||||
|
||||
@ -75,11 +75,12 @@ func (a Approval) String() string {
|
||||
}
|
||||
|
||||
func CheckBiometrics(approvalType Approval) bool {
|
||||
log.Info("Checking biometrics for %s", approvalType.String())
|
||||
if authDisabled {
|
||||
if systemAuthDisabled {
|
||||
return true
|
||||
}
|
||||
|
||||
log.Info("Checking biometrics for %s", approvalType.String())
|
||||
|
||||
authority, err := polkit.NewAuthority()
|
||||
if err != nil {
|
||||
return false
|
||||
|
11
agent/systemauth/systemauth.go
Normal file
11
agent/systemauth/systemauth.go
Normal file
@ -0,0 +1,11 @@
|
||||
package systemauth
|
||||
|
||||
import "os"
|
||||
|
||||
var systemAuthDisabled = false
|
||||
|
||||
func init() {
|
||||
if os.Getenv("GOLDWARDEN_SYSTEM_AUTH_DISABLED") == "true" {
|
||||
systemAuthDisabled = true
|
||||
}
|
||||
}
|
@ -8,7 +8,6 @@ import (
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/LlamaNite/llamalog"
|
||||
"github.com/quexten/goldwarden/agent/actions"
|
||||
"github.com/quexten/goldwarden/agent/bitwarden"
|
||||
"github.com/quexten/goldwarden/agent/bitwarden/crypto"
|
||||
@ -17,6 +16,7 @@ import (
|
||||
"github.com/quexten/goldwarden/agent/ssh"
|
||||
"github.com/quexten/goldwarden/agent/vault"
|
||||
"github.com/quexten/goldwarden/ipc"
|
||||
"github.com/quexten/goldwarden/logging"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
@ -25,7 +25,7 @@ const (
|
||||
TokenRefreshInterval = 30 * time.Minute
|
||||
)
|
||||
|
||||
var log = llamalog.NewLogger("Goldwarden", "Agent")
|
||||
var log = logging.GetLogger("Goldwarden", "Agent")
|
||||
|
||||
func writeError(c net.Conn, errMsg error) error {
|
||||
payload := ipc.ActionResponse{
|
||||
@ -102,17 +102,28 @@ type AgentState struct {
|
||||
config *config.ConfigFile
|
||||
}
|
||||
|
||||
func StartUnixAgent(path string, websocket bool, sshAgent bool) error {
|
||||
func StartUnixAgent(path string, runtimeConfig config.RuntimeConfig) error {
|
||||
ctx := context.Background()
|
||||
|
||||
// check if exists
|
||||
keyring := crypto.NewKeyring(nil)
|
||||
var vault = vault.NewVault(&keyring)
|
||||
cfg, err := config.ReadConfig()
|
||||
cfg, err := config.ReadConfig(runtimeConfig)
|
||||
if err != nil {
|
||||
var cfg = config.DefaultConfig()
|
||||
cfg.WriteConfig()
|
||||
}
|
||||
cfg.ConfigFile.RuntimeConfig = runtimeConfig
|
||||
if cfg.ConfigFile.RuntimeConfig.ApiURI != "" {
|
||||
cfg.ConfigFile.ApiUrl = cfg.ConfigFile.RuntimeConfig.ApiURI
|
||||
}
|
||||
if cfg.ConfigFile.RuntimeConfig.IdentityURI != "" {
|
||||
cfg.ConfigFile.IdentityUrl = cfg.ConfigFile.RuntimeConfig.IdentityURI
|
||||
}
|
||||
if cfg.ConfigFile.RuntimeConfig.DeviceUUID != "" {
|
||||
cfg.ConfigFile.DeviceUUID = cfg.ConfigFile.RuntimeConfig.DeviceUUID
|
||||
}
|
||||
|
||||
if !cfg.IsLocked() {
|
||||
log.Warn("Config is not locked. SET A PIN!!")
|
||||
token, err := cfg.GetToken()
|
||||
@ -134,11 +145,11 @@ func StartUnixAgent(path string, websocket bool, sshAgent bool) error {
|
||||
}
|
||||
|
||||
disableDumpable()
|
||||
if websocket {
|
||||
if !runtimeConfig.WebsocketDisabled {
|
||||
go bitwarden.RunWebsocketDaemon(ctx, vault, &cfg)
|
||||
}
|
||||
|
||||
if sshAgent {
|
||||
if !runtimeConfig.DisableSSHAgent {
|
||||
vaultAgent := ssh.NewVaultAgent(vault)
|
||||
vaultAgent.SetUnlockRequestAction(func() bool {
|
||||
err := cfg.TryUnlock(vault)
|
||||
|
@ -70,18 +70,29 @@ func serveVirtualAgent(recv chan []byte, send chan []byte, ctx context.Context,
|
||||
}
|
||||
}
|
||||
|
||||
func StartVirtualAgent() (chan []byte, chan []byte) {
|
||||
func StartVirtualAgent(runtimeConfig config.RuntimeConfig) (chan []byte, chan []byte) {
|
||||
ctx := context.Background()
|
||||
|
||||
// check if exists
|
||||
keyring := crypto.NewKeyring(nil)
|
||||
var vault = vault.NewVault(&keyring)
|
||||
cfg, err := config.ReadConfig()
|
||||
cfg, err := config.ReadConfig(runtimeConfig)
|
||||
if err != nil {
|
||||
var cfg = config.DefaultConfig()
|
||||
cfg.WriteConfig()
|
||||
}
|
||||
if !cfg.IsLocked() {
|
||||
cfg.ConfigFile.RuntimeConfig = runtimeConfig
|
||||
if cfg.ConfigFile.RuntimeConfig.ApiURI != "" {
|
||||
cfg.ConfigFile.ApiUrl = cfg.ConfigFile.RuntimeConfig.ApiURI
|
||||
}
|
||||
if cfg.ConfigFile.RuntimeConfig.IdentityURI != "" {
|
||||
cfg.ConfigFile.IdentityUrl = cfg.ConfigFile.RuntimeConfig.IdentityURI
|
||||
}
|
||||
if cfg.ConfigFile.RuntimeConfig.DeviceUUID != "" {
|
||||
cfg.ConfigFile.DeviceUUID = cfg.ConfigFile.RuntimeConfig.DeviceUUID
|
||||
}
|
||||
|
||||
if !cfg.IsLocked() && !cfg.ConfigFile.RuntimeConfig.DoNotPersistConfig {
|
||||
log.Warn("Config is not locked. SET A PIN!!")
|
||||
token, err := cfg.GetToken()
|
||||
if err == nil {
|
||||
|
@ -11,7 +11,7 @@ import (
|
||||
"github.com/quexten/goldwarden/ipc"
|
||||
)
|
||||
|
||||
func GetLoginByUUID(uuid string) (ipc.DecryptedLoginCipher, error) {
|
||||
func GetLoginByUUID(uuid string, client client.Client) (ipc.DecryptedLoginCipher, error) {
|
||||
resp, err := client.SendToAgent(ipc.GetLoginRequest{
|
||||
UUID: uuid,
|
||||
})
|
||||
@ -31,7 +31,7 @@ func GetLoginByUUID(uuid string) (ipc.DecryptedLoginCipher, error) {
|
||||
}
|
||||
}
|
||||
|
||||
func ListLogins() ([]ipc.DecryptedLoginCipher, error) {
|
||||
func ListLogins(client client.Client) ([]ipc.DecryptedLoginCipher, error) {
|
||||
resp, err := client.SendToAgent(ipc.ListLoginsRequest{})
|
||||
if err != nil {
|
||||
return []ipc.DecryptedLoginCipher{}, err
|
||||
@ -49,8 +49,8 @@ func ListLogins() ([]ipc.DecryptedLoginCipher, error) {
|
||||
}
|
||||
}
|
||||
|
||||
func Run(layout string, useCopyPaste bool) {
|
||||
logins, err := ListLogins()
|
||||
func Run(layout string, useCopyPaste bool, client client.Client) {
|
||||
logins, err := ListLogins(client)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@ -65,7 +65,7 @@ func Run(layout string, useCopyPaste bool) {
|
||||
}
|
||||
|
||||
RunAutofill(autofillEntries, func(uuid string, c chan bool) {
|
||||
login, err := GetLoginByUUID(uuid)
|
||||
login, err := GetLoginByUUID(uuid, client)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
//go:build autofill
|
||||
//go:build !noautofill
|
||||
|
||||
package cmd
|
||||
|
||||
@ -14,7 +14,7 @@ var autofillCmd = &cobra.Command{
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
layout := cmd.Flag("layout").Value.String()
|
||||
useCopyPaste, _ := cmd.Flags().GetBool("use-copy-paste")
|
||||
autofill.Run(layout, useCopyPaste)
|
||||
autofill.Run(layout, useCopyPaste, commandClient)
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -15,12 +15,13 @@ var daemonizeCmd = &cobra.Command{
|
||||
Long: `Starts the agent as a daemon. The agent will run in the background and will
|
||||
run in the background until it is stopped.`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
websocketDisabled := os.Getenv("GOLDWARDEN_WEBSOCKET_DISABLED") == "true"
|
||||
websocketDisabled := runtimeConfig.WebsocketDisabled
|
||||
sshDisabled := runtimeConfig.DisableSSHAgent
|
||||
|
||||
if websocketDisabled {
|
||||
println("Websocket disabled")
|
||||
}
|
||||
|
||||
sshDisabled := os.Getenv("GOLDWARDEN_SSH_DISABLED") == "true"
|
||||
if sshDisabled {
|
||||
println("SSH agent disabled")
|
||||
}
|
||||
@ -35,7 +36,7 @@ var daemonizeCmd = &cobra.Command{
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
err = agent.StartUnixAgent(home+"/.goldwarden.sock", websocketDisabled, sshDisabled)
|
||||
err = agent.StartUnixAgent(home+"/.goldwarden.sock", runtimeConfig)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
62
cmd/logins.go
Normal file
62
cmd/logins.go
Normal file
@ -0,0 +1,62 @@
|
||||
/*
|
||||
Copyright © 2023 NAME HERE <EMAIL ADDRESS>
|
||||
*/
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/quexten/goldwarden/ipc"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var baseLoginCmd = &cobra.Command{
|
||||
Use: "logins",
|
||||
Short: "Commands for managing logins.",
|
||||
Long: `Commands for managing logins.`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
cmd.Help()
|
||||
},
|
||||
}
|
||||
|
||||
var getLoginCmd = &cobra.Command{
|
||||
Use: "get",
|
||||
Short: "Gets a login in your vault",
|
||||
Long: `Gets a login in your vault.`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
loginIfRequired()
|
||||
|
||||
uuid, _ := cmd.Flags().GetString("uuid")
|
||||
name, _ := cmd.Flags().GetString("name")
|
||||
username, _ := cmd.Flags().GetString("username")
|
||||
|
||||
resp, err := commandClient.SendToAgent(ipc.GetLoginRequest{
|
||||
Name: name,
|
||||
Username: username,
|
||||
UUID: uuid,
|
||||
})
|
||||
if err != nil {
|
||||
println("Error: " + err.Error())
|
||||
println("Is the daemon running?")
|
||||
return
|
||||
}
|
||||
|
||||
switch resp.(type) {
|
||||
case ipc.GetLoginResponse:
|
||||
response := resp.(ipc.GetLoginResponse)
|
||||
fmt.Println(response.Result.Password)
|
||||
break
|
||||
case ipc.ActionResponse:
|
||||
println("Error: " + resp.(ipc.ActionResponse).Message)
|
||||
return
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(baseLoginCmd)
|
||||
baseLoginCmd.AddCommand(getLoginCmd)
|
||||
getLoginCmd.PersistentFlags().String("name", "", "")
|
||||
getLoginCmd.PersistentFlags().String("username", "", "")
|
||||
getLoginCmd.PersistentFlags().String("uuid", "", "")
|
||||
}
|
41
cmd/root.go
41
cmd/root.go
@ -4,11 +4,14 @@ import (
|
||||
"os"
|
||||
|
||||
"github.com/quexten/goldwarden/agent"
|
||||
"github.com/quexten/goldwarden/agent/config"
|
||||
"github.com/quexten/goldwarden/client"
|
||||
"github.com/quexten/goldwarden/ipc"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var commandClient client.Client
|
||||
var runtimeConfig config.RuntimeConfig
|
||||
|
||||
var rootCmd = &cobra.Command{
|
||||
Use: "goldwarden",
|
||||
@ -18,7 +21,17 @@ var rootCmd = &cobra.Command{
|
||||
biometric unlock, and more.`,
|
||||
}
|
||||
|
||||
func Execute() {
|
||||
func Execute(cfg config.RuntimeConfig) {
|
||||
runtimeConfig = cfg
|
||||
|
||||
goldwardenSingleProcess := os.Getenv("GOLDWARDEN_SINGLE_PROCESS")
|
||||
if goldwardenSingleProcess == "true" {
|
||||
recv, send := agent.StartVirtualAgent(runtimeConfig)
|
||||
commandClient = client.NewVirtualClient(send, recv)
|
||||
} else {
|
||||
commandClient = client.NewUnixSocketClient()
|
||||
}
|
||||
|
||||
err := rootCmd.Execute()
|
||||
if err != nil {
|
||||
os.Exit(1)
|
||||
@ -26,13 +39,23 @@ func Execute() {
|
||||
}
|
||||
|
||||
func init() {
|
||||
goldwardenSingleProcess := os.Getenv("GOLDWARDEN_SINGLE_PROCESS")
|
||||
if goldwardenSingleProcess == "true" {
|
||||
recv, send := agent.StartVirtualAgent()
|
||||
commandClient = client.NewVirtualClient(send, recv)
|
||||
} else {
|
||||
commandClient = client.NewUnixSocketClient()
|
||||
}
|
||||
|
||||
rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
|
||||
}
|
||||
|
||||
func loginIfRequired() error {
|
||||
var err error
|
||||
|
||||
if runtimeConfig.AuthMethod == "password" {
|
||||
_, err = commandClient.SendToAgent(ipc.DoLoginRequest{
|
||||
Email: runtimeConfig.User,
|
||||
Password: runtimeConfig.Password,
|
||||
})
|
||||
} else if runtimeConfig.AuthMethod == "passwordless" {
|
||||
_, err = commandClient.SendToAgent(ipc.DoLoginRequest{
|
||||
Email: runtimeConfig.User,
|
||||
Passwordless: true,
|
||||
})
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
@ -27,6 +27,8 @@ var sshAddCmd = &cobra.Command{
|
||||
Long: `Runs a command with environment variables from your vault.
|
||||
The variables are stored as a secure note. Consult the documentation for more information.`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
loginIfRequired()
|
||||
|
||||
name, _ := cmd.Flags().GetString("name")
|
||||
copyToClipboard, _ := cmd.Flags().GetBool("clipboard")
|
||||
|
||||
@ -60,6 +62,8 @@ var listSSHCmd = &cobra.Command{
|
||||
Short: "Lists all SSH keys in your vault",
|
||||
Long: `Lists all SSH keys in your vault.`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
loginIfRequired()
|
||||
|
||||
result, err := commandClient.SendToAgent(ipc.GetSSHKeysRequest{})
|
||||
if err != nil {
|
||||
println("Error: " + err.Error())
|
||||
|
22
logging/llamalogger.go
Normal file
22
logging/llamalogger.go
Normal file
@ -0,0 +1,22 @@
|
||||
package logging
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/LlamaNite/llamalog"
|
||||
)
|
||||
|
||||
type SilentWriter struct {
|
||||
}
|
||||
|
||||
func (w SilentWriter) Write(p []byte) (n int, err error) {
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
func GetLogger(packageDir ...string) *llamalog.Logger {
|
||||
if os.Getenv("GOLDWARDEN_SILENT_LOGGING") == "true" {
|
||||
return llamalog.NewLoggerFromWriter(SilentWriter{}, packageDir...)
|
||||
} else {
|
||||
return llamalog.NewLogger(packageDir...)
|
||||
}
|
||||
}
|
43
main.go
43
main.go
@ -6,6 +6,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/quexten/goldwarden/agent/config"
|
||||
"github.com/quexten/goldwarden/browserbiometrics"
|
||||
"github.com/quexten/goldwarden/cmd"
|
||||
)
|
||||
@ -16,10 +17,48 @@ func main() {
|
||||
return
|
||||
}
|
||||
|
||||
if !cmd.IsPolkitSetup() {
|
||||
var configPath string
|
||||
if path, found := os.LookupEnv("GOLDWARDEN_CONFIG_DIRECTORY"); found {
|
||||
configPath = path
|
||||
} else {
|
||||
configPath = config.DefaultConfigPath
|
||||
}
|
||||
userHome, _ := os.UserHomeDir()
|
||||
configPath = strings.ReplaceAll(configPath, "~", userHome)
|
||||
|
||||
runtimeConfig := config.RuntimeConfig{
|
||||
DisableAuth: os.Getenv("GOLDWARDEN_WEBSOCKET_DISABLED") == "true",
|
||||
DisablePinRequirement: os.Getenv("GOLDWARDEN_PIN_REQUIREMENT_DISABLED") == "true",
|
||||
DoNotPersistConfig: os.Getenv("GOLDWARDEN_DO_NOT_PERSIST_CONFIG") == "true",
|
||||
ApiURI: os.Getenv("GOLDWARDEN_API_URI"),
|
||||
IdentityURI: os.Getenv("GOLDWARDEN_IDENTITY_URI"),
|
||||
SingleProcess: os.Getenv("GOLDWARDEN_SINGLE_PROCESS") == "true",
|
||||
DeviceUUID: os.Getenv("GOLDWARDEN_DEVICE_UUID"),
|
||||
AuthMethod: os.Getenv("GOLDWARDEN_AUTH_METHOD"),
|
||||
User: os.Getenv("GOLDWARDEN_AUTH_USER"),
|
||||
Password: os.Getenv("GOLDWARDEN_AUTH_PASSWORD"),
|
||||
Pin: os.Getenv("GOLDWARDEN_PIN"),
|
||||
|
||||
ConfigDirectory: configPath,
|
||||
}
|
||||
|
||||
if runtimeConfig.SingleProcess {
|
||||
runtimeConfig.DisablePinRequirement = true
|
||||
runtimeConfig.DisableAuth = true
|
||||
}
|
||||
|
||||
if runtimeConfig.DisablePinRequirement {
|
||||
runtimeConfig.DoNotPersistConfig = true
|
||||
}
|
||||
|
||||
if runtimeConfig.DisableAuth {
|
||||
os.Setenv("GOLDWARDEN_SYSTEM_AUTH_DISABLED", "true")
|
||||
}
|
||||
|
||||
if !cmd.IsPolkitSetup() && !runtimeConfig.DisableAuth {
|
||||
fmt.Println("Polkit is not setup. Run 'goldwarden setup polkit' to set it up.")
|
||||
time.Sleep(3 * time.Second)
|
||||
}
|
||||
|
||||
cmd.Execute()
|
||||
cmd.Execute(runtimeConfig)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user