Hi! Before I start - many thanks for the amazing work on this project.
Describe the problem
I’m setting up LibreTime for an existing web radio, and I’m having trouble exposing the API behind nginx.
A few considerations:
- LibreTime must run on the same host as the radio itself (which is a very simple nodejs express site, also running in a Docker container)
- The radio’s UI should be accessible at
https://example.com; LibreTime should be accessible athttps://libretime.example.com
At first glance, this already works nicely: I pass a custom default.conf to the nginx container, which redirects traffic to the relevant Docker container based on the server name. So far so good. Looking at the logs below however, I see that everything isn’t running so smoothly. What I read from the logs is:
- The
playoutcontainer executesself.services.version_url()["api_version"]which sends a request toLIBRETIME_GENERAL_PUBLIC_URLfrom the container’s environment, which is set tohttp://nginxhere - The
nginxcontainer receives the request and forwards it toexample.com - The server returns a 404 because
example.com/api/version/api_key/[REDACTED]doesn’t exist; howeverlibretime.example.com/api/version/api_key/[REDACTED]does exist
Same thing for liquidsoap, and again https://example.com/api/v2/info doesn’t exist, but https://libretime.example.com/api/v2/info does.
The fix is pretty obvious: somehow I need to get nginx to redirect all requests to the API to libretime.example.com instead of example.com. In practice though, I’m not sure how to do that and I’d appreciate any hints. I can’t seem to attach files to this post, so I’m pasting my nginx default.conf below.
A more general thought also: I find it odd that LibreTime components would connect to the API through the public URL (i.e. nginx). Since this traffic is internal, I would expect the components to connect to the API container directly. I tried setting LIBRETIME_GENERAL_PUBLIC_URL to http://api and http://api:9001 to see if that would work - no luck. Am I missing something?
nginx default.conf
server {
server_name example.com;
listen 80;
return 301 https://$server_name$request_uri;
}
server{
server_name example.com;
location / {
proxy_pass http://web;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
listen [::]:443 ssl ipv6only=on; # managed by Certbot
listen 443 ssl; # managed by Certbot
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}
server {
server_name libretime.example.com;
listen 80;
return 301 https://$server_name$request_uri;
}
server {
server_name libretime.example.com;
listen 443 ssl; # managed by Certbot
ssl_certificate /etc/letsencrypt/live/libretime.example.com/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/libretime.example.com/privkey.pem; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
root /var/www/html/public;
index index.php index.html index.htm;
client_max_body_size 512M;
client_body_timeout 300s;
location ~ \.php$ {
fastcgi_buffers 64 4K;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
#try_files $uri =404;
try_files $fastcgi_script_name =404;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
set $path_info $fastcgi_path_info;
fastcgi_param PATH_INFO $path_info;
include fastcgi_params;
fastcgi_index index.php;
fastcgi_pass legacy:9000;
}
location / {
try_files $uri $uri/ /index.php$is_args$args;
}
location ~ ^/api/(v2|browser) {
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_redirect off;
proxy_pass http://api:9001;
}
}
Expected behavior
All LibreTime components should be able to access the API.
Relevant log output or error messages
playout
nginx_1 | 192.168.144.11 - - [15/Oct/2022:11:05:14 +0000] "nginx" "GET /api/version/api_key/[REDACTED] HTTP/1.1" 301 169 "-" "python-requests/2.28.1" 0.000 <"-" >"
nginx_1 | 192.168.144.1 - - [15/Oct/2022:11:05:14 +0000] "example.com" "GET /api/version/api_key/[REDACTED] HTTP/1.1" 404 174 "-" "python-requests/2.28.1" 0.002 <"-" >"
playout_1 | ERROR:root:GET https://example.com/api/version/api_key/[REDACTED] request failed '404':
playout_1 | Payload: None
playout_1 | Response: <!DOCTYPE html>
playout_1 | <html lang="en">
playout_1 | <head>
playout_1 | <meta charset="UTF-8">
playout_1 | </head>
playout_1 | <body>
playout_1 | <h1>404</h1>
playout_1 | <p>There's nothing here.</p>
playout_1 | </body>
playout_1 | </html>
playout_1 |
playout_1 | ERROR:root:404 Client Error: Not Found for url: https://example.com/api/version/api_key/[REDACTED]
playout_1 | Traceback (most recent call last):
playout_1 | File "/usr/local/lib/python3.10/site-packages/libretime_api_client/v1.py", line 90, in __get_api_version
playout_1 | return self.services.version_url()["api_version"]
playout_1 | File "/usr/local/lib/python3.10/site-packages/libretime_api_client/_utils.py", line 108, in __call__
playout_1 | res.raise_for_status()
playout_1 | File "/usr/local/lib/python3.10/site-packages/requests/models.py", line 1021, in raise_for_status
playout_1 | raise HTTPError(http_error_msg, response=self)
playout_1 | requests.exceptions.HTTPError: 404 Client Error: Not Found for url: https://example.com/api/version/api_key/[REDACTED]
liquidsoap
nginx_1 | 192.168.144.6 - - [15/Oct/2022:11:18:42 +0000] "nginx" "GET /api/v2/info HTTP/1.1" 301 169 "-" "python-requests/2.28.1" 0.000 <"-" >"
nginx_1 | 192.168.144.1 - - [15/Oct/2022:11:18:42 +0000] "example.com" "GET /api/v2/info HTTP/1.1" 404 174 "-" "python-requests/2.28.1" 0.008 <"-" >"
liquidsoap_1 | 2022-10-15 11:18:42.328 | ERROR | libretime_api_client._client:_request:78 - 404 Client Error: Not Found for url: https://example.com/api/v2/info
liquidsoap_1 | Traceback (most recent call last):
liquidsoap_1 | File "/usr/local/bin/libretime-liquidsoap", line 8, in <module>
liquidsoap_1 | sys.exit(cli())
liquidsoap_1 | File "/usr/local/lib/python3.10/site-packages/click/core.py", line 1128, in __call__
liquidsoap_1 | return self.main(*args, **kwargs)
liquidsoap_1 | File "/usr/local/lib/python3.10/site-packages/click/core.py", line 1053, in main
liquidsoap_1 | rv = self.invoke(ctx)
liquidsoap_1 | File "/usr/local/lib/python3.10/site-packages/click/core.py", line 1395, in invoke
liquidsoap_1 | return ctx.invoke(self.callback, **ctx.params)
liquidsoap_1 | File "/usr/local/lib/python3.10/site-packages/click/core.py", line 754, in invoke
liquidsoap_1 | return __callback(*args, **kwargs)
liquidsoap_1 | File "/src/libretime_playout/liquidsoap/main.py", line 37, in cli
liquidsoap_1 | info = Info(**api_client.get_info().json())
liquidsoap_1 | File "/usr/local/lib/python3.10/site-packages/libretime_api_client/v2.py", line 12, in get_info
liquidsoap_1 | return self._request("GET", "/api/v2/info", **kwargs)
liquidsoap_1 | File "/usr/local/lib/python3.10/site-packages/libretime_api_client/_client.py", line 79, in _request
liquidsoap_1 | raise exception
liquidsoap_1 | File "/usr/local/lib/python3.10/site-packages/libretime_api_client/_client.py", line 74, in _request
liquidsoap_1 | response.raise_for_status()
liquidsoap_1 | File "/usr/local/lib/python3.10/site-packages/requests/models.py", line 1021, in raise_for_status
liquidsoap_1 | raise HTTPError(http_error_msg, response=self)
liquidsoap_1 | requests.exceptions.HTTPError: 404 Client Error: Not Found for url: https://example.com/api/v2/info
LibreTime version
LibreTime version: 3.0.0-beta.2
Installation method and OS / Environment
Operating system: Debian
Method: Docker compose
Cheers,
Oliver