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
playout
container executesself.services.version_url()["api_version"]
which sends a request toLIBRETIME_GENERAL_PUBLIC_URL
from the container’s environment, which is set tohttp://nginx
here - The
nginx
container 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