Securing Transmission Daemon using OAuth2-Proxy

Vinsen Muliadi
5 min readFeb 15, 2023

--

transmission-daemon is a very helpful tool for someone that downloads files from time to time using torrent (use torrent responsibly folks). For people that never use transmission-daemon before, transmission-daemon has a webpage to manage things. That means you need to set up an authentication method to secure it.

Previously, I set up .htpasswd authentication method to access the transmission daemon’s web UI. Really simple and straightforward. In case you need to add a new user, you only execute htpasswd .htpasswd newuser . Until I found a new way to optimize this so that I no longer execute htpasswd commands anymore in case add a new user.

OAuth2-Proxy is a reverse proxy and static file server that provides authentication using Providers (Google, GitHub, and others) to validate accounts by email, domain, or group.

That means we can use our account like GitHub or Google to access our service, a perfect replacement for .htpasswd . In this medium, we’ll walk through the required steps to make your transmission-daemon protected by OAuth2-Proxy (I’m going to use GCP’s API and Service).

Google Cloud Platform’s Section

First, you need to create an OAuth2 Client ID and Secret from GCP. I assume you already have an GCP account and have an active project.

  • Access “OAuth consent screen” under the “API & Services” section.
    Set the “User type” to “Internal”. The rest is a general question, you can directly set up your custom consent page (like using your own logo when someone authenticates themselves. If you left it blank, it’ll use the Google logo).
  • Open “Credentials” under “API & Services” section.
    Click on “+ CREATE CREDENTIALS” and click on “OAuth client ID”. Set the “Application type” to “Web application”. There are some fields that you need to pay attention to, the rest is up to you. I only left some guidance to make it easier for you to set it up.

    Authorized JavaScript origins. The HTTP origins that host your web application. This value can’t contain wildcards or paths. If you use a port other than 80, you must specify it.

    Fill in the domain that you want to integrate with your service. For example https://tr.example.com (without trailing slash and include the scheme — https:// ).

    Authorized redirect URIs. Users will be redirected to this path after they have authenticated with Google. The path will be appended with the authorization code for access and must have a protocol. It can’t contain URL fragments, relative paths, or wildcards, and can’t be a public IP address.

    Like “Authorized JavaScript origins”, you need to fill in the domain that you want to integrate with your service, but you need to add an additional path to it. Default, you need to add /oauth2/auth to the domain. For example https://tr.example.com/oauth2/auth.
  • If everything is OK, you’ll get a client id and client secret. We need this to integrate with OAuth2-Proxy. Until this step, we’re finished with the GCP’s part. Let’s continue to the VM and set up OAuth2-Proxy.

OAuth2-Proxy’s Part

  • Download the latest release based on your computer architecture from the GitHub release page and extract it to your server. After that, you need to add execute permission to the oauth2-proxy binary by executing chmod +x oauth2-proxy-{{ version }}.linux-amd64/oauth2-proxy and move it to /usr/bin .
  • Create a directory /opt/oauth2-proxy/ and then create a file named transmission-daemon.conf. Copy these templates and paste them to your transmission-daemon.conf . You need to replace some parts of the template with these.

<CLIENT_ID_FROM_GCP_OAUTH> and <CLIENT_SECRET_FROM_GCP_OAUTH> from the GCP’s latest part on this medium. You can open the webpage again to check your client id and secret in case you forgot to store it somewhere.

<COOKIE_SECRET> can be generated using a random string. You can generate one by using this command and copying the output. You need to store it safely (because this is a secret). Basically the command will produce some random string with base64 encoded.

dd if=/dev/urandom bs=32 count=1 2>/dev/null | base64 | tr -d -- '\n' | tr -- '+/' '-_'; echo

# General config
upstreams = ["http://127.0.0.1:9091"] # default transmission-daemon port
email_domains = [ "<DOMAIN_NAME>" ] # use your google-email domain
http_address = ":8080" # use port where the oauth2-proxy will be running
request_logging = true
ssl_insecure_skip_verify = false

# The OAuth Client ID, Secret
client_id = "<CLIENT_ID_FROM_GCP_OAUTH>"
client_secret = "<CLIENT_SECRET_FROM_GCP_OAUTH>"

#Cookie Settings
cookie_secret = "<COOKIE_SECRET>"
cookie_name = "transmission_daemon"
cookie_expire = "24h"
cookie_secure = true
cookie_httponly = true
  • Install supervisor on your system and ensure /etc/supervisor/conf.d/ existed in your server (assuming you’re using a Debian-based operating system. Other OS may vary). Create a file named transmission-daemon.conf and copy these template and paste it.
[program:transmission-daemon]
command=/usr/bin/oauth2-proxy --config=/opt/oauth2-proxy/transmission-daemon.conf --ssl-upstream-insecure-skip-verify --reverse-proxy=true
process_name=%(program_name)s
priority=1
user=root
group=root
autostart=true
autorestart=true
redirect_stderr=true
stdout_logfile=/var/log/supervisor/transmission-daemon.log
  • Run sudo supervisorctl reread and sudo supervisorctl update to load the configuration that we recently created. To ensure everything is running perfectly, watch the log by executing sudo supervisorctl tail -f transmission-daemon . If everything is OK, we finally arrived at the last part. NGINX configuration.
  • Final part. Copy these templates and paste them to /etc/nginx/conf.d/transmission-daemon.conf . After that execute nginx -t to ensure the configuration is correct and working. If everything is OK, execute nginx -s reload to apply the configuration.
server {
listen 80;
server_name <DOMAIN_NAME>;
return 301 https://$host$request_uri;
}

server {
listen 443 ssl;
server_name <DOMAIN_NAME>;

access_log /var/log/nginx/transmission.access.log;
error_log /var/log/nginx/transmission.error.log;

ssl_certificate <SSL_CHAIN_CERTIFICATE_FILE>;
ssl_certificate_key <SSL_PRIVATE_KEY_FILE>;

# optional part. this one for SSL config.
# include /etc/nginx/options-ssl-nginx.conf;
# ssl_dhparam /etc/nginx/ssl-dhparams.pem;

location /oauth2/ {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Scheme $scheme;
proxy_set_header X-Auth-Request-Redirect $request_uri;
}

location / {
auth_request /oauth2/auth;
error_page 401 = /oauth2/sign_in;
proxy_pass http://127.0.0.1:8080;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}

Basically, the NGINX config said something like this. In case you’re not authenticated using the mail domain that you registered during the OAuth2-Proxy’s part, you’ll be redirected to /oauth/sign_in to authenticate yourself against /oauth2/auth .

If the authentication succeeds, the system will create a cookie named transmission_daemon on your browser so the next time you access the transmission webpage, you won’t be asked anymore. Else, you’ll be kicked out and need to retry the authentication process.

OAuth2-Proxy default webpage that asking for authentication

Hasta la vista .htpasswd and thank you for your service.

--

--