Akawa

ETY001的博客

有时候为了发送一定量的邮件,而使用类似QQ邮箱这样厂商的产品就会受到各种限制。
为此,需要自己搭建个简易的发邮件的服务器。需求很简单:服务器只需要能发邮件,
不需要接收邮件,且发邮件可以使用 smtp 登陆验证来使用。

安装MTA服务器

我的VPS是 centos7 ,系统自带了 postfix,然后还需要安装 sasl
这个是为了实现 smtp 验证必备的软件。

1
yum install cyrus*

如果你的服务器上没有 postfix ,而是 sendmail,你需要先卸载再安装。

1
2
rpm -e sendmail
yum install postfix

配置DNS

我用的是DNSPOD,要配置的域名为 mypi.win,也就是我希望发邮件的地址是
[email protected]。需要在DNSPOD做以下三条配置

dns配置

其中 SPF 配置中的内容为 v=spf1 a mx ip4:x.x.x.x -all,把其中的
x.x.x.x 换成你的服务器IP,也就是你的主域名指向的IP。

SPF 的作用是为了降低邮件被识别为垃圾邮件的概率,配置好以后,可以去这里
测试下是否配置正确了。

配置postfix

修改 /etc/postfix/main.cf 的内容。

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
##将下面的配置项注释取消,后面填上合适值##
myhostname = mypi.win
#大约在75行,postfix主机名,修改成你的域名 此项需要添加A记录并指向postfix所在主机公网IP
mydomain = mypi.win
#大约在83行,后面为主机域名
myorigin = $mydomain
#大约在100行,设置postfix邮箱的域名后缀为$mydomain,即mypi.win
inet_interfaces = localhost
#大约在117行
#指定postfix系统监听的网络接口 此处必须是localhost或127.0.0.1或内网ip
#若注释或填入公网ip 服务器的25端口将对公网开放
#默认值为all 即监听所有网络接口
#此项指定localhost后 本机postfix就只能发邮件不能接收邮件
inet_protocols = ipv4
#大约在120行,指定网络协议
mydestination = $myhostname, localhost.$mydomain, localhost, $mydomain
#大约在165行
#指定postfix接收邮件时收件人的域名,换句话说,也就是你的postfix系统要接收什么样的邮件。
#此项配置中$myhostname表示postfix接受@$myhostname为后缀的邮箱的邮件 逗号分割支持指多项
#此项默认值使用myhostname
local_recipient_maps =
#此项制定接收邮件的规则 可以是hash文件 此项对本次配置无意义 可以直接注释
mynetworks = x.x.x.x, 172.17.0.1, 127.0.0.1
#大约在266行
#指定你所在的网络的网络地址
#本服务器的公网IP x.x.x.x、内网ip 172.17.0.1、以及localhost的ip127.0.0.1
#请依据实际情况修改
smtpd_banner = ETY001 Steemit Mention Server
#大约在571行
#指定MUA通过smtp连接postfix时返回的header头信息

#SMTP Config
broken_sasl_auth_clients = yes
#指定postfix兼容MUA使用不规则的smtp协议--主要针对老版本的outlook 此项对于本次配置无意义
smtpd_client_restrictions = permit_sasl_authenticated
#指定可以向postfix发起SMTP连接的客户端的主机名或ip地址
#此处permit_sasl_authenticated意思是允许通过sasl认证(也就是smtp链接时通过了账号、密码效验的用户)的所有用户
smtpd_recipient_restrictions = permit_mynetworks, permit_sasl_authenticated, reject_unauth_destination
#发件人在执行RCPT TO命令时提供的地址进行限制规则 此处照搬复制即可
smtpd_sasl_auth_enable = yes
#指定postfix使用sasl验证 通俗的将就是启用smtp并要求进行账号、密码效验
smtpd_sasl_local_domain = $mydomain
#指定SMTP认证的本地域名 本次配置可以使用 smtpd_sasl_local_domain = '' 或干脆注释掉 默认为空
smtpd_sasl_security_options = noanonymous
#取消smtp的匿名登录 此项默认值为noanonymous smtp若能匿名登录危害非常大 此项请务必指定为noanonymous
message_size_limit = 5242880
#指定通过postfix发送邮件的体积大小 此处表示5M

配置 SMTP

配置 postfix 启用 sasldb2 的账号密码验证方式:

1
2
3
4
5
6
vim /etc/sasl2/smtpd.conf
#向这个文件写入如下的内容(这个文件的路径为64位系统的,如果是32位系统应该在/usr/lib/sasl2/smtpd.cof)

pwcheck_method: auxprop
auxprop_plugin: sasldb
mech_list: plain login CRAM-MD5 DIGEST-MD5

接下来新建一个连接stmp服务的用户名和密码:

1
[root@mypi ~]# saslpasswd2 -c -u `postconf -h mydomain` steemit

这条命令会新建一个 [email protected] 的用户,接着会让你输入密码,假设密码为 123456789

新建完成后,可以使用 sasldblistusers2 命令 来查看 已经添加了哪些用户(密码使用userPassword代替):

1
2
[root@mypi ~]# sasldblistusers2
[email protected]: userPassword

修改 sasldb 的权限,让 postfix 可以读取其中的内容

1
2
[root@mypi ~]# chgrp postfix /etc/sasldb2
[root@mypi ~]# chmod 640 /etc/sasldb2

启动 postfix 并添加开机启动

如果是 centos7 ,执行下面的命令

1
2
systemctl start postfix
systemctl enable postfix

测试smtp

在另外的电脑上执行下面的命令

1
2
3
4
5
6
7
8
/ # nc mypi.win 25
220 ETY001 Steemit Mention Server
auth login
334 VXNlcm5hbWU6
c3RlZW1pdEBteXBpLndpbg==
334 UGFzc3dvcmQ6
MTIzNDU2Nzg5
235 2.7.0 Authentication successful

其中 auth login 为登陆验证指令, c3RlZW1pdEBteXBpLndpbg== 是邮箱地址
[email protected]base64 编码后字符串,MTIzNDU2Nzg5 是密码
123456789base64 编码后字符串。

使用 yaourt 安装 frp 的时候,报告以下的错误:

1
2
3
4
5
6
7
==> Validating source files with sha256sums...
frps.service ... FAILED
frpc.service ... FAILED
[email protected] ... FAILED
[email protected] ... FAILED
==> ERROR: One or more files did not pass the validity check!
:: failed to verify frp integrity

解决方案,重新自己生成 PKGBUILD 文件:

1
2
3
4
5
$ yaourt -G frp
$ cd frp
$ makepkg -g >> PKGBUILD
$ makepkg
$ sudo pacman -S frp-0.14.0-2-x86_64.pkg.tar.xz

使用方法:

1
2
3
4
5
6
7
8
9
To start the client, simply modify /etc/frp/frpc.ini, and start the service by:

systemctl start frpc

or create your own config file, for example myconf.ini, and start the service by:

systemctl start frpc@myconf

You can also start frps the same way

具体配置参数见 https://github.com/fatedier/frp

今天配置服务器的https,结果配置完后,浏览器访问报错,显示

1
2
This site can’t provide a secure connection
1hour.win sent an invalid response.

openssl 看了下,发现报错如下

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
[root@host wwwroot]# openssl s_client -connect 1hour.win:443 -debug
CONNECTED(00000003)
write to 0x1d66480 [0x1e64440] (247 bytes => 247 (0xF7))
0000 - 16 03 01 00 f2 01 00 00-ee 03 03 59 77 63 d6 28 ...........Ywc.(
0010 - 1e d6 5a c6 ba ef 29 51-7a 37 7f 93 4b 6d 05 e1 ..Z...)Qz7..Km..
0020 - 1c 45 37 ba c2 11 3e 12-1d 36 2a 00 00 84 c0 30 .E7...>..6*....0
0030 - c0 2c c0 28 c0 24 c0 14-c0 0a 00 a3 00 9f 00 6b .,.(.$.........k
0040 - 00 6a 00 39 00 38 00 88-00 87 c0 32 c0 2e c0 2a .j.9.8.....2...*
0050 - c0 26 c0 0f c0 05 00 9d-00 3d 00 35 00 84 c0 2f .&.......=.5.../
0060 - c0 2b c0 27 c0 23 c0 13-c0 09 00 a2 00 9e 00 67 .+.'.#.........g
0070 - 00 40 00 33 00 32 00 9a-00 99 00 45 00 44 c0 31 [email protected]
0080 - c0 2d c0 29 c0 25 c0 0e-c0 04 00 9c 00 3c 00 2f .-.).%.......<./
0090 - 00 96 00 41 c0 12 c0 08-00 16 00 13 c0 0d c0 03 ...A............
00a0 - 00 0a 00 07 c0 11 c0 07-c0 0c c0 02 00 05 00 04 ................
00b0 - 00 ff 01 00 00 41 00 0b-00 04 03 00 01 02 00 0a .....A..........
00c0 - 00 08 00 06 00 19 00 18-00 17 00 23 00 00 00 0d ...........#....
00d0 - 00 20 00 1e 06 01 06 02-06 03 05 01 05 02 05 03 . ..............
00e0 - 04 01 04 02 04 03 03 01-03 02 03 03 02 01 02 02 ................
00f0 - 02 03 00 0f 00 01 01 .......
read from 0x1d66480 [0x1e699a0] (7 bytes => 7 (0x7))
0000 - 48 54 54 50 2f 31 2e HTTP/1.
140304408045472:error:140770FC:SSL routines:SSL23_GET_SERVER_HELLO:unknown protocol:s23_clnt.c:769:
---
no peer certificate available
---
No client certificate CA names sent
---
SSL handshake has read 7 bytes and written 247 bytes
---
New, (NONE), Cipher is (NONE)
Secure Renegotiation IS NOT supported
Compression: NONE
Expansion: NONE
---

unknown protocol? 这是啥情况,仔细比对配置,发现原来是在 listen 配置项中掉了 ssl
正确配置应该是

1
listen 443 ssl;

之前写成了

1
listen 443;

在服务器端部署代码的时候,新版本的 composer 不再支持 root 用户来运行。因为会带来潜在的安全问题,
这导致 composer update 之后,不会再执行 scripts 里的脚本命令。

但是一般服务器上的web服务都是运行在 www 之类的用户下面的,且这些用户是不允许登陆的,那么我们该咋整?

经过搜索和实验后,发现 runuser 不好使,可以使用 sudo,具体命令如下:

1
sudo -u www /usr/local/bin/composer update
注意要执行的命令需要完整路径。

最近需要做一个快速上线的项目,要求使用 Symfony 框架。

久闻 SymfonyBundle 很丰富,于是搜索了下现成的后台系统。
首先发现的是 Sonata Admin Panel,是个老牌的系统了,可惜经过研究发现,
它对于 Symfony3 下的新版本的 FOS Bundle(一个用户系统) 不支持。于是又找了一个,
名字是 EasyAdminBundle

这篇博客会用最短最快的方式,让你建立一个简易的博客后台,其中的各个步骤则不解释。

体验环境:MacOS X 或 Linux (注意 phppdo_mysql.so 请打开)

  • 安装 Symfony

    1
    2
    3
    $ sudo mkdir -p /usr/local/bin
    $ sudo curl -LsS https://symfony.com/installer -o /usr/local/bin/symfony
    $ sudo chmod a+x /usr/local/bin/symfony
  • 创建 Symfony 项目 并启动服务

    1
    2
    3
    $ symfony new blog
    $ cd blog
    $ php bin/console server:start

访问下 http://127.0.0.1:8000 看是否正常启动。

  • 如需停止服务,执行下面的命令

    1
    $ php bin/console server:stop
  • 下载安装 EasyAdmin

    1
    $ composer require javiereguiluz/easyadmin-bundle
  • 激活插件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    <?php
    // app/AppKernel.php
    // ...
    class AppKernel extends Kernel
    {
    public function registerBundles()
    {
    $bundles = array(
    // ...
    new JavierEguiluz\Bundle\EasyAdminBundle\EasyAdminBundle(),
    );
    }
    // ...
    }
  • 载入路由

    1
    2
    3
    4
    5
    # app/config/routing.yml
    easy_admin_bundle:
    resource: "@EasyAdminBundle/Controller/"
    type: annotation
    prefix: /admin
  • 载入静态资源

    1
    2
    # Symfony 3
    php bin/console assets:install --symlink
  • app/config/parameters.yml 中配置数据库连接

  • 创建 Entity

    1
    2
    $ php bin/console doctrine:generate:entity --entity="AppBundle:Category" --fields="name:string(255) enabled:boolean" --no-interaction
    $ php bin/console doctrine:generate:entity --entity="AppBundle:Posts" --fields="title:string(255) contents:text() enabled:boolean" --no-interaction
  • src/AppBundle/Entity/PostsEntity.php 中增加关联关系

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    <?php
    // ...
    class Posts
    {
    // ...
    /**
    * @ORM\ManyToOne(targetEntity="Category", inversedBy="posts")
    * @ORM\JoinColumn(name="category_id", referencedColumnName="id")
    */
    private $category;
    }
  • src/AppBundle/Entity/Category.php 中增加对应的关联关系(新增代码如下)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    // src/AppBundle/Entity/Category.php

    // ...
    use Doctrine\Common\Collections\ArrayCollection;

    class Category
    {
    // ...

    /**
    * @ORM\OneToMany(targetEntity="Posts", mappedBy="category")
    */
    private $posts;

    public function __construct()
    {
    $this->posts = new ArrayCollection();
    }
    }
  • 执行以下命令,完善对应属性的方法

    1
    2
    $ composer update
    $ php bin/console doctrine:generate:entities AppBundle --no-backup
  • 执行以下命令,生成数据表

    1
    $ php bin/console doctrine:schema:update --force
  • 配置后台

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    # app/config/config.yml
    easy_admin:
    entities:
    Category:
    class: AppBundle\Entity\Category
    form:
    fields:
    - name
    Posts:
    class: AppBundle\Entity\Posts
    form:
    fields:
    - title
    - { property: 'category', type: 'easyadmin_autocomplete' }
    - contents
  • 配置语言

    1
    2
    3
    # app/config/config.yml
    framework:
    translator: { fallbacks: ['%locale%'] }

这时,访问 http://127.0.0.1:8000/admin,就能看到我们的后台了。

下面是集成 FOS Bundle 的操作。

  1. 安装 FOS

    1
    $ composer require friendsofsymfony/user-bundle "~2.0"
  2. 启用 FOS

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    <?php
    // app/AppKernel.php

    public function registerBundles()
    {
    $bundles = array(
    // ...
    new FOS\UserBundle\FOSUserBundle(),
    // ...
    );
    }
  3. 创建 User Class

    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
    <?php
    // src/AppBundle/Entity/User.php

    namespace AppBundle\Entity;

    use FOS\UserBundle\Model\User as BaseUser;
    use Doctrine\ORM\Mapping as ORM;

    /**
    * @ORM\Entity
    * @ORM\Table(name="fos_user")
    */
    class User extends BaseUser
    {
    /**
    * @ORM\Id
    * @ORM\Column(type="integer")
    * @ORM\GeneratedValue(strategy="AUTO")
    */
    protected $id;

    public function __construct()
    {
    parent::__construct();
    // your own logic
    }
    }
  4. 增加防火墙配置(全部替换原有内容)

    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
    # app/config/security.yml
    security:
    encoders:
    FOS\UserBundle\Model\UserInterface: bcrypt

    role_hierarchy:
    ROLE_ADMIN: ROLE_USER
    ROLE_SUPER_ADMIN: ROLE_ADMIN

    providers:
    fos_userbundle:
    id: fos_user.user_provider.username

    firewalls:
    main:
    pattern: ^/
    form_login:
    provider: fos_userbundle
    csrf_token_generator: security.csrf.token_manager
    # if you are using Symfony < 2.8, use the following config instead:
    # csrf_provider: form.csrf_provider

    logout: true
    anonymous: true

    access_control:
    - { path: ^/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
    - { path: ^/register, role: IS_AUTHENTICATED_ANONYMOUSLY }
    - { path: ^/resetting, role: IS_AUTHENTICATED_ANONYMOUSLY }
    - { path: ^/admin/, role: ROLE_ADMIN }
  5. 配置 FOS

    1
    2
    3
    4
    5
    6
    7
    8
    # app/config/config.yml
    fos_user:
    db_driver: orm # other valid values are 'mongodb' and 'couchdb'
    firewall_name: main
    user_class: AppBundle\Entity\User
    from_email:
    address: "%mailer_user%"
    sender_name: "%mailer_user%"
  6. app/config/parameters.yml 中配置第5步中的 mailer_user 参数

  7. 导入 FOS 的路由配置

    1
    2
    3
    # app/config/routing.yml
    fos_user:
    resource: "@FOSUserBundle/Resources/config/routing/all.xml"
  8. 更新数据库

    1
    $ php bin/console doctrine:schema:update --force
  9. 创建新的控制器

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    // src/AppBundle/Controller/AdminController.php
    namespace AppBundle\Controller;

    use JavierEguiluz\Bundle\EasyAdminBundle\Controller\AdminController as BaseAdminController;

    class AdminController extends BaseAdminController
    {
    public function createNewUserEntity()
    {
    return $this->get('fos_user.user_manager')->createUser();
    }

    public function prePersistUserEntity($user)
    {
    $this->get('fos_user.user_manager')->updateUser($user, false);
    }

    public function preUpdateUserEntity($user)
    {
    $this->get('fos_user.user_manager')->updateUser($user, false);
    }
    }
  10. 在 config.yml 中增加 User 的配置

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    easy_admin:
    entities:
    User:
    class: AppBundle\Entity\User
    form:
    fields:
    - username
    - email
    - enabled
    - lastLogin
    # if administrators are allowed to edit users' passwords and roles, add this:
    - { property: 'plainPassword', type: 'text', type_options: { required: false } }
    - { property: 'roles', type: 'choice', type_options: { multiple: true, choices: { 'ROLE_USER': 'ROLE_USER', 'ROLE_ADMIN': 'ROLE_ADMIN' } } }

此时你再访问的时候,就会弹出来一个很丑的登陆界面(因为FOS默认不带皮肤)。
使用下面的命令创建一个管理员账号

1
$ php bin/console fos:user:create admin --super-admin

按照提示,即可完成管理员创建的工作。回到网页上,登陆即可。

至此,一个简单的后台就搭建完毕了。

接下来还有很多的工作可以做,这个以后再来完善补充。

  1. composer 更新代码

    1
    php composer.phar install --no-dev --optimize-autoloader
  2. Symfony 清空cache

    1
    php bin/console cache:clear --env=prod --no-debug
  3. 生成 Assets

    1
    php bin/console assetic:dump --env=prod --no-debug

其他的总结

1
2
rm -rf bin/cache/* #手动清空缓存
php bin/console route:debug # 查看哪些路由已经注册

最近好几个服务器,ssh登录的时候,都有同一个报错,

1
setlocale: LC_CTYPE: cannot change locale (UTF-8): No such file or directory

经过搜索,找到了解决方案

1
2
3
4
5
6
7
8
9
# tee /etc/environment <<- 'EOF'
LANG=en_US.utf-8
LC_ALL=en_US.utf-8
EOF

# source /etc/environment

/* 生成 en_US.UTF-8 locale文件 CentOS没有locale-gen命令*/
# localedef -v -c -i en_US -f UTF-8 en_US.UTF-8

起因,在代码上线过程中发现了一个bug,列表中缺失的值为 NULL 的记录。
于是检查 SQL 语句,自己感觉没有什么问题。就是列表中不要显示值为 sample 的记录。

原有的SQL大致是这么写的

1
select * from tableA where key != 'sample';

其中还有很多 keyNULL 的记录没有检索出来。

经过搜索引擎的查找,发现原来普通的运算符不能用在 NULL 上。。。

真是惭愧。。居然不知道这个。。。

改为下面的语句后就可以了

1
select * from tableA where key != 'sample' or key is null;

昨天项目中的一个js的时间间隔计算的bug,真的是被搞的焦头烂额。
最终无奈,只能把js计算过程中的每一步都 console.log 出来,
然后才发现,原来是同事的机器本地时间涉及到冬令时和夏令时。

源代码大致是这样:

1
2
3
4
5
6
7
var start_date = '2017/02/10';
var end_date = '2017/05/20';
var tmp = new Date(start_date);
start_time = tmp.getTime();
tmp = new Date(end_date);
end_time = tmp.getTime();
//时间戳相减求间隔的代码就不写了。。。

由于存在夏令时和冬令时的缘故,两次的 tmp 对象的时区其实是不一样的,
这就导致最终的时间戳做差就会少一天。

最终的解决方案是,把时间都统一到 UTC 后,然后再计算。

具体的获取时间戳的方法是:

1
var timestamp = Date.UTC(year, month-1, day);

一定要注意的是 js 中的 month 是从 0 开始的!!!

本打算在从清迈回国的路上,把年终总结写了,结果也是因为懒的可以了,也就放弃了。
回来后一直在忙着各种聚会,也就放下了,好在现在又能挤出时间来完整这篇总结。

2016年是最繁忙的一年了,也是人生最重要的一年。这一年无论是生活还是工作,都有比较多的事情发生。

首先,结婚了,不再单身。这意味以前一个人吃饱即可万事大吉的生活不再返了。新的开始,新的责任,
新的生活。一切都在新的节奏中慢慢去调整自己,适应当前的生活。

最大的感受可能就是个人研究东西的时间大幅减少了。再就是做一些决定的时候,要考虑担忧的方面也多起来了。
这也许就是一种成长。我们不可能永远都是个孩子,总有一天要长大,亲自挑起自己该肩负的担子。

现在回想之前的创业,就觉得的确是做什么事情要趁年轻呀。无论是在精力还是时间方面,都有很大的优势。
越长大,可能羁绊就越多了。

而我就是在这种羁绊的状态中,思考着工作的下一步该如何进展。

总结一年下来的工作,只能用碌碌无为而形容。而过去的多年,现在来看也算称得上碌碌无为了。
工作近5年了,但感觉自己进步甚微,不得不感慨,有个指路人的必要性。有时候,也许指路人的一句话,
就可以点醒自己,或者让自己少走弯路,而我却很少碰到这样的指路人。

越是焦急反而就越是混乱,真的不知道,这样状态什么时候是个了结。

当下,无论做什么,都感觉心有余而力不足。经常的去开导别人的我,现在回过头来看看自己,才发觉自己
才是个可笑之人。只能说,自己想办法开导开导自己,让自己快点走出泥潭吧。

2017,是乱?是迷?还是破?

0%