No subject alternative DNS name matching xxx.com found

[TOC]

故事经过 TLDR;

故事太长,可以直接看这里 解决

昨天下午,突然有同事说OA打不开了,打开页面一看出现如下错误:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
500 Servlet Exception
[show] java.security.cert.CertificateException: No subject alternative DNS name
matching xxx.com found.

java.lang.RuntimeException: javax.net.ssl.SSLHandshakeException: java.security.cert.CertificateException:
No subject alternative DNS name matching xxx.com found.
at org.jasig.cas.client.util.CommonUtils.getResponseFromServer(CommonUtils.java:328)
at org.jasig.cas.client.util.CommonUtils.getResponseFromServer(CommonUtils.java:291)
at org.jasig.cas.client.validation.AbstractCasProtocolUrlBasedTicketValidator.retrieveResponseFromServer(AbstractCasProtocolUrlBasedTicketValidator.java:32)
at org.jasig.cas.client.validation.AbstractUrlBasedTicketValidator.validate(AbstractUrlBasedTicketValidator.java:187)
at org.jasig.cas.client.validation.AbstractTicketValidationFilter.doFilter(AbstractTicketValidationFilter.java:164)
at com.caucho.server.dispatch.FilterFilterChain.doFilter(FilterFilterChain.java:87)
at com.xxx.plugin.AuthenticationFilter.doFilter(AuthenticationFilter.java:163)
at com.caucho.server.dispatch.FilterFilterChain.doFilter(FilterFilterChain.java:87)
at org.jasig.cas.client.session.SingleSignOutFilter.doFilter(SingleSignOutFilter.java:65)
at com.caucho.server.dispatch.FilterFilterChain.doFilter(FilterFilterChain.java:87)
at com.caucho.server.webapp.WebAppFilterChain.doFilter(WebAppFilterChain.java:187)
at com.caucho.server.dispatch.ServletInvocation.service(ServletInvocation.java:265)
at com.caucho.server.http.HttpRequest.handleRequest(HttpRequest.java:273)
at com.caucho.server.port.TcpConnection.run(TcpConnection.java:682)
at com.caucho.util.ThreadPool$Item.runTasks(ThreadPool.java:730)
at com.caucho.util.ThreadPool$Item.run(ThreadPool.java:649)
at java.lang.Thread.run(Thread.java:662)
Caused by: javax.net.ssl.SSLHandshakeException: java.security.cert.CertificateException:
No subject alternative DNS name matching xxx.com found.
at com.sun.net.ssl.internal.ssl.Alerts.getSSLException(Alerts.java:174)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1699)
at com.sun.net.ssl.internal.ssl.Handshaker.fatalSE(Handshaker.java:241)
at com.sun.net.ssl.internal.ssl.Handshaker.fatalSE(Handshaker.java:235)

内心中第一反应是证书的问题,于是赶紧上cas服务器查看日志,一切正常😵
然后上OA的服务器,将证书导入,重启服务,该是什么错还是什么错。。。

上谷歌一查,应该是匹配不到证书里的DNS名,但是确实是有的呀。

由于还有其他java系统接入了CAS登录,都是正常的,于是开始怀疑是OA那台的有问题,开始查看是不是有人最近改动过什么

然鹅并没有。。。

然后查看OA的日志(用的Resin中间件。。。
发现如下错误:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
[17:00:15.288] {http--8080-6$1533061820} java.lang.RuntimeException: javax.net.ssl.SSLHandshakeException: java.security.cert.CertificateException: No subject alternative DNS name matching auth.corp.flamingo-inc.com found.
[17:00:15.288] {http--8080-6$1533061820} at org.jasig.cas.client.util.CommonUtils.getResponseFromServer(CommonUtils.java:295)
[17:00:15.288] {http--8080-6$1533061820} at org.jasig.cas.client.validation.AbstractCasProtocolUrlBasedTicketValidator.retrieveResponseFromServer(AbstractCasProtocolUrlBasedTicketValidator.java:33)
[17:00:15.288] {http--8080-6$1533061820} at org.jasig.cas.client.validation.AbstractUrlBasedTicketValidator.validate(AbstractUrlBasedTicketValidator.java:178)
[17:00:15.288] {http--8080-6$1533061820} at org.jasig.cas.client.validation.AbstractTicketValidationFilter.doFilter(AbstractTicketValidationFilter.java:132)
[17:00:15.288] {http--8080-6$1533061820} at com.caucho.server.dispatch.FilterFilterChain.doFilter(FilterFilterChain.java:87)
[17:00:15.288] {http--8080-6$1533061820} at com.xxx.plugin.AuthenticationFilter.doFilter(AuthenticationFilter.java:163)
[17:00:15.288] {http--8080-6$1533061820} at com.caucho.server.dispatch.FilterFilterChain.doFilter(FilterFilterChain.java:87)
[17:00:15.288] {http--8080-6$1533061820} at org.jasig.cas.client.session.SingleSignOutFilter.doFilter(SingleSignOutFilter.java:110)
[17:00:15.288] {http--8080-6$1533061820} at com.caucho.server.dispatch.FilterFilterChain.doFilter(FilterFilterChain.java:87)
[17:00:15.288] {http--8080-6$1533061820} at com.caucho.server.webapp.WebAppFilterChain.doFilter(WebAppFilterChain.java:187)
[17:00:15.288] {http--8080-6$1533061820} at com.caucho.server.dispatch.ServletInvocation.service(ServletInvocation.java:265)
[17:00:15.288] {http--8080-6$1533061820} at com.caucho.server.http.HttpRequest.handleRequest(HttpRequest.java:273)
[17:00:15.288] {http--8080-6$1533061820} at com.caucho.server.port.TcpConnection.run(TcpConnection.java:682)
[17:00:15.288] {http--8080-6$1533061820} at com.caucho.util.ThreadPool$Item.runTasks(ThreadPool.java:730)
[17:00:15.288] {http--8080-6$1533061820} at com.caucho.util.ThreadPool$Item.run(ThreadPool.java:649)
[17:00:15.288] {http--8080-6$1533061820} at java.lang.Thread.run(Thread.java:662)
[17:00:15.288] {http--8080-6$1533061820} Caused by: javax.net.ssl.SSLHandshakeException: java.security.cert.CertificateException: No subject alternative DNS name matching auth.corp.flamingo-inc.com found.
[17:00:15.288] {http--8080-6$1533061820} at com.sun.net.ssl.internal.ssl.Alerts.getSSLException(Alerts.java:174)
[17:00:15.288] {http--8080-6$1533061820} at com.sun.net.ssl.internal.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1699)
[17:00:15.288] {http--8080-6$1533061820} at com.sun.net.ssl.internal.ssl.Handshaker.fatalSE(Handshaker.java:241)
[17:00:15.288] {http--8080-6$1533061820} at com.sun.net.ssl.internal.ssl.Handshaker.fatalSE(Handshaker.java:235)
[17:00:15.288] {http--8080-6$1533061820} at com.sun.net.ssl.internal.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1206)
[17:00:15.288] {http--8080-6$1533061820} at com.sun.net.ssl.internal.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:136)
[17:00:15.288] {http--8080-6$1533061820} at com.sun.net.ssl.internal.ssl.Handshaker.processLoop(Handshaker.java:593)
[17:00:15.288] {http--8080-6$1533061820} at com.sun.net.ssl.internal.ssl.Handshaker.process_record(Handshaker.java:529)
[17:00:15.288] {http--8080-6$1533061820} at com.sun.net.ssl.internal.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:893)
[17:00:15.288] {http--8080-6$1533061820} at com.sun.net.ssl.internal.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1138)
[17:00:15.288] {http--8080-6$1533061820} at com.sun.net.ssl.internal.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1165)
[17:00:15.288] {http--8080-6$1533061820} at com.sun.net.ssl.internal.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1149)
[17:00:15.288] {http--8080-6$1533061820} at sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:434)
[17:00:15.288] {http--8080-6$1533061820} at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:166)
[17:00:15.288] {http--8080-6$1533061820} at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1172)
[17:00:15.288] {http--8080-6$1533061820} at sun.net.www.protocol.https.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.java:234)
[17:00:15.288] {http--8080-6$1533061820} at org.jasig.cas.client.util.CommonUtils.getResponseFromServer(CommonUtils.java:281)
[17:00:15.288] {http--8080-6$1533061820} ... 15 more
[17:00:15.288] {http--8080-6$1533061820} Caused by: java.security.cert.CertificateException: No subject alternative DNS name matching auth.corp.flamingo-inc.com found.
[17:00:15.288] {http--8080-6$1533061820} at sun.security.util.HostnameChecker.matchDNS(HostnameChecker.java:193)
[17:00:15.288] {http--8080-6$1533061820} at sun.security.util.HostnameChecker.match(HostnameChecker.java:77)
[17:00:15.288] {http--8080-6$1533061820} at com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.checkIdentity(X509TrustManagerImpl.java:264)
[17:00:15.288] {http--8080-6$1533061820} at com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:250)
[17:00:15.288] {http--8080-6$1533061820} at com.sun.net.ssl.internal.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1185)

开始怀疑是我写的AuthenticationFilter的锅,于是用了个新版本的cas-client-core,依然不行

开始升级jdk,由于是windows,直接安装的exe,然后改了启动脚本里面的JAVA_HOME,重启依然发现不行。。。 (((φ(◎ロ◎;)φ)))

于是暂时回退到了没有用CAS接入的版本,下班了。。。

今天回来不甘心啊,继续尝试解决

既然证书找不到xxx.com,那我换台nginx直接写host试试?

更新了一下测服nginx的证书,OA主机指定测服nginx ip

然后!!!! 可以了!!!! 😭

然后跟同事调试了一会,发现他昨天下午添加了一个域名abc.com,然后导致了default_server不是adc.com了。。。

解决方法

更新一下jdk就可以了(摊手),OA用的是jdk1.6,jdk1.6旧版本不支持SNI,至于什么是SNI等,看这里 https://github.com/ditunes/blog/issues/13

之前没成功是因为windows的服务里面写死了jdk的路径。。。

使用Openresty将pastebin内容格式化显示

前情提要

这里用的 https://github.com/solusipse/fiche 的pastebin服务端,功能不多但是贴一些代码或者日志足够了

痛点

fiche将贴的内容保存为文件,然后用nginx显示,如下:

1
2
3
4
5
6
7
8
9
10
server {
listen 80;
server_name mysite.com www.mysite.com;
charset utf-8;

location / {
root /home/www/code/;
index index.txt index.html;
}
}

这样显示出来的文本在行数多的时候就看瞎眼了 @_@

解决痛点

我们可以用openresty将要显示的文本提前拼凑成html,html中用google的code prettify格式化处理。
对于用curl请求的我们不做处理,直接返回纯文本。

lua代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
local headers = ngx.req.get_headers()
local reqUri = ngx.var.request_uri
local uri = ngx.var.uri
local userAgent = headers["user-agent"]
local codePath = "/data/fiche/code"

if uri == "/" then
ngx.say("Welcome to p.qiyuos.cn")
ngx.exit(200)
end

if uri == "/favicon.ico" then
ngx.exit(200)
end

local function file_exists(path)
local file = io.open(path, "rb")
if file then file:close() end
return file ~= nil
end

--读取文件到内存
local function readFile2Mem(file)
local fp = io.open(file,"r")
if fp then
return fp:read("*all")
end
end

local codeBeauty1 = [[
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<script src="https://cdn.bootcss.com/prettify/r298/run_prettify.min.js?autoload=true&amp;lang=html"></script>
<style type="text/css">
body {
background: #fff;
}
pre.prettyprint {
background-color: #eee;
}
.prettyprint ol.linenums > li { list-style-type: decimal; }
.pln{color:#000}@media screen{.str{color:#080}.kwd{color:#008}.com{color:#800}.typ{color:#606}.lit{color:#066}.clo,.opn,.pun{color:#660}.tag{color:#008}.atn{color:#606}.atv{color:#080}.dec,.var{color:#606}.fun{color:red}}@media print,projection{.kwd,.tag,.typ{font-weight:700}.str{color:#060}.kwd{color:#006}.com{color:#600;font-style:italic}.typ{color:#404}.lit{color:#044}.clo,.opn,.pun{color:#440}.tag{color:#006}.atn{color:#404}.atv{color:#060}}pre.prettyprint{padding:2px;border:1px solid #888}ol.linenums{margin-top:0;margin-bottom:0}li.L0,li.L1,li.L2,li.L3,li.L5,li.L6,li.L7,li.L8{list-style-type:none}li.L1,li.L3,li.L5,li.L7,li.L9{background:#eee}
</style>
</head>
<body>
<pre class="prettyprint linenums" id="quine">
]]
local codeBeauty2 = [[
</pre>
<script type="text/javascript">//<![CDATA[
(function () {
function htmlEscape(s) {
return s;
// .replace(/&/g, '&amp;')
// .replace(/</g, '&lt;')
// .replace(/>/g, '&gt;');
}
// this page's own source code
var quineHtml = htmlEscape(
document.getElementById("quine").innerHTML
);
// Highlight the operative parts:
quineHtml = quineHtml.replace(
/&lt;script src[\s\S]*?&gt;&lt;\/script&gt;|&lt;!--\?[\s\S]*?--&gt;|&lt;pre\b[\s\S]*?&lt;\/pre&gt;/g,
'<span class="operative">$&<\/span>');
// insert into PRE
document.getElementById("quine").innerHTML = quineHtml;
})();
//\]\]>
</script>
</body>
</html>
]]

if not file_exists(codePath .. uri .. "/index.txt") then
ngx.exit(404)
end

local paste = readFile2Mem(codePath .. uri .. "/index.txt")
if not userAgent or string.len(userAgent) < 20 or not string.match(userAgent, "Mozilla") then
ngx.say(paste)
ngx.exit(200)
else
ngx.say(codeBeauty1 .. paste .. codeBeauty2)
ngx.exit(200)
end

nginx 配置如下:

1
2
3
4
5
6
7
8
9
server {
listen 80;
server_name mysite.com www.mysite.com;
charset utf-8;
location / {
default_type text/html;
content_by_lua_file "/usr/local/openresty/scripts/fiche.lua";
}
}

最后

代码还不完善,可能存在一些bug,欢迎提出

使用openresty搭一个类似ip.cn的接口

Openresty是什么

OpenResty® 是一个基于 Nginx 与 Lua 的高性能 Web 平台,其内部集成了大量精良的 Lua 库、第三方模块以及大多数的依赖项。用于方便地搭建能够处理超高并发、扩展性极高的动态 Web 应用、Web 服务和动态网关。

官网 https://openresty.org/cn/

Openresty解决了nginx不能很好的添加一些逻辑判断的痛点,而且又不失性能。

接口代码

代码简单到不能再简单了,就三行

1
2
3
local ip = ngx.var.remote_addr
ngx.header["Content-Type"] = "text/plain charset=utf-8"
ngx.say(ip)
nginx.conf配置:
1
2
3
4
5
6
7
...
http
{
...
lua_package_path "/usr/local/openresty/script/waf/?.lua;/usr/local/openresty/lualib/?.lua";
...

vhost配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
server
{
listen 80;
server_name ip.testing.cn;
index index.html index.htm index.php;
charset utf-8;
# 原生nginx的方式,貌似更简单...
# location / {
# default_type text/plain;
# return 200 $remote_addr;
# }
location /{
content_by_lua_block {
local ip = ngx.var.remote_addr
ngx.header["Content-Type"] = "text/plain charset=utf-8"
ngx.say(ip)
}
}

}

cas根据请求参数跳过验证

接上一篇 https://birdzhang.xyz/2018/05/15/Java%E6%94%B9%E5%86%99HttpServletRequest%E8%AF%B7%E6%B1%82%E5%8F%82%E6%95%B0/ ,这里说一下cas的坑(这里的cas是3.5版本)

虽然cas有CAS Authentication Filter,但是这个只是根据请求的uri过滤的,那么这里就出现了一个奇怪的bug

即使我在excludePaths里面添加了/test1/,但是当请求地址为/test1/123?ticket=123的时候,还是会去cas服务器验证!!!

我们来看代码:

CAS Authentication Filter 代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
package com.birdzhang.demo.check;

import java.io.IOException;

import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.jasig.cas.client.authentication.DefaultGatewayResolverImpl;
import org.jasig.cas.client.authentication.GatewayResolver;
import org.jasig.cas.client.util.AbstractCasFilter;
import org.jasig.cas.client.util.CommonUtils;
import org.jasig.cas.client.validation.Assertion;

public class AuthenticationFilter extends AbstractCasFilter{


/**
* The URL to the CAS Server login.
*/
private String casServerLoginUrl;

/**
* Whether to send the renew request or not.
*/
private boolean renew = false;

/**
* Whether to send the gateway request or not.
*/
private boolean gateway = false;
/**
* 添加属性,这里用来存放不过滤地址正则表达式,可以根据自己需求定制---1
*/
private String excludePaths;

private GatewayResolver gatewayStorage = new DefaultGatewayResolverImpl();

protected void initInternal(final FilterConfig filterConfig) throws ServletException {
if (!isIgnoreInitConfiguration()) {
super.initInternal(filterConfig);
setCasServerLoginUrl(getPropertyFromInitParams(filterConfig, "casServerLoginUrl", null));
//log.trace("Loaded CasServerLoginUrl parameter: " + this.casServerLoginUrl);
setRenew(parseBoolean(getPropertyFromInitParams(filterConfig, "renew", "false")));
//log.trace("Loaded renew parameter: " + this.renew);
setGateway(parseBoolean(getPropertyFromInitParams(filterConfig, "gateway", "false")));
//log.trace("Loaded gateway parameter: " + this.gateway);

final String gatewayStorageClass = getPropertyFromInitParams(filterConfig, "gatewayStorageClass", null);

if (gatewayStorageClass != null) {
try {
this.gatewayStorage = (GatewayResolver) Class.forName(gatewayStorageClass).newInstance();
} catch (final Exception e) {
//log.error(e,e);
throw new ServletException(e);
}
}
//自定义添加代码,用来读取web配置文件中excludes属性值 ---2
excludePaths = getPropertyFromInitParams(filterConfig, "excludePaths", null);//filterConfig.getInitParameter("excludePaths");
excludePaths = excludePaths.trim();
}
}

public void init() {
super.init();
CommonUtils.assertNotNull(this.casServerLoginUrl, "casServerLoginUrl cannot be null.");
}
//url判断逻辑,这里大家可以根据自己需要来制订规则
private boolean isExclude(String uri){
boolean isInWhiteList = false;
if(excludePaths!=null&& uri!=null){
isInWhiteList = uri.matches(excludePaths);
}
return isInWhiteList;
}


public final void doFilter(final ServletRequest servletRequest, final ServletResponse servletResponse, final FilterChain filterChain) throws IOException, ServletException {
final HttpServletRequest request = (HttpServletRequest) servletRequest;
final HttpServletResponse response = (HttpServletResponse) servletResponse;
final HttpSession session = request.getSession(false);
final Assertion assertion = session != null ? (Assertion) session.getAttribute(CONST_CAS_ASSERTION) : null;
// 该判断是自定义的对符合条件的url进行通过处理 ---3
if(isExclude(request.getRequestURI())){
filterChain.doFilter(request, response);
return;
}

if (assertion != null) {
filterChain.doFilter(request, response);
return;
}

final String serviceUrl = constructServiceUrl(request, response);
final String ticket = CommonUtils.safeGetParameter(request,getArtifactParameterName());
final boolean wasGatewayed = this.gatewayStorage.hasGatewayedAlready(request, serviceUrl);

if (CommonUtils.isNotBlank(ticket) || wasGatewayed) {
filterChain.doFilter(request, response);
return;
}

final String modifiedServiceUrl;

//log.debug("no ticket and no assertion found");
if (this.gateway) {
//log.debug("setting gateway attribute in session");
modifiedServiceUrl = this.gatewayStorage.storeGatewayInformation(request, serviceUrl);
} else {
modifiedServiceUrl = serviceUrl;
}

/* if (log.isDebugEnabled()) {
log.debug("Constructed service url: " + modifiedServiceUrl);
}*/

final String urlToRedirectTo = CommonUtils.constructRedirectUrl(this.casServerLoginUrl, getServiceParameterName(), modifiedServiceUrl, this.renew, this.gateway);

/* if (log.isDebugEnabled()) {
log.debug("redirecting to \"" + urlToRedirectTo + "\"");
}*/

response.sendRedirect(urlToRedirectTo);
}

public final void setRenew(final boolean renew) {
this.renew = renew;
}

public final void setGateway(final boolean gateway) {
this.gateway = gateway;
}

public final void setCasServerLoginUrl(final String casServerLoginUrl) {
this.casServerLoginUrl = casServerLoginUrl;
}

public final void setGatewayStorage(final GatewayResolver gatewayStorage) {
this.gatewayStorage = gatewayStorage;
}
}

可以看到在uri匹配到excludepath之后会走filterChain.doFilter(request, response);,由于我们并没有对filterChain做什么修改,所以最终还是跑到cas那里去了

修改后的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
package com.birdzhang.plugin;

/**
* @author debo.zhang
*
*/
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.jasig.cas.client.authentication.DefaultGatewayResolverImpl;
import org.jasig.cas.client.authentication.GatewayResolver;
import org.jasig.cas.client.util.AbstractCasFilter;
import org.jasig.cas.client.util.CommonUtils;
import org.jasig.cas.client.validation.Assertion;

public class AuthenticationFilter extends AbstractCasFilter{


/**
* The URL to the CAS Server login.
*/
private String casServerLoginUrl;

/**
* Whether to send the renew request or not.
*/
private boolean renew = false;

/**
* Whether to send the gateway request or not.
*/
private boolean gateway = false;
/**
* 添加属性,这里用来存放不过滤地址正则表达式,可以根据自己需求定制---1
*/
private String excludePaths;

private GatewayResolver gatewayStorage = new DefaultGatewayResolverImpl();

protected void initInternal(final FilterConfig filterConfig) throws ServletException {
if (!isIgnoreInitConfiguration()) {
super.initInternal(filterConfig);
setCasServerLoginUrl(getPropertyFromInitParams(filterConfig, "casServerLoginUrl", null));
//log.trace("Loaded CasServerLoginUrl parameter: " + this.casServerLoginUrl);
setRenew(parseBoolean(getPropertyFromInitParams(filterConfig, "renew", "false")));
//log.trace("Loaded renew parameter: " + this.renew);
setGateway(parseBoolean(getPropertyFromInitParams(filterConfig, "gateway", "false")));
//log.trace("Loaded gateway parameter: " + this.gateway);

final String gatewayStorageClass = getPropertyFromInitParams(filterConfig, "gatewayStorageClass", null);

if (gatewayStorageClass != null) {
try {
this.gatewayStorage = (GatewayResolver) Class.forName(gatewayStorageClass).newInstance();
} catch (final Exception e) {
//log.error(e,e);
throw new ServletException(e);
}
}
//自定义添加代码,用来读取web配置文件中excludes属性值 ---2
excludePaths = getPropertyFromInitParams(filterConfig, "excludePaths", null);//filterConfig.getInitParameter("excludePaths");
excludePaths = excludePaths.trim();
}
}

public void init() {
super.init();
CommonUtils.assertNotNull(this.casServerLoginUrl, "casServerLoginUrl cannot be null.");
}

//url判断逻辑,这里大家可以根据自己需要来制订规则
private boolean isExclude(String uri){
boolean isInWhiteList = false;
if(excludePaths!=null&& uri!=null){
isInWhiteList = uri.matches(excludePaths);
}
return isInWhiteList;
}



public final void doFilter(final ServletRequest servletRequest, final ServletResponse servletResponse, final FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
final HttpServletResponse response = (HttpServletResponse) servletResponse;
final HttpSession session = request.getSession(false);

final Assertion assertion = session != null ? (Assertion) session.getAttribute(CONST_CAS_ASSERTION) : null;

//hack for 云桥
Map<String, String[]> paramsMap = request.getParameterMap();

if(null != paramsMap ) {
if(paramsMap.containsKey("ticket") && paramsMap.containsKey("operation")) {
/* String token = Arrays.toString(paramsMap.get("ticket"));
request.removeAttribute("ticket");
request.setAttribute("token", token.substring(1, token.length()-1));
filterChain.doFilter(request, response);
return;*/

/*StringBuffer paramsBuff = new StringBuffer();
for(String param: paramsMap.keySet()) {
String newString = Arrays.toString(paramsMap.get(param));
if(param.equals("ticket")) {
paramsBuff.append("&").append("eticket").append("=").append(newString.substring(1, newString.length()-1));
}else {
paramsBuff.append("&").append(param).append("=").append(newString.substring(1, newString.length()-1));
}
}
String port = request.getServerPort() == 80?"":":"+request.getServerPort();
String scheme = request.getScheme();
String host = request.getServerName();
String redirectUrl = String.format("%s://%s%s%s?%s",
scheme,
host,
port,
request.getRequestURI(),
paramsBuff.toString().substring(1)
);
System.out.println("request url:"+redirectUrl);
response.sendRedirect(redirectUrl);
return;*/

Map<String, String[]> extraParams = new HashMap<String, String[]>();
HttpServletRequest wrappedRequest = new PrettyFacesWrappedRequest(request, extraParams);
request.getRequestDispatcher(request.getRequestURI()).forward(wrappedRequest, response);
return;
}
}


// 该判断是自定义的对符合条件的url进行通过处理
if(isExclude(request.getRequestURI())){
filterChain.doFilter(request, response);
return;
}



if (assertion != null) {
filterChain.doFilter(request, response);
return;
}

final String serviceUrl = constructServiceUrl(request, response);
final String ticket = CommonUtils.safeGetParameter(request,getArtifactParameterName());
final boolean wasGatewayed = this.gatewayStorage.hasGatewayedAlready(request, serviceUrl);

if (CommonUtils.isNotBlank(ticket) || wasGatewayed) {
filterChain.doFilter(request, response);
return;
}

final String modifiedServiceUrl;

//log.debug("no ticket and no assertion found");
if (this.gateway) {
//log.debug("setting gateway attribute in session");
modifiedServiceUrl = this.gatewayStorage.storeGatewayInformation(request, serviceUrl);
} else {
modifiedServiceUrl = serviceUrl;
}

/* if (log.isDebugEnabled()) {
log.debug("Constructed service url: " + modifiedServiceUrl);
}*/

final String urlToRedirectTo = CommonUtils.constructRedirectUrl(this.casServerLoginUrl, getServiceParameterName(), modifiedServiceUrl, this.renew, this.gateway);

/* if (log.isDebugEnabled()) {
log.debug("redirecting to \"" + urlToRedirectTo + "\"");
}*/

response.sendRedirect(urlToRedirectTo);
}

public final void setRenew(final boolean renew) {
this.renew = renew;
}

public final void setGateway(final boolean gateway) {
this.gateway = gateway;
}

public final void setCasServerLoginUrl(final String casServerLoginUrl) {
this.casServerLoginUrl = casServerLoginUrl;
}

public final void setGatewayStorage(final GatewayResolver gatewayStorage) {
this.gatewayStorage = gatewayStorage;
}
}

Java改写HttpServletRequest请求参数

为什么会有这个需求呢? 因为被 casAuthenticationFilter坑了

下一篇再说一下AuthenticationFilter的坑,这里主要说修改请求的参数

代码是从网上找的,找不到具体来源了,直接贴代码吧

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
package com.birdzhang.plugin;

import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;


/**
* @author birdzhang
*
*/
public class PrettyFacesWrappedRequest extends HttpServletRequestWrapper
{
private final Map<String, String[]> modifiableParameters;
private Map<String, String[]> allParameters = null;

/**
* Create a new request wrapper that will merge additional parameters into
* the request object without prematurely reading parameters from the
* original request.
*
* @param request
* @param additionalParams
*/
public PrettyFacesWrappedRequest(final HttpServletRequest request,
final Map<String, String[]> additionalParams)
{
super(request);
modifiableParameters = new HashMap<String, String[]>();
modifiableParameters.putAll(additionalParams);
}

@Override
public String getParameter(final String name)
{
String[] strings = getParameterMap().get(name);
if (strings != null)
{
return strings[0];
}
return super.getParameter(name);
}

@Override
public Map<String, String[]> getParameterMap()
{
if (allParameters == null)
{
allParameters = new TreeMap<String, String[]>();
Map<String, String[]> superMap = super.getParameterMap();
// 在这里根据你的需要修改
for (String key : superMap.keySet()) {
if(key.equals("ticket")) {
allParameters.put("token", superMap.get(key));
}else {
allParameters.put(key, superMap.get(key));
}
}
allParameters.putAll(modifiableParameters);
}
//Return an unmodifiable collection because we need to uphold the interface contract.
return Collections.unmodifiableMap(allParameters);
}

@Override
public Enumeration<String> getParameterNames()
{
return Collections.enumeration(getParameterMap().keySet());
}

@Override
public String[] getParameterValues(final String name)
{
return getParameterMap().get(name);
}
}

使用:

1
2
3
4
Map<String, String[]> extraParams = new HashMap<String, String[]>();
HttpServletRequest wrappedRequest = new PrettyFacesWrappedRequest(request, extraParams);
request.getRequestDispatcher(request.getRequestURI()).forward(wrappedRequest, response);
return;

完美的将ticket参数替换为token参数

SailfishOS移植到Redmi 5 Plus的一些记录

移植旗鱼系统到红米5p的过程记录

!!!可以去ホロ 🐺的博客看看更详细的!!!

https://blog.yoitsu.moe/category/sailfish.html

准备工作

搭建环境

Android编译环境(HADK 第4章)

需要先配置一下hadk的环境变量,一共三个文件,可以参考这里: https://github.com/CancroSailors/sailfish-build-environment

然后source ~/.hadk.env一下

1. 安装git等

sudo apt-get install git

2. 同步CM的代码

配置git的用户名跟邮箱,填自己的就行,随便填也行
1
2
git config --global user.name "Your Name"
git config --global user.email "[email protected]"
创建检出代码的目录,所在的盘空间要足够
1
2
3
4
sudo mkdir -p $ANDROID_ROOT
sudo chown -R $USER $ANDROID_ROOT
cd $ANDROID_ROOT
repo init -u git://github.com/mer-hybris/android.git -b hybris-14.1
配置你的设备的仓库,这里是vince
1
2
3
4
5
6
7
8
9
10
11
12
mkdir $ANDROID_ROOT/.repo/local_manifests
cat <<'EOF' >> $ANDROID_ROOT/.repo/local_manifests/vince.xml
<?xml version="1.0" encoding="UTF-8"?>
<manifest>
<project path="device/xiaomi/vince" name="Sailfish-On-Vince/device_xiaomi_vince" revision="cm-14.1" />
<project path="vendor/xiaomi/vince" name="Sailfish-On-Vince/vendor_xiaomi_vince" revision="cm-14.1" />
<project path="kernel/xiaomi/vince" name="Sailfish-On-Vince/kernel_xiaomi_msm8953" revision="cm-14.1" />
<project path="hybris/droid-hal-version-vince" name="Sailfish-On-Vince/droid-hal-version-vince" revision="master" />
<project path="hybris/droid-config-vince" name="Sailfish-On-Vince/droid-config-vince" revision="master" />
<project path="rpm" name="Sailfish-On-Vince/droid-hal-vince" revision="master" />
</manifest>
EOF
同步代码

repo sync --fetch-submodules

如果你要求快,可以用 repo sync --fetch-submodules -c --no-tags --no-clone-bundle,这样只会拉取当前要用的代码,但是不方便后面的修改提交

出错就多同步几次

Mer编译环境

参见官方教程: https://sailfishos.org/wiki/Platform_SDK_Installation

安装Platform SDK

1
2
3
4
5
6
7
8
9
export PLATFORM_SDK_ROOT=/srv/mer
curl -k -O http://releases.sailfishos.org/sdk/installers/latest/Jolla-latest-SailfishOS_Platform_SDK_Chroot-i486.tar.bz2 ;
sudo mkdir -p $PLATFORM_SDK_ROOT/sdks/sfossdk ;
sudo tar --numeric-owner -p -xjf Jolla-latest-SailfishOS_Platform_SDK_Chroot-i486.tar.bz2 -C $PLATFORM_SDK_ROOT/sdks/sfossdk ;
echo "export PLATFORM_SDK_ROOT=$PLATFORM_SDK_ROOT" >> ~/.bashrc
echo 'alias sfossdk=$PLATFORM_SDK_ROOT/sdks/sfossdk/mer-sdk-chroot' >> ~/.bashrc ; exec bash ;
echo 'PS1="PlatformSDK $PS1"' > ~/.mersdk.profile ;
echo '[ -d /etc/bash_completion.d ] && for i in /etc/bash_completion.d/*;do . $i;done' >> ~/.mersdk.profile ;
sfossdk

另开一个终端,输入sfossdk,进入mer下

安装targets,官方教程:https://sailfishos.org/wiki/Platform_SDK_Target_Installation

其实就是执行下面的命令,要下载这两个包,过程有些慢

(2019-06-13更新,修复链接地址为最新的)

1
2
sdk-assistant create xiaomi-vince-latest http://releases.sailfishos.org/sdk/targets/Sailfish_OS-latest-Sailfish_SDK_Tooling-i486.tar.7z
sdk-assistant create xiaomi-vince-armv7hl http://releases.sailfishos.org/sdk/targets/Sailfish_OS-latest-Sailfish_SDK_Target-armv7hl.tar.7z

更新到最新(Update to latest)

1
2
3
sudo ssu re x.y.z.ab
sudo zypper ref
sudo zypper dup

安装打包的工具

1
sudo zypper in android-tools createrepo_c

修改fixup-mountpoints

文件在hybris/hybris-boot/fixup-mountpoints,添加你的设备的,这里是vince。
adb到手机上,输入ls -l /dev/block/platform/*/by-name/, 获取分区信息,或者其他路径的,HADK里面有写

Camera支持

1
2
3
4
5
6
7
8
cd $ANDROID_ROOT/external/droidmedia
echo 'DROIDMEDIA_32 := true' >> env.mk
echo 'FORCE_HAL:=1' >> env.mk

# For video recorder
# https://github.com/sailfishos/droidmedia/pull/11
echo 'MINIMEDIA_AUDIOPOLICYSERVICE_ENABLE:=1' >> env.mk
echo 'AUDIOPOLICYSERVICE_ENABLE:=1' >> env.mk

编译hybris-hal

1
2
3
4
5
cd $ANDROID_ROOT
source build/envsetup.sh
export USE_CCACHE=1
breakfast $DEVICE
make -j8 hybris-hal

期间可能会报错,谷歌搜一下

验证Kernel

1
2
cd $ANDROID_ROOT
hybris/mer-kernel-check/mer_verify_kernel_config ./out/target/product/$DEVICE/obj/KERNEL_OBJ/.config

出现WARNING或者ERROR,将提示的加入到你的defconfig中,我的在kernel/xiaomi/vince/arch/arm64/configs/vince_defconfig
然后执行make hybris-root后重新验证。没有出现ERROR后可以执行make hybris-recovery

打包dhd(HADK 第7章)

再开一个终端(我们这里称终端2),输入sfossdk,进入mer打包环境下

1
2
cd $ANDROID_ROOT
rpm/dhd/helpers/build_packages.sh -d

打包droidmedia与audioflingerglue

> 如果你的机器是32位的话那么下面的命令去掉_32,下面的也一样

在终端1中

1
make -j$(nproc --all) $(external/droidmedia/detect_build_targets.sh $PORT_ARCH $(gettargetarch))

在终端2中

1
2
3
4
5
6
7
8
9
10
11
DROIDMEDIA_VERSION=$(git --git-dir external/droidmedia/.git describe --tags | sed \
-r "s/\-/\+/g")
rpm/dhd/helpers/pack_source_droidmedia-localbuild.sh $DROIDMEDIA_VERSION
mkdir -p hybris/mw/droidmedia-localbuild/rpm
cp rpm/dhd/helpers/droidmedia-localbuild.spec \
hybris/mw/droidmedia-localbuild/rpm/droidmedia.spec
sed -ie "s/0.0.0/$DROIDMEDIA_VERSION/" \
hybris/mw/droidmedia-localbuild/rpm/droidmedia.spec
mv hybris/mw/droidmedia-$DROIDMEDIA_VERSION.tgz hybris/mw/droidmedia-localbuild
rpm/dhd/helpers/build_packages.sh --build=hybris/mw/droidmedia-localbuild

在终端1中

1
make -j$(nproc --all) $(external/audioflingerglue/detect_build_targets.sh $PORT_ARCH $(gettargetarch))

在终端2中

1
2
3
4
5
6
7
8
9
10
AUDIOFLINGERGLUE_VERSION=$(git --git-dir external/audioflingerglue/.git describe --tags | sed \
-r "s/\-/\+/g")
rpm/dhd/helpers/pack_source_audioflingerglue-localbuild.sh $AUDIOFLINGERGLUE_VERSION
mkdir -p hybris/mw/audioflingerglue-localbuild/rpm
cp rpm/dhd/helpers/audioflingerglue-localbuild.spec \
hybris/mw/audioflingerglue-localbuild/rpm/audioflingerglue.spec
sed -ie "s/0.0.0/$AUDIOFLINGERGLUE_VERSION/" \
hybris/mw/audioflingerglue-localbuild/rpm/audioflingerglue.spec
mv hybris/mw/audioflingerglue-$AUDIOFLINGERGLUE_VERSION.tgz hybris/mw/audioflingerglue-localbuild
rpm/dhd/helpers/build_packages.sh --build=hybris/mw/audioflingerglue-localbuild

然后重新打包dhd

1
rpm/dhd/helpers/build_packages.sh --droid-hal

打包其他中间件包(mw)

注意到 https://wiki.merproject.org/wiki/Adaptations/faq-hadk 搜索当前版本需要对应的mw版本号,不一定用最新的

执行 rpm/dhd/helpers/build_packages.sh --mw ,然后选择 all 即可

【进阶】上传到obs打包

将droid-local-repo/vince下 droid-hal-vince/.rpm 跟audioflingerglue.rpm 、 droidmedia*.rpm 上传到obs的droid-hal-vince下

例如这些包:https://build.merproject.org/package/show/nemo:devel:hw:xiaomi:vince/droid-hal-vince

obs打包还需要dhc,dhv等等几个包,此处不详细说明了,可以到 https://github.com/mer-hybris 看其他机型的

打包dhv

也就是 droid-hal-$DEVICE-version

rpm/dhd/helpers/build_packages.sh -v

如果提示 droid-configs 之类的找不到,需要手动安装

1
sb2 -t $VENDOR-$DEVICE-$PORT_ARCH -R -msdk-install zypper -n install droid-config-$DEVICE

Jolla-@RELEASE@-$DEVICE-@[email protected]

~obs打包完之后,将droid-config-vince-kickstart-configuration-0.2.4*.armv7hl.rpm 下载下来,解压获得Jolla-@RELEASE@-$DEVICE-@[email protected]放到$ANDROID_ROOT下面~

1
2
3
4
5
6
7
8
cd $ANDROID_ROOT
HA_REPO="repo --name=adaptation-community-common-$DEVICE-@RELEASE@"
HA_DEV="repo --name=adaptation-community-$DEVICE-@RELEASE@"
KS="Jolla-@RELEASE@-$DEVICE-@[email protected]"
sed \
"/$HA_REPO/i$HA_DEV --baseurl=file:\/\/$ANDROID_ROOT\/droid-local-repo\/$DEVICE" \
$ANDROID_ROOT/hybris/droid-configs/installroot/usr/share/kickstarts/$KS \
> $KS

镜像制作

1
2
3
4
5
6
7
8
9
10
cd $ANDROID_ROOT
RELEASE=2.1.4.14
EXTRA_NAME=-alpha1
hybris/droid-configs/droid-configs-device/helpers/process_patterns.sh
sudo mic create fs --arch=$PORT_ARCH \
--tokenmap=ARCH:$PORT_ARCH,RELEASE:$RELEASE,EXTRA_NAME:$EXTRA_NAME \
--record-pkgs=name,url \
--outdir=sfe-$DEVICE-$RELEASE$EXTRA_NAME \
--pack-to=sfe-$DEVICE-$RELEASE$EXTRA_NAME.tar.bz2 \
$ANDROID_ROOT/Jolla-@RELEASE@-$DEVICE-@[email protected]

在当前目录下会有一个 sfe-$DEVICE-$RELEASE$EXTRA_NAME目录,下面的 sailfishos-$DEVICE-$RELEASE$EXTRA_NAME.zip就是要使用的刷机包

刷机

刷入 lineageos-14.1的包,刷入上面的sailfishos-$DEVICE-$RELEASE$EXTRA_NAME.zip的包,重启

可以参考 https://wiki.merproject.org/wiki/Adaptations/libhybris/Install_SailfishOS_for_Vince

调试

欢迎加入 IRC #jolla-cn #sailfishos-porters (只限英文交流) 或 https://t.me/joinchat/GTqoL1HLIYXWNf-JeijTAg 讨论

相关文档

Tomcat信任https网站

在使用微信发送告警的时候出现无法使用https网址的问题,解决方式如下

导出微信的证书

openssl s_client -connect qyapi.weixin.qq.com:443 2>&1 |sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > weixin.crt

使用SSLPoke.class确认是否还有上层证书,如果存在也导入

java -Djavax.net.debug=ssl SSLPoke qyapi.weixin.qq.com 443

看到有类似下面的则表明有上层证书:

1
2
3
4
5
6
7
8
9
AuthorityInfoAccess [
[
accessMethod: ocsp
accessLocation: URIName: http://gn.symcd.com
,
accessMethod: caIssuers
accessLocation: URIName: http://gn.symcb.com/gn.crt
]
]

http://gn.symcb.com/gn.crt下载下来,并导入
sudo keytool -import -v -trustcacerts -alias gnsymcb -keystore $JAVA_HOME/jre/lib/security/jssecacerts -file gn.crt

导入到jdk中

sudo keytool -import -v -trustcacerts -alias qyapiweixin -keystore $JAVA_HOME/jre/lib/security/jssecacerts -file weixin.crt

更新证书

需要先删除再导入新的

1
2
sudo keytool -delete -keystore $JAVA_HOME/jre/lib/security/cacerts -alias qyapiweixin    
sudo keytool -import -v -trustcacerts -alias qyapiweixin -keystore $JAVA_HOME/jre/lib/security/cacerts -file weixin.crt

指定tomcat使用证书(非必须)

1
2
vim /usr/local/apache-tomcat-8.5.6/bin/setenv.sh
JAVA_OPTS="-Djava.awt.headless=true -Dfile.encoding=UTF-8 -server -Xms2048m -Xmx2048m -XX:NewSize=512m -XX:MaxNewSize=512m -XX:PermSize=512m -XX:MaxPermSize=512m -XX:+DisableExplicitGC -Djavax.net.ssl.trustStore=/usr/local/java/jdk8/jre/lib/security/jssecacerts"

或者在 /usr/local/apache-tomcat-8.5.6/conf/server.xml的ssl配置部分指定keystore,如下

1
2
3
4
5
6
7
<Connector port="443" protocol="org.apache.coyote.http11.Http11Protocol"
maxThreads="150" SSLEnabled="true" scheme="https" secure="true"
clientAuth="false" sslProtocol="TLS"
URIEncoding="utf-8"
keystoreFile="/usr/local/java/jdk7/jre/lib/security/jssecacerts"
keystorePass="yourpassword"
/>

opensips安装使用整理

部分内容来自 https://github.com/Ci-Jie/OpenSIPS ,其他的很多教程都是坑!!!

安装opensips

  • CentOS release 6.9 (Final) (内核 4.10.4)
  • MySQL 5.6
  • Opensips 2.1

安装依赖库等

sudo yum -y install mysql mysql-server mysql-devel git make bison libdbi-dbd-mysql (具体还有什么忘记了,不能重现当时安装的了,后面缺什么再装吧)

检出代码

cd ~/ && git clone https://github.com/OpenSIPS/opensips.git -b 2.1 opensips_2_1

修改配置开启mysql支持

vim ~/opensips_2_1/Makefile.conf.tmplate ,移除exclude_modules中db_mysql

安装opensips

1
2
3
cd ~/opensips_2_1
sudo make all
sudo make install

安裝完后,修改部分opensipsctlrc文件,如下:

sudo vim /usr/local/etc/opensips/opensipsctlrc

将DB开头的修改为自己的mysql连接情况(事先创建好opensips用户,或者偷懒用root,但是一定要注意安全),SIP_DOMAIN修改为主机ip

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
## your SIP domain
SIP_DOMAIN=yourip
## chrooted directory
# $CHROOT_DIR="/path/to/chrooted/directory"
## database type: MYSQL, PGSQL, ORACLE, DB_BERKELEY, or DBTEXT,
## by default none is loaded
# If you want to setup a database with opensipsdbctl, you must at least specify
# this parameter.
DBENGINE=MYSQL
## database host
DBHOST=localhost
## database name (for ORACLE this is TNS name)
DBNAME=opensips
# database path used by dbtext or db_berkeley
DB_PATH="/usr/local/etc/opensips/dbtext"
## database read/write user
DBRWUSER=opensips
## password for database read/write user
DBRWPW="opensipsrw"
## database super user (for ORACLE this is 'scheme-creator' user)
DBROOTUSER="root"
# user name column
USERCOL="username"

初始化mysql数据库表

sudo /usr/local/sbin/opensipsdbctl create,会提示输入root密码,然后会出现下面的提示

1
2
3
4
5
6
7
8
9
10
MySQL password for root: 
INFO: test server charset
INFO: creating database opensips ...
INFO: Core OpenSIPS tables succesfully created.
Install presence related tables? (y/n): y
INFO: creating presence tables into opensips ...
INFO: Presence tables succesfully created.
Install tables for imc cpl siptrace domainpolicy carrierroute userblacklist? (y/n): y
INFO: creating extra tables into opensips ...
INFO: Extra tables succesfully created.

如果出现 ERROR: database engine not specified, please setup one in the config script,看看是否有执行权限

自定义监听端口及协议

可以通过修改/usr/local/etc/opensips/opensips.cfg文件,修改listen=,注意这里的ip要用外网ip,udp改为tcp可以使用keepalive,让手机长时间在线。我的配置如下:

1
2
3
4
5
6
7
8
9
10
advertised_address=myip
alias=myip
...
listen=tcp:myip:5060 # CUSTOMIZE ME
tcp_connection_lifetime=3600
tcp_connect_timeout=5000
tcp_keepalive=1
tcp_keepcount=30
tcp_keepidle=60
tcp_keepinterval = 60

新增domain到数据库

这个domain就是上面填写的ip地址,创建用户的时候也要用到。

用root连接mysql,选择opensips库,插入一条记录INSERT INTO opensips.domain(domain) VALUES('your ip');(注意修改为自己的ip)

opensips操作

启动 /usr/local/sbin/opensipsctl start

停止 /usr/local/sbin/opensipsctl stop

重启 /usr/local/sbin/opensipsctl restart

创建用户

/usr/local/sbin/opensipsctl add user@myourip password

这里的user就是号码,尽量用数字(键盘上没有字母啊)

客户端

  • PC
    • Zoiper
    • Linphone
  • Ios
    • Zoiper

其他未测试,可以到 http://www.voip-info.org/wiki/view/Open+Source+VOIP+Software 查找

ios端配置(尽量后台运行)

如图所示:

微信图片_20180129170327.jpg
微信图片_20180129170352.jpg
微信图片_20180129170357.jpg

自动清理k8s中的容器、卷、镜像

镜像源码 https://github.com/meltwater/docker-cleanup

注意:这个镜像会将所有已经退出的容器、未使用的镜像和data-only的容器,除非你将他们加到保存的变量中。注意正确配置docker api的版本,以免删除所有的镜像。小心挂载 /var/lib/docker,因为如果挂载后没有使用的话,也会被当作未使用的卷删掉。

支持的变量

  • CLEAN_PERIOD=1800 - Interval in seconds to sleep after completing a cleaning run. Defaults to 1800 seconds = 30 minutes.
  • DELAY_TIME=1800 - Seconds to wait before removing exited containers and unused images. Defaults to 1800 seconds = 30 minutes.
  • KEEP_IMAGES - List of images to avoid cleaning, e.g. “ubuntu:trusty, ubuntu:latest”. Defaults to clean all unused images.
  • KEEP_CONTAINERS - List of images for exited or dead containers to avoid cleaning, e.g. “ubuntu:trusty, ubuntu:latest”.
  • KEEP_CONTAINERS_NAMED - List of names for exited or dead containers to avoid cleaning, e.g. “my-container1, persistent-data”.
  • LOOP - Add the ability to do non-looped cleanups, run it once and exit. Options are true, false. Defaults to true to run it forever in loops.
  • DEBUG - Set to 1 to enable more debugging output on pattern matches
  • DOCKER_API_VERSION - The docker API version to use. This defaults to 1.20, but you can override it here in case the docker version on your host differs from the one that is installed in this container. You can find - this on your host system by running docker version --format '{{.Client.APIVersion}}'.

对于即使已经不运行了也不想清理的镜像,使用KEEP_IMAGES变量处理,此处我们添写:

vmware/harbor-*:*,*calico:*,*registry:*,*kubernetes-dashboard-amd64:*,*nginx-ingress-controller:*,*cvallance/mongo-k8s-sidecar:*

docker-cleanup-daemonset.yaml 配置如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
apiVersion: extensions/v1beta1
kind: DaemonSet
metadata:
labels:
name: clean-up
name: clean-up
namespace: kube-system
spec:
updateStrategy:
type: "RollingUpdate"
rollingUpdate:
maxUnavailable: 1
template:
metadata:
labels:
app: clean-up
spec:
tolerations:
- key: "LB"
operator: "Exists"
effect: "NoExecute"
volumes:
- name: docker-sock
hostPath:
path: /var/run/docker.sock
- name: docker-directory
hostPath:
path: /data/kubernetes/docker
containers:
- image: meltwater/docker-cleanup:latest
name: clean-up
env:
- name: CLEAN_PERIOD
value: "1800"
- name: DELAY_TIME
value: "60"
- name: DOCKER_API_VERSION
value: "1.29"
- name: KEEP_IMAGES
value: "vmware/harbor-*:*,*calico:*,*registry:*,*kubernetes-dashboard-amd64:*,*nginx-ingress-controller:*,*cvallance/mongo-k8s-sidecar:*"
volumeMounts:
- mountPath: /var/run/docker.sock
name: docker-sock
readOnly: false
- mountPath: /var/lib/docker
name: docker-directory
readOnly: false