From e56005f901141f7dcd5e82fc0755e6637f0784d8 Mon Sep 17 00:00:00 2001 From: tryfail <31454293+tryfail@users.noreply.github.com> Date: Mon, 1 Aug 2022 06:16:38 +0100 Subject: [PATCH 1/6] Fix typos in backup documentation (#20567) Fixed multiple typos "/user/local/bin" to the proper path "/usr/local/bin" --- docs/content/doc/installation/with-docker.zh-cn.md | 8 ++++---- docs/content/doc/usage/backup-and-restore.en-us.md | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/content/doc/installation/with-docker.zh-cn.md b/docs/content/doc/installation/with-docker.zh-cn.md index 9122ee84264f7..91892fdaf95c0 100644 --- a/docs/content/doc/installation/with-docker.zh-cn.md +++ b/docs/content/doc/installation/with-docker.zh-cn.md @@ -301,7 +301,7 @@ volumes: sudo -u git ssh-keygen -t rsa -b 4096 -C "Gitea Host Key" ``` -在下一步中,需要在主机上创建一个名为 `/user/local/bin/gitea` 的文件(具有可执行权限)。该文件将发出从主机到容器的 SSH 转发。将以下内容添加到 `/user/local/bin/gitea`: +在下一步中,需要在主机上创建一个名为 `/usr/local/bin/gitea` 的文件(具有可执行权限)。该文件将发出从主机到容器的 SSH 转发。将以下内容添加到 `/usr/local/bin/gitea`: ```bash ssh -p 2222 -o StrictHostKeyChecking=no git@127.0.0.1 "SSH_ORIGINAL_COMMAND=\"$SSH_ORIGINAL_COMMAND\" $0 $@" @@ -324,14 +324,14 @@ ports: ssh-rsa # other keys from users -command="/user/local/bin/gitea --config=/data/gitea/conf/app.ini serv key-1",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty +command="/usr/local/bin/gitea --config=/data/gitea/conf/app.ini serv key-1",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty ``` 这是详细的说明,当发出 SSH 请求时会发生什么: 1. 使用 `git` 用户向主机发出 SSH 请求,例如 `git clone git@domain:user/repo.git`。 -2. 在 `/home/git/.ssh/authorized_keys` 中,该命令执行 `/user/local/bin/gitea` 脚本。 -3. `/user/local/bin/gitea` 将 SSH 请求转发到端口 2222,该端口已映射到容器的 SSH 端口(22)。 +2. 在 `/home/git/.ssh/authorized_keys` 中,该命令执行 `/usr/local/bin/gitea` 脚本。 +3. `/usr/local/bin/gitea` 将 SSH 请求转发到端口 2222,该端口已映射到容器的 SSH 端口(22)。 4. 由于 `/home/git/.ssh/authorized_keys` 中存在 `git` 用户的公钥,因此身份验证主机 → 容器成功,并且 SSH 请求转发到在 docker 容器中运行的 Gitea。 如果在 Gitea Web 界面中添加了新的 SSH 密钥,它将以与现有密钥相同的方式附加到 `.ssh/authorized_keys` 中。 diff --git a/docs/content/doc/usage/backup-and-restore.en-us.md b/docs/content/doc/usage/backup-and-restore.en-us.md index 95e6d376d09d2..c9c41c413c771 100644 --- a/docs/content/doc/usage/backup-and-restore.en-us.md +++ b/docs/content/doc/usage/backup-and-restore.en-us.md @@ -74,7 +74,7 @@ The command has to be executed with the `RUN_USER = ` specified in Example: ```none -docker exec -u -it -w <--tempdir> $(docker ps -qf 'name=^$') bash -c '/user/local/bin/gitea dump -c ' +docker exec -u -it -w <--tempdir> $(docker ps -qf 'name=^$') bash -c '/usr/local/bin/gitea dump -c ' ``` \*Note: `--tempdir` refers to the temporary directory of the docker environment used by Gitea; if you have not specified a custom `--tempdir`, then Gitea uses `/tmp` or the `TMPDIR` environment variable of the docker container. For `--tempdir` adjust your `docker exec` command options accordingly. From 72b1fd7fdde76815ce3b2e6d19978fa7ade30a66 Mon Sep 17 00:00:00 2001 From: Gary Wang Date: Mon, 1 Aug 2022 20:15:40 +0800 Subject: [PATCH 2/6] Should also support upper-case README files (#20581) Co-authored-by: Lunny Xiao --- modules/markup/renderer.go | 2 ++ modules/markup/renderer_test.go | 10 ++++++++++ 2 files changed, 12 insertions(+) diff --git a/modules/markup/renderer.go b/modules/markup/renderer.go index 9278d4c65d4ac..5f69dc72354f0 100644 --- a/modules/markup/renderer.go +++ b/modules/markup/renderer.go @@ -328,11 +328,13 @@ func IsReadmeFile(name string) bool { // the length of the provided extension list. // Note that the '.' should be provided in ext, e.g ".md" func IsReadmeFileExtension(name string, ext ...string) (int, bool) { + name = strings.ToLower(name) if len(name) < 6 || name[:6] != "readme" { return 0, false } for i, extension := range ext { + extension = strings.ToLower(extension) if name[6:] == extension { return i, true } diff --git a/modules/markup/renderer_test.go b/modules/markup/renderer_test.go index 1e0f7db194f75..950ee15b91072 100644 --- a/modules/markup/renderer_test.go +++ b/modules/markup/renderer_test.go @@ -58,6 +58,16 @@ func TestMisc_IsReadmeFile(t *testing.T) { expected: true, idx: 0, }, + { + name: "README.md", + expected: true, + idx: 0, + }, + { + name: "ReAdMe.Md", + expected: true, + idx: 0, + }, { name: "readme.txt", expected: true, From 4f14c6de1e98461f5e06dc1c5d44521999e5a5c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?PEN=C2=B2?= Date: Mon, 1 Aug 2022 23:36:50 +0800 Subject: [PATCH 3/6] docs: zh-cn translations for fail2ban setup (#20588) Co-authored-by: wxiaoguang --- .../content/doc/usage/fail2ban-setup.zh-cn.md | 92 +++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 docs/content/doc/usage/fail2ban-setup.zh-cn.md diff --git a/docs/content/doc/usage/fail2ban-setup.zh-cn.md b/docs/content/doc/usage/fail2ban-setup.zh-cn.md new file mode 100644 index 0000000000000..446d192aa619f --- /dev/null +++ b/docs/content/doc/usage/fail2ban-setup.zh-cn.md @@ -0,0 +1,92 @@ +--- +date: "2022-08-01T00:00:00+00:00" +title: "使用: 设置 Fail2ban" +slug: "fail2ban-setup" +weight: 16 +toc: false +draft: false +menu: + sidebar: + parent: "usage" + name: "设置 Fail2ban" + weight: 16 + identifier: "fail2ban-setup" +--- + +# 使用 Fail2ban 阻止攻击者的暴力登录 + +**Fail2ban 检查客户端登录日志,将多次登录失败的客户端识别为攻击者并在一段时间内阻止其访问服务。如果你的实例是公开的,这一点尤其重要。请管理员仔细设置 fail2ban,错误的配置将导致防火墙阻止你访问自己的服务器。** + +Gitea 会在日志文件 `log/gitea.log` 中记录登录失败的 CLI、SSH 或 HTTP 客户端 IP 地址,而你需要将 Gitea 的日志输出模式从默认的 `console` 更改为 `file`。这表示将日志输出到文件,使得 fail2ban 可以定期扫描日志内容。 + +当用户的身份验证失败时,日志中会记录此类信息: + +```log +2018/04/26 18:15:54 [I] Failed authentication attempt for user from xxx.xxx.xxx.xxx +``` + +```log +2020/10/15 16:08:44 [E] invalid credentials from xxx.xxx.xxx.xxx +``` + +## 设置 Fail2ban + +添加日志过滤器规则到配置文件 `/etc/fail2ban/filter.d/gitea.conf`: + +```ini +[Definition] +failregex = .*(Failed authentication attempt|invalid credentials|Attempted access of unknown user).* from +ignoreregex = +``` + +添加监狱规则到配置文件 `/etc/fail2ban/jail.d/gitea.conf`: + +```ini +[gitea] +enabled = true +filter = gitea +logpath = /var/lib/gitea/log/gitea.log +maxretry = 10 +findtime = 3600 +bantime = 900 +action = iptables-allports +``` + +如果你的 Gitea 实例运行在 Docker 容器中,并且直接将容器端口暴露到外部网络, +你还需要添加 `chain="FORWARD"` 到监狱规则配置文件 `/etc/fail2ban/jail.d/gitea-docker.conf` +以适应 Docker 的网络转发规则。但如果你在容器的宿主机上使用 Nginx 反向代理连接到 Gitea 则无需这样配置。 + +```ini +[gitea-docker] +enabled = true +filter = gitea +logpath = /var/lib/gitea/log/gitea.log +maxretry = 10 +findtime = 3600 +bantime = 900 +action = iptables-allports[chain="FORWARD"] +``` + +最后,运行 `systemctl restart fail2ban` 即可应用更改。现在,你可以使用 `systemctl status fail2ban` 检查 fail2ban 运行状态。 + +上述规则规定客户端在 1 小时内,如果登录失败的次数达到 10 次,则通过 iptables 锁定该客户端 IP 地址 15 分钟。 + +## 设置反向代理 + +如果你使用 Nginx 反向代理到 Gitea 实例,你还需要设置 Nginx 的 HTTP 头部值 `X-Real-IP` 将真实的客户端 IP 地址传递给 Gitea。否则 Gitea 程序会将客户端地址错误解析为反向代理服务器的地址,例如回环地址 `127.0.0.1`。 + +``` +proxy_set_header X-Real-IP $remote_addr; +``` + +额外注意,在 Gitea 的配置文件 `app.ini` 中存在下列默认值: + +``` +REVERSE_PROXY_LIMIT = 1 +REVERSE_PROXY_TRUSTED_PROXIES = 127.0.0.0/8,::1/128 +``` + +`REVERSE_PROXY_LIMIT` 限制反向代理服务器的层数,设置为 `0` 表示不使用这些标头。 +`REVERSE_PROXY_TRUSTED_PROXIES` 表示受信任的反向代理服务器网络地址, +经过该网络地址转发来的流量会经过解析 `X-Real-IP` 头部得到真实客户端地址。 +(参考 [configuration cheat sheet](https://docs.gitea.io/en-us/config-cheat-sheet/#security-security)) From cf4061921548e1cffff0af66ef34ac793a1696c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?PEN=C2=B2?= Date: Tue, 2 Aug 2022 00:20:59 +0000 Subject: [PATCH 4/6] [skip ci] Updated translations via Crowdin --- options/locale/locale_tr-TR.ini | 71 +++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/options/locale/locale_tr-TR.ini b/options/locale/locale_tr-TR.ini index b17272725b40c..4197f14e5069d 100644 --- a/options/locale/locale_tr-TR.ini +++ b/options/locale/locale_tr-TR.ini @@ -40,9 +40,14 @@ webauthn_sign_in=Güvenlik anahtarınızdaki düğmeye basın. Eğer düğme yok webauthn_press_button=Lütfen güvenlik anahtarınızdaki düğmeye basın… webauthn_use_twofa=Telefonunuzdan iki aşamalı doğrulama kodu kullanın webauthn_error=Güvenlik anahtarınız okunamıyor. +webauthn_unsupported_browser=Tarayıcınız henüz WebAuthn desteklemiyor. webauthn_error_unknown=Bilinmeyen bir hata oluştu. Lütfen tekrar deneyin. +webauthn_error_insecure=WebAuthn sadece güvenli bağlantıyı destekler. HTTP üzerinden test etmek için "localhost" veya "127.0.0.1" adreslerini kullanabilirsiniz. webauthn_error_unable_to_process=Sunucu isteğinizi işleyemedi. webauthn_error_duplicated=Güvenlik anahtarının bu istek için izni yok. Anahtarın halihazırda kayıtlı olmadığından emin olun. +webauthn_error_empty=Bu anahtar için bir isim belirlemelisiniz. +webauthn_error_timeout=Anahtarınız okunamadan zaman aşımı oldu. Lütfen sayfayı yenileyin ve tekrar deneyin. +webauthn_reload=Yeniden yükle repository=Depo organization=Organizasyon @@ -104,7 +109,9 @@ rss_feed=RSS Beslemesi [error] occurred=Bir hata oluştu +report_message=Bunun bir Gitea hatası olduğundan eminseniz, lütfen GitHub sayfasında sorunu arayın veya gerekiyorsa yeni bir sorun açın. missing_csrf=Hatalı İstek: CSRF anahtarı yok +not_found=Hedef bulunamadı. network_error=Ağ hatası [startpage] @@ -560,6 +567,7 @@ comment_type_group_lock=Kilit Durumu comment_type_group_review_request=İnceleme isteği comment_type_group_pull_request_push=Eklenen işlemeler comment_type_group_project=Proje +comment_type_group_issue_ref=Konu referansı saved_successfully=Ayarlarınız başarılı bir şekilde kaydedildi. privacy=Gizlilik keep_activity_private=Etkinliği profil sayfasından gizle @@ -651,6 +659,8 @@ key_signature_gpg_placeholder='-----PGP İMZA BAŞLAT -----' ile başlar verify_gpg_key_success=GPG anahtarı '%s' doğrulandı. ssh_key_verified=Doğrulanmış Anahtar ssh_key_verified_long=Bu anahtar bir belirteç ile doğrulandı ve bu kullanıcı için etkinleştirilmiş herhangi bir e-posta adresi ile uyuşan işlemeleri doğrulamak için kullanılabilir. +ssh_key_verify=Doğrula +ssh_token_help=Şunu kullanarak bir imza oluşturabilirsiniz: subkeys=Alt anahtarlar key_id=Anahtar Kimliği key_name=Anahtar İsmi @@ -800,8 +810,14 @@ visibility_fork_helper=(Bunu değiştirmek tüm çatallamaları etkileyecektir.) clone_helper=Klonlama konusunda yardıma mı ihtiyacınız var? Yardım adresini ziyaret edin. fork_repo=Depoyu Çatalla fork_from=Buradan Çatalla +already_forked=%s deposunu zaten çatalladınız +fork_to_different_account=Başka bir hesaba çatalla fork_visibility_helper=Çatallanmış bir deponun görünürlüğü değiştirilemez. use_template=Bu şablonu kullan +clone_in_vsc=VS Code'ta klonla +download_zip=ZIP indir +download_tar=TAR.GZ indir +download_bundle=BUNDLE indir generate_repo=Depo Oluştur generate_from=Şuradan Oluştur repo_desc=Açıklama @@ -924,6 +940,15 @@ migrate.migrate=%s Konumundan Göç Et migrate.migrating=%s konumundan taşınıyor ... migrate.migrating_failed=%s konumundan taşıma başarısız oldu. migrate.migrating_failed.error=Hata: %s +migrate.migrating_failed_no_addr=Göç başarısız oldu. +migrate.github.description=Github.com veya diğer Github sunucularından veri aktar. +migrate.git.description=Herhangi bir Git hizmetinden sadece bir depoyu aktar. +migrate.gitlab.description=Gitlab.com veya diğer Gitlab sunucularından veri aktar. +migrate.gitea.description=Gitea.com veya diğer Gitea sunucularından veri aktar. +migrate.gogs.description=Notabug.org veya diğer Gogs sunucularından veri aktar. +migrate.onedev.description=Code.onedev.io ve diğer OneDev sunucularından veri aktar. +migrate.codebase.description=Codebasehq.com sitesinden veri aktar. +migrate.gitbucket.description=GitBucket sunucularından veri aktar. migrate.migrating_git=Git Verilerini Taşıma migrate.migrating_topics=Konuları Taşıma migrate.migrating_milestones=Kilometre Taşlarını Taşıma @@ -992,7 +1017,9 @@ commit_graph=İşleme Grafiği commit_graph.select=Dalları seç commit_graph.hide_pr_refs=Değişiklik İsteklerini Gizle commit_graph.monochrome=Siyah Beyaz +commit_graph.color=Renk blame=Suçlama +download_file=Dosya indir normal_view=Normal Görünüm line=satır lines=satır @@ -1020,6 +1047,10 @@ editor.add_tmpl='' eklendi editor.add='%s' ekle editor.update='%s' güncelle editor.delete='%s' sil +editor.patch=Yama Uygula +editor.patching=Yamalanıyor: +editor.fail_to_apply_patch='%s' yaması uygulanamıyor +editor.new_patch=Yeni Yama editor.commit_message_desc=İsteğe bağlı uzun bir açıklama ekleyin… editor.signoff_desc=İşleme günlüğü mesajının sonuna işleyen tarafından imzalanan bir fragman ekleyin. editor.commit_directly_to_this_branch=Doğrudan %s bölümüne uygula. @@ -1044,6 +1075,8 @@ editor.commit_empty_file_text=İşlemek üzere olduğunuz dosya boş. Devam edil editor.no_changes_to_show=Gösterilecek değişiklik yok. editor.fail_to_update_file='%s' dosyası güncellenemedi/oluşturulamadı. editor.fail_to_update_file_summary=Hata Mesajı: +editor.push_rejected_no_message=Değişiklik, bir ileti olmadan sunucu tarafından reddedildi. Git Hooks'u kontrol edin. +editor.push_rejected=Değişiklik sunucu tarafından reddedildi. Lütfen Git Hooks'u kontrol edin. editor.push_rejected_summary=Tam Red Mesajı: editor.add_subdir=Bir dizin ekle… editor.unable_to_upload_files=Şu hata ile dosyalar '%s' 'a yüklenemedi: %v @@ -1071,8 +1104,16 @@ commits.signed_by=İmzalayan commits.signed_by_untrusted_user=Güvenilmeyen kullanıcı tarafından imzalandı commits.signed_by_untrusted_user_unmatched=İşleyici ile eşleşmeyen güvenilmeyen kullanıcı tarafından imzalanmış commits.gpg_key_id=GPG Anahtar Kimliği +commits.ssh_key_fingerprint=SSH Anahtar Parmak İzi +commit.actions=Eylemler +commit.revert=Geri Al +commit.revert-header=Geri al: %s +commit.cherry-pick=Cımbızla +commit.cherry-pick-header=Cımbızla: %s +commit.cherry-pick-content=Cımbızlamak için dal seçin: +ext_issues=Harici Konulara Erişim ext_issues.desc=Dışsal konu takip sistemine bağla. projects=Projeler @@ -1106,6 +1147,7 @@ projects.board.set_default=Varsayılana Ayarla projects.board.set_default_desc=Kategorize edilmemiş konular ve çekme istekleri için bu panoyu varsayılan olarak ayarlayın projects.board.delete=Panoyu Sil projects.board.deletion_desc=Bir proje panosunun silinmesi, ilgili tüm konuları 'Kategorize edilmemiş'e taşır. Devam edilsin mi? +projects.board.color=Renk projects.open=Aç projects.close=Kapat @@ -1212,6 +1254,7 @@ issues.previous=Önceki issues.next=Sonraki issues.open_title=Açık issues.closed_title=Kapalı +issues.draft_title=Taslak issues.num_comments=%d yorum issues.commented_at=`%s yorum yaptı` issues.delete_comment_confirm=Bu yorumu silmek istediğinizden emin misiniz? @@ -1290,6 +1333,9 @@ issues.lock.reason=Kilitleme nedeni issues.lock.title=Konuşmayı kilitle. issues.unlock.title=Konuşmanın kilidini aç. issues.comment_on_locked=Kilitli bir konuya yorum yapamazsınız. +issues.delete=Sil +issues.delete.title=Bu konu silinsin mi? +issues.delete.text=Bu konuyu gerçekten silmek istiyor musunuz? (Bu işlem tüm içeriği kalıcı olarak silecektir. Arşivde tutma niyetiniz varsa silmek yerine kapatmayı düşünün) issues.tracker=Zaman Takibi issues.start_tracking_short=Zamanlayıcıyı Başlat issues.start_tracking=Zaman İzlemeyi Başlat @@ -1329,6 +1375,8 @@ issues.due_date_remove=%[2]s %[1]s bitiş tarihini kaldırdı issues.due_date_overdue=Süresi Geçmiş issues.due_date_invalid=Bitiş tarihi geçersiz veya aralık dışında. Lütfen 'yyyy-aa-gg' biçimini kullanın. issues.dependency.title=Bağımlılıklar +issues.dependency.issue_no_dependencies=Bağımlılık yok. +issues.dependency.pr_no_dependencies=Bağımlılık yok. issues.dependency.add=Bağımlılık ekle… issues.dependency.cancel=İptal issues.dependency.remove=Kaldır @@ -1367,6 +1415,7 @@ issues.review.add_review_request=%s tarafından %s inceleme istedi issues.review.remove_review_request=%s %s için inceleme isteği kaldırıldı issues.review.remove_review_request_self=%s incelemeyi reddetti issues.review.pending=Beklemede +issues.review.pending.tooltip=Bu yorum başkaları tarafından görünmüyor. Bekleyen yorumlarınızı göndermek için, sayfanın üstünde '%s' -> '%s/%s/%s' seçin. issues.review.review=Gözden Geçir issues.review.reviewers=Gözden Geçirenler issues.review.outdated=Eskimiş @@ -1379,14 +1428,25 @@ issues.review.un_resolve_conversation=Konuşmayı çözme issues.review.resolved_by=bu konuşmayı çözümlenmiş olarak işaretledi issues.assignee.error=Beklenmeyen bir hata nedeniyle tüm atananlar eklenmedi. issues.reference_issue.body=Gövde +issues.content_history.deleted=silindi +issues.content_history.edited=düzenlendi +issues.content_history.created=oluşturuldu +issues.content_history.delete_from_history=Geçmişten kaldır +issues.content_history.options=Seçenekler +issues.reference_link=Referans: %s compare.compare_base=temel compare.compare_head=karşılaştır pulls.desc=Değişiklik isteklerini ve kod incelemelerini etkinleştir. pulls.new=Yeni Değişiklik İsteği +pulls.view=Değişiklik İsteği Görüntüle pulls.compare_changes=Yeni Değişiklik İsteği +pulls.allow_edits_from_maintainers_err=Güncelleme başarısız oldu pulls.compare_changes_desc=Birleştirmek için hedef ve kaynak dalı seçin. +pulls.has_viewed_file=Görüldü +pulls.has_changed_since_last_review=Son incelemenizden sonra değişti +pulls.viewed_files_label=%[1]d / %[2]d dosya görüldü pulls.compare_base=birleştir pulls.compare_compare=şuradan çek pulls.switch_comparison_type=Karşılaştırma türünü değiştir @@ -1395,6 +1455,7 @@ pulls.filter_branch=Dal filtrele pulls.no_results=Sonuç bulunamadı. pulls.nothing_to_compare=Bu dallar eşit. Değişiklik isteği oluşturmaya gerek yok. pulls.nothing_to_compare_and_allow_empty_pr=Bu dallar eşittir. Bu Dİ boş olacak. +pulls.has_pull_request=`Bu dallar arasında zaten bir değişiklik isteği var: %[2]s#%[3]d` pulls.create=Değişiklik İsteği Oluştur pulls.title_desc=%[2]s içindeki %[1]d işlemeyi %[3]s ile birleştirmek istiyor pulls.merged_title_desc=%[4]s %[2]s içindeki %[1]d işlemeyi %[3]s ile birleştirdi @@ -1418,6 +1479,8 @@ pulls.remove_prefix=%s ön ekini kaldır pulls.data_broken=Bu değişiklik isteği, çatallama bilgilerinin eksik olması nedeniyle bozuldu. pulls.files_conflicted=Bu değişiklik isteğinde, hedef dalla çakışan değişiklikler var. pulls.is_checking=Birleştirme çakışması denetimi devam ediyor. Birkaç dakika sonra tekrar deneyin. +pulls.is_ancestor=Bu dal zaten hedef dalda mevcut. Birleştirilecek bir şey yok. +pulls.is_empty=Bu daldaki değişiklikler zaten hedef dalda mevcut. Bu boş bir işleme olacaktır. pulls.required_status_check_failed=Bazı gerekli denetimler başarılı olmadı. pulls.required_status_check_missing=Gerekli bazı kontroller eksik. pulls.required_status_check_administrator=Yönetici olarak, bu değişiklik isteğini yine de birleştirebilirsiniz. @@ -1481,6 +1544,9 @@ pulls.merge_instruction_hint=`komut satırı talimat pulls.merge_instruction_step1_desc=Proje deponuzdan yeni bir dala göz atın ve değişiklikleri test edin. pulls.merge_instruction_step2_desc=Gitea'daki değişiklikleri ve güncellemeleri birleştirin. +pulls.auto_merge_button_when_succeed=(Denetlemeler başarılı olduğunda) +pulls.auto_merge_when_succeed=Tüm denetlemeler başarılı olduğundan otomatik olarak birleştir +pulls.auto_merge_newly_scheduled=Değişiklik İsteği tüm denetlemeler başarılı olduğunda birleştirilecek şekilde ayarlanmış. @@ -2234,6 +2300,7 @@ total=Toplam: %d dashboard.statistic=Özet dashboard.operations=Bakım İşlemleri dashboard.system_status=Sistem Durumu +dashboard.statistic_info=Gitea veritabanında %d kullanıcılar, %d organizasyonlar, %d açık anahtarlar, %d depolar, %d izlemeler, %d yıldızlar, ~%d eylemler, %d erişimler, %d konular, %d yorumlar, %d sosyal hesaplar, %d takipler, %d yansılar, %d sürümler, %d kimlik doğrulama kaynakları, %d web istemcileri, %d dönüm noktaları, %d etiketler, %d istemci görevler, %d takımlar, %d güncelleme görevleri, %d ekler bulunuyor. dashboard.operation_name=İşlem Adı dashboard.operation_switch=Geç dashboard.operation_run=Çalıştır @@ -2704,6 +2771,10 @@ notices.delete_success=Sistem bildirimleri silindi. [action] create_repo=depo %s oluşturuldu rename_repo=%[1]s olan depo adını %[3]s buna çevirdi +create_issue=`%[3]s#%[2]s konusunu açtı` +close_issue=`%[3]s#%[2]s konusunu kapattı` +reopen_issue=`%[3]s#%[2]s konusunu tekrar açtı` +comment_issue=`%[3]s#%[2]s konusuna yorum yaptı` transfer_repo=depo %s %s'a aktarıldı delete_tag=%[2]s etiketi %[3]s deposundan silindi delete_branch=%[3]s deposundan %[2]s dalı silindi From ae3b88bef36179fb43ddcf2a14b46ca0969d8aad Mon Sep 17 00:00:00 2001 From: silverwind Date: Tue, 2 Aug 2022 04:52:05 +0200 Subject: [PATCH 5/6] Add default value for clone URLs (#20600) Default clone URLs to HTTP(S) in DOM rendering. JS will immediately replace this if the user preference is SSH. Fixes: https://github.com/go-gitea/gitea/issues/20558 Co-authored-by: wxiaoguang --- templates/repo/clone_buttons.tmpl | 2 +- templates/repo/empty.tmpl | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/templates/repo/clone_buttons.tmpl b/templates/repo/clone_buttons.tmpl index 67a509835f4f6..0d0fe73014739 100644 --- a/templates/repo/clone_buttons.tmpl +++ b/templates/repo/clone_buttons.tmpl @@ -9,7 +9,7 @@ SSH {{end}} - + diff --git a/templates/repo/empty.tmpl b/templates/repo/empty.tmpl index 99f662cd0bb93..24547758a7f4b 100644 --- a/templates/repo/empty.tmpl +++ b/templates/repo/empty.tmpl @@ -37,7 +37,7 @@ git init {{if ne .Repository.DefaultBranch "master"}}git checkout -b {{.Repository.DefaultBranch}}{{end}} git add README.md git commit -m "first commit" -git remote add origin +git remote add origin {{$.CloneButtonOriginLink.HTTPS}} git push -u origin {{.Repository.DefaultBranch}} @@ -46,7 +46,7 @@ git push -u origin {{.Repository.DefaultBranch}}

{{.locale.Tr "repo.push_exist_repo"}}

-
git remote add origin 
+									
git remote add origin {{$.CloneButtonOriginLink.HTTPS}}
 git push -u origin {{.Repository.DefaultBranch}}
From 036dd8a788468e7730b29982747cc3cf8829ce86 Mon Sep 17 00:00:00 2001 From: Clar Fon <15850505+clarfonthey@users.noreply.github.com> Date: Tue, 2 Aug 2022 01:24:18 -0400 Subject: [PATCH 6/6] Rework mailer settings (#18982) * `PROTOCOL`: can be smtp, smtps, smtp+startls, smtp+unix, sendmail, dummy * `SMTP_ADDR`: domain for SMTP, or path to unix socket * `SMTP_PORT`: port for SMTP; defaults to 25 for `smtp`, 465 for `smtps`, and 587 for `smtp+startls` * `ENABLE_HELO`, `HELO_HOSTNAME`: reverse `DISABLE_HELO` to `ENABLE_HELO`; default to false + system hostname * `FORCE_TRUST_SERVER_CERT`: replace the unclear `SKIP_VERIFY` * `CLIENT_CERT_FILE`, `CLIENT_KEY_FILE`, `USE_CLIENT_CERT`: clarify client certificates here Co-authored-by: wxiaoguang Co-authored-by: Lunny Xiao --- cmd/admin.go | 8 +- custom/conf/app.example.ini | 62 +++--- .../doc/advanced/config-cheat-sheet.en-us.md | 52 ++--- modules/setting/mailer.go | 199 ++++++++++++++---- options/locale/locale_en-US.ini | 3 +- routers/install/install.go | 8 +- routers/web/admin/auths.go | 2 +- services/auth/source/smtp/auth.go | 6 +- services/auth/source/smtp/source.go | 2 +- .../auth/source/smtp/source_authenticate.go | 2 +- services/forms/auth_form.go | 2 +- services/forms/user_form.go | 3 +- services/mailer/mailer.go | 95 +++++---- templates/install.tmpl | 8 +- 14 files changed, 297 insertions(+), 155 deletions(-) diff --git a/cmd/admin.go b/cmd/admin.go index 3375435749b05..6c2a8626c41a4 100644 --- a/cmd/admin.go +++ b/cmd/admin.go @@ -414,9 +414,9 @@ var ( Usage: "SMTP Authentication Type (PLAIN/LOGIN/CRAM-MD5) default PLAIN", }, cli.StringFlag{ - Name: "host", + Name: "addr", Value: "", - Usage: "SMTP Host", + Usage: "SMTP Addr", }, cli.IntFlag{ Name: "port", @@ -956,8 +956,8 @@ func parseSMTPConfig(c *cli.Context, conf *smtp.Source) error { } conf.Auth = c.String("auth-type") } - if c.IsSet("host") { - conf.Host = c.String("host") + if c.IsSet("addr") { + conf.Addr = c.String("addr") } if c.IsSet("port") { conf.Port = c.Int("port") diff --git a/custom/conf/app.example.ini b/custom/conf/app.example.ini index ac0c9e9c8670b..367553f1fa44b 100644 --- a/custom/conf/app.example.ini +++ b/custom/conf/app.example.ini @@ -1503,30 +1503,42 @@ ROUTER = console ;; Prefix displayed before subject in mail ;SUBJECT_PREFIX = ;; -;; Mail server -;; Gmail: smtp.gmail.com:587 -;; QQ: smtp.qq.com:465 -;; As per RFC 8314 using Implicit TLS/SMTPS on port 465 (if supported) is recommended, -;; otherwise STARTTLS on port 587 should be used. -;HOST = -;; -;; Disable HELO operation when hostnames are different. -;DISABLE_HELO = -;; -;; Custom hostname for HELO operation, if no value is provided, one is retrieved from system. +;; Mail server protocol. One of "smtp", "smtps", "smtp+startls", "smtp+unix", "sendmail", "dummy". +;; - sendmail: use the operating system's `sendmail` command instead of SMTP. This is common on Linux systems. +;; - dummy: send email messages to the log as a testing phase. +;; If your provider does not explicitly say which protocol it uses but does provide a port, +;; you can set SMTP_PORT instead and this will be inferred. +;; (Before 1.18, this was controlled via MAILER_TYPE and IS_TLS_ENABLED.) +;PROTOCOL = +;; +;; Mail server address, e.g. smtp.gmail.com. +;; For smtp+unix, this should be a path to a unix socket instead. +;; (Before 1.18, this was combined with SMTP_PORT as HOST.) +;SMTP_ADDR = +;; +;; Mail server port. Common ports are: +;; 25: insecure SMTP +;; 465: SMTP Secure +;; 587: StartTLS +;; If no protocol is specified, it will be inferred by this setting. +;; (Before 1.18, this was combined with SMTP_ADDR as HOST.) +;SMTP_PORT = +;; +;; Enable HELO operation. Defaults to true. +;ENABLE_HELO = true +;; +;; Custom hostname for HELO operation. +;; If no value is provided, one is retrieved from system. ;HELO_HOSTNAME = ;; -;; Whether or not to skip verification of certificates; `true` to disable verification. This option is unsafe. Consider adding the certificate to the system trust store instead. -;SKIP_VERIFY = false +;; If set to `true`, completely ignores server certificate validation errors. +;; This option is unsafe. Consider adding the certificate to the system trust store instead. +;FORCE_TRUST_SERVER_CERT = false ;; -;; Use client certificate -;USE_CERTIFICATE = false -;CERT_FILE = custom/mailer/cert.pem -;KEY_FILE = custom/mailer/key.pem -;; -;; Should SMTP connect with TLS, (if port ends with 465 TLS will always be used.) -;; If this is false but STARTTLS is supported the connection will be upgraded to TLS opportunistically. -;IS_TLS_ENABLED = false +;; Use client certificate in connection. +;USE_CLIENT_CERT = false +;CLIENT_CERT_FILE = custom/mailer/cert.pem +;CLIENT_KEY_FILE = custom/mailer/key.pem ;; ;; Mail from address, RFC 5322. This can be just an email address, or the `"Name" ` format ;FROM = @@ -1534,19 +1546,15 @@ ROUTER = console ;; Sometimes it is helpful to use a different address on the envelope. Set this to use ENVELOPE_FROM as the from on the envelope. Set to `<>` to send an empty address. ;ENVELOPE_FROM = ;; -;; Mailer user name and password -;; Please Note: Authentication is only supported when the SMTP server communication is encrypted with TLS (this can be via STARTTLS) or `HOST=localhost`. +;; Mailer user name and password, if required by provider. ;USER = ;; ;; Use PASSWD = `your password` for quoting if you use special characters in the password. ;PASSWD = ;; -;; Send mails as plain text +;; Send mails only in plain text, without HTML alternative ;SEND_AS_PLAIN_TEXT = false ;; -;; Set Mailer Type (either SMTP, sendmail or dummy to just send to the log) -;MAILER_TYPE = smtp -;; ;; Specify an alternative sendmail binary ;SENDMAIL_PATH = sendmail ;; 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 e4e7ad7b19863..c6a4d989a6f61 100644 --- a/docs/content/doc/advanced/config-cheat-sheet.en-us.md +++ b/docs/content/doc/advanced/config-cheat-sheet.en-us.md @@ -647,41 +647,35 @@ Define allowed algorithms and their minimum key length (use -1 to disable a type ## Mailer (`mailer`) - `ENABLED`: **false**: Enable to use a mail service. -- `DISABLE_HELO`: **\**: Disable HELO operation. -- `HELO_HOSTNAME`: **\**: Custom hostname for HELO operation. -- `HOST`: **\**: SMTP mail host address and port (example: smtp.gitea.io:587). - - As per RFC 8314, if supported, Implicit TLS/SMTPS on port 465 is recommended, otherwise opportunistic TLS via STARTTLS on port 587 should be used. -- `IS_TLS_ENABLED` : **false** : Forcibly use TLS to connect even if not on a default SMTPS port. - - Note, if the port ends with `465` Implicit TLS/SMTPS/SMTP over TLS will be used despite this setting. - - Otherwise if `IS_TLS_ENABLED=false` and the server supports `STARTTLS` this will be used. Thus if `STARTTLS` is preferred you should set `IS_TLS_ENABLED=false`. -- `FROM`: **\**: Mail from address, RFC 5322. This can be just an email address, or - the "Name" \ format. -- `ENVELOPE_FROM`: **\**: Address set as the From address on the SMTP mail envelope. Set to `<>` to send an empty address. +- `PROTOCOL`: **\**: Mail server protocol. One of "smtp", "smtps", "smtp+startls", "smtp+unix", "sendmail", "dummy". _Before 1.18, this was inferred from a combination of `MAILER_TYPE` and `IS_TLS_ENABLED`._ + - SMTP family, if your provider does not explicitly say which protocol it uses but does provide a port, you can set SMTP_PORT instead and this will be inferred. + - **sendmail** Use the operating system's `sendmail` command instead of SMTP. This is common on Linux systems. + - **dummy** Send email messages to the log as a testing phase. + - Note that enabling sendmail will ignore all other `mailer` settings except `ENABLED`, `FROM`, `SUBJECT_PREFIX` and `SENDMAIL_PATH`. + - Enabling dummy will ignore all settings except `ENABLED`, `SUBJECT_PREFIX` and `FROM`. +- `SMTP_ADDR`: **\**: Mail server address. e.g. smtp.gmail.com. For smtp+unix, this should be a path to a unix socket instead. _Before 1.18, this was combined with `SMTP_PORT` under the name `HOST`._ +- `SMTP_PORT`: **\**: Mail server port. If no protocol is specified, it will be inferred by this setting. Common ports are listed below. _Before 1.18, this was combined with `SMTP_ADDR` under the name `HOST`._ + - 25: insecure SMTP + - 465: SMTP Secure + - 587: StartTLS +- `USE_CLIENT_CERT`: **false**: Use client certificate for TLS/SSL. +- `CLIENT_CERT_FILE`: **custom/mailer/cert.pem**: Client certificate file. +- `CLIENT_KEY_FILE`: **custom/mailer/key.pem**: Client key file. +- `FORCE_TRUST_SERVER_CERT`: **false**: If set to `true`, completely ignores server certificate validation errors. This option is unsafe. Consider adding the certificate to the system trust store instead. - `USER`: **\**: Username of mailing user (usually the sender's e-mail address). - `PASSWD`: **\**: Password of mailing user. Use \`your password\` for quoting if you use special characters in the password. - - Please note: authentication is only supported when the SMTP server communication is encrypted with TLS (this can be via `STARTTLS`) or `HOST=localhost`. See [Email Setup]({{< relref "doc/usage/email-setup.en-us.md" >}}) for more information. -- `SEND_AS_PLAIN_TEXT`: **false**: Send mails as plain text. -- `SKIP_VERIFY`: **false**: Whether or not to skip verification of certificates; `true` to disable verification. - - **Warning:** This option is unsafe. Consider adding the certificate to the system trust store instead. - - **Note:** Gitea only supports SMTP with STARTTLS. -- `USE_CERTIFICATE`: **false**: Use client certificate. -- `CERT_FILE`: **custom/mailer/cert.pem** -- `KEY_FILE`: **custom/mailer/key.pem** + - Please note: authentication is only supported when the SMTP server communication is encrypted with TLS (this can be via `STARTTLS`) or SMTP host is localhost. See [Email Setup]({{< relref "doc/usage/email-setup.en-us.md" >}}) for more information. +- `ENABLE_HELO`: **true**: Enable HELO operation. +- `HELO_HOSTNAME`: **(retrieved from system)**: HELO hostname. +- `FROM`: **\**: Mail from address, RFC 5322. This can be just an email address, or the "Name" \ format. +- `ENVELOPE_FROM`: **\**: Address set as the From address on the SMTP mail envelope. Set to `<>` to send an empty address. - `SUBJECT_PREFIX`: **\**: Prefix to be placed before e-mail subject lines. -- `MAILER_TYPE`: **smtp**: \[smtp, sendmail, dummy\] - - **smtp** Use SMTP to send mail - - **sendmail** Use the operating system's `sendmail` command instead of SMTP. - This is common on Linux systems. - - **dummy** Send email messages to the log as a testing phase. - - Note that enabling sendmail will ignore all other `mailer` settings except `ENABLED`, - `FROM`, `SUBJECT_PREFIX` and `SENDMAIL_PATH`. - - Enabling dummy will ignore all settings except `ENABLED`, `SUBJECT_PREFIX` and `FROM`. -- `SENDMAIL_PATH`: **sendmail**: The location of sendmail on the operating system (can be - command or full path). -- `SENDMAIL_ARGS`: **_empty_**: Specify any extra sendmail arguments. (NOTE: you should be aware that email addresses can look like options - if your `sendmail` command takes options you must set the option terminator `--`) +- `SENDMAIL_PATH`: **sendmail**: The location of sendmail on the operating system (can be command or full path). +- `SENDMAIL_ARGS`: **\**: Specify any extra sendmail arguments. (NOTE: you should be aware that email addresses can look like options - if your `sendmail` command takes options you must set the option terminator `--`) - `SENDMAIL_TIMEOUT`: **5m**: default timeout for sending email through sendmail - `SENDMAIL_CONVERT_CRLF`: **true**: Most versions of sendmail prefer LF line endings rather than CRLF line endings. Set this to false if your version of sendmail requires CRLF line endings. - `SEND_BUFFER_LEN`: **100**: Buffer length of mailing queue. **DEPRECATED** use `LENGTH` in `[queue.mailer]` +- `SEND_AS_PLAIN_TEXT`: **false**: Send mails only in plain text, without HTML alternative. ## Cache (`cache`) diff --git a/modules/setting/mailer.go b/modules/setting/mailer.go index 8a26f8b0c49f8..d6f1dae0f7156 100644 --- a/modules/setting/mailer.go +++ b/modules/setting/mailer.go @@ -5,7 +5,9 @@ package setting import ( + "net" "net/mail" + "strings" "time" "code.gitea.io/gitea/modules/log" @@ -23,18 +25,19 @@ type Mailer struct { FromName string FromEmail string SendAsPlainText bool - MailerType string SubjectPrefix string // SMTP sender - Host string - User, Passwd string - DisableHelo bool - HeloHostname string - SkipVerify bool - UseCertificate bool - CertFile, KeyFile string - IsTLSEnabled bool + Protocol string + SMTPAddr string + SMTPPort string + User, Passwd string + EnableHelo bool + HeloHostname string + ForceTrustServerCert bool + UseClientCert bool + ClientCertFile string + ClientKeyFile string // Sendmail sender SendmailPath string @@ -56,19 +59,19 @@ func newMailService() { MailService = &Mailer{ Name: sec.Key("NAME").MustString(AppName), SendAsPlainText: sec.Key("SEND_AS_PLAIN_TEXT").MustBool(false), - MailerType: sec.Key("MAILER_TYPE").In("", []string{"smtp", "sendmail", "dummy"}), - - Host: sec.Key("HOST").String(), - User: sec.Key("USER").String(), - Passwd: sec.Key("PASSWD").String(), - DisableHelo: sec.Key("DISABLE_HELO").MustBool(), - HeloHostname: sec.Key("HELO_HOSTNAME").String(), - SkipVerify: sec.Key("SKIP_VERIFY").MustBool(), - UseCertificate: sec.Key("USE_CERTIFICATE").MustBool(), - CertFile: sec.Key("CERT_FILE").String(), - KeyFile: sec.Key("KEY_FILE").String(), - IsTLSEnabled: sec.Key("IS_TLS_ENABLED").MustBool(), - SubjectPrefix: sec.Key("SUBJECT_PREFIX").MustString(""), + + Protocol: sec.Key("PROTOCOL").In("", []string{"smtp", "smtps", "smtp+startls", "smtp+unix", "sendmail", "dummy"}), + SMTPAddr: sec.Key("SMTP_ADDR").String(), + SMTPPort: sec.Key("SMTP_PORT").String(), + User: sec.Key("USER").String(), + Passwd: sec.Key("PASSWD").String(), + EnableHelo: sec.Key("ENABLE_HELO").MustBool(true), + HeloHostname: sec.Key("HELO_HOSTNAME").String(), + ForceTrustServerCert: sec.Key("FORCE_TRUST_SERVER_CERT").MustBool(false), + UseClientCert: sec.Key("USE_CLIENT_CERT").MustBool(false), + ClientCertFile: sec.Key("CLIENT_CERT_FILE").String(), + ClientKeyFile: sec.Key("CLIENT_KEY_FILE").String(), + SubjectPrefix: sec.Key("SUBJECT_PREFIX").MustString(""), SendmailPath: sec.Key("SENDMAIL_PATH").MustString("sendmail"), SendmailTimeout: sec.Key("SENDMAIL_TIMEOUT").MustDuration(5 * time.Minute), @@ -77,26 +80,123 @@ func newMailService() { MailService.From = sec.Key("FROM").MustString(MailService.User) MailService.EnvelopeFrom = sec.Key("ENVELOPE_FROM").MustString("") - // FIXME: DEPRECATED to be removed in v1.18.0 - deprecatedSetting("mailer", "ENABLE_HTML_ALTERNATIVE", "mailer", "SEND_AS_PLAIN_TEXT") - if sec.HasKey("ENABLE_HTML_ALTERNATIVE") { - MailService.SendAsPlainText = !sec.Key("ENABLE_HTML_ALTERNATIVE").MustBool(false) + // FIXME: DEPRECATED to be removed in v1.19.0 + deprecatedSetting("mailer", "MAILER_TYPE", "mailer", "PROTOCOL") + if sec.HasKey("MAILER_TYPE") && !sec.HasKey("PROTOCOL") { + if sec.Key("MAILER_TYPE").String() == "sendmail" { + MailService.Protocol = "sendmail" + } } - // FIXME: DEPRECATED to be removed in v1.18.0 - deprecatedSetting("mailer", "USE_SENDMAIL", "mailer", "MAILER_TYPE") - if sec.HasKey("USE_SENDMAIL") { - if MailService.MailerType == "" && sec.Key("USE_SENDMAIL").MustBool(false) { - MailService.MailerType = "sendmail" + // FIXME: DEPRECATED to be removed in v1.19.0 + deprecatedSetting("mailer", "HOST", "mailer", "SMTP_ADDR") + if sec.HasKey("HOST") && !sec.HasKey("SMTP_ADDR") { + givenHost := sec.Key("HOST").String() + addr, port, err := net.SplitHostPort(givenHost) + if err != nil { + log.Fatal("Invalid mailer.HOST (%s): %v", givenHost, err) } + MailService.SMTPAddr = addr + MailService.SMTPPort = port } - parsed, err := mail.ParseAddress(MailService.From) - if err != nil { - log.Fatal("Invalid mailer.FROM (%s): %v", MailService.From, err) + // FIXME: DEPRECATED to be removed in v1.19.0 + deprecatedSetting("mailer", "IS_TLS_ENABLED", "mailer", "PROTOCOL") + if sec.HasKey("IS_TLS_ENABLED") && !sec.HasKey("PROTOCOL") { + if sec.Key("IS_TLS_ENABLED").MustBool() { + MailService.Protocol = "smtps" + } else { + MailService.Protocol = "smtp+startls" + } + } + + if MailService.SMTPPort == "" { + switch MailService.Protocol { + case "smtp": + MailService.SMTPPort = "25" + case "smtps": + MailService.SMTPPort = "465" + case "smtp+startls": + MailService.SMTPPort = "587" + } + } + + if MailService.Protocol == "" { + if strings.ContainsAny(MailService.SMTPAddr, "/\\") { + MailService.Protocol = "smtp+unix" + } else { + switch MailService.SMTPPort { + case "25": + MailService.Protocol = "smtp" + case "465": + MailService.Protocol = "smtps" + case "587": + MailService.Protocol = "smtp+startls" + default: + log.Error("unable to infer unspecified mailer.PROTOCOL from mailer.SMTP_PORT = %q, assume using smtps", MailService.SMTPPort) + MailService.Protocol = "smtps" + } + } + } + + // we want to warn if users use SMTP on a non-local IP; + // we might as well take the opportunity to check that it has an IP at all + ips := tryResolveAddr(MailService.SMTPAddr) + if MailService.Protocol == "smtp" { + for _, ip := range ips { + if !ip.IsLoopback() { + log.Warn("connecting over insecure SMTP protocol to non-local address is not recommended") + break + } + } + } + + // FIXME: DEPRECATED to be removed in v1.19.0 + deprecatedSetting("mailer", "DISABLE_HELO", "mailer", "ENABLE_HELO") + if sec.HasKey("DISABLE_HELO") && !sec.HasKey("ENABLE_HELO") { + MailService.EnableHelo = !sec.Key("DISABLE_HELO").MustBool() + } + + // FIXME: DEPRECATED to be removed in v1.19.0 + deprecatedSetting("mailer", "SKIP_VERIFY", "mailer", "FORCE_TRUST_SERVER_CERT") + if sec.HasKey("SKIP_VERIFY") && !sec.HasKey("FORCE_TRUST_SERVER_CERT") { + MailService.ForceTrustServerCert = sec.Key("SKIP_VERIFY").MustBool() + } + + // FIXME: DEPRECATED to be removed in v1.19.0 + deprecatedSetting("mailer", "USE_CERTIFICATE", "mailer", "USE_CLIENT_CERT") + if sec.HasKey("USE_CERTIFICATE") && !sec.HasKey("USE_CLIENT_CERT") { + MailService.UseClientCert = sec.Key("USE_CLIENT_CERT").MustBool() + } + + // FIXME: DEPRECATED to be removed in v1.19.0 + deprecatedSetting("mailer", "CERT_FILE", "mailer", "CLIENT_CERT_FILE") + if sec.HasKey("CERT_FILE") && !sec.HasKey("CLIENT_CERT_FILE") { + MailService.ClientCertFile = sec.Key("CERT_FILE").String() + } + + // FIXME: DEPRECATED to be removed in v1.19.0 + deprecatedSetting("mailer", "KEY_FILE", "mailer", "CLIENT_KEY_FILE") + if sec.HasKey("KEY_FILE") && !sec.HasKey("CLIENT_KEY_FILE") { + MailService.ClientKeyFile = sec.Key("KEY_FILE").String() + } + + // FIXME: DEPRECATED to be removed in v1.19.0 + deprecatedSetting("mailer", "ENABLE_HTML_ALTERNATIVE", "mailer", "SEND_AS_PLAIN_TEXT") + if sec.HasKey("ENABLE_HTML_ALTERNATIVE") && !sec.HasKey("SEND_AS_PLAIN_TEXT") { + MailService.SendAsPlainText = !sec.Key("ENABLE_HTML_ALTERNATIVE").MustBool(false) + } + + if MailService.From != "" { + parsed, err := mail.ParseAddress(MailService.From) + if err != nil { + log.Fatal("Invalid mailer.FROM (%s): %v", MailService.From, err) + } + MailService.FromName = parsed.Name + MailService.FromEmail = parsed.Address + } else { + log.Error("no mailer.FROM provided, email system may not work.") } - MailService.FromName = parsed.Name - MailService.FromEmail = parsed.Address switch MailService.EnvelopeFrom { case "": @@ -105,7 +205,7 @@ func newMailService() { MailService.EnvelopeFrom = "" MailService.OverrideEnvelopeFrom = true default: - parsed, err = mail.ParseAddress(MailService.EnvelopeFrom) + parsed, err := mail.ParseAddress(MailService.EnvelopeFrom) if err != nil { log.Fatal("Invalid mailer.ENVELOPE_FROM (%s): %v", MailService.EnvelopeFrom, err) } @@ -113,11 +213,8 @@ func newMailService() { MailService.EnvelopeFrom = parsed.Address } - if MailService.MailerType == "" { - MailService.MailerType = "smtp" - } - - if MailService.MailerType == "sendmail" { + if MailService.Protocol == "sendmail" { + var err error MailService.SendmailArgs, err = shellquote.Split(sec.Key("SENDMAIL_ARGS").String()) if err != nil { log.Error("Failed to parse Sendmail args: %s with error %v", CustomConf, err) @@ -148,3 +245,21 @@ func newNotifyMailService() { Service.EnableNotifyMail = true log.Info("Notify Mail Service Enabled") } + +func tryResolveAddr(addr string) []net.IP { + if strings.HasPrefix(addr, "[") && strings.HasSuffix(addr, "]") { + addr = addr[1 : len(addr)-1] + } + ip := net.ParseIP(addr) + if ip != nil { + ips := make([]net.IP, 1) + ips[0] = ip + return ips + } + ips, err := net.LookupIP(addr) + if err != nil { + log.Warn("could not look up mailer.SMTP_ADDR: %v", err) + return make([]net.IP, 0) + } + return ips +} diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index aad10ce87b1b2..a774bf92fff42 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -179,7 +179,8 @@ log_root_path_helper = Log files will be written to this directory. optional_title = Optional Settings email_title = Email Settings -smtp_host = SMTP Host +smtp_addr = SMTP Host +smtp_port = SMTP Port smtp_from = Send Email As smtp_from_helper = Email address Gitea will use. Enter a plain email address or use the "Name" format. mailer_user = SMTP Username diff --git a/routers/install/install.go b/routers/install/install.go index 27c3509fdec51..8060414a1115a 100644 --- a/routers/install/install.go +++ b/routers/install/install.go @@ -133,7 +133,8 @@ func Install(ctx *context.Context) { // E-mail service settings if setting.MailService != nil { - form.SMTPHost = setting.MailService.Host + form.SMTPAddr = setting.MailService.SMTPAddr + form.SMTPPort = setting.MailService.SMTPPort form.SMTPFrom = setting.MailService.From form.SMTPUser = setting.MailService.User form.SMTPPasswd = setting.MailService.Passwd @@ -421,9 +422,10 @@ func SubmitInstall(ctx *context.Context) { cfg.Section("server").Key("LFS_START_SERVER").SetValue("false") } - if len(strings.TrimSpace(form.SMTPHost)) > 0 { + if len(strings.TrimSpace(form.SMTPAddr)) > 0 { cfg.Section("mailer").Key("ENABLED").SetValue("true") - cfg.Section("mailer").Key("HOST").SetValue(form.SMTPHost) + cfg.Section("mailer").Key("SMTP_ADDR").SetValue(form.SMTPAddr) + cfg.Section("mailer").Key("SMTP_PORT").SetValue(form.SMTPPort) cfg.Section("mailer").Key("FROM").SetValue(form.SMTPFrom) cfg.Section("mailer").Key("USER").SetValue(form.SMTPUser) cfg.Section("mailer").Key("PASSWD").SetValue(form.SMTPPasswd) diff --git a/routers/web/admin/auths.go b/routers/web/admin/auths.go index 7ea8a52809e60..b79b317555966 100644 --- a/routers/web/admin/auths.go +++ b/routers/web/admin/auths.go @@ -159,7 +159,7 @@ func parseLDAPConfig(form forms.AuthenticationForm) *ldap.Source { func parseSMTPConfig(form forms.AuthenticationForm) *smtp.Source { return &smtp.Source{ Auth: form.SMTPAuth, - Host: form.SMTPHost, + Addr: form.SMTPAddr, Port: form.SMTPPort, AllowedDomains: form.AllowedDomains, ForceSMTPS: form.ForceSMTPS, diff --git a/services/auth/source/smtp/auth.go b/services/auth/source/smtp/auth.go index 8d0cbb11cdc9a..a9e4b0e5f4454 100644 --- a/services/auth/source/smtp/auth.go +++ b/services/auth/source/smtp/auth.go @@ -58,10 +58,10 @@ var ErrUnsupportedLoginType = errors.New("Login source is unknown") func Authenticate(a smtp.Auth, source *Source) error { tlsConfig := &tls.Config{ InsecureSkipVerify: source.SkipVerify, - ServerName: source.Host, + ServerName: source.Addr, } - conn, err := net.Dial("tcp", net.JoinHostPort(source.Host, strconv.Itoa(source.Port))) + conn, err := net.Dial("tcp", net.JoinHostPort(source.Addr, strconv.Itoa(source.Port))) if err != nil { return err } @@ -71,7 +71,7 @@ func Authenticate(a smtp.Auth, source *Source) error { conn = tls.Client(conn, tlsConfig) } - client, err := smtp.NewClient(conn, source.Host) + client, err := smtp.NewClient(conn, source.Addr) if err != nil { return fmt.Errorf("failed to create NewClient: %w", err) } diff --git a/services/auth/source/smtp/source.go b/services/auth/source/smtp/source.go index 5e69f912da35b..b2286d42a0ff7 100644 --- a/services/auth/source/smtp/source.go +++ b/services/auth/source/smtp/source.go @@ -19,7 +19,7 @@ import ( // Source holds configuration for the SMTP login source. type Source struct { Auth string - Host string + Addr string Port int AllowedDomains string `xorm:"TEXT"` ForceSMTPS bool diff --git a/services/auth/source/smtp/source_authenticate.go b/services/auth/source/smtp/source_authenticate.go index dff24d494ee0f..63fd3e55110b7 100644 --- a/services/auth/source/smtp/source_authenticate.go +++ b/services/auth/source/smtp/source_authenticate.go @@ -32,7 +32,7 @@ func (source *Source) Authenticate(user *user_model.User, userName, password str var auth smtp.Auth switch source.Auth { case PlainAuthentication: - auth = smtp.PlainAuth("", userName, password, source.Host) + auth = smtp.PlainAuth("", userName, password, source.Addr) case LoginAuthentication: auth = &loginAuthenticator{userName, password} case CRAMMD5Authentication: diff --git a/services/forms/auth_form.go b/services/forms/auth_form.go index 7e7c75675299b..9064be2cca38e 100644 --- a/services/forms/auth_form.go +++ b/services/forms/auth_form.go @@ -45,7 +45,7 @@ type AuthenticationForm struct { IsActive bool IsSyncEnabled bool SMTPAuth string - SMTPHost string + SMTPAddr string SMTPPort int AllowedDomains string SecurityProtocol int `binding:"Range(0,2)"` diff --git a/services/forms/user_form.go b/services/forms/user_form.go index 405b4a9a490f2..c8f2b02d8c809 100644 --- a/services/forms/user_form.go +++ b/services/forms/user_form.go @@ -40,7 +40,8 @@ type InstallForm struct { AppURL string `binding:"Required"` LogRootPath string `binding:"Required"` - SMTPHost string + SMTPAddr string + SMTPPort string SMTPFrom string SMTPUser string `binding:"OmitEmpty;MaxSize(254)" locale:"install.mailer_user"` SMTPPasswd string diff --git a/services/mailer/mailer.go b/services/mailer/mailer.go index f4bc2ddc630cd..c86c54c748c59 100644 --- a/services/mailer/mailer.go +++ b/services/mailer/mailer.go @@ -147,65 +147,82 @@ type smtpSender struct{} func (s *smtpSender) Send(from string, to []string, msg io.WriterTo) error { opts := setting.MailService - host, port, err := net.SplitHostPort(opts.Host) - if err != nil { - return err + var network string + var address string + if opts.Protocol == "smtp+unix" { + network = "unix" + address = opts.SMTPAddr + } else { + network = "tcp" + address = net.JoinHostPort(opts.SMTPAddr, opts.SMTPPort) } - tlsconfig := &tls.Config{ - InsecureSkipVerify: opts.SkipVerify, - ServerName: host, + conn, err := net.Dial(network, address) + if err != nil { + return fmt.Errorf("failed to establish network connection to SMTP server: %v", err) } + defer conn.Close() - if opts.UseCertificate { - cert, err := tls.LoadX509KeyPair(opts.CertFile, opts.KeyFile) - if err != nil { - return err + var tlsconfig *tls.Config + if opts.Protocol == "smtps" || opts.Protocol == "smtp+startls" { + tlsconfig = &tls.Config{ + InsecureSkipVerify: opts.ForceTrustServerCert, + ServerName: opts.SMTPAddr, } - tlsconfig.Certificates = []tls.Certificate{cert} - } - conn, err := net.Dial("tcp", net.JoinHostPort(host, port)) - if err != nil { - return err + if opts.UseClientCert { + cert, err := tls.LoadX509KeyPair(opts.ClientCertFile, opts.ClientKeyFile) + if err != nil { + return fmt.Errorf("could not load SMTP client certificate: %v", err) + } + tlsconfig.Certificates = []tls.Certificate{cert} + } } - defer conn.Close() - isSecureConn := opts.IsTLSEnabled || (strings.HasSuffix(port, "465")) - // Start TLS directly if the port ends with 465 (SMTPS protocol) - if isSecureConn { + if opts.Protocol == "smtps" { conn = tls.Client(conn, tlsconfig) } + host := "localhost" + if opts.Protocol == "smtp+unix" { + host = opts.SMTPAddr + } client, err := smtp.NewClient(conn, host) if err != nil { - return fmt.Errorf("NewClient: %v", err) + return fmt.Errorf("could not initiate SMTP session: %v", err) } - if !opts.DisableHelo { + if opts.EnableHelo { hostname := opts.HeloHostname if len(hostname) == 0 { hostname, err = os.Hostname() if err != nil { - return err + return fmt.Errorf("could not retrieve system hostname: %v", err) } } if err = client.Hello(hostname); err != nil { - return fmt.Errorf("Hello: %v", err) + return fmt.Errorf("failed to issue HELO command: %v", err) } } - // If not using SMTPS, always use STARTTLS if available - hasStartTLS, _ := client.Extension("STARTTLS") - if !isSecureConn && hasStartTLS { - if err = client.StartTLS(tlsconfig); err != nil { - return fmt.Errorf("StartTLS: %v", err) + if opts.Protocol == "smtp+startls" { + hasStartTLS, _ := client.Extension("STARTTLS") + if hasStartTLS { + if err = client.StartTLS(tlsconfig); err != nil { + return fmt.Errorf("failed to start TLS connection: %v", err) + } + } else { + log.Warn("StartTLS requested, but SMTP server does not support it; falling back to regular SMTP") } } canAuth, options := client.Extension("AUTH") - if canAuth && len(opts.User) > 0 { + if len(opts.User) > 0 { + if !canAuth { + return fmt.Errorf("SMTP server does not support AUTH, but credentials provided") + } + var auth smtp.Auth if strings.Contains(options, "CRAM-MD5") { @@ -219,34 +236,34 @@ func (s *smtpSender) Send(from string, to []string, msg io.WriterTo) error { if auth != nil { if err = client.Auth(auth); err != nil { - return fmt.Errorf("Auth: %v", err) + return fmt.Errorf("failed to authenticate SMTP: %v", err) } } } if opts.OverrideEnvelopeFrom { if err = client.Mail(opts.EnvelopeFrom); err != nil { - return fmt.Errorf("Mail: %v", err) + return fmt.Errorf("failed to issue MAIL command: %v", err) } } else { if err = client.Mail(from); err != nil { - return fmt.Errorf("Mail: %v", err) + return fmt.Errorf("failed to issue MAIL command: %v", err) } } for _, rec := range to { if err = client.Rcpt(rec); err != nil { - return fmt.Errorf("Rcpt: %v", err) + return fmt.Errorf("failed to issue RCPT command: %v", err) } } w, err := client.Data() if err != nil { - return fmt.Errorf("Data: %v", err) + return fmt.Errorf("failed to issue DATA command: %v", err) } else if _, err = msg.WriteTo(w); err != nil { - return fmt.Errorf("WriteTo: %v", err) + return fmt.Errorf("SMTP write failed: %v", err) } else if err = w.Close(); err != nil { - return fmt.Errorf("Close: %v", err) + return fmt.Errorf("SMTP close failed: %v", err) } return client.Quit() @@ -338,13 +355,13 @@ func NewContext() { return } - switch setting.MailService.MailerType { - case "smtp": - Sender = &smtpSender{} + switch setting.MailService.Protocol { case "sendmail": Sender = &sendmailSender{} case "dummy": Sender = &dummySender{} + default: + Sender = &smtpSender{} } mailQueue = queue.CreateQueue("mail", func(data ...queue.Data) []queue.Data { diff --git a/templates/install.tmpl b/templates/install.tmpl index 8f87a9e0d64c9..36f58218d4638 100644 --- a/templates/install.tmpl +++ b/templates/install.tmpl @@ -173,8 +173,12 @@ {{.locale.Tr "install.email_title"}}
- - + + +
+
+ +