Reverse Proxy for IP Camera

it flag

I'm using an Amcrest camera which requires basic auth to render its stream (documentation - page 17). The camera is accessed through http://admin:password@IP_CAMERA/cgi-bin/mjpg/video.cgi .

When I try to hit :56700, I am prompted for auth even though it's hardcoded (below). Even when I enter the correct credentials, it fails. What am I doing wrong?

    server {
        listen 56700;
        location / {
            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_pass http://IP_CAMERA/cgi-bin/mjpg/video.cgi;
            proxy_set_header Authorization "Basic xxx";

I've also tried adding proxy_pass_header Authorization; as described here.

CrazyTux avatar
in flag
I think that you have mixed the `rtsp` syntax with the `http` syntax, try `http://<ip>/cgi-bin/realmonitor.cgi?action=getStream&channel=<channelNo>&subtype=<typeNo>` for `http` and then authenticate with username and password OR use `rtsp` with `VLC` with this syntax `rtsp://<username>:<password>@<ip>:<port>/cam/realmonitor?channel=<channelNo>&subtype=<typeNo>`
Kermit avatar
it flag
@CrazyTux I can hit the URL above in my browser without any issue
us flag

As said above it can be solved by

proxy_set_header Authorization "Basic dXNlcjpwYXNzd29yZA==";

where dXNlcjpwYXNzd29yZA== is result of command: echo -n "user:password" | base64

i guess you already tried that

anyway, i believe correct location will looks like

location / {
    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_pass http://IP_CAMERA;
    proxy_set_header Authorization "Basic dXNlcjpwYXNzd29yZA==";
    proxy_set_header Authorization "Basic $http_authorization"; # For interactive mode

without /cgi-bin/mjpg/video.cgi because it will passing at browser or http client side

OR If it doesn't work for you, it can be related to this case

If Camera have some realm checks you can know expected Realm via Dev Tools at Headers tab - Response headers section.

But you will need compile nginx with headers-more-nginx-module

You can make it with this script

cd /usr/src
NGINXFILE=$(wget -qO- | tr ' ' '\n' | egrep -o 'nginx.+?tar.gz' | head -1)
tar zxvf ${NGINXFILE}
cd ${NGINXFILE%.*.*}

cp -r /etc/nginx /root/nginx_$(date +%F) #Backup current nginx configs

cd /usr/src
git clone

./configure --add-module=/usr/src/headers-more-nginx-module --prefix=/etc/nginx --sbin-path=/usr/sbin/nginx --modules-path=/usr/lib64/nginx/modules --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --pid-path=/var/run/ --lock-path=/var/run/nginx.lock --http-client-body-temp-path=/var/cache/nginx/client_temp --http-proxy-temp-path=/var/cache/nginx/proxy_temp --http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp --http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp --http-scgi-temp-path=/var/cache/nginx/scgi_temp --user=nginx --group=nginx --with-compat --with-file-aio --with-threads --with-http_addition_module --with-http_auth_request_module --with-http_dav_module --with-http_flv_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_mp4_module --with-http_random_index_module --with-http_realip_module --with-http_secure_link_module --with-http_slice_module --with-http_ssl_module --with-http_stub_status_module --with-http_sub_module --with-http_v2_module --with-mail --with-mail_ssl_module --with-stream --with-stream_realip_module --with-stream_ssl_module --with-stream_ssl_preread_module --with-cc-opt='-O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches -m64 -mtune=generic -fPIC' --with-ld-opt='-Wl,-z,relro -Wl,-z,now -pie'
make install

once it done you can try such config:

location / {
  proxy_http_version      1.1;
  proxy_pass_request_headers on;
  proxy_set_header        Host            $host;
  proxy_set_header        X-Real-IP       $remote_addr;
  proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;

  more_set_input_headers  'Authorization: Basic dXNlcjpwYXNzd29yZA=='; 
  #more_set_input_headers  'Authorization: $http_authorization'; # For interactive mode
  proxy_set_header  Accept-Encoding  "";

  proxy_pass              http://IP_CAMERA;
  proxy_redirect          default;
  more_set_headers        -s 401 'www-authenticate: Basic realm="Authentication Required"';

where www-authenticate: Basic realm="Authentication Required" your actual data of this header

I checked both cases, it works for me, i tested it on custom flask application. Unfortunatelly i have not such camera for personal debug

cl flag

Edit with Amcrest-specific solution

At some point (around 2017?) Amcrest released a firmware update that removed Basic Authentication from their IP cameras, leaving Digest Authentication as the only option.

This Stack Overflow answer may be your best option for stripping the Digest Authentication out like you want using FastCGI and nginx.

Either that or perhaps you can find a way to downgrade the firmware on your camera to support Basic Authentication again.

Previous Answer

Are you Base64-encoding the username and password with a colon in between?

For HTTP basic authentication, you will need to do the following:

proxy_set_header Authorization "Basic xxx";

Replace the xxx with Base64(<username>:<password>). That is, find yourself a Base-64 encoder, enter the username, a literal colon (:) character, and the password, and replace the xxx with the resulting string.

For example, if the username is admin, and the password is hunter2, we could run the following at a Bash prompt:

printf %s 'admin:hunter2' | base64

and put the resulting string in the header like this:

proxy_set_header Authorization "Basic YWRtaW46aHVudGVyMg==";

Kermit avatar
it flag
The username and password is being Base64 encoded with a `:`, yes.
ne flag

Shot in the dark

I know sometimes if the file permissions are not set correctly you can get a sign-in box which in some cases is for the file permission level and not connection level if that makes sense. Perhaps that's what's happening is some kind of file permission issue on the web server end? You can end up with two different credentials a connection/service based one and then the file level one if the connection/service does not have file permission.

Post an answer

Most people don’t grasp that asking a lot of questions unlocks learning and improves interpersonal bonding. In Alison’s studies, for example, though people could accurately recall how many questions had been asked in their conversations, they didn’t intuit the link between questions and liking. Across four studies, in which participants were engaged in conversations themselves or read transcripts of others’ conversations, people tended not to realize that question asking would influence—or had influenced—the level of amity between the conversationalists.