diff --git a/api/kiwi_vpn_api/config.py b/api/kiwi_vpn_api/config.py index 1bb2e6a..6b2c3b6 100644 --- a/api/kiwi_vpn_api/config.py +++ b/api/kiwi_vpn_api/config.py @@ -194,7 +194,7 @@ class LockableCountry(LockableString): value: constr(max_length=2) -class DNParts(BaseModel): +class ServerDN(BaseModel): """ This server's "distinguished name" """ @@ -204,6 +204,8 @@ class DNParts(BaseModel): city: LockableString organization: LockableString organizational_unit: LockableString + email: LockableString + common_name: str class CertificateAlgo(Enum): @@ -227,9 +229,10 @@ class CryptoConfig(BaseModel): schemes: list[str] = ["bcrypt"] # pki settings - ca_password: str | None cert_algo: CertificateAlgo | None - expiry_days: int | None + ca_password: str | None + ca_expiry_days: int | None + cert_expiry_days: int | None @property def context(self) -> CryptContext: @@ -244,15 +247,13 @@ class Config(BaseModel): Configuration for `kiwi-vpn-api` """ - # common name for the server - server_name: str # may include client-to-client, cipher etc. openvpn_extra_options: dict[str, Any] | None db: DBConfig jwt: JWTConfig crypto: CryptoConfig - default_dn: DNParts + server_dn: ServerDN __singleton: Config | None = None diff --git a/api/kiwi_vpn_api/easyrsa.py b/api/kiwi_vpn_api/easyrsa.py index 60f8a23..450baa8 100644 --- a/api/kiwi_vpn_api/easyrsa.py +++ b/api/kiwi_vpn_api/easyrsa.py @@ -9,7 +9,7 @@ from pathlib import Path from OpenSSL import crypto from passlib import pwd -from .config import Config, Settings +from .config import CertificateAlgo, Config, Settings class EasyRSA: @@ -53,9 +53,39 @@ class EasyRSA: def __build_cert( self, cert_filename: Path, + expiry_days: int | None, *easyrsa_args: str, ) -> crypto.X509: - self.__easyrsa(*easyrsa_args) + config = Config._ + + extra_args: tuple[str] = tuple() + + if expiry_days is not None: + extra_args += tuple([f"--days={expiry_days}"]) + + if (algo := config.crypto.cert_algo) is not None: + if algo is CertificateAlgo.rsa2048: + extra_args += ("--use-algo=rsa", "--keysize=2048") + + elif algo is CertificateAlgo.rsa4096: + extra_args += ("--use-algo=rsa", "--keysize=4096") + + elif algo is CertificateAlgo.secp256r1: + extra_args += ("--use-algo=ec", "--curve=secp256r1") + + elif algo is CertificateAlgo.secp384r1: + extra_args += ("--use-algo=ec", "--curve=secp384r1") + + elif algo is CertificateAlgo.ed25519: + extra_args += ("--use-algo=ed", "--curve=ed25519") + + else: + raise ValueError(f"Unexpected algorithm: {algo}") + + self.__easyrsa( + *extra_args, + *easyrsa_args + ) with open( self.pki_directory.joinpath(cert_filename), "r" @@ -67,30 +97,25 @@ class EasyRSA: def init_pki(self) -> bool: self.__easyrsa("init-pki") - def build_ca( - self, - ) -> crypto.X509: + def build_ca(self) -> crypto.X509: config = Config._ + server_dn = config.server_dn cert = self.__build_cert( Path("ca.crt"), + config.crypto.ca_expiry_days, f"--passout=pass:{self.ca_password}", f"--passin=pass:{self.ca_password}", - # "--dn-mode=org", - # "--req-c=EX", - # "--req-st=EXAMPLE", - # "--req-city=EXAMPLE", - # "--req-org=EXAMPLE", - # "--req-ou=EXAMPLE", - # "--req-email=EXAMPLE", - - f"--req-cn={config.server_name}", - f"--days={config.crypto.expiry_days}", - - # "--use-algo=ed", - # "--curve=ed25519", + "--dn-mode=org", + f"--req-c={server_dn.country.value}", + f"--req-st={server_dn.state.value}", + f"--req-city={server_dn.city.value}", + f"--req-org={server_dn.organization.value}", + f"--req-ou={server_dn.organizational_unit.value}", + f"--req-email={server_dn.email.value}", + f"--req-cn={server_dn.common_name}", "build-ca", ) @@ -100,16 +125,16 @@ class EasyRSA: def issue( self, + cert_type: str = "client", cn: str = "kiwi-vpn-client", - cert_type: str = "client" ) -> crypto.X509: config = Config._ return self.__build_cert( Path(f"issued/{cn}.crt"), + config.crypto.cert_expiry_days, f"--passin=pass:{self.ca_password}", - f"--days={config.crypto.expiry_days}", f"build-{cert_type}-full", cn,