# vim: ft=nginx

keepalive_timeout 5;

if ($host = "www.quay.io") {
    return 301 $proper_scheme://quay.io$request_uri;
}

# Disable the ability to be embedded into iframes
add_header X-Frame-Options DENY;


# Proxy Headers
proxy_set_header X-Forwarded-For $proper_forwarded_for;
proxy_set_header X-Forwarded-Proto $proper_scheme;
proxy_set_header Host $host;
proxy_redirect off;

proxy_set_header Transfer-Encoding $http_transfer_encoding;

location / {
    proxy_pass   http://web_app_server;
}

location /push {
    proxy_pass   http://web_app_server;
    client_max_body_size 5M;
}

location /realtime {
    proxy_pass   http://web_app_server;
    proxy_buffering off;
    proxy_request_buffering off;
}

location ~ ^/_storage_proxy/([^/]+)/([^/]+)/([^/]+)/(.+) {
    include resolver.conf;

    auth_request /_storage_proxy_auth;

    proxy_pass $2://$3/$4$is_args$args;

    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $3;
    proxy_set_header Authorization "";

    add_header Host $3;

    proxy_buffering off;
    proxy_request_buffering off;

    proxy_read_timeout 60s;
}

location = /_storage_proxy_auth {
    proxy_pass http://web_app_server;
    proxy_pass_request_body off;
    proxy_set_header Content-Length "";

    proxy_set_header X-Original-URI $request_uri;

    proxy_read_timeout 10;
}

location ~ ^/v2/_catalog(.*)$ {
    proxy_pass http://registry_app_server;
    proxy_read_timeout 10;
    keepalive_timeout 0; # Disables HTTP 1.1 keep-alive and forces round-robin.

    {% if enable_rate_limits %}
    limit_req zone=dynamicauth_heavy_http1 burst=1 nodelay;
    limit_req zone=dynamicauth_heavy_http2 burst=5 nodelay;
    {% endif %}
}

location /secscan/ {
    proxy_pass http://secscan_app_server;
}

{% if signing_enabled %}
location ~ ^/v2/(.+)/_trust/tuf/(.*)$ {
  set $upstream_tuf {{ tuf_server }};
  proxy_pass $upstream_tuf$uri;
  proxy_set_header Host "{{ tuf_host }}";
}
{% endif %}

location /cnr {
    proxy_buffering off;

    proxy_request_buffering off;

    proxy_pass http://registry_app_server;
    proxy_read_timeout 120;
    proxy_temp_path /tmp 1 2;

    client_max_body_size {{ maximum_cnr_layer_size }};

    {% if enable_rate_limits %}
    limit_req zone=staticauth burst=5 nodelay;
    {% endif %}
}

location /api/ {
    proxy_pass   http://web_app_server;

    {% if enable_rate_limits %}
    limit_req zone=dynamicauth_heavy_http1 burst=25 nodelay;
    limit_req zone=dynamicauth_heavy_http2 burst=100 nodelay;
    {% endif %}

    keepalive_timeout 0; # Disables HTTP 1.1 keep-alive and forces round-robin.
}

location /api/suconfig {
    proxy_pass   http://web_app_server;

    # For suconfig, set our read timeout as super large for both DB migrations
    # and awaiting for secrets to be updated.
    proxy_read_timeout 2000;
}

# This block handles blob requests, and will receive a high volume of traffic, so we set the burst
# much higher.
location ~ /v2/([^/]+)\/[^/]+/blobs/ {
    # If we're being accessed via v1.quay.io, pretend we don't support v2.
    if ($host = "v1.quay.io") {
        return 404;
    }

    # NOTE: We disable gzip for HEAD requests because Docker issues them to determine the Content
    # Length of a blob. Unfortunately, nginx, seeing an empty body, overwrites the header with
    # a length of 0, which breaks this functionality.
    if ($request_method = HEAD) {
        gzip off;
    }

    proxy_buffering off;
    proxy_request_buffering off;
    proxy_read_timeout 2000;
    proxy_temp_path /tmp 1 2;

    client_max_body_size {{ maximum_layer_size }};

    # Setting ANY header clears all inherited proxy_set_header directives
    proxy_set_header X-Forwarded-For $proper_forwarded_for;
    proxy_set_header X-Forwarded-Proto $proper_scheme;
    proxy_set_header Host $host;

    proxy_http_version 1.1;

    proxy_pass   http://registry_app_server;

    set $namespace $1;

    {% if enable_rate_limits %}
    limit_req zone=namespaced_dynamicauth_light_http1 burst=50 nodelay;
    limit_req zone=namespaced_dynamicauth_light_http2 burst=100 nodelay;
    {% endif %}

    keepalive_timeout 0; # Disables HTTP 1.1 keep-alive and forces round-robin.
}

# This block handles tags endpoint requests, for which we want to restrict traffic due to how
# heavy an operation it can be
location ~ /v2/([^/]+)\/[^/]+/tags/ {
    # If we're being accessed via v1.quay.io, pretend we don't support v2.
    if ($host = "v1.quay.io") {
        return 404;
    }

    # Setting ANY header clears all inherited proxy_set_header directives
    proxy_set_header X-Forwarded-For $proper_forwarded_for;
    proxy_set_header X-Forwarded-Proto $proper_scheme;
    proxy_set_header Host $host;

    proxy_http_version 1.1;

    proxy_pass   http://registry_app_server;

    set $namespace $1;

    {% if enable_rate_limits %}
    limit_req zone=namespaced_dynamicauth_heavy_http1 burst=2 nodelay;
    limit_req zone=namespaced_dynamicauth_heavy_http2 burst=2 nodelay;
    {% endif %}

    keepalive_timeout 0; # Disables HTTP 1.1 keep-alive and forces round-robin.
}

# This block handles manifests endpoint requests, for which we want to restrict traffic heavier than
# the generic V2 operations, as it handles pushes and pulls.
location ~ /v2/([^/]+)\/[^/]+/manifests/ {
    # If we're being accessed via v1.quay.io, pretend we don't support v2.
    if ($host = "v1.quay.io") {
        return 404;
    }

    # Setting ANY header clears all inherited proxy_set_header directives
    proxy_set_header X-Forwarded-For $proper_forwarded_for;
    proxy_set_header X-Forwarded-Proto $proper_scheme;
    proxy_set_header Host $host;

    proxy_http_version 1.1;

    proxy_pass   http://registry_app_server;

    set $namespace $1;

    {% if enable_rate_limits %}
    limit_req zone=namespaced_dynamicauth_light_http1 burst=10 nodelay;
    limit_req zone=namespaced_dynamicauth_light_http2 burst=50 nodelay;
    {% endif %}

    keepalive_timeout 0; # Disables HTTP 1.1 keep-alive and forces round-robin.
}

# This block applies to the beginning of a push or pull
location = /v2/auth {
    # If we're being accessed via v1.quay.io, pretend we don't support v2.
    if ($host = "v1.quay.io") {
        return 404;
    }

    # Setting ANY header clears all inherited proxy_set_header directives
    proxy_set_header X-Forwarded-For $proper_forwarded_for;
    proxy_set_header X-Forwarded-Proto $proper_scheme;
    proxy_set_header Host $host;

    proxy_http_version 1.1;

    proxy_pass   http://registry_app_server;

    {% if enable_rate_limits %}
    limit_req zone=staticauth burst=2 nodelay;
    {% endif %}

    keepalive_timeout 0; # Disables HTTP 1.1 keep-alive and forces round-robin.
}

# This block handles all other V2 requests, for which we can use a higher rate limit.
location ~ ^/v2 {
    # If we're being accessed via v1.quay.io, pretend we don't support v2.
    if ($host = "v1.quay.io") {
        return 404;
    }

    # NOTE: We disable gzip for HEAD requests because Docker issues them to determine the Content
    # Length of a blob. Unfortunately, nginx, seeing an empty body, overwrites the header with
    # a length of 0, which breaks this functionality. Included here for completeness.
    if ($request_method = HEAD) {
        gzip off;
    }

    # Setting ANY header clears all inherited proxy_set_header directives
    proxy_set_header X-Forwarded-For $proper_forwarded_for;
    proxy_set_header X-Forwarded-Proto $proper_scheme;
    proxy_set_header Host $host;

    proxy_http_version 1.1;

    proxy_pass   http://registry_app_server;

    {% if enable_rate_limits %}
    limit_req zone=dynamicauth_very_light_http1 burst=20 nodelay;
    limit_req zone=dynamicauth_very_light_http2 burst=80 nodelay;
    {% endif %}

    keepalive_timeout 0; # Disables HTTP 1.1 keep-alive and forces round-robin.
}

location /v1/ {
    # Setting ANY header clears all inherited proxy_set_header directives
    proxy_set_header X-Forwarded-For $proper_forwarded_for;
    proxy_set_header X-Forwarded-Proto $proper_scheme;
    proxy_set_header Host $host;

    proxy_buffering off;

    proxy_request_buffering off;

    proxy_http_version 1.1;

    proxy_pass   http://registry_app_server;
    proxy_temp_path /tmp 1 2;

    client_max_body_size {{ maximum_layer_size }};

    {% if enable_rate_limits %}
    limit_req zone=dynamicauth_heavy_http1 burst=5 nodelay;
    limit_req zone=dynamicauth_heavy_http2 burst=25 nodelay;
    {% endif %}

    keepalive_timeout 0; # Disables HTTP 1.1 keep-alive and forces round-robin.
}

location = /v1/_ping {
    add_header Content-Type text/plain;
    add_header X-Docker-Registry-Version 0.6.0;
    add_header X-Docker-Registry-Standalone 0;
    return 200 'true';
}

location /static/ {
    # checks for static file, if not found proxy to app
    alias      {{static_dir}}/;
    error_page 404 /404;
}

error_page 502 {{static_dir}}/502.html;
