smcli self-healing sessions (refresh grant + auto-relogin) #4
+31
-1
@@ -21,6 +21,8 @@ type Client struct {
|
||||
HTTP *http.Client
|
||||
Email string
|
||||
Password string
|
||||
TOTPSecret string
|
||||
reloginDone bool
|
||||
|
||||
AccessToken string
|
||||
RefreshToken string
|
||||
@@ -180,7 +182,7 @@ func (c *Client) Login() error {
|
||||
if t.AccessToken == "" {
|
||||
// 2FA retry path (provider 0 = authenticator TOTP)
|
||||
if strings.Contains(string(out), "Two factor required") {
|
||||
secret := os.Getenv("SM_TOTP_SECRET")
|
||||
secret := c.TOTPSecret
|
||||
if secret != "" {
|
||||
code, terr := totpNow(secret, time.Now())
|
||||
if terr != nil {
|
||||
@@ -327,3 +329,31 @@ func (c *Client) DeleteCipher(id string) error {
|
||||
_, _ = c.api("PUT", "/api/ciphers/"+id+"/purge", map[string]any{}, true)
|
||||
return nil
|
||||
}
|
||||
|
||||
// selfRelogin performs the full login+unlock using SM_* env credentials
|
||||
// (injected by the sm shims from the TSGCOO vault-account env). Saves state.
|
||||
func (c *Client) selfRelogin() error {
|
||||
if c.Password == "" {
|
||||
c.Password = os.Getenv("SM_PASSWORD")
|
||||
}
|
||||
if c.TOTPSecret == "" {
|
||||
c.TOTPSecret = os.Getenv("SM_TOTP_SECRET")
|
||||
}
|
||||
if c.Password == "" {
|
||||
return errors.New("relogin unavailable: SM_PASSWORD not set")
|
||||
}
|
||||
if err := c.Login(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := c.Unlock(); err != nil {
|
||||
return err
|
||||
}
|
||||
if s, err := loadState(); err == nil {
|
||||
s.AccessToken = c.AccessToken
|
||||
s.UserSymKey = toHex(c.UserSymKey)
|
||||
s.StretchedKey = toHex(c.StretchedKey)
|
||||
s.MasterKey = toHex(c.MasterKey)
|
||||
_ = saveState(s)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -96,6 +96,7 @@ func loadState() (*State, error) {
|
||||
func newClientFromState(s *State) (*Client, error) {
|
||||
c := &Client{
|
||||
Server: s.Server, Email: s.Email,
|
||||
Password: os.Getenv("SM_PASSWORD"), TOTPSecret: os.Getenv("SM_TOTP_SECRET"),
|
||||
AccessToken: s.AccessToken, RefreshToken: s.RefreshToken,
|
||||
KDFType: s.KDFType, KDFIter: s.KDFIter, KDFMemory: s.KDFMemory, KDFParallel: s.KDFParallel,
|
||||
HTTP: &http.Client{Timeout: 30 * time.Second},
|
||||
@@ -114,14 +115,15 @@ func newClientFromState(s *State) (*Client, error) {
|
||||
}
|
||||
|
||||
func cmdLogin(server, email, password string) error {
|
||||
c := &Client{Server: server, Email: email, Password: password,
|
||||
c := &Client{Server: server, Email: email, Password: password, TOTPSecret: os.Getenv("SM_TOTP_SECRET"),
|
||||
HTTP: &http.Client{Timeout: 30 * time.Second}}
|
||||
if err := c.Login(); err != nil {
|
||||
return err
|
||||
}
|
||||
s := &State{
|
||||
Version: stateVersion, Server: server, Email: email,
|
||||
AccessToken: c.AccessToken, RefreshToken: c.RefreshToken,
|
||||
AccessToken: c.AccessToken,
|
||||
RefreshToken: c.RefreshToken,
|
||||
KDFType: c.KDFType, KDFIter: c.KDFIter, KDFMemory: c.KDFMemory, KDFParallel: c.KDFParallel,
|
||||
MasterKey: toHex(c.MasterKey),
|
||||
StretchedKey: toHex(c.StretchedKey),
|
||||
|
||||
+1
-1
@@ -10,7 +10,7 @@
|
||||
name: knel-secretsmanager
|
||||
services:
|
||||
smcli:
|
||||
image: git.knownelement.com/knel/knel-secretsmanager-cli@sha256:46d80c0a0ef53a9303dd54b3799a64a61cd4e4dc892282bc4d8a220378117ce9
|
||||
image: git.knownelement.com/knel/knel-secretsmanager-cli@sha256:8abfc55dfa7ca9e70b286a249b7ca823531bd55da29bed70d3354a58ec4d8fec
|
||||
container_name: ukrrs-secretsmgr-cli
|
||||
restart: unless-stopped
|
||||
entrypoint: ["sleep", "infinity"]
|
||||
|
||||
Reference in New Issue
Block a user