<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.4.1">Jekyll</generator><link href="https://scfhao.cn/feed.xml" rel="self" type="application/atom+xml" /><link href="https://scfhao.cn/" rel="alternate" type="text/html" /><updated>2026-05-11T06:58:40+00:00</updated><id>https://scfhao.cn/feed.xml</id><title type="html">scfhao 的个人博客</title><subtitle>生活与修行</subtitle><entry><title type="html">2025 年终总结</title><link href="https://scfhao.cn/2026/01/10/2025-summary.html" rel="alternate" type="text/html" title="2025 年终总结" /><published>2026-01-10T01:05:19+00:00</published><updated>2026-01-10T01:05:19+00:00</updated><id>https://scfhao.cn/2026/01/10/2025-summary</id><content type="html" xml:base="https://scfhao.cn/2026/01/10/2025-summary.html"><![CDATA[<p>这个博客虽然更新的频率极低，但我其实一直没有放弃。</p>

<p>年龄越大，日子就会越发的千篇一律，今天是昨天的重复，明天又是今天的重复。所以如果一直是这种重复，记录就失去了它的意义。2025 年对于我来说比较特别，所以今年非常例外的想写一篇“年终总结”。</p>

<h2 id="骑行">骑行</h2>

<p>造成 2025 年的改变的原因是 4 月初买了一辆二手山地自行车。</p>

<p>之前一直是骑电动车上下班，那辆电动车是 2019 年 6 月份买的，6 年的风里来雨里去已经使它变得满目疮痍了。3月份的时候一周之内就出现了两次上班路上出问题，那种情况就只能打车。</p>

<p>在这 6 年的骑电动车生涯中，发生过很多次上班要走的时候发现后胎没气，也发生过很多次明明晚上充了电，但是刚骑了没多远就发现电量不足的情况。我这几年正在做的事情就是尽量减少这种会让我产生焦虑情绪的事情，包括在 2024 年停了花呗和一张需要刷 6 次才免次年年费的信用卡（我现在只保留了一张终身免年费的信用卡用于日常消费，各大 App 的先用后付全都没开通）。关闭这些超前消费账户不是因为管不住自己的消费欲，而是觉得每个月要在多个 App 还钱的操作很累。</p>

<p>同样的，骑电动车也会给我带来焦虑，所以 4 月初就在家附近的一个自行车店买了一辆出厂已经有 13 年的山地自行车。从那开始就风雨无阻的每天骑自行车上下班，单程 13 公里，需要 43 分钟左右。</p>

<p>像是打开了新世界的大门，骑自行车可比骑电动车好太多了。我现在觉得自行车就是陆地上最完美的交通工具！</p>

<p>从骑自行车开始，我整个人都变了，像是换了个人：</p>

<ul>
  <li>更快乐（起码在路上是快乐的）</li>
  <li>更健康（伴随我很多年的脂肪肝几个月就骑没了，血压也降了）</li>
  <li>体型也更好（半年瘦了十几斤，而且到现在也还在一直降）</li>
  <li>更自信（从内而外的更自信了）</li>
</ul>

<p>另外，买了自行车后，随机就买了个自行车上的儿童座椅。夏天的傍晚、周末的午后都很多次带儿子去骑，增加了很多父子相处时间，这都是让我快乐的事。在感受到骑自行车的好处后，给老婆也买了一辆山地车，秋天的时候她还和我一起出去骑过几次，冬天就很少了。我觉得，持续去做可以让自己快乐的事，减少会让自己焦虑的事，人就能越来越好。</p>

<h2 id="ai">AI</h2>

<p>不得不提 AI，这一年我也是尽量让自己拥抱 Vibe Coding，AI 帮我写了很多代码，这让我有空可以学习新技术。</p>

<p>但是在学习新技术的时候，又常常会疑惑，AI 都这么强了，学这些还有用吗？看不清未来的发展方向，不知道未来会怎么样，常常陷入自我怀疑。</p>

<p>不过怎么说呢，AI 不仅仅会颠覆 IT 行业，还会颠覆传统行业，只不过 IT 行业离变革的中心最近。举个例子，我的自行车骑的时候嘎吱嘎吱响，正常需要找个经验丰富的自行车维修技师帮我看一下才能定位到原因，但是现在只要把状况描述清楚，AI 就能直接给我定位到问题了，所以传统行业肯定也会受到冲击，只是目前无法预估这种冲击带来的影响有多大。</p>

<p>AI 同时也提供了极大的便利性。比如这几天我在给我的旧 MacBook Pro 安装 Arch Linux 系统，安装的过程中遇到的问题都被 DeepSeek 手把手教我处理了。我现在唯一比 AI 强的地方在于，我能实际的执行操作（比如安装系统），而 AI 则是理论知识非常丰富，但无法亲自上阵，这可能就是我们的机会所在。</p>

<h2 id="结尾">结尾</h2>

<p>其他的再没什么可讲了</p>]]></content><author><name></name></author><summary type="html"><![CDATA[这个博客虽然更新的频率极低，但我其实一直没有放弃。]]></summary></entry><entry><title type="html">iOS 中常见浏览器的 UserAgent</title><link href="https://scfhao.cn/2024/06/14/useragents-in-ios.html" rel="alternate" type="text/html" title="iOS 中常见浏览器的 UserAgent" /><published>2024-06-14T00:16:44+00:00</published><updated>2024-06-14T00:16:44+00:00</updated><id>https://scfhao.cn/2024/06/14/useragents-in-ios</id><content type="html" xml:base="https://scfhao.cn/2024/06/14/useragents-in-ios.html"><![CDATA[<p>统计了一下在 iOS 上几个常用的浏览器（Safari、360、QQ、UC、Firefox、Edge、Chrome）的 UserAgent，可能会随着时间推移而失真，仅供参考。</p>

<p>iPad Safari：Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.6.6 Safari/605.1.15</p>

<p>iPhone Safari：Mozilla/5.0 (iPhone; CPU iPhone OS 17_5_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.5 Mobile/15E148 Safari/604.1</p>

<p>360浏览器：Mozilla/5.0 (iPhone; CPU iPhone OS 17_5_1 like Mac OS X) AppleWebKit/618.2.12 (KHTML, like Gecko) Mobile/21F90 Version/13.0 Safari/604.1</p>

<p>QQ浏览器：Mozilla/5.0 (iPhone; CPU iPhone OS 17_5_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 MQQBrowser/15.1.5 Mobile/15E148 Safari/604.1 QBWebViewUA/2 QBWebViewType/1 WKType/1</p>

<p>UC浏览器：Mozilla/5.0 (iPhone; CPU iPhone OS 17_5_1 like Mac OS X; zh-CN) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/21F90 UCBrowser/16.5.4.2337 Mobile AliApp(TUnionSDK/0.1.20.4)</p>

<p>Firefox：Mozilla/5.0 (iPhone; CPU iPhone OS 17_5_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) FxiOS/127.0 Mobile/15E148 Safari/605.1.15</p>

<p>Edge：Mozilla/5.0 (iPhone; CPU iPhone OS 17_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) EdgiOS/125.0.2535.87 Version/17.0 Mobile/15E148 Safari/604.1</p>

<p>Chrome：Mozilla/5.0 (iPhone; CPU iPhone OS 17_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/126.0.6478.35 Mobile/15E148 Safari/604.1</p>]]></content><author><name></name></author><summary type="html"><![CDATA[统计了一下在 iOS 上几个常用的浏览器（Safari、360、QQ、UC、Firefox、Edge、Chrome）的 UserAgent，可能会随着时间推移而失真，仅供参考。]]></summary></entry><entry><title type="html">脚本监控苹果企业开发者账号配置文件有效期</title><link href="https://scfhao.cn/2021/07/17/monitor-profiles.html" rel="alternate" type="text/html" title="脚本监控苹果企业开发者账号配置文件有效期" /><published>2021-07-17T06:09:13+00:00</published><updated>2021-07-17T06:09:13+00:00</updated><id>https://scfhao.cn/2021/07/17/monitor-profiles</id><content type="html" xml:base="https://scfhao.cn/2021/07/17/monitor-profiles.html"><![CDATA[<p>上午写了个脚本，想着很久都没写博客了，所以就有了这篇博客。其实我也不想水博客，只是我现在在除 iOS 外的其他领域也是初学者状态，就觉得在别的领域到现在还没能找到一点可以写的内容。（当我想到这里的时候，我希望这是我写的最后一篇和 iOS 开发有关的内容了），好了，开始正文。。。</p>

<p>现在苹果企业开发者账号那么难申请，估计已经没多少人还在用企业账号了。不过话说回来，确实好多公司都没必要使用企业开发者账号，毕竟企业开发者账号的初衷是用于企业内部 App 的发布，而有在内部发行专门的 App 的企业又有几个呢？</p>

<p>不过在一些大型劳动密集型的企业里，苹果企业账号就有它的用武之地了。假如你在这样的企业，企业旗下有多个部门在做移动开发，这些部门共用一个开发者账号，那么这个企业账号下的应用的数量就比较多了。</p>

<p>这里需要先介绍一下企业账号的优缺点：</p>

<ul>
  <li>优点：
    <ol>
      <li>不需要 App Store 审核。因为企业账号打包的 App 只能发布于公司内部网站，所以不存在 App Store 审核的问题，发布流程大大简化。不过这也导致一些人利用这种机制来钻空子导致企业账号滥用，以至于现在基本上申请不到新的企业账号。</li>
      <li>没了，没了，剩下的都是缺点。</li>
    </ol>
  </li>
  <li>缺点：
    <ol>
      <li>发布证书过期后，已安装的 App 就打不开了。</li>
      <li>配置文件（provisioning profile）文件过期后，已安装的 App 也打不开了。</li>
      <li>没办法上 App Store，这样也就没办法像 App Store 里的应用可以自动更新。</li>
      <li>发布时服务器需要部署 TLS。</li>
      <li>用户安装 App 后，需要在设置里“信任开发者”，才能使用 App。</li>
    </ol>
  </li>
</ul>

<blockquote>
  <p>这里要说明的是，企业应用也可以通过自己搭建 MDM 服务器的方式来实现自动更新应用，但到目前为止，我还没见过投入 MDM 的公司。
如果你是通过搜索引擎来到这里，如果贵公司同样申请不下企业开发者账号，本人建议了解一下<a href="https://www.apple.com.cn/business/">Apple 商务</a>，本人认为 Apple 商务是企业开发者账号的替代品。</p>
</blockquote>

<hr />

<p>（接上文），可以看到，企业账号发布的应用一旦配置文件过期，已安装的 App 就打不开了，而这个配置文件的有效期只有短短的一年。如果我们没有 MDM，那唯一能做的就是在配置文件过期前让用户更新 App，通过更新使用新的配置文件打包的 App 来更新配置文件。</p>

<p>如果公司的企业开发者账号只有个别应用，还可以在脑子里记住过期时间，但如果开发的应用多了，那么就需要用工具来提醒自己了，当然最简单的方式就是在手机上设置一个脑子，应用快过期的时候来提醒你。</p>

<p>我最初想到的也是设置闹钟，但是我迟迟没有动手。仔细想想，设置/维护十几二十个闹钟也是一项大工程，能写几行代码就搞定的事，为什么要干成体力活呢？</p>

<p>于是我就画一两个个小时写了一个脚本，虽然可能我设置闹钟反而用不了这么长时间。。。如果这个脚本其他人也会用到，那么我这一两个小时就花的值了，虽然这种需求实在太少了，最起码我在别的公司没遇到过。其实这个脚本的内容很简单，就是遍历自己电脑上的配置文件，并查看其有效期，根据有效期的剩余时间来判断是否该提醒自己了。另外，这个脚本可以再改进一下，先是配置开机自动执行这个脚本，然后提醒方式改为弹窗提醒。</p>

<pre><code class="language-Bash">#!/bin/bash
##############################################################
#	Author: scfhao
# 	Desciption: 检查本地 Provisioning Profile 文件的有效期
# 	虽然没什么用，但还是贴出来了
##############################################################

# 配置 PlistBuddy 环境变量
PATH=$PATH:/usr/libexec
# Profile 中的日期是英文格式，将 LANG 切换为英文才可正常解析到日期
LANG="en_US.UTF-8"
# 定义提醒时间限制 31 天
((limit=31*24*60*60))

#for file in ~/Library/MobileDevice/Provisioning\ Profiles/*
for file in ~/Library/MobileDevice/MonitorProfiles/*
do
	if [[ "${file##*.}" = "mobileprovision" ]]; then
		expirationDate=`PlistBuddy -c "Print ExpirationDate" /dev/stdin &lt;&lt;&lt; $(security cms -D -i "$file")`
		# 获取过期时间的时间戳（秒）
		expirationTs=`date -j -f "%a %b %d %T %Z %Y" "$expirationDate" +%s`
		# 获取当前时间（秒）
		currentTs=`date +%s`
		# 计算剩余时间
		((remain=$expirationTs-$currentTs))
		# 判断剩余时间是否小于要提醒自己的时间
		if [[ $expirationTs-$currentTs -lt $limit ]]; then
			appID=`PlistBuddy -c "Print Entitlements:application-identifier" /dev/stdin &lt;&lt;&lt; $(security cms -D -i "$file")`
			echo "配置文件 $appID 即将到期，请及时更新！"
		fi
	fi
done
</code></pre>]]></content><author><name></name></author><summary type="html"><![CDATA[上午写了个脚本，想着很久都没写博客了，所以就有了这篇博客。其实我也不想水博客，只是我现在在除 iOS 外的其他领域也是初学者状态，就觉得在别的领域到现在还没能找到一点可以写的内容。（当我想到这里的时候，我希望这是我写的最后一篇和 iOS 开发有关的内容了），好了，开始正文。。。]]></summary></entry><entry><title type="html">使用 GitHub Action 定时提醒自己基金涨跌</title><link href="https://scfhao.cn/2021/04/17/use-github-action-reminder-fund.html" rel="alternate" type="text/html" title="使用 GitHub Action 定时提醒自己基金涨跌" /><published>2021-04-17T05:50:57+00:00</published><updated>2021-04-17T05:50:57+00:00</updated><id>https://scfhao.cn/2021/04/17/use-github-action-reminder-fund</id><content type="html" xml:base="https://scfhao.cn/2021/04/17/use-github-action-reminder-fund.html"><![CDATA[<p>我在 2018 年的时候第一次买基金，但那年基本上是一直买一直跌，到了 2019 年，买的几个指数基金回本后就清仓了，结果 2019 年就涨了好多，只是和我没关系了。</p>

<p>今年再一次决定买点基金作为长期投资，毕竟还算新手，使用的最简单的策略，觉得当天的价位较低时买入一点，大涨就不管了。但是上班时经常忘记去看盘，想起来的时候就过了下午 3 点了，前几天突然想起来可以用 GitHub Action 来每天定时执行比价，也省的自己投入精力就看涨跌，于是就花了几个小时，做了这个服务。</p>

<p>GitHub Action 是 GitHub 于 2018 年推出的持续集成服务，之前读过阮一峰老师的一篇<a href="https://www.ruanyifeng.com/blog/2019/12/github_actions.html">GitHub Actions 教程：定时发送天气邮件</a>，当时觉得邮件通知还是不够及时，而且我那段时间也没在买基金，所以就没往这方面想。</p>

<h2 id="bark">Bark</h2>

<p>对于我来说，更及时的通知就是手机系统的推送了，但一方面我没有自己的苹果开发者账号，另一方面开发一个 App 用于接收通知也需要浪费一些时间。前段时间就正好知道了一个App，叫 Bark，可以在 App Store 下载。这个 App 只有一个功能，就是接收并保存推送通知，而我要做的只是调用一个 HTTP 请求。</p>

<h2 id="shell">Shell</h2>

<p>有了 GitHub Action 和 Bark，接下来就是写点代码了，先看一下我的目录结构：</p>

<pre><code class="language-Bash">.github/workflows/judgment.yml
jedgment.sh
001549.json
001548.json
001593.json
</code></pre>

<p>下面的这 3 个 json 文件代表了三个基金，里面定义了各自基金的基本信息（基金名称、基金代码、持仓成本价）。<code class="language-plaintext highlighter-rouge">.github/workflows/judgment.yml</code>是 GitHub Action 的配置文件，这个文件中的定义了任务的执行时机及执行步骤。其中一个步骤就是执行<code class="language-plaintext highlighter-rouge">jedgment.sh</code>，这个脚本文件是唯一的一个代码文件，里面的代码也很简单，会依次读取下面的 json 文件中的基金信息，然后将自己的持仓成本价和实时的净值估算进行比较，得出购买结论后，调用 Bark 的接口像自己的手机发送推送。</p>

<p>涉及到的代码很简单，可以在<a href="https://github.com/scfhao/FundReminder">GitHub</a>查看完整代码。</p>]]></content><author><name></name></author><summary type="html"><![CDATA[我在 2018 年的时候第一次买基金，但那年基本上是一直买一直跌，到了 2019 年，买的几个指数基金回本后就清仓了，结果 2019 年就涨了好多，只是和我没关系了。]]></summary></entry><entry><title type="html">我的2020</title><link href="https://scfhao.cn/2021/02/07/my-2020.html" rel="alternate" type="text/html" title="我的2020" /><published>2021-02-07T07:05:18+00:00</published><updated>2021-02-07T07:05:18+00:00</updated><id>https://scfhao.cn/2021/02/07/my-2020</id><content type="html" xml:base="https://scfhao.cn/2021/02/07/my-2020.html"><![CDATA[<p>今天是我牛年春节假期的第二天，虽然现在已经 2021 年 2 月份了，但我还是觉得有必要记录一下过去的 2020 年。</p>

<h2 id="技术方面">技术方面</h2>

<p>还是从技术方面开始吧！</p>

<p>这一整年写 iOS 代码的时间没超过一个月，只做了一个很初级的采集员工照片的 App 用于智慧园区的人脸识别。其他时间都在做 Java Web，这和我的个人规划还是相当重合的，从 2013 年以来一直在 iOS 领域，虽然中间自己自学过很多其他领域的东西，但都没在真实项目中实践，可以说一直在 iOS 给我的舒适圈内。现在已经 30+ 的年龄，虽然我一直对本国程序员的 35 魔咒不屑一顾，但奈何环境如此；另外，从自身来讲，也有对掌握其他技术，尤其是 Web 技术的迫切需求，所以未来我的重心将会偏向于服务端。</p>

<h3 id="数据库技术">数据库技术</h3>

<p>在之前做 iOS 的时候，对数据库的使用可以说是浅尝辄止，基本上大学里教的《数据库概论》前几章的知识就够用了。而做 Web 时，每天都会与数据库打交道。正好遇到公司的数据库要从 Oracle 切换至 PostgreSQL，前半年参与了很多项目的 Oracle 到 PostgreSQL 迁移工作，刚开始是手工把 Oracle 数据库中的对象翻译成 PostgreSQL 中的数据库对象，后来自己在 Linux 虚拟机中部署了 ora2pg 后，工作量就大大的下降了。在这期间基本上同时把 Oracle 和 PostgreSQL 两种数据库都熟悉了，以前都没听过存储过程的人现在也已经写了很多存储过程了[比个耶]。</p>

<h3 id="java-web">Java Web</h3>

<p>因为公司有很多比较老的系统，所以各种架构的项目都有。有直接在 JSP 中写 Java 代码的项目，有 Struts 为 MVC 框架的，也有用 Spring MVC 框架的，个人觉得各种架构都了解一下也没什么坏处，就像是看了一遍 Java Web 架构发展史一样。虽然缺点也很明显：学习进度明显很慢。</p>

<h3 id="其他">其他</h3>

<p>目前 Go 语言作为本人业余项目使用的语言，也许当服务器端 Swift 发展的比较成熟的时候，我会再次切换回 Swift。其实在在学习过这么多语言后，切换一门语言是很容易的事，万变不离其宗。</p>

<p>前端技术也是本人要学习的一个方面，目前在公司做前端页面的时候还是用的 EasyUI，所以现在本人前端能力很弱，计划学一下 Vue 框架。</p>

<p>Nginx、Docker 现在也在使用。</p>

<h2 id="做饭">做饭</h2>

<p>如果说有什么本领是永远不会过时的，那应该就是做饭吧。虽然机器人也可以做饭，但并不一定每时每刻都有一个会做饭的机器人在你身边。学会做饭，在这个世界上生存的难度会更小一点。妈妈再也不用担心我会被饿死了：）</p>

<p>馒头：馒头是中国北方最主要的主食之一吧，年初在家办公的那段时间，很容易的就学会了把面粉变成馒头的技术。而且味道也比外面卖的更好一点。基本上这一整年，早餐都是在吃自己做的馒头。今年我们两个人一共消耗了三四袋 25Kg 的面粉。</p>

<p>炒菜：今年除了夏天，其他时间的午饭基本上都是从家里带的。每天吃从家里带的饭，会比去食堂吃饭更多一份幸福感，缺点就是很浪费时间，毕竟上班已经很累了，下了班还要准备第二天的午饭。在这期间，在老婆这位炒菜高手的指点下慢慢的就学会了炒菜，现在醋溜土豆丝、西红柿鸡蛋这两道最基本的菜已经完全不在话下了。</p>

<h2 id="其他-1">其他</h2>

<p>疫情在家办公的时候，看了几本东野圭吾的小说，里面最长的是《白夜行》。现在正在看《西游记》，解开很多当初看电视剧时留下的疑惑。</p>

<p>今年生活给我最大的教训就是借给别人钱的时候就要做好别人不还的准备，这年头借钱不还的人太多了，真是凭本事借钱。如果别人不还对自己的生活没有影响，那么这钱就可以借，如果对自己的生活有影响就不能借，即使是关系特别好的朋友。</p>]]></content><author><name></name></author><summary type="html"><![CDATA[今天是我牛年春节假期的第二天，虽然现在已经 2021 年 2 月份了，但我还是觉得有必要记录一下过去的 2020 年。]]></summary></entry><entry><title type="html">通过 Nginx 反向代理访问荣耀路由器 Web 管理页面</title><link href="https://scfhao.cn/2020/09/24/access-web-admin-for-huawei-router.html" rel="alternate" type="text/html" title="通过 Nginx 反向代理访问荣耀路由器 Web 管理页面" /><published>2020-09-24T08:18:20+00:00</published><updated>2020-09-24T08:18:20+00:00</updated><id>https://scfhao.cn/2020/09/24/access-web-admin-for-huawei-router</id><content type="html" xml:base="https://scfhao.cn/2020/09/24/access-web-admin-for-huawei-router.html"><![CDATA[<p>这是<a href="https://club.huawei.com/thread-20336697-1-1.html">需求</a>，像帖子里说的那样：“手机app管理功能实在有限，希望能从外网web登陆后进行直接管理”。怕有人不明白这个意思，这里在贴一个正常路由器厂家提供的<a href="https://service.tp-link.com.cn/detail_article_2032.html">教程</a>。</p>

<p>因为华为荣耀路由器的 Web 管理页面目前没办法在公网访问，只能在内网访问。所以如果实在要从公网访问的话，就需要在内网有“人”把公网请求转发一下变成内网请求，这就是所谓的“反向代理”。</p>

<p>这个方案中的一个重要环节就是这个中间人，如果家里有 htpc、nas、树莓派、群晖，或者一台一直开机的电脑，那这件事就好办了。如果家里没有这些硬件设施，可以购买一台二手的玩客云，成本不到 50 元，参考<a href="/2020/02/23/deploy-server-at-home.html">低成本在家部署一个Server</a>。</p>

<p>以我的使用场景举例，荣耀路由器的 IP 为 192.168.3.1，假设另一台内网主机电脑(下面缩写为主机)的 IP 为 192.168.3.2。配置反向代理的步骤如下：</p>

<ol>
  <li>安装 Nginx。</li>
</ol>

<p>Nginx 是用处非常广泛且非常流行的一个服务器软件，而且非常擅长于做反向代理。根据自己的主机的操作系统，安装自己操作系统版本的 Nginx 即可。Nginx 可以在<a href="https://nginx.org/en/download.html">这里</a>下载 Nginx，安装好 Nginx 后，启动 Nginx。</p>

<p>这一步做完后，在局域网中其他设备的浏览器中输入主机的 IP 地址 192.168.3.2，可以看到 Nginx 的默认页面。</p>

<ol>
  <li>配置路由器。</li>
</ol>

<p>默认情况下，从公网是无法访问到家里局域网中的一台主机的。公网的请求可以通过路由器的公网 IP 到达路由器，如果需要转发到内网，可以通过端口转发把具体的请求转发给主机上的 Nginx。</p>

<p>在内网打开路由器的管理地址<code class="language-plaintext highlighter-rouge">192.168.3.1</code>，输入管理密码，进入到路由器管理页面点击“更多功能-安全设置-NAT服务-端口映射”，端口映射功能可以把路由器的某个端口映射到局域网中一台主机的某个端口上。Nginx 默认的端口是 80，我们这里可以设置把路由器的某个非常用端口比如 369 映射到主机 192.168.3.2 的 80 端口，这样当访问路由器公网 IP 的 369 端口时，就等于访问局域网中主机的 80 端口，即 Nginx 服务监听的端口。</p>

<blockquote>
  <p>上面不一定非用 369 端口，可以自己随便定一个端口，比如自己的幸运数字，但是切记不能用 80、8080 这种常用端口，因为运营商会封掉个人宽带的常用端口，这样当在公网访问这些常用端口时就访问不到了。当然内网 Nginx 的 80 端口可以认为是固定的。</p>
</blockquote>

<p>在一步做完后，在任意一个已接入互联网的设备的浏览器里输入“你的路由器的公网 IP:你定的端口”后，都能看到 Nginx 的默认页面。</p>

<ol>
  <li>配置反向代理</li>
</ol>

<p>接下来就是最关键的一步，配置反向代理。在上一步中我们已经可以在公网访问到内网的 Nginx，而这一步就是把访问 Nginx 的请求转发给路由器，对于路由器来说，这个请求是从内网中安装 Nginx 的主机访问过来的，所以可以访问路由器的管理页面。</p>

<p>修改 Nginx 的配置文件<code class="language-plaintext highlighter-rouge">nginx.conf</code>，这个文件的具体位置你可以上网查一下，如果你的 Nginx 是安装在 Linux 操作系统上，这个配置文件默认位于<code class="language-plaintext highlighter-rouge">/etc/nginx/nginx.conf</code>。下面贴出了这个文件中需要修改的部分，其中省略号代表不需要修改的无关紧要的部分。</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>...
http {
	...
	server {
		...
		location / {
			proxy_pass		http://192.168.3.1/;
			proxy_redirect	off;
		}
		...
	}
	...
} 
</code></pre></div></div>

<p>在做完这一步后，在任意一个已接入互联网的设备的浏览器里输入“你的路由器的公网 IP:你定的端口”后，就可以打开你的路由器的管理页面了。</p>

<p>对于专业用户来说，这几个步骤都非常简单；对于非专业用户来说，可能即使看了本教程也不知道该怎么做，那么欢迎你在文末评论。</p>]]></content><author><name></name></author><summary type="html"><![CDATA[这是需求，像帖子里说的那样：“手机app管理功能实在有限，希望能从外网web登陆后进行直接管理”。怕有人不明白这个意思，这里在贴一个正常路由器厂家提供的教程。]]></summary></entry><entry><title type="html">不推荐华为荣耀路由器的几个理由</title><link href="https://scfhao.cn/2020/09/16/about-honor-router.html" rel="alternate" type="text/html" title="不推荐华为荣耀路由器的几个理由" /><published>2020-09-16T02:49:43+00:00</published><updated>2020-09-16T02:49:43+00:00</updated><id>https://scfhao.cn/2020/09/16/about-honor-router</id><content type="html" xml:base="https://scfhao.cn/2020/09/16/about-honor-router.html"><![CDATA[<p>本人于2019年1月份购入一款荣耀路由器 PRO 2，当时花了345元，在这之前只是用过几款 TP-LINK 的百元级的路由器，所以这款荣耀路由器是我目前用过最贵的路由器了。从买入至今，已经过去一年半多一点的时间，对这款路由器表示失望是家常便饭，所以就想总结一下个人觉得荣耀路由器不好的地方，供诸君参考。当然本人的出发点并不是从路由器的基本功能出发，如果你是只使用路由器基本功能的普通用户，不需要往下看。海狗也请绕道。</p>

<ol>
  <li>USB及SMB</li>
</ol>

<p>当时选购路由器时一个指标是有 USB 插口，那时候简单的以为有 USB 插口的路由器连接移动硬盘后可以作为一个简易的私人云盘，当然对于绝大多数路由器确实是这样的，但是荣耀的路由器没有提供从公网访问这个 USB 设备的方法，若只能从内网访问这个 USB 设备的话那就相当鸡肋了，所以我这一年多也一直让这个 USB 口处于空闲状态。</p>

<p>SMB 是访问自己路由器上连着的 USB 设备的协议，最新的是 3.0，而荣耀路由器用的是 SMB 1.0，所以。。。</p>

<ol>
  <li>SSH</li>
</ol>

<p>同等价位的很多路由器都支持 SSH 远程登录路由器，比如极路由、小米路由器等都支持，但是荣耀的并不支持。</p>

<ol>
  <li>远程管理</li>
</ol>

<p>华为提供了“华为智能家居”移动App，可以通过这个 App 对路由器进行一些简单的设置，但是一些更高级的设置就只能在路由器的管理页面进行设置了。我经常会想在公网设置自己的路由器，但是抱歉荣耀路由器只允许在内网设置。但就连以前用的百元级的路由器都可以在公网设置，这就很无奈。</p>

<ol>
  <li>不支持自定义 hosts 映射</li>
</ol>

<p>路由器实际上包含 DNS 的功能，如果路由器上支持自定义 DNS 记录将会非常方便，比如 GitHub 访问太慢，如果你知道一个响应较快的 GitHub 镜像，你可以直接在路由器上设置好如果访问 GitHub 就访问比较快的那个镜像。抱歉不支持哦。</p>

<p>其实，每次对华为荣耀路由器失望的时候，我都会百度一下是否有解决方案，然后搜索结果里就会有花粉俱乐部论坛有同样痛点的留言，这些功能有很多人想要，但官方就是不给你加，所以你也没办法。这款荣耀路由器是我买的第一个华为产品，当初也是想支持一下国产(虽然知道华三，但价钱摆在那里)，结果满是后悔。这款路由器于是成了我 2019 年购买的最后悔的电子产品，也许我以后都不会再尝试购买华为的产品了。</p>]]></content><author><name></name></author><summary type="html"><![CDATA[本人于2019年1月份购入一款荣耀路由器 PRO 2，当时花了345元，在这之前只是用过几款 TP-LINK 的百元级的路由器，所以这款荣耀路由器是我目前用过最贵的路由器了。从买入至今，已经过去一年半多一点的时间，对这款路由器表示失望是家常便饭，所以就想总结一下个人觉得荣耀路由器不好的地方，供诸君参考。当然本人的出发点并不是从路由器的基本功能出发，如果你是只使用路由器基本功能的普通用户，不需要往下看。海狗也请绕道。]]></summary></entry><entry><title type="html">从Oracle迁移数据到PostgreSQL</title><link href="https://scfhao.cn/2020/07/07/migrate-date-from-oracle-to-postgresql.html" rel="alternate" type="text/html" title="从Oracle迁移数据到PostgreSQL" /><published>2020-07-07T01:55:21+00:00</published><updated>2020-07-07T01:55:21+00:00</updated><id>https://scfhao.cn/2020/07/07/migrate-date-from-oracle-to-postgresql</id><content type="html" xml:base="https://scfhao.cn/2020/07/07/migrate-date-from-oracle-to-postgresql.html"><![CDATA[<p>公司的数据库由原来的 Oracle 转向 PostgreSQL 也有半年多了，大部分系统里的数据也已由 Oracle 转为 PostgreSQL，有 ora2pg 这个神器也使这项工作轻松了不少。我是在自己电脑里的 VirtualBox 里的 Linux 虚拟机里搭建的 ora2pg 环境，在虚拟机里用 ora2pg 还是有很多限制：</p>

<ul>
  <li>一是给虚拟机分配的内存太小，经常会有内存不够而无法继续的情况，这种情况比较好解决，加大虚拟机的内存就没事了。</li>
  <li>另一个限制就是硬盘，如果源 Oracle 数据库中数据较多，ora2pg 输出导出文件时，硬盘容量占满后就无法输出了，当然这个问题也可以通过调整虚拟机磁盘大小来改善。</li>
  <li>在 ora2pg 的配置文件里，只能设置导出对象的类型，比如 INSERT 就把所有的表数据导出来了，而不能只导出指定某个表的数据。</li>
</ul>

<p>所以我有时候就需要只导某个表的数据到 PostgreSQL，思路很简单：</p>

<ol>
  <li>从 Oracle 导出某个表的数据。</li>
  <li>将导出的数据导入PostgreSQL。</li>
</ol>

<p>因为从 Oracle 导出的都是 INSERT 语句，语法比较简单，大部分 INSERT 语句都可以直接在 PostgreSQL 中运行，个别的数据导入会有问题，比如 Date，从 Oracle 中导出的日期格式如：“22-3月 -12”，而 PostgreSQL 可识别的格式是：“22-May-12”，如果数据量小的话可以自己手动替换一下，但是数据量稍大一点手动替换就会让你怀疑人生。本着能动脑就不动手的原则，加上久仰 Shell 命令里的 sed 命令的大名（sed命令可以用于替换文件内容）。写了个简单的脚本来做这件事。</p>

<p>Ora2pgDate.sh</p>

<pre><code class="language-Bash">#!/bin/bash

if [[ ! -f "$1" ]]; then
	echo "Usage: ora2pgDate.sh /path/to/ora.sql"
	exit
fi

sed -i "" 's/DD-MON-RR/DD-Mon-YY/g' "$1"
sed -i "" 's/-1月 -/-Jan-/g' "$1"
sed -i "" 's/-1月 -/-Jan-/g' "$1"
sed -i "" 's/-2月 -/-Feb-/g' "$1"
sed -i "" 's/-3月 -/-Mar-/g' "$1"
sed -i "" 's/-4月 -/-Apr-/g' "$1"
sed -i "" 's/-5月 -/-May-/g' "$1"
sed -i "" 's/-6月 -/-Jun-/g' "$1"
sed -i "" 's/-7月 -/-Jul-/g' "$1"
sed -i "" 's/-8月 -/-Aug-/g' "$1"
sed -i "" 's/-9月 -/-Sep-/g' "$1"
sed -i "" 's/-10月-/-Oct-/g' "$1"
sed -i "" 's/-11月-/-Nov-/g' "$1"
sed -i "" 's/-12月-/-Dec-/g' "$1"
</code></pre>

<p>这样针对单表数据迁移就比较方便了。</p>

<ol>
  <li>从 Oracle 导出某个表的数据。</li>
</ol>

<p>我这边用的是“Oracle SQL Developer”客户端，直接选中一个表，右键导出，取消选中“导出DDL”，取消选中“显示方案”，点击下一步，这样导出的就是最简单的 INSERT 语句了。</p>

<ol>
  <li>替换导出文件中的日期</li>
</ol>

<pre><code class="language-Bash">Ora2pgDate.sh 导出.sql
</code></pre>

<ol>
  <li>导入 PostgreSQL</li>
</ol>

<pre><code class="language-Bash">psql -d dbname -h 127.0.0.1 -U uname -f 导出.sql
</code></pre>]]></content><author><name></name></author><summary type="html"><![CDATA[公司的数据库由原来的 Oracle 转向 PostgreSQL 也有半年多了，大部分系统里的数据也已由 Oracle 转为 PostgreSQL，有 ora2pg 这个神器也使这项工作轻松了不少。我是在自己电脑里的 VirtualBox 里的 Linux 虚拟机里搭建的 ora2pg 环境，在虚拟机里用 ora2pg 还是有很多限制：]]></summary></entry><entry><title type="html">IDEDebugSessionErrorDomain: could not attach to pid</title><link href="https://scfhao.cn/2020/04/13/could-not-attach-to-pid.html" rel="alternate" type="text/html" title="IDEDebugSessionErrorDomain: could not attach to pid" /><published>2020-04-13T06:17:58+00:00</published><updated>2020-04-13T06:17:58+00:00</updated><id>https://scfhao.cn/2020/04/13/could-not-attach-to-pid</id><content type="html" xml:base="https://scfhao.cn/2020/04/13/could-not-attach-to-pid.html"><![CDATA[<p>这几天用 iOS 模拟器调试的时候，总遇到一个问题，程序更新到模拟器以后程序自动就退出了，Xcode会报一个错，内容如下：</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Details

Could not attach to pid : “15612”
Domain: IDEDebugSessionErrorDomain
Code: 3
Failure Reason: Error 1
--
Error 1
Domain: IDEDebugSessionErrorDomain
Code: 3
--
</code></pre></div></div>

<p>先说一下我当前的软件环境：macOS Catalina 10.15.2、Xcode 11.3.1，这类问题很容器过时。网上能查到的解决方案有以下几种：</p>

<ul>
  <li>重启Xcode、模拟器、电脑。</li>
  <li>Edit Schema-&gt;Run-&gt;Uncheck “Debug executable”。</li>
  <li><code class="language-plaintext highlighter-rouge">sudo DevToolsSecurity -enable</code></li>
</ul>

<p>第一个方案和第三个方案效果都不明显，第二个方案虽然管用但那样操作就不能调试了。偶然之间发现个可以解决我电脑上这个问题的方法，我这边网络环境比较复杂，模拟器要连的服务器在公司内网，而本公司内网默认是访问不了外网的，但这个问题貌似和网络有关：当电脑可以访问外网时，没这个问题，当电脑不能访问外网时，这个问题出现的几率很大。所以我这边只有同时让 Mac 连接公网后这个问题就不再出现了。</p>]]></content><author><name></name></author><summary type="html"><![CDATA[这几天用 iOS 模拟器调试的时候，总遇到一个问题，程序更新到模拟器以后程序自动就退出了，Xcode会报一个错，内容如下：]]></summary></entry><entry><title type="html">正确处理CentOS上Nginx部署的静态资源403问题</title><link href="https://scfhao.cn/2020/02/24/using-nginx-plus-with-selinux.html" rel="alternate" type="text/html" title="正确处理CentOS上Nginx部署的静态资源403问题" /><published>2020-02-24T01:40:13+00:00</published><updated>2020-02-24T01:40:13+00:00</updated><id>https://scfhao.cn/2020/02/24/using-nginx-plus-with-selinux</id><content type="html" xml:base="https://scfhao.cn/2020/02/24/using-nginx-plus-with-selinux.html"><![CDATA[<h2 id="前言">前言</h2>

<p>接着上一篇博客<a href="../23/deploy-server-at-home.html">低成本在家部署一个Server</a>，这台服务器在我家里充当了一个服务总网关的作用，我在这台服务器上安装了Nginx，在Nginx上部署了HTTPS，然后当我在我工作使用的MacBook上启动一个测试Web服务的时候，我可以通过在Nginx上配置反向代理来对外提供这个Web服务。</p>

<p>我同样在Nginx上配置了一个静态目录，我在这个静态目录里放了一些学习资料，这样我就可以随时随地访问这些学习资料了。因为macOS上自带了Apache，所以我之前一直用Apache做我的静态文件服务器，Nginx还是属于刚开始接触。我在Nginx的配置文件里配置好静态目录，也给静态目录及其子目录设置了所有用户可以读取的权限，但是启动Nginx并访问，403 Forbidden。于是上网查找解决方案，在我查到的解决这种问题的博客里，绝大多数都是通过在Nginx的配置文件里把user改为root，虽然我对Linux也不是很懂，但随便给程序以root权限运行的方式绝对是下下之策。</p>

<p>后来发现这个问题是SELinux的限制导致的，在Nginx官网上找到了正确的解决方案。</p>

<p>我这里用谷歌翻译后发到博客上，以供中文朋友参考，可以点击<a href="https://www.nginx.com/blog/using-nginx-plus-with-selinux/">原文</a>查看英文原版。</p>

<h1 id="将nginx和nginx-plus与selinux结合使用">将NGINX和NGINX Plus与SELinux结合使用</h1>

<p>编者说–标题为“ NGINX：升级到RHEL 6.6 / CentOS 6.6时SELinux的更改”的博客文章重定向到此处。 本文提供了更新的通用信息。</p>

<p>现代Red Hat Enterprise Linux（RHEL）和相关发行版上的Security-Enhanced Linux（SELinux）的默认设置可能非常严格，这是出于安全性而不是便利性的考虑。 尽管默认设置在其默认配置中不限制NGINX Open Source和NGINX Plus的功能，但是除非您在SELinux中明确允许它们，否则可能会阻止您可能配置的其他功能。 本文介绍了可能的问题以及建议的解决方法。</p>

<p>[编者说–本文适用于NGINX开源和NGINX Plus。 为了便于阅读，始终使用术语“ NGINX”。</p>

<p>CentOS是最初衍生自RHEL的相关发行版，并受到NGINX和NGINX Plus的支持。 此外，NGINX Plus还支持相关的Amazon Linux和Oracle Linux发行版。 它们的默认SELinux设置可能与CentOS和RHEL不同。 请参阅供应商文档。]</p>

<h2 id="selinux概述">SELinux概述</h2>

<p>默认情况下，现代RHEL和CentOS服务器上已启用SELinux。 每个操作系统对象（进程，文件描述符，文件等）都标记有SELinux上下文，该上下文定义了对象可以执行的权限和操作。 在RHEL 6.6 / CentOS 6.6和更高版本中，NGINX带有<code class="language-plaintext highlighter-rouge">httpd_t</code>上下文标记：</p>

<pre><code class="language-Bash"># ps auZ | grep nginx
unconfined_u:system_r:httpd_t:s0 3234 ? Ss 0:00 nginx: master process /usr/sbin/nginx \
                                                -c /etc/nginx/nginx.conf
unconfined_u:system_r:httpd_t:s0 3236 ? Ss 0:00 nginx: worker process
</code></pre>

<p><code class="language-plaintext highlighter-rouge">httpd_t</code>上下文允许NGINX侦听公共Web服务器端口，访问<strong>/etc/nginx</strong>中的配置文件，以及访问标准docroot位置（<strong>/usr/share/nginx</strong>）中的内容 。 它不允许许多其他操作，例如代理上游位置或通过套接字与其他进程通信。</p>

<h2 id="暂时为nginx禁用selinux">暂时为NGINX禁用SELinux</h2>

<p>要暂时禁用针对httpd_t上下文的SELinux限制，以便NGINX可以执行与非SELinux操作系统中相同的所有操作，请将httpd_t上下文分配给许可域。 有关详细信息，请参见下一部分。</p>

<pre><code class="language-Bash"># semanage permissive -a httpd_t
</code></pre>

<h2 id="更改selinux模式">更改SELinux模式</h2>

<p>SELinux可以以“强制”，“允许”或“禁用”模式（也称为域）运行。 在进行可能会违反默认（严格）权限的NGINX配置更改之前，可以在测试环境（如果可用）或生产环境中将SELinux从“强制”模式更改为“允许”模式。 在“允许”模式下，SELinux允许所有操作，但在“强制”模式下记录可能违反安全策略的操作。</p>

<p>要将“ httpd_t”添加到“允许的”域列表中，请运行以下命令：</p>

<pre><code class="language-Bash"># semanage permissive -a httpd_t
</code></pre>

<p>要从“允许的”域列表中删除“ httpd_t”，请运行：</p>

<pre><code class="language-Bash">＃semanage permissive -d httpd_t
</code></pre>

<p>要将模式全局设置为“允许”，请运行：</p>

<pre><code class="language-Bash">＃setenforce 0
</code></pre>

<p>要将模式全局设置为<strong>执行</strong>，请运行：</p>

<pre><code class="language-Bash">＃setenforce 1
</code></pre>

<p>##解决SELinux安全异常</p>

<p>在“允许”模式下，安全例外记录到默认的Linux审核日志中，即<strong>/var/log/audit/audit.log</strong>。如果您遇到仅在NGINX以“强制”模式运行时发生的问题，请查看以“许可”模式记录的异常，并更新安全策略以允许它们。</p>

<h3 id="问题1禁止代理连接">问题1：禁止代理连接</h3>

<p>默认情况下，SELinux配置不允许NGINX连接到远程HTTP，FastCGI或其他服务器，如审核日志消息所示，如下所示：</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>type=AVC msg=audit(1415714880.156:29): avc:  denied  { name_connect } for  pid=1349 \
  comm="nginx" dest=8080 scontext=unconfined_u:system_r:httpd_t:s0 \
  tcontext=system_u:object_r:http_cache_port_t:s0 tclass=tcp_socket
type=SYSCALL msg=audit(1415714880.156:29): arch=c000003e syscall=42 success=no \
  exit=-115 a0=b \a1=16125f8 a2=10 a3=7fffc2bab440 items=0 ppid=1347 pid=1349 \
  auid=1000 uid=497 gid=496 euid=497 suid=497 fsuid=497 egid=496 sgid=496 fsgid=496 \
  tty=(none) ses=1 comm="nginx" exe="/usr/sbin/nginx" \
  subj=unconfined_u:system_r:httpd_t:s0 key=(null)
</code></pre></div></div>

<p>audit2why命令解释消息代码（1415714880.156：29）：</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># grep 1415714880.156:29 /var/log/audit/audit.log | audit2why
type=AVC msg=audit(1415714880.156:29): avc:  denied  { name_connect } for  pid=1349 \
  comm="nginx" dest=8080 scontext=unconfined_u:system_r:httpd_t:s0 \
  tcontext=system_u:object_r:http_cache_port_t:s0 tclass=tcp_socket
 
        Was caused by:
        One of the following booleans was set incorrectly.
        Description:
        Allow httpd to act as a relay
 
        Allow access by executing:
        # setsebool -P httpd_can_network_relay 1
        Description:
        Allow HTTPD scripts and modules to connect to the network using TCP.
 
        Allow access by executing:
        # setsebool -P httpd_can_network_connect 1
</code></pre></div></div>

<p>audit2why的输出表明，您可以通过启用<strong>httpd_can_network_relay</strong>和<strong>httpd_can_network_connect</strong>布尔选项中的一个或两个来允许NGINX建立代理连接。您可以临时或永久启用它们，后者可以通过添加<strong>‑P</strong>标志（如输出所示）来启用。</p>

<h4 id="了解布尔选项">了解布尔选项</h4>

<p>sesearch命令提供有关布尔选项的更多信息，并且在安装<strong>setools</strong>软件包（yum install setools）时可用。在这里，我们显示<strong>httpd_can_network_relay</strong>和<strong>httpd_can_network_connect</strong>选项的输出。</p>

<h5 id="httpd_can_network_relay布尔选项">httpd_can_network_relay布尔选项</h5>

<p>以下是sesearch命令有关<strong>httpd_can_network_relay</strong>选项的输出：</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># sesearch -A -s httpd_t -b httpd_can_network_relay
Found 10 semantic av rules:
   allow httpd_t gopher_port_t : tcp_socket name_connect ;
   allow httpd_t http_cache_client_packet_t : packet { send recv } ;
   allow httpd_t ftp_port_t : tcp_socket name_connect ;
   allow httpd_t ftp_client_packet_t : packet { send recv } ;
   allow httpd_t http_client_packet_t : packet { send recv } ;
   allow httpd_t squid_port_t : tcp_socket name_connect ;
   allow httpd_t http_cache_port_t : tcp_socket name_connect ;
   allow httpd_t http_port_t : tcp_socket name_connect ;
   allow httpd_t gopher_client_packet_t : packet { send recv } ;
   allow httpd_t memcache_port_t : tcp_socket name_connect ;
</code></pre></div></div>

<p>此输出表明<strong>httpd_can_network_relay</strong>允许标有<strong>httpd_t</strong>上下文的进程（例如NGINX）连接到各种类型的端口，包括<strong>http_port_t</strong>类型：</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># semanage port -l | grep http_port_t
http_port_t                    tcp      80, 81, 443, 488, 8008, 8009, 8443, 9000
</code></pre></div></div>

<p>要将更多端口（此处为<strong>8082</strong>）添加到<strong>http_port_t</strong>允许的端口集中，请运行：</p>

<pre><code class="language-Bash"># semanage port -a -t http_port_t -p tcp 8082
</code></pre>

<p>如果此命令的输出表明已经定义了端口，如以下示例所示，则意味着该端口已包含在另一个集中。不要将其重新分配给http_port_t，因为其他服务可能会受到负面影响。</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># semanage port -a -t http_port_t -p tcp 8080
/usr/sbin/semanage: Port tcp/8080 already defined
# semanage port -l | grep 8080
http_cache_port_t              tcp      3128, 8080, 8118, 8123, 10001-10010
</code></pre></div></div>

<h5 id="httpd_can_network_connect布尔选项">httpd_can_network_connect布尔选项</h5>

<p>这是<strong>sesearch</strong>命令关于<strong>httpd_can_network_connect</strong>选项的输出：</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># sesearch -A -s httpd_t -b httpd_can_network_connect
Found 1 semantic av rules:
   allow httpd_t port_type : tcp_socket name_connect ;
</code></pre></div></div>

<p>此输出表明<strong>httpd_can_network_connect</strong>允许标有<strong>httpd_t</strong>上下文的进程（例如NGINX）连接到所有具有<strong>port_type</strong>属性的TCP套接字类型。要列出它们，请运行：</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>＃seinfo -aport_type -x
</code></pre></div></div>

<h3 id="问题2禁止文件访问">问题2：禁止文件访问</h3>

<p>默认情况下，SELinux配置不允许NGINX访问知名授权位置之外的文件，如以下审核日志消息所示：</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>type=AVC msg=audit(1415715270.766:31): avc:  denied  { getattr } for  pid=1380 \
  comm="nginx" path="/www/t.txt" dev=vda1 ino=1084 \
  scontext=unconfined_u:system_r:httpd_t:s0 \
  tcontext=unconfined_u:object_r:default_t:s0 tclass=file
</code></pre></div></div>

<p>audit2why命令解释消息代码（<strong>1415715270.766:31</strong>）：</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># grep 1415715270.766:31 /var/log/audit/audit.log | audit2why
type=AVC msg=audit(1415715270.766:31): avc:  denied  { getattr } for  pid=1380 \
  comm="nginx" path="/www/t.txt" dev=vda1 ino=1084 \
  scontext=unconfined_u:system_r:httpd_t:s0 \
  tcontext=unconfined_u:object_r:default_t:s0 tclass=file
 
    Was caused by:
        Missing type enforcement (TE) allow rule.
 
        You can use audit2allow to generate a loadable module to allow this access.
</code></pre></div></div>

<p>禁止文件访问时，有两个选择。</p>

<h4 id="选项1修改文件标签">选项1：修改文件标签</h4>

<p>修改文件标签，以便NGINX（作为带有<strong>httpd_t</strong>上下文的进程）可以访问文件：</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>＃chcon -v --type = httpd_sys_content_t /www/t.txt
</code></pre></div></div>

<p>默认情况下，当重新标记文件系统时，将删除此修改。要使更改永久生效，请运行：</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>＃semanage fcontext -a -t httpd_sys_content_t /www/t.txt
＃restorecon -v /www/t.txt
</code></pre></div></div>

<p>要修改文件组的文件标签，请运行：</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>＃semanage fcontext -a -t httpd_sys_content_t /www(/.*)?
＃restorecon -Rv / www
</code></pre></div></div>

<h4 id="选项2扩展httpd_t域权限">选项2：扩展httpd_t域权限</h4>

<p>扩展<strong>httpd_t</strong>的策略以允许访问其他文件位置：</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># grep nginx /var/log/audit/audit.log | audit2allow -m nginx &gt; nginx.te
# cat nginx.te
 
module nginx 1.0;
 
require {
        type httpd_t;
        type default_t;
        type http_cache_port_t;
        class tcp_socket name_connect;
        class file { read getattr open };
}
 
#============= httpd_t ==============
allow httpd_t default_t:file { read getattr open };
 
#!!!! This avc can be allowed using one of these booleans:
#     httpd_can_network_relay, httpd_can_network_connect
allow httpd_t http_cache_port_t:tcp_socket name_connect;
</code></pre></div></div>

<p>要生成已编译的策略，请包含<code class="language-plaintext highlighter-rouge">-M</code>选项：</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>＃grep nginx /var/log/audit/audit.log | audit2allow -M nginx
</code></pre></div></div>

<p>要加载该策略，请运行<code class="language-plaintext highlighter-rouge">semodule -i</code>，然后使用<code class="language-plaintext highlighter-rouge">semodule -l</code>验证成功：</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>＃semodule -i nginx.pp
＃semodule -l | grep nginx
Nginx 1.0
</code></pre></div></div>

<p>此更改在重新启动后仍然存在。</p>

<h3 id="问题3nginx无法绑定到其他端口">问题3：NGINX无法绑定到其他端口</h3>

<p>默认情况下，SELinux配置不允许NGINX侦听（<strong>bind()</strong>）除<strong>http_port_t</strong>类型白名单中的默认端口外的TCP或UDP端口：</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># semanage  port -l | grep http_port_t
http_port_t                    tcp      80, 443, 488, 8008, 8009, 8443
</code></pre></div></div>

<p>如果尝试将NGINX配置为在未列入白名单的端口上侦听（在NGINX配置中的http，流或邮件上下文中使用listen指令），则在验证（nginx -t）或重新加载NGINX时会收到错误消息配置，如以下NGINX日志条目所示：</p>

<p>YYYY/MM/DD hh:mm:ss [emerg] 46123#0: bind()到0.0.0.0:8001失败（13：权限被拒绝）
您可以使用semanage将所需的端口（此处为8001）添加到http_port_t类型：</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># semanage port -a -t http_port_t -p tcp 8001
</code></pre></div></div>

<p>用新配置重新加载NGINX。</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># nginx -s reload
</code></pre></div></div>

<h2 id="其他资源">其他资源</h2>

<p>SELinux是用于管理操作系统权限的复杂而强大的工具。其他信息在以下文档中提供。</p>

<ul>
  <li>增强安全性的Linux，RHEL 6或RHEL 7</li>
  <li>SELinux（CentOS使用方法）</li>
  <li>增强安全性的Linux用户指南（Fedora项目）</li>
  <li>SELinux项目主页</li>
  <li>安全性增强的Linux（美国国家安全局）</li>
</ul>]]></content><author><name></name></author><summary type="html"><![CDATA[前言]]></summary></entry></feed>