Not allowed error (405) with drone and gitea

Hello,

I’ve managed to set up drone (version 1.0.0-rc6) with gitea on a Synology NAS. I can access to the web UI and see all my repos there. The problem comes that no matter what I do, the build process never starts.

Here is how I run the drone docker image:

    docker run \
  --volume=/var/run/docker.sock:/var/run/docker.sock \
  --volume=/volume1/docker/drone:/data \
  --env=DRONE_GITEA_SERVER=my_server:33000 \
  --env=DRONE_GIT_ALWAYS_AUTH=false \
  --env=DRONE_RUNNER_CAPACITY=2 \
  --env=DRONE_SERVER_HOST=my_server \
  --env=DRONE_SERVER_PROTO=https \
  --env=DRONE_TLS_AUTOCERT=false \
  --env=DRONE_TLS_CERT=/data/server.crt \
  --env=DRONE_TLS_KEY=/data/server.key \
  --env=DRONE_LOGS_TEXT=false \
  --env=DRONE_LOGS_PRETTY=true \
  --env=DRONE_LOGS_COLOR=true \
  --env=DRONE_LOGS_DEBUG=true \
  --env=DRONE_DATABASE_DRIVER=sqlite3 \
  --env=DRONE_DATABASE_DATASOURCE=/data/database.sqlite \
  --env=DRONE_RUNNER_OS=linux \
  --env=DRONE_RUNNER_ARCH=amd64 \
  --env=DRONE_SERVER_PORT=:33003 \
  --env=DRONE_DATADOG_ENDPOINT=https://stats.drone.ci/api/v1/series \
  --publish=33002:80 \
  --publish=33003:443 \
  --restart=always \
  --detach=true \
  --name=drone \
  drone/drone:1.0.0-rc.6

Here is an example of my .drone.yml:

    kind: pipeline
    name: default

    platform:
      os: linux
      arch: amd64

    steps:
    - name: test
      image: gcc
      commands:
      - make
      privileged: true

And here are the logs:

Gitea logs

[Macaron] 2019-03-09 00:00:05: Started GET /api/v1/repos/user/test/hooks for 172.17.0.1
[Macaron] 2019-03-09 00:00:05: Completed GET /api/v1/repos/user/test/hooks 200 OK in 501.266911ms
[Macaron] 2019-03-09 00:00:05: Started POST /api/v1/repos/user/test/hooks for 172.17.0.1
[Macaron] 2019-03-09 00:00:06: Completed POST /api/v1/repos/user/test/hooks 201 Created in 373.135493ms

Drone logs
{
  "level": "debug",
  "msg": "api: root access granted",
  "name": "test",
  "namespace": "repo",
  "request-id": "xxx",
  "time": "2019-03-09T00:00:05Z",
  "user.admin": true,
  "user.login": "name"
 },
 {
  "level": "debug",
  "msg": "api: root access granted",
  "name": "test",
  "namespace": "repo",
  "request-id": "xxx",
  "time": "2019-03-09T00:00:05Z",
  "user.admin": true,
  "user.login": "name"
 },
{
  "fields.time": "2019-03-09T00:00:06Z",
  "latency": 1031940580,
  "level": "debug",
  "method": "POST",
  "msg": "",
  "remote": "172.17.0.1:59561",
  "request": "/api/repos/repo",
  "request-id": "xxx",
  "time": "2019-03-09T00:00:06Z"
 },
 {
  "fields.time": "2019-03-09T00:00:06Z",
  "latency": 1058216,
  "level": "debug",
  "method": "GET",
  "msg": "",
  "remote": "172.17.0.1:59561",
  "request": "/api/badges/repo/status.svg",
  "request-id": "xxx",
  "time": "2019-03-09T00:00:06Z"
}
Webhook request

Request URL: url
Request method: POST
Content-Type: application/json
X-GitHub-Delivery: cb0a5a7e-8991-4559-89d6-0f07894e1326
X-GitHub-Event: push
X-Gitea-Delivery: cb0a5a7e-8991-4559-89d6-0f07894e1326
X-Gitea-Event: push
X-Gogs-Delivery: cb0a5a7e-8991-4559-89d6-0f07894e1326
X-Gogs-Event: push

{
  "secret": "xxx",
  "ref": "refs/heads/master",
  "before": "4207d8e40f0a56af624fcc2a76ceb37be6646cbf",
  "after": "4207d8e40f0a56af624fcc2a76ceb37be6646cbf",
  "compare_url": "",
  "commits": [
    {
      "id": "4207d8e40f0a56af624fcc2a76ceb37be6646cbf",
      "message": "test\n",
      "url": "url",
      "author": {
        "name": "name",
        "email": "email",
        "username": ""
      },
      "committer": {
        "name": "name",
        "email": "email",
        "username": ""
      },
      "verification": null,
      "timestamp": "0001-01-01T00:00:00Z"
    }
  ],
  "repository": {
    "id": 42,
    "owner": {
      "id": 1,
      "login": "username",
      "full_name": "name",
      "email": "email",
      "avatar_url": "https://secure.gravatar.com/avatar/04ad38c9e35bb1a3d18ff2a5f831b203?d=identicon",
      "language": "es-ES",
      "username": "username"
    },
    "name": "test",
    "full_name": "repo",
    "description": "",
    "empty": false,
    "private": true,
    "fork": false,
    "parent": null,
    "mirror": false,
    "size": 36,
    "html_url": "url",
    "ssh_url": "url",
    "clone_url": "urlt",
    "website": "",
    "stars_count": 0,
    "forks_count": 0,
    "watchers_count": 1,
    "open_issues_count": 0,
    "default_branch": "master",
    "archived": false,
    "created_at": "2019-03-08T23:22:10Z",
    "updated_at": "2019-03-08T23:53:06Z",
    "permissions": {
      "admin": false,
      "push": false,
      "pull": false
    }
  },
  "pusher": {
    "id": 1,
    "login": "username",
    "full_name": "name",
    "email": "email",
    "avatar_url": "https://secure.gravatar.com/avatar/04ad38c9e35bb1a3d18ff2a5f831b203?d=identicon",
    "language": "es-ES",
    "username": "username"
  },
  "sender": {
    "id": 1,
    "login": "username",
    "full_name": "name",
    "email": "email",
    "avatar_url": "https://secure.gravatar.com/avatar/04ad38c9e35bb1a3d18ff2a5f831b203?d=identicon",
    "language": "es-ES",
    "username": "username"
  }
}
Webhook response

Connection: keep-alive
Content-Length: 166
Content-Type: text/html
Date: Sat, 09 Mar 2019 00:30:04 GMT
Keep-Alive: timeout=20
Server: nginx

<head><title>405 Not Allowed</title></head>
<body bgcolor="white">
<center><h1>405 Not Allowed</h1></center>
<hr><center>nginx</center>
</body>
</html>

To me it seems that drone and gitea are working fine, and that there is a problem with nginx (as per the webhook response logs), but I’m not sure about that and, in that case, I don’t know how I can resolve it. I would appreciate if anyone could point me or suggest me how I can solve this 405 Not allowed error.

Thanks in advance.

Yes, I can confirm this is definitely an nginx error. Method not allowed could indicate the hook has the wrong method (e.g. GET instead of POST). We have seen this happen when the proxy auto-redirects from http to https. Gitea obeys the redirect, but the POST turns to a GET.

I am the core Drone maintainer, however, I do not have any nginx experience so I will not be able to help resolve this particular issue. Perhaps you should post your nginx configuration so that other community members, with nginx experience, can take a look and give you feedback.

Thanks for the quick response. Here are my nginx.conf:

Summary
# Copyright (c) 2000-2017 Synology Inc. All rights reserved.

worker_processes        auto;
#worker_cpu_affinity    auto;
worker_rlimit_nofile    65535;

include conf.d/main.conf;

events {
    use             epoll;
    multi_accept    on;
    accept_mutex    off;
    worker_connections 1024;

    include conf.d/events.conf;
}

http {
    include         mime.types;
    default_type    application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
        '$status $body_bytes_sent "$http_referer" '
        '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  off;
    #access_log syslog:server=unix:/dev/log,facility=local7,tag=nginx_access,nohostname main;
    error_log   syslog:server=unix:/dev/log,facility=local7,tag=nginx_error,nohostname error;

    tcp_nopush  on;
    tcp_nodelay on;

    sendfile        on;
    server_tokens   off;

    proxy_request_buffering     off;
    fastcgi_request_buffering   off;
    scgi_request_buffering      off;

    proxy_buffering     off;
    fastcgi_buffering   off;
    scgi_buffering      off;

    resolver_timeout              5s;
    client_header_timeout         10s;
    client_body_timeout           60s;
    send_timeout                  60s;
    keepalive_timeout             65s 20s;
    client_max_body_size          0;
    server_names_hash_max_size    8192;
    server_names_hash_bucket_size 128;

    ssl_certificate           /usr/syno/etc/certificate/system/default/fullchain.pem;
    ssl_certificate_key       /usr/syno/etc/certificate/system/default/privkey.pem;
    ssl_protocols             TLSv1.2;
    ssl_ciphers               xxx;
    ssl_dhparam               /usr/syno/etc/ssl/dh2048.pem;
    ssl_prefer_server_ciphers on;

    ssl_session_tickets       off;
    ssl_session_cache         shared:SSL:1m;
    ssl_session_timeout       3600s;

    real_ip_header            X-Forwarded-For;
    real_ip_recursive         on;
    set_real_ip_from          127.0.0.1;

    include     /var/tmp/nginx/trusted_proxy/*.conf;

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

    server_tag     "nginx";

    gzip_disable    "msie6";
    gzip_min_length 1000;
    gzip_types      text/plain text/css application/javascript application/json;
    gzip_vary       on;
    gzip_static     on;

    open_file_cache          max=1000 inactive=60s;
    open_file_cache_valid    3s;
    open_file_cache_min_uses 2;
    open_file_cache_errors   on;

    upstream synoscgi {
        server unix:/run/synoscgi.sock;
    }

    index index.html index.htm index.php;

    server {
        listen 40310 default_server;
        listen [::]:40310 default_server;

        server_name _;

        gzip on;

        include app.d/alias.*.conf;
        root /usr/syno/synoman;
        index index.cgi;

        ignore_invalid_headers off;

        include app.d/dsm.*.conf;
        include /usr/syno/share/nginx/conf.d/dsm.*.conf;
        include conf.d/dsm.*.conf;

        location = / {
            try_files $uri /index.cgi$is_args$query_string;
        }

        location ~ ^/volume(?:X|USB|SATA|Gluster)?\d+/ {
            internal;

            root /;

            open_file_cache off;

            include app.d/x-accel.*.conf;
            include conf.d/x-accel.*.conf;
        }

        location ~ /webman/modules/(PersonalSettings|ExternalDevices|FileBrowser)/index_ds.php$ {
            alias /usr/syno/share/OAuth/index_ds.php;
            default_type text/html;
        }

        location ~ \.cgi {
            include     scgi_params;
            scgi_pass   synoscgi;

            scgi_read_timeout   3600s;
        }

        error_page 403 404 500 502 503 504 @error_page;

        location @error_page {
            root /usr/syno/share/nginx;
            rewrite (.*) /error.html break;
            allow all;
        }

        location ~ ^/webman/modules/Indexer/ {
            deny all;
        }

        location ~ ^/webapi/lib/ {
            deny all;
        }

        location ~ ^/webapi/(:?(:?.*)\.lib|(:?.*)\.api|(:?.*)\.auth|lib.def)$ {
            deny all;
        }

        location ~ /\. { access_log off; log_not_found off; deny all; }

        location ~* \.(?:js|css|png|jpg|gif|ico)$ {
            access_log off;
            log_not_found off;
        }

        location = /favicon.ico {
            access_log off;
            log_not_found off;
        }

        location = /robots.txt {
            allow all;
            access_log off;
            log_not_found off;
        }

    }

    server {
        listen 40311 default_server ssl http2;
        listen [::]:40311 default_server ssl http2;

        server_name _;

        include app.d/alias.*.conf;
        root /usr/syno/synoman;
        index index.cgi;

        ignore_invalid_headers off;

        include app.d/dsm.*.conf;
        include /usr/syno/share/nginx/conf.d/dsm.*.conf;
        include conf.d/dsm.*.conf;

        location = / {
            try_files $uri /index.cgi$is_args$query_string;
        }

        location ~ ^/volume(?:X|USB|SATA|Gluster)?\d+/ {
            internal;

            root /;

            open_file_cache off;

            include app.d/x-accel.*.conf;
            include conf.d/x-accel.*.conf;
        }

        location ~ /webman/modules/(PersonalSettings|ExternalDevices|FileBrowser)/index_ds.php$ {
            alias /usr/syno/share/OAuth/index_ds.php;
            default_type text/html;
        }

        location ~ \.cgi {
            include     scgi_params;
            scgi_pass   synoscgi;

            scgi_read_timeout   3600s;
        }

        error_page 403 404 500 502 503 504 @error_page;

        location @error_page {
            root /usr/syno/share/nginx;
            rewrite (.*) /error.html break;
            allow all;
        }

        location ~ ^/webman/modules/Indexer/ {
            deny all;
        }

        location ~ ^/webapi/lib/ {
            deny all;
        }

        location ~ ^/webapi/(:?(:?.*)\.lib|(:?.*)\.api|(:?.*)\.auth|lib.def)$ {
            deny all;
        }

        location ~ /\. { access_log off; log_not_found off; deny all; }

        location ~* \.(?:js|css|png|jpg|gif|ico)$ {
            access_log off;
            log_not_found off;
        }

        location = /favicon.ico {
            access_log off;
            log_not_found off;
        }

        location = /robots.txt {
            allow all;
            access_log off;
            log_not_found off;
        }

    }

    server {
        listen 80 default_server;
        listen [::]:80 default_server;

        gzip on;

        server_name _;

        location ~ ^/volume(?:X|USB|SATA|Gluster)?\d+/ {
            internal;

            root /;

            open_file_cache off;

            include app.d/x-accel.*.conf;
            include conf.d/x-accel.*.conf;
        }

        include app.d/www.*.conf;
        include app.d/alias.*.conf;
        include /usr/syno/share/nginx/conf.d/www.*.conf;
        include conf.d/www.*.conf;

        location = /webdefault/images/logo.jpg {
            alias /usr/syno/share/nginx/logo.jpg;
        }

        error_page 403 404 500 502 503 504 @error_page;

        location @error_page {
            root /usr/syno/share/nginx;
            rewrite (.*) /error.html break;
            allow all;
        }

        location ^~ /.well-known/acme-challenge {
            root /var/lib/letsencrypt;
            default_type text/plain;
        }

        include app.d/.location.webstation.conf*;

        location / {
            rewrite ^ / redirect;
        }

        location ~ ^/$ {
            rewrite / http://$host:40310/ redirect;
        }
    }

    server {
        listen 443 default_server ssl;
        listen [::]:443 default_server ssl;

        server_name _;

        location ~ ^/volume(?:X|USB|SATA|Gluster)?\d+/ {
            internal;

            root /;

            open_file_cache off;

            include app.d/x-accel.*.conf;
            include conf.d/x-accel.*.conf;
        }

        include app.d/www.*.conf;
        include app.d/alias.*.conf;
        include /usr/syno/share/nginx/conf.d/www.*.conf;
        include conf.d/www.*.conf;

        location = /webdefault/images/logo.jpg {
            alias /usr/syno/share/nginx/logo.jpg;
        }

        error_page 403 404 500 502 503 504 @error_page;

        location @error_page {
            root /usr/syno/share/nginx;
            rewrite (.*) /error.html break;
            allow all;
        }

        location ^~ /.well-known/acme-challenge {
            root /var/lib/letsencrypt;
            default_type text/plain;
        }

        include app.d/.location.webstation.conf*;

        location / {
            rewrite ^ / redirect;
        }

        location ~ ^/$ {
            rewrite / https://$host:40311/ redirect;
        }
    }

    include conf.d/http.*.conf;
    include app.d/server.*.conf;
    include sites-enabled/*;
}

Hi @bradrydzewski, I’ve managed to make it work.
The address generated in the hook is not correct as it is lacking the port. If I add the port manually it works fine.
Do you know if this is due to something I’m lacking in my configuration?

the webhook is created using these variables:

DRONE_SERVER_HOST
DRONE_SERVER_PROTO

they should be set to the external address of your webserver (dns, ip, etc) not the internal or localhost address.

Yes, I have the variables
DRONE_SERVER_HOST=my.domain.com
DRONE_SERVER_PROTO=https

but I also use a port for accessing the drone external host. Thus, in order to access drone I write: https://my.domain.com:30000

The created webhook address ends up something like https://my.domain.com/hook… instead of https://my.domain.com:30000/hook
So, it seems DRONE_SERVER_PORT is not used to create the webhook address. Is this right?

if the external host includes a port, it should be included in DRONE_SERVER_HOST as well. DRONE_SERVER_HOST=foo:9000

I thought the DRONE_SERVER_HOST did not include the port. My mistake! :sweat:
This solves the problem. Thanks a lot!

Hi all,

I have the same situation on two different servers. I don’t think that it has to do with the reverse proxy as the behaviour randomly seem to change. I had the hook working on the local IP with exposed port.

and at some point it stopped working.

Calling up the webhook URL manually (even via IP with exposed port) in chrome throws a 405.

Via docker logs drone I dont see any relevant information. Can you guide me to find the root cause of this?

Thanks.

the most common root cause for what you are describing is when the hook is being sent to an http address, when it should be https. The http is auto-redirected from http to https, causing the http request to change from a GET to a POST, thus resulting in the 405 method not allowed because Drone expects webhooks to be a POST.