海运的博客

使用avahi配置mdns/dns-sd网络发现samba/ftp

发布时间:April 4, 2022 // 分类: // No Comments

配置avahi使用mdns/dns-sd零配置发现samba/smb共享存储:

apt install avahi-daemon
cat <<EOF > /etc/avahi/services/smb.service
<?xml version="1.0" standalone='no'?>
<!DOCTYPE service-group SYSTEM "avahi-service.dtd">
<service-group>
  <name replace-wildcards="yes">Samba</name>
  <service>
    <type>_smb._tcp</type>
    <host-name>smb.haiyun.me.local</host-name>
    <port>445</port>  
    <txt-record>path=/share</txt-record>
    <txt-record>u=guest</txt-record>
    <txt-record>p=pass</txt-record>
  </service>
</service-group>
EOF
echo '192.168.168.1 smb.haiyun.me.local' >> /etc/avahi/hosts 

ftp服务器:

cat <<EOF > /etc/avahi/services/ftp.service
<?xml version="1.0" standalone='no'?>
<!DOCTYPE service-group SYSTEM "avahi-service.dtd">
<service-group>
  <name replace-wildcards="yes">FTP</name>
  <service>
    <type>_ftp._tcp</type>
    <host-name>ftp.haiyun.me.local</host-name>
    <port>21</port>  
    <txt-record>path=/share</txt-record>
    <txt-record>u=guest</txt-record>
    <txt-record>p=pass</txt-record>
  </service>
</service-group>
EOF
echo '192.168.168.2 ftp.haiyun.me.local' >> /etc/avahi/hosts 

注意:
1.host-name可使用FQDN标准域名,但是smb有的客户端不识别。
2.avahi默认会为hosts内目标ip添加反查域名,所以当在/etc/avahi/hosts内添加多个域名指向同一ip时会出错:

Static host name smb.haiyun.me.local: avahi_server_add_address failure: Local name collision

可以使用avahi-publish手工发布域名且不添加ip反查域名:

avahi-publish -a -R smb.haiyun.me.local 168.168.168.1

也可以修改systemd service让avahi启动时自动启动avahi-publish:

apt install avahi-utils
cat <<EOF > /usr/local/bin/avahi-host.sh
#!/bin/bash
/usr/bin/avahi-publish -a -R smb.haiyun.me.local 168.168.168.1 > /dev/null 2>&1 &
EOF
chmod +x /usr/local/bin/avahi-host.sh
sed -i '/ExecStart/aExecStartPost=\/usr\/local\/bin\/avahi-host\.sh' /lib/systemd/system/avahi-daemon.service 
systemctl daemon-reload 
systemctl restart avahi-daemon.service 
#手工修改添加ExecStartPost=/usr/local/bin/avahi-host.sh
#systemctl edit --full avahi-daemon.service 

使用dig测试mdns/dns-sd:

dig -p 5353 @224.0.0.251 ftp.haiyun.me.local
dig -p 5353 @224.0.0.251 _smb._tcp.local ptr
dns-sd -B _services._dns-sd._udp
dns-sd -B _smb._tcp
dns-sd -Z _smb._tcp
dns-sd -Q smb.haiyun.me.local
dns-sd -Q smb.haiyun.me.local aaaa

当同一ip有多个服务时也可使用基于golang dnssd的mdns/dns-sd服务:

package main

import (
        "context"
        "encoding/json"
        "fmt"
        "github.com/brutella/dnssd"
        "net"
        "os"
        "os/signal"
        "time"
)

type service struct {
        Name   string
        Type   string
        Domain string
        Host   string
        Ips    []string
        Port   int
        Text   map[string]interface{}
}

type config struct {
        Service []service
}

func main() {
        cfg_str, err := os.ReadFile("config.json")
        if err != nil {
                fmt.Println("load config file error:", err)
                os.Exit(1)
        }
        var cfg config
        err = json.Unmarshal(cfg_str, &cfg)
        if err != nil {
                fmt.Println("decode json err:", err)
                os.Exit(1)
        }

        if resp, err := dnssd.NewResponder(); err != nil {
                fmt.Println(err)
        } else {
                for _, v := range cfg.Service {
                        var ips []net.IP
                        for _, ip := range v.Ips {
                                ips = append(ips, net.ParseIP(ip))
                        }
                        text := make(map[string]string)
                        for key, value := range v.Text {
                                text[key] = fmt.Sprintf("%v", value)
                        }
                        sd_cfg := dnssd.Config{
                                Name:   v.Name,
                                Type:   v.Type,
                                Domain: v.Domain,
                                Host:   v.Host,
                                IPs:    ips,
                                Port:   v.Port,
                                Text:   text,
                        }

                        srv, err := dnssd.NewService(sd_cfg)
                        if err != nil {
                                fmt.Println(err)
                        }
                        go func() {
                                time.Sleep(1 * time.Second)
                                handle, err := resp.Add(srv)
                                if err != nil {
                                        fmt.Println(err)
                                } else {
                                        fmt.Printf("%s  Got a reply for service %s: Name now registered and active\n", time.Now().Format("2006-01-02 15:04:05"), handle.Service().ServiceInstanceName())
                                }
                        }()
                }

                ctx, cancel := context.WithCancel(context.Background())
                defer cancel()
                go func() {
                        stop := make(chan os.Signal, 1)
                        signal.Notify(stop, os.Interrupt)

                        select {
                        case <-stop:
                                cancel()
                        }
                }()

                err = resp.Respond(ctx)
                if err != nil {
                        fmt.Println(err)
                }
        }
}

配置文件保存到同目录config.json:

{
  "service":[
    {
      "name":"Samba",
      "type":"_smb._tcp",
      "domain":"local",
      "host":"smb.haiyun.me",
      "ips":[
        "10.0.0.1",
        "fdb2:808c:d0ef:0:20c:29ff:fe7c:264d"
      ],
      "port":445,
      "text":{
        "u":"user",
        "p":"pass",
        "path":"/share"
      }
    },
    {
      "name":"Ftp",
      "type":"_ftp._tcp",
      "domain":"local",
      "host":"ftp.haiyun.me",
      "ips":[
        "10.0.0.1",
        "fdb2:808c:d0ef:0:20c:29ff:fe7c:264d"
      ],
      "port":21,
      "text":{
        "u":"user",
        "p":"pass",
        "path":"/share"
      }
    }
  ]
}

更多服务类型及参数见:
http://www.dns-sd.org/ServiceTypes.html
本地多个不同网段mdns中继:
https://www.haiyun.me/archives/1439.html
参考:
https://github.com/brutella/dnssd
https://kodi.wiki/view/Avahi_Zeroconf
https://linux.die.net/man/5/avahi.service
https://github.com/lathiat/avahi/issues/40
https://pi3g.com/2019/04/10/avahi-how-to-assign-several-local-names-to-same-ip/

golang/php使用aes加密文件

发布时间:April 28, 2020 // 分类:PHP // 1 Comment

兼容于golang语言版本:https://github.com/eliben/code-for-blog/blob/master/2019/aes-encrypt-file/aes-file.go
使用aes-256-cbc加密,加密文件前8字节为原始文件大小数字,跟着是16字节随机字节用作iv,再跟着是先补全的明文加密内容。

<?php
function decrypt($key, $infilename, $outfilename = "") {
  if (!$outfilename) {
    $outfilename = $infilename.'.dec';
  }
  $cipher_str = file_get_contents($infilename);
  if (!$cipher_str) {
    die("empty file".PHP_EOL);
  }
  $file_size = substr($cipher_str, 0, 8);
  $file_size = unpack("P", $file_size);
  $iv = substr($cipher_str, 8, 16);
  $cipher_str = substr($cipher_str, 24);
  $plain_str = openssl_decrypt($cipher_str, "aes-256-cbc", $key, 3, $iv);
  $plain_str = substr($plain_str, 0, $file_size[1]);
  file_put_contents($outfilename, $plain_str);
  echo "decrypted output file ".$outfilename.PHP_EOL;
}
function encrypt($key, $infilename, $outfilename) {
  if (!$outfilename) {
    $outfilename = $infilename.'.enc';
  }
  $plain_str = file_get_contents($infilename);
  if (!$plain_str) {
    die("empty file".PHP_EOL);
  }
  $file_size = strlen($plain_str);
  $file_size = pack("P", $file_size);
  $iv = random_bytes(16);
  if (strlen($plain_str) % 16 != 0) {
    $bytesToPad = 16 - (strlen($plain_str) % 16);
    $plain_str = $plain_str . random_bytes($bytesToPad);
  }
  $cipher_str = openssl_encrypt($plain_str, "aes-256-cbc", $key, 3, $iv);
  $cipher_str = $file_size.$iv.$cipher_str;
  file_put_contents($outfilename, $cipher_str);
  echo "encrypted output file ".$outfilename.PHP_EOL;
}
$arg = getopt('e::d::k:i:o:');
if (isset($arg['i']) && !empty($arg['i'])) {
  $infilename = $arg['i'];
} else {
  die("please input filename".PHP_EOL);
}
if (isset($arg['o']) && !empty($arg['o'])) {
  $outfilename = $arg['o'];
} else {
  $outfilename = "";
}
if (isset($arg['k']) && !empty($arg['k'])) {
  $key = $arg['k'];
} else {
  $key = "pass";
}
$key = hash('sha256', $key, true);
if (isset($arg['d'])) {
  decrypt($key, $infilename, $outfilename);
} elseif (isset($arg['e'])) {
  encrypt($key, $infilename, $outfilename);
}

golang cbc使用pkcs7补全版:

package main

import (
        "bytes"
        "crypto/aes"
        "crypto/cipher"
        "crypto/rand"
        "crypto/sha256"
        //"encoding/binary"
        "flag"
        "fmt"
        "io/ioutil"
        "log"
        "os"
)
func pkcs7strip(data *[]byte, blockSize int) (int, error) {
        length := len(*data)
        if length == 0 {
                return 0, fmt.Errorf("pkcs7: Data is empty")
        }
        if length%blockSize != 0 {
                return 0, fmt.Errorf("pkcs7: Data is not block-aligned")
        }
        padLen := int((*data)[length-1])
        ref := bytes.Repeat([]byte{byte(padLen)}, padLen)
        if padLen > blockSize || padLen == 0 || !bytes.HasSuffix(*data, ref) {
                return 0, fmt.Errorf("pkcs7: Invalid padding")
        }
        return length-padLen, nil
}

func pkcs7pad(data *[]byte, blockSize int) error {
        if blockSize < 0 || blockSize > 256 {
                return fmt.Errorf("pkcs7: Invalid block size %d", blockSize)
        } else {
                padLen := blockSize - len(*data) % blockSize
                padding := bytes.Repeat([]byte{byte(padLen)}, padLen)
                *data = append(*data, padding...)
                return nil
        }
}

func encryptFile(key []byte, filename string, outFilename string) (string, error) {
        if len(outFilename) == 0 {
                outFilename = filename + ".enc"
        }

        plaintext, err := ioutil.ReadFile(filename)
        if err != nil {
                return "", err
        }

        of, err := os.Create(outFilename)
        if err != nil {
                return "", err
        }
        defer of.Close()

        if  err := pkcs7pad(&plaintext, aes.BlockSize) ; err != nil {
                return "", err
        }

        iv := make([]byte, aes.BlockSize)
        if _, err := rand.Read(iv); err != nil {
                return "", err
        }
        if _, err = of.Write(iv); err != nil {
                return "", err
        }

        ciphertext := make([]byte, len(plaintext))

        block, err := aes.NewCipher(key)
        if err != nil {
                return "", err
        }
        mode := cipher.NewCBCEncrypter(block, iv)
        mode.CryptBlocks(ciphertext, plaintext)

        if _, err = of.Write(ciphertext); err != nil {
                return "", err
        }
        return outFilename, nil
}

func decryptFile(key []byte, filename string, outFilename string) (string, error) {
        if len(outFilename) == 0 {
                outFilename = filename + ".dec"
        }

        ciphertext, err := ioutil.ReadFile(filename)
        if err != nil {
                return "", err
        }

        of, err := os.Create(outFilename)
        if err != nil {
                return "", err
        }
        defer of.Close()

        buf := bytes.NewReader(ciphertext)
        iv := make([]byte, aes.BlockSize)
        if _, err = buf.Read(iv); err != nil {
                return "", err
        }

        paddedSize := len(ciphertext) - aes.BlockSize
        if paddedSize%aes.BlockSize != 0 {
                return "", fmt.Errorf("want padded plaintext size to be aligned to block size")
        }
        plaintext := make([]byte, paddedSize)

        block, err := aes.NewCipher(key)
        if err != nil {
                return "", err
        }
        mode := cipher.NewCBCDecrypter(block, iv)
        mode.CryptBlocks(plaintext, ciphertext[aes.BlockSize:])

        origSize, err := pkcs7strip(&plaintext, aes.BlockSize)
        if err != nil {
          return "", err
        }
        if _, err := of.Write(plaintext[:origSize]); err != nil {
                return "", err
        }
        return outFilename, nil
}

func main() {
        var keyFlag string
        encFlag := flag.Bool("e", false, "encrypt")
        decFlag := flag.Bool("d", false, "decrypt")
        flag.StringVar(&keyFlag, "k", "password", "encrypt password")

        flag.Parse()
        filename := flag.Arg(0)

        key := sha256.Sum256([]byte(keyFlag))
        fmt.Println("use password", keyFlag)
        //fmt.Println(key)
        if *encFlag {
                outFilename, err := encryptFile(key[:], filename, "")
                if err != nil {
                        log.Fatal(err)
                }
                fmt.Println("Encrypted output file:", outFilename)
        } else if *decFlag {
                outFilename, err := decryptFile(key[:], filename, "")
                if err != nil {
                        log.Fatal(err)
                }
                fmt.Println("Decrypted output file:", outFilename)
        } else {
                fmt.Println(flag.Usage)
                os.Exit(1)
        }
}

golang使用aes cfb:

package main

import (
        //"bytes"
        "crypto/aes"
        "crypto/cipher"
        "crypto/rand"
        "crypto/sha256"
        //"encoding/binary"
        "flag"
        "fmt"
        "io"
        "io/ioutil"
        "log"
        "os"
)

func encryptFile(key []byte, filename string, outFilename string) (string, error) {
        if len(outFilename) == 0 {
                outFilename = filename + ".enc"
        }

        plaintext, err := ioutil.ReadFile(filename)
        if err != nil {
                return "", err
        }

        of, err := os.Create(outFilename)
        if err != nil {
                return "", err
        }
        defer of.Close()

        block, err := aes.NewCipher(key)
        if err != nil {
                panic(err)
        }

        ciphertext := make([]byte, aes.BlockSize+len(plaintext))
        iv := ciphertext[:aes.BlockSize]
        if _, err := io.ReadFull(rand.Reader, iv); err != nil {
                panic(err)
        }

        stream := cipher.NewCFBEncrypter(block, iv)
        stream.XORKeyStream(ciphertext[aes.BlockSize:], plaintext)

        fmt.Printf("iv:\t%x\n", iv)
        fmt.Printf("cipher text:\t%x\n", ciphertext)
        if _, err = of.Write(ciphertext); err != nil {
                return "", err
        }
        return outFilename, nil
}

func decryptFile(key []byte, filename string, outFilename string) (string, error) {
        if len(outFilename) == 0 {
                outFilename = filename + ".dec"
        }

        ciphertext, err := ioutil.ReadFile(filename)
        fmt.Printf("cipher text:\t%x\n", ciphertext)
        if err != nil {
                return "", err
        }

        of, err := os.Create(outFilename)
        if err != nil {
                return "", err
        }
        defer of.Close()

        block, err := aes.NewCipher(key)
        if err != nil {
                panic(err)
        }

        iv := ciphertext[:aes.BlockSize]
        ciphertext = ciphertext[aes.BlockSize:]
        fmt.Printf("cipher2 text:\t%x\n", ciphertext)
        fmt.Printf("iv:\t%x\n", iv)

        stream := cipher.NewCFBDecrypter(block, iv)

        stream.XORKeyStream(ciphertext, ciphertext)

        if _, err := of.Write(ciphertext); err != nil {
                return "", err
        }
        return outFilename, nil
}

func main() {
        var keyFlag string
        encFlag := flag.Bool("e", false, "encrypt")
        decFlag := flag.Bool("d", false, "decrypt")
        flag.StringVar(&keyFlag, "k", "password", "encrypt password")

        flag.Parse()
        filename := flag.Arg(0)

        key := sha256.Sum256([]byte(keyFlag))
        fmt.Println("use password", keyFlag)
        //fmt.Println(key)
        if *encFlag {
                outFilename, err := encryptFile(key[:], filename, "")
                if err != nil {
                        log.Fatal(err)
                }
                fmt.Println("Encrypted output file:", outFilename)
        } else if *decFlag {
                outFilename, err := decryptFile(key[:], filename, "")
                if err != nil {
                        log.Fatal(err)
                }
                fmt.Println("Decrypted output file:", outFilename)
        } else {
                fmt.Println(flag.Usage)
                os.Exit(1)
        }
}
分类
最新文章
最近回复
  • 1: 方法一ngtcp2要改下:./configure PKG_CONFIG_PATH=/usr/l...
  • 海运: 关闭服务器
  • 海风: override.battery.charge.low以及override.battery.r...
  • koldjf: 不能过滤
  • 杰迪武士: 此文甚好甚强巨,依照此文在树莓派2 + Rasbian上部署成功 感谢博主美文共赏
  • 海运: ups不知有没选项可设置此参数,不过你可以在另外一台电脑上安装nut客户端自动关机。
  • kgami: 想请教一下,设置了的电脑自动关机之后,几秒后UPS怎么也跟着关机了,导致另外一台电脑没关机就断...
  • 海运: 写的很详细了啊,/etc/nut/hosts.conf用以nut-cgi连接nut服务器参数,...
  • ryan: 请问下nginx配置好了,怎么和这个nut链接呢?最后可视化管理这块能给个详细一点的教程么?谢谢。
  • 1: /etc/config/fstab配置文件 https://openwrt.org/zh/do...