Podman kube pod
- Added YAML for a Podman kube pod - Support configuring with environment variables
This commit is contained in:
parent
8a6a9e9380
commit
375d7fc0b7
41
README.md
41
README.md
|
@ -24,13 +24,11 @@ Binary:
|
|||
```
|
||||
Container:
|
||||
```shell
|
||||
podman run -it --rm -v hydroxide-config:/data ghcr.io/0ranki/hydroxide-push auth your.proton@email.address
|
||||
podman run -it --rm -v hydroxide-push:/data ghcr.io/0ranki/hydroxide-push auth your.proton@email.address
|
||||
```
|
||||
You will be prompted for the Proton account credentials and the details for the push server. Proton credentials are stored encrypted form.
|
||||
|
||||
The auth flow generates a separate password for the bridge, which is stored in plaintext
|
||||
to `$HOME/.config/notify.json`. Unlike upstream `hydroxide`, there is no service listening on any port,
|
||||
all communications is internal to the program.
|
||||
The auth flow generates a separate password for the bridge to fake a login to the bridge, which is stored in plaintext to `$HOME/.config/notify.json`. Unlike upstream `hydroxide`, there is no service listening on any port, the password isn't useful for anything else.
|
||||
|
||||
### Reconfigure push server
|
||||
Binary:
|
||||
|
@ -39,12 +37,13 @@ hydroxide-push setup-ntfy
|
|||
```
|
||||
Container:
|
||||
```shell
|
||||
podman run -it --rm -v hydroxide-config:/data ghcr.io/0ranki/hydroxide-push setup-ntfy
|
||||
podman run -it --rm -v hydroxide-push:/data ghcr.io/0ranki/hydroxide-push setup-ntfy
|
||||
```
|
||||
You'll be asked for the base URL of the push server, and the topic. These will probably
|
||||
be combined to a single string in future versions.
|
||||
You'll be asked for the base URL of the push server, and the topic. The push endpoint configuration can be changed while the daemon is running.
|
||||
|
||||
**NOTE:** Authentication for the push endpoint is not yet supported.
|
||||
The currently configured values are shown inside braces. Leave input blank to use the current values.
|
||||
|
||||
>**NOTE:** Authentication for the push endpoint is not yet supported.
|
||||
|
||||
### Start the service
|
||||
|
||||
|
@ -54,8 +53,32 @@ hydroxide-push notify
|
|||
```
|
||||
Container:
|
||||
```shell
|
||||
podman run -it --rm -v hydroxide-config:/data ghcr.io/0ranki/hydroxide-push
|
||||
podman run -it --rm -v hydroxide-push:/data ghcr.io/0ranki/hydroxide-push
|
||||
```
|
||||
|
||||
## Podman pod
|
||||
|
||||
A Podman kube YAML file is provided in the repo.
|
||||
|
||||
> **Note:** If you're using 2FA or just don't want to put your password to a file, use the manual method above. Make sure the volume name (claimName) in the YAML mathces what you use in the commands.
|
||||
|
||||
- Download/copy `hydroxide-push-podman.yaml` to the machine you intend to run the daemon on
|
||||
- Edit the config values at the top of the file
|
||||
- Start the pod:
|
||||
```shell
|
||||
podman kube play ./hydroxide-push-podman.yaml
|
||||
```
|
||||
- Latest container image is pulled
|
||||
- A named volume (`hydroxide-push`) will be created for the configuration
|
||||
- Login to Proton and push URL configuration is handled automatically, after which the daemon starts
|
||||
- After the initial setup, the ConfigMap (before `---`) can be removed from the YAML.). Optionally to clear the environment variables, run
|
||||
|
||||
```shell
|
||||
podman kube play ./hydroxide-push-podman.yaml --replace
|
||||
```
|
||||
The command can also be used to pull the latest version and restart the pod.
|
||||
- To reauthenticate or clear data, simply remove the named volume or run the `auth` command
|
||||
|
||||
|
||||
## License
|
||||
MIT
|
||||
|
|
|
@ -74,11 +74,117 @@ func listenEventsAndNotify(addr string, debug bool, authManager *auth.Manager, e
|
|||
}
|
||||
}
|
||||
|
||||
func authenticate(authCmd *flag.FlagSet) {
|
||||
var username string
|
||||
if os.Getenv("PROTON_ACCT") != "" {
|
||||
username = os.Getenv("PROTON_ACCT")
|
||||
} else {
|
||||
username = authCmd.Arg(0)
|
||||
}
|
||||
if username == "" {
|
||||
log.Fatal("usage: hydroxide auth <username>")
|
||||
}
|
||||
|
||||
c := newClient()
|
||||
|
||||
var a *protonmail.Auth
|
||||
/*if cachedAuth, ok := auths[username]; ok {
|
||||
var err error
|
||||
a, err = c.AuthRefresh(a)
|
||||
if err != nil {
|
||||
// TODO: handle expired token error
|
||||
log.Fatal(err)
|
||||
}
|
||||
}*/
|
||||
|
||||
var loginPassword string
|
||||
if a == nil {
|
||||
if os.Getenv("PROTON_ACCT_PASSWORD") != "" {
|
||||
loginPassword = os.Getenv("PROTON_ACCT_PASSWORD")
|
||||
} else if pass, err := askPass("Password"); err != nil {
|
||||
log.Fatal(err)
|
||||
} else {
|
||||
loginPassword = string(pass)
|
||||
}
|
||||
|
||||
authInfo, err := c.AuthInfo(username)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
a, err = c.Auth(username, loginPassword, authInfo)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if a.TwoFactor.Enabled != 0 {
|
||||
if a.TwoFactor.TOTP != 1 {
|
||||
log.Fatal("Only TOTP is supported as a 2FA method")
|
||||
}
|
||||
|
||||
scanner := bufio.NewScanner(os.Stdin)
|
||||
fmt.Printf("2FA TOTP code: ")
|
||||
scanner.Scan()
|
||||
code := scanner.Text()
|
||||
|
||||
scope, err := c.AuthTOTP(code)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
a.Scope = scope
|
||||
}
|
||||
}
|
||||
|
||||
var mailboxPassword string
|
||||
if a.PasswordMode == protonmail.PasswordSingle {
|
||||
mailboxPassword = loginPassword
|
||||
}
|
||||
if mailboxPassword == "" {
|
||||
prompt := "Password"
|
||||
if a.PasswordMode == protonmail.PasswordTwo {
|
||||
prompt = "Mailbox password"
|
||||
}
|
||||
if pass, err := askPass(prompt); err != nil {
|
||||
log.Fatal(err)
|
||||
} else {
|
||||
mailboxPassword = string(pass)
|
||||
}
|
||||
}
|
||||
|
||||
keySalts, err := c.ListKeySalts()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
_, err = c.Unlock(a, keySalts, mailboxPassword)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
secretKey, bridgePassword, err := auth.GeneratePassword()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
err = auth.EncryptAndSave(&auth.CachedAuth{
|
||||
Auth: *a,
|
||||
LoginPassword: loginPassword,
|
||||
MailboxPassword: mailboxPassword,
|
||||
KeySalts: keySalts,
|
||||
}, username, secretKey)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
cfg.BridgePw = bridgePassword
|
||||
cfg.Setup()
|
||||
}
|
||||
|
||||
const usage = `usage: hydroxide-push [options...] <command>
|
||||
Commands:
|
||||
auth <username> Login to ProtonMail via hydroxide
|
||||
status View hydroxide status
|
||||
notify Start the notification daemon
|
||||
setup-ntfy (Re)configure the push endpoint
|
||||
|
||||
Global options:
|
||||
-debug
|
||||
|
@ -123,101 +229,8 @@ func main() {
|
|||
switch cmd {
|
||||
case "auth":
|
||||
authCmd.Parse(flag.Args()[1:])
|
||||
username := authCmd.Arg(0)
|
||||
if username == "" {
|
||||
log.Fatal("usage: hydroxide auth <username>")
|
||||
}
|
||||
authenticate(authCmd)
|
||||
|
||||
c := newClient()
|
||||
|
||||
var a *protonmail.Auth
|
||||
/*if cachedAuth, ok := auths[username]; ok {
|
||||
var err error
|
||||
a, err = c.AuthRefresh(a)
|
||||
if err != nil {
|
||||
// TODO: handle expired token error
|
||||
log.Fatal(err)
|
||||
}
|
||||
}*/
|
||||
|
||||
var loginPassword string
|
||||
if a == nil {
|
||||
if pass, err := askPass("Password"); err != nil {
|
||||
log.Fatal(err)
|
||||
} else {
|
||||
loginPassword = string(pass)
|
||||
}
|
||||
|
||||
authInfo, err := c.AuthInfo(username)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
a, err = c.Auth(username, loginPassword, authInfo)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if a.TwoFactor.Enabled != 0 {
|
||||
if a.TwoFactor.TOTP != 1 {
|
||||
log.Fatal("Only TOTP is supported as a 2FA method")
|
||||
}
|
||||
|
||||
scanner := bufio.NewScanner(os.Stdin)
|
||||
fmt.Printf("2FA TOTP code: ")
|
||||
scanner.Scan()
|
||||
code := scanner.Text()
|
||||
|
||||
scope, err := c.AuthTOTP(code)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
a.Scope = scope
|
||||
}
|
||||
}
|
||||
|
||||
var mailboxPassword string
|
||||
if a.PasswordMode == protonmail.PasswordSingle {
|
||||
mailboxPassword = loginPassword
|
||||
}
|
||||
if mailboxPassword == "" {
|
||||
prompt := "Password"
|
||||
if a.PasswordMode == protonmail.PasswordTwo {
|
||||
prompt = "Mailbox password"
|
||||
}
|
||||
if pass, err := askPass(prompt); err != nil {
|
||||
log.Fatal(err)
|
||||
} else {
|
||||
mailboxPassword = string(pass)
|
||||
}
|
||||
}
|
||||
|
||||
keySalts, err := c.ListKeySalts()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
_, err = c.Unlock(a, keySalts, mailboxPassword)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
secretKey, bridgePassword, err := auth.GeneratePassword()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
err = auth.EncryptAndSave(&auth.CachedAuth{
|
||||
Auth: *a,
|
||||
LoginPassword: loginPassword,
|
||||
MailboxPassword: mailboxPassword,
|
||||
KeySalts: keySalts,
|
||||
}, username, secretKey)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
cfg.BridgePw = bridgePassword
|
||||
cfg.Setup()
|
||||
case "status":
|
||||
usernames, err := auth.ListUsernames()
|
||||
if err != nil {
|
||||
|
@ -235,7 +248,15 @@ func main() {
|
|||
|
||||
case "setup-ntfy":
|
||||
cfg.Setup()
|
||||
|
||||
case "notify":
|
||||
if os.Getenv("PROTON_ACCT_PASSWORD") != "" && os.Getenv("PROTON_ACCT") != "" && os.Getenv("PUSH_URL") != "" && os.Getenv("PUSH_TOPIC") != "" && cfg.BridgePw == "" {
|
||||
log.Println("Logging in to Proton account using values from environment")
|
||||
cfg.URL = os.Getenv("PUSH_URL")
|
||||
cfg.Topic = os.Getenv("PUSH_TOPIC")
|
||||
cfg.Save()
|
||||
authenticate(new(flag.FlagSet))
|
||||
}
|
||||
authManager := auth.NewManager(newClient)
|
||||
eventsManager := events.NewManager()
|
||||
listenEventsAndNotify("0", debug, authManager, eventsManager, tlsConfig)
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
## Remove this ConfigMap section after the initial run
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: hydroxide-push-config
|
||||
data:
|
||||
PROTON_ACCT: "my.account@protonmail.com"
|
||||
PROTON_ACCT_PASSWORD: "myprotonaccountpassword"
|
||||
PUSH_URL: "http://ntfy.sh"
|
||||
PUSH_TOPIC: ""
|
||||
## Remove the above after first run
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
creationTimestamp: "2024-03-27T07:02:59Z"
|
||||
labels:
|
||||
app: hydroxide-push
|
||||
name: hydroxide-push
|
||||
spec:
|
||||
containers:
|
||||
- args:
|
||||
- notify
|
||||
image: ghcr.io/0ranki/hydroxide-push:latest
|
||||
name: main
|
||||
volumeMounts:
|
||||
- mountPath: /data
|
||||
name: hydroxide-push-pvc
|
||||
envFrom:
|
||||
- configMapRef:
|
||||
name: hydroxide-push-config
|
||||
optional: true
|
||||
volumes:
|
||||
- name: hydroxide-push-pvc
|
||||
persistentVolumeClaim:
|
||||
claimName: hydroxide-push
|
||||
|
16
ntfy/ntfy.go
16
ntfy/ntfy.go
|
@ -63,7 +63,7 @@ func ntfyConfigFile() (string, error) {
|
|||
func Notify() {
|
||||
cfg := NtfyConfig{}
|
||||
if err := cfg.Read(); err != nil {
|
||||
log.Printf("error reading notification: %v", err)
|
||||
log.Printf("error reading configuration: %v", err)
|
||||
return
|
||||
}
|
||||
req, _ := http.NewRequest("POST", cfg.URI(), strings.NewReader("New message received"))
|
||||
|
@ -148,11 +148,25 @@ func Login(cfg *NtfyConfig, be backend.Backend) {
|
|||
}
|
||||
|
||||
func (cfg *NtfyConfig) Setup() {
|
||||
|
||||
// Configure using environment
|
||||
if os.Getenv("PUSH_URL") != "" && os.Getenv("PUSH_TOPIC") != "" {
|
||||
cfg.URL = os.Getenv("PUSH_URL")
|
||||
cfg.Topic = os.Getenv("PUSH_TOPIC")
|
||||
log.Printf("Current push endpoint: %s\n", cfg.URI())
|
||||
err := cfg.Save()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var n string
|
||||
if cfg.URL != "" && cfg.Topic != "" {
|
||||
fmt.Printf("Current push endpoint: %s\n", cfg.URI())
|
||||
n = "new "
|
||||
}
|
||||
|
||||
// Read push base URL
|
||||
notValid := true
|
||||
scanner := bufio.NewScanner(os.Stdin)
|
||||
|
|
Loading…
Reference in New Issue