Đăng bởi Để lại phản hồi

[GIT] HƯỚNG DẪN CLONE GITHUB PRIVATE REPO BẰNG SSH

🧩 Bước 1: Kiểm tra máy đã có SSH key chưa

Mở Terminal / Git Bash và gõ:

ls ~/.ssh

Nếu bạn thấy một trong các file sau:

  • id_rsa & id_rsa.pub
  • hoặc id_ed25519 & id_ed25519.pub

👉 Bỏ qua Bước 2, sang Bước 3.

Nếu không có hoặc báo lỗi → làm tiếp Bước 2.


🔑 Bước 2: Tạo SSH key mới

Gõ lệnh:

ssh-keygen -t ed25519 -C "email_github_cua_ban@example.com"

Nếu máy quá cũ không hỗ trợ ed25519, dùng:

ssh-keygen -t rsa -b 4096 -C "email_github_cua_ban@example.com"

Khi được hỏi:

Enter file in which to save the key:

👉 Nhấn Enter (dùng đường dẫn mặc định)

Enter passphrase:

👉 Có thể:

  • Nhấn Enter (không mật khẩu)
  • Hoặc nhập mật khẩu (bảo mật hơn)

📋 Bước 3: Copy SSH public key

Gõ:

cat ~/.ssh/id_ed25519.pub

(hoặc id_rsa.pub nếu dùng RSA)

→ Copy toàn bộ dòng (bắt đầu bằng ssh-ed25519)


🌐 Bước 4: Add SSH key lên GitHub

  1. Vào GitHub → Settings
  2. Chọn SSH and GPG keys
  3. Bấm New SSH key
  4. Dán public key vào ô Key
  5. Title: ví dụ My Laptop
  6. Bấm Add SSH key

🧪 Bước 5: Kiểm tra kết nối SSH

Gõ:

ssh -T git@github.com

Lần đầu sẽ hỏi:

Are you sure you want to continue connecting (yes/no)?

👉 Gõ yes

Nếu thành công sẽ thấy:

Hi USERNAME! You've successfully authenticated, but GitHub does not provide shell access.

📥 Bước 6: Clone repository private

Vào repo trên GitHub → bấm Code → SSH → copy link dạng:

git@github.com:USERNAME/REPOSITORY.git

Clone:

git clone git@github.com:USERNAME/REPOSITORY.git

🎉 Xong! Repo private đã được clone về máy


❗ Lỗi thường gặp & cách xử lý

❌ Permission denied (publickey)

Permission denied (publickey).

👉 Nguyên nhân:

  • Chưa add SSH key lên GitHub
  • Clone nhầm link HTTPS

👉 Kiểm tra:

ssh-add ~/.ssh/id_ed25519

❌ Clone nhầm HTTPS

❌ Sai:

https://github.com/...

✅ Đúng:

git@github.com:...

🔥 Mẹo hay

Có thể dùng nhiều GitHub account bằng nhiều SSH key

1 máy = 1 SSH key

Dùng SSH thì push/pull không cần đăng nhập

Đăng bởi Để lại phản hồi

Checklist bắt buộc cho Razor Pages upload file lớn

Nếu thiếu 1 bướcupload vẫn fail

1️⃣ Program.cs (Razor Pages)

using Microsoft.AspNetCore.Http.Features;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();

builder.Services.Configure<FormOptions>(options =>
{
    options.MultipartBodyLengthLimit = 1024L * 1024L * 1024L; // 1GB
});

2️⃣ Kestrel – cực kỳ quan trọng

builder.WebHost.ConfigureKestrel(options =>
{
    options.Limits.MaxRequestBodySize = 1024L * 1024L * 1024L; // 1GB
});

⚠️ Nếu bạn chạy IIS Express → bước này KHÔNG đủ


3️⃣ Razor Page Model – PHẢI CÓ attribute

❌ Sai (rất nhiều người mắc)

public class UploadModel : PageModel
{
    public async Task<IActionResult> OnPostAsync(IFormFile file)
    {
        ...
    }
}

✅ ĐÚNG

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;

[RequestSizeLimit(1024L * 1024L * 1024L)]
[RequestFormLimits(MultipartBodyLengthLimit = 1024L * 1024L * 1024L)]
public class UploadModel : PageModel
{
    [BindProperty]
    public IFormFile File { get; set; }

    public async Task<IActionResult> OnPostAsync()
    {
        if (File == null)
            return Page();

        var path = Path.Combine("wwwroot/uploads", File.FileName);
        using var stream = new FileStream(path, FileMode.Create);
        await File.CopyToAsync(stream);

        return Page();
    }
}

📌 Razor Pages bắt buộc attribute phải đặt trên PageModel class,
đặt trên method đôi khi không có tác dụng


4️⃣ Razor View (.cshtml) – rất hay bị quên

<form method="post" enctype="multipart/form-data">
    <input type="file" asp-for="File" />
    <button type="submit">Upload</button>
</form>

❌ Thiếu enctypeupload luôn fail


5️⃣ Nếu chạy IIS / IIS Express (90% lỗi nằm ở đây)

web.config

<configuration>
  <system.webServer>
    <security>
      <requestFiltering>
        <requestLimits maxAllowedContentLength="1073741824" />
      </requestFiltering>
    </security>
  </system.webServer>
</configuration>

📌 IIS mặc định chỉ 30MB

Đăng bởi Để lại phản hồi

Cách chạy lệnh cmd trên c#

Bạn có thể gọi lệnh CMD trong C# bằng cách dùng lớp System.Diagnostics.Process. Dưới đây là các cách phổ biến (từ đơn giản → nâng cao).


1. Chạy 1 lệnh CMD đơn giản (không cần lấy kết quả)

Ví dụ: chạy lệnh ipconfig

using System.Diagnostics;

Process.Start("cmd.exe", "/c ipconfig");

👉 /c nghĩa là: chạy lệnh xong thì đóng CMD.


2. Chạy lệnh CMD và lấy kết quả trả về

Ví dụ: lấy kết quả của dir

using System;
using System.Diagnostics;

class Program
{
    static void Main()
    {
        Process p = new Process();
        p.StartInfo.FileName = "cmd.exe";
        p.StartInfo.Arguments = "/c dir";
        p.StartInfo.RedirectStandardOutput = true;
        p.StartInfo.UseShellExecute = false;
        p.StartInfo.CreateNoWindow = true;

        p.Start();

        string output = p.StandardOutput.ReadToEnd();
        p.WaitForExit();

        Console.WriteLine(output);
    }
}

3. Chạy nhiều lệnh CMD cùng lúc

Dùng &&:

Process.Start("cmd.exe", "/c cd C:\\ && dir");

4. Mở CMD và giữ cửa sổ không tắt

Process.Start("cmd.exe");

Hoặc:

Process.Start("cmd.exe", "/k dir");

👉 /k = chạy lệnh nhưng không đóng CMD.


5. Chạy file .bat

Process.Start("cmd.exe", "/c test.bat");

Hoặc trực tiếp:

Process.Start("test.bat");

Lưu ý quan trọng ⚠️

  • Nếu chương trình cần quyền admin, bạn phải chạy app C# bằng quyền Administrator.
  • Không nên chạy lệnh CMD từ input người dùng nếu không kiểm soát (dễ lỗi & mất an toàn).

Nếu bạn muốn:

  • chạy CMD ngầm
  • chạy PowerShell
  • hoặc chạy trong WinForms / WPF

👉 nói mình biết bạn đang dùng loại project nào nhé 👍

Toi muon an cmd duoc khong

Có nhé 👍 Bạn hoàn toàn có thể ẩn cửa sổ CMD khi gọi lệnh từ C#.

Dưới đây là cách chuẩn và hay dùng nhất.


✅ Ẩn CMD khi chạy lệnh (khuyến nghị)

using System.Diagnostics;

Process p = new Process();
p.StartInfo.FileName = "cmd.exe";
p.StartInfo.Arguments = "/c ipconfig"; // lệnh CMD
p.StartInfo.CreateNoWindow = true;      // ẨN CMD
p.StartInfo.UseShellExecute = false;    // BẮT BUỘC để ẩn

p.Start();
p.WaitForExit();

👉 CMD sẽ chạy ngầm, người dùng không thấy gì cả.


✅ Ẩn CMD + lấy kết quả

using System.Diagnostics;

Process p = new Process();
p.StartInfo.FileName = "cmd.exe";
p.StartInfo.Arguments = "/c dir";
p.StartInfo.CreateNoWindow = true;
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;

p.Start();
string output = p.StandardOutput.ReadToEnd();
p.WaitForExit();

❌ Cách KHÔNG ẩn được CMD

Process.Start("cmd.exe", "/c dir");

⚠️ Vì mặc định UseShellExecute = true → CMD sẽ hiện ra.

CÁCH 1 (NHANH – PHỔ BIẾN): Chạy ngầm bằng Process (giống service)

👉 Phù hợp nếu:

  • App đã là Worker / Web API
  • Không cần tích hợp sâu với Windows Service
  • Muốn start / stop bằng C#

🔹 Code C# chạy ngầm

using System.Diagnostics;

Process serviceProcess;

void StartService()
{
    serviceProcess = new Process();
    serviceProcess.StartInfo.FileName = "dotnet";
    serviceProcess.StartInfo.Arguments = "Storage.GiamKichSan.dll";
    serviceProcess.StartInfo.WorkingDirectory = @"C:\App\Storage";
    serviceProcess.StartInfo.UseShellExecute = false;
    serviceProcess.StartInfo.CreateNoWindow = true;
    serviceProcess.StartInfo.RedirectStandardOutput = true;
    serviceProcess.StartInfo.RedirectStandardError = true;

    serviceProcess.Start();
}

void StopService()
{
    if (serviceProcess != null && !serviceProcess.HasExited)
    {
        serviceProcess.Kill(); // dừng service
    }
}

📌 Ứng dụng sẽ:

  • chạy ngầm
  • không hiện CMD
  • chạy liên tục cho tới khi bị kill

⚠️ Nhược điểm:

  • Không tự chạy khi Windows boot
  • Không thấy trong services.msc

✅ CÁCH 2 (CHUẨN WINDOWS): Chạy như Windows Service thật

👉 Khuyến nghị nếu chạy lâu dài / production

🔹 Bước 1: App phải là Worker Service

Nếu Storage.GiamKichSan.dllWorker Service (.NET) → OK luôn
Nếu chưa, cần tạo dạng Worker:

dotnet new worker -n Storage.GiamKichSan

🔹 Bước 2: Cho phép chạy như Windows Service

Trong Program.cs:

using Microsoft.Extensions.Hosting;

Host.CreateDefaultBuilder(args)
    .UseWindowsService()
    .ConfigureServices(services =>
    {
        services.AddHostedService<Worker>();
    })
    .Build()
    .Run();

🔹 Bước 3: Publish

dotnet publish -c Release -o C:\Services\Storage

🔹 Bước 4: Tạo Windows Service

Mở CMD Run as Administrator:

sc create StorageGiamKichSan binPath= "dotnet C:\Services\Storage\Storage.GiamKichSan.dll"

Start service:

sc start StorageGiamKichSan

Stop service:


		
Đăng bởi Để lại phản hồi

IEmailSender trong netcore

Nó hoạt động theo cơ chế Dependency Injection (DI)bạn phải tự cài đặt cách gửi email.

👉 Đây là interface, Identity chỉ biết gọi, không biết gửi bằng cách nào.

public interface IEmailSender
{
    Task SendEmailAsync(string email, string subject, string htmlMessage);
}

2. Luồng hoạt động tổng quát

Controller / Razor Page
        ↓
_emailSender.SendEmailAsync(...)
        ↓
EmailSender (class bạn viết)
        ↓
SMTP / SendGrid / MailKit
        ↓
Email được gửi

👉 Identity không can thiệp vào bước này.


3. Cách _emailSender được inject (DI)

Đăng ký trong Program.cs

builder.Services.AddTransient<IEmailSender, SmtpEmailSender>();

👉 Khi ASP.NET Core tạo Controller:

  • Nó thấy cần IEmailSender
  • Nó tạo SmtpEmailSender
  • Gán vào _emailSender

4. Ví dụ implement _emailSender bằng SMTP

Ví dụ này chỉ minh họa, không có nội dung nhạy cảm.

using System.Net;
using System.Net.Mail;

public class SmtpEmailSender : IEmailSender
{
    private readonly IConfiguration _config;

    public SmtpEmailSender(IConfiguration config)
    {
        _config = config;
    }

    public async Task SendEmailAsync(string email, string subject, string htmlMessage)
    {
        var smtpClient = new SmtpClient(
            _config["Email:SmtpServer"],
            int.Parse(_config["Email:Port"])
        )
        {
            Credentials = new NetworkCredential(
                _config["Email:Username"],
                _config["Email:Password"]
            ),
            EnableSsl = true
        };

        var mail = new MailMessage
        {
            From = new MailAddress(_config["Email:From"]),
            Subject = subject,
            Body = htmlMessage,
            IsBodyHtml = true
        };

        mail.To.Add(email);

        await smtpClient.SendMailAsync(mail);
    }
}

5. Cách gọi _emailSender trong Controller

await _emailSender.SendEmailAsync(
    user.Email,
    "Xác nhận email",
    "Vui lòng nhấn link để xác nhận tài khoản."
);

👉 Controller chỉ gọi, không quan tâm SMTP.


6. _emailSender KHÔNG tự chạy

⚠️ Quan trọng:

  • _emailSender không tự động gửi email
  • Identity không gọi nó
  • Bạn phải gọi thủ công

Ví dụ:

  • Sau khi đăng ký
  • Khi reset password
  • Khi resend email

4. Identity kết hợp _emailSender thế nào

Bước 1: Tạo User

await _userManager.CreateAsync(user, password);

Bước 2: Tạo token xác nhận email

var token = await _userManager.GenerateEmailConfirmationTokenAsync(user);

👉 Token được tạo bởi Data Protection API, không phải EmailStore.


Bước 3: Tạo link xác nhận

var link = Url.Action(
    "ConfirmEmail",
    "Account",
    new { userId = user.Id, token = token },
    Request.Scheme);

Bước 4: Gửi email

await _emailSender.SendEmailAsync(
    user.Email,
    "Xác nhận email",
    $"Click vào link: <a href='{link}'>Confirm</a>");

Bước 5: User click link → xác nhận

await _userManager.ConfirmEmailAsync(user, token);

👉 Lúc này:

  • UserManager gọi
  • IUserEmailStore.SetEmailConfirmedAsync(...)
  • Lưu EmailConfirmed = true vào DB

Identity chỉ hỗ trợ:

  • Tạo token
  • Xác nhận token
  • Lưu trạng thái email

8. Vì sao Microsoft thiết kế như vậy?

✔ Không ép dùng SMTP
✔ Dùng được cho API / Mobile
✔ Thay SendGrid → SMTP không đổi code
✔ Dễ test (mock IEmailSender)

Đăng bởi Để lại phản hồi

IUserEmailStore trong netcore

chỉ lưu trữ & quản lý dữ liệu email của User, không gửi email đi đâu cả

✅ Nó LÀM:

  • Lưu email vào database
  • Lấy email của user
  • Lưu trạng thái EmailConfirmed
  • Tìm user theo email

❌ Nó KHÔNG LÀM:

  • Gửi email
  • Tạo nội dung email
  • Gửi link xác nhận
  • Gửi OTP / reset password

1. IUserEmailStore<TUser> là gì?

IUserEmailStore<TUser>interface dùng để quản lý Email của User trong ASP.NET Core Identity.

Nó cho phép hệ thống:

  • Lưu email người dùng
  • Lấy email
  • Kiểm tra email đã xác nhận chưa
  • Set trạng thái xác nhận email

👉 Identity dùng interface này để không phụ thuộc vào database cụ thể (SQL, MongoDB, In-memory, v.v.).


2. IUserEmailStore dùng để làm gì?

Một số chức năng chính:

  • Đăng nhập bằng email
  • Xác nhận email (Email Confirmation)
  • Reset mật khẩu qua email
  • Kiểm tra email có tồn tại chưa

3. Các method quan trọng

public interface IUserEmailStore<TUser> : IUserStore<TUser>
{
    Task SetEmailAsync(TUser user, string email, CancellationToken cancellationToken);
    Task<string> GetEmailAsync(TUser user, CancellationToken cancellationToken);

    Task<bool> GetEmailConfirmedAsync(TUser user, CancellationToken cancellationToken);
    Task SetEmailConfirmedAsync(TUser user, bool confirmed, CancellationToken cancellationToken);

    Task<TUser> FindByEmailAsync(string normalizedEmail, CancellationToken cancellationToken);
    Task<string> GetNormalizedEmailAsync(TUser user, CancellationToken cancellationToken);
    Task SetNormalizedEmailAsync(TUser user, string normalizedEmail, CancellationToken cancellationToken);
}

4. UserEmailStore nằm ở đâu trong project thực tế?

Nếu bạn dùng Identity mặc định (Entity Framework):

services.AddIdentity<ApplicationUser, IdentityRole>()
        .AddEntityFrameworkStores<ApplicationDbContext>();

👉 Lúc này:

  • UserManager tự động dùng UserStore
  • UserStore đã implement sẵn IUserEmailStore

Bạn không cần tự viết UserEmailStore.

5. Khi nào cần tự implement IUserEmailStore?

Bạn cần tự implement nếu:

  • Không dùng Entity Framework
  • Dùng database riêng (MongoDB, API, file, v.v.)
  • Muốn custom cách lưu User

Ví dụ:

public class CustomUserStore :
    IUserStore<ApplicationUser>,
    IUserEmailStore<ApplicationUser>
{
    public Task SetEmailAsync(ApplicationUser user, string email, CancellationToken ct)
    {
        user.Email = email;
        return Task.CompletedTask;
    }

    public Task<string> GetEmailAsync(ApplicationUser user, CancellationToken ct)
    {
        return Task.FromResult(user.Email);
    }

    public Task<bool> GetEmailConfirmedAsync(ApplicationUser user, CancellationToken ct)
    {
        return Task.FromResult(user.EmailConfirmed);
    }

    public Task SetEmailConfirmedAsync(ApplicationUser user, bool confirmed, CancellationToken ct)
    {
        user.EmailConfirmed = confirmed;
        return Task.CompletedTask;
    }

    // Các method khác bắt buộc phải implement...
}

6. Quan hệ với UserManager

UserManager<TUser> sẽ gọi IUserEmailStore ở bên dưới:

await _userManager.SetEmailAsync(user, "test@gmail.com");
await _userManager.IsEmailConfirmedAsync(user);

👉 Bạn không gọi trực tiếp UserEmailStore, mà gọi qua UserManager.

Đăng bởi Để lại phản hồi

Ok 👍 mình hướng dẫn bạn cấu hình Postfix port 587 (submission) ĐÚNG CHUẨN, từng bước, áp dụng cho Postfix (Ubuntu) như server của bạn.

Mục tiêu sau khi xong:

  • Port 587
  • STARTTLS
  • SMTP AUTH
  • Dùng được với MailKit / .NET

🎯 MỤC TIÊU KỸ THUẬT

Postfix sẽ:

  • Lắng nghe 587
  • Bật STARTTLS
  • Cho phép AUTH LOGIN / PLAIN
  • Chỉ cho user đã auth gửi mail

1️⃣ KIỂM TRA POSTFIX ĐANG LẮNG NGHE PORT 587 CHƯA

sudo netstat -plnt | grep :587

Nếu KHÔNG thấy → cần cấu hình
Nếu nhưng chưa auth → vẫn phải chỉnh


2️⃣ SỬA FILE master.cf (QUAN TRỌNG NHẤT)

Mở file:

sudo nano /etc/postfix/master.cf

Tìm (hoặc thêm) TOÀN BỘ KHỐI DƯỚI ĐÂY
👉 NHỚ bỏ dấu # nếu đang bị comment

submission inet n       -       y       -       -       smtpd
  -o syslog_name=postfix/submission
  -o smtpd_tls_security_level=encrypt
  -o smtpd_sasl_auth_enable=yes
  -o smtpd_tls_auth_only=yes
  -o smtpd_reject_unlisted_recipient=no
  -o smtpd_recipient_restrictions=permit_sasl_authenticated,reject
  -o milter_macro_daemon_name=ORIGINATING

📌 Ý nghĩa:

  • submission = port 587
  • tls_security_level=encrypt → BẮT BUỘC STARTTLS
  • sasl_auth_enable=yes → cho AUTH
  • permit_sasl_authenticated → chỉ user auth mới gửi

3️⃣ CẤU HÌNH SASL (AUTH) – BẮT BUỘC

Mở file:

sudo nano /etc/postfix/main.cf

Thêm hoặc đảm bảo có:

# TLS
smtpd_tls_cert_file=/etc/letsencrypt/live/mail.giamkichsan.com/fullchain.pem
smtpd_tls_key_file=/etc/letsencrypt/live/mail.giamkichsan.com/privkey.pem
smtpd_tls_security_level=may
smtpd_tls_loglevel=1

# SASL AUTH
smtpd_sasl_auth_enable = yes
smtpd_sasl_type = dovecot
smtpd_sasl_path = private/auth
smtpd_sasl_security_options = noanonymous
smtpd_sasl_local_domain = $myhostname
broken_sasl_auth_clients = yes

📌 Postfix KHÔNG tự auth → nó dùng Dovecot


4️⃣ CẤU HÌNH DOVECOT (NẾU CHƯA CÓ)

Kiểm tra dovecot:

systemctl status dovecot

Nếu chưa có:

sudo apt install dovecot-core dovecot-imapd -y

Sửa file:

sudo nano /etc/dovecot/conf.d/10-master.conf

Tìm block:

service auth {

Bên trong phải có:

service auth {
  unix_listener /var/spool/postfix/private/auth {
    mode = 0660
    user = postfix
    group = postfix
  }
}

📌 Cái này cho phép Postfix gọi Dovecot để AUTH


5️⃣ KHỞI ĐỘNG LẠI DỊCH VỤ

sudo systemctl restart dovecot
sudo systemctl restart postfix

6️⃣ TEST AUTH PORT 587 (CỰC KỲ QUAN TRỌNG)

openssl s_client -starttls smtp -connect mail.giamkichsan.com:587

Sau khi hiện:

220 mail.giamkichsan.com ESMTP Postfix

Gõ:

EHLO test

👉 BẠN PHẢI THẤY:

250-AUTH LOGIN PLAIN
250-STARTTLS

✅ Thấy AUTH → cấu hình THÀNH CÔNG
❌ Không thấy → Postfix chưa nhận SASL


7️⃣ CODE .NET CHUẨN SAU KHI CẤU HÌNH XONG

client.Connect(
    "mail.giamkichsan.com",
    587,
    SecureSocketOptions.StartTls
);

client.Authenticate(
    "vcduan@giamkichsan.com",
    "Soc@2902"
);

client.Send(message);

🧨 LỖI HAY GẶP & CÁCH SỬA NHANH

LỗiNguyên nhân
No AUTHDovecot chưa link Postfix
535 auth failedSai user / mailbox
TLS errorSai path cert
timeoutFirewall chặn 587

👉 Mở firewall:

sudo ufw allow 587/tcp

✅ TÓM TẮT CHỐT

  • ❌ Đừng cố port 465
  • 587 + STARTTLS là chuẩn
  • ✅ Postfix + Dovecot
  • ✅ MailKit chạy mượt

👉 Sau khi bạn cấu hình xong, copy output của lệnh này gửi mình:

openssl s_client -starttls smtp -connect mail.giamkichsan.com:587

Mình sẽ check và xác nhận OK 100% trước khi bạn chạy tool .NET 💯

🟢 CÁCH 2 (CHUẨN MAIL SERVER – KHUYÊN DÙNG)

👉 Dùng passwd-file cho mailbox (ĐÚNG CHUẨN ISP)

1️⃣ Tạo file password mailbox

mkdir -p /etc/dovecot/passwd
nano /etc/dovecot/passwd/users

Nội dung:

vcduan@giamkichsan.com:{PLAIN}Soc@2902

2️⃣ Sửa auth config

nano /etc/dovecot/conf.d/10-auth.conf

Đổi thành:

disable_plaintext_auth = no
auth_mechanisms = plain login

!include auth-passwdfile.conf.ext

3️⃣ Sửa passwd-file config

nano /etc/dovecot/conf.d/auth-passwdfile.conf.ext

Đảm bảo:

passdb {
  driver = passwd-file
  args = scheme=PLAIN /etc/dovecot/passwd/users
}

userdb {
  driver = static
  args = uid=vmail gid=vmail home=/var/mail/vhosts/%d/%n
}
Đăng bởi Để lại phản hồi

Cài đặt email server trên Ubuntu

Bước 1: Cài đặt Postfix (nếu chưa có)

sudo apt update
sudo apt install postfix mailutils -y

Khi cài đặt:

  • Chọn Internet Site
  • System mail name: mail.giamkichsan.com

Đây là hostname mail server của bạn.

3️⃣ Kiểm tra hostname

hostnamectl set-hostname mail.giamkichsan.com
hostname
  • mail.giamkichsan.com phải là hostname server.
  • Kiểm tra DNS A record: mail.giamkichsan.com trỏ tới IP server của bạn.

Bước 2: Tạo hoặc dùng chứng chỉ SSL/TLS

Bạn có thể dùng Let’s Encrypt hoặc chứng chỉ tự ký (self-signed).

A. Chứng chỉ Let’s Encrypt: Yêu cầu chứng chỉ sử dụng chế độ --standalone (yêu cầu tạm dừng máy chủ web nếu đang chạy trên cổng 80)
sudo apt install certbot
sudo certbot certonly --standalone -d mail.giamkichsan.com

Chứng chỉ sẽ nằm ở:

/etc/letsencrypt/live/mail.giamkichsan.com/fullchain.pem
/etc/letsencrypt/live/mail.giamkichsan.com/privkey.pem

Bước 3: Cấu hình Postfix để dùng SSL/TLS

Mở file cấu hình chính của Postfix:

sudo nano /etc/postfix/main.cf

Thêm hoặc sửa các dòng sau:

# Domain chính
myhostname = mail.giamkichsan.com
mydomain = giamkichsan.com
myorigin = /etc/mailname

# Máy chủ của bạn sẽ gửi mail đi từ domain
inet_interfaces = all
inet_protocols = all

# Mail địa phương
mydestination = $myhostname, localhost.$mydomain, localhost, $mydomain

# Các máy chủ mà mail của bạn sẽ relay
#relayhost = 

# TLS (SSL)
smtp_tls_cert_file=/etc/letsencrypt/live/mail.giamkichsan.com/fullchain.pem
smtp_tls_key_file=/etc/letsencrypt/live/mail.giamkichsan.com/privkey.pem
smtp_use_tls=yes
smtp_tls_loglevel = 2
smtp_tls_security_level=may
smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache

# TLS cho Postfix SMTPD (receiving)
smtpd_tls_cert_file = /etc/letsencrypt/live/mail.giamkichsan.com/fullchain.pem
smtpd_tls_key_file = /etc/letsencrypt/live/mail.giamkichsan.com/privkey.pem
smtpd_use_tls=yes
smtpd_tls_loglevel = 2
smtpd_tls_security_level = may
smtpd_tls_session_cache_database = btree:${data_directory}/smtpd_scache
smtpd_tls_auth_only = yes
smtpd_recipient_restrictions = 
    permit_mynetworks,
    permit_sasl_authenticated,
    reject_unauth_destination

# My networks (chỉ mạng nội bộ gửi mail)
# mynetworks = 127.0.0.0/8 [::1]/128

Bước 4: Bật STARTTLS cho cổng SMTP

Trong file /etc/postfix/master.cf, đảm bảo dòng SMTPS (465) có STARTTLS:

smtps inet n       -       y       -       -       smtpd
  -o syslog_name=postfix/smtps
  -o smtpd_tls_wrappermode=yes
  -o smtpd_sasl_auth_enable=yes
  -o smtpd_recipient_restrictions=permit_sasl_authenticated,reject

Điều này cho phép các client kết nối và nâng cấp lên TLS.


Bước 5: Kiểm tra cấu hình

Kiểm tra cấu hình Postfix:

sudo systemctl restart postfix
sudo systemctl status postfix
sudo postfix check

Bước 7: Kiểm tra SSL/TLS

Bạn có thể dùng openssl để kiểm tra:

openssl s_client -connect mail.giamkichsan.com:465 -starttls smtp

Nếu thành công, bạn sẽ thấy thông tin chứng chỉ và kết nối được mã hóa TLS.

Bước 8: Cách giải quyết Gmail từ chối email vì domain mail.giamkichsan.com chưa có SPF/DKIM

1a. Cấu hình SPF cho domain

👉 Vào DNS của giamkichsan.com kiểm tra SPF

dig txt giamkichsan.com

Nếu chưa có thì add DNS record (TXT) cho giamkichsan.com:

Type: TXT
Name: @
Value:
v=spf1 ip4:109.199.101.177 -all

Lưu ý:

  • 109.199.101.177 = IP VPS gửi mail
  • -all = chỉ cho phép IP này gửi mail

1b. SPF là gì

  • SPF xác thực IP nào được phép gửi mail thay domain.
  • Nó kiểm tra mail đến từ server hợp lệ hay không.
  • SPF chỉ dựa trên địa chỉ gửi (envelope sender), không kiểm tra nội dung.

2. Cài đặt DKIM (nếu muốn tăng uy tín)

1️⃣ Cài OpenDKIM

sudo apt update
sudo apt install opendkim opendkim-tools -y

2️⃣ Tạo thư mục lưu key DKIM

sudo mkdir -p /etc/opendkim/keys/giamkichsan.com
sudo chown -R opendkim:opendkim /etc/opendkim
sudo chmod -R 700 /etc/opendkim

3️⃣ Tạo DKIM key

cd /etc/opendkim/keys/giamkichsan.com
sudo opendkim-genkey -s mail -d giamkichsan.com
sudo chown opendkim:opendkim mail.private
sudo chmod 600 mail.private
  • -s mail → selector (bạn có thể đổi tên)
  • -d giamkichsan.com → domain của bạn
  • Sau khi chạy xong, bạn sẽ có 2 file:
  • mail.private → private key
  • mail.txt → public key để thêm vào DNS

4️⃣ Cấu hình OpenDKIM

Mở file /etc/opendkim.conf và chỉnh / thêm các dòng:

sudo nano /etc/opendkim.conf
# Phiên bản OpenDKIM
Syslog                  yes
UMask                   002
UserID                  opendkim:opendkim

# Thư mục chứa key riêng
KeyTable                /etc/opendkim/KeyTable
SigningTable            /etc/opendkim/SigningTable
ExternalIgnoreList      /etc/opendkim/TrustedHosts
TrustedHosts            /etc/opendkim/TrustedHosts

# Chế độ tự động chọn selector nếu có nhiều key
AutoRestart             yes
AutoRestartRate         10/1h
Mode                    sv
Canonicalization        relaxed/simple

##
Domain                  giamkichsan.com
Socket                  inet:12301@localhost // Chinh sua phu hop server

** Tạo các file hỗ trợ

a) KeyTable

sudo nano /etc/opendkim/KeyTable

Thêm hoặc sửa dòng:

#cú pháp chuẩn: keyname  domain:selector:/duong/dan/toi/private.key
mail._domainkey.giamkichsan.com giamkichsan.com:mail:/etc/opendkim/keys/giamkichsan.com/mail.private
  • mail là selector (có thể đặt tùy ý)
  • Đường dẫn tới private key của bạn

b) SigningTable

sudo nano /etc/opendkim/SigningTable
*@giamkichsan.com mail._domainkey.giamkichsan.com
  • Nghĩa là tất cả email từ domain @giamkichsan.com sẽ dùng key mail._domainkey.giamkichsan.com

c) TrustedHosts

sudo nano /etc/opendkim/TrustedHosts
127.0.0.1
localhost
giamkichsan.com
Phân quyền đúng (rất quan trọng)
sudo chown opendkim:opendkim /etc/opendkim/KeyTable
sudo chown opendkim:opendkim /etc/opendkim/SigningTable
sudo chown opendkim:opendkim /etc/opendkim/TrustedHosts
sudo chmod 640 /etc/opendkim/KeyTable
sudo chmod 640 /etc/opendkim/SigningTable
sudo chmod 640 /etc/opendkim/TrustedHosts
  • Các host này được tin cậy để gửi mail qua DKIM.

5️⃣ Cấu hình Postfix kết nối OpenDKIM

Chỉnh file /etc/postfix/main.cf, thêm:

sudo nano /etc/postfix/main.cf

Thêm nội dung dưới vào cuối file:

milter_default_action = accept
milter_protocol = 6
smtpd_milters = inet:localhost:12301
non_smtpd_milters = $smtpd_milters

6️⃣ Khởi động OpenDKIM và Postfix

sudo systemctl enable opendkim
sudo systemctl restart opendkim
sudo systemctl restart postfix

Kiểm tra lỗi

sudo journalctl -xeu opendkim.service | tail -50 //Nếu lỗi opendkim.service
sudo journalctl -u opendkim -xe

//Hoac chay debug neu van loi
sudo opendkim -f -x /etc/opendkim.conf

7️⃣ Thêm public DKIM vào DNS

Mở file mail.txt vừa tạo, copy nội dung TXT record. Ví dụ:

sudo nano /etc/opendkim/keys/giamkichsan.com/mail.txt
hoặc
sudo cat /etc/opendkim/keys/giamkichsan.com/mail.txt
mail._domainkey    IN    TXT    "v=DKIM1; k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GN...rest_of_key..."
  • mail → selector (-s mail lúc tạo key)
  • Thêm record TXT này vào DNS của domain giamkichsan.com

Lưu ý: DNS có thể mất 15–60 phút để propagate.


8️⃣ Test DKIM

Sau khi DNS update:

opendkim-testkey -d giamkichsan.com -s mail -vvv

Nếu kết quả OK → DKIM đã hoạt động.

4 Test gửi mail
echo "Test gửi mail qua Gmail relay" | mail -s "Test Mail $(date)" duanvc2811@gmail.com

6️⃣ Kiểm tra mail queue / log

mailq # Mail còn trong queue hay đã gửi
tail -f /var/log/mail.log 
[hoac] sudo tail -f /var/log/syslog | grep --color=auto -E "postfix|DKIM|SPF|warning|error"
  • Nếu mail gửi thành công → ra khỏi queue, Gmail nhận ngay
  • Nếu bị lỗi → log sẽ chỉ ra ngay (thường là lỗi password hoặc TLS)

Nguyên nhân thường gặp

  1. File log /var/log/mail.log bị xóa, quyền sai hoặc filesystem readonly.
  2. Rsyslog không có quyền ghi vào thư mục /var/log.
  3. Một số hệ thống mới Ubuntu dùng systemd-journald để log thay vì file riêng cho mail, hoặc rsyslog chưa được cấu hình để ghi mail.

Cách fix

1️⃣ Tạo lại file log và cấp quyền

sudo touch /var/log/mail.log
sudo chown syslog:adm /var/log/mail.log
sudo chmod 640 /var/log/mail.log

2️⃣ Khởi động lại rsyslog

sudo systemctl restart rsyslog

3️⃣ Khởi động lại Postfix

sudo systemctl restart postfix

4️⃣ Kiểm tra log realtime

sudo tail -f /var/log/mail.log
Đăng bởi Để lại phản hồi

[NGINX]Cài đặt app trên ubuntu

1️⃣ Cài Nginx (nếu chưa có)

sudo apt update
sudo apt install nginx -y

Kiểm tra Nginx:

sudo systemctl status nginx

2️⃣ Cấu trúc cơ bản reverse proxy

Reverse proxy nghĩa là: Nginx nhận request từ client → chuyển tiếp đến backend (app server)

Ví dụ:

  • Client truy cập http://yourdomain.com
  • Nginx chuyển request tới http://localhost:5000 (app .NET / Node.js / Python…)

3️⃣ Tạo file cấu hình site mới

sudo nano /etc/nginx/sites-available/myapp

Ví dụ cấu hình cơ bản:

server {
    listen 80;
    server_name yourdomain.com;

    location / {
        proxy_pass http://127.0.0.1:5000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection keep-alive;
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
    }
}

4️⃣ Kích hoạt site

sudo ln -s /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled/

Kiểm tra cấu hình:

sudo nginx -t

Reload Nginx:

sudo systemctl reload nginx

5️⃣ Kiểm tra reverse proxy

  • App backend chạy ở localhost:5000:
curl http://127.0.0.1:5000
  • Truy cập domain:
http://yourdomain.com

Nginx sẽ chuyển request tới backend.


6️⃣ (Tùy chọn) Bật HTTPS với Let’s Encrypt

Cài certbot:

sudo apt install certbot python3-certbot-nginx -y

Cấp chứng chỉ SSL:

sudo certbot --nginx -d yourdomain.com
Đăng bởi Để lại phản hồi

Cài đặt dotnet trên Ubuntu

1️⃣ Thêm Microsoft package repository

sudo apt update
sudo apt install -y wget apt-transport-https software-properties-common
wget https://packages.microsoft.com/config/ubuntu/$(lsb_release -rs)/packages-microsoft-prod.deb -O packages-microsoft-prod.deb
sudo dpkg -i packages-microsoft-prod.deb
rm packages-microsoft-prod.deb

2️⃣ Cập nhật apt và cài .NET SDK 8

sudo apt update
sudo apt install -y dotnet-sdk-8.0

Nếu muốn chỉ cài runtime (chỉ chạy ứng dụng, không build):

sudo apt install -y dotnet-runtime-8.0

3️⃣ Kiểm tra cài đặt

dotnet --version

Kết quả sẽ hiển thị:

8.0.x

5️⃣ Cập nhật .NET 8 sau này

sudo apt update
sudo apt upgrade