Deployment and lifecycle management for Nix
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

move secrets to paths in config

+82 -84
-3
.envrc
··· 17 17 RELEASE_COOKIE=$(cat "$RELEASE_COOKIE_FILE") 18 18 export RELEASE_COOKIE 19 19 20 - export SOWER_SECRET_KEY_BASE_FILE="$PWD/.dev-secret-key-base" 21 20 genSecret "$SOWER_SECRET_KEY_BASE_FILE" 22 21 23 22 export SOWER_BOOTSTRAP_TOKEN_FILE="$PWD/.bootstrap.token" 24 23 genSecret "$SOWER_BOOTSTRAP_TOKEN_FILE" 25 - 26 - export SOWER_AUTH_OIDC_CLIENT_SECRET_FILE="$PWD/.dev-oidc-secret" 27 24 28 25 export ERL_AFLAGS="-kernel shell_history enabled -kernel shell_history_file_bytes 1024000"
+69 -74
config/runtime.exs
··· 7 7 defmodule Sower.Config do 8 8 require Logger 9 9 10 - @credentials [ 11 - "SOWER_AUTH_OIDC_CLIENT_SECRET_FILE" 12 - ] 13 - 14 10 @schema %{ 15 11 "type" => "object", 16 12 "required" => ["auth", "database"], ··· 23 19 "type" => "string" 24 20 }, 25 21 "oidc_client_id" => %{ 22 + "type" => "string" 23 + }, 24 + "oidc_client_secret_file" => %{ 26 25 "type" => "string" 27 26 }, 28 27 "oidc_redirect_uri" => %{ ··· 49 48 }, 50 49 "user" => %{ 51 50 "type" => "string" 51 + }, 52 + "password_file" => %{ 53 + "type" => "string" 52 54 } 53 55 } 54 56 }, ··· 65 67 "type" => "integer", 66 68 "minimum" => 80, 67 69 "maximum" => 65535 70 + }, 71 + "secret_key_base_file" => %{ 72 + "type" => "string" 68 73 } 69 74 } 70 75 } ··· 87 92 Kernel.exit(1) 88 93 end 89 94 95 + # set log level to atom and remove from config 96 + if Map.has_key?(json_config, "log_level") do 97 + level = Map.get(json_config, "log_level") |> String.to_existing_atom() 98 + Logger.info(~s"Overriding log level from config to #{level}") 99 + 100 + config :logger, :console, level: level 101 + end 102 + 90 103 Logger.debug("Loaded configuration") 91 104 Logger.debug(json_config) 105 + json_config = json_config |> Map.delete("log_level") 92 106 93 107 # load some defaults 94 108 public_url = json_config |> Map.get("public_url", "http://127.0.0.1:4000") ··· 96 110 listen_address = json_config |> Map.get("listen_address", "127.0.0.1") 97 111 listen_port = json_config |> Map.get("listen_port", 4000) 98 112 99 - # set log level to atom and remove from config 100 - if Map.has_key?(json_config, "log_level") do 101 - level = Map.get(json_config, "log_level") |> String.to_existing_atom() 102 - Logger.info(~s"Overriding log level from config to #{level}") 113 + secret_key_base = 114 + with %{"secret_key_base_file" => secret_key_base_file} <- json_config, 115 + {:ok, secret_key_base} <- read_credential(secret_key_base_file) do 116 + secret_key_base 117 + else 118 + {:error, err} -> 119 + Logger.warning("Failed to load secret_key_base from secret file, #{err}.") 120 + Kernel.exit(1) 103 121 104 - config :logger, :console, level: level 105 - end 122 + %{} -> 123 + Logger.warning("No secret_key_base_file in configuration. Exiting!") 124 + Kernel.exit(1) 125 + end 106 126 107 - json_config = json_config |> Map.delete("log_level") 127 + json_config = 128 + with %{"database" => database} <- json_config, 129 + %{"password_file" => password_file} <- database, 130 + {:ok, password} <- read_credential(password_file) do 131 + json_config |> Map.put("database", database |> Map.put("password", password)) 132 + else 133 + # assume missing password_file is intentional 134 + %{} -> 135 + Logger.debug("No database password_file to read.") 136 + json_config 108 137 109 - json_config 110 - |> Enum.map(&load_config(&1)) 138 + {:error, err} -> 139 + Logger.warning("Failed to load database password from file, #{err}.") 140 + json_config 141 + end 142 + 143 + json_config = 144 + with %{"auth" => auth} <- json_config, 145 + %{"oidc_client_secret_file" => oidc_client_secret_file} <- auth, 146 + {:ok, oidc_client_secret} <- read_credential(oidc_client_secret_file) do 147 + json_config |> Map.put("auth", auth |> Map.put("oidc_client_secret", oidc_client_secret)) 148 + else 149 + {:error, err} -> 150 + Logger.warning("Failed to load oidc_client_secret from secret file, #{err}.") 151 + Kernel.exit(1) 152 + 153 + %{} -> 154 + Logger.warning("No auth.oidc_client_secret_file in configuration. Exiting!") 155 + Kernel.exit(1) 156 + end 111 157 112 - @credentials |> Enum.map(&load_credential(&1)) 158 + Logger.debug("Modified configuration") 159 + Logger.debug(json_config) 113 160 114 - case credential("SOWER_DATABASE_PASS_FILE") do 115 - {:ok, pass} -> put_config(Sower.Repo, password: pass) 116 - _ -> nil 117 - end 161 + json_config |> Enum.map(&load_config(&1)) 118 162 119 163 # load some non-app namespaced configs 120 164 %URI{scheme: scheme, host: host, port: port} = URI.parse(public_url) ··· 122 166 put_config(SowerWeb.Endpoint, 123 167 url: [host: host, port: port, scheme: scheme], 124 168 http: [ip: ip_to_inet(listen_address), port: listen_port], 125 - secret_key_base: credential!("SOWER_SECRET_KEY_BASE_FILE"), 169 + secret_key_base: secret_key_base, 126 170 persistent: true 127 171 ) 128 172 ··· 140 184 put_config(config_atom, value) 141 185 end 142 186 143 - defp load_credential(cred) when is_binary(cred) do 144 - Logger.debug(~s"Loading credential #{cred}") 145 - 146 - captures = 147 - ~r/SOWER_(?<section>[[:alnum:]]+)_(?<key>.+)_FILE/ 148 - |> Regex.named_captures(cred) 187 + defp read_credential(path) when is_binary(path) do 188 + case path |> File.read() do 189 + {:ok, content} -> 190 + {:ok, content |> String.trim()} 149 191 150 - if captures == nil do 151 - Logger.error(~s"Credential #{cred} cannot be parsed") 152 - Kernel.exit(1) 192 + other -> 193 + other 153 194 end 154 - 155 - section = captures["section"] |> String.downcase() |> String.to_atom() 156 - 157 - key = captures["key"] |> String.downcase() |> String.to_atom() 158 - 159 - case credential(cred) do 160 - {:ok, path} -> 161 - put_config(section, [{key, path}]) 162 - :ok 163 - 164 - {:error, _err} -> 165 - :error 166 - end 167 - end 168 - 169 - defp credential(name) do 170 - credential_dir = System.get_env("CREDENTIALS_DIRECTORY") 171 - credential = System.get_env(name) 172 - 173 - case read_credential(name, credential_dir, credential) do 174 - {:ok, value} -> {:ok, value |> String.trim()} 175 - {:error, err} -> {:error, ~s"unable to load credential #{name}, #{err}"} 176 - end 177 - end 178 - 179 - def credential!(name) do 180 - case credential(name) do 181 - {:ok, value} -> value 182 - {:error, err} -> raise err 183 - end 184 - end 185 - 186 - defp read_credential(name, nil, nil) do 187 - Logger.warning(~s"Could not load credential from env: #{name}") 188 - {:error, "not found"} 189 - end 190 - 191 - defp read_credential(_, nil, cred), do: read_credential(cred) 192 - 193 - defp read_credential(name, dir, nil), do: read_credential(~s"#{dir}/#{name}") 194 - defp read_credential(_, dir, cred), do: read_credential(~s"#{dir}/#{cred}") 195 - defp read_credential(path) when is_binary(path), do: path |> File.read() 196 - 197 - defp read_credential(nil) do 198 - Logger.error("Could not find credential") 199 - Kernel.exit(1) 200 195 end 201 196 202 197 defp put_config(config_atom, new_values) when is_atom(config_atom) and is_list(new_values) do
+6 -3
dev-server.json
··· 2 2 "public_url": "http://localhost:4000", 3 3 "auth": { 4 4 "oidc_base_url": "https://id.junco.dev/oauth2/openid/sower-dev", 5 - "oidc_client_id": "sower-dev" 5 + "oidc_client_id": "sower-dev", 6 + "oidc_client_secret_file": ".dev-oidc-secret" 6 7 }, 7 8 "database": { 8 9 "username": "postgres", 9 10 "hostname": "localhost", 10 11 "database": "sower_dev", 11 - "port": 5432 12 + "port": 5432, 13 + "password_file": "VERSION" 12 14 }, 13 15 "listen_port": 4000, 14 16 "nix_cache": { ··· 18 20 "client_bin": { 19 21 "x86_64-linux": "/nix/store/path-to-client" 20 22 }, 21 - "log_level": "debug" 23 + "log_level": "debug", 24 + "secret_key_base_file": ".dev-secret-key-base" 22 25 }
+6 -3
nix/nixos-server.nix
··· 50 50 }; 51 51 52 52 config = lib.mkIf cfg.enable { 53 - services.sower.server.secrets = lib.mkIf cfg.initSecrets { 54 - release_cookie_file = "/var/lib/sower/release-cookie"; 55 - secret_key_base_file = "/var/lib/sower/secret-key-base"; 53 + services.sower.server = lib.mkIf cfg.initSecrets { 54 + secrets = { 55 + release_cookie_file = "/var/lib/sower/release-cookie"; 56 + }; 57 + 58 + settings.secret_key_base_file = "/var/lib/sower/secret-key-base"; 56 59 }; 57 60 58 61 systemd.services.sower = {
+1 -1
nix/test-end-to-end.nix
··· 38 38 initSecrets = true; 39 39 secrets = { 40 40 bootstrap_token_file = "${pkgs.writeText "token" "aninsecuretken"}"; 41 - auth_oidc_client_secret_file = "${pkgs.writeText "oidc-secret" "ok"}"; 42 41 }; 43 42 44 43 settings = { ··· 53 52 auth = { 54 53 oidc_client_id = "sower"; 55 54 oidc_base_url = "http://localhost:9000"; 55 + oidc_client_secret_file = "${pkgs.writeText "oidc-secret" "ok"}"; 56 56 }; 57 57 58 58 log_level = "debug";