From 2dd0b88a930806213b619798ee896a0b36caaa76 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Fri, 19 Aug 2022 15:01:06 +0800 Subject: [PATCH 01/14] Fix UI mis-align for PR commit history (#20845) --- web_src/less/_repository.less | 1 + 1 file changed, 1 insertion(+) diff --git a/web_src/less/_repository.less b/web_src/less/_repository.less index b66f9eb185b40..473d6f9dc7dd6 100644 --- a/web_src/less/_repository.less +++ b/web_src/less/_repository.less @@ -906,6 +906,7 @@ .singular-commit { line-height: 34px; /* this must be same as .badge height, to avoid overflow */ + clear: both; // reset the "float right shabox", in the future, use flexbox instead > .avatar.image, > .avatar.image img { From 7258a124af3b83b0224600cffc725b07ed399590 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Fri, 19 Aug 2022 23:05:07 +0800 Subject: [PATCH 02/14] Fix the mode of custom dir to 0700 in docker-rootless (#20861) --- docker/rootless/usr/local/bin/docker-setup.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/rootless/usr/local/bin/docker-setup.sh b/docker/rootless/usr/local/bin/docker-setup.sh index 47645726c4b08..feab02a3793c1 100755 --- a/docker/rootless/usr/local/bin/docker-setup.sh +++ b/docker/rootless/usr/local/bin/docker-setup.sh @@ -5,7 +5,7 @@ mkdir -p ${HOME} && chmod 0700 ${HOME} if [ ! -w ${HOME} ]; then echo "${HOME} is not writable"; exit 1; fi # Prepare custom folder -mkdir -p ${GITEA_CUSTOM} && chmod 0500 ${GITEA_CUSTOM} +mkdir -p ${GITEA_CUSTOM} && chmod 0700 ${GITEA_CUSTOM} # Prepare temp folder mkdir -p ${GITEA_TEMP} && chmod 0700 ${GITEA_TEMP} From 8cceee4084c116dedc731da050d49549a8f71a1d Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Sat, 20 Aug 2022 00:20:56 +0000 Subject: [PATCH 03/14] [skip ci] Updated translations via Crowdin --- options/locale/locale_es-ES.ini | 14 +++--- options/locale/locale_fi-FI.ini | 89 +++++++++++++++++++++++++++++++-- options/locale/locale_fr-FR.ini | 1 + 3 files changed, 94 insertions(+), 10 deletions(-) diff --git a/options/locale/locale_es-ES.ini b/options/locale/locale_es-ES.ini index ea1ce54749a72..f7343b7f44065 100644 --- a/options/locale/locale_es-ES.ini +++ b/options/locale/locale_es-ES.ini @@ -36,9 +36,9 @@ passcode=Código de acceso webauthn_insert_key=Introduzca su clave de seguridad webauthn_sign_in=Presione el botón en su clave de seguridad. Si su clave de seguridad no tiene ningún botón, vuelva a insertarla. -webauthn_press_button=Por favor, preione el botón en su clave de seguridad… +webauthn_press_button=Por favor, presione el botón de su llave de seguridad… webauthn_use_twofa=Utilice un código de doble factor desde su teléfono móvil -webauthn_error=No se pudo leer la clave de seguridad. +webauthn_error=No se pudo leer su llave de seguridad. webauthn_unsupported_browser=Su navegador no soporta actualmente WebAuthn. webauthn_error_unknown=Ha ocurrido un error desconocido. Por favor, inténtelo de nuevo. webauthn_error_insecure=WebAuthn sólo soporta conexiones seguras. Para probar sobre HTTP, puede utilizar el origen "localhost" o "127.0.0.1" @@ -533,7 +533,7 @@ twofa=Autenticación de doble factor account_link=Cuentas vinculadas organization=Organizaciones uid=UUID -webauthn=Claves Seguridades +webauthn=Llaves de Seguridad public_profile=Perfil público biography_placeholder=Cuéntenos un poco más sobre usted @@ -764,7 +764,7 @@ twofa_disable_note=Puede deshabilitar la autenticación de doble factor si lo ne twofa_disable_desc=Deshabilitar la autenticación de doble factor hará su cuenta menos segura. ¿Continuar? regenerate_scratch_token_desc=Si extravió su código de respaldo, o ya lo usó para iniciar sesión, puede restablecerlo aquí. twofa_disabled=La autenticación de doble factor ha sido deshabilitada. -scan_this_image=Analiza esta imagen con la aplicación de autenticación: +scan_this_image=Escanee esta imagen con su aplicación de autenticación: or_enter_secret=O introduzca el secreto: %s then_enter_passcode=E introduzca el código de acceso mostrado en la aplicación: passcode_invalid=El código de acceso es incorrecto. Vuelva a intentarlo. @@ -775,7 +775,7 @@ webauthn_desc=Las claves de seguridad son dispositivos hardware que contienen cl webauthn_register_key=Añadir clave de seguridad webauthn_nickname=Apodo webauthn_delete_key=Eliminar clave de seguridad -webauthn_delete_key_desc=Si elimina una clave de seguridad no podrá utilizarla para registrarte con ella. ¿Continuar? +webauthn_delete_key_desc=Si elimina una llave de seguridad ya no podrá utilizarla para iniciar sesión con ella. ¿Continuar? manage_account_links=Administrar cuentas vinculadas manage_account_links_desc=Estas cuentas externas están vinculadas a su cuenta de Gitea. @@ -1235,9 +1235,9 @@ issues.new_label_placeholder=Nombre etiqueta issues.new_label_desc_placeholder=Descripción issues.create_label=Crear etiqueta issues.label_templates.title=Carga un conjunto predefinido de etiquetas -issues.label_templates.info=No hay etiquetas existentes todavía. Crea una etiqueta con "Nueva Etiqueta" o use la etiqueta predefinida: +issues.label_templates.info=Todavía no existen etiquetas. Cree una etiqueta con "Nueva Etiqueta" o use un conjunto predefinido de etiquetas: issues.label_templates.helper=Seleccionar un conjunto de etiquetas -issues.label_templates.use=Utilice la etiqueta +issues.label_templates.use=Usar este conjunto de etiquetas issues.label_templates.fail_to_load_file=Error al cargar el archivo de plantilla de etiqueta '%s': %v issues.add_label=añadió la etiqueta %s %s issues.add_labels=añadió las etiquetas %s %s diff --git a/options/locale/locale_fi-FI.ini b/options/locale/locale_fi-FI.ini index 7c0cc310973ce..27f183e8d64c7 100644 --- a/options/locale/locale_fi-FI.ini +++ b/options/locale/locale_fi-FI.ini @@ -34,9 +34,12 @@ twofa_scratch=Kaksivaiheinen kertakäyttöinen koodi passcode=Tunnuskoodi webauthn_insert_key=Aseta turva-avaimesi +webauthn_sign_in=Paina turva-avaimesi painiketta. Jos turva-avaimessasi ei ole painiketta, irroita se ja aseta uudelleen. webauthn_press_button=Paina turva-avaimesi painiketta… +webauthn_use_twofa=Käytä kaksivaihesta vahvistusta puhelimestasi webauthn_error=Turva-avainta ei voitu lukea. webauthn_unsupported_browser=Selaimesi ei tällä hetkellä tue WebAuthnia. +webauthn_error_unknown=Tuntematon virhe. Yritä uudelleen. webauthn_error_insecure=WebAuthn tukee vain suojattuja yhteyksiä. Testaukseen HTTP:n yli, voit käyttää osoitetta "localhost" tai "127.0.0.1" webauthn_error_unable_to_process=Palvelin ei pystynyt toteuttamaan kutsua. webauthn_error_duplicated=Turva-avainta ei ole sallittu tässä pyynnössä. Varmista, ettei avainta ole jo rekisteröity. @@ -210,6 +213,7 @@ internal_token_failed=Sisäisen pääsymerkin luonti epäonnistui: %v save_config_failed=Asetusten tallentaminen epäonnistui: %v install_success=Tervetuloa! Kiitos kun valitsit Gitean. Pidä hauskaa! default_keep_email_private=Piilota sähköpostiosoitteet oletuksena +default_keep_email_private_popup=Piilota oletusarvoisesti uusien käyttäjätilien sähköpostiosoitteet. default_enable_timetracking=Ota ajan seuranta oletusarvoisesti käyttöön default_enable_timetracking_popup=Ota käyttöön uusien repojen aikaseuranta oletusarvoisesti. no_reply_address=Piilotettu sähköpostin verkkotunnus @@ -342,6 +346,7 @@ register_success=Rekisteröinti onnistui issue.x_mentioned_you=@%s mainitsi sinut: issue.action.push_1=@%[1]s työnsi %[3]d commitin kohteeseen %[2]s issue.action.push_n=@%[1]s työnsi %[3]d committia kohteeseen %[2]s +issue.action.reject=@%[1]s pyysi muutoksia tässä vetopyynnössä. release.title=Otsikko: %s release.note=Huomautus: @@ -489,6 +494,7 @@ comment_type_group_project=Projekti saved_successfully=Asetuksesi tallennettiin onnistuneesti. privacy=Yksityisyys keep_activity_private=Piilota toiminta profiilisivulta +keep_activity_private_popup=Tekee toiminnon näkyvän vain sinulle ja ylläpitäjille lookup_avatar_by_mail=Hae profiilikuva sähköpostin perusteella federated_avatar_lookup=Ulkopuolinen profiilikuvan haku @@ -601,6 +607,8 @@ generate_token=Luo pääsymerkki generate_token_success=Uusi pääsymerkkisi on nyt luotu. Kopioi se nyt, koska sitä ei näytetä enää uudelleen. delete_token=Poista access_token_deletion=Poista pääsymerkki +access_token_deletion_cancel_action=Peruuta +access_token_deletion_confirm_action=Poista edit_oauth2_application=Muokkaa OAuth2 sovellusta remove_oauth2_application=Poista OAuth2 sovellus @@ -647,6 +655,7 @@ visibility.public=Julkinen visibility.public_tooltip=Näkyvissä kaikille käyttäjille visibility.limited=Rajattu visibility.private=Yksityinen +visibility.private_tooltip=Näkyy vain organisaation jäsenille [repo] new_repo_helper=Repo sisältää kaikki projektitiedostot, mukaan lukien revisiohistorian. Onko sinulla repo jo muualla? Voit siirtää repon. @@ -714,7 +723,10 @@ migrate.github_token_desc=Voit laittaa yhden tai useamman pääsymerkin pilkulla migrate.permission_denied=Sinun ei sallita tuovan paikallisia repoja. migrate.failed=Siirto epäonnistui: %v migrate.migrate_items_options=Pääsymerkki vaaditaan lisäkohteiden siirtämiseen +migrate.migrating=Tuodaan kohteesta %s ... +migrate.migrating_failed=Tuonti kohteesta %s epäonnistui. migrate.migrating_failed.error=Virhe: %s +migrate.migrating_git=Tuodaan Git-tietoja mirror_from=peilaus alkaen forked_from=forkattu lähteestä @@ -722,7 +734,7 @@ unwatch=Lopeta tarkkailu watch=Tarkkaile unstar=Poista tähti star=Tähti -download_archive=Lataa varasto +download_archive=Lataa repo no_desc=Ei kuvausta quick_guide=Pikaopas @@ -790,6 +802,7 @@ editor.upload_files_to_dir=Lataa tiedostot kohteeseen '%s' editor.require_signed_commit=Haara vaatii vahvistetun commitin commits.commits=Commitit +commits.nothing_to_compare=Nämä haarat vastaavat toisiaan. commits.find=Haku commits.search_all=Kaikki haarat commits.author=Tekijä @@ -826,11 +839,14 @@ projects.open=Avaa projects.close=Sulje issues.desc=Ongelmien, tehtävien ja merkkipaalujen hallinta. +issues.filter_assignees=Suodata käyttäjiä issues.filter_milestones=Suodata merkkipaalu issues.new=Uusi ongelma issues.new.labels=Tunnisteet +issues.new.add_labels_title=Aseta tunniste issues.new.no_label=Ei tunnistetta issues.new.clear_labels=Tyhjennä tunnisteet +issues.new.no_items=Ei kohteita issues.new.milestone=Merkkipaalu issues.new.add_milestone_title=Aseta merkkipaalu issues.new.no_milestone=Ei merkkipaalua @@ -838,6 +854,7 @@ issues.new.clear_milestone=Tyhjennä merkkipaalu issues.new.open_milestone=Avoimet merkkipaalut issues.new.closed_milestone=Suljetut merkkipaalut issues.new.assignees=Käsittelijä +issues.new.add_assignees_title=Osoita käyttäjille issues.new.clear_assignees=Tyhjennä käsittelijä issues.new.no_assignees=Ei käsittelijää issues.choose.blank=Oletus @@ -849,8 +866,12 @@ issues.new_label_desc_placeholder=Kuvaus issues.create_label=Luo tunniste issues.label_templates.helper=Valitse tunnistejoukko issues.add_milestone_at=`lisäsi tämän merkkipaaluun %s %s` +issues.change_milestone_at=`vaihtoi merkkipaalun %s merkkipaaluun %s %s` +issues.remove_milestone_at=`poisti tämän %s merkkipaalusta %s` +issues.remove_project_at=`poisti tämän %s projektista %s` issues.deleted_milestone=`(poistettu)` issues.self_assign_at=`itse otti tämän käsittelyyn %s` +issues.change_title_at=`muutti otsikon %s otsikoksi %s %s` issues.delete_branch_at=`poisti haaran %s %s` issues.filter_label=Tunniste issues.filter_label_exclude=`Käytä alt + klikkaus/rivinvaihto poissulkeaksesi tunnisteita` @@ -892,6 +913,7 @@ issues.commented_at=`kommentoi %s` issues.delete_comment_confirm=Haluatko varmasti poistaa tämän kommentin? issues.context.copy_link=Kopioi linkki issues.context.quote_reply=Vastaa lainaamalla +issues.context.reference_issue=Viittaa uudesa ongelmassa issues.context.edit=Muokkaa issues.context.delete=Poista issues.no_content=Sisältöä ei vielä ole. @@ -936,9 +958,11 @@ issues.lock.reason=Lukitsemisen syy issues.lock.title=Lukitse keskustelu tästä ongelmasta. issues.unlock.title=Avaa keskustelu tästä ongelmasta. issues.tracker=Ajan seuranta +issues.start_tracking_short=Aloita ajanotto issues.start_tracking=Aloita ajan seuranta issues.start_tracking_history=`aloitti työskentelyn %s` issues.tracker_auto_close=Ajan seuranta pysähtyy automaattisesti kun tämä ongelma on suljettu +issues.stop_tracking=Pysäytä ajanotto issues.stop_tracking_history=`lopetti työskentelyn %s` issues.add_time=Lisää aika käsin issues.add_time_short=Lisää aika @@ -949,30 +973,44 @@ issues.add_time_minutes=Minuuttia issues.add_time_sum_to_small=Aikaa ei syötetty. issues.time_spent_from_all_authors=`Käytetty kokonaisaika: %s` issues.due_date=Määräpäivä +issues.push_commit_1=lisäsi %d commitin %s +issues.push_commits_n=lisäsi %d committia %s issues.due_date_form=vvvv-kk-pp issues.due_date_form_edit=Muokkaa issues.due_date_form_remove=Poista issues.due_date_not_set=Määräpäivää ei asetettu. issues.due_date_overdue=Myöhässä issues.dependency.title=Riippuvuudet +issues.dependency.issue_no_dependencies=Riippuvuuksia ei asetettu. +issues.dependency.pr_no_dependencies=Riippuvuuksia ei asetettu. issues.dependency.add=Lisää riippuvuus… issues.dependency.cancel=Peru issues.dependency.remove=Poista issues.dependency.remove_info=Poistä tämä riippuvuus issues.review.self.approval=Et voi hyväksyä omia vetopyyntöjä. +issues.review.self.rejection=Et voi pyytää muutoksia omaan vetopyyntöön. issues.review.approve=hyväksyi nämä muutokset %s issues.review.left_comment=jätti kommentin issues.review.pending=Odottaa +issues.review.pending.tooltip=Tämä kommentti ei tällä hetkellä näy muille käyttäjille. Lähettääksesi odottavat kommentit, valitse '%s' -> '%s/%s/%s' sivun yläreunassa. +issues.review.show_resolved=Näytä ratkaisu +issues.review.hide_resolved=Piilota ratkaisu issues.reference_issue.body=Kuvaus issues.content_history.deleted=poistettu issues.content_history.edited=muokattu issues.content_history.created=luotu -pulls.new=Uusi pull pyyntö +pulls.new=Uusi vetopyyntö +pulls.compare_changes=Uusi vetopyyntö +pulls.has_viewed_file=Katsottu +pulls.viewed_files_label=%[1]d / %[2]d tiedostoa katsottu +pulls.compare_compare=vedä kohteesta pulls.filter_branch=Suodata branch pulls.no_results=Tuloksia ei löytynyt. -pulls.nothing_to_compare=Nämä haarat ovat samanlaisia. Ei ole tarvetta luoda vetopyyntöä. +pulls.nothing_to_compare=Nämä haarat vastaavat toisiaan. Ei ole tarvetta luoda vetopyyntöä. +pulls.nothing_to_compare_and_allow_empty_pr=Nämä haarat vastaavat toisiaan. Vetopyyntö tulee olemaan tyhjä. +pulls.has_pull_request=`Vetopyyntö haarojen välillä on jo olemassa: %[2]s#%[3]d` pulls.create=Luo Pull-pyyntö pulls.title_desc=haluaa yhdistää %[1]d committia lähteestä %[2]s kohteeseen %[3]s pulls.merged_title_desc=yhdistetty %[1]d committia lähteestä %[2]s kohteeseen %[3]s %[4]s @@ -981,6 +1019,9 @@ pulls.tab_commits=Commitit pulls.tab_files=Muuttuneet tiedostot pulls.merged=Yhdistetty pulls.has_merged=Vetopyyntö on yhdistetty. +pulls.title_wip_desc=`Aloita otsikko sanalla %s estääksesi vetopyynnön yhdistämisen vahingossa.` +pulls.add_prefix=Lisää %s etuliite +pulls.remove_prefix=Poista %s etuliite pulls.can_auto_merge_desc=Tämä pull-pyyntö voidaan yhdistää automaattisesti. @@ -1113,18 +1154,37 @@ settings.githook_edit_desc=Jos koukku ei ole käytössä, esitellään esimerkki settings.githook_name=Koukun nimi settings.githook_content=Koukun sisältö settings.update_githook=Päivitys koukku +settings.payload_url=Kohde URL settings.http_method=HTTP-menetelmä settings.secret=Salaus settings.slack_username=Käyttäjätunnus settings.slack_icon_url=Kuvakkeen URL settings.discord_username=Käyttäjätunnus +settings.event_desc=Triggeröi: +settings.event_send_everything=Kaikki tapahtumat +settings.event_choose=Mukautetut tapahtumat… +settings.event_header_repository=Repon tapahtumat settings.event_create=Luo +settings.event_create_desc=Haara tai tagi luotu. settings.event_delete=Poista +settings.event_delete_desc=Haara tai tagi poistettu. settings.event_release_desc=Julkaisu julkaistu, päivitetty tai poistettu varastosta. settings.event_push=Työnnä +settings.event_push_desc=Git push repoon. settings.event_repository=Repo +settings.event_repository_desc=Repo luotu tai poistettu. +settings.event_header_issue=Ongelmien tapahtumat +settings.event_issues_desc=Ongelma avattu, suljettu, avattu uudelleen tai muokattu. +settings.event_issue_assign=Ongelma määritetty +settings.event_issue_assign_desc=Ongelma osoitettu tai osoitus poistettu. +settings.event_issue_label_desc=Ongelman tunnisteet päivitetty tai tyhjennetty. +settings.event_issue_milestone_desc=Ongelma merkkipaaluteettu tai merkkipaalu-osoitus poistettu. settings.event_issue_comment_desc=Ongelman kommentti luotu, muokattu tai poistettu. +settings.event_header_pull_request=Vetopyyntöjen tapahtumat settings.event_pull_request=Vetopyyntö +settings.event_package_desc=Paketti on luotu tai poistettu repossa. +settings.active_helper=Tiedot käynnistetyistä tapahtumista lähetetään tähän webkoukun URL-osoitteeseen. +settings.add_hook_success=Uusi webkoukku on lisätty. settings.update_webhook=Päivitä webkoukku settings.delete_webhook=Poista webkoukku settings.recent_deliveries=Viimeisimmät toimitukset @@ -1132,6 +1192,7 @@ settings.hook_type=Koukkutyyppi settings.slack_token=Pääsymerkki settings.slack_domain=Verkkotunnus settings.slack_channel=Kanava +settings.add_web_hook_desc=Integroi %s repoon. settings.web_hook_name_gitea=Gitea settings.web_hook_name_gogs=Gogs settings.web_hook_name_slack=Slack @@ -1175,13 +1236,21 @@ settings.no_protected_branch=Suojattuja haaroja ei ole. settings.edit_protected_branch=Muokkaa settings.protected_branch_required_approvals_min=Vaadittavat hyväksynnät ei voi olla negatiivinen. settings.tags=Tagit +settings.tags.protection=Tagien suojaaminen settings.tags.protection.pattern=Tagin kuvio +settings.tags.protection.allowed=Sallitut +settings.tags.protection.allowed.users=Sallitut käyttäjät +settings.tags.protection.allowed.teams=Sallitut tiimit +settings.tags.protection.allowed.noone=Ei kukaan +settings.tags.protection.create=Suojaa tagi +settings.tags.protection.none=Suojattuja tageja ei ole. settings.tags.protection.pattern.description=Voit käyttää yhtä nimeä tai glob-kuviota tai säännöllistä lauseketta, joka täsmää useisiin tageihin. Lue lisää suojatut tagit oppaasta. settings.bot_token=Botti pääsymerkki settings.matrix.homeserver_url=Kotipalvelimen URL settings.matrix.access_token=Pääsymerkki settings.archive.button=Arkistoi repo settings.archive.header=Arkistoi tämä repo +settings.archive.tagsettings_unavailable=Tagien asetukset eivät ole saatavilla, jos repo on arkistoitu. settings.lfs=LFS settings.lfs_filelist=LFS-tiedostot tallennettu tähän repoon settings.lfs_no_lfs_files=LFS-tiedostoja ei ole tallennettu tähän repoon. @@ -1224,10 +1293,15 @@ diff.view_file=Näytä tiedosto diff.file_image_width=Leveys diff.file_image_height=Korkeus diff.file_byte_size=Koko +diff.comment.markdown_info=Muotoilu markdownilla tuettu. +diff.comment.add_single_comment=Lisää yksittäinen kommentti diff.comment.add_review_comment=Lisää kommentti +diff.comment.start_review=Aloita tarkistus diff.comment.reply=Vastaa +diff.review.header=Lähetä arvio diff.review.comment=Kommentoi diff.review.approve=Hyväksy +diff.review.reject=Pyydä muutoksia release.releases=Julkaisut release.tags=Tagit @@ -1251,6 +1325,9 @@ release.publish=Julkaise versio release.save_draft=Tallenna luonnos release.edit_release=Päivitä julkaisu release.delete_release=Poista julkaisu +release.delete_tag=Poista tagi +release.deletion_tag_desc=Poistetaanko tämä tagi reposta? Repon sisältö ja historia pysyvät muuttumattomina. Jatketaanko? +release.deletion_tag_success=Tagi on poistettu. release.tag_name_invalid=Tagin nimi ei ole kelvollinen. release.downloads=Lataukset @@ -1304,7 +1381,9 @@ settings.visibility.private_shortname=Yksityinen settings.update_settings=Päivitä asetukset settings.delete=Poista organisaatio settings.delete_account=Poista tämä organisaatio +settings.delete_prompt=Organisaatio poistetaan pysyvästi, ja tätä EI VOI peruuttaa myöhemmin! settings.confirm_delete_account=Vahvista poisto +settings.hooks_desc=Lisää webkoukkuja, jotka suoritetaan kaikissa repoissa tässä organisaatiossa. members.membership_visibility=Jäsenyyden näkyvyys: @@ -1538,6 +1617,7 @@ config.show_registration_button=Näytä rekisteröidy painike config.disable_key_size_check=Poista käytöstä avaimen vähimmäiskoko tarkistus config.enable_captcha=Ota CAPTCHA käyttöön config.active_code_lives=Aktiivinen koodi elämät ennen vanhenemista +config.default_keep_email_private=Piilota sähköpostiosoitteet oletuksena config.default_visibility_organization=Uuden organisaation oletusnäkyvyys config.webhook_config=Webkoukku asetukset @@ -1592,6 +1672,7 @@ monitor.queues=Jonot monitor.queue=Jono: %s monitor.queue.name=Nimi monitor.queue.type=Tyyppi +monitor.queue.pool.addworkers.desc=Lisää käsittelijöitä tähän pooliin aikakatkaisulla tai ilman. Jos asetat aikakatkaisun, nämä käsittelijät poistetaan poolista kun aikakatkaisu on päättynyt. @@ -1612,7 +1693,9 @@ create_repo=luotu repo %s rename_repo=uudelleennimetty repo %[1]s nimelle %[3]s transfer_repo=siirretty repo %s kohteeseen %s push_tag=työnsi tagin %[3]s kohteeseen %[4]s +delete_tag=poisti tagin %[2]s kohteesta %[3]s compare_commits_general=Vertaa committeja +create_branch=loi haaran %[3]s repossa %[4]s [tool] ago=%s sitten diff --git a/options/locale/locale_fr-FR.ini b/options/locale/locale_fr-FR.ini index 18eb44b16a3d5..3417fdbef94b2 100644 --- a/options/locale/locale_fr-FR.ini +++ b/options/locale/locale_fr-FR.ini @@ -1458,6 +1458,7 @@ pulls.required_status_check_missing=Certains contrôles requis sont manquants. pulls.required_status_check_administrator=En tant qu'administrateur, vous pouvez toujours fusionner cette requête de pull. pulls.blocked_by_approvals=Cette demande d'ajout n'a pas assez d'approbation. %d sur %d approbations accordées. pulls.blocked_by_rejection=Cette demande de fusion a des modifications demandées par un réviseur officiel. +pulls.blocked_by_official_review_requests=Cette demande d'ajout a des demandes de revue officielles. pulls.blocked_by_outdated_branch=Cette demande d'ajout est bloquée car elle est obsolète. pulls.blocked_by_changed_protected_files_1=Cette demande d'ajout est bloquée car elle modifie un fichier protégé : pulls.blocked_by_changed_protected_files_n=Cette Pull Request est bloquée car elle modifie les fichiers protégés : From 3d52edc7a43ca429b7921b96c3663c32179881ab Mon Sep 17 00:00:00 2001 From: Gusted Date: Sat, 20 Aug 2022 16:47:04 +0200 Subject: [PATCH 04/14] Don't open new page for ext wiki on same repository (#20725) - When the external wiki has been set to a file on the repository, don't open the page on a tab. - Resolves #20657 --- modules/templates/helper.go | 1 + templates/repo/header.tmpl | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/templates/helper.go b/modules/templates/helper.go index 602afec4135fc..410857f37e5ba 100644 --- a/modules/templates/helper.go +++ b/modules/templates/helper.go @@ -453,6 +453,7 @@ func NewFuncMap() []template.FuncMap { } return items }, + "HasPrefix": strings.HasPrefix, }} } diff --git a/templates/repo/header.tmpl b/templates/repo/header.tmpl index e6ed76bb60fa8..a08196f264482 100644 --- a/templates/repo/header.tmpl +++ b/templates/repo/header.tmpl @@ -206,7 +206,7 @@ {{end}} {{if or (.Permission.CanRead $.UnitTypeWiki) (.Permission.CanRead $.UnitTypeExternalWiki)}} - + {{svg "octicon-book"}} {{.locale.Tr "repo.wiki"}} {{end}} From cb37c6ba5cc3c8113a7252e16b386eb887346185 Mon Sep 17 00:00:00 2001 From: JonRB <4564448+eeyrjmr@users.noreply.github.com> Date: Sat, 20 Aug 2022 22:09:41 +0100 Subject: [PATCH 05/14] call builtinUnused() if internal SSH is disabled (#20877) The graceful manager waits for 4 listeners to be created or to be told that they are not needed. If it is not told about them it will indefinitely and timeout. This leads to SVC hosts not being told of being in the readyState but on Unix would lead to the termination of the process. There was an unfortunate regression in #20299 which missed this subtly and in the case whereby SSH is disabled the `builtinUnused()` is not called. This PR adds a call to `builtinUnused()` when not using the builtin ssh to allow `createServerWaitGroup.Done()` to be called. In addition it was noted that the if/else clauses for timeout informing of the SVC host were in the wrong order. These have been swapped. Fix #20609 --- modules/graceful/manager_windows.go | 4 ++-- modules/ssh/init.go | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/modules/graceful/manager_windows.go b/modules/graceful/manager_windows.go index e7e619f53f617..10c1d67b97204 100644 --- a/modules/graceful/manager_windows.go +++ b/modules/graceful/manager_windows.go @@ -114,9 +114,9 @@ func (g *Manager) start() { // Execute makes Manager implement svc.Handler func (g *Manager) Execute(args []string, changes <-chan svc.ChangeRequest, status chan<- svc.Status) (svcSpecificEC bool, exitCode uint32) { if setting.StartupTimeout > 0 { - status <- svc.Status{State: svc.StartPending} - } else { status <- svc.Status{State: svc.StartPending, WaitHint: uint32(setting.StartupTimeout / time.Millisecond)} + } else { + status <- svc.Status{State: svc.StartPending} } log.Trace("Awaiting server start-up") diff --git a/modules/ssh/init.go b/modules/ssh/init.go index f6332bb18b724..72cb6df7a43c6 100644 --- a/modules/ssh/init.go +++ b/modules/ssh/init.go @@ -18,6 +18,7 @@ import ( func Init() error { if setting.SSH.Disabled { + builtinUnused() return nil } From 0ee96da052edd3fe25b66d2c9fd5fd3d01171091 Mon Sep 17 00:00:00 2001 From: JonRB <4564448+eeyrjmr@users.noreply.github.com> Date: Sun, 21 Aug 2022 00:20:58 +0000 Subject: [PATCH 06/14] [skip ci] Updated translations via Crowdin --- options/locale/locale_pt-BR.ini | 2 ++ 1 file changed, 2 insertions(+) diff --git a/options/locale/locale_pt-BR.ini b/options/locale/locale_pt-BR.ini index b5c81702dc702..255e657608dba 100644 --- a/options/locale/locale_pt-BR.ini +++ b/options/locale/locale_pt-BR.ini @@ -1797,6 +1797,7 @@ settings.tracker_issue_style=Formato de número do issue tracker externo settings.tracker_issue_style.numeric=Numérico settings.tracker_issue_style.alphanumeric=Alfanumérico settings.tracker_issue_style.regexp=Expressão Regular +settings.tracker_issue_style.regexp_pattern=Padrão de expressão regular settings.tracker_url_format_desc=Use os espaços reservados {user}, {repo} e {index} para o nome de usuário, nome do repositório e o índice de problemas. settings.enable_timetracker=Habilitar Cronômetro settings.allow_only_contributors_to_track_time=Permitir que apenas os colaboradores acompanhem o contador de tempo @@ -2771,6 +2772,7 @@ config.mailer_config=Configuração de Envio de E-mail config.mailer_enabled=Habilitado config.mailer_name=Nome config.mailer_protocol=Protocolo +config.mailer_smtp_addr=Addr SMTP config.mailer_smtp_port=Porta SMTP config.mailer_user=Usuário config.mailer_use_sendmail=Usar o Sendmail From 11bae504846293806fec27f3271e0d3aba8f0f93 Mon Sep 17 00:00:00 2001 From: zeripath Date: Sun, 21 Aug 2022 07:50:15 +0100 Subject: [PATCH 07/14] Pad GPG Key ID with preceding zeroes (#20878) --- models/asymkey/gpg_key.go | 9 +++++++++ routers/api/v1/user/gpg_key.go | 7 +++++++ templates/repo/commit_page.tmpl | 8 ++++---- templates/user/settings/keys_gpg.tmpl | 8 ++++---- 4 files changed, 24 insertions(+), 8 deletions(-) diff --git a/models/asymkey/gpg_key.go b/models/asymkey/gpg_key.go index 2b99972379c4c..21554d2150535 100644 --- a/models/asymkey/gpg_key.go +++ b/models/asymkey/gpg_key.go @@ -63,6 +63,15 @@ func (key *GPGKey) AfterLoad(session *xorm.Session) { } } +// PaddedKeyID show KeyID padded to 16 characters +func (key *GPGKey) PaddedKeyID() string { + if len(key.KeyID) > 15 { + return key.KeyID + } + zeros := "0000000000000000" + return zeros[0:16-len(key.KeyID)] + key.KeyID +} + // ListGPGKeys returns a list of public keys belongs to given user. func ListGPGKeys(ctx context.Context, uid int64, listOptions db.ListOptions) ([]*GPGKey, error) { sess := db.GetEngine(ctx).Table(&GPGKey{}).Where("owner_id=? AND primary_key_id=''", uid) diff --git a/routers/api/v1/user/gpg_key.go b/routers/api/v1/user/gpg_key.go index b211a24a0e0d4..b87cf0041e192 100644 --- a/routers/api/v1/user/gpg_key.go +++ b/routers/api/v1/user/gpg_key.go @@ -7,6 +7,7 @@ package user import ( "fmt" "net/http" + "strings" asymkey_model "code.gitea.io/gitea/models/asymkey" "code.gitea.io/gitea/models/db" @@ -177,6 +178,12 @@ func VerifyUserGPGKey(ctx *context.APIContext) { token := asymkey_model.VerificationToken(ctx.Doer, 1) lastToken := asymkey_model.VerificationToken(ctx.Doer, 0) + form.KeyID = strings.TrimLeft(form.KeyID, "0") + if form.KeyID == "" { + ctx.NotFound() + return + } + _, err := asymkey_model.VerifyGPGKey(ctx.Doer.ID, form.KeyID, token, form.Signature) if err != nil && asymkey_model.IsErrGPGInvalidTokenSignature(err) { _, err = asymkey_model.VerifyGPGKey(ctx.Doer.ID, form.KeyID, lastToken, form.Signature) diff --git a/templates/repo/commit_page.tmpl b/templates/repo/commit_page.tmpl index 4c4f6f5bc8faf..9e3fadee0ca7d 100644 --- a/templates/repo/commit_page.tmpl +++ b/templates/repo/commit_page.tmpl @@ -222,7 +222,7 @@ {{.Verification.SigningSSHKey.Fingerprint}} {{else}} {{.locale.Tr "repo.commits.gpg_key_id"}}: - {{.Verification.SigningKey.KeyID}} + {{.Verification.SigningKey.PaddedKeyID}} {{end}} {{else}} {{svg "octicon-shield-lock" 16 "mr-3"}} @@ -231,7 +231,7 @@ {{.Verification.SigningSSHKey.Fingerprint}} {{else}} {{.locale.Tr "repo.commits.gpg_key_id"}}: - {{.Verification.SigningKey.KeyID}} + {{.Verification.SigningKey.PaddedKeyID}} {{end}} {{end}} {{else if .Verification.Warning}} @@ -241,14 +241,14 @@ {{.Verification.SigningSSHKey.Fingerprint}} {{else}} {{.locale.Tr "repo.commits.gpg_key_id"}}: - {{.Verification.SigningKey.KeyID}} + {{.Verification.SigningKey.PaddedKeyID}} {{end}} {{else}} {{if .Verification.SigningKey}} {{if ne .Verification.SigningKey.KeyID ""}} {{svg "octicon-shield" 16 "mr-3"}} {{.locale.Tr "repo.commits.gpg_key_id"}}: - {{.Verification.SigningKey.KeyID}} + {{.Verification.SigningKey.PaddedKeyID}} {{end}} {{end}} {{if .Verification.SigningSSHKey}} diff --git a/templates/user/settings/keys_gpg.tmpl b/templates/user/settings/keys_gpg.tmpl index fbb0da5302837..58835081796f3 100644 --- a/templates/user/settings/keys_gpg.tmpl +++ b/templates/user/settings/keys_gpg.tmpl @@ -22,7 +22,7 @@

{{.locale.Tr "settings.gpg_token_help"}}

-

{{$.locale.Tr "settings.gpg_token_code" .TokenToSign .KeyID}}

+

{{$.locale.Tr "settings.gpg_token_code" .TokenToSign .PaddedKeyID}}

@@ -64,8 +64,8 @@ {{svg "octicon-mail"}} {{$.locale.Tr "settings.gpg_key_matched_identities"}} {{range .Emails}}{{.Email}} {{end}} {{end}}
- {{$.locale.Tr "settings.key_id"}}: {{.KeyID}} - {{$.locale.Tr "settings.subkeys"}}: {{range .SubsKey}} {{.KeyID}} {{end}} + {{$.locale.Tr "settings.key_id"}}: {{.PaddedKeyID}} + {{$.locale.Tr "settings.subkeys"}}: {{range .SubsKey}} {{.PaddedKeyID}} {{end}}
{{$.locale.Tr "settings.add_on"}} {{.AddedUnix.FormatShort}} @@ -87,7 +87,7 @@

{{$.locale.Tr "settings.gpg_token_help"}}

-

{{$.locale.Tr "settings.gpg_token_code" $.TokenToSign .KeyID}}

+

{{$.locale.Tr "settings.gpg_token_code" $.TokenToSign .PaddedKeyID}}


From 6784a707d101ef31a9ef3a46468c28e1d9700ac8 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Sun, 21 Aug 2022 14:50:27 +0800 Subject: [PATCH 08/14] Fix graceful doc (#20883) --- modules/graceful/manager.go | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/modules/graceful/manager.go b/modules/graceful/manager.go index 8766cfca0efb4..21f019fb56f8c 100644 --- a/modules/graceful/manager.go +++ b/modules/graceful/manager.go @@ -24,11 +24,12 @@ const ( stateTerminate ) -// There are three places that could inherit sockets: +// There are some places that could inherit sockets: // // * HTTP or HTTPS main listener +// * HTTP or HTTPS install listener // * HTTP redirection fallback -// * SSH +// * Builtin SSH listener // // If you add an additional place you must increment this number // and add a function to call manager.InformCleanup if it's not going to be used @@ -305,8 +306,9 @@ func (g *Manager) setState(st state) { g.state = st } -// InformCleanup tells the cleanup wait group that we have either taken a listener -// or will not be taking a listener +// InformCleanup tells the cleanup wait group that we have either taken a listener or will not be taking a listener. +// At the moment the total number of servers (numberOfServersToCreate) are pre-defined as a const before global init, +// so this function MUST be called if a server is not used. func (g *Manager) InformCleanup() { g.createServerWaitGroup.Done() } From 6d3181406d87503dbd15e4a7c764c8963f13977f Mon Sep 17 00:00:00 2001 From: zeripath Date: Sun, 21 Aug 2022 14:28:15 +0100 Subject: [PATCH 09/14] Double check CloneURL is acceptable (#20869) Some Migration Downloaders provide re-writing of CloneURLs that may point to unallowed urls. Recheck after the CloneURL is rewritten. Signed-off-by: Andrew Thornton --- services/migrations/dump.go | 8 ++++++-- services/migrations/gitea_uploader_test.go | 2 +- services/migrations/migrate.go | 19 +++++++++++++++++-- 3 files changed, 24 insertions(+), 5 deletions(-) diff --git a/services/migrations/dump.go b/services/migrations/dump.go index a9ec459519e56..f040c6c6630fe 100644 --- a/services/migrations/dump.go +++ b/services/migrations/dump.go @@ -560,6 +560,10 @@ func (g *RepositoryDumper) Finish() error { // DumpRepository dump repository according MigrateOptions to a local directory func DumpRepository(ctx context.Context, baseDir, ownerName string, opts base.MigrateOptions) error { + doer, err := user_model.GetAdminUser() + if err != nil { + return err + } downloader, err := newDownloader(ctx, ownerName, opts) if err != nil { return err @@ -569,7 +573,7 @@ func DumpRepository(ctx context.Context, baseDir, ownerName string, opts base.Mi return err } - if err := migrateRepository(downloader, uploader, opts, nil); err != nil { + if err := migrateRepository(doer, downloader, uploader, opts, nil); err != nil { if err1 := uploader.Rollback(); err1 != nil { log.Error("rollback failed: %v", err1) } @@ -641,7 +645,7 @@ func RestoreRepository(ctx context.Context, baseDir, ownerName, repoName string, return err } - if err = migrateRepository(downloader, uploader, migrateOpts, nil); err != nil { + if err = migrateRepository(doer, downloader, uploader, migrateOpts, nil); err != nil { if err1 := uploader.Rollback(); err1 != nil { log.Error("rollback failed: %v", err1) } diff --git a/services/migrations/gitea_uploader_test.go b/services/migrations/gitea_uploader_test.go index 9fb3d6ffd6fc0..1f1c4bde6fccb 100644 --- a/services/migrations/gitea_uploader_test.go +++ b/services/migrations/gitea_uploader_test.go @@ -46,7 +46,7 @@ func TestGiteaUploadRepo(t *testing.T) { uploader = NewGiteaLocalUploader(graceful.GetManager().HammerContext(), user, user.Name, repoName) ) - err := migrateRepository(downloader, uploader, base.MigrateOptions{ + err := migrateRepository(user, downloader, uploader, base.MigrateOptions{ CloneAddr: "https://github.com/go-xorm/builder", RepoName: repoName, AuthUsername: "", diff --git a/services/migrations/migrate.go b/services/migrations/migrate.go index 9460c66dbce97..a5715072c5133 100644 --- a/services/migrations/migrate.go +++ b/services/migrations/migrate.go @@ -128,7 +128,7 @@ func MigrateRepository(ctx context.Context, doer *user_model.User, ownerName str uploader := NewGiteaLocalUploader(ctx, doer, ownerName, opts.RepoName) uploader.gitServiceType = opts.GitServiceType - if err := migrateRepository(downloader, uploader, opts, messenger); err != nil { + if err := migrateRepository(doer, downloader, uploader, opts, messenger); err != nil { if err1 := uploader.Rollback(); err1 != nil { log.Error("rollback failed: %v", err1) } @@ -177,7 +177,7 @@ func newDownloader(ctx context.Context, ownerName string, opts base.MigrateOptio // migrateRepository will download information and then upload it to Uploader, this is a simple // process for small repository. For a big repository, save all the data to disk // before upload is better -func migrateRepository(downloader base.Downloader, uploader base.Uploader, opts base.MigrateOptions, messenger base.Messenger) error { +func migrateRepository(doer *user_model.User, downloader base.Downloader, uploader base.Uploader, opts base.MigrateOptions, messenger base.Messenger) error { if messenger == nil { messenger = base.NilMessenger } @@ -198,6 +198,21 @@ func migrateRepository(downloader base.Downloader, uploader base.Uploader, opts return err } + // If the downloader is not a RepositoryRestorer then we need to recheck the CloneURL + if _, ok := downloader.(*RepositoryRestorer); !ok { + // Now the clone URL can be rewritten by the downloader so we must recheck + if err := IsMigrateURLAllowed(repo.CloneURL, doer); err != nil { + return err + } + + // And so can the original URL too so again we must recheck + if repo.OriginalURL != "" { + if err := IsMigrateURLAllowed(repo.OriginalURL, doer); err != nil { + return err + } + } + } + log.Trace("migrating git data from %s", repo.CloneURL) messenger("repo.migrate.migrating_git") if err = uploader.CreateRepo(repo, opts); err != nil { From 0b4c166e8a90beeb1e71ee2fc16b3a240517c82d Mon Sep 17 00:00:00 2001 From: Gusted Date: Sun, 21 Aug 2022 18:24:05 +0200 Subject: [PATCH 10/14] Fix SQL Query for `SearchTeam` (#20844) - Currently the function takes in the `UserID` option, but isn't being used within the SQL query. This patch fixes that by checking that only teams are being returned that the user belongs to. Fix #20829 Co-authored-by: delvh --- integrations/api_team_test.go | 2 +- integrations/api_user_orgs_test.go | 22 ++++++++++++++++++ integrations/org_test.go | 9 ++++---- models/fixtures/org_user.yml | 6 +++++ models/fixtures/user.yml | 2 +- models/organization/team.go | 37 ++++++++++++++++++++---------- routers/web/org/teams.go | 2 +- 7 files changed, 61 insertions(+), 19 deletions(-) diff --git a/integrations/api_team_test.go b/integrations/api_team_test.go index 82824d610bd64..9ea7a6f787090 100644 --- a/integrations/api_team_test.go +++ b/integrations/api_team_test.go @@ -223,7 +223,7 @@ func TestAPITeamSearch(t *testing.T) { defer prepareTestEnv(t)() user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) - org := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 3}) + org := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 17}) var results TeamSearchResults diff --git a/integrations/api_user_orgs_test.go b/integrations/api_user_orgs_test.go index 9f3487cb7fb67..1555b5339066c 100644 --- a/integrations/api_user_orgs_test.go +++ b/integrations/api_user_orgs_test.go @@ -26,8 +26,19 @@ func TestUserOrgs(t *testing.T) { orgs := getUserOrgs(t, adminUsername, normalUsername) user3 := unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: "user3"}) + user17 := unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: "user17"}) assert.Equal(t, []*api.Organization{ + { + ID: 17, + UserName: user17.Name, + FullName: user17.FullName, + AvatarURL: user17.AvatarLink(), + Description: "", + Website: "", + Location: "", + Visibility: "public", + }, { ID: 3, UserName: user3.Name, @@ -82,8 +93,19 @@ func TestMyOrgs(t *testing.T) { var orgs []*api.Organization DecodeJSON(t, resp, &orgs) user3 := unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: "user3"}) + user17 := unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: "user17"}) assert.Equal(t, []*api.Organization{ + { + ID: 17, + UserName: user17.Name, + FullName: user17.FullName, + AvatarURL: user17.AvatarLink(), + Description: "", + Website: "", + Location: "", + Visibility: "public", + }, { ID: 3, UserName: user3.Name, diff --git a/integrations/org_test.go b/integrations/org_test.go index e4677f58de763..9fb1175d7a611 100644 --- a/integrations/org_test.go +++ b/integrations/org_test.go @@ -197,8 +197,8 @@ func TestOrgRestrictedUser(t *testing.T) { func TestTeamSearch(t *testing.T) { defer prepareTestEnv(t)() - user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) - org := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 3}) + user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 15}) + org := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 17}) var results TeamSearchResults @@ -209,8 +209,9 @@ func TestTeamSearch(t *testing.T) { resp := session.MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &results) assert.NotEmpty(t, results.Data) - assert.Len(t, results.Data, 1) - assert.Equal(t, "test_team", results.Data[0].Name) + assert.Len(t, results.Data, 2) + assert.Equal(t, "review_team", results.Data[0].Name) + assert.Equal(t, "test_team", results.Data[1].Name) // no access if not organization member user5 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 5}) diff --git a/models/fixtures/org_user.yml b/models/fixtures/org_user.yml index a0bc4b9b43a83..e06d94cfcdc97 100644 --- a/models/fixtures/org_user.yml +++ b/models/fixtures/org_user.yml @@ -63,3 +63,9 @@ uid: 29 org_id: 17 is_public: true + +- + id: 12 + uid: 2 + org_id: 17 + is_public: true diff --git a/models/fixtures/user.yml b/models/fixtures/user.yml index 67ba869c76b08..87405bfd261a4 100644 --- a/models/fixtures/user.yml +++ b/models/fixtures/user.yml @@ -309,7 +309,7 @@ avatar_email: user17@example.com num_repos: 2 is_active: true - num_members: 3 + num_members: 4 num_teams: 3 - diff --git a/models/organization/team.go b/models/organization/team.go index 0b53c84d67036..6787b9e0fa39f 100644 --- a/models/organization/team.go +++ b/models/organization/team.go @@ -96,16 +96,7 @@ type SearchTeamOptions struct { IncludeDesc bool } -// SearchTeam search for teams. Caller is responsible to check permissions. -func SearchTeam(opts *SearchTeamOptions) ([]*Team, int64, error) { - if opts.Page <= 0 { - opts.Page = 1 - } - if opts.PageSize == 0 { - // Default limit - opts.PageSize = 10 - } - +func (opts *SearchTeamOptions) toCond() builder.Cond { cond := builder.NewCond() if len(opts.Keyword) > 0 { @@ -117,10 +108,28 @@ func SearchTeam(opts *SearchTeamOptions) ([]*Team, int64, error) { cond = cond.And(keywordCond) } - cond = cond.And(builder.Eq{"org_id": opts.OrgID}) + if opts.OrgID > 0 { + cond = cond.And(builder.Eq{"`team`.org_id": opts.OrgID}) + } + + if opts.UserID > 0 { + cond = cond.And(builder.Eq{"team_user.uid": opts.UserID}) + } + + return cond +} +// SearchTeam search for teams. Caller is responsible to check permissions. +func SearchTeam(opts *SearchTeamOptions) ([]*Team, int64, error) { sess := db.GetEngine(db.DefaultContext) + opts.SetDefaultValues() + cond := opts.toCond() + + if opts.UserID > 0 { + sess = sess.Join("INNER", "team_user", "team_user.team_id = team.id") + } + count, err := sess. Where(cond). Count(new(Team)) @@ -128,7 +137,10 @@ func SearchTeam(opts *SearchTeamOptions) ([]*Team, int64, error) { return nil, 0, err } - sess = sess.Where(cond) + if opts.UserID > 0 { + sess = sess.Join("INNER", "team_user", "team_user.team_id = team.id") + } + if opts.PageSize == -1 { opts.PageSize = int(count) } else { @@ -137,6 +149,7 @@ func SearchTeam(opts *SearchTeamOptions) ([]*Team, int64, error) { teams := make([]*Team, 0, opts.PageSize) if err = sess. + Where(cond). OrderBy("lower_name"). Find(&teams); err != nil { return nil, 0, err diff --git a/routers/web/org/teams.go b/routers/web/org/teams.go index 9ee66a1a3e86c..a3c3acb4f493e 100644 --- a/routers/web/org/teams.go +++ b/routers/web/org/teams.go @@ -339,7 +339,7 @@ func SearchTeam(ctx *context.Context) { } opts := &organization.SearchTeamOptions{ - UserID: ctx.Doer.ID, + // UserID is not set because the router already requires the doer to be an org admin. Thus, we don't need to restrict to teams that the user belongs in Keyword: ctx.FormTrim("q"), OrgID: ctx.Org.Organization.ID, IncludeDesc: ctx.FormString("include_desc") == "" || ctx.FormBool("include_desc"), From 943753f560fab8bb01946618b16c694bc2032827 Mon Sep 17 00:00:00 2001 From: zeripath Date: Sun, 21 Aug 2022 19:20:43 +0100 Subject: [PATCH 11/14] Support Proxy protocol (#12527) This PR adds functionality to allow Gitea to sit behind an HAProxy and HAProxy protocolled connections directly. Fix #7508 Signed-off-by: Andrew Thornton --- cmd/web.go | 26 +- cmd/web_acme.go | 4 +- cmd/web_graceful.go | 8 +- cmd/web_https.go | 10 +- custom/conf/app.example.ini | 22 +- .../doc/advanced/config-cheat-sheet.en-us.md | 8 + modules/graceful/server.go | 48 +- modules/graceful/server_http.go | 8 +- modules/private/internal.go | 28 +- modules/proxyprotocol/conn.go | 506 ++++++++++++++++++ modules/proxyprotocol/errors.go | 45 ++ modules/proxyprotocol/listener.go | 47 ++ modules/proxyprotocol/util.go | 15 + modules/setting/setting.go | 82 +-- modules/ssh/ssh_graceful.go | 2 +- 15 files changed, 786 insertions(+), 73 deletions(-) create mode 100644 modules/proxyprotocol/conn.go create mode 100644 modules/proxyprotocol/errors.go create mode 100644 modules/proxyprotocol/listener.go create mode 100644 modules/proxyprotocol/util.go diff --git a/cmd/web.go b/cmd/web.go index 3bc61b04433f4..43f106f780b0b 100644 --- a/cmd/web.go +++ b/cmd/web.go @@ -76,7 +76,7 @@ func runHTTPRedirector() { http.Redirect(w, r, target, http.StatusTemporaryRedirect) }) - err := runHTTP("tcp", source, "HTTP Redirector", handler) + err := runHTTP("tcp", source, "HTTP Redirector", handler, setting.RedirectorUseProxyProtocol) if err != nil { log.Fatal("Failed to start port redirection: %v", err) } @@ -231,40 +231,38 @@ func listen(m http.Handler, handleRedirector bool) error { if handleRedirector { NoHTTPRedirector() } - err = runHTTP("tcp", listenAddr, "Web", m) + err = runHTTP("tcp", listenAddr, "Web", m, setting.UseProxyProtocol) case setting.HTTPS: if setting.EnableAcme { err = runACME(listenAddr, m) break - } else { - if handleRedirector { - if setting.RedirectOtherPort { - go runHTTPRedirector() - } else { - NoHTTPRedirector() - } + } + if handleRedirector { + if setting.RedirectOtherPort { + go runHTTPRedirector() + } else { + NoHTTPRedirector() } - err = runHTTPS("tcp", listenAddr, "Web", setting.CertFile, setting.KeyFile, m) } + err = runHTTPS("tcp", listenAddr, "Web", setting.CertFile, setting.KeyFile, m, setting.UseProxyProtocol, setting.ProxyProtocolTLSBridging) case setting.FCGI: if handleRedirector { NoHTTPRedirector() } - err = runFCGI("tcp", listenAddr, "FCGI Web", m) + err = runFCGI("tcp", listenAddr, "FCGI Web", m, setting.UseProxyProtocol) case setting.HTTPUnix: if handleRedirector { NoHTTPRedirector() } - err = runHTTP("unix", listenAddr, "Web", m) + err = runHTTP("unix", listenAddr, "Web", m, setting.UseProxyProtocol) case setting.FCGIUnix: if handleRedirector { NoHTTPRedirector() } - err = runFCGI("unix", listenAddr, "Web", m) + err = runFCGI("unix", listenAddr, "Web", m, setting.UseProxyProtocol) default: log.Fatal("Invalid protocol: %s", setting.Protocol) } - if err != nil { log.Critical("Failed to start server: %v", err) } diff --git a/cmd/web_acme.go b/cmd/web_acme.go index 57b400dae6e7e..d8e550b321dd5 100644 --- a/cmd/web_acme.go +++ b/cmd/web_acme.go @@ -113,14 +113,14 @@ func runACME(listenAddr string, m http.Handler) error { log.Info("Running Let's Encrypt handler on %s", setting.HTTPAddr+":"+setting.PortToRedirect) // all traffic coming into HTTP will be redirect to HTTPS automatically (LE HTTP-01 validation happens here) - err := runHTTP("tcp", setting.HTTPAddr+":"+setting.PortToRedirect, "Let's Encrypt HTTP Challenge", myACME.HTTPChallengeHandler(http.HandlerFunc(runLetsEncryptFallbackHandler))) + err := runHTTP("tcp", setting.HTTPAddr+":"+setting.PortToRedirect, "Let's Encrypt HTTP Challenge", myACME.HTTPChallengeHandler(http.HandlerFunc(runLetsEncryptFallbackHandler)), setting.RedirectorUseProxyProtocol) if err != nil { log.Fatal("Failed to start the Let's Encrypt handler on port %s: %v", setting.PortToRedirect, err) } }() } - return runHTTPSWithTLSConfig("tcp", listenAddr, "Web", tlsConfig, m) + return runHTTPSWithTLSConfig("tcp", listenAddr, "Web", tlsConfig, m, setting.UseProxyProtocol, setting.ProxyProtocolTLSBridging) } func runLetsEncryptFallbackHandler(w http.ResponseWriter, r *http.Request) { diff --git a/cmd/web_graceful.go b/cmd/web_graceful.go index 1618208c557eb..ba88cc59c21cc 100644 --- a/cmd/web_graceful.go +++ b/cmd/web_graceful.go @@ -15,8 +15,8 @@ import ( "code.gitea.io/gitea/modules/setting" ) -func runHTTP(network, listenAddr, name string, m http.Handler) error { - return graceful.HTTPListenAndServe(network, listenAddr, name, m) +func runHTTP(network, listenAddr, name string, m http.Handler, useProxyProtocol bool) error { + return graceful.HTTPListenAndServe(network, listenAddr, name, m, useProxyProtocol) } // NoHTTPRedirector tells our cleanup routine that we will not be using a fallback http redirector @@ -36,7 +36,7 @@ func NoInstallListener() { graceful.GetManager().InformCleanup() } -func runFCGI(network, listenAddr, name string, m http.Handler) error { +func runFCGI(network, listenAddr, name string, m http.Handler, useProxyProtocol bool) error { // This needs to handle stdin as fcgi point fcgiServer := graceful.NewServer(network, listenAddr, name) @@ -47,7 +47,7 @@ func runFCGI(network, listenAddr, name string, m http.Handler) error { } m.ServeHTTP(resp, req) })) - }) + }, useProxyProtocol) if err != nil { log.Fatal("Failed to start FCGI main server: %v", err) } diff --git a/cmd/web_https.go b/cmd/web_https.go index b0910ca04000f..aac11517a69f8 100644 --- a/cmd/web_https.go +++ b/cmd/web_https.go @@ -129,14 +129,14 @@ var ( defaultCiphersChaChaFirst = append(defaultCiphersChaCha, defaultCiphersAES...) ) -// runHTTPs listens on the provided network address and then calls +// runHTTPS listens on the provided network address and then calls // Serve to handle requests on incoming TLS connections. // // Filenames containing a certificate and matching private key for the server must // be provided. If the certificate is signed by a certificate authority, the // certFile should be the concatenation of the server's certificate followed by the // CA's certificate. -func runHTTPS(network, listenAddr, name, certFile, keyFile string, m http.Handler) error { +func runHTTPS(network, listenAddr, name, certFile, keyFile string, m http.Handler, useProxyProtocol, proxyProtocolTLSBridging bool) error { tlsConfig := &tls.Config{} if tlsConfig.NextProtos == nil { tlsConfig.NextProtos = []string{"h2", "http/1.1"} @@ -184,9 +184,9 @@ func runHTTPS(network, listenAddr, name, certFile, keyFile string, m http.Handle return err } - return graceful.HTTPListenAndServeTLSConfig(network, listenAddr, name, tlsConfig, m) + return graceful.HTTPListenAndServeTLSConfig(network, listenAddr, name, tlsConfig, m, useProxyProtocol, proxyProtocolTLSBridging) } -func runHTTPSWithTLSConfig(network, listenAddr, name string, tlsConfig *tls.Config, m http.Handler) error { - return graceful.HTTPListenAndServeTLSConfig(network, listenAddr, name, tlsConfig, m) +func runHTTPSWithTLSConfig(network, listenAddr, name string, tlsConfig *tls.Config, m http.Handler, useProxyProtocol, proxyProtocolTLSBridging bool) error { + return graceful.HTTPListenAndServeTLSConfig(network, listenAddr, name, tlsConfig, m, useProxyProtocol, proxyProtocolTLSBridging) } diff --git a/custom/conf/app.example.ini b/custom/conf/app.example.ini index 5e612fb42860e..0949c3d399f5d 100644 --- a/custom/conf/app.example.ini +++ b/custom/conf/app.example.ini @@ -29,6 +29,18 @@ RUN_MODE = ; prod ;; The protocol the server listens on. One of 'http', 'https', 'unix' or 'fcgi'. Defaults to 'http' ;PROTOCOL = http ;; +;; Expect PROXY protocol headers on connections +;USE_PROXY_PROTOCOL = false +;; +;; Use PROXY protocol in TLS Bridging mode +;PROXY_PROTOCOL_TLS_BRIDGING = false +;; +; Timeout to wait for PROXY protocol header (set to 0 to have no timeout) +;PROXY_PROTOCOL_HEADER_TIMEOUT=5s +;; +; Accept PROXY protocol headers with UNKNOWN type +;PROXY_PROTOCOL_ACCEPT_UNKNOWN=false +;; ;; Set the domain for the server ;DOMAIN = localhost ;; @@ -51,6 +63,8 @@ RUN_MODE = ; prod ;REDIRECT_OTHER_PORT = false ;PORT_TO_REDIRECT = 80 ;; +;; expect PROXY protocol header on connections to https redirector. +;REDIRECTOR_USE_PROXY_PROTOCOL = %(USE_PROXY_PROTOCOL) ;; Minimum and maximum supported TLS versions ;SSL_MIN_VERSION=TLSv1.2 ;SSL_MAX_VERSION= @@ -76,13 +90,19 @@ RUN_MODE = ; prod ;; Do not set this variable if PROTOCOL is set to 'unix'. ;LOCAL_ROOT_URL = %(PROTOCOL)s://%(HTTP_ADDR)s:%(HTTP_PORT)s/ ;; +;; When making local connections pass the PROXY protocol header. +;LOCAL_USE_PROXY_PROTOCOL = %(USE_PROXY_PROTOCOL) +;; ;; Disable SSH feature when not available ;DISABLE_SSH = false ;; ;; Whether to use the builtin SSH server or not. ;START_SSH_SERVER = false ;; -;; Username to use for the builtin SSH server. +;; Expect PROXY protocol header on connections to the built-in SSH server +;SSH_SERVER_USE_PROXY_PROTOCOL = false +;; +;; Username to use for the builtin SSH server. If blank, then it is the value of RUN_USER. ;BUILTIN_SSH_SERVER_USER = %(RUN_USER)s ;; ;; Domain name to be exposed in clone URL diff --git a/docs/content/doc/advanced/config-cheat-sheet.en-us.md b/docs/content/doc/advanced/config-cheat-sheet.en-us.md index 37f887d4aa3dd..8ce8641365ddc 100644 --- a/docs/content/doc/advanced/config-cheat-sheet.en-us.md +++ b/docs/content/doc/advanced/config-cheat-sheet.en-us.md @@ -238,6 +238,10 @@ The following configuration set `Content-Type: application/vnd.android.package-a ## Server (`server`) - `PROTOCOL`: **http**: \[http, https, fcgi, http+unix, fcgi+unix\] +- `USE_PROXY_PROTOCOL`: **false**: Expect PROXY protocol headers on connections +- `PROXY_PROTOCOL_TLS_BRIDGING`: **false**: When protocol is https, expect PROXY protocol headers after TLS negotiation. +- `PROXY_PROTOCOL_HEADER_TIMEOUT`: **5s**: Timeout to wait for PROXY protocol header (set to 0 to have no timeout) +- `PROXY_PROTOCOL_ACCEPT_UNKNOWN`: **false**: Accept PROXY protocol headers with Unknown type. - `DOMAIN`: **localhost**: Domain name of this server. - `ROOT_URL`: **%(PROTOCOL)s://%(DOMAIN)s:%(HTTP\_PORT)s/**: Overwrite the automatically generated public URL. @@ -262,12 +266,15 @@ The following configuration set `Content-Type: application/vnd.android.package-a most cases you do not need to change the default value. Alter it only if your SSH server node is not the same as HTTP node. Do not set this variable if `PROTOCOL` is set to `http+unix`. +- `LOCAL_USE_PROXY_PROTOCOL`: **%(USE_PROXY_PROTOCOL)**: When making local connections pass the PROXY protocol header. + This should be set to false if the local connection will go through the proxy. - `PER_WRITE_TIMEOUT`: **30s**: Timeout for any write to the connection. (Set to -1 to disable all timeouts.) - `PER_WRITE_PER_KB_TIMEOUT`: **10s**: Timeout per Kb written to connections. - `DISABLE_SSH`: **false**: Disable SSH feature when it's not available. - `START_SSH_SERVER`: **false**: When enabled, use the built-in SSH server. +- `SSH_SERVER_USE_PROXY_PROTOCOL`: **false**: Expect PROXY protocol header on connections to the built-in SSH Server. - `BUILTIN_SSH_SERVER_USER`: **%(RUN_USER)s**: Username to use for the built-in SSH Server. - `SSH_USER`: **%(BUILTIN_SSH_SERVER_USER)**: SSH username displayed in clone URLs. This is only for people who configure the SSH server themselves; in most cases, you want to leave this blank and modify the `BUILTIN_SSH_SERVER_USER`. - `SSH_DOMAIN`: **%(DOMAIN)s**: Domain name of this server, used for displayed clone URL. @@ -313,6 +320,7 @@ The following configuration set `Content-Type: application/vnd.android.package-a - `LFS_LOCKS_PAGING_NUM`: **50**: Maximum number of LFS Locks returned per page. - `REDIRECT_OTHER_PORT`: **false**: If true and `PROTOCOL` is https, allows redirecting http requests on `PORT_TO_REDIRECT` to the https port Gitea listens on. +- `REDIRECTOR_USE_PROXY_PROTOCOL`: **%(USE_PROXY_PROTOCOL)**: expect PROXY protocol header on connections to https redirector. - `PORT_TO_REDIRECT`: **80**: Port for the http redirection service to listen on. Used when `REDIRECT_OTHER_PORT` is true. - `SSL_MIN_VERSION`: **TLSv1.2**: Set the minimum version of ssl support. - `SSL_MAX_VERSION`: **\**: Set the maximum version of ssl support. diff --git a/modules/graceful/server.go b/modules/graceful/server.go index 159a9879df2f9..30a460a943c51 100644 --- a/modules/graceful/server.go +++ b/modules/graceful/server.go @@ -16,6 +16,7 @@ import ( "time" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/proxyprotocol" "code.gitea.io/gitea/modules/setting" ) @@ -79,16 +80,27 @@ func NewServer(network, address, name string) *Server { // ListenAndServe listens on the provided network address and then calls Serve // to handle requests on incoming connections. -func (srv *Server) ListenAndServe(serve ServeFunction) error { +func (srv *Server) ListenAndServe(serve ServeFunction, useProxyProtocol bool) error { go srv.awaitShutdown() - l, err := GetListener(srv.network, srv.address) + listener, err := GetListener(srv.network, srv.address) if err != nil { log.Error("Unable to GetListener: %v", err) return err } - srv.listener = newWrappedListener(l, srv) + // we need to wrap the listener to take account of our lifecycle + listener = newWrappedListener(listener, srv) + + // Now we need to take account of ProxyProtocol settings... + if useProxyProtocol { + listener = &proxyprotocol.Listener{ + Listener: listener, + ProxyHeaderTimeout: setting.ProxyProtocolHeaderTimeout, + AcceptUnknown: setting.ProxyProtocolAcceptUnknown, + } + } + srv.listener = listener srv.BeforeBegin(srv.network, srv.address) @@ -97,22 +109,44 @@ func (srv *Server) ListenAndServe(serve ServeFunction) error { // ListenAndServeTLSConfig listens on the provided network address and then calls // Serve to handle requests on incoming TLS connections. -func (srv *Server) ListenAndServeTLSConfig(tlsConfig *tls.Config, serve ServeFunction) error { +func (srv *Server) ListenAndServeTLSConfig(tlsConfig *tls.Config, serve ServeFunction, useProxyProtocol, proxyProtocolTLSBridging bool) error { go srv.awaitShutdown() if tlsConfig.MinVersion == 0 { tlsConfig.MinVersion = tls.VersionTLS12 } - l, err := GetListener(srv.network, srv.address) + listener, err := GetListener(srv.network, srv.address) if err != nil { log.Error("Unable to get Listener: %v", err) return err } - wl := newWrappedListener(l, srv) - srv.listener = tls.NewListener(wl, tlsConfig) + // we need to wrap the listener to take account of our lifecycle + listener = newWrappedListener(listener, srv) + + // Now we need to take account of ProxyProtocol settings... If we're not bridging then we expect that the proxy will forward the connection to us + if useProxyProtocol && !proxyProtocolTLSBridging { + listener = &proxyprotocol.Listener{ + Listener: listener, + ProxyHeaderTimeout: setting.ProxyProtocolHeaderTimeout, + AcceptUnknown: setting.ProxyProtocolAcceptUnknown, + } + } + + // Now handle the tls protocol + listener = tls.NewListener(listener, tlsConfig) + + // Now if we're bridging then we need the proxy to tell us who we're bridging for... + if useProxyProtocol && proxyProtocolTLSBridging { + listener = &proxyprotocol.Listener{ + Listener: listener, + ProxyHeaderTimeout: setting.ProxyProtocolHeaderTimeout, + AcceptUnknown: setting.ProxyProtocolAcceptUnknown, + } + } + srv.listener = listener srv.BeforeBegin(srv.network, srv.address) return srv.Serve(serve) diff --git a/modules/graceful/server_http.go b/modules/graceful/server_http.go index f7b22ceb5e0a7..8ab2bdf41ff9a 100644 --- a/modules/graceful/server_http.go +++ b/modules/graceful/server_http.go @@ -28,14 +28,14 @@ func newHTTPServer(network, address, name string, handler http.Handler) (*Server // HTTPListenAndServe listens on the provided network address and then calls Serve // to handle requests on incoming connections. -func HTTPListenAndServe(network, address, name string, handler http.Handler) error { +func HTTPListenAndServe(network, address, name string, handler http.Handler, useProxyProtocol bool) error { server, lHandler := newHTTPServer(network, address, name, handler) - return server.ListenAndServe(lHandler) + return server.ListenAndServe(lHandler, useProxyProtocol) } // HTTPListenAndServeTLSConfig listens on the provided network address and then calls Serve // to handle requests on incoming connections. -func HTTPListenAndServeTLSConfig(network, address, name string, tlsConfig *tls.Config, handler http.Handler) error { +func HTTPListenAndServeTLSConfig(network, address, name string, tlsConfig *tls.Config, handler http.Handler, useProxyProtocol, proxyProtocolTLSBridging bool) error { server, lHandler := newHTTPServer(network, address, name, handler) - return server.ListenAndServeTLSConfig(tlsConfig, lHandler) + return server.ListenAndServeTLSConfig(tlsConfig, lHandler, useProxyProtocol, proxyProtocolTLSBridging) } diff --git a/modules/private/internal.go b/modules/private/internal.go index a77a990627b86..2ea516ba80e19 100644 --- a/modules/private/internal.go +++ b/modules/private/internal.go @@ -14,6 +14,7 @@ import ( "code.gitea.io/gitea/modules/httplib" "code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/proxyprotocol" "code.gitea.io/gitea/modules/setting" ) @@ -50,7 +51,32 @@ func newInternalRequest(ctx context.Context, url, method string) *httplib.Reques req.SetTransport(&http.Transport{ DialContext: func(ctx context.Context, _, _ string) (net.Conn, error) { var d net.Dialer - return d.DialContext(ctx, "unix", setting.HTTPAddr) + conn, err := d.DialContext(ctx, "unix", setting.HTTPAddr) + if err != nil { + return conn, err + } + if setting.LocalUseProxyProtocol { + if err = proxyprotocol.WriteLocalHeader(conn); err != nil { + _ = conn.Close() + return nil, err + } + } + return conn, err + }, + }) + } else if setting.LocalUseProxyProtocol { + req.SetTransport(&http.Transport{ + DialContext: func(ctx context.Context, network, address string) (net.Conn, error) { + var d net.Dialer + conn, err := d.DialContext(ctx, network, address) + if err != nil { + return conn, err + } + if err = proxyprotocol.WriteLocalHeader(conn); err != nil { + _ = conn.Close() + return nil, err + } + return conn, err }, }) } diff --git a/modules/proxyprotocol/conn.go b/modules/proxyprotocol/conn.go new file mode 100644 index 0000000000000..10333b204d65f --- /dev/null +++ b/modules/proxyprotocol/conn.go @@ -0,0 +1,506 @@ +// Copyright 2020 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package proxyprotocol + +import ( + "bufio" + "bytes" + "encoding/binary" + "io" + "net" + "strconv" + "strings" + "sync" + "time" + + "code.gitea.io/gitea/modules/log" +) + +var ( + // v1Prefix is the string we look for at the start of a connection + // to check if this connection is using the proxy protocol + v1Prefix = []byte("PROXY ") + v1PrefixLen = len(v1Prefix) + v2Prefix = []byte("\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A") + v2PrefixLen = len(v2Prefix) +) + +// Conn is used to wrap and underlying connection which is speaking the +// Proxy Protocol. RemoteAddr() will return the address of the client +// instead of the proxy address. +type Conn struct { + bufReader *bufio.Reader + conn net.Conn + localAddr net.Addr + remoteAddr net.Addr + once sync.Once + proxyHeaderTimeout time.Duration + acceptUnknown bool +} + +// NewConn is used to wrap a net.Conn speaking the proxy protocol into +// a proxyprotocol.Conn +func NewConn(conn net.Conn, timeout time.Duration) *Conn { + pConn := &Conn{ + bufReader: bufio.NewReader(conn), + conn: conn, + proxyHeaderTimeout: timeout, + } + return pConn +} + +// Read reads data from the connection. +// It will initially read the proxy protocol header. +// If there is an error parsing the header, it is returned and the socket is closed. +func (p *Conn) Read(b []byte) (int, error) { + if err := p.readProxyHeaderOnce(); err != nil { + return 0, err + } + return p.bufReader.Read(b) +} + +// ReadFrom reads data from a provided reader and copies it to the connection. +func (p *Conn) ReadFrom(r io.Reader) (int64, error) { + if err := p.readProxyHeaderOnce(); err != nil { + return 0, err + } + if rf, ok := p.conn.(io.ReaderFrom); ok { + return rf.ReadFrom(r) + } + return io.Copy(p.conn, r) +} + +// WriteTo reads data from the connection and writes it to the writer. +// It will initially read the proxy protocol header. +// If there is an error parsing the header, it is returned and the socket is closed. +func (p *Conn) WriteTo(w io.Writer) (int64, error) { + if err := p.readProxyHeaderOnce(); err != nil { + return 0, err + } + return p.bufReader.WriteTo(w) +} + +// Write writes data to the connection. +// Write can be made to time out and return an error after a fixed +// time limit; see SetDeadline and SetWriteDeadline. +func (p *Conn) Write(b []byte) (int, error) { + if err := p.readProxyHeaderOnce(); err != nil { + return 0, err + } + return p.conn.Write(b) +} + +// Close closes the connection. +// Any blocked Read or Write operations will be unblocked and return errors. +func (p *Conn) Close() error { + return p.conn.Close() +} + +// LocalAddr returns the local network address. +func (p *Conn) LocalAddr() net.Addr { + _ = p.readProxyHeaderOnce() + if p.localAddr != nil { + return p.localAddr + } + return p.conn.LocalAddr() +} + +// RemoteAddr returns the address of the client if the proxy +// protocol is being used, otherwise just returns the address of +// the socket peer. If there is an error parsing the header, the +// address of the client is not returned, and the socket is closed. +// One implication of this is that the call could block if the +// client is slow. Using a Deadline is recommended if this is called +// before Read() +func (p *Conn) RemoteAddr() net.Addr { + _ = p.readProxyHeaderOnce() + if p.remoteAddr != nil { + return p.remoteAddr + } + return p.conn.RemoteAddr() +} + +// SetDeadline sets the read and write deadlines associated +// with the connection. It is equivalent to calling both +// SetReadDeadline and SetWriteDeadline. +// +// A deadline is an absolute time after which I/O operations +// fail instead of blocking. The deadline applies to all future +// and pending I/O, not just the immediately following call to +// Read or Write. After a deadline has been exceeded, the +// connection can be refreshed by setting a deadline in the future. +// +// If the deadline is exceeded a call to Read or Write or to other +// I/O methods will return an error that wraps os.ErrDeadlineExceeded. +// This can be tested using errors.Is(err, os.ErrDeadlineExceeded). +// The error's Timeout method will return true, but note that there +// are other possible errors for which the Timeout method will +// return true even if the deadline has not been exceeded. +// +// An idle timeout can be implemented by repeatedly extending +// the deadline after successful Read or Write calls. +// +// A zero value for t means I/O operations will not time out. +func (p *Conn) SetDeadline(t time.Time) error { + return p.conn.SetDeadline(t) +} + +// SetReadDeadline sets the deadline for future Read calls +// and any currently-blocked Read call. +// A zero value for t means Read will not time out. +func (p *Conn) SetReadDeadline(t time.Time) error { + return p.conn.SetReadDeadline(t) +} + +// SetWriteDeadline sets the deadline for future Write calls +// and any currently-blocked Write call. +// Even if write times out, it may return n > 0, indicating that +// some of the data was successfully written. +// A zero value for t means Write will not time out. +func (p *Conn) SetWriteDeadline(t time.Time) error { + return p.conn.SetWriteDeadline(t) +} + +// readProxyHeaderOnce will ensure that the proxy header has been read +func (p *Conn) readProxyHeaderOnce() (err error) { + p.once.Do(func() { + if err = p.readProxyHeader(); err != nil && err != io.EOF { + log.Error("Failed to read proxy prefix: %v", err) + p.Close() + p.bufReader = bufio.NewReader(p.conn) + } + }) + return err +} + +func (p *Conn) readProxyHeader() error { + if p.proxyHeaderTimeout != 0 { + readDeadLine := time.Now().Add(p.proxyHeaderTimeout) + _ = p.conn.SetReadDeadline(readDeadLine) + defer func() { + _ = p.conn.SetReadDeadline(time.Time{}) + }() + } + + inp, err := p.bufReader.Peek(v1PrefixLen) + if err != nil { + return err + } + + if bytes.Equal(inp, v1Prefix) { + return p.readV1ProxyHeader() + } + + inp, err = p.bufReader.Peek(v2PrefixLen) + if err != nil { + return err + } + if bytes.Equal(inp, v2Prefix) { + return p.readV2ProxyHeader() + } + + return &ErrBadHeader{inp} +} + +func (p *Conn) readV2ProxyHeader() error { + // The binary header format starts with a constant 12 bytes block containing the + // protocol signature : + // + // \x0D \x0A \x0D \x0A \x00 \x0D \x0A \x51 \x55 \x49 \x54 \x0A + // + // Note that this block contains a null byte at the 5th position, so it must not + // be handled as a null-terminated string. + + if _, err := p.bufReader.Discard(v2PrefixLen); err != nil { + // This shouldn't happen as we have already asserted that there should be enough in the buffer + return err + } + + // The next byte (the 13th one) is the protocol version and command. + version, err := p.bufReader.ReadByte() + if err != nil { + return err + } + + // The 14th byte contains the transport protocol and address family.otocol. + familyByte, err := p.bufReader.ReadByte() + if err != nil { + return err + } + + // The 15th and 16th bytes is the address length in bytes in network endian order. + var addressLen uint16 + if err := binary.Read(p.bufReader, binary.BigEndian, &addressLen); err != nil { + return err + } + + // Now handle the version byte: (14th byte). + // The highest four bits contains the version. As of this specification, it must + // always be sent as \x2 and the receiver must only accept this value. + if version>>4 != 0x2 { + return &ErrBadHeader{append(v2Prefix, version, familyByte, uint8(addressLen>>8), uint8(addressLen&0xff))} + } + + // The lowest four bits represents the command : + switch version & 0xf { + case 0x0: + // - \x0 : LOCAL : the connection was established on purpose by the proxy + // without being relayed. The connection endpoints are the sender and the + // receiver. Such connections exist when the proxy sends health-checks to the + // server. The receiver must accept this connection as valid and must use the + // real connection endpoints and discard the protocol block including the + // family which is ignored. + + // We therefore ignore the 14th, 15th and 16th bytes + p.remoteAddr = p.conn.LocalAddr() + p.localAddr = p.conn.RemoteAddr() + return nil + case 0x1: + // - \x1 : PROXY : the connection was established on behalf of another node, + // and reflects the original connection endpoints. The receiver must then use + // the information provided in the protocol block to get original the address. + default: + // - other values are unassigned and must not be emitted by senders. Receivers + // must drop connections presenting unexpected values here. + return &ErrBadHeader{append(v2Prefix, version, familyByte, uint8(addressLen>>8), uint8(addressLen&0xff))} + } + + // Now handle the familyByte byte: (15th byte). + // The highest 4 bits contain the address family, the lowest 4 bits contain the protocol + + // The address family maps to the original socket family without necessarily + // matching the values internally used by the system. It may be one of : + // + // - 0x0 : AF_UNSPEC : the connection is forwarded for an unknown, unspecified + // or unsupported protocol. The sender should use this family when sending + // LOCAL commands or when dealing with unsupported protocol families. The + // receiver is free to accept the connection anyway and use the real endpoint + // addresses or to reject it. The receiver should ignore address information. + // + // - 0x1 : AF_INET : the forwarded connection uses the AF_INET address family + // (IPv4). The addresses are exactly 4 bytes each in network byte order, + // followed by transport protocol information (typically ports). + // + // - 0x2 : AF_INET6 : the forwarded connection uses the AF_INET6 address family + // (IPv6). The addresses are exactly 16 bytes each in network byte order, + // followed by transport protocol information (typically ports). + // + // - 0x3 : AF_UNIX : the forwarded connection uses the AF_UNIX address family + // (UNIX). The addresses are exactly 108 bytes each. + // + // - other values are unspecified and must not be emitted in version 2 of this + // protocol and must be rejected as invalid by receivers. + + // The transport protocol is specified in the lowest 4 bits of the 14th byte : + // + // - 0x0 : UNSPEC : the connection is forwarded for an unknown, unspecified + // or unsupported protocol. The sender should use this family when sending + // LOCAL commands or when dealing with unsupported protocol families. The + // receiver is free to accept the connection anyway and use the real endpoint + // addresses or to reject it. The receiver should ignore address information. + // + // - 0x1 : STREAM : the forwarded connection uses a SOCK_STREAM protocol (eg: + // TCP or UNIX_STREAM). When used with AF_INET/AF_INET6 (TCP), the addresses + // are followed by the source and destination ports represented on 2 bytes + // each in network byte order. + // + // - 0x2 : DGRAM : the forwarded connection uses a SOCK_DGRAM protocol (eg: + // UDP or UNIX_DGRAM). When used with AF_INET/AF_INET6 (UDP), the addresses + // are followed by the source and destination ports represented on 2 bytes + // each in network byte order. + // + // - other values are unspecified and must not be emitted in version 2 of this + // protocol and must be rejected as invalid by receivers. + + if familyByte>>4 == 0x0 || familyByte&0xf == 0x0 { + // - hi 0x0 : AF_UNSPEC : the connection is forwarded for an unknown address type + // or + // - lo 0x0 : UNSPEC : the connection is forwarded for an unspecified protocol + if !p.acceptUnknown { + p.conn.Close() + return &ErrBadHeader{append(v2Prefix, version, familyByte, uint8(addressLen>>8), uint8(addressLen&0xff))} + } + p.remoteAddr = p.conn.LocalAddr() + p.localAddr = p.conn.RemoteAddr() + _, err = p.bufReader.Discard(int(addressLen)) + return err + } + + // other address or protocol + if (familyByte>>4) > 0x3 || (familyByte&0xf) > 0x2 { + return &ErrBadHeader{append(v2Prefix, version, familyByte, uint8(addressLen>>8), uint8(addressLen&0xff))} + } + + // Handle AF_UNIX addresses + if familyByte>>4 == 0x3 { + // - \x31 : UNIX stream : the forwarded connection uses SOCK_STREAM over the + // AF_UNIX protocol family. Address length is 2*108 = 216 bytes. + // - \x32 : UNIX datagram : the forwarded connection uses SOCK_DGRAM over the + // AF_UNIX protocol family. Address length is 2*108 = 216 bytes. + if addressLen != 216 { + return &ErrBadHeader{append(v2Prefix, version, familyByte, uint8(addressLen>>8), uint8(addressLen&0xff))} + } + remoteName := make([]byte, 108) + localName := make([]byte, 108) + if _, err := p.bufReader.Read(remoteName); err != nil { + return err + } + if _, err := p.bufReader.Read(localName); err != nil { + return err + } + protocol := "unix" + if familyByte&0xf == 2 { + protocol = "unixgram" + } + + p.remoteAddr = &net.UnixAddr{ + Name: string(remoteName), + Net: protocol, + } + p.localAddr = &net.UnixAddr{ + Name: string(localName), + Net: protocol, + } + return nil + } + + var remoteIP []byte + var localIP []byte + var remotePort uint16 + var localPort uint16 + + if familyByte>>4 == 0x1 { + // AF_INET + // - \x11 : TCP over IPv4 : the forwarded connection uses TCP over the AF_INET + // protocol family. Address length is 2*4 + 2*2 = 12 bytes. + // - \x12 : UDP over IPv4 : the forwarded connection uses UDP over the AF_INET + // protocol family. Address length is 2*4 + 2*2 = 12 bytes. + if addressLen != 12 { + return &ErrBadHeader{append(v2Prefix, version, familyByte, uint8(addressLen>>8), uint8(addressLen&0xff))} + } + + remoteIP = make([]byte, 4) + localIP = make([]byte, 4) + } else { + // AF_INET6 + // - \x21 : TCP over IPv6 : the forwarded connection uses TCP over the AF_INET6 + // protocol family. Address length is 2*16 + 2*2 = 36 bytes. + // - \x22 : UDP over IPv6 : the forwarded connection uses UDP over the AF_INET6 + // protocol family. Address length is 2*16 + 2*2 = 36 bytes. + if addressLen != 36 { + return &ErrBadHeader{append(v2Prefix, version, familyByte, uint8(addressLen>>8), uint8(addressLen&0xff))} + } + + remoteIP = make([]byte, 16) + localIP = make([]byte, 16) + } + + if _, err := p.bufReader.Read(remoteIP); err != nil { + return err + } + if _, err := p.bufReader.Read(localIP); err != nil { + return err + } + if err := binary.Read(p.bufReader, binary.BigEndian, &remotePort); err != nil { + return err + } + if err := binary.Read(p.bufReader, binary.BigEndian, &localPort); err != nil { + return err + } + + if familyByte&0xf == 1 { + p.remoteAddr = &net.TCPAddr{ + IP: remoteIP, + Port: int(remotePort), + } + p.localAddr = &net.TCPAddr{ + IP: localIP, + Port: int(localPort), + } + } else { + p.remoteAddr = &net.UDPAddr{ + IP: remoteIP, + Port: int(remotePort), + } + p.localAddr = &net.UDPAddr{ + IP: localIP, + Port: int(localPort), + } + } + return nil +} + +func (p *Conn) readV1ProxyHeader() error { + // Read until a newline + header, err := p.bufReader.ReadString('\n') + if err != nil { + p.conn.Close() + return err + } + + if header[len(header)-2] != '\r' { + return &ErrBadHeader{[]byte(header)} + } + + // Strip the carriage return and new line + header = header[:len(header)-2] + + // Split on spaces, should be (PROXY ) + parts := strings.Split(header, " ") + if len(parts) < 2 { + p.conn.Close() + return &ErrBadHeader{[]byte(header)} + } + + // Verify the type is known + switch parts[1] { + case "UNKNOWN": + if !p.acceptUnknown || len(parts) != 2 { + p.conn.Close() + return &ErrBadHeader{[]byte(header)} + } + p.remoteAddr = p.conn.LocalAddr() + p.localAddr = p.conn.RemoteAddr() + return nil + case "TCP4": + case "TCP6": + default: + p.conn.Close() + return &ErrBadAddressType{parts[1]} + } + + if len(parts) != 6 { + p.conn.Close() + return &ErrBadHeader{[]byte(header)} + } + + // Parse out the remote address + ip := net.ParseIP(parts[2]) + if ip == nil { + p.conn.Close() + return &ErrBadRemote{parts[2], parts[4]} + } + port, err := strconv.Atoi(parts[4]) + if err != nil { + p.conn.Close() + return &ErrBadRemote{parts[2], parts[4]} + } + p.remoteAddr = &net.TCPAddr{IP: ip, Port: port} + + // Parse out the destination address + ip = net.ParseIP(parts[3]) + if ip == nil { + p.conn.Close() + return &ErrBadLocal{parts[3], parts[5]} + } + port, err = strconv.Atoi(parts[5]) + if err != nil { + p.conn.Close() + return &ErrBadLocal{parts[3], parts[5]} + } + p.localAddr = &net.TCPAddr{IP: ip, Port: port} + + return nil +} diff --git a/modules/proxyprotocol/errors.go b/modules/proxyprotocol/errors.go new file mode 100644 index 0000000000000..2acf9d84b0ca0 --- /dev/null +++ b/modules/proxyprotocol/errors.go @@ -0,0 +1,45 @@ +// Copyright 2020 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package proxyprotocol + +import "fmt" + +// ErrBadHeader is an error demonstrating a bad proxy header +type ErrBadHeader struct { + Header []byte +} + +func (e *ErrBadHeader) Error() string { + return fmt.Sprintf("Unexpected proxy header: %v", e.Header) +} + +// ErrBadAddressType is an error demonstrating a bad proxy header with bad Address type +type ErrBadAddressType struct { + Address string +} + +func (e *ErrBadAddressType) Error() string { + return fmt.Sprintf("Unexpected proxy header address type: %s", e.Address) +} + +// ErrBadRemote is an error demonstrating a bad proxy header with bad Remote +type ErrBadRemote struct { + IP string + Port string +} + +func (e *ErrBadRemote) Error() string { + return fmt.Sprintf("Unexpected proxy header remote IP and port: %s %s", e.IP, e.Port) +} + +// ErrBadLocal is an error demonstrating a bad proxy header with bad Local +type ErrBadLocal struct { + IP string + Port string +} + +func (e *ErrBadLocal) Error() string { + return fmt.Sprintf("Unexpected proxy header local IP and port: %s %s", e.IP, e.Port) +} diff --git a/modules/proxyprotocol/listener.go b/modules/proxyprotocol/listener.go new file mode 100644 index 0000000000000..64d9b323e512e --- /dev/null +++ b/modules/proxyprotocol/listener.go @@ -0,0 +1,47 @@ +// Copyright 2020 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package proxyprotocol + +import ( + "net" + "time" +) + +// Listener is used to wrap an underlying listener, +// whose connections may be using the HAProxy Proxy Protocol (version 1 or 2). +// If the connection is using the protocol, the RemoteAddr() will return +// the correct client address. +// +// Optionally define ProxyHeaderTimeout to set a maximum time to +// receive the Proxy Protocol Header. Zero means no timeout. +type Listener struct { + Listener net.Listener + ProxyHeaderTimeout time.Duration + AcceptUnknown bool // allow PROXY UNKNOWN +} + +// Accept implements the Accept method in the Listener interface +// it waits for the next call and returns a wrapped Conn. +func (p *Listener) Accept() (net.Conn, error) { + // Get the underlying connection + conn, err := p.Listener.Accept() + if err != nil { + return nil, err + } + + newConn := NewConn(conn, p.ProxyHeaderTimeout) + newConn.acceptUnknown = p.AcceptUnknown + return newConn, nil +} + +// Close closes the underlying listener. +func (p *Listener) Close() error { + return p.Listener.Close() +} + +// Addr returns the underlying listener's network address. +func (p *Listener) Addr() net.Addr { + return p.Listener.Addr() +} diff --git a/modules/proxyprotocol/util.go b/modules/proxyprotocol/util.go new file mode 100644 index 0000000000000..b12771b686a86 --- /dev/null +++ b/modules/proxyprotocol/util.go @@ -0,0 +1,15 @@ +// Copyright 2020 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package proxyprotocol + +import "io" + +var localHeader = append(v2Prefix, '\x20', '\x00', '\x00', '\x00', '\x00') + +// WriteLocalHeader will write the ProxyProtocol Header for a local connection to the provided writer +func WriteLocalHeader(w io.Writer) error { + _, err := w.Write(localHeader) + return err +} diff --git a/modules/setting/setting.go b/modules/setting/setting.go index 3ab25fef6ea83..931b6523ea9af 100644 --- a/modules/setting/setting.go +++ b/modules/setting/setting.go @@ -94,45 +94,52 @@ var ( LocalURL string // Server settings - Protocol Scheme - Domain string - HTTPAddr string - HTTPPort string - RedirectOtherPort bool - PortToRedirect string - OfflineMode bool - CertFile string - KeyFile string - StaticRootPath string - StaticCacheTime time.Duration - EnableGzip bool - LandingPageURL LandingPage - LandingPageCustom string - UnixSocketPermission uint32 - EnablePprof bool - PprofDataPath string - EnableAcme bool - AcmeTOS bool - AcmeLiveDirectory string - AcmeEmail string - AcmeURL string - AcmeCARoot string - SSLMinimumVersion string - SSLMaximumVersion string - SSLCurvePreferences []string - SSLCipherSuites []string - GracefulRestartable bool - GracefulHammerTime time.Duration - StartupTimeout time.Duration - PerWriteTimeout = 30 * time.Second - PerWritePerKbTimeout = 10 * time.Second - StaticURLPrefix string - AbsoluteAssetURL string + Protocol Scheme + UseProxyProtocol bool // `ini:"USE_PROXY_PROTOCOL"` + ProxyProtocolTLSBridging bool //`ini:"PROXY_PROTOCOL_TLS_BRIDGING"` + ProxyProtocolHeaderTimeout time.Duration + ProxyProtocolAcceptUnknown bool + Domain string + HTTPAddr string + HTTPPort string + LocalUseProxyProtocol bool + RedirectOtherPort bool + RedirectorUseProxyProtocol bool + PortToRedirect string + OfflineMode bool + CertFile string + KeyFile string + StaticRootPath string + StaticCacheTime time.Duration + EnableGzip bool + LandingPageURL LandingPage + LandingPageCustom string + UnixSocketPermission uint32 + EnablePprof bool + PprofDataPath string + EnableAcme bool + AcmeTOS bool + AcmeLiveDirectory string + AcmeEmail string + AcmeURL string + AcmeCARoot string + SSLMinimumVersion string + SSLMaximumVersion string + SSLCurvePreferences []string + SSLCipherSuites []string + GracefulRestartable bool + GracefulHammerTime time.Duration + StartupTimeout time.Duration + PerWriteTimeout = 30 * time.Second + PerWritePerKbTimeout = 10 * time.Second + StaticURLPrefix string + AbsoluteAssetURL string SSH = struct { Disabled bool `ini:"DISABLE_SSH"` StartBuiltinServer bool `ini:"START_SSH_SERVER"` BuiltinServerUser string `ini:"BUILTIN_SSH_SERVER_USER"` + UseProxyProtocol bool `ini:"SSH_SERVER_USE_PROXY_PROTOCOL"` Domain string `ini:"SSH_DOMAIN"` Port int `ini:"SSH_PORT"` User string `ini:"SSH_USER"` @@ -717,6 +724,10 @@ func loadFromConf(allowEmpty bool, extraConfig string) { HTTPAddr = filepath.Join(AppWorkPath, HTTPAddr) } } + UseProxyProtocol = sec.Key("USE_PROXY_PROTOCOL").MustBool(false) + ProxyProtocolTLSBridging = sec.Key("PROXY_PROTOCOL_TLS_BRIDGING").MustBool(false) + ProxyProtocolHeaderTimeout = sec.Key("PROXY_PROTOCOL_HEADER_TIMEOUT").MustDuration(5 * time.Second) + ProxyProtocolAcceptUnknown = sec.Key("PROXY_PROTOCOL_ACCEPT_UNKNOWN").MustBool(false) GracefulRestartable = sec.Key("ALLOW_GRACEFUL_RESTARTS").MustBool(true) GracefulHammerTime = sec.Key("GRACEFUL_HAMMER_TIME").MustDuration(60 * time.Second) StartupTimeout = sec.Key("STARTUP_TIMEOUT").MustDuration(0 * time.Second) @@ -770,8 +781,10 @@ func loadFromConf(allowEmpty bool, extraConfig string) { } LocalURL = sec.Key("LOCAL_ROOT_URL").MustString(defaultLocalURL) LocalURL = strings.TrimRight(LocalURL, "/") + "/" + LocalUseProxyProtocol = sec.Key("LOCAL_USE_PROXY_PROTOCOL").MustBool(UseProxyProtocol) RedirectOtherPort = sec.Key("REDIRECT_OTHER_PORT").MustBool(false) PortToRedirect = sec.Key("PORT_TO_REDIRECT").MustString("80") + RedirectorUseProxyProtocol = sec.Key("REDIRECTOR_USE_PROXY_PROTOCOL").MustBool(UseProxyProtocol) OfflineMode = sec.Key("OFFLINE_MODE").MustBool() DisableRouterLog = sec.Key("DISABLE_ROUTER_LOG").MustBool() if len(StaticRootPath) == 0 { @@ -836,6 +849,7 @@ func loadFromConf(allowEmpty bool, extraConfig string) { SSH.KeygenPath = sec.Key("SSH_KEYGEN_PATH").MustString("ssh-keygen") SSH.Port = sec.Key("SSH_PORT").MustInt(22) SSH.ListenPort = sec.Key("SSH_LISTEN_PORT").MustInt(SSH.Port) + SSH.UseProxyProtocol = sec.Key("SSH_SERVER_USE_PROXY_PROTOCOL").MustBool(false) // When disable SSH, start builtin server value is ignored. if SSH.Disabled { diff --git a/modules/ssh/ssh_graceful.go b/modules/ssh/ssh_graceful.go index 9b91baf09e9bb..166ea0b9829c2 100644 --- a/modules/ssh/ssh_graceful.go +++ b/modules/ssh/ssh_graceful.go @@ -17,7 +17,7 @@ func listen(server *ssh.Server) { gracefulServer.PerWriteTimeout = setting.SSH.PerWriteTimeout gracefulServer.PerWritePerKbTimeout = setting.SSH.PerWritePerKbTimeout - err := gracefulServer.ListenAndServe(server.Serve) + err := gracefulServer.ListenAndServe(server.Serve, setting.SSH.UseProxyProtocol) if err != nil { select { case <-graceful.GetManager().IsShutdown(): From 36dfe544f49b26a77f7ee87d30b5464f1df1a6a7 Mon Sep 17 00:00:00 2001 From: Gary Wang Date: Mon, 22 Aug 2022 02:23:50 +0800 Subject: [PATCH 12/14] Fix mirror address setting not working (#20850) This patch fixes the issue that the mirror address field is ignored from the repo setting form. --- routers/web/repo/setting.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/routers/web/repo/setting.go b/routers/web/repo/setting.go index a59824cecdb42..2a04dc06a3f8c 100644 --- a/routers/web/repo/setting.go +++ b/routers/web/repo/setting.go @@ -228,14 +228,17 @@ func SettingsPost(ctx *context.Context) { form.MirrorPassword, _ = u.User.Password() } - err = migrations.IsMigrateURLAllowed(u.String(), ctx.Doer) + address, err := forms.ParseRemoteAddr(form.MirrorAddress, form.MirrorUsername, form.MirrorPassword) + if err == nil { + err = migrations.IsMigrateURLAllowed(address, ctx.Doer) + } if err != nil { ctx.Data["Err_MirrorAddress"] = true handleSettingRemoteAddrError(ctx, err, form) return } - if err := mirror_service.UpdateAddress(ctx, ctx.Repo.Mirror, u.String()); err != nil { + if err := mirror_service.UpdateAddress(ctx, ctx.Repo.Mirror, address); err != nil { ctx.ServerError("UpdateAddress", err) return } From 585e80a7facd961c7c472ca7cfd9b62fc6711eee Mon Sep 17 00:00:00 2001 From: Gary Wang Date: Mon, 22 Aug 2022 00:20:55 +0000 Subject: [PATCH 13/14] [skip ci] Updated translations via Crowdin --- options/locale/locale_ja-JP.ini | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/options/locale/locale_ja-JP.ini b/options/locale/locale_ja-JP.ini index efb41ddd84c46..04e5d25436827 100644 --- a/options/locale/locale_ja-JP.ini +++ b/options/locale/locale_ja-JP.ini @@ -1035,6 +1035,13 @@ file_view_rendered=レンダリング表示 file_view_raw=Rawデータを見る file_permalink=パーマリンク file_too_large=このファイルは大きすぎるため、表示できません。 +invisible_runes_header=`このファイルには不可視のUnicode文字が含まれています!` +invisible_runes_description=`このファイルには不可視Unicode文字が含まれており、下に見えているものとは異なる処理が行われる可能性があります。 あなたのユースケースが意図的かつ正当な場合はこの警告を無視して構いません。 不可視文字を表示するにはエスケープボタンを使用します。` +ambiguous_runes_header=`このファイルには曖昧(ambiguous)なUnicode文字が含まれています!` +ambiguous_runes_description=`このファイルには曖昧(ambiguous)なUnicode文字が含まれており、あなたが使用しているロケールにおいて他の文字と混同する可能性があります。 あなたのユースケースが意図的かつ正当な場合はこの警告を無視して構いません。 それらの文字をハイライトするにはエスケープボタンを使用します。` +invisible_runes_line=`この行には不可視のUnicode文字があります` +ambiguous_runes_line=`この行には曖昧(ambiguous)なUnicode文字があります` +ambiguous_character=`%[1]c [U+%04[1]X] は %[2]c [U+%04[2]X] と混同するおそれがあります` escape_control_characters=エスケープ unescape_control_characters=エスケープ解除 @@ -2888,6 +2895,7 @@ monitor.queue.nopool.title=ワーカープールはありません monitor.queue.nopool.desc=このキューは他のキューをラップし、これ自体にはワーカープールがありません。 monitor.queue.wrapped.desc=wrappedキューは、すぐに開始されないキューをラップし、入ってきたリクエストをチャンネルにバッファリングします。 これ自体にはワーカープールがありません。 monitor.queue.persistable-channel.desc=persistable-channelキューは二つのキューをラップします。 一つはchannelキューで、自分のワーカープールを持ちます。もう一つはlevelキューで、前回のシャットダウンからリクエストを引き継ぐためのものです。 これ自体にはワーカープールがありません。 +monitor.queue.flush=掃き出しワーカー monitor.queue.pool.timeout=タイムアウト monitor.queue.pool.addworkers.title=ワーカーの追加 monitor.queue.pool.addworkers.submit=ワーカーを追加 From 7854c447d920334e941f43d00622592a0869ef0d Mon Sep 17 00:00:00 2001 From: techknowlogick Date: Sun, 21 Aug 2022 22:14:13 -0400 Subject: [PATCH 14/14] update current stable version --- docs/config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/config.yaml b/docs/config.yaml index d9544bd301775..92e033a90758f 100644 --- a/docs/config.yaml +++ b/docs/config.yaml @@ -18,7 +18,7 @@ params: description: Git with a cup of tea author: The Gitea Authors website: https://docs.gitea.io - version: 1.16.9 + version: 1.17.1 minGoVersion: 1.18 goVersion: 1.19 minNodeVersion: 14