149 lines
		
	
	
	
		
			3.7 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			149 lines
		
	
	
	
		
			3.7 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
import base64
 | 
						|
 | 
						|
def derive_mailadmin_secret(metadata, salt):
 | 
						|
    node_id = metadata.get('id')
 | 
						|
    raw = base64.b64decode(
 | 
						|
        repo.vault.random_bytes_as_base64_for(f'{node_id}_{salt}', length=32).value
 | 
						|
    )
 | 
						|
    return base64.urlsafe_b64encode(raw).rstrip(b'=').decode('ascii')
 | 
						|
 | 
						|
 | 
						|
defaults = {
 | 
						|
    'apt': {
 | 
						|
        'packages': {
 | 
						|
            'mailman3-full': {
 | 
						|
                'needs': {
 | 
						|
                    'postgres_db:mailman',
 | 
						|
                    'postgres_role:mailman',
 | 
						|
                    'zfs_dataset:tank/mailman',
 | 
						|
                }
 | 
						|
            },
 | 
						|
            'postfix': {},
 | 
						|
            'python3-psycopg2': {
 | 
						|
                'needed_by': {
 | 
						|
                    'pkg_apt:mailman3-full',
 | 
						|
                },
 | 
						|
            },
 | 
						|
            'apache2': {
 | 
						|
                'installed': False,
 | 
						|
                'needs': {
 | 
						|
                    'pkg_apt:mailman3-full',
 | 
						|
                },
 | 
						|
            },
 | 
						|
        },
 | 
						|
    },
 | 
						|
    'zfs': {
 | 
						|
        'datasets': {
 | 
						|
            'tank/mailman': {
 | 
						|
                'mountpoint': '/var/lib/mailman3',
 | 
						|
            },
 | 
						|
        },
 | 
						|
    },
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
@metadata_reactor.provides(
 | 
						|
    'postgresql',
 | 
						|
    'mailman',
 | 
						|
)
 | 
						|
def postgresql(metadata):
 | 
						|
    node_id = metadata.get('id')
 | 
						|
    db_password = repo.vault.password_for(f'{node_id} database mailman')
 | 
						|
 | 
						|
    return {
 | 
						|
        'postgresql': {
 | 
						|
            'databases': {
 | 
						|
                'mailman': {
 | 
						|
                    'owner': 'mailman',
 | 
						|
                },
 | 
						|
            },
 | 
						|
            'roles': {
 | 
						|
                'mailman': {
 | 
						|
                    'password': db_password,
 | 
						|
                },
 | 
						|
            },
 | 
						|
        },
 | 
						|
        'mailman': {
 | 
						|
            'db_password': db_password,
 | 
						|
        },
 | 
						|
    }
 | 
						|
 | 
						|
 | 
						|
@metadata_reactor.provides(
 | 
						|
    'nginx/vhosts',
 | 
						|
)
 | 
						|
def nginx(metadata):
 | 
						|
    return {
 | 
						|
        'nginx': {
 | 
						|
            'vhosts': {
 | 
						|
                metadata.get('mailman/hostname'): {
 | 
						|
                    'content': 'mailman/vhost.conf',
 | 
						|
                },
 | 
						|
            },
 | 
						|
        },
 | 
						|
    }
 | 
						|
 | 
						|
 | 
						|
@metadata_reactor.provides(
 | 
						|
    'mailman/secret_key',
 | 
						|
)
 | 
						|
def secret_key(metadata):
 | 
						|
    import base64
 | 
						|
 | 
						|
    node_id = metadata.get('id')
 | 
						|
    raw = base64.b64decode(
 | 
						|
        repo.vault.random_bytes_as_base64_for(f'{node_id}_mailman_secret_key', length=32).value
 | 
						|
    )
 | 
						|
    secret_key = base64.urlsafe_b64encode(raw).rstrip(b'=').decode('ascii')
 | 
						|
 | 
						|
    return {
 | 
						|
        'mailman': {
 | 
						|
            'secret_key': secret_key,
 | 
						|
        },
 | 
						|
    }
 | 
						|
 | 
						|
 | 
						|
@metadata_reactor.provides(
 | 
						|
    'mailman',
 | 
						|
)
 | 
						|
def secrets(metadata):
 | 
						|
    return {
 | 
						|
        'mailman': {
 | 
						|
            'web_secret': derive_mailadmin_secret(metadata, 'secret_key'),
 | 
						|
            'api_password': derive_mailadmin_secret(metadata, 'api_password'),
 | 
						|
            'archiver_key': derive_mailadmin_secret(metadata, 'archiver_key'),
 | 
						|
        },
 | 
						|
    }
 | 
						|
 | 
						|
 | 
						|
@metadata_reactor.provides(
 | 
						|
    'dns',
 | 
						|
)
 | 
						|
def dns(metadata):
 | 
						|
    report_email = metadata.get('mailman/dmarc_report_email')
 | 
						|
 | 
						|
    return {
 | 
						|
        'dns': {
 | 
						|
            metadata.get('mailman/hostname'): {
 | 
						|
                'MX': [f"5 {metadata.get('mailman/hostname')}."],
 | 
						|
                'TXT': [
 | 
						|
                    'v=spf1 a mx -all',
 | 
						|
                    '; '.join(f'{k}={v}' for k, v in {
 | 
						|
                    # dmarc version
 | 
						|
                    'v': 'DMARC1',
 | 
						|
                    # reject on failure
 | 
						|
                    'p': 'reject',
 | 
						|
                    # standard reports
 | 
						|
                    'rua': f'mailto:{report_email}',
 | 
						|
                    # forensic reports
 | 
						|
                    'fo': 1,
 | 
						|
                    'ruf': f'mailto:{report_email}',
 | 
						|
                    # require alignment between the DKIM domain and the parent Header From domain
 | 
						|
                    'adkim': 's',
 | 
						|
                    # require alignment between the SPF domain (the sender) and the Header From domain
 | 
						|
                    'aspf': 's',
 | 
						|
                }.items())
 | 
						|
                ],
 | 
						|
            },
 | 
						|
        },
 | 
						|
    }
 |