diff --git a/content/post/2021-07-25-setting-up-owntracks-recorder-and-oauth2-with-nginx-and-podman.md b/content/post/2021-07-25-setting-up-owntracks-recorder-and-oauth2-with-nginx-and-podman.md index 0007c0a..798dd36 100644 --- a/content/post/2021-07-25-setting-up-owntracks-recorder-and-oauth2-with-nginx-and-podman.md +++ b/content/post/2021-07-25-setting-up-owntracks-recorder-and-oauth2-with-nginx-and-podman.md @@ -220,7 +220,7 @@ systemctl --user daemon-reload This will create a bunch of files there, namely `pod-owntracks.service`, `container-ot_recorder.service` and `container-ot_frontend.service`. You can then enable owntracks at boot with -``` +```shell systemctl --user enable pod-owntracks.service ``` @@ -314,9 +314,175 @@ server { Check the configuration with `nginx -t` and then restart your webserver. If you access `https://tracks.example.com` you should see a map (OpenStreetMap) and if you access `https://tracks.example.com/owntracks` you should be presented with a list of locations and users. Of course everything is empty, because we haven't added any device yet. +Before we actually start recording, we need to secure access, otherwise anyone could see where you're going (not good). That means adding some form of authentication. + ## Authentication: is httpasswd the only way? -## oauth2-proxy +The simplest solution would be to use HTTP Basic Authentication and secure the root, `/owntracks` and `/owntracks/pub` paths. However, that's not what I wanted: as I planned to allow a few trusted users for viewing, I didn't want to have them remember another series of usernames and passwords. I had already a central source of authentication (more on that below), so I wanted to use that. + +On the other hand, the OwnTracks app only understands Basic Authentication and nothing else. So, what to do? + +### oauth2-proxy + +I could have used LDAP instead, but I don't have LDAP on my system and I didn't want to retrofit it in the existing services. The solution was to use the [OAuth 2.0 standard](https://oauth.net/2/) in conjunction with [nginx's `auth_request` module](https://nginx.org/en/docs/http/ngx_http_auth_request_module.html) to allow authentication through another source. + +For the actual OAuth2 service, I wanted something simple so I looked at [oauth2-proxy](https://oauth2-proxy.github.io/oauth2-proxy/). It is used often in conjunction with Kubernetes and the nginx_ingress controller, but it can be used also with the "regular" nginx. + +A warning before going further. This guide assumes you put the OAuth2 service in a top-level domain called `auth.example.com`. + +As oauth2-proxy is written in Go, you can just clone the [git repo](https://github.com/oauth2-proxy/oauth2-proxy) and build it yourself, or download a pre-built binary (I built it and installed it in `/usr/local/bin`). While a Docker image is offered, in my opinion there's no need for Docker containers for a single application. You're free to use whatever option you want, of course. + +With oauth2-proxy installed, it's time to set things up. Create a path to host the configuration (I used `/etc/oauth2-proxy`) and write the following in the configuration file (`oauth2-proxy.cfg`; some comments are from [the sample configuration](https://github.com/oauth2-proxy/oauth2-proxy/blob/master/contrib/oauth2-proxy.cfg.example) plus some of my own): + +``` + +## OAuth2 Proxy Config File +## https://github.com/oauth2-proxy/oauth2-proxy + +# There are plenty of other options; see the sample configuration for details + +## : to listen on for HTTP/HTTPS clients +http_address = "127.0.0.1:4180" + +## Are we running behind a reverse proxy? Will not accept headers like X-Real-Ip unless this is set. +reverse_proxy = true +## Alternative users for Basic Authentication +htpasswd_file = "/etc/nginx/owntracks.htpasswd" +display_htpasswd_form = true + +## the OAuth Redirect URL. +# defaults to the "https://" + requested host header + "/oauth2/callback" +redirect_url = "https://auth.example.com/oauth2/callback" + +## oauth2-proxy can also acts a proxy for files, but we'll just use nginx +## So we make sure it doesn't proxy anything +upstreams = [ + "file:///dev/null" +] + +# Put ALL domains you want oauth2-proxy to redirect to after authentication +# otherwise redirection will *NOT* work +whitelist_domains = [ + ".example.com", +] + +## pass HTTP Basic Auth, X-Forwarded-User and X-Forwarded-Email information to upstream +# These are needed for the OwnTracks application +pass_basic_auth = true +pass_user_headers = true +pass_authorization_header = true +set_basic_auth = true +## pass the request Host Header to upstream +## when disabled the upstream Host is used as the Host Header +pass_host_header = true + +## Email Domains to allow authentication for (this authorizes any email on this domain) +## for more granular authorization use `authenticated_emails_file` +## To authorize any email addresses use "*" + +# I use my own mail domains here: adjust this configuration to your liking + +email_domains = [ + "example.com" +] + +## The OAuth Client ID, Secret +provider = "YOUR_PROVIDER" +client_id = "CLIENT_ID" +client_secret = "CLIENT_SECRET" + +# Put provider specific options here + +# Basic authentication users - for the OwnTrack apps ONLY + +## Additionally authenticate against a htpasswd file. Entries must be created with "htpasswd -B" for bcrypt encryption +## enabling exposes a username/login signin form +htpasswd_file = "/etc/nginx/owntracks.htpasswd" +display_htpasswd_form = true + +## Cookie Settings +## Name - the cookie name +## Secret - the seed string for secure cookies; should be 16, 24, or 32 bytes +## for use with an AES cipher when cookie_refresh or pass_access_token +## is set +## Domain - (optional) cookie domain to force cookies to (ie: .yourcompany.com) +## Expire - (duration) expire timeframe for cookie +## Refresh - (duration) refresh the cookie when duration has elapsed after cookie was initially set. +## Should be less than cookie_expire; set to 0 to disable. +## On refresh, OAuth token is re-validated. +## (ie: 1h means tokens are refreshed on request 1hr+ after it was set) +## Secure - secure cookies are only sent by the browser of a HTTPS connection (recommended) +## HttpOnly - httponly cookies are not readable by javascript (recommended) +cookie_name = "YOUR_COOKIE_NAME" +# See the oauth2-proxy docs on how to generate this +cookie_secret = "YOUR_COOKIE_SECRET" +cookie_domain = "example.com" +cookie_secure = true +cookie_httponly = true +``` + +Note there are a few options to fill in, in particular the OAuth2 provider. There are plenty of options to choose from: they range from external services (GitHub, Google) to self-hosted ones (Nextcloud, Gitea, Gitlab, Keycloak...). Refer to the [oauth2-proxy documentation](https://oauth2-proxy.github.io/oauth2-proxy/docs/configuration/oauth_provider) on how to set them up. I'll go through my personal choice later. + +You also need to create the static user(s) for the OwnTracks app. I have found no other way to avoid it. If you know a better way that avoids this, let me know. + +```shell +htpasswd -c /etc/nginx/owntracks.htpasswd +# The -B switch is *mandatory* if you want to use htpasswd with oauth2-proxy +htpasswd -c -B /etc/nginx/owntracks.htpasswd myusername +# Insert the password when prompted +# Repeat if you have more than one +``` + +Once you have oauth2-proxy set up, try running it: + +```shell +oauth2-proxy --config /etc/oauth2-proxy/oauth2-proxy.cfg +``` + +Quit with Ctrl-C. If it starts up correctly, it's time to make it start on boot. This assumes you are using a recent enough version of systemd (I used 234). + +Create this unit file (`/etc/systemd/system/oauth2-proxy.service`): + +```systemd +[Unit] +Description=oauth2-proxy daemon service +After=syslog.target network.target + +[Service] +# Change it to any non-privileged user you want; "nobody" may work too +# DynamicUser may also work, but I have not tested it +User=nginx +Group=nginx + +ExecStart=/usr/local/bin/oauth2-proxy --config=/etc/oauth2-proxy/oauth2-proxy.cfg +ExecReload=/bin/kill -HUP $MAINPID + +# As it only needs to listen and forward requests, limit its access +# With later systemd versions you can also sandbox it further +ProtectHome=true +ProtectSystem=full +PrivateTmp=true + +KillMode=process +Restart=always + +[Install] +WantedBy=multi-user.target +``` + +Afterwards, it's time to enable and start the service: + +```shell +systemctl daemon-reload +systemctl enable --now oauth2-proxy.service +``` + +If you want to be fancier and want it to start only when required, you can always use systemd's socket activation. However, I had no need for this so I left it always running. + +### Adjusting nginx configuration + +Now, we need to add the relevant information to nginx. + ## The ideal source of authentication