Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add possibility to pass secrets via files #31

Merged
merged 10 commits into from
Oct 25, 2024
33 changes: 32 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,37 @@ Now we could configure the FRITZ!Box
to `http://[docker-host-ip]:49000/ip?v4=<ipaddr>&v6=<ip6addr>&prefix=<ip6lanprefix>` and it should trigger the update
process.

## Passing secrets

As shown above, secrets can be passed via environment variables.
If passing secrets via environment variables does not work for your use case, it's also possible to pass them via the filesystem.
In order to pass a secret via a file, append `_FILE` to the respective environment variable name and configure it to point to the file containing the secret.
For example in order to pass the Cloudflare API token via a file, configure an environment variable with name `CLOUDFLARE_API_TOKEN_FILE` with the absolute path to a file containing the secret.

Here is an example `docker-compose.yml` passing the file `cloudflare_api_key.txt` from the host to the docker container using docker compose secrets:

```
version: '3.7'

services:
updater:
image: ghcr.io/cromefire/fritzbox-cloudflare-dyndns:1
network_mode: host
environment:
- DYNDNS_SERVER_BIND=:8080
- CLOUDFLARE_API_KEY_FILE=/run/secrets/cloudflare_api_key
- CLOUDFLARE_ZONES_IPV4=test.example.com
- CLOUDFLARE_ZONES_IPV6=test.example.com
secrets:
- cloudflare_api_key

secrets:
cloudflare_api_key:
file: ./cloudflare_api_key.txt
cromefire marked this conversation as resolved.
Show resolved Hide resolved
```

See https://docs.docker.com/compose/how-tos/use-secrets/ for more information about docker compose secrets.

## Docker build

A pre-built docker image is also available on this
Expand Down Expand Up @@ -188,4 +219,4 @@ trigger it by calling `http://127.0.0.1:8888/ip?v4=127.0.0.1&v6=::1` and review

## History & Credit

Most of the credit goes to [@adrianrudnik](https://github.com/adrianrudnik), who wrote and maintained the software for years. Meanwhile I stepped in at a later point when the repository was transferred to me to continue its basic maintenance should it be required.
Most of the credit goes to [@adrianrudnik](https://github.com/adrianrudnik), who wrote and maintained the software for years. Meanwhile I stepped in at a later point when the repository was transferred to me to continue its basic maintenance should it be required.
25 changes: 22 additions & 3 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,9 +100,9 @@ func newFritzBox() *avm.FritzBox {
func newUpdater() *cloudflare.Updater {
u := cloudflare.NewUpdater(slog.Default())

token := os.Getenv("CLOUDFLARE_API_TOKEN")
token := readSecret("CLOUDFLARE_API_TOKEN")
email := os.Getenv("CLOUDFLARE_API_EMAIL")
key := os.Getenv("CLOUDFLARE_API_KEY")
key := readSecret("CLOUDFLARE_API_KEY")

if token == "" {
if email == "" || key == "" {
Expand Down Expand Up @@ -155,7 +155,7 @@ func startPushServer(out chan<- *net.IP, localIp *net.IP, cancel context.CancelC

server := dyndns.NewServer(out, localIp, slog.Default())
server.Username = os.Getenv("DYNDNS_SERVER_USERNAME")
server.Password = os.Getenv("DYNDNS_SERVER_PASSWORD")
server.Password = readSecret("DYNDNS_SERVER_PASSWORD")

s := &http.Server{
Addr: bind,
Expand Down Expand Up @@ -272,3 +272,22 @@ func startPollServer(out chan<- *net.IP, localIp *net.IP) {
}
}()
}

func readSecret(envName string) string {
secret := os.Getenv(envName)

if secret != "" {
return secret
}

passwordFilePath := os.Getenv(envName + "_FILE")
if passwordFilePath != "" {
content, err := os.ReadFile(passwordFilePath)
if err != nil {
slog.Error("Failed to read secret from file "+passwordFilePath, logging.ErrorAttr(err))
} else {
secret = string(content)
cromefire marked this conversation as resolved.
Show resolved Hide resolved
}
}
return secret
}
Loading