Contents
×
DDNS项目文档
  •  
    1 方案概述
  •  
    2 编译和运行PowerDns
    •  
      2.1 安装依赖库
    •  
      2.2 编译
    •  
      2.3 如有`dnsmasq`在运行,先停止
    •  
      2.4 建库
    •  
      2.5 插入数据
    •  
      2.6 运行pdns_server
    •  
      2.7 测试
  •  
    3 Yii2 basic模板项目
    •  
      3.1 初始化配置
    •  
      3.2 restFul接口 - 注册域名
      •  
        3.2.1 用例图
      •  
        3.2.2 用例说明
      •  
        3.2.3 用例流程图
      •  
        3.2.4 测试方法
      •  
        3.2.5 客户端bash脚本
      •  
        3.2.6 接口安全性
    •  
      3.3 restFul接口 - 上报ip地址
      •  
        3.3.1 用例图(和注册公用)
      •  
        3.3.2 用例说明
      •  
        3.3.3 用例图
  •  
    4 服务器需求及部署

DDNS项目文档

Keywords: 设计,ddns
Original published url: http://www.geiliedu.com/manual/23
Shared: Visits: 135 Created at: 2016.06.29 Updated at: 2016.10.20 Author: geiliedu(282055808@qq.com)

1 方案概述

NAS产品的DDNS服务,包括DNS服务、NAS终端的域名注册、NAS终端的IP上报三个部分。其中DNS服务使用PowerDns,本项目直接编译源码安装,这样不会影响服务器的其他软件(比如,用apt-get install的方式安装,会影响lnmp的mysql)。然后用yii2的restFul接口提供NAS的域名注册和IP上报服务。

2 编译和运行PowerDns

下载最新的源码(release中下载,而不是git中获取),到这里下载

编译的方法参照这里,以及阅读代码中的README,步骤如下:

2.1 安装依赖库

$ sudo apt-get install autoconf automake bison flex g++ git libboost-all-dev libtool make pkg-config ragel libmysqlclient-dev

2.2 编译

$ ./configure --with-modules="bind gmysql gpgsql"
$ make
$ sudo make install

2.3 如有dnsmasq在运行,先停止

ubuntu 16.04自带的DNS服务叫dnsmasq,这样会占用53端口,所以需要停止。方法是打开文件/etc/NetworkManager/NetworkManager.conf,注释掉dns=dnsmasq这行:

[main]
plugins=ifupdown,keyfile,ofono
#dns=dnsmasq

[ifupdown]
managed=false

2.4 建库

CREATE TABLE domains (
  id                    INT AUTO_INCREMENT,
  name                  VARCHAR(160) NOT NULL,
  master                VARCHAR(128) DEFAULT NULL,
  last_check            INT DEFAULT NULL,
  type                  VARCHAR(6) NOT NULL,
  notified_serial       INT DEFAULT NULL,
  account               VARCHAR(40) DEFAULT NULL,
  PRIMARY KEY (id)
) Engine=InnoDB;

CREATE UNIQUE INDEX name_index ON domains(name);


CREATE TABLE records (
  id                    INT AUTO_INCREMENT,
  domain_id             INT DEFAULT NULL,
  name                  VARCHAR(160) DEFAULT NULL,
  type                  VARCHAR(10) DEFAULT NULL,
  content               VARCHAR(10240) DEFAULT NULL,
  ttl                   INT DEFAULT NULL,
  prio                  INT DEFAULT NULL,
  change_date           INT DEFAULT NULL,
  disabled              TINYINT(1) DEFAULT 0,
  ordername             VARCHAR(160) BINARY DEFAULT NULL,
  auth                  TINYINT(1) DEFAULT 1,
  PRIMARY KEY (id)
) Engine=InnoDB;

CREATE INDEX nametype_index ON records(name,type);
CREATE INDEX domain_id ON records(domain_id);
CREATE INDEX recordorder ON records (domain_id, ordername);


CREATE TABLE supermasters (
  ip                    VARCHAR(64) NOT NULL,
  nameserver            VARCHAR(160) NOT NULL,
  account               VARCHAR(40) NOT NULL,
  PRIMARY KEY (ip, nameserver)
) Engine=InnoDB;


CREATE TABLE comments (
  id                    INT AUTO_INCREMENT,
  domain_id             INT NOT NULL,
  name                  VARCHAR(160) NOT NULL,
  type                  VARCHAR(10) NOT NULL,
  modified_at           INT NOT NULL,
  account               VARCHAR(40) NOT NULL,
  comment               VARCHAR(10240) NOT NULL,
  PRIMARY KEY (id)
) Engine=InnoDB;

CREATE INDEX comments_domain_id_idx ON comments (domain_id);
CREATE INDEX comments_name_type_idx ON comments (name, type);
CREATE INDEX comments_order_idx ON comments (domain_id, modified_at);


CREATE TABLE domainmetadata (
  id                    INT AUTO_INCREMENT,
  domain_id             INT NOT NULL,
  kind                  VARCHAR(32),
  content               TEXT,
  PRIMARY KEY (id)
) Engine=InnoDB;

CREATE INDEX domainmetadata_idx ON domainmetadata (domain_id, kind);


CREATE TABLE cryptokeys (
  id                    INT AUTO_INCREMENT,
  domain_id             INT NOT NULL,
  flags                 INT NOT NULL,
  active                BOOL,
  content               TEXT,
  PRIMARY KEY(id)
) Engine=InnoDB;

CREATE INDEX domainidindex ON cryptokeys(domain_id);


CREATE TABLE tsigkeys (
  id                    INT AUTO_INCREMENT,
  name                  VARCHAR(160),
  algorithm             VARCHAR(50),
  secret                VARCHAR(160),
  PRIMARY KEY (id)
) Engine=InnoDB;

CREATE UNIQUE INDEX namealgoindex ON tsigkeys(name, algorithm);

2.5 插入数据

INSERT INTO domains (name, type) values ('hskedu.cn', 'NATIVE');
INSERT INTO records (domain_id, name, content, type,ttl,prio)
VALUES (1,'hskedu.cn','121.40.171.102','SOA',86400,NULL);
INSERT INTO records (domain_id, name, content, type,ttl,prio)
VALUES (1,'hskedu.cn','121.40.171.102','NS',86400,NULL);
INSERT INTO records (domain_id, name, content, type,ttl,prio)
VALUES (1,'www.hskedu.cn','192.0.2.10','A',120,NULL);
INSERT INTO records (domain_id, name, content, type,ttl,prio)
VALUES (1,'mail.hskedu.cn','192.0.2.12','A',120,NULL);
INSERT INTO records (domain_id, name, content, type,ttl,prio)
VALUES (1,'localhost.hskedu.cn','127.0.0.1','A',120,NULL);
INSERT INTO records (domain_id, name, content, type,ttl,prio)
VALUES (1,'hskedu.cn','mail.hskedu.cn','MX',120,25);

INSERT INTO records (domain_id, name, content, type,ttl,prio)
VALUES (1,'host1.hskedu.cn','192.0.2.21','A',120,NULL);
INSERT INTO records (domain_id, name, content, type,ttl,prio)
VALUES (1,'host2.hskedu.cn','192.0.2.22','A',120,NULL);
INSERT INTO records (domain_id, name, content, type,ttl,prio)
VALUES (1,'host3.hskedu.cn','192.0.2.23','A',120,NULL);
INSERT INTO records (domain_id, name, content, type,ttl,prio)
VALUES (1,'ddnsreg.hskedu.cn','121.40.171.102','A',120,NULL);

注意:以上SOA,NS记录很重要,如果没有,dns好像不能工作,只是dig查询测试的时候回出错。

2.6 运行pdns_server

不适用配置文件运行pdns的方法是:

sudo pdns_server --no-config --daemon=no --launch=gmysql --gmysql-user=root --gmysql-password= --gmysql-dbname=pdns --guardian=no --control-console --loglevel=9

如何适用配置文件运行的方法,需再研究。大致方法是将gmysql的参数放到配置文件/usr/local/etc/pdns.conf中。默认没有这个文件,只有make install拷贝的/usr/local/etc/pdns.conf-dist

2.7 测试

可以在本机(跑pdns_server的服务器)上用dig +short host4.hskedu.cn @127.0.0.1这样测试pdns_server是否工作正常。如果正常,则可以在域名提供商那里设置域名服务器了。域名服务器设置OK后,可以在任何地方都能使用dns服务了,例如可以在其他机器上ping host4.hskedu.cn

3 Yii2 basic模板项目

3.1 初始化配置

  • 确定域名为ddnsreg.hskedu.cn

  • 配置config/db.php可连接数据库pdns

  • 在index.php所在目录,生成.htaccess

.-[fengyh@iZ23ed2he9xZ:~/web/ddnsreg.hskedu.cn/web] (master)
↳ $ ls -la
total 40
drwxrwxr-x  4 fengyh fengyh 4096 Jun 23 17:41 .
drwxrwxr-x 14 fengyh fengyh 4096 Jun 22 20:57 ..
drwxrwxr-x  5 fengyh fengyh 4096 Jun 22 21:56 assets
drwxrwxr-x  2 fengyh fengyh 4096 Jun 22 20:57 css
-rw-rw-r--  1 fengyh fengyh  318 Jun 22 20:57 favicon.ico
-rw-rw-r--  1 fengyh fengyh  315 Jun 23 17:41 .htaccess
-rw-rw-r--  1 fengyh fengyh  370 Jun 22 20:57 index.php
-rw-rw-r--  1 fengyh fengyh  525 Jun 22 20:57 index-test.php
-rw-rw-r--  1 fengyh fengyh   18 Jun 22 21:52 phpinfo.php
-rw-rw-r--  1 fengyh fengyh   23 Jun 22 20:57 robots.txt

其中.htaccess的内容是:

Options +FollowSymLinks
IndexIgnore */*
RewriteEngine on

AddType video/ogg .ogv
AddType video/mp4 .mp4
AddType video/webm .webm

# if a directory or a file exists, use it directly
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d

# otherwise forward it to index.php

RewriteRule . index.php
  • 修改config/web.php,设置url美化,以及gii的允许地址
$ cat config/web.php 
<?php

$params = require(__DIR__ . '/params.php');

$config = [
    'id' => 'basic',
    'basePath' => dirname(__DIR__),
    'bootstrap' => ['log'],
    'components' => [
        'request' => [
            // !!! insert a secret key in the following (if it is empty) - this is required by cookie validation
            'cookieValidationKey' => '-zaAVnC_cQD7EaqXrREZ1kQfebN8ynav',
        ],
        'cache' => [
            'class' => 'yii\caching\FileCache',
        ],
        'user' => [
            'identityClass' => 'app\models\User',
            'enableAutoLogin' => true,
        ],
        'errorHandler' => [
            'errorAction' => 'site/error',
        ],
        'mailer' => [
            'class' => 'yii\swiftmailer\Mailer',
            // send all mails to a file by default. You have to set
            // 'useFileTransport' to false and configure a transport
            // for the mailer to send real emails.
            'useFileTransport' => true,
        ],
        'log' => [
            'traceLevel' => YII_DEBUG ? 3 : 0,
            'targets' => [
                [
                    'class' => 'yii\log\FileTarget',
                    'levels' => ['error', 'warning'],
                ],
            ],
        ],
        'db' => require(__DIR__ . '/db.php'),
        'urlManager' => [
            'enablePrettyUrl' => true,
            'showScriptName' => false,
            'rules' => [
            ],
        ],
    ],
    'params' => $params,
];

if (YII_ENV_DEV) {
    // configuration adjustments for 'dev' environment
    $config['bootstrap'][] = 'debug';
    $config['modules']['debug'] = [
        'class' => 'yii\debug\Module',
    ];

    $config['bootstrap'][] = 'gii';
    $config['modules']['gii'] = [
        'class' => 'yii\gii\Module',
        'allowedIPs' => ['127.0.0.1', '::1', '192.168.*.*', '192.168.178.20'] // adjust this to your needs
    ];
}

return $config;
  • 配置nginx虚拟站
$ cat ddnsreg.hskedu.cn.conf 
server {
listen 80;
server_name ddnsreg.hskedu.cn;
access_log /data/wwwlogs/ddnsreg.hskedu.cn_nginx.log combined;
index index.html index.htm index.php;
root /home/fengyh/web/ddnsreg.hskedu.cn/web;
if ($host != ddnsreg.hskedu.cn) {
        rewrite ^/(.*)$ $scheme://ddnsreg.hskedu.cn/$1 permanent;
        }

location / {
    try_files $uri @apache;
    }
location @apache {
    proxy_pass http://127.0.0.1:88;
    include proxy.conf;
    }
location ~ .*\.(php|php5|cgi|pl)?$ {
    proxy_pass http://127.0.0.1:88;
    include proxy.conf;
    }
location ~ .*\.(gif|jpg|jpeg|png|bmp|swf|flv|ico)$ {
    expires 30d;
    access_log off;
    }
location ~ .*\.(js|css)?$ {
    expires 7d;
    access_log off;
    }
}
  • 配置apache虚拟站
$ cat ddnsreg.hskedu.cn.conf 
<VirtualHost *:88>
    ServerAdmin gordon.von@qq.com
    DocumentRoot "/home/fengyh/web/ddnsreg.hskedu.cn/web"
    ServerName ddnsreg.hskedu.cn
    ServerAlias ddnsreg.hskedu.cn
    ErrorLog "/data/wwwlogs/ddnsreg.hskedu.cn_error_apache.log"
    CustomLog "/data/wwwlogs/ddnsreg.hskedu.cn_apache.log" common
<Directory "/home/fengyh/web/ddnsreg.hskedu.cn/web">
    SetOutputFilter DEFLATE
    Options FollowSymLinks ExecCGI
    Require all granted
    AllowOverride All
    Order allow,deny
    Allow from all
    DirectoryIndex index.html index.php
</Directory>
</VirtualHost>

3.2 restFul接口 - 注册域名

3.2.1 用例图

注册及更新IP脚本(bash程序)flash加密区ddns-regddns-update-ip读取/写入token注册更新IP

3.2.2 用例说明

  • Use case name: 注册域名
  • Summary: NAS端用bash脚本加成注册和上报IP的过程,上报的IP包括内网IP和公网IP。注册只做一次,且只允许在工厂内做首次注册最理想,后台通过过滤remote ip的方式做限制(如果工厂没有固定IP的互联网连接,则次方案不成立),最低限度也要求bash脚本和后台约定一个秘钥,用这个秘钥做签名(因为终端的token是域名注册完成后才获得token的,所以注册域名本身不能使用token做签名)。
  • Actor: bash脚本(开机自启动)
  • Precondition:
  • Main sequence:

    1. 取mac地址(约定使用wifi mac),取内网ip地址(LAN或者WIFI的ip),取域名(研发测试使用hskedu.cn,正式交付客户是用客户的DNS域名)
    2. 尝试读取token
    3. 如果没有token,说明还没有注册域名,这时通过curl命令,用http协议向ddns服务器post域名注册请求。
    4. ddns注册服务器收到请求后,判断该mac是否已经被占用,如没有则写数据库记录,并标识该注册等待NAS终端确认。
    5. ddns注册服务器返回结果给NAS,如果成功则将返回结果中的token值写入NAS。要求可永久保存。
    6. 然后用curl命令,向ddns服务器post注册成功的确信信息。
    7. ddns服务器收到确认信息,表明终端已经保存了token。
    
  • Alternative sequences:

    3A 上述3中,如果有token,说明已经注册过了,流程结束,转到定时上报IP地址的流程。
    5A 上述5中,如果失败,则分情况处理,①是网络问题,比如NAS没上网,则sleep 10秒后重试。
    5B 如果是mac已被占用,则打印信息,将错误状态写到/tmp/ddns-status,以便app查询或者串口调试。
    
  • Postcondition:

3.2.3 用例流程图

注意:流程图中的序号和用例说明的序号没有对应关系。

bash定时器bash定时器ddns-regddns-regpdns.recordspdns.records[1]读取token[2]REGREQ [注册请求]本地没有token时,才发送注册请求[3]查找该mac,是否已经注册过[4]返回查询结果alt[已经注册过][5]‘result’=>’ERROR’, …已对应记录,且域名标志已确认终端掉token,客服改标志?[6]屏幕打印失败信息[7]/tmp/ddns-status写错误信息alt[MAC已经被占用][8]结束[9]sleep 30s, 重复[2][否则][10]写记录,注册新域名等待确认[11]写数据库结果[12]‘result’=>’OK’,‘token’=>’hash值’, …[13]REGCFM [注册确认][14]设置域名标志为已确认[15]写数据库结果[16]‘result’=>’OK’,‘token’=>’hash值’, …[17]写token, 要求掉电保存[18]屏幕打印成功信息[19]进入定时更新ip流程

3.2.4 测试方法

curl -X POST -H "Accept: application/json"  -d 'cmd=REGREQ&mac=fa:4b:69:51:99:d0&domain=hskedu.cn'  'http://ddnsreg.hskedu.cn/api/ddns-reg'

3.2.5 客户端bash脚本

最后写客户端bash脚本,其中写token的方法,暂时写入/tmp/ddnstoken文件,待NAS终端提供保存方法再改进。

3.2.6 接口安全性

注册接口可限制remote id实现只允许出厂前做注册,要做到这一点,需要配置后台的过滤ip。

对于掉token的终端,交由客服后台修改域名确认标记。

3.3 restFul接口 - 上报ip地址

3.3.1 用例图(和注册公用)

注册及更新IP脚本(bash程序)flash加密区ddns-regddns-update-ip读取/写入token注册更新IP

3.3.2 用例说明

(略)

3.3.3 用例图

bash定时器bash定时器ddns-update-ipddns-update-ippdns.recordspdns.records[1]读取token,mac,domainalt[没有取到token][2]转到注册域名流程[3]用token,mac,timestamp等计算签名[4]UPDATEREQ [IP更新请求][5]验证合法性alt[验证合法性失败][6]‘result’=>’ERROR’,…[7]/tmp/ddns-status写错误信息[8]结束(不循环)[9]取公网IP, 取请求数据mac,私网IP等[10]写数据库的records表[11]返回结果alt[mac没有注册域名][12]‘result’=>’ERROR’,…[13]/tmp/ddns-status写错误信息[14]结束(不循环)[15]‘result’=>’OK’,…[16]sleep 30s[17]跳转[3]继续循环

4 服务器需求及部署

  • DDNS服务器[客户购买]:由于需要安装ddns软件以及nas注册域名上报ip的程序,需要一台独立的部署在公网的服务器。建议使用amazon aws的ec2服务器。参考: https://aws.amazon.com/ec2/pricing/on-demand/ pic 初期可选择配置低的,后期根据需要变配。

  • DDNS的主域名[客户购买]: 该域名有些特别,建议专门重新注册一个。① 该域名专供NAS使用,例如现在研发使用的是hskedu.cn;② 需要在域名商平台上将该域名的二级域名的解析转到DDNS服务器,例如hskedu.cn所有二级域名的解析,都是由dns.hskedu.cn(研发用服务器)负责。③ 购买前咨询一下域名商,是否能将解析转交给自建服务器处理,建议选择https://www.godaddy.com这类知名域名商。hskedu.cn的域名商是阿里云万网(www.net.cn)。

  • 第三方域名解析的设定[客户设定]: 请到域名商的管理页面做设定,将域名解析转交给DDNS服务器

  • DDNS服务器的OS[客户购买服务器时选择]: 最新版的ubuntu server 16.04LTS。

  • DDNS服务器的安全规则[客户购买服务器后的设定]: 需要开启ssh, http, dns等相关的端口。这些端口可能会需要在空间商(例如amazon aws)的管理页面设定。

  • 远程访问及安装部署[研发人员负责]: 研发人员需要远程访问,并安装软件,这个期间需要这台服务器的root权限。