- #8: Remove hardcoded credentials, require env vars (YK_ROOT_PIN, YK_INT_PIN, ADMIN_PASSWORD, JWT_SECRET) - #11: JWT secret now random via secrets.token_hex(32) if not set - #12: Admin password from env var, not hardcoded - #14: XSS prevention - sanitize error messages, html.escape - #15: CSRF tokens on all forms - #16: Cookie Secure flag added - #7: datetime.utcnow() → datetime.now(timezone.utc) - #3: Temp files in tempfile.mkdtemp, cleaned after use - #22: Private keys via cryptography library (NoEncryption for now) - #26: DER construction via cryptography library - #27: CRL table added for certificate revocation - #29: WAL autocheckpoint enabled - #30: Caddyfile already has TLS (no change needed) - #6: .gitignore for .password, *.pem, *.key
92 lines
3.0 KiB
Python
92 lines
3.0 KiB
Python
import sqlite3
|
|
import bcrypt
|
|
import logging
|
|
from config import DB_PATH, ADMIN_USERNAME, ADMIN_PASSWORD
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
def get_db():
|
|
conn = sqlite3.connect(DB_PATH, timeout=30)
|
|
conn.row_factory = sqlite3.Row
|
|
conn.execute("PRAGMA journal_mode=WAL")
|
|
conn.execute("PRAGMA busy_timeout=30000")
|
|
conn.execute("PRAGMA foreign_keys=ON")
|
|
conn.execute("PRAGMA wal_autocheckpoint=1000")
|
|
return conn
|
|
|
|
def init_db():
|
|
conn = get_db()
|
|
conn.executescript("""
|
|
CREATE TABLE IF NOT EXISTS users (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
username TEXT UNIQUE NOT NULL,
|
|
password_hash TEXT NOT NULL,
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
CREATE TABLE IF NOT EXISTS domains (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
name TEXT UNIQUE NOT NULL,
|
|
description TEXT,
|
|
status TEXT DEFAULT 'active',
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
created_by INTEGER REFERENCES users(id)
|
|
);
|
|
CREATE TABLE IF NOT EXISTS certificates (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
domain_id INTEGER REFERENCES users(id),
|
|
subject TEXT NOT NULL,
|
|
san TEXT,
|
|
serial TEXT UNIQUE,
|
|
status TEXT DEFAULT 'pending',
|
|
cert_path TEXT,
|
|
issued_at TIMESTAMP,
|
|
expires_at TIMESTAMP,
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
created_by INTEGER REFERENCES users(id)
|
|
);
|
|
CREATE TABLE IF NOT EXISTS api_keys (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
name TEXT NOT NULL,
|
|
key_hash TEXT UNIQUE NOT NULL,
|
|
prefix TEXT NOT NULL,
|
|
permissions TEXT DEFAULT 'read',
|
|
active BOOLEAN DEFAULT 1,
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
created_by INTEGER REFERENCES users(id)
|
|
);
|
|
CREATE TABLE IF NOT EXISTS audit_log (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
action TEXT NOT NULL,
|
|
details TEXT,
|
|
user_id INTEGER REFERENCES users(id),
|
|
ip_address TEXT,
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
CREATE TABLE IF NOT EXISTS crl (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
serial TEXT UNIQUE NOT NULL,
|
|
revoked_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
reason TEXT
|
|
);
|
|
""")
|
|
cur = conn.cursor()
|
|
cur.execute("SELECT id FROM users WHERE username = ?", (ADMIN_USERNAME,))
|
|
if not cur.fetchone():
|
|
pw_hash = bcrypt.hashpw(ADMIN_PASSWORD.encode(), bcrypt.gensalt())
|
|
if isinstance(pw_hash, bytes):
|
|
pw_hash = pw_hash.decode()
|
|
cur.execute("INSERT INTO users (username, password_hash) VALUES (?, ?)",
|
|
(ADMIN_USERNAME, pw_hash))
|
|
conn.commit()
|
|
conn.close()
|
|
logger.info("Database initialized")
|
|
|
|
def hash_password(password):
|
|
h = bcrypt.hashpw(password.encode(), bcrypt.gensalt())
|
|
return h.decode() if isinstance(h, bytes) else h
|
|
|
|
def verify_password(password, hash_):
|
|
if isinstance(hash_, str):
|
|
hash_ = hash_.encode()
|
|
return bcrypt.checkpw(password.encode(), hash_)
|