আধুনিক সার্টিফিকেট রিনিউয়াল CLI টুল lego-এর মাধ্যমে DNS অথেন্টিকেশন স্বয়ংক্রিয় করা

7 min

language: ja bn en es hi pt ru zh-cn zh-tw

হ্যালো, আমি মুনো (Munou)।

সম্প্রতি ফাইল অথেন্টিকেশন থেকে সরে আসার চেষ্টা করছি, আসলে অস্বস্তিকর বিষয় হলো আমি certbot দিয়ে সার্টিফিকেট রিনিউ করছি, কিন্তু যেহেতু এটি ফাইল অথেন্টিকেশন ব্যবহার করে, তাই আমাকে mail.example.com ডোমেইনটি ডামি হিসেবে Nginx সাইডে Listen করিয়ে রাখতে হচ্ছে।

এটি অটোমেটিক চলছে তাই ভালো, কিন্তু certbot সাময়িকভাবে ফাইল অথেন্টিকেশনের পাথে ফাইল রেখে আপডেট করে। তবে, মেইল সার্ভারের জন্য আলাদাভাবে http এর প্রয়োজন নেই এবং সত্যি বলতে আমি এটি গ্রহণ করতে চাই না।
তাছাড়া, যদি A রেকর্ডে একাধিক অরিজিন (Origin) নির্দিষ্ট করা থাকে, তবে এই পদ্ধতিটি কাজ করবে না। কারণ:

$dig soulminingrig.com +short
91.98.169.80
163.44.113.145

এভাবে যখন দুটি আইপি (IP) রিকোয়েস্ট গ্রহণ করে, তখন একটি সার্ভারে certbot চললে অন্য সার্ভারে ফাইল অথেন্টিকেশনের ফাইলটি থাকে না। HTTP বা DNS ক্লায়েন্ট সাইডের নিকটতম সার্ভারে রিকোয়েস্ট পাঠানোর বৈশিষ্ট্যের কারণে হয়তো এটি সফল হচ্ছে, কিন্তু এমন জাদুর ওপর নির্ভর করা অপারেশনাল দিক থেকে ব্যর্থতা।

acme.sh এবং certbot-এর স্পেসিফিকেশন অনুযায়ী DNS অথেন্টিকেশন চালু করার কঠিন দিকগুলো

এই দুটিতেও DNS অথেন্টিকেশন সম্ভব।
তবে certbot-এর ক্ষেত্রে অটোমেটিক DNS আপডেট করার API প্রিসেট করা নেই। তাই রেকর্ড রেজিস্ট্রেশন ইত্যাদি আমাদের নিজেদেরই করতে হয়, ফলে DNS অথেন্টিকেশনের ক্ষেত্রে অটো-রিনিউয়াল কার্যত ব্যবহার করা যায় না।
acme.sh-এর ক্ষেত্রে এটি ডিফল্টভাবে ConoHa DNS API সাপোর্ট করে না। এটি অপশন দিয়ে ব্যবহার করতে গেলে অপশনগুলো বেশ জটিল (chaos) হয়ে যায়। এক্ষেত্রে DNS সার্ভার পরিবর্তন হলে কাজটা বেশ কঠিন হয়ে পড়ে।

lego

অন্য কিছু আছে কিনা খুঁজছিলাম, তখন দেখলাম Go দিয়ে তৈরি একটি টুল আশ্চর্যজনকভাবে ConoHa DNS API সাপোর্ট করছে।

ConoHa v3 :: Let’s Encrypt client and ACME library written in Go.

GitHub - go-acme/lego: Let's Encrypt/ACME client and library written in Go · GitHub

দারুণ!

ইনস্টলেশন

pkg-এও এটি আছে, যা বেশ সুবিধাজনক...।

# pkg search lego
lego-4.33.0                    Let's Encrypt client and ACME library written in Go
pkg install lego-4.33.0

এক্সিকিউশন স্ক্রিপ্ট

আমার ক্ষেত্রে প্রায় সব ডোমেইনই Nginx সাইড থেকে ম্যানেজ করা হয় এবং বাকিগুলো সামান্য, তাই রিনিউয়াল স্ক্রিপ্টটি আমি চ্যাপি (Chappy)-কে দিয়ে তৈরি করিয়ে নিয়েছি।
আমার ক্ষেত্রে, www.example.com এবং example.com-এর জন্য SAN সার্টিফিকেট ইস্যু করতে চাই কিন্তু ওয়াইল্ডকার্ড সার্টিফিকেট ইস্যু করতে চাই না, তাই এটি এমন হয়েছে:

#!/bin/sh
set -eu
SITES_DIR="/usr/local/etc/nginx/sites-enabled"
export CONOHAV3_TENANT_ID=""
export CONOHAV3_API_USER_ID=""
export CONOHAV3_API_PASSWORD=""
export CONOHAV3_PROPAGATION_TIMEOUT="600"
export CONOHAV3_POLLING_INTERVAL="300"
LEGO="/usr/local/bin/lego"
SSLDIR="/usr/local/etc/ssl/lego"
EMAIL="taro@example.com"
DNS_PROVIDER="conohav3"
# Nginx-এর বাইরেও যে ডোমেইনগুলো আপডেট করতে চান সেগুলো এখানে যোগ করুন
# স্পেস দিয়ে আলাদা করে একাধিক ডোমেইন লিখুন
EXTRA_DOMAINS="
mail.example.com
"
tmp_all="$(mktemp)"
tmp_done="$(mktemp)"
trap 'rm -f "$tmp_all" "$tmp_done"' EXIT INT TERM
# nginx-এর server_name থেকে এক্সট্রাক্ট করা হচ্ছে
grep -RhoE 'server_name[[:space:]]+[^;]+' "$SITES_DIR" \
  | sed -E 's/^server_name[[:space:]]+//' \
  | tr ' ' '\n' \
  | sed 's/;$//' \
  | sed '/^$/d' \
  | sed '/^\*\./d' \
  | sed '/^_/d' \
  >> "$tmp_all"
printf '%s\n' "$EXTRA_DOMAINS" \
  | tr ' ' '\n' \
  | sed '/^$/d' \
  >> "$tmp_all"
sort -u -o "$tmp_all" "$tmp_all"
: > "$tmp_done"
issue_cert() {
  echo "==> issuing certificate for: $*"
  "$LEGO" \
    --accept-tos \
    --path "${SSLDIR}" \
    --email "$EMAIL" \
    --dns "$DNS_PROVIDER" \
    --dns.resolvers 1.1.1.1 \
    "$@" \
    run
}
already_done() {
  grep -Fxq "$1" "$tmp_done"
}
mark_done() {
  printf '%s\n' "$1" >> "$tmp_done"
}
while IFS= read -r host; do
  [ -n "$host" ] || continue
  if already_done "$host"; then
    continue
  fi
  case "$host" in
    www.*)
      apex="${host#www.}"
      if grep -Fxq "$apex" "$tmp_all"; then
        issue_cert -d "$apex" -d "$host"
        mark_done "$apex"
        mark_done "$host"
      else
        issue_cert -d "$host"
        mark_done "$host"
      fi
      ;;
    *.*.*)
      issue_cert -d "$host"
      mark_done "$host"
      ;;
    *.*)
      if grep -Fxq "www.$host" "$tmp_all"; then
        issue_cert -d "$host" -d "www.$host"
        mark_done "$host"
        mark_done "www.$host"
      else
        issue_cert -d "$host"
        mark_done "$host"
      fi
      ;;
    *)
      echo "skip invalid host: $host" >&2
      ;;
  esac
done < "$tmp_all"

আপডেট করার জন্য শুধু run অপশনটি পরিবর্তন করে renew করে দিলেই হবে।

অটোমেটিক আপডেট

আমার ক্ষেত্রে, যেহেতু আমি FreeBSD ব্যবহার করছি, তাই /etc/periodic.conf-এ এটি লিখেছি।

weekly_lego_enable="YES"
weekly_lego_renewscript="/usr/local/etc/lego/dns.sh"
weekly_lego_deployscript="/usr/local/etc/lego/deploy.sh"

যেহেতু ডিফল্ট /usr/local/etc/lego/deploy.sh এবং /usr/local/etc/lego/lego.sh বিদ্যমান, তাই মূলত এই ডিজাইনটি অনুসরণ করাই ভালো হবে। তবে, যতদূর দেখা যাচ্ছে ডিফল্টভাবে এটি ফাইল অথেন্টিকেশন ব্যবহার করে, তাই এবারের মতো যদি আপনি DNS অথেন্টিকেশন করতে চান, তবে পরিবর্তনের প্রয়োজন হবে।

Related Posts