跳转至

为什么 Gitlab 配置项 allowed_hosts 不能使用通配符 ["*"]

1. 问题发现与背景

用户上传头像报错 Request failed with status code 403

blog_2025_04_01_light blog_2025_04_01_dark

2. 排查

2.1. 对比正常使用的GitLab实例配置

gitlab.rb
### Allowed hosts
###! Customize the `host` headers that should be catered by the Rails
###! application. By default, everything is allowed.
# gitlab_rails['allowed_hosts'] = []
gitlab.rb
### Allowed hosts
###! Customize the `host` headers that should be catered by the Rails
###! application. By default, everything is allowed.
gitlab_rails['allowed_hosts'] = ["*"]

2.2. 确定是 allowed_hosts 参数问题

在配置文件里进行注释掉后,重启服务。上传头像恢复正常。

blog_2025_04_02_light blog_2025_04_02_dark

3. 找寻原因

3.1. 是否是nginx配置

发现并没有对 host 相关参数的要求。

/var/opt/gitlab/nginx/
root@gitlab:/var/opt/gitlab/nginx# ll
total 48
drwxr-x--- 10 root       gitlab-www 4096 Apr 15 19:04 ./
drwxr-xr-x 16 root       root       4096 Apr 15 19:03 ../
drwx------  2 gitlab-www root       4096 Apr 18 15:20 client_body_temp/
drwxr-x---  2 root       gitlab-www 4096 Apr 15 19:04 conf/
drwx------  2 gitlab-www root       4096 Jan  6 19:18 fastcgi_temp/
lrwxrwxrwx  1 root       root         21 Jan  6 19:17 logs -> /var/log/gitlab/nginx/
-rw-r--r--  1 root       root          8 Apr  7 10:01 nginx.pid
drwx------  2 gitlab-www root       4096 Jan  6 19:18 proxy_cache/
drwx------ 12 gitlab-www root       4096 Feb 21 09:57 proxy_temp/
drwx------  2 gitlab-www root       4096 Jan  6 19:18 scgi_temp/
drwx------  2 gitlab-www root       4096 Jan  6 19:18 uwsgi_temp/
-rw-r--r--  1 root       root         28 Mar 16 11:38 VERSION
drwxr-xr-x  3 root       root       4096 Jan  6 19:18 www/
conf/nginx.conf
# This file is managed by gitlab-ctl. Manual changes will be
# erased! To change the contents below, edit /etc/gitlab/gitlab.rb
# and run `sudo gitlab-ctl reconfigure`.

user gitlab-www gitlab-www;
worker_processes 4;
error_log stderr;
pid nginx.pid;

daemon off;

events {
  worker_connections 10240;
}

http {
  log_format gitlab_access '$remote_addr - $remote_user [$time_local] "$request_method $filtered_request_uri $server_protocol" $status $body_bytes_sent "$filtered_http_referer" "$http_user_agent" $gzip_ratio';
  log_format gitlab_mattermost_access '$remote_addr - $remote_user [$time_local] "$request_method $filtered_request_uri $server_protocol" $status $body_bytes_sent "$filtered_http_referer" "$http_user_agent" $gzip_ratio';

  hide_server_tokens off;

  server_names_hash_bucket_size 64;

  sendfile on;
  tcp_nopush on;
  tcp_nodelay on;

  keepalive_timeout 65;
  keepalive_time 1h;

  gzip on;
  gzip_http_version 1.1;
  gzip_comp_level 2;
  gzip_proxied no-cache no-store private expired auth;
  gzip_types text/plain text/css application/x-javascript text/xml application/xml application/xml+rss text/javascript application/json;

  include /opt/gitlab/embedded/conf/mime.types;

  proxy_cache_path proxy_cache keys_zone=gitlab:10m max_size=1g levels=1:2;
  proxy_cache gitlab;

  map $http_upgrade $connection_upgrade {
      default upgrade;
      ''      close;
  }

  # Remove private_token from the request URI
  # In:  /foo?private_token=unfiltered&authenticity_token=unfiltered&rss_token=unfiltered&...
  # Out: /foo?private_token=[FILTERED]&authenticity_token=unfiltered&rss_token=unfiltered&...
  map $request_uri $temp_request_uri_1 {
    default $request_uri;
    ~(?i)^(?<start>.*)(?<temp>[\?&]private[\-_]token)=[^&]*(?<rest>.*)$ "$start$temp=[FILTERED]$rest";
  }

  # Remove authenticity_token from the request URI
  # In:  /foo?private_token=[FILTERED]&authenticity_token=unfiltered&rss_token=unfiltered&...
  # Out: /foo?private_token=[FILTERED]&authenticity_token=[FILTERED]&rss_token=unfiltered&...
  map $temp_request_uri_1 $temp_request_uri_2 {
    default $temp_request_uri_1;
    ~(?i)^(?<start>.*)(?<temp>[\?&]authenticity[\-_]token)=[^&]*(?<rest>.*)$ "$start$temp=[FILTERED]$rest";
  }

  # Remove rss_token from the request URI
  # In:  /foo?private_token=[FILTERED]&authenticity_token=[FILTERED]&rss_token=unfiltered&...
  # Out: /foo?private_token=[FILTERED]&authenticity_token=[FILTERED]&rss_token=[FILTERED]&...
  map $temp_request_uri_2 $filtered_request_uri {
    default $temp_request_uri_2;
    ~(?i)^(?<start>.*)(?<temp>[\?&]rss[\-_]token)=[^&]*(?<rest>.*)$ "$start$temp=[FILTERED]$rest";
  }

  # A version of the referer without the query string
  map $http_referer $filtered_http_referer {
    default $http_referer;
    ~^(?<temp>.*)\? $temp;
  }

  # Enable vts status module.
  vhost_traffic_status_zone;

  upstream gitlab-workhorse {
    server unix:/var/opt/gitlab/gitlab-workhorse/sockets/socket;
  }

  include /var/opt/gitlab/nginx/conf/gitlab-http.conf;






  include /var/opt/gitlab/nginx/conf/nginx-status.conf;


}
conf/gitlab-http.conf
# This file is managed by gitlab-ctl. Manual changes will be
# erased! To change the contents below, edit /etc/gitlab/gitlab.rb
# and run `sudo gitlab-ctl reconfigure`.

## GitLab
## Modified from https://gitlab.com/gitlab-org/gitlab-foss/blob/master/lib/support/nginx/gitlab-ssl & https://gitlab.com/gitlab-org/gitlab-foss/blob/master/lib/support/nginx/gitlab
##
## Lines starting with two hashes (##) are comments with information.
## Lines starting with one hash (#) are configuration parameters that can be uncommented.
##
##################################
##        CHUNKED TRANSFER      ##
##################################
##
## It is a known issue that Git-over-HTTP requires chunked transfer encoding [0]
## which is not supported by Nginx < 1.3.9 [1]. As a result, pushing a large object
## with Git (i.e. a single large file) can lead to a 411 error. In theory you can get
## around this by tweaking this configuration file and either:
## - installing an old version of Nginx with the chunkin module [2] compiled in, or
## - using a newer version of Nginx.
##
## At the time of writing we do not know if either of these theoretical solutions works.
## As a workaround users can use Git over SSH to push large files.
##
## [0] https://git.kernel.org/cgit/git/git.git/tree/Documentation/technical/http-protocol.txt#n99
## [1] https://github.com/agentzh/chunkin-nginx-module#status
## [2] https://github.com/agentzh/chunkin-nginx-module
##
###################################
##         configuration         ##
###################################


server { ## HTTPS server
  listen *:443 ssl http2;


  server_name gitlab-lovelink.qiu-ai.com;
  server_tokens off; ## Don't show the nginx version number, a security best practice

  ## Increase this if you want to upload large attachments
  ## Or if you want to accept large git objects over http
  client_max_body_size 25000m;

  ## Strong SSL Security
  ## https://raymii.org/s/tutorials/Strong_SSL_Security_On_nginx.html & https://cipherli.st/
  ssl_certificate /etc/gitlab/ssl/gitlab-lovelink.qiu-ai.com.crt;
  ssl_certificate_key /etc/gitlab/ssl/gitlab-lovelink.qiu-ai.com.key;

  # GitLab needs backwards compatible ciphers to retain compatibility with Java IDEs
  ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384';
  ssl_protocols  TLSv1.2 TLSv1.3;
  ssl_prefer_server_ciphers off;
  ssl_session_cache  shared:SSL:10m;
  ssl_session_tickets off;
  ssl_session_timeout  1d;



  ## Real IP Module Config
  ## http://nginx.org/en/docs/http/ngx_http_realip_module.html

  ## HSTS Config
  ## https://www.nginx.com/blog/http-strict-transport-security-hsts-and-nginx/
  add_header Strict-Transport-Security "max-age=63072000" always;

  # Rails sets a default policy of strict-origin-when-cross-origin, so
  # hide that and just send the one we've configured for nginx
  proxy_hide_header Referrer-Policy;
  add_header Referrer-Policy strict-origin-when-cross-origin;

  ## Individual nginx logs for this GitLab vhost
  access_log  /var/log/gitlab/nginx/gitlab_access.log gitlab_access;
  error_log   /var/log/gitlab/nginx/gitlab_error.log error;

  if ($http_host = "") {
    set $http_host_with_default "gitlab-lovelink.qiu-ai.com";
  }

  if ($http_host != "") {
    set $http_host_with_default $http_host;
  }

  gzip on;
  gzip_static on;
  gzip_comp_level 2;
  gzip_http_version 1.1;
  gzip_vary on;
  gzip_disable "msie6";
  gzip_min_length 250;
  gzip_proxied no-cache no-store private expired auth;
  gzip_types text/plain text/css application/x-javascript text/xml application/xml application/xml+rss text/javascript application/json;

  ## https://github.com/gitlabhq/gitlabhq/issues/694
  ## Some requests take more than 30 seconds.
  proxy_read_timeout      3600;
  proxy_connect_timeout   300;
  proxy_redirect          off;
  proxy_http_version 1.1;

  proxy_set_header Host $http_host_with_default;
  proxy_set_header X-Real-IP $remote_addr;
  proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  proxy_set_header Upgrade $http_upgrade;
  proxy_set_header Connection $connection_upgrade;
  proxy_set_header X-Forwarded-Ssl on;
  proxy_set_header X-Forwarded-Proto https;

  location ~ (/api/v\d/jobs/\d+/artifacts$|/import/gitlab_project$|\.git/git-receive-pack$|\.git/ssh-receive-pack$|\.git/ssh-upload-pack$|\.git/gitlab-lfs/objects|\.git/info/lfs/objects/batch$) {
    proxy_cache off;
    proxy_pass http://gitlab-workhorse;
    proxy_request_buffering off;
  }

  location ~ ^/api/v\d {
    proxy_cache off;
    proxy_pass http://gitlab-workhorse;
    proxy_intercept_errors off;
  }

  location = /-/kubernetes-agent/ {
    proxy_pass http://localhost:8150/;
    proxy_intercept_errors off;
  }

  location /-/kubernetes-agent/k8s-proxy/ {
    proxy_pass http://localhost:8154/;
    proxy_buffering off;
    proxy_intercept_errors off;
  }

  # health checks configuration
  include /var/opt/gitlab/nginx/conf/gitlab-health.conf;


  location / {
    proxy_cache off;
    proxy_pass  http://gitlab-workhorse;
  }

  location /assets {
    add_header X-Content-Type-Options nosniff;
    proxy_cache gitlab;
    proxy_pass  http://gitlab-workhorse;
  }

  error_page 404 /404.html;
  error_page 500 /500.html;
  error_page 502 /502.html;
  location ~ ^/(404|500|502)(-custom)?\.html$ {
    root /opt/gitlab/embedded/service/gitlab-rails/public;
    internal;
  }


} ## end HTTPS server

3.2. 源代码

3.2.1. 源代码仓库搜索

config/initializers/1_settings.rb
Settings.gitlab['content_security_policy'] ||= {}

Settings.gitlab['allowed_hosts'] ||= []
Settings.gitlab['impersonation_enabled'] ||= true if Settings.gitlab['impersonation_enabled'].nil?
config/initializers/rails_host_authorization.rb
4
5
6
7
if Gitlab.config.gitlab.allowed_hosts.present?
  Rails.application.config.hosts << Gitlab.config.gitlab.host << 'unix'
  Rails.application.config.hosts += Gitlab.config.gitlab.allowed_hosts
end

3.2.2. 发现与 Ruby on Rails 框架配置有关

🎯 找到返回 403 的原因:当请求发送到未经授权的主机时,默认的 Rack 应用程序将运行并响应 403 Forbidden (不支持使用 *)。

更多详情请参考:3.5.1. ActionDispatch::HostAuthorization

4. 总结

  1. 后端 Ruby on Rails 框架延续使用了 gitlab_rails['allowed_hosts'] 配置的值
  2. Ruby on Rails 框架不支持使用 ["*"]

5. 正确设置

1. 修改gitlab.rb
# 快捷(不更改默认配置)
# gitlab_rails['allowed_hosts'] = []

# 常规
gitlab_rails['allowed_hosts'] = ['gitlab.example.com']

# 如果有外部代理
gitlab_rails['allowed_hosts'] = ['gitlab.example.com', '127.0.0.1', 'localhost']
2. 重载配置
sudo gitlab-ctl reconfigure