LINUX.ORG.RU

Как исправить ошибку CORS?

 , , , ,


0

1

В локальной сети между машинами, а также на одной машине в режиме отладки, делаю POST запросы XMLHttpRequest для выгрузки файла на второй сервер.

Со страницы https://xxx (, где ключи и сертификат самоподписанные (уровня 1). Тестовый сервер Nginx, в продакшене будет другой, но там такие же ошибки.)

на сервер https://upserver:8888 (, где ключи и сертификаты самоподписанные (уровня 2, с использованием собственного центра сертификации, корневых, промежуточных и конечных сертификатов) На порту 8888 работает Cowboy (Erlang/OTP v.23). Код с заголовками показан ниже.) Пока, там нет авторизации, а кросс-доменные запросы разрешены для всех (*).

Оба домена прописаны в hosts. В продакшене будут прописаны в DNS. Разрешения CORS установлены на обоих серверах, код показан ниже.

При первом открытии index.html на обоих серверах в браузере Chrome получаю ошибку сертификата, но после разрешения ресурса, страница открывается и далее работает. Хотелось бы чтобы она открывалась без этой ошибки и необходимости разрешения.

При загрузке файла на второй сервер в логе браузера (вкладка console) получаю следующие ошибки

Access to XMLHttpRequest at 'https://upserver:8888/upload' from origin 'https://xxx' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

upload.js:73 onreadystatechange Error 0 occurred when trying to upload your file.

upload.js:78 POST https://upserver:8888/upload net::ERR_FAILED
fileUpload @ upload.js:78
(anonymous) @ upload.js:103

upload.js:78 XHR failed loading: POST "https://upserver:8888/upload".
fileUpload @ upload.js:78
(anonymous) @ upload.js:103

Но файл успешно загружается на сервер. Не могу понять, что ему еще надо, и как исправить эту CORS ошибку?

Код заголовков на upserver:8888

options(Req, Opts) ->
    Req1 = cowboy_req:set_resp_header(<<"access-control-allow-methods">>, <<"HEAD, OPTIONS, GET, PUT, POST">>, Req),
    Req2 = cowboy_req:set_resp_header(<<"access-control-allow-headers">>, <<"Origin, X-Requested-With, Content-Type, 
    Accept, x-client-key, x-client-token, x-client-secret">>, Req1),
    Req3 = cowboy_req:set_resp_header(<<"access-control-allow-origin">>, <<$*>>, Req2),
    {ok, Req3, Opts}.

init(Req, Opts) ->
    case cowboy_req:method(Req) of
        <<"POST">> ->
	   {ok, Req1, Opts1} = options(Req, Opts),
           {ok, Headers, Req2} = cowboy_req:read_part(Req1),
    	   {ok, Data, Req3} = cowboy_req:read_part_body(Req2),
    	   {file, <<"upfile">>, Filename, ContentType} = cow_multipart:form_data(Headers),
    	   io:format("Received file ~p of content-type ~p as follow:~n~p~n~n", [Filename, ContentType, Data]),
    	   Filepath = filename:join([code:priv_dir("upserver"), "upload", Filename]),
    	   file:write_file(Filepath, Data),
    	   {ok, Req3, Opts1};
        _ ->
    	   io:format("Method=~p~n",[cowboy_req:method(Req)]),
	   options(Req, Opts)
    end.

Код запроса на клиенте

function fileUpload(blob){
    var filename="test.png";
    var file=new File([blob],filename,{type:"image/png"});
    var formData = new FormData();
    formData.append('upfile', file, filename);
    const xhr = new XMLHttpRequest();
    xhr.fileinfo = {'filename': filename};
    xhr.enctype = "multipart/form-data";
    xhr.overrideMimeType('multipart/form-data');

// эта функция работает без ошибок и сообщений
    xhr.open("POST", "https://upserver:8888/upload", true);

// Это не помогло, бесполезно, поэтому закоментировано
// xhr.setRequestHeader("Access-Control-Allow-Origin","https://upserver:8888");
// xhr.setRequestHeader("Access-Control-Request-Header","X-Requested-With");


// Это сообщение показано в логе, смотрите лог, Error = xhr.status = 0, вместо 200
    xhr.onreadystatechange = function() {
	if (xhr.status == 200) {
	    console.log("onreadystatechange Uploaded!");
	} else {
	    console.log("onreadystatechange Error " + xhr.status + " occurred when trying to upload your file.");
	}
    };

// здесь выдается сообщение об ошибке CORS
    xhr.send(formData);

// В результате, formData успешно передается и правильно сохраняется на сервере, несмотря на все эти ошибки!

}

На стороне первого сервера https://xxx в конфигурации сайта под Nginx прописана следующая конфигурация

server {
        listen 443 ssl;
        listen [::]:443 ssl;
configuration.
        root /var/www/https;
        index index.html index.htm index.nginx-debian.html;

        server_name xxx;
        ssl_certificate /etc/nginx/xxx-list.crt;
        ssl_certificate_key /etc/nginx/xxx.key;
location / {
     if ($request_method = 'OPTIONS') {
        add_header 'Access-Control-Allow-Origin' 'https://yyy:8888';
        add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
        add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';

        add_header 'Access-Control-Max-Age' 1728000;
        add_header 'Content-Type' 'text/plain charset=UTF-8';
        add_header 'Content-Length' 0;
        return 204;
     }
     if ($request_method = 'POST') {
        add_header 'Access-Control-Allow-Origin' 'https://upserver:8888';
#        add_header 'Access-Control-Allow-Credentials' 'true';
        add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
        add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';
     }
     if ($request_method = 'GET') {
        add_header 'Access-Control-Allow-Origin' '*';
#        add_header 'Access-Control-Allow-Credentials' 'true';
        add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
        add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';
     }
     try_files $uri $uri/ =404;
}
Видно, что кросс-доменные запросы разрешены для второго сервера upserver:8888

Вроде бы, везде всё настроено белее менее правильно, но ошибки мне очень не нравятся. Хотелось бы разобраться, что не так, и избавиться от ошибок.

Ответ на: комментарий от Anoxemian

CORS - очень распространенная проблема, ничего дичайшего. Особенно раздражает для локальных приложений. Тестовый пример тривиальный - простой клиент на JavaScript и минимальный сервер на Erlang/Cowboy. В каком месте не понятно, я поясню?

MariaRTI ()
Ответ на: комментарий от LamerOk

В продакшене домены будут прописаны в DNS, hosts - временное решение. Проблема не в этом. В том смысле, что они в любом варианте разные и CORS не исчезнет.

MariaRTI ()
Последнее исправление: MariaRTI (всего исправлений: 1)
Ответ на: комментарий от Miguel

статус запроса - CORS error, type xhr

Request URL: https://upserver:8888/upload
Referrer Policy: strict-origin-when-cross-origin
:authority: upserver:8888
:method: POST
:path: /upload
:scheme: https
accept: */*
accept-encoding: gzip, deflate, br
accept-language: ru-RU,ru;q=0.9,en-US;q=0.8,en;q=0.7
content-length: 8352
content-type: multipart/form-data; boundary=----WebKitFormBoundaryVAdICtoeBOYBlIUi
origin: https://xxx
referer: https://xxx/
sec-ch-ua: "Google Chrome";v="93", " Not;A Brand";v="99", "Chromium";v="93"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Linux"
sec-fetch-dest: empty
sec-fetch-mode: cors
sec-fetch-site: cross-site
user-agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.82 Safari/537.36
upfile: (binary)

Возвращается xhr.status = 0, который, в частности, устанавливается при CORS - ошибках.

оттуда же взял fetch-лог, если из этого что-то можно понять? Код передаваемого файла я подрезал, т.к. он слишком большой для поста. И повторю, этот файл передается, принимается и сохраняется на сервере, несмотря на ошибку CORS и сообщение что запрос якобы заблокирован... Вот это и непонятно.

fetch("https://upserver:8888/upload", {
  "headers": {
    "accept": "*/*",
    "accept-language": "ru-RU,ru;q=0.9,en-US;q=0.8,en;q=0.7",
    "content-type": "multipart/form-data; boundary=----WebKitFormBoundaryVAdICtoeBOYBlIUi",
    "sec-ch-ua": "\"Google Chrome\";v=\"93\", \" Not;A Brand\";v=\"99\", \"Chromium\";v=\"93\"",
    "sec-ch-ua-mobile": "?0",
    "sec-ch-ua-platform": "\"Linux\"",
    "sec-fetch-dest": "empty",
    "sec-fetch-mode": "cors",
    "sec-fetch-site": "cross-site"
  },
  "referrer": "https://xxx/",
  "referrerPolicy": "strict-origin-when-cross-origin",
  "body": "------WebKitFormBoundaryVAdICtoeBOYBlIUi\r\nContent-Disposition: form-data; name=\"upfile\"; filename=\"127.0.0.1/2021.11.22/127.0.0.1-user/127.0.0.1_2021.11.22_[127.0.0.1-user]_12:07:20.092.png\"\r\nContent-Type: image/png\r\n\r\n‰PNG\r\n\u001a\n\u0000\u0000\u0000\rIHDR\u0000\u0000\u0007€\u0000\u0000\u0000–\b\u0006\u0000\u0000\u0000?Š•Î\u0000\u0000\u0000\u0001sRGB\u0000®Î\u001cé\u0000\u0000\u001fQIDATx^íÝ{Ì×ãÿ\u0007ðË\u001cf”ãœ\u000f‰þÉ99S\u0018“г°°\u000esL\u000e‰‰9\u0015¢\u001cW‰0Ç9渜Â0’ÃJ3DŽŒ\u001crÌ!Q¿½®í¾w¯Ý¾¹ï_Šëz¼·Öî>‡÷ûõxÞýõÜu]K̛7o^r\u0011 @€\u0000\u0001\u0002\u0004\b\u0010 @€\u0000\u0001\u0002\u0004\b\u0010 @€\u0000\u0001\u0002\u0004\b\u0010h½@‹\nà5×\\3-·Üry…íܹsS\u0014”±½ñȑ#ÓYg•6ÜpÃÔ¾}ûtþùçç3xŸ}öÙ´÷Þ{§½öÚ+-»ì²©_¿~y…p|6ζÝh£R¬rÝl³Íò\u0004—\\rIzñÅ\u0017S›6mrq\u001bçàÆ=ã»b+å)S¦¤¶mÛæ÷þúë¯yûçgžy¦qú‹/¾8E©\u0019÷‹ò1¶cîÚµkš4iRš3gNêÝ»w:ûì³Ówß}—Ž9æ˜ôø㏧þýû§‡\u001f~8}ýõ×ùû¢TŽÕÃÏ?ÿ|Z}õÕÓý÷ߟb;績úê«tꩧ¦/¿ü2¿\u0016ÛKŸ~úéé°Ã\u000e˯7½ÿСCÓ\u0005\u0017\\®½öÚ|ß»îº+Ï¿ÕV[åùßxã<çm·Ý–bÕp\\±Z9ŠôŸ~ú)­»îºéÓO?Íç\u00187œ\u0001\u001c«O9唜AüÛá‡\u001fž\u000b÷ñãÇç9âyF\u001a•öÜsÏüï3gÎÌ[07·\u00029¶ôþ»y\u000e\u001f><?{K½\u0014À­ÿé“\u0004\b\u0010 @€\u0000\u0001\u0002\u0004\b\u0010 @€\u0000\u0001\u0002\u0004\b\u0010 @€\u0000\u0001\u0002\u0004Z#Т\u00028\nÞ(õ–^zé\\”~òÉ'iâĉù¾\u001bl°A:óÌ3\u001bW\u0000ßzë­ùço¾ù¦ñ¹¢ˆÜÿýÓE\u0017]”\u000bÙ(0cÕl\u0014»¯¿þz~múôéi©¥–Ê«x£È\\o½õò*Ø(€ß~ûíÆ\u0002ø\u0007\u001eȅgÓퟣԌó€ãùâ9c5p¼gÉ%—L¯¾új.ƒo¼ñÆÔ³gÏôÒK/¥.]º¤\t\u0013&¤M7Ý4½üò˹Ì\u001e2dHêÕ«W~æøyã7NwÜqGþy÷ÝwÏ%w|G\\ñ¯¼òJ._ãjzÿø9\nÖ(µ£\u0000nx½Á0žéÀ\u0003\u000fL³fÍJ/¼ðB~=ÎXŽ‚|Ĉ\u0011ùç¦[@ϛ7/m±Å\u0016©OŸ>¹\u0004Ž•Õa\u001e÷Ò;\f£\\¾ï¾ûÒ¾ûî›zôè‘n¾ùæ´Â\n+4û{\u0011Ïڒ<[ã¥\u0000nÍIŸ!@€\u0000\u0001\u0002\u0004\b\u0010 @€\u0000\u0001\u0002\u0004\b\u0010 @€\u0000\u0001\u0002\u0004\b\u0010 Ðz\u0016\u0015ÀQPƊҸF\u001eW¸ÆY±qýÝ\u00028ÎÅ\u001d4hPþÌk¯½–\u000bÏ(cµj\u0014¢“'ON\u001d:tXàDQpFÁ»þúëç÷¾ûî»y{ê(Žãj®|<蠃òêÚ(‰\u001b\nÍX\r¼ÒJ+åϬµÖZéÂ\u000b/L}ûöÍ?ŸvÚiùyb%s¬XŽ\u00026~Þ|óÍóëüñG.°£$žÿþñzs\u0005pSÃk®¹&şXÙ\u001bEúvÛm—Kîø¾¸š\u0016À±b8>\u001b«‚£\u0014+VP\u001füñiÀ€\u0001ùç\u0003\u0007¦»ï¾;Ÿ=|ðÁ\u0007§vÚé/\u001d£\u0000nIž-õú«\f\u0016\u0018¬7\u0010 @€\u0000\u0001\u0002\u0004\b\u0010 @€\u0000\u0001\u0002\u0004\b\u0010 @€\u0000\u0001\u0002\u0004\b\u0010 ÐjV\u0017ÀMËɸ{k\nào¿ý6­ºêªy›å}öÙ'í²Ë.¹híÞ½{Þjy‡\u001dvhv°æ¶ŽUÅQ&ÇöÏq5W\u0000NJäG\u001f}4½÷Þ{Í\u0016À±ír”ÓQªÆ\u0015ïr:¶ƒŽg<ôÐCÓ\u000f?üÐìªÚùï\u001fŸ_P\u0001Üô\u0019c‹è£Ž:*om\u001dÛeÇÕÔøÁ\u0007\u001fÌ¥n”»±z8®Ø&;Šð†UпüòKÚd“MrA\u001dÛZÿ¯kþ\u0002xAy6W\u0000ÿ/¯¿Ê Õ¿©>H€\u0000\u0001\u0002\u0004\b\u0010 @€\u0000\u0001\u0002\u0004\b\u0010 @€\u0000\u0001\u0002\u0004\b\u0010 @€À\u0002\u0005\u0016k\u0001\u001cçîÆYº<òH>7VÔFA\u001bgðÆ*ÝÇ\u001e{,Ÿ!<ÿ\u0015el¬„ów\u001b®(ã<àØþ9®æ\nàØ:9¶zŽÕ´--4cËé(_cKë(­ç¿æ¿¼Þ’\u00028¶Ì>î¸ãÒìÙ³Ó2Ë,“¿¾i)\u001b+{cÛíï¿ÿ>­¸âŠÍ\u0006ûã?æ\"=¶¦~óÍ7\u001bÏVnîÍ\nà\u0005þßð\u0006\u0002\u0004\b\u0010 @€\u0000\u0001\u0002\u0004\b\u0010 @€\u0000\u0001\u0002\u0004\b\u0010 @€\u0000\u0001\u0002ÿ9ÅZ\u00007l\u0001ýÎ;洛W^9­²Ê*\u0005nœ\u0007¼Æ\u001ak4ž·ÛTöˆ#ŽHC‡\u000emÜþ9>?lØ°\\˜6\\Í\u0015Àq6nœ‰\u001bejK\u000bà†- c5p”½M¯æî\u001f¯·¤\u0000\u001e?~|Úc=þr\u000bè8ëwÇ\u001dwÌ+’;wîÜì/Ú\t'œÏ\u0007>ûì³Slm\u001dg\u000b/±Ä\u0012;W\u0001üŸû¿ê\t\u0010 @€\u0000\u0001\u0002\u0004\b\u0010 @€\u0000\u0001\u0002\u0004\b\u0010 @€\u0000\u0001\u0002\u0004\b,P`¡\u0015ÀqnmlÝ<dȐ|Ó{ï½7õìÙ3Å*߶mÛæÛrË-ó*چ3€ûô铦NšÏ؍U½qÅë\rÇûã½çž{nºóÎ;Ӕ)SòöÇQ\u000eÇj߆+Î\"Ží¢»uëÖøoó\u0017ÀQÒvêÔ)EÑ\u001ag㶴\u0000Ž/Žâ÷÷ßÏϺÎ:ëä{ýüóÏùìáùï\u001f¯µ¤\u0000ž3gNÚpÃ\ró³Ý~ûí¹\b\u001f>|x.ºg̘‘þüóÏ|æolñ|Ï=÷¤6mÚä\u0015ÓsçÎÍ+†ÇŽ\u001d›¦OŸžúõ뗦M›–Ï\u0011\u001e1bD^U\u001cWl\u001dß\u0011+«ãZP\u0001<ž­ñj®„_ào¤7\u0010 @€\u0000\u0001\u0002\u0004\b\u0010 @€\u0000\u0001\u0002\u0004\b\u0010 @€\u0000\u0001\u0002\u0004\b\u0010 Ðj¿U\u0000ÇV̱ºtíµ×Î+mW[mµ|îìsÏ=—FŽ\u001c™‹Þ«¯¾:wÞyùüÙ(.;t萺víšb[â\u0003\u000e8 oÉ\u001c…n\u0014›\u001d;vÌeåòË/Ÿ·{Ž25¶->ñÄ\u0013óªÞyóæå×F\u001eÿnZ\u0000?ñÄ\u0013ù³M·îÒ¥K.‘\u001b¶\u000eX){饗æ’tÖ¬Yiæ̙©ÿþéØcÍçøÆó\u00193&sÎ9iÀ€\u0001yõpœã»óÎ;ç¿ã|â˜ç‹/¾H×]w].·cûçø\\Ü+J×õÖ[/¯¸Ù›Þ?¶i¾æškÒå—_ž\râ¾±\u0012·wïލ†±â9¾+¶¤\u001e5jTÞÞyÒ¤IÙ9Îömß¾}~ï¸qãR\u0014Üñœ\u001f|ðA.x'Ožœ3ˆB8îóÔSOåg\u001e<xp:úè£ówöêÕ+?o\u0014ÈQ\u0002G9\u001e®ñޖæ\u0019ß\u001d«¦[â\u0015>ñ{0qâÄ4pàÀ|¦s”Ö.\u0002\u0004\b\u0010 @€\u0000\u0001\u0002\u0004\b\u0010 @€\u0000\u0001\u0002\u0004\b\u0010 @€\u0000\u0001\u0002e\u000bü\u001fX‰n·Œ¢ïV\u0000\u0000\u0000\u0000IEND®B`‚\r\n------WebKitFormBoundaryVAdICtoeBOYBlIUi--\r\n",
  "method": "POST",
  "mode": "cors",
  "credentials": "omit"
});
MariaRTI ()
Последнее исправление: MariaRTI (всего исправлений: 4)
Ответ на: комментарий от MariaRTI

Почитал тут другие посты. Было предположение, что ошибка CORS может возникать, когда сервер не отвечает на OPTIONS. Однако, я специально разделил в обработчике на сервере запросы POST и все остальные.

[code=Erlang] init(Req, Opts) -> case cowboy_req:method(Req) of ««POST»» -> {ok, Req1, Opts1} = options(Req, Opts), {ok, Headers, Req2} = cowboy_req:read_part(Req1), {ok, Data, Req3} = cowboy_req:read_part_body(Req2), {file, ««upfile»», Filename, ContentType} = cow_multipart:form_data(Headers), io:format(«Received file ~p of content-type ~p as follow:~n~p~n~n», [Filename, ContentType, Data]), Filepath = filename:join([code:priv_dir(«upserver»), «upload», Filename]), file:write_file(Filepath, Data), {ok, Req3, Opts1}; _ -> io:format(«Method=~p~n»,[cowboy_req:method(Req)]), options(Req, Opts) end. [/code]

Если бы запрос OPTIONS выполнялся, то вывелось бы сообщение в лог «Method=OPTIONS», а после него вывелся бы лог для POST. Однако такого не наблюдается, отрабатывает только POST.

Запрос OPTIONS, приходил, когда я использовал следующую строчку в клиентском запросе. Сейчас она закоментирована, смотри в первом сообщении

[code=JavaScript] xhr.setRequestHeader(«Access-Control-Allow-Origin»,«https://upserver:8888»); [/code]

Но в этом случае файл не отправлялся, без каких либо ошибок и сообщений, запрос POST не приходил на сервер, как будто данная строчка аннулировала предыдущие настройки запроса.

MariaRTI ()
Последнее исправление: MariaRTI (всего исправлений: 4)
Ответ на: комментарий от cocucka

Пробовал просто двойные кавычки ‘‘*’’,но увидел, что люди пишут $*, для единичных символов. Оба варианта работают одинаково.

Извиняюсь, сейчас перепроверил. Одинарные кавычки не годятся - синтаксическая ошибка. Значит, только двойные кавычки для строк и доллар для одинарных символов, или всегда двойные кавычки, если по фигу.

MariaRTI ()
Последнее исправление: MariaRTI (всего исправлений: 3)

CORS – это просто!

  1. Перед GET-запросом браузер может отправить HEAD (а может и нет).
  2. Перед POST-запросом браузер отправляет предварительный OPTIONS запрос.

Головы запроса при этом

Origin: https://origin.example.com
Access-Control-Request-Method: DELETE
Access-Control-Request-Headers: origin, x-requested-with

Т.е. Origin, метод(ы), головы. И если ответ включает всё это дело, то ок. Например

Access-Control-Allow-Origin: https://origin.example.com
Access-Control-Allow-Methods: POST, GET, OPTIONS, DELETE
Access-Control-Allow-Headers: origin, x-requested-with
Access-Control-Max-Age: 86400

При использовании «*» можно выгрести немало гемороя, т.к. он работает по каким-то вообще хер пойми правилам. Ещё CORS не будет работать для https:// запрашиваемого с http://.

Можно открыть консоль браузера и посмотреть там, что за запросы и что за головы. Можно снифнуть Вайршарком. Логнуть сервером. Варианты.

anonymous ()
Ответ на: комментарий от anonymous

В данном примере я не делаю GET запросов, только POST. При этом, как я только что написал, браузер почему то НЕ делает OPTIONS запрос, судя по тому, какие запросы приходят на сервер. Звездочку я пишу, т.к. запросы могут приходить со множества клиентов, у которых множество оригинальных серверов (например, в датацентре или в компании) и у каждого может быть свой домен. Поэтому, конкретный домен original я указать не могу, но могу указать единственный домен сервера upserver (на клиенте или original-серверах), который известен всем. Клиентский код присутствует на всех узлах.

Сейчас добавил DELETE на сервере,

options(Req, Opts) ->
    %%Req0 = cowboy_req:set_resp_header(<<"access-control-max-age">>, <<"1728000">>, Req),
    Req1 = cowboy_req:set_resp_header(<<"access-control-allow-methods">>, <<"HEAD, OPTIONS, GET, PUT, POST, DELETE">>, Req),
    Req2 = cowboy_req:set_resp_header(<<"access-control-allow-headers">>, <<"Origin, X-Requested-With, Content-Type, Accept, x-client-key, x-client-token, x-client-secret">>, Req1),
    Req3 = cowboy_req:set_resp_header(<<"access-control-allow-origin">>, <<"*">>, Req2),
    {ok, Req3, Opts}.

но ничего не поменялось. Приходят исключительно только запросы POST. Приходят пересылаемые файлы, а также, на сервере, получаю сообщение об ошибке

=NOTICE REPORT==== 23-Nov-2021::11:07:32.934745 ===
TLS server: In state wait_finished received CLIENT ALERT: Fatal - Certificate Unknown

файлы приходят после этих ошибок.

Странно, сертификаты я создавал много раз, по проверенным инструкциям, но все они самоподписанные. Есть мнение, что только КУПЛЕННЫЕ у коммерческого центра сертификации корневые сертификаты сделают цепочку сертификатов валидной. Неужели нет способа это обойти для локальных проектов, использующих SSL/TLS? Кормушка мля какая-то.

Может быть сертификаты немного неправильно созданы или неправильно подключены? Есть мысли на этот счет?

MariaRTI ()
Последнее исправление: MariaRTI (всего исправлений: 1)
Ответ на: комментарий от MariaRTI

Что можно сказать об этом? Делаю такой curl запрос на upserver:8888

curl -H "Origin: https://upserver:8888" \
 -H "Access-Control-Request-Method: GET" \
 -H "Access-Control-Request-Headers: X-Requested-With" \
 -X OPTIONS --verbose \
 https://upserver:8888/upload

выхлоп:

* Expire in 0 ms for 6 (transfer 0x562bea5fefb0)
* Expire in 1 ms for 1 (transfer 0x562bea5fefb0)
* Expire in 0 ms for 1 (transfer 0x562bea5fefb0)
* Expire in 1 ms for 1 (transfer 0x562bea5fefb0)
* Expire in 0 ms for 1 (transfer 0x562bea5fefb0)
* Expire in 0 ms for 1 (transfer 0x562bea5fefb0)
* Expire in 1 ms for 1 (transfer 0x562bea5fefb0)
* Expire in 0 ms for 1 (transfer 0x562bea5fefb0)
* Expire in 0 ms for 1 (transfer 0x562bea5fefb0)
* Expire in 1 ms for 1 (transfer 0x562bea5fefb0)
* Expire in 0 ms for 1 (transfer 0x562bea5fefb0)
* Expire in 0 ms for 1 (transfer 0x562bea5fefb0)
* Expire in 1 ms for 1 (transfer 0x562bea5fefb0)
* Expire in 0 ms for 1 (transfer 0x562bea5fefb0)
* Expire in 0 ms for 1 (transfer 0x562bea5fefb0)
* Expire in 1 ms for 1 (transfer 0x562bea5fefb0)
* Expire in 0 ms for 1 (transfer 0x562bea5fefb0)
* Expire in 0 ms for 1 (transfer 0x562bea5fefb0)
* Expire in 1 ms for 1 (transfer 0x562bea5fefb0)
* Expire in 0 ms for 1 (transfer 0x562bea5fefb0)
* Expire in 0 ms for 1 (transfer 0x562bea5fefb0)
* Expire in 1 ms for 1 (transfer 0x562bea5fefb0)
* Expire in 0 ms for 1 (transfer 0x562bea5fefb0)
* Expire in 0 ms for 1 (transfer 0x562bea5fefb0)
* Expire in 1 ms for 1 (transfer 0x562bea5fefb0)
* Expire in 0 ms for 1 (transfer 0x562bea5fefb0)
* Expire in 0 ms for 1 (transfer 0x562bea5fefb0)
* Expire in 1 ms for 1 (transfer 0x562bea5fefb0)
* Expire in 0 ms for 1 (transfer 0x562bea5fefb0)
* Expire in 0 ms for 1 (transfer 0x562bea5fefb0)
* Expire in 1 ms for 1 (transfer 0x562bea5fefb0)
* Expire in 0 ms for 1 (transfer 0x562bea5fefb0)
* Expire in 0 ms for 1 (transfer 0x562bea5fefb0)
* Expire in 1 ms for 1 (transfer 0x562bea5fefb0)
* Expire in 0 ms for 1 (transfer 0x562bea5fefb0)
* Expire in 0 ms for 1 (transfer 0x562bea5fefb0)
* Expire in 1 ms for 1 (transfer 0x562bea5fefb0)
* Expire in 0 ms for 1 (transfer 0x562bea5fefb0)
* Expire in 0 ms for 1 (transfer 0x562bea5fefb0)
* Expire in 1 ms for 1 (transfer 0x562bea5fefb0)
* Expire in 0 ms for 1 (transfer 0x562bea5fefb0)
* Expire in 0 ms for 1 (transfer 0x562bea5fefb0)
* Expire in 0 ms for 1 (transfer 0x562bea5fefb0)
*   Trying 192.168.0.66...
* TCP_NODELAY set
* Expire in 200 ms for 4 (transfer 0x562bea5fefb0)
* Connected to upserver (192.168.0.66) port 8888 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
*   CAfile: none
  CApath: /etc/ssl/certs
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
* TLSv1.3 (IN), TLS handshake, Certificate (11):
* TLSv1.3 (OUT), TLS alert, unknown CA (560):
* SSL certificate problem: unable to get local issuer certificate
* Closing connection 0
curl: (60) SSL certificate problem: unable to get local issuer certificate
More details here: https://curl.haxx.se/docs/sslcerts.html

curl failed to verify the legitimacy of the server and therefore could not
establish a secure connection to it. To learn more about this situation and
how to fix it, please visit the web page mentioned above.

почему требуется «local issuer certificate» и почему задействуются сертификаты в /etc/ssl/certs я недопонимаю? на этом сервере я создал собственный центр сертификации CA в отдельном каталоге в приложении, сертификаты из /etc/ssl/certs при этом не используются. По крайней мере их некуда приткнуть при создании корневого сертификата и в инструкциях по этому поводу ничего не сказано.

Неофициальная инструкция: https://coderoad.ru/34807073/%D0%9A%D0%B0%D0%BA-%D1%81%D0%B3%D0%B5%D0%BD%D0%B5%D1%80%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D1%82%D1%8C-%D1%81%D0%B5%D1%80%D1%82%D0%B8%D1%84%D0%B8%D0%BA%D0%B0%D1%82%D1%8B-cowboy-SSL

подключение сертификатов в исходнике сервера

start(_Type, _Args) ->
	Dispatch = cowboy_router:compile([
		{'_', [
			{"/"            , cowboy_static, {priv_file, upserver, "index.html"}},
			{"/assets/[...]", cowboy_static, {priv_dir , upserver, "assets"}},
			{"/upload"      , upload_handler, []}
		]}
	]),
	PrivDir = code:priv_dir(upserver),
	{ok, _} = cowboy:start_tls(https, [
	     	  {port, 8888},
		  {cacertfile, PrivDir ++ "/ssl/end_cert/end.csr"},
		  {certfile, PrivDir ++ "/ssl/end_cert/end.crt"},
		  {keyfile, PrivDir ++ "/ssl/end_cert/end.key"}
		], #{env => #{dispatch => Dispatch}}),
	upserver_sup:start_link().

MariaRTI ()
Последнее исправление: MariaRTI (всего исправлений: 2)
Ответ на: комментарий от MariaRTI

Странно, сертификаты я создавал много раз, по проверенным инструкциям, но все они самоподписанные. Есть мнение, что только КУПЛЕННЫЕ у коммерческого центра сертификации корневые сертификаты сделают цепочку сертификатов валидной. Неужели нет способа это обойти для локальных проектов, использующих SSL/TLS? Кормушка мля какая-то.

Может быть сертификаты немного неправильно созданы или неправильно подключены? Есть мысли на этот счет?

Сертификат нужно в браузере установить. И тогда он схавает из все. Устанавливается только корневой. И может быть что-то там ещё. В общем это довольно просто. Но локально обычно никакие сертификаты не используются вообще, потому что нафиг не нужно. А почему бы вообще их не отрубить?

При этом, как я только что написал, браузер почему то НЕ делает OPTIONS запрос

Ещё браузер может проверить адерс хоста, распознать в нём localhost и вести себя немного иначе для этого дела. В запросе должна быть голова Origin.

anonymous ()
Ответ на: комментарий от anonymous
Сертификат нужно в браузере установить. 

Не я же его должен устанавливать на персоналке пользователя… Браузер сам должен его запрашивать у сервера. Разве нет?

Я так понимаю, что сервер не может найти локальный корневой сертификат на сервере, хотя он указан в исходниках сервера, или не может продолжить цепочку дальше корневого сертификата приложения. И каким боком он вдруг полез в /etc/ssl/certs, это же путь openssl по умолчанию, а в приложении задан свой путь.

Как мою сгенерированную цепочку сертификатов связать с той, что в openssl ?

Могу заново создать свои сертификаты если разберусь как их связать с /etc/ssl/certs. Поскольку связать постфактум явно не получится.

MariaRTI ()
Ответ на: комментарий от MariaRTI

Не я же его должен устанавливать на персоналке пользователя… Браузер сам должен его запрашивать у сервера. Разве нет?

Нет. Если сертификат выдан каким-то там центром сертификации, то браузер уже имеет корневой сертиификат на этот случай. И он может проверить всё это дело. Если дело делается локально, то генерируется сертификат (самоподписанный) и он устанавливается в браузер. Чтобы браузер смог его схавать. Т.к. проверки по корневому уже нету.

Есть куча инструкций в общем в интернете на этот счёт. Google: HTTPS локально. Самоподписанный сертификат. И так далее.

В общем случае браузеру нужен корневой сертификат, на основе которого выписан сертификат сервера. Но они могут быть и одним и тем же тоже.

anonymous ()
Ответ на: комментарий от MariaRTI

Ну и в сертификате ещё прописано доменное имя или несколько или вообще все поверх какого-то домена. Если не будет совпадать с реально запрашиваемым, то браузер опять брыкнётся. Но локально это обычно не требуется вообще. Просто сертификат и всё.

anonymous ()
Ответ на: комментарий от anonymous

У меня не совсем всё локально. Отлаживаю на одном компе, проверяю в локальной сети, а юзать потом возможно будут глобально, из дома например.

Про корневой сертификат я читал, что как и приватный ключ, его нельзя передавать никому. Для этого создается промежуточный сертификат и от него уже создаются конечные, которые используются в приложениях и передаются по запросу клиента.

Доменное имя указывается в Common Name при создании сертификата для данного домена. Всё так и сделано.

Посмотрите инструкцию, на которую я ссылаюсь. Точно по ней сделал. Что-то в ней не полностью или не так как надо.

В результате, с end-сертификата не виден промежуточный inter-сертификат, а с промежуточного не виден корневой root-сертификат, а про корневой вообще молчу, при его создании не указывался какой либо общеизвестный сертификат из /etc/ssl/certs, который возможно устроил бы браузер. В общем, цепочка программно не выстраивается

MariaRTI ()
Последнее исправление: MariaRTI (всего исправлений: 2)
Ответ на: комментарий от MariaRTI

Да я без понятия. Создайте новую тему про сертификаты. А то сюда только заходят про CORS. И может кто-то подскажет. Я вообще всё всегда делал только по открытым инструкция в интернете и не особо врубаясь что там и куда. Нафиг не ещё это гемор, пусть девопсы разбираются, это их пространство.

anonymous ()

Сдаётся мне ты не в ту сторону воюешь с сертификатом. Браузер же написал в консоли что ему не нравится. В чем беда сделать так как он просит? И кстати в треде приведён лог запроса, но нет лога ответа.

cobold ★★★★ ()