[Ktor 部署] 添加 https 支持

现在的站点不支持 https 都已经说不过去了,特别是最近在搞微信小程序,后端用的 ktor,然而在上架的时候发现,所有的请求都必须是 https 的,那自然就得在 ktor 的程序里把 https 配上了。


首先想办法弄到了一个备过案的域名(不然微信审不过去),绑定解析 IP 后,ssh 登录到远程机器。然后就可以进行一系列操作了,为了方便起见(另外也不想花钱),我选了 Let's Encrypt 的证书服务。

在服务器上先进行安装操作,需要安装 certbot:

$ sudo apt install whois certbot

安装完成后,就可以申请证书了:

$ export DOMAIN=rarnu.com
$ export EMAIL=admin@rarnu.com
$ export PORT=80
$ export ALIAS=rarnu
$ sudo certbot certonly -n -d $DOMAIN --email "$EMAIL" --agree-tos --standalone --preferred-challenges http --http-01-port $PORT

这里的 DOMAIN 即是要申请证书的域名,注意必须是域名,IP 地址不可以。后面的 EMAIL 是注册域名时填的邮箱地址(经过实际验证似乎任意邮箱都行,假的也行)。再往后是服务的端口号,默认填80就可以了,如果填了别的,也会被重定向到80,因此在申请证书时,千万记得不能开启 ktor 服务。最后的 ALIAS 是证书的别名,随便填写就好。

申请成功后,会看到如下的提示:

Saving debug log to /var/log/letsencrypt/letsencrypt.log
Plugins selected: Authenticator standalone, Installer None
Obtaining a new certificate
Performing the following challenges:
http-01 challenge for rarnu.com
Waiting for verification...
Cleaning up challenges

IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at:
   /etc/letsencrypt/live/rarnu.com/fullchain.pem
   Your key file has been saved at:
   /etc/letsencrypt/live/rarnu.com/privkey.pem
   Your cert will expire on 2020-08-22. To obtain a new or tweaked
   version of this certificate in the future, simply run certbot
   again. To non-interactively renew *all* of your certificates, run
   "certbot renew"
 - If you like Certbot, please consider supporting our work by:

   Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
   Donating to EFF:                    https://eff.org/donate-le

这个时候我们可以进入目录看到证书文件:

$ cd /etc/letsencrypt/live/rarnu.com
$ ls
README  cert.pem  chain.pem  fullchain.pem  privkey.pem

下面我们来把证书转换成 ktor 所需的 keystore.jks 文件:

openssl pkcs12 -export -out /etc/letsencrypt/live/$DOMAIN/keystore.p12 -inkey /etc/letsencrypt/live/$DOMAIN/privkey.pem -in /etc/letsencrypt/live/$DOMAIN/fullchain.pem -name $ALIAS

这个命令会让你设置证书的密码,设置完成后会生成一个 keystore.p12 文件,然后再把 p12 转为我们要的最终文件:

keytool -importkeystore -alias $ALIAS -destkeystore /etc/letsencrypt/live/$DOMAIN/keystore.jks -srcstoretype PKCS12 -srckeystore /etc/letsencrypt/live/$DOMAIN/keystore.p12

这个命令也会让你输入密码,这是 keystore 的密码,可以与 p12 一致或不一致,别忘了就好,完成后生成 keystore.jks,这个文件就是我们最终要用的了。


现在,打开 ktor 项目,在 application.conf 内加入相关的配置:

ktor {
    deployment {
        port = 80
        port = ${?PORT}
        sslPort = 443
        sslPort = ${?PORT_SSL}
    }
    security {
        ssl {
            keyStore = /etc/letsencrypt/live/rarnu.com/keystore.jks
            keyAlias = rarnu
            keyStorePassword = 123456
            privateKeyPassword = 123456
        }
    }
    application {
        modules = [ ... ]
    }
}

然后编译并把项目部署到服务器上就可以了,现在可以支持 http 和 https 两种请求方式。

如果希望只启用 https,可以把 port = * 的两行代码删除,这个时候就只能以 https 访问了。


原本到了这里就结束了,但是还有另一种情况,就是本机调试,如果每次改代码都得搞到服务器上去测试,就太累人了,我们有必要在本地也部署一套 https。

操作方式是在 /etc/hosts 里把域名配到本地:

127.0.0.1  rarnu.com

然后直接把服务器端的 keystore.jks 拷到本地的目录(这里指的是 /etc/letsencrypt/live/rarnu.com/)就可以本地调试了。


原本到了这里又要结束了,但是还有另一种情况,是不采用真实证书,而是用一个假的自签名证书来取代,通常情况下这是没有证书又要调试的最佳办法。

要实现自签名证书非常简单,一段代码即可:

import io.ktor.network.tls.certificates.generateCertificate
import java.io.File

fun main(args: Array<String>) { 
    generateCertificate(File("keystore.jks")) 
}

编译代码前记得先加入依赖:

compile "io.ktor:ktor-network-tls:$ktor_version"

然后就可以把生成的 jks 配到项目里了,需要注意的是自签名证书拥有默认的 alias 和 password:

security {
    ssl {
        keyStore = keystore.jks
        keyAlias = mykey
        keyStorePassword = changeit
        privateKeyPassword = changeit
    }
}


这次是真的结束了,ktor 配 https 还是非常简便的,最后我的小程序也终于成功上线了!

推荐阅读更多精彩内容