Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • ai3/tools/acmeserver
  • godog/acmeserver
  • svp-bot/acmeserver
3 results
Show changes
Showing
with 1035 additions and 118 deletions
package clientutil
import (
"fmt"
"sync"
"time"
)
var dnsWatcherInterval = 1 * time.Minute
// A DNSWatcher monitors a DNS name for changes, constantly attempting
// to resolve it every minute and notifying a channel when the list of
// returned IP addresses changes. All addresses must be in host:port
// format.
type DNSWatcher struct {
hostport string
resolver resolver
addrs []string
updateCh chan []string
stopCh chan struct{}
}
// NewDNSWatcher creates a new DNSWatcher.
func NewDNSWatcher(hostport string) (*DNSWatcher, error) {
return newDNSWatcherWithResolver(hostport, defaultResolver)
}
func newDNSWatcherWithResolver(hostport string, resolver resolver) (*DNSWatcher, error) {
// Resolve names once before returning. Return a fatal error
// when there are no results, as it may indicate a syntax
// error in hostport.
addrs := resolver.ResolveIP(hostport)
if len(addrs) == 0 {
return nil, fmt.Errorf("can't resolve %s", hostport)
}
w := &DNSWatcher{
hostport: hostport,
resolver: resolver,
addrs: addrs,
updateCh: make(chan []string, 10),
stopCh: make(chan struct{}),
}
w.updateCh <- addrs
go w.loop()
return w, nil
}
// Stop the watcher.
func (w *DNSWatcher) Stop() {
close(w.stopCh)
}
// Changes returns a channel where the resolved addresses are sent
// whenever they change.
func (w *DNSWatcher) Changes() <-chan []string {
return w.updateCh
}
func (w *DNSWatcher) check() {
addrs := w.resolver.ResolveIP(w.hostport)
if len(addrs) > 0 && !addrListEqual(addrs, w.addrs) {
w.addrs = addrs
w.updateCh <- addrs
}
}
func (w *DNSWatcher) loop() {
defer close(w.updateCh)
tick := time.NewTicker(dnsWatcherInterval)
defer tick.Stop()
for {
select {
case <-tick.C:
w.check()
case <-w.stopCh:
return
}
}
}
type multiDNSUpdate struct {
hostport string
addrs []string
}
// A MultiDNSWatcher watches multiple addresses for DNS changes. The
// results are merged and returned as a list of addresses.
type MultiDNSWatcher struct {
watchers []*DNSWatcher
addrmap map[string][]string
faninCh chan multiDNSUpdate
updateCh chan []string
}
// NewMultiDNSWatcher creates a new MultiDNSWatcher.
func NewMultiDNSWatcher(hostports []string) (*MultiDNSWatcher, error) {
return newMultiDNSWatcherWithResolver(hostports, defaultResolver)
}
func newMultiDNSWatcherWithResolver(hostports []string, resolver resolver) (*MultiDNSWatcher, error) {
mw := &MultiDNSWatcher{
addrmap: make(map[string][]string),
faninCh: make(chan multiDNSUpdate, 10),
updateCh: make(chan []string, 10),
}
// All the MultiDNSWatcher does is multiplex updates from the
// individual DNSWatchers onto faninCh, then merging those
// updates with all the others and sending the result to
// updateCh.
go func() {
defer close(mw.updateCh)
for up := range mw.faninCh {
mw.addrmap[up.hostport] = up.addrs
mw.updateCh <- mw.allAddrs()
}
}()
var wg sync.WaitGroup
for _, hostport := range hostports {
w, err := newDNSWatcherWithResolver(hostport, resolver)
if err != nil {
return nil, err
}
mw.watchers = append(mw.watchers, w)
wg.Add(1)
go func(hostport string) {
for addrs := range w.Changes() {
mw.faninCh <- multiDNSUpdate{
hostport: hostport,
addrs: addrs,
}
}
wg.Done()
}(hostport)
}
go func() {
wg.Wait()
close(mw.faninCh)
}()
return mw, nil
}
func (mw *MultiDNSWatcher) allAddrs() []string {
var out []string
for _, addrs := range mw.addrmap {
out = append(out, addrs...)
}
return out
}
// Stop the watcher.
func (mw *MultiDNSWatcher) Stop() {
for _, w := range mw.watchers {
w.Stop()
}
}
// Changes returns a channel where the aggregate resolved addresses
// are sent whenever they change.
func (mw *MultiDNSWatcher) Changes() <-chan []string {
return mw.updateCh
}
func addrListEqual(a, b []string) bool {
if len(a) != len(b) {
return false
}
tmp := make(map[string]struct{})
for _, aa := range a {
tmp[aa] = struct{}{}
}
for _, bb := range b {
if _, ok := tmp[bb]; !ok {
return false
}
delete(tmp, bb)
}
return len(tmp) == 0
}
......@@ -2,6 +2,7 @@ package common
import (
"crypto/x509"
"fmt"
"io/ioutil"
)
......@@ -12,6 +13,8 @@ func LoadCA(path string) (*x509.CertPool, error) {
return nil, err
}
cas := x509.NewCertPool()
cas.AppendCertsFromPEM(data)
if !cas.AppendCertsFromPEM(data) {
return nil, fmt.Errorf("no certificates could be parsed in %s", path)
}
return cas, nil
}
File deleted
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"config:base"
]
}
......@@ -3,6 +3,7 @@ package serverutil
import (
"encoding/json"
"log"
"mime"
"net/http"
)
......@@ -14,7 +15,7 @@ func DecodeJSONRequest(w http.ResponseWriter, r *http.Request, obj interface{})
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return false
}
if r.Header.Get("Content-Type") != "application/json" {
if ct, _, _ := mime.ParseMediaType(r.Header.Get("Content-Type")); ct != "application/json" {
http.Error(w, "Need JSON request", http.StatusBadRequest)
return false
}
......@@ -29,19 +30,15 @@ func DecodeJSONRequest(w http.ResponseWriter, r *http.Request, obj interface{})
// EncodeJSONResponse writes an application/json response to w.
func EncodeJSONResponse(w http.ResponseWriter, obj interface{}) {
data, err := json.Marshal(obj)
if err != nil {
log.Printf("JSON serialization error: %v", err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json")
w.Header().Set("Pragma", "no-cache")
w.Header().Set("Cache-Control", "no-store")
w.Header().Set("Expires", "-1")
w.Header().Set("X-Content-Type-Options", "nosniff")
if _, err = w.Write(data); err != nil {
log.Printf("error writing response: %v", err)
err := json.NewEncoder(w).Encode(obj)
if err != nil {
log.Printf("error writing JSON response: %v", err)
// Too late to return an error to the client now.
}
}
......@@ -4,13 +4,12 @@ import (
"fmt"
"net"
"net/http"
"github.com/gorilla/handlers"
"strings"
)
type proxyHeaders struct {
wrap, phWrap http.Handler
forwarders []net.IPNet
wrap http.Handler
forwarders []net.IPNet
}
func newProxyHeaders(h http.Handler, trustedForwarders []string) (http.Handler, error) {
......@@ -20,7 +19,6 @@ func newProxyHeaders(h http.Handler, trustedForwarders []string) (http.Handler,
}
return &proxyHeaders{
wrap: h,
phWrap: handlers.ProxyHeaders(h),
forwarders: f,
}, nil
}
......@@ -32,12 +30,28 @@ func (p *proxyHeaders) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}
ip := net.ParseIP(host)
if ip != nil && matchIPNetList(ip, p.forwarders) {
p.phWrap.ServeHTTP(w, r)
return
if fwd := getForwardedIP(r); fwd != "" {
r.RemoteAddr = fwd
}
}
p.wrap.ServeHTTP(w, r)
}
// Parse the X-Real-IP or X-Forwarded-For headers, if present, to get
// the original client IP.
func getForwardedIP(r *http.Request) string {
if s := r.Header.Get("X-Real-IP"); s != "" {
return s
}
if s := r.Header.Get("X-Forwarded-For"); s != "" {
if n := strings.IndexByte(s, ','); n > 0 {
s = s[:n]
}
return s
}
return ""
}
func fullMask(ip net.IP) net.IPMask {
if ip.To4() == nil {
return net.CIDRMask(128, 128)
......
This diff is collapsed.
include:
- "https://git.autistici.org/pipelines/debian/raw/master/common.yml"
- "https://git.autistici.org/pipelines/images/test/golang/raw/master/ci.yml"
This diff is collapsed.
......@@ -122,3 +122,15 @@ directory* to run the repository tools on, and the final workflow is:
* run the metadata-generation tools on the staging dir;
* synchronize the data back to replds using the *sync* command.
## Usage
The Debian package comes with a
[replds-instance-create](debian/replds-instance-create) script that
can be used to set up multiple replds instances. For an instance named
*foo*, the script will setup the *replds@foo* systemd service, and it
will create the *replds-foo* user and group. Add users that need to
read the repository files to that group. The configuration will be
read from */etc/replds/foo.yml*.
Note that files created by the daemon will be world-readable by
default. Set the process umask if you wish to restrict this further.
......@@ -31,17 +31,17 @@ func newClient(config *clientutil.BackendConfig) (*client, error) {
func (c *client) internalGetNodes(ctx context.Context, req *internalGetNodesRequest) (*internalGetNodesResponse, error) {
var resp internalGetNodesResponse
err := clientutil.DoJSONHTTPRequest(ctx, c.be.Client(""), c.be.URL("")+"/api/internal/get_nodes", req, &resp)
err := c.be.Call(ctx, "", "/api/internal/get_nodes", req, &resp)
return &resp, err
}
func (c *client) internalUpdateNodes(ctx context.Context, req *internalUpdateNodesRequest) error {
return clientutil.DoJSONHTTPRequest(ctx, c.be.Client(""), c.be.URL("")+"/api/internal/update_nodes", req, nil)
return c.be.Call(ctx, "", "/api/internal/update_nodes", req, nil)
}
func (c *client) SetNodes(ctx context.Context, req *SetNodesRequest) (*SetNodesResponse, error) {
var resp SetNodesResponse
err := clientutil.DoJSONHTTPRequest(ctx, c.be.Client(""), c.be.URL("")+"/api/set_nodes", req, &resp)
err := c.be.Call(ctx, "", "/api/set_nodes", req, &resp)
return &resp, err
}
......
......@@ -84,7 +84,7 @@ func (s *HTTPServer) Handler() http.Handler {
return h
}
func nodes2str(nodes []Node) string {
func nodes2str(nodes []*Node) string {
var tmp []string
for _, node := range nodes {
tmp = append(tmp, fmt.Sprintf("%s@%d", node.Path, node.Timestamp.Unix()))
......
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"config:base"
]
}
*.swp
This diff is collapsed.
This diff is collapsed.