SSL Certificates¶
This page covers retrieving free SSL certificates from Porkbun.
Free SSL Certificates
Porkbun provides free SSL certificates (via Let's Encrypt) for domains using their nameservers. Certificates auto-renew automatically.
Quick Start¶
Retrieve SSL Bundle¶
The SSL bundle contains three components:
- Certificate Chain - The full certificate chain (your cert + intermediates)
- Private Key - The private key for the certificate
- Public Key - The public key
from oinker import AsyncPiglet
async with AsyncPiglet() as piglet:
bundle = await piglet.ssl.retrieve("example.com")
# Certificate chain (PEM format)
print("=== Certificate Chain ===")
print(bundle.certificate_chain[:500] + "...")
# Private key (PEM format)
print("\n=== Private Key ===")
print(bundle.private_key[:100] + "...")
# Public key (PEM format)
print("\n=== Public Key ===")
print(bundle.public_key[:100] + "...")
from oinker import Piglet
with Piglet() as piglet:
bundle = piglet.ssl.retrieve("example.com")
# Certificate chain (PEM format)
print("=== Certificate Chain ===")
print(bundle.certificate_chain[:500] + "...")
# Private key (PEM format)
print("\n=== Private Key ===")
print(bundle.private_key[:100] + "...")
# Public key (PEM format)
print("\n=== Public Key ===")
print(bundle.public_key[:100] + "...")
Save to Files¶
from pathlib import Path
from oinker import AsyncPiglet
async with AsyncPiglet() as piglet:
bundle = await piglet.ssl.retrieve("example.com")
# Create output directory
output_dir = Path("/etc/ssl/example.com")
output_dir.mkdir(parents=True, exist_ok=True)
# Save certificate chain
cert_path = output_dir / "fullchain.pem"
cert_path.write_text(bundle.certificate_chain)
print(f"Saved certificate chain to {cert_path}")
# Save private key with restricted permissions
key_path = output_dir / "privkey.pem"
key_path.write_text(bundle.private_key)
key_path.chmod(0o600) # Owner read/write only
print(f"Saved private key to {key_path}")
# Save public key (optional)
pub_path = output_dir / "pubkey.pem"
pub_path.write_text(bundle.public_key)
print(f"Saved public key to {pub_path}")
from pathlib import Path
from oinker import Piglet
with Piglet() as piglet:
bundle = piglet.ssl.retrieve("example.com")
# Create output directory
output_dir = Path("/etc/ssl/example.com")
output_dir.mkdir(parents=True, exist_ok=True)
# Save certificate chain
cert_path = output_dir / "fullchain.pem"
cert_path.write_text(bundle.certificate_chain)
print(f"Saved certificate chain to {cert_path}")
# Save private key with restricted permissions
key_path = output_dir / "privkey.pem"
key_path.write_text(bundle.private_key)
key_path.chmod(0o600) # Owner read/write only
print(f"Saved private key to {key_path}")
# Save public key (optional)
pub_path = output_dir / "pubkey.pem"
pub_path.write_text(bundle.public_key)
print(f"Saved public key to {pub_path}")
Real-World Patterns¶
Deploy to Nginx¶
import subprocess
from pathlib import Path
from oinker import AsyncPiglet
async def deploy_ssl_nginx(domain: str) -> None:
"""Fetch SSL cert and deploy to Nginx."""
async with AsyncPiglet() as piglet:
bundle = await piglet.ssl.retrieve(domain)
# Standard Nginx SSL paths
ssl_dir = Path(f"/etc/nginx/ssl/{domain}")
ssl_dir.mkdir(parents=True, exist_ok=True)
# Write certificate files
(ssl_dir / "fullchain.pem").write_text(bundle.certificate_chain)
key_path = ssl_dir / "privkey.pem"
key_path.write_text(bundle.private_key)
key_path.chmod(0o600)
# Test Nginx configuration
result = subprocess.run(
["nginx", "-t"],
capture_output=True,
text=True
)
if result.returncode != 0:
print(f"Nginx config error: {result.stderr}")
return
# Reload Nginx
subprocess.run(["systemctl", "reload", "nginx"])
print(f"✅ SSL certificate deployed for {domain}")
# Usage
import asyncio
asyncio.run(deploy_ssl_nginx("example.com"))
import subprocess
from pathlib import Path
from oinker import Piglet
def deploy_ssl_nginx(domain: str) -> None:
"""Fetch SSL cert and deploy to Nginx."""
with Piglet() as piglet:
bundle = piglet.ssl.retrieve(domain)
# Standard Nginx SSL paths
ssl_dir = Path(f"/etc/nginx/ssl/{domain}")
ssl_dir.mkdir(parents=True, exist_ok=True)
# Write certificate files
(ssl_dir / "fullchain.pem").write_text(bundle.certificate_chain)
key_path = ssl_dir / "privkey.pem"
key_path.write_text(bundle.private_key)
key_path.chmod(0o600)
# Test Nginx configuration
result = subprocess.run(
["nginx", "-t"],
capture_output=True,
text=True
)
if result.returncode != 0:
print(f"Nginx config error: {result.stderr}")
return
# Reload Nginx
subprocess.run(["systemctl", "reload", "nginx"])
print(f"✅ SSL certificate deployed for {domain}")
# Usage
deploy_ssl_nginx("example.com")
Fetch Certificates for Multiple Domains¶
import asyncio
from pathlib import Path
from oinker import AsyncPiglet
async with AsyncPiglet() as piglet:
# Get all domains
domains = await piglet.domains.list()
output_base = Path("./ssl_certs")
output_base.mkdir(exist_ok=True)
for domain_info in domains:
domain = domain_info.domain
try:
bundle = await piglet.ssl.retrieve(domain)
domain_dir = output_base / domain
domain_dir.mkdir(exist_ok=True)
(domain_dir / "fullchain.pem").write_text(bundle.certificate_chain)
key_path = domain_dir / "privkey.pem"
key_path.write_text(bundle.private_key)
key_path.chmod(0o600)
print(f"✅ {domain}")
except Exception as e:
print(f"❌ {domain}: {e}")
from pathlib import Path
from oinker import Piglet
with Piglet() as piglet:
# Get all domains
domains = piglet.domains.list()
output_base = Path("./ssl_certs")
output_base.mkdir(exist_ok=True)
for domain_info in domains:
domain = domain_info.domain
try:
bundle = piglet.ssl.retrieve(domain)
domain_dir = output_base / domain
domain_dir.mkdir(exist_ok=True)
(domain_dir / "fullchain.pem").write_text(bundle.certificate_chain)
key_path = domain_dir / "privkey.pem"
key_path.write_text(bundle.private_key)
key_path.chmod(0o600)
print(f"✅ {domain}")
except Exception as e:
print(f"❌ {domain}: {e}")
Check Certificate Expiration¶
from datetime import datetime
from cryptography import x509
from oinker import AsyncPiglet
async with AsyncPiglet() as piglet:
bundle = await piglet.ssl.retrieve("example.com")
# Parse the certificate
cert = x509.load_pem_x509_certificate(
bundle.certificate_chain.encode()
)
# Get expiration info
not_before = cert.not_valid_before_utc
not_after = cert.not_valid_after_utc
days_left = (not_after - datetime.now(not_after.tzinfo)).days
print(f"Subject: {cert.subject}")
print(f"Issuer: {cert.issuer}")
print(f"Valid from: {not_before}")
print(f"Valid until: {not_after}")
print(f"Days remaining: {days_left}")
if days_left < 30:
print("⚠️ Certificate expires soon!")
else:
print("✅ Certificate is healthy")
from datetime import datetime
from cryptography import x509
from oinker import Piglet
with Piglet() as piglet:
bundle = piglet.ssl.retrieve("example.com")
# Parse the certificate
cert = x509.load_pem_x509_certificate(
bundle.certificate_chain.encode()
)
# Get expiration info
not_before = cert.not_valid_before_utc
not_after = cert.not_valid_after_utc
days_left = (not_after - datetime.now(not_after.tzinfo)).days
print(f"Subject: {cert.subject}")
print(f"Issuer: {cert.issuer}")
print(f"Valid from: {not_before}")
print(f"Valid until: {not_after}")
print(f"Days remaining: {days_left}")
if days_left < 30:
print("⚠️ Certificate expires soon!")
else:
print("✅ Certificate is healthy")