Vue.component("ssl-config-box", { props: [ "v-ssl-policy", "v-protocol", "v-server-id", "v-support-http3" ], created: function () { let that = this setTimeout(function () { that.sortableCipherSuites() }, 100) }, data: function () { let policy = this.vSslPolicy if (policy == null) { policy = { id: 0, isOn: true, certRefs: [], certs: [], clientCARefs: [], clientCACerts: [], clientAuthType: 0, minVersion: "TLS 1.1", hsts: null, cipherSuitesIsOn: false, cipherSuites: [], http2Enabled: true, http3Enabled: false, ocspIsOn: false } } else { if (policy.certRefs == null) { policy.certRefs = [] } if (policy.certs == null) { policy.certs = [] } if (policy.clientCARefs == null) { policy.clientCARefs = [] } if (policy.clientCACerts == null) { policy.clientCACerts = [] } if (policy.cipherSuites == null) { policy.cipherSuites = [] } } let hsts = policy.hsts let hstsMaxAgeString = "31536000" if (hsts == null) { hsts = { isOn: false, maxAge: 31536000, includeSubDomains: false, preload: false, domains: [] } } if (hsts.maxAge != null) { hstsMaxAgeString = hsts.maxAge.toString() } return { policy: policy, // hsts hsts: hsts, hstsOptionsVisible: false, hstsDomainAdding: false, hstsMaxAgeString: hstsMaxAgeString, addingHstsDomain: "", hstsDomainEditingIndex: -1, // 相关数据 allVersions: window.SSL_ALL_VERSIONS, allCipherSuites: window.SSL_ALL_CIPHER_SUITES.$copy(), modernCipherSuites: window.SSL_MODERN_CIPHER_SUITES, intermediateCipherSuites: window.SSL_INTERMEDIATE_CIPHER_SUITES, allClientAuthTypes: window.SSL_ALL_CLIENT_AUTH_TYPES, cipherSuitesVisible: false, // 高级选项 moreOptionsVisible: false } }, watch: { hsts: { deep: true, handler: function () { this.policy.hsts = this.hsts } } }, methods: { // 删除证书 removeCert: function (index) { let that = this teaweb.confirm("确定删除此证书吗?证书数据仍然保留,只是当前网站不再使用此证书。", function () { that.policy.certRefs.$remove(index) that.policy.certs.$remove(index) }) }, // 选择证书 selectCert: function () { let that = this let selectedCertIds = [] if (this.policy != null && this.policy.certs.length > 0) { this.policy.certs.forEach(function (cert) { selectedCertIds.push(cert.id.toString()) }) } let serverId = this.vServerId if (serverId == null) { serverId = 0 } teaweb.popup("/servers/certs/selectPopup?selectedCertIds=" + selectedCertIds + "&serverId=" + serverId, { width: "50em", height: "30em", callback: function (resp) { if (resp.data.cert != null && resp.data.certRef != null) { that.policy.certRefs.push(resp.data.certRef) that.policy.certs.push(resp.data.cert) } if (resp.data.certs != null && resp.data.certRefs != null) { that.policy.certRefs.$pushAll(resp.data.certRefs) that.policy.certs.$pushAll(resp.data.certs) } that.$forceUpdate() } }) }, // 上传证书 uploadCert: function () { let that = this let serverId = this.vServerId if (typeof serverId != "number" && typeof serverId != "string") { serverId = 0 } teaweb.popup("/servers/certs/uploadPopup?serverId=" + serverId, { height: "30em", callback: function (resp) { teaweb.success("上传成功", function () { that.policy.certRefs.push(resp.data.certRef) that.policy.certs.push(resp.data.cert) }) } }) }, // 批量上传 uploadBatch: function () { let that = this let serverId = this.vServerId if (typeof serverId != "number" && typeof serverId != "string") { serverId = 0 } teaweb.popup("/servers/certs/uploadBatchPopup?serverId=" + serverId, { callback: function (resp) { if (resp.data.cert != null) { that.policy.certRefs.push(resp.data.certRef) that.policy.certs.push(resp.data.cert) } if (resp.data.certs != null) { that.policy.certRefs.$pushAll(resp.data.certRefs) that.policy.certs.$pushAll(resp.data.certs) } that.$forceUpdate() } }) }, // 申请证书 requestCert: function () { // 已经在证书中的域名 let excludeServerNames = [] if (this.policy != null && this.policy.certs.length > 0) { this.policy.certs.forEach(function (cert) { excludeServerNames.$pushAll(cert.dnsNames) }) } let that = this teaweb.popup("/servers/server/settings/https/requestCertPopup?serverId=" + this.vServerId + "&excludeServerNames=" + excludeServerNames.join(","), { callback: function () { that.policy.certRefs.push(resp.data.certRef) that.policy.certs.push(resp.data.cert) } }) }, // 更多选项 changeOptionsVisible: function () { this.moreOptionsVisible = !this.moreOptionsVisible }, // 格式化时间 formatTime: function (timestamp) { return new Date(timestamp * 1000).format("Y-m-d") }, // 格式化加密套件 formatCipherSuite: function (cipherSuite) { return cipherSuite.replace(/(AES|3DES)/, "$1") }, // 添加单个套件 addCipherSuite: function (cipherSuite) { if (!this.policy.cipherSuites.$contains(cipherSuite)) { this.policy.cipherSuites.push(cipherSuite) } this.allCipherSuites.$removeValue(cipherSuite) }, // 删除单个套件 removeCipherSuite: function (cipherSuite) { let that = this teaweb.confirm("确定要删除此套件吗?", function () { that.policy.cipherSuites.$removeValue(cipherSuite) that.allCipherSuites = window.SSL_ALL_CIPHER_SUITES.$findAll(function (k, v) { return !that.policy.cipherSuites.$contains(v) }) }) }, // 清除所选套件 clearCipherSuites: function () { let that = this teaweb.confirm("确定要清除所有已选套件吗?", function () { that.policy.cipherSuites = [] that.allCipherSuites = window.SSL_ALL_CIPHER_SUITES.$copy() }) }, // 批量添加套件 addBatchCipherSuites: function (suites) { var that = this teaweb.confirm("确定要批量添加套件?", function () { suites.$each(function (k, v) { if (that.policy.cipherSuites.$contains(v)) { return } that.policy.cipherSuites.push(v) }) }) }, /** * 套件拖动排序 */ sortableCipherSuites: function () { var box = document.querySelector(".cipher-suites-box") Sortable.create(box, { draggable: ".label", handle: ".icon.handle", onStart: function () { }, onUpdate: function (event) { } }) }, // 显示所有套件 showAllCipherSuites: function () { this.cipherSuitesVisible = !this.cipherSuitesVisible }, // 显示HSTS更多选项 showMoreHSTS: function () { this.hstsOptionsVisible = !this.hstsOptionsVisible; if (this.hstsOptionsVisible) { this.changeHSTSMaxAge() } }, // 监控HSTS有效期修改 changeHSTSMaxAge: function () { var v = parseInt(this.hstsMaxAgeString) if (isNaN(v) || v < 0) { this.hsts.maxAge = 0 this.hsts.days = "-" return } this.hsts.maxAge = v this.hsts.days = v / 86400 if (this.hsts.days == 0) { this.hsts.days = "-" } }, // 设置HSTS有效期 setHSTSMaxAge: function (maxAge) { this.hstsMaxAgeString = maxAge.toString() this.changeHSTSMaxAge() }, // 添加HSTS域名 addHstsDomain: function () { this.hstsDomainAdding = true this.hstsDomainEditingIndex = -1 let that = this setTimeout(function () { that.$refs.addingHstsDomain.focus() }, 100) }, // 修改HSTS域名 editHstsDomain: function (index) { this.hstsDomainEditingIndex = index this.addingHstsDomain = this.hsts.domains[index] this.hstsDomainAdding = true let that = this setTimeout(function () { that.$refs.addingHstsDomain.focus() }, 100) }, // 确认HSTS域名添加 confirmAddHstsDomain: function () { this.addingHstsDomain = this.addingHstsDomain.trim() if (this.addingHstsDomain.length == 0) { return; } if (this.hstsDomainEditingIndex > -1) { this.hsts.domains[this.hstsDomainEditingIndex] = this.addingHstsDomain } else { this.hsts.domains.push(this.addingHstsDomain) } this.cancelHstsDomainAdding() }, // 取消HSTS域名添加 cancelHstsDomainAdding: function () { this.hstsDomainAdding = false this.addingHstsDomain = "" this.hstsDomainEditingIndex = -1 }, // 删除HSTS域名 removeHstsDomain: function (index) { this.cancelHstsDomainAdding() this.hsts.domains.$remove(index) }, // 选择客户端CA证书 selectClientCACert: function () { let that = this teaweb.popup("/servers/certs/selectPopup?isCA=1", { width: "50em", height: "30em", callback: function (resp) { if (resp.data.cert != null && resp.data.certRef != null) { that.policy.clientCARefs.push(resp.data.certRef) that.policy.clientCACerts.push(resp.data.cert) } if (resp.data.certs != null && resp.data.certRefs != null) { that.policy.clientCARefs.$pushAll(resp.data.certRefs) that.policy.clientCACerts.$pushAll(resp.data.certs) } that.$forceUpdate() } }) }, // 上传CA证书 uploadClientCACert: function () { let that = this teaweb.popup("/servers/certs/uploadPopup?isCA=1", { height: "28em", callback: function (resp) { teaweb.success("上传成功", function () { that.policy.clientCARefs.push(resp.data.certRef) that.policy.clientCACerts.push(resp.data.cert) }) } }) }, // 删除客户端CA证书 removeClientCACert: function (index) { let that = this teaweb.confirm("确定删除此证书吗?证书数据仍然保留,只是当前网站不再使用此证书。", function () { that.policy.clientCARefs.$remove(index) that.policy.clientCACerts.$remove(index) }) } }, template: `

SSL/TLS相关配置

启用HTTP/2
启用HTTP/3
设置证书
{{cert.name}} / {{cert.dnsNames}} / 有效至{{formatTime(cert.timeEndAt)}}  
选择或上传证书后HTTPSTLS服务才能生效。
  |       |  
TLS最低版本
加密算法套件(CipherSuites)
已添加套件({{policy.cipherSuites.length}}):
 

点击可选套件添加。

开启HSTS

开启后,会自动在响应Header中加入 Strict-Transport-Security: ... max-age={{hsts.maxAge}} ; includeSubDomains ; preload 修改

HSTS有效时间(max-age)
{{hsts.days}}天

[1年/365天]     [6个月/182.5天]     [1个月/30天]

HSTS包含子域名(includeSubDomains)
HSTS预加载(preload)
HSTS生效的域名
{{domain}}  
  取消

如果没有设置域名的话,则默认支持所有的域名。

OCSP Stapling

选中表示启用OCSP Stapling。

客户端认证方式
客户端认证CA证书
{{cert.name}} / {{cert.dnsNames}} / 有效至{{formatTime(cert.timeEndAt)}}  
 

用来校验客户端证书以增强安全性,通常不需要设置。

` })